PluginProbe
Datafeedr API / trunk
Datafeedr API vtrunk
1.4.2 1.0.125 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.19 1.0.2 1.0.20 1.0.21 1.0.22 1.0.23 1.0.24 1.0.25 1.0.26 1.0.27 1.0.28 1.0.29 1.0.3 1.0.30 1.0.31 1.0.32 1.0.33 All 181 releases
datafeedr-api / functions / global.php

global.php in Datafeedr API trunk, at functions/global.php

3,400 lines 99.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 defined( 'ABSPATH' ) || exit;
4
5 /**
6 * Returns true if the user's Datafeedr API keys exist or false if they do not.
7 *
8 * @return bool
9 */
10 function dfrapi_datafeedr_api_keys_exist(): bool {
11 return dfrapi_get_datafeedr_access_id() && dfrapi_get_datafeedr_secret_key();
12 }
13
14 /**
15 * Returns the user's Datafeedr API Access ID or false if it does not exist.
16 *
17 * @return false|string
18 */
19 function dfrapi_get_datafeedr_access_id() {
20
21 $configuration = (array) get_option( 'dfrapi_configuration', [] );
22
23 if ( ! isset( $configuration['access_id'] ) ) {
24 return false;
25 }
26
27 $access_id = trim( $configuration['access_id'] );
28
29 return ! empty( $access_id ) ? $access_id : false;
30 }
31
32 /**
33 * Returns the user's Datafeedr API Secret Key or false if it does not exist.
34 *
35 * @return false|string
36 */
37 function dfrapi_get_datafeedr_secret_key() {
38
39 $configuration = (array) get_option( 'dfrapi_configuration', [] );
40
41 if ( ! isset( $configuration['secret_key'] ) ) {
42 return false;
43 }
44
45 $secret_key = trim( $configuration['secret_key'] );
46
47 return ! empty( $secret_key ) ? $secret_key : false;
48 }
49
50 /**
51 * Returns the user's Datafeedr API Version or the default.
52 *
53 * @return string
54 */
55 function dfrapi_get_datafeedr_api_version(): string {
56
57 $configuration = (array) get_option( 'dfrapi_configuration', [] );
58
59 if ( ! isset( $configuration['api_version'] ) ) {
60 return dfrapi_get_default_api_version();
61 }
62
63 $api_version = trim( $configuration['api_version'] );
64
65 return in_array( $api_version, dfrapi_get_valid_api_versions(), true )
66 ? $api_version
67 : dfrapi_get_default_api_version();
68 }
69
70 /**
71 * Returns the valid API versions.
72 *
73 * @return array
74 */
75 function dfrapi_get_valid_api_versions(): array {
76 return [ 'stable' ];
77 }
78
79 /**
80 * Returns the default API version.
81 *
82 * @return string
83 */
84 function dfrapi_get_default_api_version(): string {
85 return 'stable';
86 }
87
88 /**
89 * Returns the user's API requests usage as a percentage of their total requests allowed.
90 *
91 * @return float|int
92 */
93 function dfrapi_get_api_usage_percentage() {
94 _deprecated_function( __FUNCTION__, '1.3.0', 'dfrapi_get_api_usage_as_percentage()' );
95
96 return dfrapi_get_api_usage_as_percentage();
97 }
98
99 /**
100 * Returns true if the user has used more than 90% of their API requests. Otherwise returns false.
101 *
102 * @return bool
103 */
104 function dfrapi_api_usage_over_90_percent(): bool {
105 return dfrapi_get_api_usage_as_percentage() >= 90;
106 }
107
108 /**
109 * Helper method for setting emails as HTML.
110 *
111 * @return string
112 */
113 function dfrapi_set_html_content_type(): string {
114 return 'text/html';
115 }
116
117 /**
118 * Get Zanox Keys.
119 *
120 * @return array|bool Array of keys or false if they do not exist.
121 */
122 function dfrapi_get_zanox_keys() {
123
124 $configuration = (array) get_option( 'dfrapi_configuration' );
125
126 $zanox_connection_key = false;
127 $zanox_secret_key = false;
128
129 if ( isset( $configuration['zanox_connection_key'] ) && ( $configuration['zanox_connection_key'] != '' ) ) {
130 $zanox_connection_key = $configuration['zanox_connection_key'];
131 }
132
133 if ( isset( $configuration['zanox_secret_key'] ) && ( $configuration['zanox_secret_key'] != '' ) ) {
134 $zanox_secret_key = $configuration['zanox_secret_key'];
135 }
136
137 if ( $zanox_connection_key && $zanox_secret_key ) {
138 return array(
139 'connection_key' => $zanox_connection_key,
140 'secret_key' => $zanox_secret_key,
141 );
142 }
143
144 return false;
145 }
146
147 /**
148 * Get Partnerize Keys.
149 *
150 * @since 1.0.66
151 *
152 * @return array|bool Array of keys or false if they do not exist.
153 */
154 function dfrapi_get_ph_keys() {
155
156 $configuration = (array) get_option( 'dfrapi_configuration' );
157
158 $ph_application_key = false;
159 $ph_user_api_key = false;
160 $ph_publisher_id = false;
161
162 if ( isset( $configuration['ph_application_key'] ) && ( $configuration['ph_application_key'] != '' ) ) {
163 $ph_application_key = $configuration['ph_application_key'];
164 }
165
166 if ( isset( $configuration['ph_user_api_key'] ) && ( $configuration['ph_user_api_key'] != '' ) ) {
167 $ph_user_api_key = $configuration['ph_user_api_key'];
168 }
169
170 if ( isset( $configuration['ph_publisher_id'] ) && ( $configuration['ph_publisher_id'] != '' ) ) {
171 $ph_publisher_id = $configuration['ph_publisher_id'];
172 }
173
174 if ( $ph_application_key && $ph_user_api_key && $ph_publisher_id ) {
175 return array(
176 'application_key' => $ph_application_key,
177 'user_api_key' => $ph_user_api_key,
178 'publisher_id' => $ph_publisher_id,
179 );
180 }
181
182 return false;
183 }
184
185 /**
186 * Get Effiliation Keys.
187 *
188 * @since 1.0.81
189 *
190 * @return array|bool Array of keys or false if they do not exist.
191 */
192 function dfrapi_get_effiliation_keys() {
193
194 $configuration = (array) get_option( 'dfrapi_configuration' );
195
196 $effiliation_key = false;
197
198 if ( isset( $configuration['effiliation_key'] ) && ( $configuration['effiliation_key'] != '' ) ) {
199 $effiliation_key = $configuration['effiliation_key'];
200 }
201
202 if ( $effiliation_key ) {
203 return array(
204 'effiliation_key' => $effiliation_key,
205 );
206 }
207
208 return false;
209 }
210
211 /**
212 * Returns Amazon API key credentials if they exist.
213 *
214 * If the Amazon API keys such as the Access Key ID, Secret Access Key, Tracking ID and Locale all
215 * exists, then this function returns them in array format. Otherwise it returns false.
216 *
217 * @since 1.0.33
218 *
219 * @return array|bool Returns array of values if all values exist, otherwise false.
220 */
221 function dfrapi_get_amazon_keys() {
222
223 $configuration = (array) get_option( 'dfrapi_configuration' );
224
225 $amazon_access_key_id = false;
226 $amazon_secret_access_key = false;
227 $amazon_tracking_id = false;
228 $amazon_locale = false;
229
230 if ( isset( $configuration['amazon_access_key_id'] ) && ( $configuration['amazon_access_key_id'] != '' ) ) {
231 $amazon_access_key_id = $configuration['amazon_access_key_id'];
232 }
233
234 if ( isset( $configuration['amazon_secret_access_key'] ) && ( $configuration['amazon_secret_access_key'] != '' ) ) {
235 $amazon_secret_access_key = $configuration['amazon_secret_access_key'];
236 }
237
238 if ( isset( $configuration['amazon_tracking_id'] ) && ( $configuration['amazon_tracking_id'] != '' ) ) {
239 $amazon_tracking_id = $configuration['amazon_tracking_id'];
240 }
241
242 if ( isset( $configuration['amazon_locale'] ) && ( $configuration['amazon_locale'] != '' ) ) {
243 $amazon_locale = $configuration['amazon_locale'];
244 }
245
246 if ( $amazon_access_key_id && $amazon_secret_access_key && $amazon_tracking_id && $amazon_locale ) {
247 return array(
248 'amazon_access_key_id' => $amazon_access_key_id,
249 'amazon_secret_access_key' => $amazon_secret_access_key,
250 'amazon_tracking_id' => $amazon_tracking_id,
251 'amazon_locale' => $amazon_locale,
252 );
253 }
254
255 return false;
256 }
257
258 /**
259 * Returns a link to a user page on v4.datafeedr.com.
260 */
261 function dfrapi_user_pages( $page ) {
262
263 $pages = array(
264 'edit' => 'https://datafeedr.me/dashboard',
265 'invoices' => 'https://datafeedr.me/dashboard',
266 'billing' => 'https://datafeedr.me/dashboard',
267 'cancel' => 'https://datafeedr.me/dashboard',
268 'change' => 'https://datafeedr.me/dashboard',
269 'signup' => 'https://datafeedr.me/dashboard',
270 'summary' => 'https://datafeedr.me/dashboard',
271 'api' => 'https://datafeedr.me/dashboard',
272 'factory' => 'https://datafeedr.me/dashboard',
273 );
274
275 return $pages[ $page ];
276 }
277
278 /**
279 * Adds option name to transient whitelist. This is so we know
280 * all transient options that can be deleted when deleting the
281 * API cache on Tools page.
282 */
283 function dfrapi_update_transient_whitelist( $option_name ) {
284 $whitelist = (array) get_option( 'dfrapi_transient_whitelist', [] );
285 $whitelist[] = $option_name;
286 update_option( 'dfrapi_transient_whitelist', array_unique( $whitelist ) );
287 }
288
289 /**
290 * Add affiliate ID and tracking ID to an affiliate link.
291 *
292 * @param array $product An array of a single Datafeedr $product.
293 *
294 * @return string A URL with affiliate ID inserted or an empty string if the affiliate ID is missing.
295 */
296 function dfrapi_url( $product ) {
297
298 // Get all the user's selected networks.
299 $networks = (array) get_option( 'dfrapi_networks' );
300
301 // Support added for Amazon in version 1.0.60 (2017-10-18) Ticket #15201
302 if ( substr( $product['source'], 0, 6 ) === "Amazon" ) {
303 // Get the user's Amazon Associate Tag
304 $affiliate_id = dfrapi_get_amazon_associate_tag();
305 } else {
306 // Extract the affiliate ID from the $networks array.
307 $affiliate_id = isset( $networks['ids'][ $product['source_id'] ]['aid'] ) ? $networks['ids'][ $product['source_id'] ]['aid'] : '';
308 }
309
310 $affiliate_id = apply_filters( 'dfrapi_affiliate_id', $affiliate_id, $product, $networks );
311 $affiliate_id = trim( $affiliate_id );
312
313 // Extract the Tracking ID from the $networks array.
314 $tracking_id = ( isset( $networks['ids'][ $product['source_id'] ]['tid'] ) ) ? $networks['ids'][ $product['source_id'] ]['tid'] : '';
315 $tracking_id = apply_filters( 'dfrapi_tracking_id', $tracking_id, $product, $networks );
316 $tracking_id = trim( $tracking_id );
317
318 // Affiliate ID is missing. Do action and return empty string.
319 if ( $affiliate_id == '' ) {
320 do_action( 'dfrapi_affiliate_id_is_missing', $product );
321
322 return '';
323 }
324
325 // Determine which URL field to get: 'url' OR 'ref_url'. Return 'url' if $tracking_id is empty, otherwise, use 'ref_url'.
326 $url = ( $tracking_id !== '' && isset( $product['ref_url'] ) ) ? $product['ref_url'] : $product['url'];
327
328 // Apply filters to URL before affiliate & tracking ID insertion.
329 $url = apply_filters( 'dfrapi_before_affiliate_id_insertion', $url, $product, $affiliate_id );
330 $url = apply_filters( 'dfrapi_before_tracking_id_insertion', $url, $product, $tracking_id );
331
332 // Replace placeholders in URL.
333 $placeholders = array( "@@@", "###" );
334 $replacements = array( $affiliate_id, $tracking_id );
335 $url = str_replace( $placeholders, $replacements, $url );
336
337 // Apply filters to URL after affiliate & tracking ID insertion.
338 $url = apply_filters( 'dfrapi_after_affiliate_id_insertion', $url, $product, $affiliate_id );
339 $url = apply_filters( 'dfrapi_after_tracking_id_insertion', $url, $product, $tracking_id );
340
341 // Return URL
342 return $url;
343 }
344
345 /**
346 * Return Amazon Associate Tag (ie. Tracking ID).
347 *
348 * @since 1.0.60
349 *
350 * @return string Associate Tag or empty string if it does not exist.
351 */
352 function dfrapi_get_amazon_associate_tag() {
353 $config = get_option( 'dfrapi_configuration' );
354
355 return ( isset( $config['amazon_tracking_id'] ) ) ? $config['amazon_tracking_id'] : '';
356 }
357
358 /**
359 * Add affiliate ID to impression URL.
360 *
361 * Since 1.0.39
362 *
363 * @param $product - An array of a single's product's information.
364 */
365 function dfrapi_impression_url( $product ) {
366
367 $impression_url = ( isset( $product['impressionurl'] ) ) ? trim( $product['impressionurl'] ) : false;
368
369 if ( ! $impression_url ) {
370 return '';
371 }
372
373 // Get all the user's selected networks.
374 $networks = (array) get_option( 'dfrapi_networks' );
375
376 // Extract the affiliate ID from the $networks array.
377 $affiliate_id = $networks['ids'][ $product['source_id'] ]['aid'];
378 $affiliate_id = apply_filters( 'dfrapi_affiliate_id', $affiliate_id, $product, $networks );
379 $affiliate_id = trim( $affiliate_id );
380
381 // Affiliate ID is missing. Do action and return empty string.
382 if ( $affiliate_id == '' ) {
383 do_action( 'dfrapi_affiliate_id_is_missing_impression', $product );
384
385 return '';
386 }
387
388 // Apply filters to URL before affiliate & tracking ID insertion.
389 $impression_url = apply_filters( 'dfrapi_before_affiliate_id_insertion_impression', $impression_url, $product, $affiliate_id );
390
391 // Replace placeholders in URL.
392 $placeholders = array( "@@@" );
393 $replacements = array( $affiliate_id );
394 $impression_url = str_replace( $placeholders, $replacements, $impression_url );
395
396 // Apply filters to URL after affiliate & tracking ID insertion.
397 $impression_url = apply_filters( 'dfrapi_after_affiliate_id_insertion_impression', $impression_url, $product, $affiliate_id );
398
399 // Return URL
400 return $impression_url;
401 }
402
403 /**
404 * Output an error message generated by the API.
405 */
406 function dfrapi_output_api_error( $data ) {
407 $error = @$data['dfrapi_api_error'];
408 $params = @$data['dfrapi_api_error']['params'];
409 ?>
410 <div class="dfrapi_api_error">
411 <div class="dfrapi_head"><?php _e( 'Datafeedr API Error', 'datafeedr-api' ); ?></div>
412 <div class="dfrapi_msg">
413 <strong><?php _e( 'Message:', 'datafeedr-api' ); ?></strong> <?php echo $error['msg']; ?>
414 </div>
415 <div class="dfrapi_code"><strong><?php _e( 'Code:', 'datafeedr-api' ); ?></strong> <?php echo $error['code']; ?>
416 </div>
417 <div class="dfrapi_class">
418 <strong><?php _e( 'Class:', 'datafeedr-api' ); ?></strong> <?php echo $error['class']; ?></div>
419 <?php if ( is_array( $params ) ) : ?>
420 <div class="dfrps_query"><strong><?php _e( 'Query:', 'datafeedr-api' ); ?></strong>
421 <span><?php echo dfrapi_display_api_request( $params ); ?></span></div>
422 <?php endif; ?>
423 </div>
424 <?php
425 }
426
427 /**
428 * Convert a currency code to sign. USD => $
429 *
430 * @https://github.com/pelle/bux/blob/master/src/bux/currencies.clj
431 *
432 * Currently supported currencies:
433 *
434 * AUD Australia &#36;
435 * BRL Brazil R$
436 * CAD Canada &#36;
437 * CHF Switzerland Fr
438 * DKK Denmark kr
439 * EUR Belgium &euro;
440 * EUR Finland &euro;
441 * EUR France &euro;
442 * EUR Germany &euro;
443 * EUR Ireland &euro;
444 * EUR Italy &euro;
445 * EUR Netherlands &euro;
446 * EUR Spain &euro;
447 * GBP United Kingdom &pound;
448 * HUF Hungary Fr
449 * INR India &#8377;
450 * MYR Malaysia RM
451 * NOK Norway kr
452 * NZD New Zealand &#36;
453 * PHP Philippines &#8369;
454 * PLN Poland zł
455 * RON Romania L
456 * RUB Russia ₽
457 * SEK Sweden kr
458 * TRY Turkey &#8356;
459 * USD United States &#36;
460 *
461 * @param string $code 3-character ISO 4217 currency code.
462 *
463 * @return mixed|string
464 */
465 function dfrapi_currency_code_to_sign( $code ) {
466 return dfrapi_currency( $code )->get_currency_symbol();
467 }
468
469 /**
470 * This displays the API request in PHP format.
471 */
472 function dfrapi_display_api_request( $params = array() ) {
473
474 $html = '';
475
476 if ( empty( $params ) ) {
477 return $html;
478 }
479
480 $html .= '$search = $api->searchRequest();<br />';
481 foreach ( $params as $k => $v ) {
482
483 // Handle query.
484 if ( $k === 'query' ) {
485 foreach ( $v as $query ) {
486 if ( substr( $query, 0, 9 ) !== 'source_id' || substr( $query, 0, 11 ) !== 'merchant_id' ) {
487 $query = str_replace( ",", ", ", $query );
488 }
489 $html .= '$search->addFilter( \'' . ( $query ) . '\' );<br />';
490 }
491 }
492
493 // Handle sort.
494 if ( $k === 'sort' ) {
495 foreach ( $v as $sort ) {
496 $html .= '$search->addSort( \'' . stripslashes( $sort ) . '\' );<br />';
497 }
498 }
499
500 // Handle limit.
501 if ( $k === 'limit' ) {
502 $html .= '$search->setLimit( \'' . stripslashes( $v ) . '\' );<br />';
503 }
504
505 // Handle merchant_limit.
506 if ( $k === 'merchant_limit' ) {
507 $html .= '$search->setMerchantLimit( \'' . stripslashes( absint( $v ) ) . '\' );<br />';
508 }
509
510 // Handle Offset.
511 if ( $k === 'offset' ) {
512 $html .= '$search->setOffset( \'' . stripslashes( $v ) . '\' );<br />';
513 }
514
515 // Handle Exclude duplicates.
516 if ( $k === 'exclude_duplicates' ) {
517 $html .= '$search->excludeDuplicates( \'' . $v . '\' );<br />';
518 }
519 }
520
521 $html .= '$products = $search->execute();';
522
523 return $html;
524
525 }
526
527 function dfrapi_get_query_param( $query, $param ) {
528 if ( is_array( $query ) && ! empty( $query ) ) {
529 foreach ( $query as $k => $v ) {
530 if ( $v['field'] == $param ) {
531 return array(
532 'field' => @$v['field'],
533 'operator' => @$v['operator'],
534 'value' => @$v['value'],
535 );
536 }
537 }
538 }
539
540 return false;
541 }
542
543 /**
544 * Converts a value in cents into a value with proper
545 * decimal placement.
546 *
547 * Example: 14999 => 149.99
548 */
549 function dfrapi_int_to_price( $price ) {
550 return number_format( ( $price / 100 ), 2 );
551 }
552
553 /**
554 * Converts decimal or none decimal prices into values in cents.
555 *
556 * assert(dfrapi_price_to_int('123') ==12300);
557 * assert(dfrapi_price_to_int('123.4') ==12340);
558 * assert(dfrapi_price_to_int('1234.56') ==123456);
559 * assert(dfrapi_price_to_int('123,4') ==12340);
560 * assert(dfrapi_price_to_int('1234,56') ==123456);
561 * assert(dfrapi_price_to_int('1,234,567') ==123456700);
562 * assert(dfrapi_price_to_int('1,234,567.8') ==123456780);
563 * assert(dfrapi_price_to_int('1,234,567.89') ==123456789);
564 * assert(dfrapi_price_to_int('1.234.567') ==123456700);
565 * assert(dfrapi_price_to_int('1.234.567,8') ==123456780);
566 * assert(dfrapi_price_to_int('1.234.567,89') ==123456789);
567 * assert(dfrapi_price_to_int('FOO 123 BAR') ==12300);
568 */
569 function dfrapi_price_to_int( $price ) {
570 $d = $price;
571 $d = preg_replace( '~^[^\d.,]+~', '', $d );
572 $d = preg_replace( '~[^\d.,]+$~', '', $d );
573
574 // 123 => 12300
575 if ( preg_match( '~^(\d+)$~', $d, $m ) ) {
576 return intval( $m[1] . '00' );
577 }
578
579 // 123.4 => 12340, 123,45 => 12345
580 if ( preg_match( '~^(\d+)[.,](\d{1,2})$~', $d, $m ) ) {
581 return intval( $m[1] . substr( $m[2] . '0000', 0, 2 ) );
582 }
583
584 // 1,234,567.89 => 123456789
585 if ( preg_match( '~^((?:\d{1,3})(?:,\d{3})*)(\.\d{1,2})?$~', $d, $m ) ) {
586 $f = isset( $m[2] ) ? $m[2] : '.';
587
588 return intval( str_replace( ',', '', $m[1] ) . substr( $f . '0000', 1, 2 ) );
589 }
590
591 // 1.234.567,89 => 123456789
592 if ( preg_match( '~^((?:\d{1,3})(?:\.\d{3})*)(,\d{1,2})?$~', $d, $m ) ) {
593 $f = isset( $m[2] ) ? $m[2] : '.';
594
595 return intval( str_replace( '.', '', $m[1] ) . substr( $f . '0000', 1, 2 ) );
596 }
597
598 return null;
599 }
600
601 function dfrapi_html_output_api_error( $data ) {
602 $error = $data['dfrapi_api_error'];
603 $params = @$data['dfrapi_api_error']['params'];
604 ?>
605 <div class="dfrapi_api_error">
606 <div class="dfrapi_head"><?php _e( 'Datafeedr API Error', 'datafeedr-api' ); ?></div>
607 <div class="dfrapi_msg">
608 <strong><?php _e( 'Message:', 'datafeedr-api' ); ?></strong> <?php echo $error['msg']; ?>
609 </div>
610 <div class="dfrapi_code"><strong><?php _e( 'Code:', 'datafeedr-api' ); ?></strong> <?php echo $error['code']; ?>
611 </div>
612 <div class="dfrapi_class">
613 <strong><?php _e( 'Class:', 'datafeedr-api' ); ?></strong> <?php echo $error['class']; ?></div>
614 <?php if ( is_array( $params ) ) : ?>
615 <div class="dfrapi_query"><strong><?php _e( 'Query:', 'datafeedr-api' ); ?></strong>
616 <span><?php echo dfrapi_helper_display_api_request( $params ); ?></span></div>
617 <?php endif; ?>
618 </div>
619 <?php
620 }
621
622 /**
623 * Returns the total number of products in the Datafeedr database (pulled from dfrapi_account option).
624 *
625 * @param bool $formatted
626 * @param mixed $default
627 *
628 * @return int|string
629 */
630 function dfrapi_get_total_products_in_db( $formatted = true, $default = 0 ) {
631 $account = (array) get_option( 'dfrapi_account', [] );
632 $count = absint( $account['product_count'] ?? $default );
633
634 return $formatted ? number_format_i18n( $count ) : $count;
635 }
636
637 /**
638 * Imports an image from a URL into the WordPress Media Library.
639 *
640 * @since 1.2.2 Will return either the Attachment ID or WP_Error if there was an error importing the image.
641 *
642 * @since 1.0.71
643 *
644 * @param string $url Image URL.
645 * @param array $args Optional. An array of options.
646 *
647 * $args = array(
648 *
649 * This is the ID of the post we want to attach this image to. If we do not
650 * want this image to be attached to a post, leave this set to 0.
651 * 'post_id' => 0,
652 *
653 * This is name of the file name the image will have once it is stored on
654 * on the server in the WordPress uploads directory.
655 * 'file_name' => '',
656 *
657 * This is the ID of the User this image will be associated with.
658 * 'user_id' => 0,
659 *
660 * This is the title of the image (which is different than the file name).
661 * 'title' => '',
662 *
663 * The description of the image.
664 * 'description' => '',
665 *
666 * The caption for the image.
667 * 'caption' => '',
668 *
669 * The alt text. Text to display if image cannot be loaded.
670 * 'alt_text' => '',
671 *
672 * Whether this image should be set as the post's thumbnail. If the post_id is 0, this setting will be ignored.
673 * 'is_post_thumbnail' => false,
674 *
675 * The number of seconds to spend attempting to download the image.
676 * 'timeout' => 5
677 *
678 * Sets the image's owner and source. _owner_datafeedr : dfrapi
679 * '_source_plugin' => 'dfrapi'
680 * );
681 *
682 * @return Datafeedr_Image_Importer|int|WP_Error
683 */
684 function datafeedr_import_image( $url, $args = [] ) {
685
686 if ( dfrapi_use_legacy_image_importer() ) {
687 return ( new Datafeedr_Image_Importer( $url, $args ) )->import();
688 }
689
690 $default_args = [
691 'title' => '',
692 'file_name' => '',
693 'description' => '',
694 'caption' => '',
695 'alt_text' => '',
696 'user_id' => 0,
697 'post_id' => 0,
698 'is_post_thumbnail' => true,
699 'timeout' => 5,
700 '_source_plugin' => 'dfrapi',
701 ];
702
703 $args = array_merge( $default_args, $args );
704
705 $image_data = dfrapi_image_data( $url );
706
707 $image_data->set_title( $args['title'] );
708 $image_data->set_filename( $args['file_name'] );
709 $image_data->set_description( $args['description'] );
710 $image_data->set_caption( $args['caption'] );
711 $image_data->set_alternative_text( $args['alt_text'] );
712 $image_data->set_author_id( absint( $args['user_id'] ) );
713 $image_data->set_post_parent_id( absint( $args['post_id'] ) );
714 $image_data->set_post_thumbnail( (bool) $args['is_post_thumbnail'] );
715
716 $image_data = apply_filters( 'datafeedr_import_image_image_data', $image_data, $url, $args );
717
718 $uploader = dfrapi_image_uploader( $image_data );
719
720 $uploader->set_timeout( absint( $args['timeout'] ) );
721
722 $attachment_id = $uploader->upload();
723
724 if ( ! is_wp_error( $attachment_id ) ) {
725 update_post_meta( $attachment_id, '_owner_datafeedr', sanitize_text_field( $args['_source_plugin'] ) );
726 }
727
728 do_action( 'datafeedr_import_image_attachment_id', $attachment_id, $image_data, $url, $args );
729
730 return $attachment_id;
731 }
732
733 /**
734 * Returns true if the $string starts with one of the $patterns. Otherwise returns false.
735 *
736 * @since 1.0.78
737 *
738 * @param string|array $patterns The patterns to search for in the beginning of the $string.
739 *
740 * @param string $string The haystack.
741 *
742 * @return bool True if string starts with the pattern(s) else returns false.
743 */
744 function dfrapi_string_starts_with( $string, $patterns ) {
745 $patterns = ( is_string( $patterns ) ) ? array( $patterns ) : $patterns;
746 foreach ( $patterns as $pattern ) {
747 $length = mb_strlen( $pattern );
748 if ( mb_substr( $string, 0, $length ) === $pattern ) {
749 return true;
750 }
751 }
752
753 return false;
754 }
755
756 /**
757 * Returns true if we are viewing a Datafeedr-specific page in the WordPress Admin Area.
758 *
759 * @since 1.0.84
760 *
761 * @return bool
762 * @global $pagenow
763 *
764 */
765 function dfrapi_is_datafeedr_admin_page() {
766
767 /**
768 * For post edit pages (ie. post.php?post=1&action=edit).
769 */
770 $post_types = [
771 'datafeedr-productset',
772 ];
773
774 /**
775 * For $_GET params (ie. admin.php?page=dfrps_configuration).
776 */
777 $params = [
778 'page' => [
779 'dfrapi',
780 'dfrapi_networks',
781 'dfrapi_merchants',
782 'dfrapi_tools',
783 'dfrapi_export',
784 'dfrapi_import',
785 'dfrapi_account',
786 'dfrcs_options',
787 'dfrps_configuration',
788 'dfrps_tools',
789 'dfrpswc_options',
790 ],
791 'post_type' => [
792 'datafeedr-productset'
793 ]
794 ];
795
796 foreach ( $params as $k => $v ) {
797 if ( isset( $_GET[ $k ] ) && in_array( $_GET[ $k ], $v ) ) {
798 return true;
799 }
800 }
801
802 global $pagenow;
803
804 if ( 'post.php' === $pagenow && in_array( get_post_type(), $post_types ) ) {
805 return true;
806 }
807
808 return false;
809 }
810
811 /**
812 * @param integer $network_id
813 * @param string $id_type
814 *
815 * @return WP_Error|string
816 */
817 function dfrapi_get_affiliate_and_tracking_id( $network_id, $id_type = 'aid' ) {
818
819 static $networks = null;
820
821 $key = 'ids';
822 $type = ( 'tid' === $id_type ) ? 'tid' : 'aid';
823
824 if ( null === $networks ) {
825 $networks = get_option( 'dfrapi_networks', [] );
826 }
827
828 if ( empty( $networks ) ) {
829 return new WP_Error(
830 'dfrapi_get_affiliate_id_no_networks',
831 __( 'No networks selected.', 'datafeedr-api' )
832 );
833 }
834
835 if ( ! isset( $networks[ $key ] ) ) {
836 return new WP_Error(
837 'dfrapi_get_affiliate_id_no_network_ids',
838 __( 'No network IDs selected.', 'datafeedr-api' )
839 );
840 }
841
842 if ( ! isset( $networks[ $key ][ $network_id ] ) ) {
843 return new WP_Error(
844 'dfrapi_get_affiliate_id_no_network_ids',
845 __( 'No data for network with ID of ' . intval( $network_id ), 'datafeedr-api' )
846 );
847 }
848
849 if ( ! isset( $networks[ $key ][ $network_id ][ $type ] ) || empty( $networks[ $key ][ $network_id ][ $type ] ) ) {
850 return new WP_Error(
851 'dfrapi_get_affiliate_id_empty_type',
852 __( 'No affiliate or tracking ID entered for network with ID of ' . intval( $network_id ), 'datafeedr-api' )
853 );
854 }
855
856 return $networks[ $key ][ $network_id ][ $type ];
857 }
858
859 /**
860 * Get The Affiliate Gateway SID from this page WordPress Admin Area > Datafeedr API > Configuration
861 *
862 * @since 1.0.102
863 * @return string|WP_Error
864 */
865 function dfrapi_get_affiliate_gateway_sid() {
866
867 static $sid = null;
868
869 if ( null === $sid ) {
870
871 $config = get_option( 'dfrapi_configuration', [] );
872
873 $sid = ( isset( $config['affiliate_gateway_sid'] ) && ! empty( $config['affiliate_gateway_sid'] ) ) ?
874 trim( $config['affiliate_gateway_sid'] ) :
875 new WP_Error(
876 'missing_affiliate_gateway_sid',
877 'Please enter your The Affiliate Gateway SID <a href="' . admin_url( 'admin.php?page=dfrapi' ) . '" target="_blank">here</a>.'
878 );
879 }
880
881 return $sid;
882 }
883
884 /**
885 * Get Adservice Media ID from this page WordPress Admin Area > Datafeedr API > Configuration
886 *
887 * @since 1.0.102
888 * @return string|WP_Error
889 */
890 function dfrapi_get_adservice_mid() {
891
892 static $sid = null;
893
894 if ( null === $sid ) {
895
896 $config = get_option( 'dfrapi_configuration', [] );
897
898 $sid = ( isset( $config['adservice_mid'] ) && ! empty( $config['adservice_mid'] ) ) ?
899 trim( $config['adservice_mid'] ) :
900 new WP_Error(
901 'missing_adservice_mid',
902 'Please enter your Adservice Media ID <a href="' . admin_url( 'admin.php?page=dfrapi' ) . '" target="_blank">here</a>.'
903 );
904 }
905
906 return $sid;
907 }
908
909 /**
910 * Get Belboon Adspace ID from this page WordPress Admin Area > Datafeedr API > Configuration
911 *
912 * @since 1.0.124
913 * @return string|WP_Error
914 */
915 function dfrapi_get_belboon_adspace_id() {
916
917 static $aid = null;
918
919 if ( null === $aid ) {
920
921 $config = get_option( 'dfrapi_configuration', [] );
922
923 $aid = ( isset( $config['belboon_aid'] ) && ! empty( $config['belboon_aid'] ) ) ?
924 trim( $config['belboon_aid'] ) :
925 new WP_Error(
926 'missing_belboon_aid',
927 'Please enter your Belboon Adspace ID <a href="' . admin_url( 'admin.php?page=dfrapi' ) . '" target="_blank">here</a>.'
928 );
929 }
930
931 return $aid;
932 }
933
934 /**
935 * @param string $url
936 * @param string $method
937 * @param array $args
938 *
939 * @return SimpleXMLElement|WP_Error
940 */
941 function dfrapi_get_xml_response( $url, $method = 'GET', array $args = [] ) {
942
943 $response = $method === 'GET' ? wp_remote_get( $url, $args ) : wp_remote_post( $url, $args );
944
945 if ( is_wp_error( $response ) ) {
946 return $response;
947 }
948
949 $code = wp_remote_retrieve_response_code( $response );
950 $body = wp_remote_retrieve_body( $response );
951
952 if ( $code < 200 || $code >= 300 ) {
953 return new WP_Error( $code, strip_tags( $body ) );
954 }
955
956 if ( ! strlen( $body ) ) {
957 return new WP_Error( 'connection_error', esc_html__( 'Empty response', 'datafeedr' ) );
958 }
959
960 $xml = simplexml_load_string( $body, null, LIBXML_NOCDATA );
961
962 if ( $xml->getName() === 'error' ) {
963 return new WP_Error( $code, esc_html( strval( $xml->message ) ) );
964 }
965
966 return $xml;
967 }
968
969 /**
970 * Determine if a given string ends with a given substring.
971 *
972 * @link https://github.com/illuminate/support/blob/7.x/Str.php
973 *
974 * @param string $haystack
975 * @param string|string[] $needles
976 *
977 * @return bool
978 */
979 function dfrapi_ends_with( $haystack, $needles ) {
980 foreach ( (array) $needles as $needle ) {
981 if ( $needle !== '' && substr( $haystack, - strlen( $needle ) ) === (string) $needle ) {
982 return true;
983 }
984 }
985
986 return false;
987 }
988
989 /**
990 * Determine if a given string starts with a given substring.
991 *
992 * @link https://github.com/illuminate/support/blob/7.x/Str.php
993 *
994 * @param string $haystack
995 * @param string|string[] $needles
996 *
997 * @return bool
998 */
999 function dfrapi_starts_with( $haystack, $needles ) {
1000 foreach ( (array) $needles as $needle ) {
1001 if ( (string) $needle !== '' && strncmp( $haystack, $needle, strlen( $needle ) ) === 0 ) {
1002 return true;
1003 }
1004 }
1005
1006 return false;
1007 }
1008
1009 /**
1010 * Returns the portion of string specified by the start and length parameters.
1011 *
1012 * @link https://github.com/illuminate/support/blob/7.x/Str.php
1013 *
1014 * @param string $string
1015 * @param int $start
1016 * @param int|null $length
1017 *
1018 * @return string
1019 */
1020 function dfrapi_substr( $string, $start, $length = null ) {
1021 return mb_substr( $string, $start, $length, 'UTF-8' );
1022 }
1023
1024 /**
1025 * Get the portion of a string before the first occurrence of a given value.
1026 *
1027 * @link https://github.com/illuminate/support/blob/7.x/Str.php
1028 *
1029 * @param string $subject
1030 * @param string $search
1031 *
1032 * @return string
1033 */
1034 function dfrapi_str_before( $subject, $search ) {
1035 return $search === '' ? $subject : explode( $search, $subject )[0];
1036 }
1037
1038 /**
1039 * Get the portion of a string before the last occurrence of a given value.
1040 *
1041 * @link https://github.com/illuminate/support/blob/7.x/Str.php
1042 *
1043 * @param string $subject
1044 * @param string $search
1045 *
1046 * @return string
1047 */
1048 function dfrapi_str_before_last( $subject, $search ) {
1049
1050 if ( $search === '' ) {
1051 return $subject;
1052 }
1053
1054 $pos = mb_strrpos( $subject, $search );
1055
1056 if ( $pos === false ) {
1057 return $subject;
1058 }
1059
1060 return dfrapi_substr( $subject, 0, $pos );
1061 }
1062
1063 /**
1064 * Get the portion of a string between two given values.
1065 *
1066 * @link https://github.com/illuminate/support/blob/7.x/Str.php
1067 *
1068 * @param string $subject
1069 * @param string $from
1070 * @param string $to
1071 *
1072 * @return string
1073 */
1074 function dfrapi_str_between( $subject, $from, $to ) {
1075 if ( $from === '' || $to === '' ) {
1076 return $subject;
1077 }
1078
1079 return dfrapi_str_before_last( dfrapi_str_after( $subject, $from ), $to );
1080 }
1081
1082 /**
1083 * Return the remainder of a string after the first occurrence of a given value.
1084 *
1085 * @link https://github.com/illuminate/support/blob/7.x/Str.php
1086 *
1087 * @param string $subject
1088 * @param string $search
1089 *
1090 * @return string
1091 */
1092 function dfrapi_str_after( $subject, $search ): string {
1093 return $search === '' ? $subject : array_reverse( explode( $search, $subject, 2 ) )[0];
1094 }
1095
1096 /**
1097 * Return the remainder of a string after the last occurrence of a given value.
1098 *
1099 * @link https://github.com/illuminate/support/blob/7.x/Str.php
1100 *
1101 * @param string $subject
1102 * @param string $search
1103 *
1104 * @return string
1105 */
1106 function dfrapi_str_after_last( $subject, $search ) {
1107
1108 if ( $search === '' ) {
1109 return $subject;
1110 }
1111
1112 $position = strrpos( $subject, (string) $search );
1113
1114 if ( $position === false ) {
1115 return $subject;
1116 }
1117
1118 return substr( $subject, $position + strlen( $search ) );
1119 }
1120
1121 /**
1122 * Determine if a given string contains a given substring.
1123 *
1124 * @link https://github.com/illuminate/support/blob/7.x/Str.php
1125 *
1126 * @param string $haystack
1127 * @param string|string[] $needles
1128 *
1129 * @return bool
1130 */
1131 function dfrapi_str_contains( $haystack, $needles ) {
1132 foreach ( (array) $needles as $needle ) {
1133 if ( $needle !== '' && mb_strpos( $haystack, $needle ) !== false ) {
1134 return true;
1135 }
1136 }
1137
1138 return false;
1139 }
1140
1141 /**
1142 * Determine if a given string contains all array values.
1143 *
1144 * @link https://github.com/illuminate/support/blob/7.x/Str.php
1145 *
1146 * @param string $haystack
1147 * @param string[] $needles
1148 *
1149 * @return bool
1150 */
1151 function dfrapi_str_contains_all( $haystack, array $needles ) {
1152 foreach ( $needles as $needle ) {
1153 if ( ! dfrapi_str_contains( $haystack, $needle ) ) {
1154 return false;
1155 }
1156 }
1157
1158 return true;
1159 }
1160
1161 /**
1162 * Returns an integer value of string by:
1163 * - removing all non-numeric characters
1164 * - retaining any negative sign
1165 * - running through intval to clean up any other weirdness.
1166 *
1167 * This function is pretty forgiving.
1168 *
1169 * Examples:
1170 *
1171 * '123.45' => 12345
1172 * '-123.45' => -12345
1173 * 'foo' => 0
1174 * '-foo' => 0
1175 * '-123-56' => -123
1176 * '123-45' => 123
1177 * '-123,56' => -12356
1178 * '$-1.111,45' => -111145
1179 * '00012.34' => 1234
1180 *
1181 * @param string|int|numeric $int
1182 *
1183 * @return int
1184 */
1185 function dfrapi_intify( $int ): int {
1186 return (int) preg_replace( '/[^0-9-]/', '', $int );
1187 }
1188
1189 /**
1190 * Returns an instance of the Dfrapi_Price class.
1191 *
1192 * @param mixed $value The value to use as the price.
1193 * @param string $currency_code 3-character ISO 4217 currency code.
1194 * @param mixed $context Optional.
1195 *
1196 * @return Dfrapi_Price
1197 */
1198 function dfrapi_price( $value, $currency_code, $context = null ): Dfrapi_Price {
1199 return new Dfrapi_Price( $value, dfrapi_currency( $currency_code, $context ), $context );
1200 }
1201
1202 /**
1203 * Returns an instance of the Dfrapi_Currency class.
1204 *
1205 * @param string $currency_code 3-character ISO 4217 currency code.
1206 * @param mixed $context Optional.
1207 *
1208 * @return Dfrapi_Currency
1209 */
1210 function dfrapi_currency( $currency_code, $context = null ): Dfrapi_Currency {
1211 return new Dfrapi_Currency( $currency_code, $context );
1212 }
1213
1214 /**
1215 * Returns the fully formatted price.
1216 *
1217 * @param mixed $value The value to use as the price.
1218 * @param string $currency_code 3-character ISO 4217 currency code.
1219 * @param mixed $context Optional.
1220 *
1221 * @return string
1222 */
1223 function dfrapi_get_price( $value, $currency_code, $context = null ): string {
1224 return dfrapi_price( $value, $currency_code, $context )->get_price();
1225 }
1226
1227 /**
1228 * Returns an instance of the Dfrapi_Image_Data class.
1229 *
1230 * @param string $url The URL of the image we will be uploading.
1231 *
1232 * @return Dfrapi_Image_Data
1233 */
1234 function dfrapi_image_data( string $url ): Dfrapi_Image_Data {
1235 return new Dfrapi_Image_Data( $url );
1236 }
1237
1238 /**
1239 * Returns an instance of the Dfrapi_Image_Uploader class.
1240 *
1241 * @param Dfrapi_Image_Data $image_data
1242 *
1243 * @return Dfrapi_Image_Uploader
1244 */
1245 function dfrapi_image_uploader( Dfrapi_Image_Data $image_data ): Dfrapi_Image_Uploader {
1246 return new Dfrapi_Image_Uploader( $image_data );
1247 }
1248
1249 /**
1250 * Returns the string to use as the prefix for the ActionScheduler hook name.
1251 *
1252 * @return string
1253 */
1254 function dfrapi_as_hook_prefix(): string {
1255 return 'dfrapi_as_';
1256 }
1257
1258 /**
1259 * Formats and returns the hook name.
1260 *
1261 * @param string $hook
1262 *
1263 * @return string
1264 */
1265 function dfrapi_as_hook_name( string $hook ): string {
1266 return dfrapi_as_hook_prefix() . trim( $hook );
1267 }
1268
1269 /**
1270 * Returns true is the ActionScheduler library exists otherwise returns WP_Error.
1271 *
1272 * The ActionScheduler ships with WooCommerce but can also be installed independently
1273 * here: https://wordpress.org/plugins/action-scheduler/
1274 *
1275 * @return true|WP_Error
1276 */
1277 function dfrapi_action_scheduler_exists() {
1278 return function_exists( 'as_schedule_recurring_action' )
1279 ? true
1280 : new WP_Error( 'dfrapi_action_scheduler_does_not_exist.', __( 'The ActionScheduler library does not exist.', 'datafeedr-api' ) );
1281 }
1282
1283 /**
1284 * Enqueue an action to run one time, as soon as possible
1285 *
1286 * @param string $hook
1287 * @param array $args
1288 * @param string $group
1289 *
1290 * @return string The action ID.
1291 */
1292 function dfrapi_schedule_async_action( string $hook, array $args = [], string $group = 'datafeedr' ) {
1293 return ( dfrapi_action_scheduler_exists() === true )
1294 ? as_enqueue_async_action( dfrapi_as_hook_name( $hook ), $args, $group )
1295 : dfrapi_action_scheduler_exists();
1296 }
1297
1298 /**
1299 * Schedule an action to run one time
1300 *
1301 * @param int $timestamp
1302 * @param string $hook
1303 * @param array $args
1304 * @param string $group
1305 *
1306 * @return string The action ID
1307 */
1308 function dfrapi_schedule_single_action( int $timestamp, string $hook, array $args = [], string $group = 'datafeedr' ) {
1309 return ( dfrapi_action_scheduler_exists() === true )
1310 ? as_schedule_single_action( $timestamp, dfrapi_as_hook_name( $hook ), $args, $group )
1311 : dfrapi_action_scheduler_exists();
1312 }
1313
1314 /**
1315 * Schedule a recurring action using ActionScheduler.
1316 *
1317 * @param int $timestamp
1318 * @param int $interval_in_seconds
1319 * @param string $hook
1320 * @param array $args
1321 * @param string $group
1322 *
1323 * @return int|WP_Error The action ID or WP_Error if as_schedule_recurring_action() function does not exist.
1324 */
1325 function dfrapi_schedule_recurring_action( int $timestamp, int $interval_in_seconds, string $hook, array $args = [], string $group = 'datafeedr' ) {
1326 return ( dfrapi_action_scheduler_exists() === true )
1327 ? as_schedule_recurring_action( $timestamp, $interval_in_seconds, dfrapi_as_hook_name( $hook ), $args, $group )
1328 : dfrapi_action_scheduler_exists();
1329 }
1330
1331 /**
1332 * Schedule an action that recurs on a cron-like schedule.
1333 *
1334 * @param int $timestamp The first instance of the action will be scheduled to run at a time calculated after this timestamp matching the cron expression. This can be used to delay the first instance of the action.
1335 * @param string $schedule A cron-like schedule string (See: http://en.wikipedia.org/wiki/Cron)
1336 * * * * * * *
1337 * ┬ ┬ ┬ ┬ ┬ ┬
1338 * | | | | | |
1339 * | | | | | + year [optional]
1340 * | | | | +----- day of week (0 - 7) (Sunday=0 or 7)
1341 * | | | +---------- month (1 - 12)
1342 * | | +--------------- day of month (1 - 31)
1343 * | +-------------------- hour (0 - 23)
1344 * +------------------------- min (0 - 59)
1345 * @param string $hook
1346 * @param array $args
1347 * @param string $group
1348 *
1349 * @return int|WP_Error The action ID or WP_Error if as_schedule_cron_action() function does not exist.
1350 */
1351 function dfrapi_schedule_cron_action( int $timestamp, string $schedule, string $hook, array $args = [], string $group = 'datafeedr' ) {
1352 return ( dfrapi_action_scheduler_exists() === true )
1353 ? as_schedule_cron_action( $timestamp, $schedule, dfrapi_as_hook_name( $hook ), $args, $group )
1354 : dfrapi_action_scheduler_exists();
1355 }
1356
1357 /**
1358 * Unschedule a scheduled action.
1359 *
1360 * @param string $hook
1361 * @param array $args
1362 * @param string $group
1363 *
1364 * @return string|null|WP_Error The scheduled action ID if a scheduled action was found, or null if no matching action found. WP_Error if as_ function doesn't exist.
1365 */
1366 function dfrapi_unschedule_action( string $hook, array $args = [], string $group = 'datafeedr' ) {
1367 return ( dfrapi_action_scheduler_exists() === true )
1368 ? as_unschedule_action( dfrapi_as_hook_name( $hook ), $args, $group )
1369 : dfrapi_action_scheduler_exists();
1370 }
1371
1372 /**
1373 * Returns the timestamp for the next occurrence of a pending scheduled action,
1374 * returns true for an async or in-progress action or false if there is no matching action.
1375 *
1376 * @param string $hook
1377 * @param array $args
1378 * @param string $group
1379 *
1380 * @return int|bool|WP_Error The timestamp for the next occurrence of a pending scheduled action, true for an async or in-progress action or false if there is no matching action. WP_Error if as_ function doesn't exist.
1381 */
1382 function dfrapi_next_scheduled_action( string $hook, array $args = [], string $group = 'datafeedr' ) {
1383 return ( dfrapi_action_scheduler_exists() === true )
1384 ? as_next_scheduled_action( dfrapi_as_hook_name( $hook ), $args, $group )
1385 : dfrapi_action_scheduler_exists();
1386 }
1387
1388 /**
1389 * Returns true if the Jetpack::class exists.
1390 *
1391 * @return bool
1392 */
1393 function dfrapi_jetpack_exists(): bool {
1394 return class_exists( Jetpack::class, false );
1395 }
1396
1397 /**
1398 * Returns true is Jetpack is active, otherwise returns false.
1399 *
1400 * @return bool
1401 */
1402 function dfrapi_jetpack_is_active(): bool {
1403 return dfrapi_jetpack_exists() ? Jetpack::is_active() : false;
1404 }
1405
1406 /**
1407 * Returns true if Jetpack is in Dev/Debug mode.
1408 *
1409 * @return bool
1410 */
1411 function dfrapi_jetpack_is_in_dev_mode(): bool {
1412 return dfrapi_jetpack_exists() ? defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG === true : false;
1413 }
1414
1415 /**
1416 * Returns true if "Speed up image load times" is ON here:
1417 * WordPress Admin Area > Jetpack > Settings > Performance > Performance & speed
1418 *
1419 * @return bool
1420 */
1421 function dfrapi_jetpack_photon_module_is_active(): bool {
1422 return dfrapi_jetpack_exists() ? in_array( 'photon', Jetpack::get_active_modules() ) : false;
1423 }
1424
1425 /**
1426 * Returns true if "Speed up static file load times" is ON here:
1427 * WordPress Admin Area > Jetpack > Settings > Performance > Performance & speed
1428 *
1429 * @return bool
1430 */
1431 function dfrapi_jetpack_photon_cdn_module_is_active(): bool {
1432 return dfrapi_jetpack_exists() ? in_array( 'photon-cdn', Jetpack::get_active_modules() ) : false;
1433 }
1434
1435 /**
1436 * @param string $image_url URL to the publicly accessible image you want to manipulate.
1437 * @param array|string $args An array of arguments, i.e. array( 'w' => '300', 'resize' => array( 123, 456 ) ), or in string form (w=123&h=456).
1438 * @param string|null $scheme URL protocol.
1439 *
1440 * @return false|string
1441 */
1442 function dfrapi_jetpack_photon_url( $image_url, $args = [], $scheme = null ) {
1443
1444 if ( ! dfrapi_jetpack_is_active() && ! dfrapi_jetpack_is_in_dev_mode() ) {
1445 return $image_url;
1446 }
1447
1448 return jetpack_photon_url( $image_url, $args, $scheme );
1449 }
1450
1451 /**
1452 * Whether to use the legacy image importer. Default false.
1453 *
1454 * @return bool
1455 */
1456 function dfrapi_use_legacy_image_importer(): bool {
1457 return (bool) apply_filters( 'dfrapi_use_legacy_image_importer', false );
1458 }
1459
1460 /**
1461 * Formats an Admin Notice and echos it.
1462 *
1463 * You must send a fully escaped $message to dfrapi_admin_notice() because this
1464 * function will NOT escape the $message variable.
1465 *
1466 * Also, $message is already wrapped in <p></p> tags. Therefore, it cannot contain
1467 * additional <p> tags or any other HTML element not allowed as a child to <p> tags.
1468 *
1469 * @param string $message Notice message. Will NOT be escaped.
1470 * @param string $status Either error, warning, success or info.
1471 * @param string|null $heading Optional. The notice heading or title.
1472 * @param string|null $plugin Optional. The name of the plugin responsible for generating this notice.
1473 *
1474 * @return void
1475 */
1476 function dfrapi_admin_notice( string $message, string $status, ?string $heading = null, ?string $plugin = null ) {
1477 $plugin = $plugin ? esc_html( trim( $plugin ) ) : '';
1478 $heading = $heading ? esc_html( trim( $heading ) ) : '';
1479 $separator = $plugin && $heading ? ' &mdash; ' : '';
1480 $label = $plugin || $heading ? sprintf( '<strong>%1$s%2$s%3$s</strong><br>', $plugin, $separator, $heading ) : '';
1481
1482 $status = in_array( $status, [ 'error', 'warning', 'success', 'info' ] ) ? $status : 'info';
1483 $class = esc_attr( 'notice notice-' . $status );
1484
1485 printf( '<div class="%1$s"><p>%2$s%3$s</p></div>', $class, $label, $message );
1486 }
1487
1488 /**
1489 * Get selected networks. Format is like this:
1490 *
1491 * Array (
1492 * [ids] => Array (
1493 * [18] => Array (
1494 * [nid] => 18
1495 * [aid] => abc123
1496 * [tid] =>
1497 * )
1498 * [1200] => Array (
1499 * [nid] => 1200
1500 * [aid] => qwerty
1501 * [tid] =>
1502 * )
1503 * [126] => Array (
1504 * [nid] => 126
1505 * [aid] => 15759
1506 * [tid] =>
1507 * )
1508 * )
1509 * )
1510 *
1511 * @return array
1512 */
1513 function dfrapi_get_selected_networks(): array {
1514 return (array) get_option( 'dfrapi_networks', [] );
1515 }
1516
1517 /**
1518 * Get the list of selected Network IDs.
1519 *
1520 * @return array
1521 */
1522 function dfrapi_get_selected_network_ids(): array {
1523
1524 static $ids = null;
1525
1526 if ( $ids === null ) {
1527
1528 $ids = [];
1529 $networks = dfrapi_get_selected_networks();
1530
1531 if ( ! isset( $networks['ids'] ) ) {
1532 return $ids;
1533 }
1534
1535 if ( empty( $networks['ids'] ) ) {
1536 return $ids;
1537 }
1538
1539 if ( ! is_array( $networks['ids'] ) ) {
1540 return $ids;
1541 }
1542
1543 foreach ( $networks['ids'] as $k => $v ) {
1544 $nid = absint( $v['nid'] ?? 0 );
1545 if ( $nid > 0 ) {
1546 $ids[] = absint( $nid );
1547 }
1548 }
1549
1550 $ids = array_filter( array_unique( $ids ) );
1551 }
1552
1553 return $ids;
1554 }
1555
1556 /**
1557 * Get the total number of selected networks.
1558 *
1559 * @return int
1560 */
1561 function dfrapi_selected_network_count(): int {
1562 return count( dfrapi_get_selected_network_ids() );
1563 }
1564
1565 /**
1566 * Returns true if the user has selected at least 1 network. Otherwise, returns false.
1567 *
1568 * @return bool
1569 */
1570 function dfrapi_user_has_selected_networks(): bool {
1571 return dfrapi_selected_network_count() > 0;
1572 }
1573
1574 /**
1575 * Get selected merchants. Format is like this:
1576 *
1577 * Array (
1578 * [ids] => Array (
1579 * [0] => 1258
1580 * [1] => 12927
1581 * [2] => 1312
1582 * [3] => 14342
1583 * )
1584 * )
1585 *
1586 * @return array
1587 */
1588 function dfrapi_get_selected_merchants(): array {
1589 return (array) get_option( 'dfrapi_merchants', [] );
1590 }
1591
1592 /**
1593 * Get the list of selected Merchant IDs.
1594 *
1595 * @return array
1596 */
1597 function dfrapi_get_selected_merchant_ids(): array {
1598
1599 static $ids = null;
1600
1601 if ( $ids === null ) {
1602
1603 $ids = [];
1604 $merchants = dfrapi_get_selected_merchants();
1605
1606 if ( ! isset( $merchants['ids'] ) ) {
1607 return $ids;
1608 }
1609
1610 if ( empty( $merchants['ids'] ) ) {
1611 return $ids;
1612 }
1613
1614 if ( ! is_array( $merchants['ids'] ) ) {
1615 return $ids;
1616 }
1617
1618 foreach ( $merchants['ids'] as $id ) {
1619 $ids[] = absint( $id );
1620 }
1621
1622 $ids = array_filter( array_unique( $ids ) );
1623 }
1624
1625 return $ids;
1626 }
1627
1628 /**
1629 * Get the total number of selected merchants.
1630 *
1631 * @return int
1632 */
1633 function dfrapi_selected_merchant_count(): int {
1634 return count( dfrapi_get_selected_merchant_ids() );
1635 }
1636
1637 /**
1638 * Returns true if the user has selected at least 1 merchant. Otherwise, returns false.
1639 *
1640 * @return bool
1641 */
1642 function dfrapi_user_has_selected_merchants(): bool {
1643 return dfrapi_selected_merchant_count() > 0;
1644 }
1645
1646 /**
1647 * Returns the absolute URL for the Datafeedr API > Networks page.
1648 *
1649 * @return string
1650 */
1651 function dfrapi_networks_page_url(): string {
1652 return add_query_arg( [ 'page' => 'dfrapi_networks' ], admin_url( 'admin.php' ) );
1653 }
1654
1655 /**
1656 * Returns the absolute URL for the Datafeedr API > Merchants page.
1657 *
1658 * @return string
1659 */
1660 function dfrapi_merchants_page_url(): string {
1661 return add_query_arg( [ 'page' => 'dfrapi_merchants' ], admin_url( 'admin.php' ) );
1662 }
1663
1664 /**
1665 * Returns the absolute URL for the Datafeedr API > Configuration page.
1666 *
1667 * @return string
1668 */
1669 function dfrapi_configuration_page_url(): string {
1670 return add_query_arg( [ 'page' => 'dfrapi' ], admin_url( 'admin.php' ) );
1671 }
1672
1673 /**
1674 * Returns the absolute URL for the Datafeedr API > Tools page.
1675 *
1676 * @return string
1677 */
1678 function dfrapi_tools_page_url(): string {
1679 return add_query_arg( [ 'page' => 'dfrapi_tools' ], admin_url( 'admin.php' ) );
1680 }
1681
1682 /**
1683 * Returns an array of Network IDs of those networks who require an affiliate ID but the
1684 * user has not yet entered an affiliate ID.
1685 *
1686 * @return array
1687 */
1688 function dfrapi_get_network_ids_missing_affiliate_id(): array {
1689
1690 static $ids = null;
1691
1692 if ( $ids === null ) {
1693
1694 $ids = [];
1695 $networks = dfrapi_get_selected_networks();
1696
1697 if ( ! isset( $networks['ids'] ) ) {
1698 return $ids;
1699 }
1700
1701 if ( ! is_array( $networks['ids'] ) ) {
1702 return $ids;
1703 }
1704
1705 $no_affiliate_id_required = dfrapi_get_ids_of_networks_which_dont_require_affiliate_ids();
1706
1707 foreach ( $networks['ids'] as $k => $v ) {
1708
1709 $nid = absint( $v['nid'] ?? 0 );
1710
1711 if ( in_array( $nid, $no_affiliate_id_required, true ) ) {
1712 continue;
1713 }
1714
1715 $aid = trim( $v['aid'] ?? '' );
1716
1717 if ( $nid > 0 && empty( $aid ) ) {
1718 $ids[] = $nid;
1719 }
1720 }
1721 }
1722
1723 return array_filter( array_unique( $ids ) );
1724 }
1725
1726 /**
1727 * Returns an array of Network IDs which don't require the user to enter an affiliate ID.
1728 *
1729 * @return array
1730 */
1731 function dfrapi_get_ids_of_networks_which_dont_require_affiliate_ids(): array {
1732 return array_merge(
1733 dfrapi_get_partnerize_network_ids(),
1734 dfrapi_get_effiliation_network_ids()
1735 );
1736 }
1737
1738 /**
1739 * Returns true if user is missing at least one affiliate ID. Otherwise, returns false.
1740 *
1741 * @return bool
1742 */
1743 function dfrapi_user_is_missing_affiliate_ids(): bool {
1744 return count( dfrapi_get_network_ids_missing_affiliate_id() ) > 0;
1745 }
1746
1747 // Functions from functions/api.php 2022-02-14 14:02:41 from here to effiliation_ids function
1748 function dfrapi_api_get_status() {
1749 $api = dfrapi_api( dfrapi_get_transport_method() );
1750 try {
1751 $status = $api->getStatus();
1752 dfrapi_api_update_status( $api );
1753
1754 return $status;
1755 } catch ( Exception $err ) {
1756 return dfrapi_api_error( $err );
1757 }
1758 }
1759
1760 /**
1761 * Removed configuration. Always returns 'wordpress'. 2017-02-21 10:23:10
1762 */
1763 function dfrapi_get_transport_method(): string {
1764 return 'wordpress';
1765 }
1766
1767 /**
1768 * This instantiates the Datafeedr API Library and returns the $api object.
1769 */
1770 function dfrapi_api( $transport = 'curl', $timeout = 0, $returnObjects = false ) {
1771
1772 $configuration = (array) get_option( 'dfrapi_configuration' );
1773
1774 if ( isset( $configuration['disable_api'] ) && ( $configuration['disable_api'] === 'yes' ) ) {
1775 $configuration['disable_api'] = 'no';
1776 update_option( 'dfrapi_configuration', $configuration );
1777 }
1778
1779 $access_id = false;
1780 $secret_key = false;
1781 $transport = dfrapi_get_transport_method();
1782
1783 if ( isset( $configuration['access_id'] ) && ( $configuration['access_id'] != '' ) ) {
1784 $access_id = $configuration['access_id'];
1785 }
1786
1787 if ( isset( $configuration['secret_key'] ) && ( $configuration['secret_key'] != '' ) ) {
1788 $secret_key = $configuration['secret_key'];
1789 }
1790
1791 if ( $access_id && $secret_key ) {
1792
1793 $options = [
1794 'transport' => 'wordpress',
1795 'timeout' => 60,
1796 'returnObjects' => false,
1797 'retry' => 3, // The number of retries if an API request times-out.
1798 'retryTimeout' => 5, // The number of seconds to wait between retries.
1799 ];
1800
1801 $options = apply_filters( 'dfrapi_api_options', $options );
1802
1803 $options['domain'] = parse_url( get_site_url(), PHP_URL_HOST );
1804
1805 return new DatafeedrApi( $access_id, $secret_key, $options );
1806
1807 } else {
1808 return false;
1809 }
1810 }
1811
1812 /**
1813 * Creates an associate array with the API's error details.
1814 */
1815 function dfrapi_api_error( $error, $params = false ) {
1816
1817 // Change "request_count" to "max_requests" because sometimes there's
1818 // not even enough API requests left to update the Account info with
1819 // the most update to date information.
1820 if ( $error->getCode() == 301 ) {
1821 $account = get_option( 'dfrapi_account', array() );
1822 $account['request_count'] = $account['max_requests'];
1823 update_option( 'dfrapi_account', $account );
1824 }
1825
1826 return array(
1827 'dfrapi_api_error' => array(
1828 'class' => get_class( $error ),
1829 'code' => $error->getCode(),
1830 'msg' => $error->getMessage(),
1831 'params' => $params,
1832 )
1833 );
1834 }
1835
1836 /**
1837 * Creates the proper API request from the $query.
1838 */
1839 function dfrapi_api_query_to_filters( $query, $useSelected = true ) {
1840 $sform = new Dfrapi_SearchForm();
1841
1842 return $sform->makeFilters( $query, $useSelected );
1843 }
1844
1845 /**
1846 * Returns a parameter value from the $query array.
1847 */
1848 function dfrapi_api_get_query_param( $query, $param ) {
1849 if ( is_array( $query ) && ! empty( $query ) ) {
1850 foreach ( $query as $k => $v ) {
1851 if ( $v['field'] == $param ) {
1852 return array(
1853 'field' => $v['field'] ?? '',
1854 'operator' => $v['operator'] ?? '',
1855 'value' => $v['value'] ?? '',
1856 );
1857 }
1858 }
1859 }
1860
1861 return false;
1862 }
1863
1864 /**
1865 * This updates the "dfrapi_account" option with the most recent
1866 * API status information for this user.
1867 */
1868 function dfrapi_api_update_status( &$api ) {
1869 if ( $status = $api->lastStatus() ) {
1870 $account = get_option( 'dfrapi_account', array() );
1871 $account['user_id'] = $status['user_id'];
1872 $account['plan_id'] = $status['plan_id'];
1873 $account['bill_day'] = $status['bill_day'];
1874 $account['max_total'] = $status['max_total'];
1875 $account['max_length'] = $status['max_length'];
1876 $account['max_requests'] = $status['max_requests'];
1877 $account['request_count'] = $status['request_count'];
1878 $account['network_count'] = $status['network_count'];
1879 $account['product_count'] = $status['product_count'];
1880 $account['merchant_count'] = $status['merchant_count'];
1881 update_option( 'dfrapi_account', $account );
1882 }
1883 }
1884
1885 /**
1886 * This returns all affiliate networks' information.
1887 * This accepts an array of source_ids (network ids)
1888 * to return a subset of networks.
1889 */
1890 function dfrapi_api_get_all_networks( $nids = array() ) {
1891 $option_name = 'dfrapi_all_networks';
1892 $use_cache = wp_using_ext_object_cache( false );
1893 $networks = get_transient( $option_name );
1894 wp_using_ext_object_cache( $use_cache );
1895 if ( false === $networks || empty ( $networks ) ) {
1896 $api = dfrapi_api( dfrapi_get_transport_method() );
1897 try {
1898 $networks = $api->getNetworks( $nids, true );
1899 dfrapi_api_set_network_types( $networks );
1900 dfrapi_api_update_status( $api );
1901 } catch ( Exception $err ) {
1902 return dfrapi_api_error( $err );
1903 }
1904 $use_cache = wp_using_ext_object_cache( false );
1905 set_transient( $option_name, $networks, MONTH_IN_SECONDS );
1906 wp_using_ext_object_cache( $use_cache );
1907 }
1908 dfrapi_update_transient_whitelist( $option_name );
1909
1910 usort( $networks, function ( $a, $b ) {
1911 return strnatcasecmp( $a['name'], $b['name'] );
1912 } );
1913
1914 return array_filter( $networks, static function ( $network ) {
1915 return ! in_array( absint( $network['_id'] ), dfrapi_inactive_networks(), true );
1916 } );
1917 }
1918
1919 /**
1920 * Returns a Zanox zmid value.
1921 */
1922 function dfrapi_api_get_zanox_zmid( $merchant_id, $adspace_id ) {
1923
1924 $option_name = 'zmid_' . $merchant_id . '_' . $adspace_id;
1925 $use_cache = wp_using_ext_object_cache( false );
1926 $zmid = get_transient( $option_name );
1927 wp_using_ext_object_cache( $use_cache );
1928
1929 if ( $zmid ) {
1930 return $zmid;
1931 }
1932
1933 $keys = dfrapi_get_zanox_keys();
1934 $api = dfrapi_api();
1935
1936 try {
1937 $zmid = $api->getZanoxMerchantIds(
1938 $merchant_id,
1939 $adspace_id,
1940 $keys['connection_key']
1941 );
1942 } catch ( Exception $err ) {
1943 $zmid = 'dfrapi_unapproved_zanox_merchant';
1944 }
1945
1946 $use_cache = wp_using_ext_object_cache( false );
1947 set_transient( $option_name, $zmid, WEEK_IN_SECONDS );
1948 wp_using_ext_object_cache( $use_cache );
1949
1950 dfrapi_update_transient_whitelist( $option_name );
1951
1952 return $zmid;
1953 }
1954
1955 /**
1956 * Returns a Partnerize camref value.
1957 */
1958 function dfrapi_api_get_ph_camref( $merchant_id ) {
1959
1960 $option_name = 'camref_' . $merchant_id;
1961 $use_cache = wp_using_ext_object_cache( false );
1962 $camref = get_transient( $option_name );
1963 wp_using_ext_object_cache( $use_cache );
1964
1965 if ( $camref ) {
1966 return $camref;
1967 }
1968
1969 $keys = dfrapi_get_ph_keys();
1970 $api = dfrapi_api();
1971
1972 try {
1973 $camref = $api->getPerformanceHorizonCamrefs(
1974 $merchant_id,
1975 $keys['application_key'],
1976 $keys['user_api_key'],
1977 $keys['publisher_id']
1978 );
1979 } catch ( Exception $err ) {
1980 $camref = 'dfrapi_unapproved_ph_merchant';
1981 }
1982
1983 $use_cache = wp_using_ext_object_cache( false );
1984 set_transient( $option_name, $camref, WEEK_IN_SECONDS );
1985 wp_using_ext_object_cache( $use_cache );
1986
1987 dfrapi_update_transient_whitelist( $option_name );
1988
1989 return $camref;
1990 }
1991
1992 /**
1993 * Returns a Effiliation affiliate ID.
1994 *
1995 * @since 1.0.81
1996 */
1997 function dfrapi_api_get_effiliation_affiliate_id( $merchant_id ) {
1998
1999 $option_name = 'effiliation_' . $merchant_id;
2000 $use_cache = wp_using_ext_object_cache( false );
2001 $affiliate_id = get_transient( $option_name );
2002 wp_using_ext_object_cache( $use_cache );
2003
2004 if ( $affiliate_id ) {
2005 return $affiliate_id;
2006 }
2007
2008 try {
2009 $affiliate_id = dfrapi_get_affiliate_id_for_effiliation_merchant( $merchant_id );
2010 } catch ( Exception $err ) {
2011 $affiliate_id = 'dfrapi_unapproved_effiliation_merchant';
2012 }
2013
2014 $use_cache = wp_using_ext_object_cache( false );
2015 set_transient( $option_name, $affiliate_id, WEEK_IN_SECONDS );
2016 wp_using_ext_object_cache( $use_cache );
2017
2018 dfrapi_update_transient_whitelist( $option_name );
2019
2020 return $affiliate_id;
2021 }
2022
2023 /**
2024 * This creates 2 options in the options table each time the option
2025 * "dfrapi_all_networks" is updated with new network information from the API.
2026 *
2027 * - dfrapi_product_networks
2028 * - dfrapi_coupon_networks
2029 *
2030 * These are just helper options to figure out if a network is a "product"
2031 * network or a "coupon" network.
2032 */
2033 function dfrapi_api_set_network_types( $networks ) {
2034 $product_networks = array();
2035 $coupon_networks = array();
2036 foreach ( $networks as $network ) {
2037 if ( $network['type'] === 'products' ) {
2038 $product_networks[ $network['_id'] ] = $network;
2039 } elseif ( $network['type'] === 'coupons' ) {
2040 $coupon_networks[ $network['_id'] ] = $network;
2041 }
2042 }
2043 update_option( 'dfrapi_product_networks', $product_networks );
2044 update_option( 'dfrapi_coupon_networks', $coupon_networks );
2045 }
2046
2047 /**
2048 * This stores all merchants for a given source_id ($nid).
2049 *
2050 * It is possible to pass "all" to this function however this creates
2051 * memory_limit errors when memory is set to less than 64MB.
2052 */
2053 function dfrapi_api_get_all_merchants( $nid ) {
2054 $option_name = 'dfrapi_all_merchants_for_nid_' . $nid;
2055 $use_cache = wp_using_ext_object_cache( false );
2056 $merchants = get_transient( $option_name );
2057 wp_using_ext_object_cache( $use_cache );
2058 if ( false === $merchants || empty ( $merchants ) ) {
2059 $api = dfrapi_api( dfrapi_get_transport_method() );
2060 try {
2061 $merchants = $api->getMerchants( array( intval( $nid ) ), true );
2062 dfrapi_api_update_status( $api );
2063 } catch ( Exception $err ) {
2064 return dfrapi_api_error( $err );
2065 }
2066 $use_cache = wp_using_ext_object_cache( false );
2067 set_transient( $option_name, $merchants, MONTH_IN_SECONDS );
2068 wp_using_ext_object_cache( $use_cache );
2069 }
2070 dfrapi_update_transient_whitelist( $option_name );
2071
2072 return $merchants;
2073 }
2074
2075 /**
2076 * This returns merchant or merchants' information by merchant_id or
2077 * an array of merchant IDs.
2078 */
2079 function dfrapi_api_get_merchants_by_id( $ids, $includeEmpty = false ) {
2080 $name = false;
2081 if ( is_array( $ids ) ) {
2082 sort( $ids, SORT_NUMERIC );
2083 $id_string = implode( ",", $ids );
2084 $name = md5( $id_string );
2085 } elseif ( $ids != '' ) {
2086 $name = trim( $ids );
2087 }
2088 if ( ! $name ) {
2089 return;
2090 }
2091 $name = substr( $name, 0, 20 );
2092 $option_name = 'dfrapi_merchants_byid_' . $name;
2093 $use_cache = wp_using_ext_object_cache( false );
2094 $merchants = get_transient( $option_name );
2095 wp_using_ext_object_cache( $use_cache );
2096 if ( false === $merchants || empty ( $merchants ) ) {
2097 $api = dfrapi_api( dfrapi_get_transport_method() );
2098 try {
2099 $merchants = $api->getMerchantsById( $ids, $includeEmpty );
2100 dfrapi_api_update_status( $api );
2101 } catch ( Exception $err ) {
2102 return dfrapi_api_error( $err );
2103 }
2104 $use_cache = wp_using_ext_object_cache( false );
2105 set_transient( $option_name, $merchants, MONTH_IN_SECONDS );
2106 wp_using_ext_object_cache( $use_cache );
2107 }
2108 dfrapi_update_transient_whitelist( $option_name );
2109
2110 return $merchants;
2111 }
2112
2113 /**
2114 * Returns a $response array containing:
2115 * - ids: the query passed to the function.
2116 * - products: array of products.
2117 * - last_status: value of $api->lastStatus().
2118 * - found_count: value of $search->getFoundCount().
2119 *
2120 * If the API throws an exception, that will return dfrapi_api_error( $err );
2121 *
2122 * @param array $ids An array of product IDs.
2123 * @param int $ppp The number of products to return in 1 API request. Max is dictated by API, not plugin.
2124 * @param int $page The page number for returning products. This is used to figure the offset.
2125 */
2126 function dfrapi_api_get_products_by_id( $ids, $ppp = 20, $page = 1 ) {
2127
2128 $response = array();
2129
2130 // Return false if no $ids or no $postid
2131 if ( empty( $ids ) ) {
2132 return $response;
2133 }
2134
2135 // Make sure $page is a positive integer.
2136 $page = absint( $page );
2137
2138 // Make sure $ppp is a positive integer.
2139 $ppp = absint( $ppp );
2140
2141 // Make sure $ppp is not greater than "max_length".
2142 $account = (array) get_option( 'dfrapi_account' );
2143 if ( $ppp > $account['max_length'] ) {
2144 $ppp = $account['max_length'];
2145 }
2146
2147 // The maximum number of results a request to the API can return.
2148 // Changing this will only break your site. It's not overridable.
2149 $max_total = $account['max_total'];
2150
2151 // Determine offset.
2152 $offset = ( ( $page - 1 ) * $ppp );
2153
2154 // Make sure $limit doesn't go over 10,000.
2155 if ( ( $offset + $ppp ) > $max_total ) {
2156 $ppp = ( $max_total - $offset );
2157 }
2158
2159 // If $ppp is negative, return empty array();
2160 if ( $ppp < 1 ) {
2161 return array();
2162 }
2163
2164 // If offset is greater than 10,000 return empty array();
2165 if ( $offset >= ( $max_total - $ppp ) ) {
2166 return array();
2167 }
2168
2169 try {
2170
2171 // Initialize API.
2172 $api = dfrapi_api( dfrapi_get_transport_method() );
2173 if ( ! $api ) {
2174 return $response;
2175 }
2176
2177 // Get a range of product IDs to query.
2178 $id_range = array_slice( $ids, $offset, $ppp );
2179
2180 // Return immediately if $id_range is empty.
2181 if ( empty( $id_range ) ) {
2182 $response['ids'] = array();
2183 $response['products'] = array();
2184 $response['last_status'] = $api->lastStatus();
2185 $response['found_count'] = 0;
2186
2187 return $response;
2188 }
2189
2190 // Begin query
2191 $search = $api->searchRequest();
2192
2193 // Get filters
2194 $filters = dfrapi_api_query_to_filters( array() );
2195 if ( isset( $filters['error'] ) ) {
2196 throw new DatafeedrError( $filters['error'], 0 );
2197 }
2198
2199 // Loop thru filters.
2200 foreach ( $filters as $filter ) {
2201 $search->addFilter( $filter );
2202 }
2203
2204 $search->addFilter( 'id IN ' . implode( ",", $id_range ) );
2205 $search->setLimit( $ppp );
2206 $products = $search->execute();
2207
2208 // Keep track of IDs which were returned via the API to compare with $id_range (unreturned)
2209 $included_ids = array();
2210 if ( ! empty( $products ) ) {
2211 foreach ( $products as $product ) {
2212 $included_ids[] = $product['_id'];
2213 }
2214 }
2215
2216 // Excluded product IDs.
2217 $excluded_ids = array_diff( $id_range, $included_ids );
2218
2219 // Add "message" values to excluded IDs if there are some.
2220 $excluded_products = array();
2221 if ( ! empty( $included_ids ) && ! empty( $excluded_ids ) ) {
2222 foreach ( $excluded_ids as $excluded_id ) {
2223
2224 $wc_url = add_query_arg(
2225 array(
2226 's' => $excluded_id,
2227 'post_status' => 'trash',
2228 'post_type' => 'product',
2229 ),
2230 admin_url( 'edit.php' )
2231 );
2232
2233 // Do not add a 'url' field to this array or the unavailable product WILL be imported.
2234 // See /datafeedr-product-sets/classes/class-dfrps-update.php:73
2235 $excluded_products[] = array(
2236 '_id' => $excluded_id,
2237 '_wc_url' => $wc_url,
2238 'name' => $excluded_id . ' - ' . __( 'Unavailable', 'datafeedr-api' ),
2239 'price' => 0,
2240 'finalprice' => 0,
2241 'description' => __( 'This product is either temporarily or permanently unavailable.', 'datafeedr-api' ),
2242 'image' => DFRAPI_URL . 'images/icons/noimage.png',
2243 'merchant' => 'n/a',
2244 'source' => 'n/a',
2245 );
2246 }
2247 }
2248
2249 // Update API status
2250 dfrapi_api_update_status( $api );
2251
2252 // Build $response array().
2253 $response['ids'] = $ids;
2254 $response['products'] = array_merge( $products, $excluded_products );
2255 $response['last_status'] = $api->lastStatus();
2256 $response['found_count'] = count( $ids );
2257 $response['params'] = $search->getParams();
2258 $response['score'] = $search->getQueryScore();
2259
2260 // Return it!
2261 return $response;
2262
2263 } catch ( Exception $err ) {
2264 return dfrapi_api_error( $err );
2265 }
2266 }
2267
2268 /**
2269 * Returns a $response array containing:
2270 * - query: the query passed to the function.
2271 * - excluded: ids of excluded products.
2272 * - products: array of products.
2273 * - last_status: value of $api->lastStatus().
2274 * - found_count: value of $search->getFoundCount().
2275 * - params: value of $search->getParams().
2276 *
2277 * Example of $query array():
2278 *
2279 *
2280 * $query[] = array(
2281 * 'value' => 'shoes',
2282 * 'field' => 'any',
2283 * 'operator' => 'contain'
2284 * );
2285 *
2286 * $query[] = array(
2287 * 'value' => 'image',
2288 * 'field' => 'duplicates',
2289 * 'operator' => 'is'
2290 * );
2291 *
2292 * $query[] = array(
2293 * 'field' => 'sort',
2294 * 'operator' => '+saleprice'
2295 * );
2296 *
2297 *
2298 * If the API throws an exception, that will return dfrapi_api_error( $err, $params );
2299 *
2300 * @param array $query The complete query to pass to the API.
2301 * @param int $ppp The number of products to return in 1 API request. Max is dictated by API, not plugin.
2302 * @param int $page The page number for returning products. This is used to figure the offset.
2303 * @param array $excluded An array of product IDs to exclude from being returned.
2304 */
2305 function dfrapi_api_get_products_by_query( $query, $ppp = 20, $page = 1, $excluded = array() ) {
2306
2307 $response = array();
2308
2309 // Return false if no $query.
2310 if ( empty( $query ) ) {
2311 return $response;
2312 }
2313
2314 // Make sure $page is a positive integer.
2315 $page = absint( $page );
2316
2317 // Make sure $ppp is a positive integer.
2318 $ppp = absint( $ppp );
2319
2320 // Make sure $ppp is not greater than "max_length".
2321 $account = (array) get_option( 'dfrapi_account' );
2322 if ( $ppp > $account['max_length'] ) {
2323 $ppp = $account['max_length'];
2324 }
2325
2326 // The maximum number of results a request to the API can return.
2327 // Changing this will only break your site. It's not overridable.
2328 $max_total = $account['max_total'];
2329
2330 // Determine query limit (if exists).
2331 $query_limit = dfrapi_api_get_query_param( $query, 'limit' );
2332 $query_limit = ( $query_limit )
2333 ? $query_limit['value']
2334 : false;
2335
2336 // No query shall try to return more than 10,000 products.
2337 if ( $query_limit && ( $query_limit > $max_total ) ) {
2338 $query_limit = $max_total;
2339 }
2340
2341 // Determine merchant limit (if exists).
2342 $merchant_limit = dfrapi_api_get_query_param( $query, 'merchant_limit' );
2343 $merchant_limit = ( $merchant_limit )
2344 ? absint( $merchant_limit['value'] )
2345 : 0;
2346
2347 // Determine offset.
2348 $offset = ( ( $page - 1 ) * $ppp );
2349
2350 // If offset is greater than 10,000 return empty array();
2351 if ( $offset >= $max_total ) {
2352 return array();
2353 }
2354
2355 // Factor in query limit
2356 if ( $query_limit ) {
2357 if ( ( $ppp + $offset ) > $query_limit ) {
2358 $ppp = ( $query_limit - $offset );
2359 }
2360 }
2361
2362 // Make sure $limit doesn't go over 10,000.
2363 if ( ( $offset + $ppp ) > $max_total ) {
2364 $ppp = ( $max_total - $offset );
2365 }
2366
2367 // If $ppp is negative, return empty array();
2368 if ( $ppp < 1 ) {
2369 return $response;
2370 }
2371
2372 try {
2373
2374 // Initialize API.
2375 $api = dfrapi_api( dfrapi_get_transport_method() );
2376 if ( ! $api ) {
2377 return $response;
2378 }
2379
2380 $search = $api->searchRequest();
2381
2382 // Get filters
2383 $filters = dfrapi_api_query_to_filters( $query );
2384 if ( isset( $filters['error'] ) ) {
2385 throw new DatafeedrError( $filters['error'], 0 );
2386 }
2387
2388 // Loop thru filters.
2389 foreach ( $filters as $filter ) {
2390 $search->addFilter( $filter );
2391 }
2392
2393 // Exclude duplicates.
2394 $duplicates = dfrapi_api_get_query_param( $query, 'duplicates' );
2395 if ( $duplicates ) {
2396 $excludes = $duplicates['value'];
2397 $search->excludeDuplicates( $excludes );
2398 }
2399
2400 // Exclude blocked products.
2401 $excluded = (array) $excluded;
2402 if ( ! empty( $excluded ) ) {
2403 $search->addFilter( 'id !IN ' . implode( ",", $excluded ) );
2404 }
2405
2406 // Sort products.
2407 $sort = dfrapi_api_get_query_param( $query, 'sort' );
2408 if ( $sort && strlen( $sort['operator'] ) ) {
2409 $search->addSort( $sort['operator'] );
2410 }
2411
2412 // Set Merchant Limit
2413 $search->setMerchantLimit( $merchant_limit );
2414
2415 // Set limits and offset.
2416 $search->setLimit( $ppp );
2417 $search->setOffset( $offset );
2418
2419 // Execute query.
2420 $products = $search->execute();
2421
2422 // Update API status
2423 dfrapi_api_update_status( $api );
2424
2425 // Build $response array().
2426 $response['query'] = $query;
2427 $response['excluded'] = $excluded;
2428 $response['products'] = $products;
2429 $response['last_status'] = $api->lastStatus();
2430 $response['found_count'] = $search->getResultCount();
2431 $response['params'] = $search->getParams();
2432 $response['score'] = $search->getQueryScore();
2433
2434 // Return it!
2435 return $response;
2436
2437 } catch ( Exception $err ) {
2438 $params = $search->getParams();
2439
2440 return dfrapi_api_error( $err, $params );
2441
2442 }
2443 }
2444
2445 /**
2446 * Returns the URL to get the Effiliation product feeds URL with user's API injected into URL.
2447 *
2448 * @param string $api_key
2449 *
2450 * @return string
2451 */
2452 function dfrapi_get_effiliation_product_feeds_url( string $api_key ): string {
2453 return sprintf( 'http://apiv2.effiliation.com/apiv2/productfeeds.xml?key=%s&filter=mines&type=33&fields=0001010000110001', $api_key );
2454 }
2455
2456 /**
2457 * Get affiliate IDs from Effiliation.
2458 *
2459 * @param $api_key
2460 *
2461 * @return array|mixed|SimpleXMLElement|WP_Error
2462 */
2463 function dfrapi_request_effiliation_affiliate_ids( $api_key = null ) {
2464
2465 $option_name = 'effiliation_affiliate_ids';
2466 $use_cache = wp_using_ext_object_cache( false );
2467 $affiliate_ids = get_transient( $option_name );
2468 wp_using_ext_object_cache( $use_cache );
2469
2470 if ( $affiliate_ids ) {
2471 return $affiliate_ids;
2472 }
2473
2474 $keys = dfrapi_get_effiliation_keys();
2475 $api_key = $api_key ?: $keys['effiliation_key'];
2476 $method = 'GET';
2477 $url = dfrapi_get_effiliation_product_feeds_url( $api_key );
2478
2479 $xml = dfrapi_get_xml_response( $url, $method, [ 'timeout' => 30 ] );
2480
2481 if ( is_wp_error( $xml ) ) {
2482 return $xml;
2483 }
2484
2485 $affiliate_ids = [];
2486
2487 foreach ( $xml->feed as $e ) {
2488 $item = json_decode( json_encode( $e ), true );
2489 $suid = sanitize_text_field( $item['id_affilieur'] );
2490
2491 $affiliate_ids[ $suid ]['suid'] = ( $suid );
2492 $affiliate_ids[ $suid ]['affiliate_id'] = sanitize_text_field( $item['id_compteur'] );
2493 }
2494
2495 $use_cache = wp_using_ext_object_cache( false );
2496 set_transient( $option_name, $affiliate_ids, ( MINUTE_IN_SECONDS * 20 ) );
2497 wp_using_ext_object_cache( $use_cache );
2498 dfrapi_update_transient_whitelist( $option_name );
2499
2500 return $affiliate_ids;
2501 }
2502
2503 /**
2504 * @param $merchant_id
2505 *
2506 * @return mixed|string
2507 * @throws Exception
2508 */
2509 function dfrapi_get_affiliate_id_for_effiliation_merchant( $merchant_id ) {
2510 $merchants = dfrapi_api_get_merchants_by_id( $merchant_id );
2511 $merchant = $merchants[0] ?? [ 'suids' => '' ];
2512 $affiliate_ids = dfrapi_request_effiliation_affiliate_ids();
2513
2514 if ( is_wp_error( $affiliate_ids ) ) {
2515 throw new Exception( 'Unable to query Effiliation at this time. Please try again in 15 minutes.' );
2516 }
2517
2518 if ( ! isset( $affiliate_ids[ $merchant['suids'] ]['affiliate_id'] ) ) {
2519 throw new Exception( 'Suid does not exist for affiliate ID.' );
2520 }
2521
2522 return $affiliate_ids[ $merchant['suids'] ]['affiliate_id'];
2523 }
2524
2525 /**
2526 * An array of data about the user's Datafeedr account. Formatted like:
2527 *
2528 * Array (
2529 * [network_count] => 227
2530 * [plan_id] => 30600000
2531 * [user_id] => 70123
2532 * [max_total] => 10000
2533 * [merchant_count] => 84031
2534 * [max_requests] => 100000
2535 * [bill_day] => 25
2536 * [request_count] => 11061
2537 * [product_count] => 797373259
2538 * [max_length] => 100
2539 * )
2540 *
2541 * @return array
2542 */
2543 function dfrapi_get_user_account_data(): array {
2544 return (array) get_option( 'dfrapi_account', [] );
2545 }
2546
2547 /**
2548 * Returns the total number of networks in the Datafeedr API.
2549 *
2550 * @return int
2551 */
2552 function dfrapi_get_network_count(): int {
2553 $data = dfrapi_get_user_account_data();
2554
2555 return absint( $data['network_count'] ?? 0 );
2556 }
2557
2558 /**
2559 * Returns the total number of merchants in the Datafeedr API.
2560 *
2561 * @return int
2562 */
2563 function dfrapi_get_merchant_count(): int {
2564 $data = dfrapi_get_user_account_data();
2565
2566 return absint( $data['merchant_count'] ?? 0 );
2567 }
2568
2569 /**
2570 * Returns the total number of products in the Datafeedr API.
2571 *
2572 * @return int
2573 */
2574 function dfrapi_get_product_count(): int {
2575 $data = dfrapi_get_user_account_data();
2576
2577 return absint( $data['product_count'] ?? 0 );
2578 }
2579
2580 /**
2581 * The maximum number of API requests the user is allowed to make during a single subscription period (i.e. 30 days).
2582 *
2583 * @return int
2584 */
2585 function dfrapi_get_max_requests(): int {
2586 $data = dfrapi_get_user_account_data();
2587
2588 return absint( $data['max_requests'] ?? 0 );
2589 }
2590
2591 /**
2592 * The current number of API requests the user has made during the current subscription period (i.e. 30 days).
2593 *
2594 * @return int
2595 */
2596 function dfrapi_get_request_count(): int {
2597 $data = dfrapi_get_user_account_data();
2598
2599 return absint( $data['request_count'] ?? 0 );
2600 }
2601
2602 /**
2603 * Returns the user's API requests usage as a percentage of their total requests allowed.
2604 *
2605 * @param int $precision Default: 2
2606 *
2607 * @return float|int
2608 */
2609 function dfrapi_get_api_usage_as_percentage( int $precision = 2 ) {
2610 $max_requests = dfrapi_get_max_requests();
2611 $request_count = dfrapi_get_request_count();
2612
2613 return $max_requests > 0 ? round( ( $request_count / $max_requests * 100 ), $precision ) : 0;
2614 }
2615
2616 /**
2617 * Returns an array of network IDs for the Partnerize affiliate network.
2618 *
2619 * @return int[]
2620 */
2621 function dfrapi_get_partnerize_network_ids(): array {
2622 return [ 801, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823 ];
2623 }
2624
2625 /**
2626 * Returns the Group ID for Partnerize.
2627 *
2628 * @return int
2629 */
2630 function dfrapi_get_partnerize_group_id(): int {
2631 return 10027;
2632 }
2633
2634 /**
2635 * Returns an array of network IDs for the Effiliation affiliate network.
2636 *
2637 * @return int[]
2638 */
2639 function dfrapi_get_effiliation_network_ids(): array {
2640 return [ 805, 806, 807 ];
2641 }
2642
2643 /**
2644 * Returns the Group ID for Effiliation.
2645 *
2646 * @return int
2647 */
2648 function dfrapi_get_effiliation_group_id(): int {
2649 return 10017;
2650 }
2651
2652 /**
2653 * Returns the Group ID for Belboon.
2654 *
2655 * @return int
2656 */
2657 function dfrapi_get_belboon_group_id(): int {
2658 return 10007;
2659 }
2660
2661 /**
2662 * Get the affiliate ID for a specific network.
2663 *
2664 * @param int $network_id
2665 * @param mixed $default
2666 *
2667 * @return mixed|string Returns the affiliate ID if found otherwise it returns the value of $default.
2668 */
2669 function dfrapi_get_affiliate_id_by_network_id( int $network_id, $default = false ) {
2670
2671 static $network_ids = null;
2672
2673 if ( $network_ids === null ) {
2674
2675 $network_ids = [];
2676
2677 $networks = dfrapi_get_selected_networks();
2678
2679 if ( isset( $networks['ids'] ) && is_array( $networks['ids'] ) && ! empty( $networks['ids'] ) ) {
2680 $network_ids = $networks['ids'];
2681 }
2682 }
2683
2684 foreach ( $network_ids as $k => $v ) {
2685 $nid = absint( $v['nid'] ?? 0 );
2686 if ( $nid === $network_id ) {
2687 $aid = trim( $v['aid'] ?? '' );
2688
2689 return ! empty( $aid ) ? $aid : $default;
2690 }
2691 }
2692
2693 return $default;
2694 }
2695
2696 /**
2697 * Get one or more fields from a Datafeedr Product array.
2698 *
2699 * @since 1.3.1
2700 *
2701 * @param array $product A Datafeedr Product array (as returned from Datafeedr API).
2702 * @param string|array $fields A single field or an array of fields to return. Examples:
2703 * - 'barcode'
2704 * - ['barcode']
2705 * - ['barcode', 'ean']
2706 * @param mixed $default Value to return if no fields are found in $product array. Default: null
2707 * @param false|string $concatenate False to return the first field found or a separator to concatenate all found fields.
2708 *
2709 * @return mixed
2710 */
2711 function dfrapi_get_fields_from_product( array $product, $fields, $default = null, $concatenate = false ) {
2712
2713 if ( ! is_string( $fields ) && ! is_array( $fields ) ) {
2714 return $default;
2715 }
2716
2717 if ( is_string( $fields ) ) {
2718 $fields = [ (string) $fields ];
2719 }
2720
2721 $fields = array_filter( $fields );
2722
2723 if ( empty( $fields ) ) {
2724 return $default;
2725 }
2726
2727 if ( count( $fields ) === 1 ) {
2728 return $product[ $fields[0] ] ?? $default;
2729 }
2730
2731 $values = [];
2732
2733 foreach ( $fields as $field ) {
2734 if ( isset( $product[ $field ] ) ) {
2735 $values[] = $product[ $field ];
2736 }
2737 }
2738
2739 if ( empty( $values ) ) {
2740 return $default;
2741 }
2742
2743 return is_string( $concatenate ) ? implode( $concatenate, $values ) : $values[0];
2744 }
2745
2746 /**
2747 * Returns a URL to install a plugin from the WordPress.org repo.
2748 *
2749 * @since 1.3.1
2750 *
2751 * @param string $plugin Path to the plugin file relative to the plugin's directory. Ex: datafeedr-api/datafeedr-api.php
2752 *
2753 * @return string
2754 */
2755 function dfrapi_get_install_plugin_url( string $plugin ): string {
2756
2757 if ( ! current_user_can( 'install_plugins' ) ) {
2758 return admin_url( 'plugins.php' );
2759 }
2760
2761 return add_query_arg(
2762 [ 'action' => 'install-plugin', 'plugin' => dfrapi_parse_plugin_path( $plugin, 'dirname' ) ],
2763 wp_nonce_url( admin_url( 'update.php' ), 'install-plugin_' . dfrapi_parse_plugin_path( $plugin, 'dirname' ) )
2764 );
2765 }
2766
2767 /**
2768 * Returns a URL to activate a plugin.
2769 *
2770 * @since 1.3.1
2771 *
2772 * @param string $plugin Path to the plugin file relative to the plugin's directory. Ex: datafeedr-api/datafeedr-api.php
2773 *
2774 * @return string
2775 */
2776 function dfrapi_get_activate_plugin_url( string $plugin ): string {
2777
2778 if ( ! current_user_can( 'activate_plugins' ) ) {
2779 return admin_url( 'plugins.php' );
2780 }
2781
2782 return add_query_arg(
2783 [ 'action' => 'activate', 'plugin' => dfrapi_parse_plugin_path( $plugin ), 'paged' => '1', 's' => '' ],
2784 wp_nonce_url( network_admin_url( 'plugins.php' ), 'activate-plugin_' . dfrapi_parse_plugin_path( $plugin ) )
2785 );
2786 }
2787
2788 /**
2789 * Returns true if plugin is installed. Otherwise, returns false.
2790 *
2791 * @since 1.3.1
2792 *
2793 * @param string $plugin Path to the plugin file relative to the plugin's directory. Ex: datafeedr-api/datafeedr-api.php
2794 *
2795 * @return bool
2796 */
2797 function dfrapi_plugin_is_installed( string $plugin ): bool {
2798 return file_exists( dfrapi_parse_plugin_path( $plugin, 'absolute' ) );
2799 }
2800
2801 /**
2802 * This function parses and sanitizes a plugin path and returns it in the desired format.
2803 *
2804 * @since 1.3.1
2805 *
2806 * @param string $plugin Path to the plugin file relative to the plugin's directory. Ex: datafeedr-api/datafeedr-api.php
2807 * @param string $format The format in which to return the plugin info.
2808 *
2809 * @return string
2810 */
2811 function dfrapi_parse_plugin_path( string $plugin, string $format = 'relative' ): string {
2812
2813 // For examples below, if $plugin equals = "hello-dolly/hello.php"...
2814 $valid_formats = [
2815 'absolute', // /home/public_html/user/wp-content/plugins/hello-dolly/hello.php
2816 'relative', // hello-dolly/hello.php
2817 'dirname', // hello-dolly
2818 'basename', // hello.php
2819 'filename', // hello
2820 'extension', // php
2821 ];
2822
2823 $path = pathinfo( $plugin );
2824
2825 $dirname = sanitize_file_name( $path['dirname'] ?? '' );
2826 $basename = sanitize_file_name( $path['basename'] ?? '' );
2827 $extension = sanitize_file_name( $path['extension'] ?? '' );
2828 $filename = sanitize_file_name( $path['filename'] ?? '' );
2829
2830 $format = in_array( $format, $valid_formats, true ) ? $format : 'relative';
2831
2832 if ( $format === 'absolute' ) {
2833 return trailingslashit( WP_PLUGIN_DIR ) . trailingslashit( $dirname ) . $basename;
2834 }
2835
2836 if ( $format === 'dirname' ) {
2837 return $dirname;
2838 }
2839
2840 if ( $format === 'basename' ) {
2841 return $basename;
2842 }
2843
2844 if ( $format === 'filename' ) {
2845 return $filename;
2846 }
2847
2848 if ( $format === 'extension' ) {
2849 return $extension;
2850 }
2851
2852 return empty( $dirname ) ? $basename : trailingslashit( $dirname ) . $basename;
2853 }
2854
2855 /**
2856 * Returns an array of Network IDs which should be considered inactive.
2857 *
2858 * @since 1.3.8
2859 *
2860 * @return array
2861 */
2862 function dfrapi_inactive_networks(): array {
2863
2864 $inactive_network_ids = [
2865 14, // Prophetably
2866 ];
2867
2868 return array_map( 'absint', apply_filters( 'dfrapi_inactive_networks', $inactive_network_ids ) );
2869 }
2870
2871 /**
2872 * Returns the Amazon API to use.
2873 *
2874 * @since 1.4.0
2875 *
2876 * @return string The Amazon API to use.
2877 */
2878 function dfrapi_get_amazon_api(): string {
2879 $configuration = (array) get_option( 'dfrapi_configuration' );
2880
2881 return $configuration['amazon_api'] ?? '';
2882 }
2883
2884 /**
2885 * Returns an array of Amazon Creator API regions.
2886 *
2887 * @since 1.4.0
2888 *
2889 * @return array The Amazon Creator API regions.
2890 */
2891 function dfrapi_get_capi_regions(): array {
2892
2893 $regions = [];
2894
2895 $regions['NA'] = [
2896 'code' => 'NA',
2897 'name' => 'North America',
2898 'version' => '2.1',
2899 'token_endpoint' => 'creatorsapi.auth.us-east-1.amazoncognito.com/oauth2/token',
2900 ];
2901
2902 $regions['EU'] = [
2903 'code' => 'EU',
2904 'name' => 'Europe',
2905 'version' => '2.2',
2906 'token_endpoint' => 'creatorsapi.auth.eu-south-2.amazoncognito.com/oauth2/token',
2907 ];
2908
2909 $regions['FE'] = [
2910 'code' => 'FE',
2911 'name' => 'Far East',
2912 'version' => '2.3',
2913 'token_endpoint' => 'creatorsapi.auth.us-west-2.amazoncognito.com/oauth2/token',
2914 ];
2915
2916 return $regions;
2917 }
2918
2919 /**
2920 * Returns an Amazon Creator API region by its code.
2921 *
2922 * @since 1.4.0
2923 *
2924 * @param string $code The region code (Ex. NA, EU, FE).
2925 *
2926 * @return array|WP_Error The region data or WP_Error if the code is invalid.
2927 */
2928 function dfrapi_get_capi_region( string $code ) {
2929 $code = strtoupper( trim( $code ) );
2930 $regions = dfrapi_get_capi_regions();
2931
2932 return $regions[ $code ] ?? new WP_Error( 'invalid_capi_region_code', 'Invalid Creator API region code.', [ 'code' => $code ] );
2933 }
2934
2935 /**
2936 * Returns an array of Amazon Creator API marketplaces.
2937 *
2938 * @since 1.4.0
2939 *
2940 * @return array The Amazon Creator API marketplaces.
2941 */
2942 function dfrapi_get_capi_marketplaces(): array {
2943
2944 $regions = dfrapi_get_capi_regions();
2945
2946 $marketplaces = [];
2947 $marketplaces['AU'] = [
2948 'locale' => 'Australia',
2949 'domain' => 'www.amazon.com.au',
2950 'region' => $regions['FE'],
2951 ];
2952
2953 $marketplaces['BE'] = [
2954 'locale' => 'Belgium',
2955 'domain' => 'www.amazon.com.be',
2956 'region' => $regions['EU'],
2957 ];
2958
2959 $marketplaces['BR'] = [
2960 'locale' => 'Brazil',
2961 'domain' => 'www.amazon.com.br',
2962 'region' => $regions['NA'],
2963 ];
2964
2965 $marketplaces['CA'] = [
2966 'locale' => 'Canada',
2967 'domain' => 'www.amazon.ca',
2968 'region' => $regions['NA'],
2969 ];
2970
2971 $marketplaces['EG'] = [
2972 'locale' => 'Egypt',
2973 'domain' => 'www.amazon.eg',
2974 'region' => $regions['EU'],
2975 ];
2976
2977 $marketplaces['FR'] = [
2978 'locale' => 'France',
2979 'domain' => 'www.amazon.fr',
2980 'region' => $regions['EU'],
2981 ];
2982
2983 $marketplaces['DE'] = [
2984 'locale' => 'Germany',
2985 'domain' => 'www.amazon.de',
2986 'region' => $regions['EU'],
2987 ];
2988
2989 $marketplaces['IN'] = [
2990 'locale' => 'India',
2991 'domain' => 'www.amazon.in',
2992 'region' => $regions['EU'],
2993 ];
2994
2995 $marketplaces['IE'] = [
2996 'locale' => 'Ireland',
2997 'domain' => 'www.amazon.ie',
2998 'region' => $regions['EU'],
2999 ];
3000
3001 $marketplaces['IT'] = [
3002 'locale' => 'Italy',
3003 'domain' => 'www.amazon.it',
3004 'region' => $regions['EU'],
3005 ];
3006
3007 $marketplaces['JP'] = [
3008 'locale' => 'Japan',
3009 'domain' => 'www.amazon.co.jp',
3010 'region' => $regions['FE'],
3011 ];
3012
3013 $marketplaces['MX'] = [
3014 'locale' => 'Mexico',
3015 'domain' => 'www.amazon.com.mx',
3016 'region' => $regions['NA'],
3017 ];
3018
3019 $marketplaces['NL'] = [
3020 'locale' => 'Netherlands',
3021 'domain' => 'www.amazon.nl',
3022 'region' => $regions['EU'],
3023 ];
3024
3025 $marketplaces['PL'] = [
3026 'locale' => 'Poland',
3027 'domain' => 'www.amazon.pl',
3028 'region' => $regions['EU'],
3029 ];
3030
3031 $marketplaces['SG'] = [
3032 'locale' => 'Singapore',
3033 'domain' => 'www.amazon.sg',
3034 'region' => $regions['FE'],
3035 ];
3036
3037 $marketplaces['SA'] = [
3038 'locale' => 'Saudi Arabia',
3039 'domain' => 'www.amazon.sa',
3040 'region' => $regions['EU'],
3041 ];
3042
3043 $marketplaces['ES'] = [
3044 'locale' => 'Spain',
3045 'domain' => 'www.amazon.es',
3046 'region' => $regions['EU'],
3047 ];
3048
3049 $marketplaces['SE'] = [
3050 'locale' => 'Sweden',
3051 'domain' => 'www.amazon.se',
3052 'region' => $regions['EU'],
3053 ];
3054
3055 $marketplaces['TR'] = [
3056 'locale' => 'Turkey',
3057 'domain' => 'www.amazon.com.tr',
3058 'region' => $regions['EU'],
3059 ];
3060
3061 $marketplaces['AE'] = [
3062 'locale' => 'United Arab Emirates',
3063 'domain' => 'www.amazon.ae',
3064 'region' => $regions['EU'],
3065 ];
3066
3067 $marketplaces['UK'] = [
3068 'locale' => 'United Kingdom',
3069 'domain' => 'www.amazon.co.uk',
3070 'region' => $regions['EU'],
3071 ];
3072
3073 $marketplaces['US'] = [
3074 'locale' => 'United States',
3075 'domain' => 'www.amazon.com',
3076 'region' => $regions['NA'],
3077 ];
3078
3079 return $marketplaces;
3080 }
3081
3082 /**
3083 * Returns an Amazon Creator API marketplace by its code.
3084 *
3085 * @since 1.4.0
3086 *
3087 * @param string $code The marketplace code (Ex. US, CA, UK, etc.).
3088 *
3089 * @return array|WP_Error The marketplace data or WP_Error if the code is invalid.
3090 */
3091 function dfrapi_get_capi_marketplace( string $code ) {
3092 $code = strtoupper( trim( $code ) );
3093 $marketplaces = dfrapi_get_capi_marketplaces();
3094
3095 return $marketplaces[ $code ] ?? new WP_Error( 'invalid_capi_marketplace_code', 'Invalid Creator API marketplace code.', [ 'code' => $code ] );
3096 }
3097
3098 /**
3099 * Returns Creator API credentials.
3100 *
3101 * @return array {
3102 * Creator API credentials.
3103 *
3104 * @type string $id Creator API credential ID.
3105 * @type string $secret Creator API credential secret.
3106 * @type string $marketplace Marketplace code (Ex. US, CA, UK, DE, etc...)
3107 * @type string $partner_tag Partner tag (Ex. xyz-20).
3108 * @type string $version Version (2.1, 2.2 or 2.3).
3109 * @type string $endpoint Access token generation endpoint URL.
3110 * }
3111 */
3112 function dfrapi_get_capi_credentials(): array {
3113
3114 $credentials = [];
3115 $configuration = (array) get_option( 'dfrapi_configuration' );
3116
3117 $credentials['id'] = trim( $configuration['capi_credential_id'] ?? '' );
3118 $credentials['secret'] = trim( $configuration['capi_credential_secret'] ?? '' );
3119 $credentials['partner_tag'] = trim( $configuration['capi_partner_tag'] ?? '' );
3120 $credentials['marketplace'] = trim( $configuration['capi_marketplace'] ?? 'US' );
3121
3122 $marketplace = dfrapi_get_capi_marketplace( $credentials['marketplace'] );
3123
3124 if ( ! is_wp_error( $marketplace ) ) {
3125 $credentials['version'] = $marketplace['region']['version'];
3126 $credentials['endpoint'] = $marketplace['region']['token_endpoint'];
3127 }
3128
3129 return $credentials;
3130 }
3131
3132 /**
3133 * Checks if Amazon Creator API credentials exist.
3134 *
3135 * @since 1.4.0
3136 *
3137 * @return bool True if credentials exist, false otherwise.
3138 */
3139 function dfrapi_capi_credentials_exist(): bool {
3140
3141 $credentials = dfrapi_get_capi_credentials();
3142
3143 $keys = [ 'id', 'secret', 'partner_tag', 'version', 'endpoint', 'marketplace' ];
3144
3145 foreach ( $keys as $key ) {
3146 if ( empty( $credentials[ $key ] ) ) {
3147 return false;
3148 }
3149 }
3150
3151 return true;
3152 }
3153
3154 /**
3155 * Returns an Amazon Creator API access token.
3156 *
3157 * @since 1.4.0
3158 *
3159 * @return string|WP_Error The access token or WP_Error on failure.
3160 */
3161 function dfrapi_get_capi_access_token() {
3162
3163 $transient_key = 'dfrapi_capi_access_token';
3164
3165 $capi_access_token = get_transient( $transient_key );
3166
3167 if ( is_string( $capi_access_token ) && $capi_access_token !== '' ) {
3168 return $capi_access_token;
3169 }
3170
3171 if ( ! dfrapi_capi_credentials_exist() ) {
3172 return new WP_Error( 'missing_capi_credentials', 'Missing Amazon Creators API credentials' );
3173 }
3174
3175 $credentials = dfrapi_get_capi_credentials();
3176
3177 $response = wp_remote_request(
3178 'https://' . $credentials['endpoint'],
3179 [
3180 'method' => 'POST',
3181 'headers' => [
3182 'Content-Type' => 'application/x-www-form-urlencoded',
3183 ],
3184 'body' => [
3185 'grant_type' => 'client_credentials',
3186 'client_id' => $credentials['id'],
3187 'client_secret' => $credentials['secret'],
3188 'scope' => 'creatorsapi/default',
3189 ],
3190 'timeout' => 15,
3191 ]
3192 );
3193
3194 if ( is_wp_error( $response ) ) {
3195 error_log( $response->get_error_message() );
3196
3197 return $response;
3198 }
3199
3200 $status = wp_remote_retrieve_response_code( $response );
3201 $body = wp_remote_retrieve_body( $response );
3202
3203 /**
3204 * [
3205 * "access_token" => "eyJra.....YXE3A",
3206 * "expires_in" => 3600,
3207 * "token_type" => "Bearer",
3208 * ]
3209 */
3210 $data = json_decode( $body, true );
3211
3212 if ( 200 !== $status || empty( $data['access_token'] ) ) {
3213 $error_message = isset( $data['error'] ) ? $data['error'] : 'Failed to retrieve CAPI access token (HTTP ' . $status . ')';
3214 error_log( '[Datafeedr CAPI] Token error: ' . $error_message );
3215
3216 return new WP_Error( 'capi_token_error', $error_message );
3217 }
3218
3219 $capi_access_token = $data['access_token'];
3220 $capi_expires_in = (int) $data['expires_in'];
3221
3222 set_transient(
3223 $transient_key,
3224 $capi_access_token,
3225 max( 60, $capi_expires_in - 60 ) // refresh 1 minute early
3226 );
3227
3228 return $capi_access_token;
3229 }
3230
3231 /**
3232 * Returns a value from a multi-dimensional array using dot notation.
3233 *
3234 * @since 1.4.0
3235 *
3236 * @param array $array The array to search.
3237 * @param string $path The path to the value using dot notation.
3238 * @param mixed $default The default value to return if the path is not found.
3239 *
3240 * @return mixed The value from the array or the default value.
3241 */
3242 function dfrapi_array_get_dot( array $array, string $path, $default = null ) {
3243
3244 foreach ( explode( '.', $path ) as $key ) {
3245
3246 if ( ! is_array( $array ) || ! array_key_exists( $key, $array ) ) {
3247 return $default;
3248 }
3249
3250 $array = $array[ $key ];
3251 }
3252
3253 return $array;
3254 }
3255
3256 /**
3257 * Transforms a CAPI item array into a Datafeedr product array.
3258 *
3259 * @since 1.4.0
3260 *
3261 * @param array $item The CAPI item data.
3262 *
3263 * @return array The transformed Datafeedr product array.
3264 */
3265 function dfrapi_transform_capi_item_into_datafeedr_product_array( array $item ): array {
3266
3267 $product = [];
3268 $asin = dfrapi_array_get_dot( $item, 'asin', '' );
3269
3270 // Hard-coded values.
3271 $product['id'] = 7777 . $asin;
3272 $product['v5_id'] = 7777 . $asin;
3273 $product['network_id'] = 7777;
3274 $product['source_id'] = 7777;
3275 $product['merchant_id'] = 7777;
3276 $product['asin'] = $asin;
3277 $product['v5_suid'] = $asin;
3278 $product['network'] = 'Amazon';
3279 $product['source'] = 'Amazon';
3280 $product['merchant'] = 'Amazon';
3281 $product['time_added'] = date_i18n( 'Y-m-d H:i:s' );
3282 $product['time_updated'] = date_i18n( 'Y-m-d H:i:s' );
3283 $product['iscommissionable'] = 1;
3284
3285 // Specific item values.
3286 $product['name'] = dfrapi_array_get_dot( $item, 'itemInfo.title.displayValue' );
3287 $product['description'] = dfrapi_array_get_dot( $item, 'itemInfo.title.displayValue' );
3288
3289 $features = dfrapi_array_get_dot( $item, 'itemInfo.features.displayValues' );
3290 if ( is_array( $features ) && ! empty( $features ) ) {
3291 $product['description'] = implode( ' ', $features );
3292 }
3293
3294 $product['brand'] = dfrapi_array_get_dot( $item, 'itemInfo.byLineInfo.brand.displayValue' );
3295 $product['color'] = dfrapi_array_get_dot( $item, 'itemInfo.productInfo.color.displayValue' );
3296 $product['manufacturer'] = dfrapi_array_get_dot( $item, 'itemInfo.byLineInfo.manufacturer.displayValue' );
3297 $product['url'] = dfrapi_array_get_dot( $item, 'detailPageURL' );
3298 $product['ref_url'] = dfrapi_array_get_dot( $item, 'detailPageURL' );
3299 $product['image'] = dfrapi_array_get_dot( $item, 'images.primary.large.url' );
3300 $product['thumbnail'] = dfrapi_array_get_dot( $item, 'images.primary.medium.url' );
3301
3302 $upc = dfrapi_array_get_dot( $item, 'itemInfo.externalIds.upcs.displayValues.0' );
3303 $ean = dfrapi_array_get_dot( $item, 'itemInfo.externalIds.eans.displayValues.0' );
3304 $isbn = dfrapi_array_get_dot( $item, 'itemInfo.externalIds.isbns.displayValues.0' );
3305 $gtin = dfrapi_array_get_dot( $item, 'itemInfo.externalIds.gtins.displayValues.0' );
3306
3307 if ( $upc ) {
3308 $product['upc'] = $upc;
3309 }
3310 if ( $ean ) {
3311 $product['ean'] = $ean;
3312 }
3313 if ( $isbn ) {
3314 $product['isbn'] = $isbn;
3315 }
3316 if ( $gtin ) {
3317 $product['gtin'] = $gtin;
3318 }
3319
3320 if ( ! empty( $product['upc'] ) ) {
3321 $product['barcode'] = $product['upc'];
3322 } elseif ( ! empty( $product['ean'] ) ) {
3323 $product['barcode'] = $product['ean'];
3324 } elseif ( ! empty( $product['isbn'] ) ) {
3325 $product['barcode'] = $product['isbn'];
3326 } elseif ( ! empty( $product['gtin'] ) ) {
3327 $product['barcode'] = $product['gtin'];
3328 }
3329
3330 $listings = dfrapi_array_get_dot( $item, 'offersV2.listings', [] );
3331
3332 $info = [];
3333
3334 foreach ( $listings as $listing ) {
3335
3336 // Valid Condition Values: New, Used, Refurbished, Unknown
3337 $condition = strtolower( dfrapi_array_get_dot( $listing, 'condition.value' ) );
3338
3339 // If the list price is missing, use the current price as the base price
3340 $price_amount = dfrapi_array_get_dot( $listing, 'price.money.amount', 0 );
3341 $list_price_amount = dfrapi_array_get_dot( $listing, 'price.savingBasis.money.amount' );
3342 $regular_price = $list_price_amount !== null ? $list_price_amount : $price_amount;
3343
3344 // Add pricing info for each $condition.
3345 $info[ $condition ]['currency'] = dfrapi_array_get_dot( $listing, 'price.money.currency', 'USD' );
3346 $info[ $condition ]['price'] = dfrapi_price_to_int( $regular_price );
3347
3348 $saleprice = dfrapi_price_to_int( $price_amount );
3349 if ( $saleprice < $info[ $condition ]['price'] ) {
3350 $info[ $condition ]['saleprice'] = $saleprice;
3351 }
3352
3353 $info[ $condition ]['finalprice'] = dfrapi_price_to_int( $price_amount );
3354 $info[ $condition ]['salediscount'] = dfrapi_array_get_dot( $listing, 'price.savings.percentage', 0 );
3355
3356 // Set the usedprice if applicable.
3357 if ( in_array( $condition, [ 'used', 'refurbished' ], true ) ) {
3358 $info[ $condition ]['usedprice'] = dfrapi_price_to_int( $price_amount );
3359 }
3360
3361 // Set `onsale`
3362 $info[ $condition ]['onsale'] = ( $info[ $condition ]['finalprice'] < $info[ $condition ]['price'] ) ? 1 : 0;
3363
3364 // Set availability
3365 $availability = dfrapi_array_get_dot( $listing, 'availability.type' );
3366 if ( in_array( $availability, [ 'IN_STOCK', 'IN_STOCK_SCARCE' ], true ) ) {
3367 $info[ $condition ]['instock'] = 1;
3368 } elseif ( in_array( $availability, [ 'UNKNOWN' ], true ) ) {
3369 $info[ $condition ]['instock'] = 9;
3370 } else {
3371 $info[ $condition ]['instock'] = 0;
3372 }
3373 }
3374
3375 // Set the `usedprice` key if a used or refurbished listing exists.
3376 if ( isset( $info['used']['finalprice'] ) ) {
3377 $product['usedprice'] = $info['used']['finalprice'];
3378 } elseif ( isset( $info['refurbished']['finalprice'] ) ) {
3379 $product['usedprice'] = $info['refurbished']['finalprice'];
3380 }
3381
3382 // Set defaults from priority condition
3383 foreach ( [ 'new', 'unknown', 'used', 'refurbished' ] as $cond ) {
3384 if ( isset( $info[ $cond ] ) ) {
3385 $product = array_merge( $product, $info[ $cond ] );
3386 break;
3387 }
3388 }
3389
3390 /**
3391 * Filters the transformed Datafeedr product array.
3392 *
3393 * @since 1.0.0
3394 *
3395 * @param array $product The transformed Datafeedr product array.
3396 * @param array $item The original CAPI item data.
3397 */
3398 return apply_filters( 'dfrapi_transform_capi_item_into_datafeedr_product_array', $product, $item );
3399 }
3400