2 Star 0 Fork 0

mirrors_facebook/facebook-for-woocommerce

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
facebook-commerce.php 117.52 KB
一键复制 编辑 原始数据 按行查看 历史
Chase Wiseman 提交于 2020-03-03 10:26 . Release v1.10.0 (#1208)
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204
<?php
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*
* @package FacebookCommerce
*/
use SkyVerge\WooCommerce\PluginFramework\v5_5_4 as Framework;
use SkyVerge\WooCommerce\Facebook\Products;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
require_once 'facebook-config-warmer.php';
require_once 'includes/fbproduct.php';
require_once 'facebook-commerce-pixel-event.php';
class WC_Facebookcommerce_Integration extends WC_Integration {
/** @var string the WordPress option name where the page access token is stored */
const OPTION_PAGE_ACCESS_TOKEN = 'wc_facebook_page_access_token';
/** @var string the WordPress option name where the product catalog ID is stored */
const OPTION_PRODUCT_CATALOG_ID = 'wc_facebook_product_catalog_id';
/** @var string the WordPress option name where the external merchant settings ID is stored */
const OPTION_EXTERNAL_MERCHANT_SETTINGS_ID = 'wc_facebook_external_merchant_settings_id';
/** @var string the WordPress option name where the feed ID is stored */
const OPTION_FEED_ID = 'wc_facebook_feed_id';
/** @var string the WordPress option name where the JS SDK version is stored */
const OPTION_JS_SDK_VERSION = 'wc_facebook_js_sdk_version';
/** @var string the WordPress option name where the latest pixel install time is stored */
const OPTION_PIXEL_INSTALL_TIME = 'wc_facebook_pixel_install_time';
/** @var string the facebook page ID setting ID */
const SETTING_FACEBOOK_PAGE_ID = 'facebook_page_id';
/** @var string the facebook pixel ID setting ID */
const SETTING_FACEBOOK_PIXEL_ID = 'facebook_pixel_id';
/** @var string the "enable advanced matching" setting ID */
const SETTING_ENABLE_ADVANCED_MATCHING = 'enable_advanced_matching';
/** @var string the "enable product sync" setting ID */
const SETTING_ENABLE_PRODUCT_SYNC = 'enable_product_sync';
/** @var string the excluded product category IDs setting ID */
const SETTING_EXCLUDED_PRODUCT_CATEGORY_IDS = 'excluded_product_category_ids';
/** @var string the excluded product tag IDs setting ID */
const SETTING_EXCLUDED_PRODUCT_TAG_IDS = 'excluded_product_tag_ids';
/** @var string the product description mode setting ID */
const SETTING_PRODUCT_DESCRIPTION_MODE = 'product_description_mode';
/** @var string the scheduled resync offset setting ID */
const SETTING_SCHEDULED_RESYNC_OFFSET = 'scheduled_resync_offset';
/** @var string the "enable messenger" setting ID */
const SETTING_ENABLE_MESSENGER = 'enable_messenger';
/** @var string the messenger locale setting ID */
const SETTING_MESSENGER_LOCALE = 'messenger_locale';
/** @var string the messenger greeting setting ID */
const SETTING_MESSENGER_GREETING = 'messenger_greeting';
/** @var string the messenger color HEX setting ID */
const SETTING_MESSENGER_COLOR_HEX = 'messenger_color_hex';
/** @var string the standard product description mode name */
const PRODUCT_DESCRIPTION_MODE_STANDARD = 'standard';
/** @var string the short product description mode name */
const PRODUCT_DESCRIPTION_MODE_SHORT = 'short';
/** @var string the hook for the recurreing action that syncs products */
const ACTION_HOOK_SCHEDULED_RESYNC = 'sync_all_fb_products_using_feed';
/** @var string|null the configured page access token */
private $page_access_token;
/** @var string|null the configured product catalog ID */
public $product_catalog_id;
/** @var string|null the configured external merchant settings ID */
public $external_merchant_settings_id;
/** @var string|null the configured feed ID */
public $feed_id;
/** @var string|null the configured pixel install time */
public $pixel_install_time;
/** @var string|null the configured JS SDK version */
private $js_sdk_version;
/** Legacy properties *********************************************************************************************/
// TODO probably some of these meta keys need to be moved to Facebook\Products {FN 2020-01-13}
const FB_PRODUCT_GROUP_ID = 'fb_product_group_id';
const FB_PRODUCT_ITEM_ID = 'fb_product_item_id';
const FB_PRODUCT_DESCRIPTION = 'fb_product_description';
/** @var string the API flag to set a product as visible in the Facebook shop */
const FB_SHOP_PRODUCT_VISIBLE = 'published';
/** @var string the API flag to set a product as not visible in the Facebook shop */
const FB_SHOP_PRODUCT_HIDDEN = 'staging';
const FB_CART_URL = 'fb_cart_url';
const FB_MESSAGE_DISPLAY_TIME = 180;
// Number of days to query tip.
const FB_TIP_QUERY = 1;
// TODO: this constant is no longer used and can probably be removed {WV 2020-01-21}
const FB_VARIANT_IMAGE = 'fb_image';
const FB_ADMIN_MESSAGE_PREPEND = '<b>Facebook for WooCommerce</b><br/>';
const FB_SYNC_IN_PROGRESS = 'fb_sync_in_progress';
const FB_SYNC_REMAINING = 'fb_sync_remaining';
const FB_SYNC_TIMEOUT = 30;
const FB_PRIORITY_MID = 9;
private $test_mode = false;
public function init_pixel() {
WC_Facebookcommerce_Pixel::initialize();
// Migrate WC customer pixel_id from WC settings to WP options.
// This is part of a larger effort to consolidate all the FB-specific
// settings for all plugin integrations.
if ( is_admin() ) {
$pixel_id = WC_Facebookcommerce_Pixel::get_pixel_id();
$settings_pixel_id = $this->get_facebook_pixel_id();
if (
WC_Facebookcommerce_Utils::is_valid_id( $settings_pixel_id ) &&
( ! WC_Facebookcommerce_Utils::is_valid_id( $pixel_id ) ||
$pixel_id != $settings_pixel_id
)
) {
WC_Facebookcommerce_Pixel::set_pixel_id( $settings_pixel_id );
}
// migrate Advanced Matching enabled (use_pii) from the integration setting to the pixel option,
// so that it works the same way the pixel ID does
$settings_advanced_matching_enabled = $this->is_advanced_matching_enabled();
WC_Facebookcommerce_Pixel::set_use_pii_key( $settings_advanced_matching_enabled );
}
}
/**
* Init and hook in the integration.
*
* @access public
* @return void
*/
public function __construct() {
if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) {
include_once 'facebook-commerce-events-tracker.php';
}
$this->id = WC_Facebookcommerce::INTEGRATION_ID;
$this->method_title = __(
'Facebook for WooCommerce',
'facebook-for-commerce'
);
$this->method_description = __(
'Facebook Commerce and Dynamic Ads (Pixel) Extension',
'facebook-for-commerce'
);
// Load the settings.
$this->init_settings();
$pixel_id = WC_Facebookcommerce_Pixel::get_pixel_id();
// if there is a pixel option saved and no integration setting saved, inherit the pixel option
if ( $pixel_id && ! $this->get_facebook_pixel_id() ) {
$this->settings[ self::SETTING_FACEBOOK_PIXEL_ID ] = $pixel_id;
}
$advanced_matching_enabled = WC_Facebookcommerce_Pixel::get_use_pii_key();
// if Advanced Matching (use_pii) is enabled on the saved pixel option and not on the saved integration setting,
// inherit the pixel option
if ( $advanced_matching_enabled && ! $this->is_advanced_matching_enabled() ) {
$this->settings[ self::SETTING_ENABLE_ADVANCED_MATCHING ] = $advanced_matching_enabled;
}
if ( ! class_exists( 'WC_Facebookcommerce_Utils' ) ) {
include_once 'includes/fbutils.php';
}
WC_Facebookcommerce_Utils::$ems = $this->get_external_merchant_settings_id();
if ( ! class_exists( 'WC_Facebookcommerce_Graph_API' ) ) {
include_once 'includes/fbgraph.php';
$this->fbgraph = new WC_Facebookcommerce_Graph_API( $this->get_page_access_token() );
}
WC_Facebookcommerce_Utils::$fbgraph = $this->fbgraph;
// Hooks
if ( is_admin() ) {
$this->init_pixel();
$this->init_form_fields();
if ( ! class_exists( 'WC_Facebookcommerce_EventsTracker' ) ) {
include_once 'includes/fbutils.php';
}
// Display an info banner for eligible pixel and user.
if ( $this->get_external_merchant_settings_id()
&& $this->get_facebook_pixel_id()
&& $this->get_pixel_install_time() ) {
$should_query_tip =
WC_Facebookcommerce_Utils::check_time_cap(
get_option( 'fb_info_banner_last_query_time', '' ),
self::FB_TIP_QUERY
);
$last_tip_info = WC_Facebookcommerce_Utils::get_cached_best_tip();
if ( $should_query_tip || $last_tip_info ) {
if ( ! class_exists( 'WC_Facebookcommerce_Info_Banner' ) ) {
include_once 'includes/fbinfobanner.php';
}
WC_Facebookcommerce_Info_Banner::get_instance(
$this->get_external_merchant_settings_id(),
$this->fbgraph,
$should_query_tip
);
}
}
if ( ! class_exists( 'WC_Facebook_Integration_Test' ) ) {
include_once 'includes/test/facebook-integration-test.php';
}
$integration_test = WC_Facebook_Integration_Test::get_instance( $this );
$integration_test::$fbgraph = $this->fbgraph;
if ( ! $this->get_pixel_install_time() && $this->get_facebook_pixel_id() ) {
$this->update_pixel_install_time( time() );
}
add_action( 'admin_notices', array( $this, 'checks' ) );
add_action(
'woocommerce_update_options_integration_facebookcommerce',
array( $this, 'process_admin_options' )
);
add_action( 'admin_enqueue_scripts', array( $this, 'load_assets' ) );
add_action(
'wp_ajax_ajax_save_fb_settings',
array( $this, 'ajax_save_fb_settings' ),
self::FB_PRIORITY_MID
);
add_action(
'wp_ajax_ajax_delete_fb_settings',
array( $this, 'ajax_delete_fb_settings' ),
self::FB_PRIORITY_MID
);
add_action(
'wp_ajax_ajax_sync_all_fb_products',
array( $this, 'ajax_sync_all_fb_products' ),
self::FB_PRIORITY_MID
);
add_action(
'wp_ajax_ajax_sync_all_fb_products_using_feed',
array( $this, 'ajax_sync_all_fb_products_using_feed' ),
self::FB_PRIORITY_MID
);
add_action(
'wp_ajax_ajax_check_feed_upload_status',
array( $this, 'ajax_check_feed_upload_status' ),
self::FB_PRIORITY_MID
);
add_action(
'wp_ajax_ajax_reset_all_fb_products',
array( $this, 'ajax_reset_all_fb_products' ),
self::FB_PRIORITY_MID
);
add_action(
'wp_ajax_ajax_display_test_result',
array( $this, 'ajax_display_test_result' )
);
add_action(
'wp_ajax_ajax_schedule_force_resync',
array( $this, 'ajax_schedule_force_resync' ),
self::FB_PRIORITY_MID
);
add_action(
'wp_ajax_ajax_update_fb_option',
array( $this, 'ajax_update_fb_option' ),
self::FB_PRIORITY_MID
);
// Only load product processing hooks if we have completed setup.
if ( $this->get_page_access_token() && $this->get_product_catalog_id() ) {
add_action( 'woocommerce_process_product_meta', [ $this, 'on_product_save' ], 20 );
add_action(
'woocommerce_product_quick_edit_save',
array( $this, 'on_quick_and_bulk_edit_save' ),
10, // Action priority
1 // Args passed to on_quick_and_bulk_edit_save ('product')
);
add_action(
'woocommerce_product_bulk_edit_save',
array( $this, 'on_quick_and_bulk_edit_save' ),
10, // Action priority
1 // Args passed to on_quick_and_bulk_edit_save ('product')
);
add_action(
'before_delete_post',
array( $this, 'on_product_delete' ),
10,
1
);
add_action( 'add_meta_boxes', array( $this, 'fb_product_metabox' ), 10, 1 );
add_action(
'transition_post_status',
array( $this, 'fb_change_product_published_status' ),
10,
3
);
add_action(
'wp_ajax_ajax_fb_toggle_visibility',
array( $this, 'ajax_fb_toggle_visibility' )
);
add_action(
'wp_ajax_ajax_reset_single_fb_product',
array( $this, 'ajax_reset_single_fb_product' )
);
add_action(
'wp_ajax_ajax_delete_fb_product',
array( $this, 'ajax_delete_fb_product' )
);
add_filter(
'woocommerce_duplicate_product_exclude_meta',
array( $this, 'fb_duplicate_product_reset_meta' )
);
add_action(
'pmxi_after_xml_import',
array( $this, 'wp_all_import_compat' )
);
add_action(
'wp_ajax_wpmelon_adv_bulk_edit',
[ $this, 'ajax_woo_adv_bulk_edit_compat' ],
self::FB_PRIORITY_MID
);
// used to remove the 'you need to resync' message
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( isset( $_GET['remove_sticky'] ) ) {
$this->remove_sticky_message();
}
if ( defined( 'ICL_LANGUAGE_CODE' ) ) {
include_once 'includes/fbwpml.php';
new WC_Facebook_WPML_Injector();
}
}
$this->load_background_sync_process();
}
// Must be outside of admin for cron to schedule correctly.
add_action( 'sync_all_fb_products_using_feed', [ $this, 'handle_scheduled_resync_action' ], self::FB_PRIORITY_MID );
if ( $this->get_facebook_pixel_id() ) {
$user_info = WC_Facebookcommerce_Utils::get_user_info( $this->is_advanced_matching_enabled() );
$this->events_tracker = new WC_Facebookcommerce_EventsTracker( $user_info );
}
if ( $this->is_messenger_enabled() ) {
$this->messenger_chat = new WC_Facebookcommerce_MessengerChat( [
'fb_page_id' => $this->get_facebook_page_id(),
'is_messenger_chat_plugin_enabled' => wc_bool_to_string( $this->is_messenger_enabled() ),
'msger_chat_customization_greeting_text_code' => $this->get_messenger_greeting(),
'msger_chat_customization_locale' => $this->get_messenger_locale(),
'msger_chat_customization_theme_color_code' => $this->get_messenger_color_hex(),
'facebook_jssdk_version' => $this->get_js_sdk_version(),
] );
}
}
public function load_background_sync_process() {
// Attempt to load background processing (Woo 3.x.x only)
include_once 'includes/fbbackground.php';
if ( class_exists( 'WC_Facebookcommerce_Background_Process' ) ) {
if ( ! isset( $this->background_processor ) ) {
$this->background_processor =
new WC_Facebookcommerce_Background_Process( $this );
}
}
add_action(
'wp_ajax_ajax_fb_background_check_queue',
array( $this, 'ajax_fb_background_check_queue' )
);
}
public function ajax_fb_background_check_queue() {
WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'background check queue', true );
check_ajax_referer( 'wc_facebook_settings_jsx' );
$request_time = null;
if ( isset( $_POST['request_time'] ) ) {
$request_time = esc_js( sanitize_text_field( wp_unslash( $_POST['request_time'] ) ) );
}
if ( $this->get_page_access_token() ) {
if ( isset( $this->background_processor ) ) {
$is_processing = $this->background_processor->handle_cron_healthcheck();
$remaining = $this->background_processor->get_item_count();
$response = array(
'connected' => true,
'background' => true,
'processing' => $is_processing,
'remaining' => $remaining,
'request_time' => $request_time,
);
} else {
$response = array(
'connected' => true,
'background' => false,
);
}
} else {
$response = array(
'connected' => false,
'background' => false,
);
}
printf( json_encode( $response ) );
wp_die();
}
/**
* Adds a new tab to the Product edit page.
*
* @internal
* @deprecated since 1.10.0-dev.1
*
* @param array $tabs array of tabs
* @return array
*/
public function fb_new_product_tab( $tabs ) {
wc_deprecated_function( __METHOD__, '1.10.0-dev.1', '\\SkyVerge\\WooCommerce\\Facebook\\Admin::add_product_settings_tab()' );
return $tabs;
}
/**
* Adds content to the new Facebook tab on the Product edit page.
*
* @internal
* @deprecated since 1.10.0-dev.1
*/
public function fb_new_product_tab_content() {
wc_deprecated_function( __METHOD__, '1.10.0-dev.1', '\\SkyVerge\\WooCommerce\\Facebook\\Admin::add_product_settings_tab_content()' );
}
/**
* Filters the product columns in the admin edit screen.
*
* @internal
* @deprecated since 1.10.0-dev.1
*
* @param array $existing_columns array of columns and labels
* @return array
*/
public function fb_product_columns( $existing_columns ) {
wc_deprecated_function( __METHOD__, '1.10.0-dev.1', '\\SkyVerge\\WooCommerce\\Facebook\\Admin::add_product_list_table_column()' );
return $existing_columns;
}
/**
* Outputs content for the FB Shop column in the edit screen.
*
* @internal
* @deprecated since 1.10.0-dev.1
*
* @param string $column name of the column to display
*/
public function fb_render_product_columns( $column ) {
wc_deprecated_function( __METHOD__, '1.10.0-dev.1', '\\SkyVerge\\WooCommerce\\Facebook\\Admin::add_product_list_table_columns_content()' );
}
public function fb_product_metabox() {
$ajax_data = array(
'nonce' => wp_create_nonce( 'wc_facebook_metabox_jsx' ),
);
wp_enqueue_script(
'wc_facebook_metabox_jsx',
plugins_url(
'/assets/js/facebook-metabox.min.js?ts=' . time(),
__FILE__
)
);
wp_localize_script(
'wc_facebook_metabox_jsx',
'wc_facebook_metabox_jsx',
$ajax_data
);
add_meta_box(
'facebook_metabox', // Meta box ID
'Facebook', // Meta box Title
array( $this, 'fb_product_meta_box_html' ), // Callback
'product', // Screen to which to add the meta box
'side' // Context
);
}
/**
* Renders the content of the product meta box.
*/
public function fb_product_meta_box_html() {
global $post;
$woo_product = new WC_Facebook_Product( $post->ID );
$fb_product_group_id = $this->get_product_fbid(
self::FB_PRODUCT_GROUP_ID,
$post->ID,
$woo_product
);
?>
<span id="fb_metadata">
<?php
if ( $fb_product_group_id ) {
?>
<?php echo esc_html__( 'Facebook ID:', 'facebook-for-woocommerce' ); ?>
<a href="https://facebook.com/<?php echo esc_attr( $fb_product_group_id ); ?>"
target="_blank">
<?php echo esc_html( $fb_product_group_id ); ?>
</a>
<p/>
<?php
if ( WC_Facebookcommerce_Utils::is_variable_type( $woo_product->get_type() ) ) {
?>
<p><?php echo esc_html__( 'Variant IDs:', 'facebook-for-woocommerce' ); ?><br/>
<?php
$children = $woo_product->get_children();
foreach ( $children as $child_id ) {
$fb_product_item_id = $this->get_product_fbid(
self::FB_PRODUCT_ITEM_ID,
$child_id
);
?>
<?php echo esc_html( $child_id ); ?>:
<a href="https://facebook.com/<?php echo esc_attr( $fb_product_item_id ); ?>"
target="_blank">
<?php echo esc_html( $fb_product_item_id ); ?>
</a><br/>
<?php
}
?>
</p>
<?php
}
?>
<?php echo esc_html__( 'Visible:', 'facebook-for-woocommerce' ); ?>
<input name="<?php echo esc_attr( Products::VISIBILITY_META_KEY ); ?>"
type="checkbox"
value="1"
<?php echo checked( ! $woo_product->woo_product instanceof \WC_Product || Products::is_product_visible( $woo_product->woo_product ) ); ?>/>
<p/>
<input name="is_product_page" type="hidden" value="1"/>
<p/>
<a href="#" onclick="fb_reset_product( <?php echo esc_js( $post->ID ); ?> )">
<?php echo esc_html__( 'Reset Facebook metadata', 'facebook-for-woocommerce' ); ?>
</a>
<p/>
<a href="#" onclick="fb_delete_product( <?php echo esc_js( $post->ID ); ?> )">
<?php echo esc_html__( 'Delete product(s) on Facebook', 'facebook-for-woocommerce' ); ?>
</a>
<?php
} else {
?>
<b><?php echo esc_html__( 'This product is not yet synced to Facebook.', 'facebook-for-woocommerce' ); ?></b>
<?php
}
?>
</span>
<?php
}
/**
* Gets the total of published products.
*
* @return int
*/
private function get_product_count() {
$args = [
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => -1,
'fields' => 'ids',
];
$products = new WP_Query( $args );
wp_reset_postdata();
return $products->found_posts;
}
/**
* Load DIA specific JS Data
*/
public function load_assets() {
$screen = get_current_screen();
$ajax_data = array(
'nonce' => wp_create_nonce( 'wc_facebook_infobanner_jsx' ),
);
// load banner assets
wp_enqueue_script(
'wc_facebook_infobanner_jsx',
plugins_url(
'/assets/js/facebook-infobanner.min.js?ts=' . time(),
__FILE__
)
);
wp_localize_script(
'wc_facebook_infobanner_jsx',
'wc_facebook_infobanner_jsx',
$ajax_data
);
wp_enqueue_style(
'wc_facebook_infobanner_css',
plugins_url(
'/assets/css/facebook-infobanner.css',
__FILE__
)
);
if ( strpos( $screen->id, 'page_wc-settings' ) == 0 ) {
return;
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( empty( $_GET['tab'] ) ) {
return;
}
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( 'integration' !== $_GET['tab'] ) {
return;
}
?>
<script>
window.facebookAdsToolboxConfig = {
hasGzipSupport: '<?php echo extension_loaded( 'zlib' ) ? 'true' : 'false'; ?>',
enabledPlugins: ['MESSENGER_CHAT','INSTAGRAM_SHOP', 'PAGE_SHOP'],
enableSubscription: '<?php echo class_exists( 'WC_Subscriptions' ) ? 'true' : 'false'; ?>',
popupOrigin: '<?php echo isset( $_GET['url'] ) ? esc_js( sanitize_text_field( wp_unslash( $_GET['url'] ) ) ) : 'https://www.facebook.com/'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended ?>',
feedWasDisabled: 'true',
platform: 'WooCommerce',
pixel: {
pixelId: '<?php echo $this->get_facebook_pixel_id() ? esc_js( $this->get_facebook_pixel_id() ) : ''; ?>',
advanced_matching_supported: true
},
diaSettingId: '<?php echo $this->get_external_merchant_settings_id() ? esc_js( $this->get_external_merchant_settings_id() ) : ''; ?>',
store: {
baseUrl: window.location.protocol + '//' + window.location.host,
baseCurrency:'<?php echo esc_js( WC_Admin_Settings::get_option( 'woocommerce_currency' ) ); ?>',
timezoneId: '<?php echo esc_js( date( 'Z' ) ); ?>',
storeName: '<?php echo esc_js( WC_Facebookcommerce_Utils::get_store_name() ); ?>',
version: '<?php echo esc_js( WC()->version ) ; ?>',
php_version: '<?php echo PHP_VERSION; ?>',
plugin_version: '<?php echo esc_js( WC_Facebookcommerce_Utils::PLUGIN_VERSION ); ?>'
},
feed: {
totalVisibleProducts: '<?php echo esc_js( $this->get_product_count() ); ?>',
hasClientSideFeedUpload: '<?php echo esc_js( ! ! $this->get_feed_id() ); ?>'
},
feedPrepared: {
feedUrl: '',
feedPingUrl: '',
samples: <?php echo $this->get_sample_product_feed(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
},
tokenExpired: '<?php echo $this->get_page_access_token() && ! $this->get_page_name(); ?>',
excludedCategoryIDs: <?php echo json_encode( $this->get_excluded_product_category_ids() ); ?>,
excludedTagIDs: <?php echo json_encode( $this->get_excluded_product_tag_ids() ); ?>,
messengerGreetingMaxCharacters: <?php echo esc_js( $this->get_messenger_greeting_max_characters() ); ?>
};
</script>
<?php
$ajax_data = [
'nonce' => wp_create_nonce( 'wc_facebook_settings_jsx' ),
];
wp_enqueue_script(
'wc_facebook_settings_jsx',
plugins_url(
'/assets/js/facebook-settings.min.js?ts=' . time(),
__FILE__
)
);
wp_localize_script(
'wc_facebook_settings_jsx',
'wc_facebook_settings_jsx',
$ajax_data
);
wp_enqueue_style(
'wc_facebook_css',
plugins_url(
'/assets/css/facebook.css',
__FILE__
)
);
}
/**
* Checks the product type and calls the corresponding on publish method.
*
*
* @internal
*
* @since 1.10.0-dev.1
*
* @param int $wp_id post ID
*/
function on_product_save( $wp_id ) {
$product = wc_get_product( $wp_id );
if ( ! $product ) {
return;
}
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$sync_enabled = ! empty( $_POST['fb_sync_enabled'] );
$is_visible = ! empty( $_POST['fb_visibility'] );
if ( ! $product->is_type( 'variable' ) ) {
if ( $sync_enabled ) {
Products::enable_sync_for_products( [ $product ] );
$this->save_product_settings( $product );
} else {
Products::disable_sync_for_products( [ $product ] );
}
}
$this->update_fb_visibility( $product->get_id(), $is_visible ? self::FB_SHOP_PRODUCT_VISIBLE : self::FB_SHOP_PRODUCT_HIDDEN );
if ( $sync_enabled ) {
switch ( $product->get_type() ) {
case 'simple':
case 'booking':
case 'external':
$this->on_simple_product_publish( $wp_id );
break;
case 'variable':
$this->on_variable_product_publish( $wp_id );
break;
case 'subscription':
case 'variable-subscription':
case 'bundle':
$this->on_product_publish( $wp_id );
break;
}
}
}
/**
* Saves the submitted Facebook settings for a product.
*
* @since 1.10.0-dev.1
*
* @param \WC_Product $product the product object
*/
private function save_product_settings( \WC_Product $product ) {
$woo_product = new WC_Facebook_Product( $product->get_id() );
// phpcs:disable WordPress.Security.NonceVerification.Missing
if ( isset( $_POST[ self::FB_PRODUCT_DESCRIPTION ] ) ) {
$woo_product->set_description( sanitize_text_field( wp_unslash( $_POST[ self::FB_PRODUCT_DESCRIPTION ] ) ) );
}
if ( isset( $_POST[ WC_Facebook_Product::FB_PRODUCT_PRICE ] ) ) {
$woo_product->set_price( sanitize_text_field( wp_unslash( $_POST[ WC_Facebook_Product::FB_PRODUCT_PRICE ] ) ) );
}
if ( isset( $_POST[ 'fb_product_image_source' ] ) ) {
$product->update_meta_data( Products::PRODUCT_IMAGE_SOURCE_META_KEY, sanitize_key( wp_unslash( $_POST[ 'fb_product_image_source' ] ) ) );
$product->save_meta_data();
}
if ( isset( $_POST[ WC_Facebook_Product::FB_PRODUCT_IMAGE ] ) ) {
$woo_product->set_product_image( sanitize_text_field( wp_unslash( $_POST[ WC_Facebook_Product::FB_PRODUCT_IMAGE ] ) ) );
}
// phpcs:enable WordPress.Security.NonceVerification.Missing
}
/**
* Deletes a product from Facebook.
*
* @param int $wp_id product ID
*/
public function on_product_delete( $wp_id ) {
$woo_product = new WC_Facebook_Product( $wp_id );
if ( ! $woo_product->exists() ) {
// This happens when the wp_id is not a product or it's already
// been deleted.
return;
}
// skip if not enabled for sync
if ( ! $woo_product->woo_product instanceof \WC_Product || ! \SkyVerge\WooCommerce\Facebook\Products::product_should_be_synced( $woo_product->woo_product ) ) {
return;
}
$fb_product_group_id = $this->get_product_fbid(
self::FB_PRODUCT_GROUP_ID,
$wp_id,
$woo_product
);
$fb_product_item_id = $this->get_product_fbid(
self::FB_PRODUCT_ITEM_ID,
$wp_id,
$woo_product
);
if ( ! ( $fb_product_group_id || $fb_product_item_id ) ) {
return; // No synced product, no-op.
}
$products = array( $wp_id );
if ( WC_Facebookcommerce_Utils::is_variable_type( $woo_product->get_type() ) ) {
$children = $woo_product->get_children();
$products = array_merge( $products, $children );
}
foreach ( $products as $item_id ) {
$this->delete_product_item( $item_id );
}
if ( $fb_product_group_id ) {
$pg_result = $this->fbgraph->delete_product_group( $fb_product_group_id );
WC_Facebookcommerce_Utils::log( $pg_result );
}
}
/**
* Update FB visibility for trashing and restore.
*/
function fb_change_product_published_status( $new_status, $old_status, $post ) {
global $post;
if ( ! $post ) {
return;
}
$visibility = $new_status === 'publish' ? self::FB_SHOP_PRODUCT_VISIBLE : self::FB_SHOP_PRODUCT_HIDDEN;
$product = wc_get_product( $post->ID );
// bail if this product isn't enabled for sync
if ( ! $product instanceof \WC_Product || ! Products::is_sync_enabled_for_product( $product ) ) {
return;
}
// change from publish status -> unpublish status, e.g. trash, draft, etc.
// change from trash status -> publish status
// no need to update for change from trash <-> unpublish status
if ( ( $old_status == 'publish' && $new_status != 'publish' ) ||
( $old_status == 'trash' && $new_status == 'publish' ) ) {
$this->update_fb_visibility( $post->ID, $visibility );
}
}
/**
* Generic function for use with any product publishing.
*
* Will determine product type (simple or variable) and delegate to
* appropriate handler.
*
* @param int $wp_id product ID
*/
public function on_product_publish( $wp_id ) {
if ( get_post_status( $wp_id ) != 'publish' ) {
return;
}
$woo_product = new WC_Facebook_Product( $wp_id );
// skip if not enabled for sync
if ( ! $woo_product->woo_product instanceof \WC_Product || ! \SkyVerge\WooCommerce\Facebook\Products::product_should_be_synced( $woo_product->woo_product ) ) {
return;
}
if ( $woo_product->woo_product->is_type( 'variable' ) ) {
$this->on_variable_product_publish( $wp_id, $woo_product );
} else {
$this->on_simple_product_publish( $wp_id, $woo_product );
}
}
/**
* If the user has opt-in to remove products that are out of stock,
* this function will delete the product from FB Page as well.
*/
function delete_on_out_of_stock( $wp_id, $woo_product ) {
if ( get_option( 'woocommerce_hide_out_of_stock_items' ) === 'yes' &&
! $woo_product->is_in_stock() ) {
$this->delete_product_item( $wp_id );
return true;
}
return false;
}
/**
* Syncs product to Facebook when saving a variable product.
*
* @param int $wp_id product post ID
* @param WC_Facebook_Product|null $woo_product product object
*/
function on_variable_product_publish( $wp_id, $woo_product = null ) {
if ( ! $this->is_product_sync_enabled() ) {
return;
}
if ( get_post_status( $wp_id ) != 'publish' ) {
return;
}
// Check if product group has been published to FB. If not, it's new.
// If yes, loop through variants and see if product items are published.
if ( ! $woo_product ) {
$woo_product = new WC_Facebook_Product( $wp_id );
}
if ( $this->delete_on_out_of_stock( $wp_id, $woo_product ) ) {
return;
}
$fb_product_group_id = $this->get_product_fbid(
self::FB_PRODUCT_GROUP_ID,
$wp_id,
$woo_product
);
if ( $fb_product_group_id ) {
$woo_product->fb_visibility = Products::is_product_visible( $woo_product->woo_product );
$this->update_product_group( $woo_product );
$child_products = $woo_product->get_children();
$variation_id = $woo_product->find_matching_product_variation();
// check if item_id is default variation. If yes, update in the end.
// If default variation value is to update, delete old fb_product_item_id
// and create new one in order to make it order correctly.
foreach ( $child_products as $item_id ) {
$fb_product_item_id = $this->on_simple_product_publish( $item_id, null, $woo_product );
if ( $item_id == $variation_id && $fb_product_item_id ) {
$this->set_default_variant( $fb_product_group_id, $fb_product_item_id );
}
}
} else {
$this->create_product_variable( $woo_product );
}
}
/**
* Syncs product to Facebook when saving a simple product.
*
* @param int $wp_id product post ID
* @param WC_Facebook_Product|null $woo_product product object
* @param WC_Facebook_Product|null $parent_product parent object
* @return int|mixed|void|null
*/
function on_simple_product_publish( $wp_id, $woo_product = null, &$parent_product = null ) {
if ( ! $this->is_product_sync_enabled() ) {
return;
}
if ( get_post_status( $wp_id ) != 'publish' ) {
return;
}
if ( ! $woo_product ) {
$woo_product = new WC_Facebook_Product( $wp_id, $parent_product );
}
// skip if not enabled for sync
if ( ! $woo_product->woo_product instanceof \WC_Product || ! \SkyVerge\WooCommerce\Facebook\Products::product_should_be_synced( $woo_product->woo_product ) ) {
return;
}
if ( $this->delete_on_out_of_stock( $wp_id, $woo_product ) ) {
return;
}
// Check if this product has already been published to FB.
// If not, it's new!
$fb_product_item_id = $this->get_product_fbid( self::FB_PRODUCT_ITEM_ID, $wp_id, $woo_product );
if ( $fb_product_item_id ) {
$woo_product->fb_visibility = Products::is_product_visible( $woo_product->woo_product );
$this->update_product_item( $woo_product, $fb_product_item_id );
return $fb_product_item_id;
} else {
// Check if this is a new product item for an existing product group
if ( $woo_product->get_parent_id() ) {
$fb_product_group_id = $this->get_product_fbid(
self::FB_PRODUCT_GROUP_ID,
$woo_product->get_parent_id(),
$woo_product
);
// New variant added
if ( $fb_product_group_id ) {
return $this->create_product_simple( $woo_product, $fb_product_group_id );
} else {
WC_Facebookcommerce_Utils::fblog(
'Wrong! simple_product_publish called without group ID for
a variable product!',
[],
true
);
}
} else {
return $this->create_product_simple( $woo_product ); // new product
}
}
}
function create_product_variable( $woo_product ) {
$retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id( $woo_product );
$fb_product_group_id = $this->create_product_group(
$woo_product,
$retailer_id,
true
);
if ( $fb_product_group_id ) {
$child_products = $woo_product->get_children();
$variation_id = $woo_product->find_matching_product_variation();
foreach ( $child_products as $item_id ) {
$child_product = new WC_Facebook_Product( $item_id, $woo_product );
$retailer_id =
WC_Facebookcommerce_Utils::get_fb_retailer_id( $child_product );
$fb_product_item_id = $this->create_product_item(
$child_product,
$retailer_id,
$fb_product_group_id
);
if ( $item_id == $variation_id && $fb_product_item_id ) {
$this->set_default_variant( $fb_product_group_id, $fb_product_item_id );
}
}
}
}
/**
* Create product group and product, store fb-specific info
**/
function create_product_simple( $woo_product, $fb_product_group_id = null ) {
$retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id( $woo_product );
if ( ! $fb_product_group_id ) {
$fb_product_group_id = $this->create_product_group(
$woo_product,
$retailer_id
);
}
if ( $fb_product_group_id ) {
$fb_product_item_id = $this->create_product_item(
$woo_product,
$retailer_id,
$fb_product_group_id
);
return $fb_product_item_id;
}
}
function create_product_group( $woo_product, $retailer_id, $variants = false ) {
$product_group_data = array(
'retailer_id' => $retailer_id,
);
// Default visibility on create = published
$woo_product->fb_visibility = true;
update_post_meta( $woo_product->get_id(), Products::VISIBILITY_META_KEY, true );
if ( $variants ) {
$product_group_data['variants'] =
$woo_product->prepare_variants_for_group();
}
$create_product_group_result = $this->check_api_result(
$this->fbgraph->create_product_group(
$this->get_product_catalog_id(),
$product_group_data
),
$product_group_data,
$woo_product->get_id()
);
// New variant added
if ( $create_product_group_result ) {
$decode_result = WC_Facebookcommerce_Utils::decode_json( $create_product_group_result['body'] );
$fb_product_group_id = $decode_result->id;
// update_post_meta is actually more of a create_or_update
update_post_meta(
$woo_product->get_id(),
self::FB_PRODUCT_GROUP_ID,
$fb_product_group_id
);
$this->display_success_message(
'Created product group <a href="https://facebook.com/' .
$fb_product_group_id . '" target="_blank">' .
$fb_product_group_id . '</a> on Facebook.'
);
return $fb_product_group_id;
}
}
function create_product_item( $woo_product, $retailer_id, $product_group_id ) {
// Default visibility on create = published
$woo_product->fb_visibility = true;
$product_data = $woo_product->prepare_product( $retailer_id );
if ( ! $product_data['price'] ) {
return 0;
}
update_post_meta( $woo_product->get_id(), Products::VISIBILITY_META_KEY, true );
$product_result = $this->check_api_result(
$this->fbgraph->create_product_item(
$product_group_id,
$product_data
),
$product_data,
$woo_product->get_id()
);
if ( $product_result ) {
$decode_result = WC_Facebookcommerce_Utils::decode_json( $product_result['body'] );
$fb_product_item_id = $decode_result->id;
update_post_meta(
$woo_product->get_id(),
self::FB_PRODUCT_ITEM_ID,
$fb_product_item_id
);
$this->display_success_message(
'Created product item <a href="https://facebook.com/' .
$fb_product_item_id . '" target="_blank">' .
$fb_product_item_id . '</a> on Facebook.'
);
return $fb_product_item_id;
}
}
/**
* Update existing product group (variant data only)
**/
function update_product_group( $woo_product ) {
$fb_product_group_id = $this->get_product_fbid(
self::FB_PRODUCT_GROUP_ID,
$woo_product->get_id(),
$woo_product
);
if ( ! $fb_product_group_id ) {
return;
}
$variants = $woo_product->prepare_variants_for_group();
if ( ! $variants ) {
WC_Facebookcommerce_Utils::log(
sprintf(
__(
'Nothing to update for product group for %1$s',
'facebook-for-woocommerce'
),
$fb_product_group_id
)
);
return;
}
$product_group_data = array(
'variants' => $variants,
);
$result = $this->check_api_result(
$this->fbgraph->update_product_group(
$fb_product_group_id,
$product_group_data
)
);
if ( $result ) {
$this->display_success_message(
'Updated product group <a href="https://facebook.com/' .
$fb_product_group_id . '" target="_blank">' . $fb_product_group_id .
'</a> on Facebook.'
);
}
}
/**
* Update existing product
**/
function update_product_item( $woo_product, $fb_product_item_id ) {
$product_data = $woo_product->prepare_product();
$result = $this->check_api_result(
$this->fbgraph->update_product_item(
$fb_product_item_id,
$product_data
)
);
if ( $result ) {
$this->display_success_message(
'Updated product <a href="https://facebook.com/' . $fb_product_item_id .
'" target="_blank">' . $fb_product_item_id . '</a> on Facebook.'
);
}
}
/**
* Saves settings via AJAX (to preserve window context for onboarding).
*
* @internal
*/
public function ajax_save_fb_settings() {
WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'save settings', true );
check_ajax_referer( 'wc_facebook_settings_jsx' );
if ( ! isset( $_REQUEST['facebook_for_woocommerce'] ) ) {
// This is not a request from our plugin,
// some other handler or plugin probably
// wants to handle it and wp_die() after.
return;
}
if ( isset( $_REQUEST['api_key'] ) ) {
$api_key = sanitize_text_field( wp_unslash( $_REQUEST['api_key'] ) );
if ( ctype_alnum( $api_key ) ) {
$this->update_page_access_token( $api_key );
}
}
if ( isset( $_REQUEST['product_catalog_id'] ) ) {
$product_catalog_id = sanitize_text_field( wp_unslash( $_REQUEST['product_catalog_id'] ) );
if ( ctype_digit( $product_catalog_id ) ) {
if ( ! empty( $this->get_product_catalog_id() ) && $_REQUEST['product_catalog_id'] !== $this->get_product_catalog_id() ) {
$this->reset_all_products();
}
$this->update_product_catalog_id( sanitize_text_field( wp_unslash( $_REQUEST['product_catalog_id'] ) ) );
}
}
if ( isset( $_REQUEST['pixel_id'] ) ) {
$pixel_id = sanitize_text_field( wp_unslash( $_REQUEST['pixel_id'] ) );
if ( ctype_digit( $pixel_id ) ) {
// to prevent race conditions with pixel-only settings, only save a pixel if we already have an access token
if ( $this->get_page_access_token() ) {
if ( $this->get_facebook_pixel_id() !== $pixel_id ) {
$this->update_pixel_install_time( time() );
}
$this->settings[ self::SETTING_FACEBOOK_PIXEL_ID ] = $pixel_id;
} else {
WC_Facebookcommerce_Utils::log( 'Got pixel-only settings, doing nothing' );
echo 'Not saving pixel-only settings';
wp_die();
}
}
}
if ( isset( $_REQUEST['pixel_use_pii'] ) ) {
$this->settings[ self::SETTING_ENABLE_ADVANCED_MATCHING ] = wc_bool_to_string( wc_clean( wp_unslash( $_REQUEST['pixel_use_pii'] ) ) );
}
if ( isset( $_REQUEST['page_id'] ) ) {
$page_id = sanitize_text_field( wp_unslash( $_REQUEST['page_id'] ) );
if ( ctype_digit( $page_id ) ) {
$this->settings[ self::SETTING_FACEBOOK_PAGE_ID ] = $page_id;
}
}
if ( isset( $_REQUEST['external_merchant_settings_id'] ) ) {
$external_merchant_settings_id = sanitize_text_field( wp_unslash( $_REQUEST['external_merchant_settings_id'] ) );
if ( ctype_digit( $external_merchant_settings_id ) ) {
$this->update_external_merchant_settings_id( $external_merchant_settings_id );
}
}
if ( isset( $_REQUEST['is_messenger_chat_plugin_enabled'] ) ) {
$this->settings[ self::SETTING_ENABLE_MESSENGER ] = wc_bool_to_string( wc_clean( wp_unslash( $_REQUEST['is_messenger_chat_plugin_enabled'] ) ) );
}
if ( isset( $_REQUEST['facebook_jssdk_version'] ) ) {
$this->update_js_sdk_version( sanitize_text_field( wp_unslash( $_REQUEST['facebook_jssdk_version'] ) ) );
}
if ( ! empty( $_REQUEST['msger_chat_customization_greeting_text_code'] ) ) {
$this->settings[ self::SETTING_MESSENGER_GREETING ] = sanitize_text_field( wp_unslash( $_REQUEST['msger_chat_customization_greeting_text_code'] ) );
}
if ( isset( $_REQUEST['msger_chat_customization_locale'] ) ) {
$this->settings[ self::SETTING_MESSENGER_LOCALE ] = sanitize_text_field( wp_unslash( $_REQUEST['msger_chat_customization_locale'] ) );
}
if ( ! empty( $_REQUEST['msger_chat_customization_theme_color_code'] ) ) {
$this->settings[ self::SETTING_MESSENGER_COLOR_HEX ] = sanitize_hex_color( wp_unslash( $_REQUEST['msger_chat_customization_theme_color_code'] ) );
}
/** This filter is defined by WooCommerce in includes/abstracts/abstract-wc-settings-api.php */
update_option( $this->get_option_key(), apply_filters( 'woocommerce_settings_api_sanitized_fields_' . $this->id, $this->settings ) );
WC_Facebookcommerce_Utils::log( 'Settings saved!' );
echo 'settings_saved';
wp_die();
}
/**
* Delete all settings via AJAX
**/
function ajax_delete_fb_settings() {
check_ajax_referer( 'wc_facebook_settings_jsx' );
if ( ! WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'delete settings', false ) ) {
return;
}
// Do not allow reset in the middle of product sync
$currently_syncing = get_transient( self::FB_SYNC_IN_PROGRESS );
if ( $currently_syncing ) {
wp_send_json(
'A Facebook product sync is currently in progress.
Deleting settings during product sync may cause errors.'
);
return;
}
if ( isset( $_REQUEST ) ) {
$ems = $this->get_external_merchant_settings_id();
if ( $ems ) {
WC_Facebookcommerce_Utils::fblog(
'Deleted all settings!',
array(),
false,
$ems
);
}
$this->init_settings();
$this->update_page_access_token( '' );
$this->update_product_catalog_id( '' );
$this->settings[ self::SETTING_FACEBOOK_PIXEL_ID ] = '';
$this->settings[ self::SETTING_ENABLE_ADVANCED_MATCHING ] = 'no';
$this->settings[ self::SETTING_FACEBOOK_PAGE_ID ] = '';
$this->update_external_merchant_settings_id( '' );
$this->update_pixel_install_time( 0 );
$this->update_feed_id( '' );
$this->settings['fb_upload_id'] = '';
$this->settings['upload_end_time'] = '';
WC_Facebookcommerce_Pixel::set_pixel_id( 0 );
update_option(
$this->get_option_key(),
apply_filters(
'woocommerce_settings_api_sanitized_fields_' . $this->id,
$this->settings
)
);
// Clean up old messages
delete_transient( 'facebook_plugin_api_error' );
delete_transient( 'facebook_plugin_api_success' );
delete_transient( 'facebook_plugin_api_warning' );
delete_transient( 'facebook_plugin_api_info' );
delete_transient( 'facebook_plugin_api_sticky' );
$this->reset_all_products();
WC_Facebookcommerce_Utils::log( 'Settings deleted' );
echo 'Settings Deleted';
}
wp_die();
}
/**
* Check Feed Upload Status
**/
function ajax_check_feed_upload_status() {
WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'check feed upload status', true );
check_ajax_referer( 'wc_facebook_settings_jsx' );
if ( $this->get_page_access_token() ) {
$response = array(
'connected' => true,
'status' => 'in progress',
);
if ( $this->settings['fb_upload_id'] ) {
if ( ! isset( $this->fbproductfeed ) ) {
if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) {
include_once 'includes/fbproductfeed.php';
}
$this->fbproductfeed = new WC_Facebook_Product_Feed(
$this->get_product_catalog_id(),
$this->fbgraph
);
}
$status = $this->fbproductfeed->is_upload_complete( $this->settings );
$response['status'] = $status;
} else {
$response = array(
'connected' => true,
'status' => 'error',
);
}
if ( $response['status'] == 'complete' ) {
update_option(
$this->get_option_key(),
apply_filters(
'woocommerce_settings_api_sanitized_fields_' . $this->id,
$this->settings
)
);
}
} else {
$response = array(
'connected' => false,
);
}
printf( json_encode( $response ) );
wp_die();
}
/**
* Display custom success message (sugar)
**/
function display_success_message( $msg ) {
$msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
set_transient(
'facebook_plugin_api_success',
$msg,
self::FB_MESSAGE_DISPLAY_TIME
);
}
/**
* Display custom warning message (sugar)
**/
function display_warning_message( $msg ) {
$msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
set_transient(
'facebook_plugin_api_warning',
$msg,
self::FB_MESSAGE_DISPLAY_TIME
);
}
/**
* Display custom info message (sugar)
**/
function display_info_message( $msg ) {
$msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
set_transient(
'facebook_plugin_api_info',
$msg,
self::FB_MESSAGE_DISPLAY_TIME
);
}
/**
* Display custom "sticky" info message.
* Call remove_sticky_message or wait for time out.
**/
function display_sticky_message( $msg ) {
$msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
set_transient(
'facebook_plugin_api_sticky',
$msg,
self::FB_MESSAGE_DISPLAY_TIME
);
}
/**
* Remove custom "sticky" info message
**/
function remove_sticky_message() {
delete_transient( 'facebook_plugin_api_sticky' );
}
function remove_resync_message() {
$msg = get_transient( 'facebook_plugin_api_sticky' );
if ( $msg && strpos( $msg, 'Sync' ) !== false ) {
delete_transient( 'facebook_plugin_resync_sticky' );
}
}
/**
* Logs and stores custom error message (sugar).
*
* @param string $msg
*/
function display_error_message( $msg ) {
$msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
WC_Facebookcommerce_Utils::log( $msg );
set_transient(
'facebook_plugin_api_error',
$msg,
self::FB_MESSAGE_DISPLAY_TIME
);
}
/**
* Displays error message from API result (sugar).
*
* @param array $result
*/
function display_error_message_from_result( $result ) {
$msg = json_decode( $result['body'] )->error->message;
$this->display_error_message( $msg );
}
/**
* Deals with FB API responses, displays error if FB API returns error.
*
* @param WP_Error|array $result API response
* @param array|null $logdata additional data for logging
* @param int|null $wpid post ID
* @return array|null|void result if response is 200, null otherwise
*/
function check_api_result( $result, $logdata = null, $wpid = null ) {
if ( is_wp_error( $result ) ) {
WC_Facebookcommerce_Utils::log( $result->get_error_message() );
$message = sprintf(
/* translators: Placeholders %1$s - original error message from Facebook API */
esc_html__( 'There was an issue connecting to the Facebook API: %1$s', 'facebook-for-woocommerce' ),
$result->get_error_message()
);
$this->display_error_message( $message );
return;
}
if ( $result['response']['code'] != '200' ) {
// Catch 10800 fb error code ("Duplicate retailer ID") and capture FBID
// if possible, otherwise let user know we found dupe SKUs
$body = WC_Facebookcommerce_Utils::decode_json( $result['body'] );
if ( $body && $body->error->code == '10800' ) {
$error_data = $body->error->error_data; // error_data may contain FBIDs
if ( $error_data && $wpid ) {
$existing_id = $this->get_existing_fbid( $error_data, $wpid );
if ( $existing_id ) {
// Add "existing_id" ID to result
$body->id = $existing_id;
$result['body'] = json_encode( $body );
return $result;
}
}
} else {
$this->display_error_message_from_result( $result );
}
WC_Facebookcommerce_Utils::log( $result );
$data = [
'result' => $result,
'data' => $logdata,
];
WC_Facebookcommerce_Utils::fblog(
'Non-200 error code from FB',
$data,
true
);
return null;
}
return $result;
}
/**
* Displays out of sync message if products are edited using WooCommerce Advanced Bulk Edit.
*
* @param $import_id
*/
function ajax_woo_adv_bulk_edit_compat( $import_id ) {
if ( ! WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'adv bulk edit', false ) ) {
return;
}
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$type = isset( $_POST['type'] ) ? sanitize_text_field( wp_unslash( $_POST['type'] ) ) : '';
if ( strpos( $type, 'product' ) !== false && strpos( $type, 'load' ) === false ) {
$this->display_out_of_sync_message( 'advanced bulk edit' );
}
}
function wp_all_import_compat( $import_id ) {
$import = new PMXI_Import_Record();
$import->getById( $import_id );
if ( ! $import->isEmpty() && in_array( $import->options['custom_type'], array( 'product', 'product_variation' ) ) ) {
$this->display_out_of_sync_message( 'import' );
}
}
function display_out_of_sync_message( $action_name ) {
$this->display_sticky_message(
sprintf(
'Products may be out of Sync with Facebook due to your recent ' . $action_name . '.' .
' <a href="%s&fb_force_resync=true&remove_sticky=true">Re-Sync them with FB.</a>',
WOOCOMMERCE_FACEBOOK_PLUGIN_SETTINGS_URL
)
);
}
/**
* If we get a product group ID or product item ID back for a dupe retailer
* id error, update existing ID.
*
* @return null
**/
function get_existing_fbid( $error_data, $wpid ) {
if ( isset( $error_data->product_group_id ) ) {
update_post_meta(
$wpid,
self::FB_PRODUCT_GROUP_ID,
(string) $error_data->product_group_id
);
return $error_data->product_group_id;
} elseif ( isset( $error_data->product_item_id ) ) {
update_post_meta(
$wpid,
self::FB_PRODUCT_ITEM_ID,
(string) $error_data->product_item_id
);
return $error_data->product_item_id;
} else {
return;
}
}
/**
* Checks for API key and other API errors.
*/
public function checks() {
// TODO improve this by checking the settings page with Framework method and ensure error notices are displayed under the Integration sections {FN 2020-01-30}
if ( isset( $_GET['page'], $_GET['section'] ) && 'wc-settings' === $_GET['page'] && \WC_Facebookcommerce::INTEGRATION_ID === $_GET['section'] ) {
$this->display_errors();
}
// check required fields
if ( ! $this->get_page_access_token() || ! $this->get_product_catalog_id() ) {
$message = sprintf(
/* translators: Placeholders %1$s - opening strong HTML tag, %2$s - closing strong HTML tag, %3$s - opening link HTML tag, %4$s - closing link HTML tag */
esc_html__(
'%1$sFacebook for WooCommerce is almost ready.%2$s To complete your configuration, %3$scomplete the setup steps%4$s.',
'facebook-for-woocommerce'
),
'<strong>',
'</strong>',
'<a href="' . esc_url( WOOCOMMERCE_FACEBOOK_PLUGIN_SETTINGS_URL ) . '">',
'</a>'
);
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $this->get_message_html( $message, 'info' );
}
// WooCommerce 2.x upgrade nag
if ( $this->get_page_access_token() && ( ! isset( $this->background_processor ) ) ) {
$message = sprintf(
/* translators: Placeholders %1$s - WooCommerce version */
esc_html__(
'Facebook product sync may not work correctly in WooCommerce version %1$s. Please upgrade to WooCommerce 3.',
'facebook-for-woocommerce'
),
esc_html( WC()->version )
);
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $this->get_message_html( $message, 'warning' );
}
$this->maybe_display_facebook_api_messages();
}
/**
* Gets a sample feed with up to 12 published products.
*
* @return string
*/
function get_sample_product_feed() {
ob_start();
// get up to 12 published posts that are products
$args = [
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => 12,
'fields' => 'ids',
];
$post_ids = get_posts( $args );
$items = [];
foreach ( $post_ids as $post_id ) {
$woo_product = new WC_Facebook_Product( $post_id );
$product_data = $woo_product->prepare_product();
$feed_item = [
'title' => strip_tags( $product_data['name'] ),
'availability' => $woo_product->is_in_stock() ? 'in stock' :
'out of stock',
'description' => strip_tags( $product_data['description'] ),
'id' => $product_data['retailer_id'],
'image_link' => $product_data['image_url'],
'brand' => strip_tags( WC_Facebookcommerce_Utils::get_store_name() ),
'link' => $product_data['url'],
'price' => $product_data['price'] . ' ' . get_woocommerce_currency(),
];
array_push( $items, $feed_item );
}
// https://codex.wordpress.org/Function_Reference/wp_reset_postdata
wp_reset_postdata();
ob_end_clean();
return json_encode( [ $items ] );
}
/**
* Loop through array of WPIDs to remove metadata.
**/
function delete_post_meta_loop( $products ) {
foreach ( $products as $product_id ) {
delete_post_meta( $product_id, self::FB_PRODUCT_GROUP_ID );
delete_post_meta( $product_id, self::FB_PRODUCT_ITEM_ID );
delete_post_meta( $product_id, Products::VISIBILITY_META_KEY );
}
}
/**
* Remove FBIDs from all products when resetting store.
**/
function reset_all_products() {
if ( ! is_admin() ) {
WC_Facebookcommerce_Utils::log(
'Not resetting any FBIDs from products,
must call reset from admin context.'
);
return false;
}
$test_instance = WC_Facebook_Integration_Test::get_instance( $this );
$this->test_mode = $test_instance::$test_mode;
// Include draft products (omit 'post_status' => 'publish')
WC_Facebookcommerce_Utils::log( 'Removing FBIDs from all products' );
$post_ids = get_posts(
array(
'post_type' => 'product',
'posts_per_page' => -1,
'fields' => 'ids',
)
);
$children = array();
foreach ( $post_ids as $post_id ) {
$children = array_merge(
get_posts(
array(
'post_type' => 'product_variation',
'posts_per_page' => -1,
'post_parent' => $post_id,
'fields' => 'ids',
)
),
$children
);
}
$post_ids = array_merge( $post_ids, $children );
$this->delete_post_meta_loop( $post_ids );
WC_Facebookcommerce_Utils::log( 'Product FBIDs deleted' );
return true;
}
/**
* Remove FBIDs from a single WC product
**/
function reset_single_product( $wp_id ) {
$woo_product = new WC_Facebook_Product( $wp_id );
$products = array( $woo_product->get_id() );
if ( WC_Facebookcommerce_Utils::is_variable_type( $woo_product->get_type() ) ) {
$products = array_merge( $products, $woo_product->get_children() );
}
$this->delete_post_meta_loop( $products );
WC_Facebookcommerce_Utils::log( 'Deleted FB Metadata for product ' . $wp_id );
}
function ajax_reset_all_fb_products() {
WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'reset products', true );
check_ajax_referer( 'wc_facebook_settings_jsx' );
$this->reset_all_products();
wp_reset_postdata();
wp_die();
}
function ajax_reset_single_fb_product() {
WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'reset single product', true );
check_ajax_referer( 'wc_facebook_metabox_jsx' );
if ( ! isset( $_POST['wp_id'] ) ) {
wp_die();
}
$wp_id = sanitize_text_field( wp_unslash( $_POST['wp_id'] ) );
$woo_product = new WC_Facebook_Product( $wp_id );
if ( $woo_product ) {
$this->reset_single_product( $wp_id );
}
wp_reset_postdata();
wp_die();
}
function ajax_delete_fb_product() {
WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'delete single product', true );
check_ajax_referer( 'wc_facebook_metabox_jsx' );
if ( ! isset( $_POST['wp_id'] ) ) {
wp_die();
}
$wp_id = sanitize_text_field( wp_unslash( $_POST['wp_id'] ) );
$this->on_product_delete( $wp_id );
$this->reset_single_product( $wp_id );
wp_reset_postdata();
wp_die();
}
/**
* Special function to run all visible products through on_product_publish
**/
function ajax_sync_all_fb_products() {
WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'syncall products', true );
check_ajax_referer( 'wc_facebook_settings_jsx' );
if ( ! $this->is_product_sync_enabled() ) {
WC_Facebookcommerce_Utils::log( 'Sync to Facebook is disabled' );
wp_die();
}
if ( ! $this->get_page_access_token() || ! $this->get_product_catalog_id() ) {
WC_Facebookcommerce_Utils::log(
'No API key or catalog ID: ' .
$this->get_page_access_token() . ' and ' . $this->get_product_catalog_id()
);
wp_die();
return;
}
$this->remove_resync_message();
$currently_syncing = get_transient( self::FB_SYNC_IN_PROGRESS );
if ( isset( $this->background_processor ) ) {
if ( $this->background_processor->is_updating() ) {
$this->background_processor->handle_cron_healthcheck();
$currently_syncing = 1;
}
}
if ( $currently_syncing ) {
WC_Facebookcommerce_Utils::log( 'Not syncing, sync in progress' );
WC_Facebookcommerce_Utils::fblog(
'Tried to sync during an in-progress sync!',
array(),
true
);
$this->display_warning_message(
'A product sync is in progress.
Please wait until the sync finishes before starting a new one.'
);
wp_die();
return;
}
$is_valid_product_catalog =
$this->fbgraph->validate_product_catalog( $this->get_product_catalog_id() );
if ( ! $is_valid_product_catalog ) {
WC_Facebookcommerce_Utils::log( 'Not syncing, invalid product catalog!' );
WC_Facebookcommerce_Utils::fblog(
'Tried to sync with an invalid product catalog!',
array(),
true
);
$this->display_warning_message(
'We\'ve detected that your
Facebook Product Catalog is no longer valid. This may happen if it was
deleted, or this may be a transient error.
If this error persists please remove your settings via
"Advanced Options > Advanced Settings > Remove"
and try setup again'
);
wp_die();
return;
}
// Cache the cart URL to display a warning in case it changes later
$cart_url = get_option( self::FB_CART_URL );
if ( $cart_url != wc_get_cart_url() ) {
update_option( self::FB_CART_URL, wc_get_cart_url() );
}
// Get all published posts. First unsynced then already-synced.
$post_ids_new = WC_Facebookcommerce_Utils::get_wp_posts(
self::FB_PRODUCT_GROUP_ID,
'NOT EXISTS'
);
$post_ids_old = WC_Facebookcommerce_Utils::get_wp_posts(
self::FB_PRODUCT_GROUP_ID,
'EXISTS'
);
$total_new = count( $post_ids_new );
$total_old = count( $post_ids_old );
$post_ids = array_merge( $post_ids_new, $post_ids_old );
$total = count( $post_ids );
WC_Facebookcommerce_Utils::fblog(
'Attempting to sync ' . $total . ' ( ' .
$total_new . ' new) products with settings: ',
$this->settings,
false
);
// Check for background processing (Woo 3.x.x)
if ( isset( $this->background_processor ) ) {
$starting_message = sprintf(
'Starting background sync to Facebook: %d products...',
$total
);
set_transient(
self::FB_SYNC_IN_PROGRESS,
true,
self::FB_SYNC_TIMEOUT
);
set_transient(
self::FB_SYNC_REMAINING,
(int) $total
);
$this->display_info_message( $starting_message );
WC_Facebookcommerce_Utils::log( $starting_message );
foreach ( $post_ids as $post_id ) {
WC_Facebookcommerce_Utils::log( 'Pushing post to queue: ' . $post_id );
$this->background_processor->push_to_queue( $post_id );
}
$this->background_processor->save()->dispatch();
// reset FB_SYNC_REMAINING to avoid race condition
set_transient(
self::FB_SYNC_REMAINING,
(int) $total
);
// handle_cron_healthcheck must be called
// https://github.com/A5hleyRich/wp-background-processing/issues/34
$this->background_processor->handle_cron_healthcheck();
} else {
// Oldschool sync for WooCommerce 2.x
$count = ( $total_old === $total ) ? 0 : $total_old;
foreach ( $post_ids as $post_id ) {
// Repeatedly overwrite sync total while in actual sync loop
set_transient(
self::FB_SYNC_IN_PROGRESS,
true,
self::FB_SYNC_TIMEOUT
);
$this->display_sticky_message(
sprintf(
'Syncing products to Facebook: %d out of %d...',
// Display different # when resuming to avoid confusion.
min( $count, $total ),
$total
),
true
);
$this->on_product_publish( $post_id );
$count++;
}
WC_Facebookcommerce_Utils::log( 'Synced ' . $count . ' products' );
$this->remove_sticky_message();
$this->display_info_message( 'Facebook product sync complete!' );
delete_transient( self::FB_SYNC_IN_PROGRESS );
WC_Facebookcommerce_Utils::fblog(
'Product sync complete. Total products synced: ' . $count
);
}
// https://codex.wordpress.org/Function_Reference/wp_reset_postdata
wp_reset_postdata();
// This is important, for some reason.
// See https://codex.wordpress.org/AJAX_in_Plugins
wp_die();
}
/**
* Special function to run all visible products by uploading feed.
**/
function ajax_sync_all_fb_products_using_feed() {
WC_Facebookcommerce_Utils::check_woo_ajax_permissions(
'syncall products using feed',
! $this->test_mode
);
check_ajax_referer( 'wc_facebook_settings_jsx' );
return $this->sync_all_fb_products_using_feed();
}
/**
* Syncs Facebook products using a Feed.
*
* @see https://developers.facebook.com/docs/marketing-api/fbe/fbe1/guides/feed-approach
*
* @return bool
*/
public function sync_all_fb_products_using_feed() {
if ( ! $this->is_product_sync_enabled() ) {
WC_Facebookcommerce_Utils::log( 'Sync to Facebook is disabled' );
return false;
}
if ( ! $this->get_page_access_token() || ! $this->get_product_catalog_id() ) {
self::log(
'No API key or catalog ID: ' . $this->get_page_access_token() .
' and ' . $this->get_product_catalog_id()
);
return false;
}
$this->remove_resync_message();
$is_valid_product_catalog =
$this->fbgraph->validate_product_catalog( $this->get_product_catalog_id() );
if ( ! $is_valid_product_catalog ) {
WC_Facebookcommerce_Utils::log( 'Not syncing, invalid product catalog!' );
WC_Facebookcommerce_Utils::fblog(
'Tried to sync with an invalid product catalog!',
array(),
true
);
$this->display_warning_message(
'We\'ve detected that your
Facebook Product Catalog is no longer valid. This may happen if it was
deleted, or this may be a transient error.
If this error persists please remove your settings via
"Advanced Options > Advanced Settings > Remove"
and try setup again'
);
return false;
}
// Cache the cart URL to display a warning in case it changes later
$cart_url = get_option( self::FB_CART_URL );
if ( $cart_url != wc_get_cart_url() ) {
update_option( self::FB_CART_URL, wc_get_cart_url() );
}
if ( ! class_exists( 'WC_Facebook_Product_Feed' ) ) {
include_once 'includes/fbproductfeed.php';
}
if ( $this->test_mode ) {
$this->fbproductfeed = new WC_Facebook_Product_Feed_Test_Mock(
$this->get_product_catalog_id(),
$this->fbgraph,
$this->get_feed_id()
);
} else {
$this->fbproductfeed = new WC_Facebook_Product_Feed(
$this->get_product_catalog_id(),
$this->fbgraph,
$this->get_feed_id()
);
}
$upload_success = $this->fbproductfeed->sync_all_products_using_feed();
if ( $upload_success ) {
$this->update_feed_id( $this->fbproductfeed->feed_id );
$this->settings['fb_upload_id'] = $this->fbproductfeed->upload_id;
update_option(
$this->get_option_key(),
apply_filters(
'woocommerce_settings_api_sanitized_fields_' .
$this->id,
$this->settings
)
);
wp_reset_postdata();
return true;
}
WC_Facebookcommerce_Utils::fblog(
'Sync all products using feed, curl failed',
array(),
true
);
return false;
}
/**
* Toggles product visibility via AJAX.
*
* @internal
* @deprecated since 1.10.0-dev.1
**/
public function ajax_fb_toggle_visibility() {
wc_deprecated_function( __METHOD__, '1.10.0-dev.1' );
}
/**
* Initializes the settings form fields.
*
* @since 1.0.0
*
* @internal
*/
public function init_form_fields() {
$term_query = new \WP_Term_Query( [
'taxonomy' => 'product_cat',
'hide_empty' => false,
'fields' => 'id=>name',
] );
$product_categories = $term_query->get_terms();
$term_query = new \WP_Term_Query( [
'taxonomy' => 'product_tag',
'hide_empty' => false,
'hierarchical' => false,
'fields' => 'id=>name',
] );
$product_tags = $term_query->get_terms();
$messenger_locales = \WC_Facebookcommerce_MessengerChat::get_supported_locales();
// tries matching with WordPress locale, otherwise English, otherwise first available language
if ( isset( $messenger_locales[ get_locale() ] ) ) {
$default_locale = get_locale();
} elseif ( isset( $messenger_locales[ 'en_US' ] ) ) {
$default_locale = 'en_US';
} elseif ( ! empty( $messenger_locales ) && is_array( $messenger_locales ) ) {
$default_locale = key( $messenger_locales );
} else {
// fallback to English in case of invalid/empty filtered list of languages
$messenger_locales = [ 'en_US' => _x( 'English (United States)', 'language', 'facebook-for-woocommerce' ) ];
$default_locale = 'en_US';
}
$form_fields = [
/** @see \WC_Facebookcommerce_Integration::generate_manage_connection_title_html() */
[
'type' => 'manage_connection_title',
],
/** @see \WC_Facebookcommerce_Integration::generate_facebook_page_name_html() */
self::SETTING_FACEBOOK_PAGE_ID => [
'type' => 'facebook_page_name',
'default' => '',
],
/** @see \WC_Facebookcommerce_Integration::generate_facebook_pixel_id_html() */
self::SETTING_FACEBOOK_PIXEL_ID => [
'type' => 'facebook_pixel_id',
'default' => '',
],
self::SETTING_ENABLE_ADVANCED_MATCHING => [
'title' => __( 'Use Advanced Matching', 'facebook-for-woocommerce' ),
'description' => sprintf(
/* translators: Placeholders: %1$s - opening <a> HTML link tag, %2$s - closing </a> HTML link tag */
__( 'Improve the ability to match site visitors to people on Facebook by passing additional site visitor information (such as email address or phone number). %1$sLearn more%2$s.', 'facebook-for-woocommerce' ),
'<a href="https://developers.facebook.com/docs/facebook-pixel/advanced/advanced-matching" target="_blank">',
'</a>'
),
'type' => 'checkbox',
'label' => ' ',
'default' => 'yes',
],
/** @see \WC_Facebookcommerce_Integration::generate_create_ad_html() */
[
'type' => 'create_ad',
],
/** @see \WC_Facebookcommerce_Integration::generate_product_sync_title_html() */
[
'type' => 'product_sync_title',
],
self::SETTING_ENABLE_PRODUCT_SYNC => [
'title' => __( 'Enable product sync', 'facebook-for-woocommerce' ),
'type' => 'checkbox',
'class' => 'product-sync-field toggle-fields-group',
'label' => ' ',
'default' => 'yes',
],
self::SETTING_EXCLUDED_PRODUCT_CATEGORY_IDS => [
'title' => __( 'Exclude categories from sync', 'facebook-for-woocommerce' ),
'type' => 'multiselect',
'class' => 'wc-enhanced-select product-sync-field',
'css' => 'min-width: 300px;',
'desc_tip' => __( 'Products in one or more of these categories will not sync to Facebook.', 'facebook-for-woocommerce' ),
'default' => [],
'options' => is_array( $product_categories ) ? $product_categories : [],
'custom_attributes' => [
'data-placeholder' => __( 'Search for a product category&hellip;', 'facebook-for-woocommerce' ),
],
],
self::SETTING_EXCLUDED_PRODUCT_TAG_IDS => [
'title' => __( 'Exclude tags from sync', 'facebook-for-woocommerce' ),
'type' => 'multiselect',
'class' => 'wc-enhanced-select product-sync-field',
'css' => 'min-width: 300px;',
'desc_tip' => __( 'Products with one or more of these tags will not sync to Facebook.', 'facebook-for-woocommerce' ),
'default' => [],
'options' => is_array( $product_tags ) ? $product_tags : [],
'custom_attributes' => [
'data-placeholder' => __( 'Search for a product tag&hellip;', 'facebook-for-woocommerce' ),
],
],
self::SETTING_PRODUCT_DESCRIPTION_MODE => [
'title' => __( 'Product description sync', 'facebook-for-woocommerce' ),
'type' => 'select',
'class' => 'product-sync-field',
'desc_tip' => __( 'Choose which product description to display in the Facebook catalog.', 'facebook-for-woocommerce' ),
'default' => self::PRODUCT_DESCRIPTION_MODE_STANDARD,
'options' => [
self::PRODUCT_DESCRIPTION_MODE_STANDARD => __( 'Standard description', 'facebook-for-woocommerce' ),
self::PRODUCT_DESCRIPTION_MODE_SHORT => __( 'Short description', 'facebook-for-woocommerce' ),
],
],
/** @see \WC_Facebookcommerce_Integration::generate_resync_schedule_html() */
/** @see \WC_Facebookcommerce_Integration::validate_resync_schedule_field() */
self::SETTING_SCHEDULED_RESYNC_OFFSET => [
'title' => __( 'Force daily resync at', 'facebook-for-woocommerce' ),
'class' => 'product-sync-field resync-schedule-fieldset',
'type' => 'resync_schedule',
],
[
'title' => __( 'Messenger', 'facebook-for-woocommerce' ),
'type' => 'title',
],
self::SETTING_ENABLE_MESSENGER => [
'title' => __( 'Enable Messenger', 'facebook-for-woocommerce' ),
'type' => 'checkbox',
'class' => 'messenger-field toggle-fields-group',
'label' => ' ',
'desc_tip' => __( 'Enable and customize Facebook Messenger on your store.', 'facebook-for-woocommerce' ),
'default' => 'no',
],
self::SETTING_MESSENGER_LOCALE => [
'title' => __( 'Language', 'facebook-for-woocommerce' ),
'type' => 'select',
'class' => 'wc-enhanced-select messenger-field',
'default' => $default_locale,
'options' => $messenger_locales,
],
/** @see \WC_Facebookcommerce_Integration::generate_messenger_greeting_html() */
/** @see \WC_Facebookcommerce_Integration::validate_messenger_greeting_field() */
self::SETTING_MESSENGER_GREETING => [
'title' => __( 'Greeting', 'facebook-for-woocommerce' ),
'type' => 'messenger_greeting',
'class' => 'messenger-field',
'default' => __( "Hi! We're here to answer any questions you may have.", 'facebook-for-woocommerce' ),
'css' => 'max-width: 400px; margin-bottom: 10px',
'custom_attributes' => [
'maxlength' => $this->get_messenger_greeting_max_characters(),
],
],
self::SETTING_MESSENGER_COLOR_HEX => [
'title' => __( 'Colors', 'facebook-for-woocommerce' ),
'type' => 'color',
'class' => 'messenger-field',
'default' => '#0084ff',
'css' => 'width: 6em;',
],
];
$this->form_fields = $form_fields;
}
/**
* Gets the "Manage connection" field HTML.
*
* @see \WC_Settings_API::generate_title_html()
*
* @since 1.10.0-dev.1
*
* @param string|int $key field key or index
* @param array $args associative array of field arguments
* @return string HTML
*/
protected function generate_manage_connection_title_html( $key, array $args = [] ) {
$key = $this->get_field_key( $key );
ob_start();
?>
</table>
<h3 class="wc-settings-sub-title" id="<?php echo esc_attr( $key ); ?>">
<?php esc_html_e( 'Connection', 'facebook-for-woocommerce' ); ?>
<a
id="woocommerce-facebook-settings-manage-connection"
class="button"
href="#"
style="vertical-align: middle; margin-left: 20px;"
onclick="facebookConfig();"
><?php esc_html_e( 'Manage connection', 'facebook-for-woocommerce' ); ?></a>
</h3>
<?php if ( empty( $this->get_page_name() ) ) : ?>
<div id="connection-message-invalid">
<p style="color: #DC3232;">
<?php esc_html_e( 'Your connection has expired.', 'facebook-for-woocommerce' ); ?>
<strong>
<?php esc_html_e( 'Please click Manage connection > Advanced Options > Update Token to refresh your connection to Facebook.', 'facebook-for-woocommerce' ); ?>
</strong>
</p>
</div>
<div id="connection-message-refresh" style="display: none;">
<p>
<?php esc_html_e( 'Your access token has been updated.', 'facebook-for-woocommerce' ); ?>
<strong>
<?php esc_html_e( 'Please refresh the page.', 'facebook-for-woocommerce' ); ?>
</strong>
</p>
</div>
<?php endif; ?>
<table class="form-table">
<?php
return ob_get_clean();
}
/**
* Gets the "Facebook page" field HTML.
*
* @see \WC_Settings_API::generate_settings_html()
*
* @since 1.10.0-dev.1
*
* @param string|int $key field key or index
* @param array $args associative array of field arguments
* @return string HTML
*/
protected function generate_facebook_page_name_html( $key, array $args = [] ) {
$key = $this->get_field_key( $key );
$page_name = $this->get_page_name();
$page_url = $this->get_page_url();
ob_start();
?>
<tr valign="top">
<th scope="row" class="titledesc">
<?php esc_html_e( 'Facebook page', 'facebook-for-woocommerce' ); ?>
</th>
<td class="forminp">
<?php if ( $page_name ) : ?>
<?php if ( $page_url ) : ?>
<a
href="<?php echo esc_url( $page_url ); ?>"
target="_blank"
style="text-decoration: none;">
<?php echo esc_html( $page_name ); ?>
<span
class="dashicons dashicons-external"
style="margin-right: 8px; vertical-align: bottom;"
></span>
</a>
<?php else : ?>
<?php echo esc_html( $page_name ); ?>
<?php endif; ?>
<?php else : ?>
&mdash;
<?php endif; ?>
<input
type="hidden"
name="<?php echo esc_attr( $key ); ?>"
id="<?php echo esc_attr( $key ); ?>"
value="<?php echo esc_attr( $this->get_facebook_page_id() ); ?>"
/>
</td>
</tr>
<?php
return ob_get_clean();
}
/**
* Gets the "Facebook Pixel" field HTML.
*
* @see \WC_Settings_API::generate_settings_html()
*
* @since 1.10.0-dev.1
*
* @param string|int $key field key or index
* @param array $args associative array of field arguments
* @return string HTML
*/
protected function generate_facebook_pixel_id_html( $key, array $args = [] ) {
$key = $this->get_field_key( $key );
$pixel_id = $this->get_facebook_pixel_id();
ob_start();
?>
<tr valign="top">
<th scope="row" class="titledesc">
<?php esc_html_e( 'Pixel', 'facebook-for-woocommerce' ); ?>
</th>
<td class="forminp">
<?php if ( $pixel_id ) : ?>
<code style="padding: 4px 8px; color: #333;"><?php echo esc_html( $pixel_id ); ?></code>
<?php else : ?>
&mdash;
<?php endif; ?>
<input
type="hidden"
name="<?php echo esc_attr( $key ); ?>"
id="<?php echo esc_attr( $key ); ?>"
value="<?php echo esc_attr( $pixel_id ); ?>"
/>
</td>
</tr>
<?php
return ob_get_clean();
}
/**
* Gets the "Create ad" field HTML.
*
* @see \WC_Settings_API::generate_settings_html()
*
* @since 1.10.0-dev.1
*
* @param string|int $key field key or index
* @param array $args associative array of field arguments
* @return string HTML
*/
protected function generate_create_ad_html( $key, array $args = [] ) {
$create_ad_url = sprintf( 'https://www.facebook.com/ads/dia/redirect/?settings_id=%s&version=2&entry_point=admin_panel', rawurlencode( $this->get_external_merchant_settings_id() ) );
ob_start();
?>
<tr valign="top">
<th class="forminp" colspan="2">
<a
class="button button-primary"
target="_blank"
href="<?php echo esc_url( $create_ad_url ); ?>"
><?php esc_html_e( 'Create ad', 'facebook-for-woocommerce' ); ?></a>
</th>
</tr>
<?php
return ob_get_clean();
}
/**
* Gets the "Sync products" field HTML.
*
* @see \WC_Settings_API::generate_title_html()
*
* @since 1.10.0-dev.1
*
* @param string|int $key field key or index
* @param array $args associative array of field arguments
* @return string HTML
*/
protected function generate_product_sync_title_html( $key, array $args = [] ) {
$key = $this->get_field_key( $key );
ob_start();
?>
</table>
<h3 class="wc-settings-sub-title" id="<?php echo esc_attr( $key ); ?>">
<?php esc_html_e( 'Product sync', 'facebook-for-woocommerce' ); ?>
<a
id="woocommerce-facebook-settings-sync-products"
class="button product-sync-field"
href="#"
style="vertical-align: middle; margin-left: 20px;"
><?php esc_html_e( 'Sync products', 'facebook-for-woocommerce' ); ?></a>
<span id="sync_progress" style="margin-left: 10px"></span>
</h3>
<table class="form-table">
<?php
return ob_get_clean();
}
/**
* Processes and saves options.
*
* @see \WC_Settings_API::process_admin_options()
*
* @internal
*
* @since 1.10.0-dev.1
*/
public function process_admin_options() {
$current_resync_offset = $this->get_scheduled_resync_offset();
parent::process_admin_options();
$saved_resync_offset = $this->get_scheduled_resync_offset();
if ( null === $saved_resync_offset || ! $this->is_product_sync_enabled() ) {
$this->unschedule_resync();
} elseif ( $saved_resync_offset !== $current_resync_offset || false === $this->is_resync_scheduled() ) {
$this->schedule_resync( $saved_resync_offset );
}
}
/**
* Generates the force resync fieldset HTML.
*
* @see \WC_Settings_API::generate_settings_html()
*
* @since 1.10.0-dev.1
*
* @param string $key field key
* @param array $data field data
* @return string HTML
*/
protected function generate_resync_schedule_html( $key, array $data ) {
$fieldset_key = $this->get_field_key( $key );
$enabled_field_key = $this->get_field_key( 'scheduled_resync_enabled' );
$hours_field_key = $this->get_field_key( 'scheduled_resync_hours' );
$minutes_field_key = $this->get_field_key( 'scheduled_resync_minutes' );
$meridiem_field_key = $this->get_field_key( 'scheduled_resync_meridiem' );
// check if the sites uses 12-hours or 24-hours time format
$time_format = wc_time_format();
// TODO replace these string search functions with Framework string helpers {FN 2020-01-31}
$is_12_hours = ( false !== strpos( $time_format, 'g' ) || false !== strpos( $time_format, 'h' ) );
// default to 24-hours format if no hour specifier is found on the string
$is_24_hours = ! $is_12_hours;
if ( $this->is_scheduled_resync_enabled() ) {
try {
$offset = $this->get_scheduled_resync_offset();
$resync_time = ( new DateTime( 'today' ) )->add( new DateInterval( "PT${offset}S" ) );
$resync_hours = $is_24_hours ? $resync_time->format( 'G' ) : $resync_time->format( 'g' );
$resync_minutes = $resync_time->format( 'i' );
} catch ( \Exception $e ) {}
}
// default to 23:00
if ( empty( $resync_hours ) ) {
$resync_hours = $is_24_hours ? '23' : '11';
$resync_minutes = '00';
$resync_meridiem = $is_24_hours ? '' : 'pm';
}
$defaults = [
'title' => '',
'disabled' => false,
'class' => '',
'css' => '',
'desc_tip' => false,
];
$data = wp_parse_args( $data, $defaults );
ob_start();
?>
<tr valign="top">
<th scope="row" class="titledesc">
<label for="<?php echo esc_attr( $fieldset_key ); ?>"><?php echo wp_kses_post( $data['title'] ); ?> <?php echo $this->get_tooltip_html( $data ); ?></label>
</th>
<td class="forminp">
<fieldset class="<?php echo esc_attr( $data['class'] ); ?>">
<legend class="screen-reader-text"><span><?php echo wp_kses_post( $data['title'] ); ?></span></legend>
<input
class="toggle-fields-group resync-schedule-field"
<?php disabled( $data['disabled'], true ); ?>
type="checkbox"
name="<?php echo esc_attr( $enabled_field_key ); ?>"
id="<?php echo esc_attr( $enabled_field_key ); ?>"
style="<?php echo esc_attr( $data['css'] ); ?>"
value="1"
<?php checked( $this->is_scheduled_resync_enabled() ); ?>
/>
<input
class="input-number regular-input resync-schedule-field"
type="number"
min="0"
max="<?php echo $is_24_hours ? 24 : 12; ?>"
name="<?php echo esc_attr( $hours_field_key ); ?>"
id="<?php echo esc_attr( $hours_field_key ); ?>"
style="<?php echo esc_attr( $data['css'] ); ?>"
value="<?php echo ! empty( $resync_hours ) ? esc_attr( $resync_hours ) : ''; ?>"
<?php disabled( $data['disabled'], true ); ?>
/>
<strong>:</strong>
<input
class="input-number regular-input resync-schedule-field"
type="number"
min="0"
max="59"
name="<?php echo esc_attr( $minutes_field_key ); ?>"
id="<?php echo esc_attr( $minutes_field_key ); ?>"
style="<?php echo esc_attr( $data['css'] ); ?>"
value="<?php echo ! empty( $resync_minutes ) ? esc_attr( $resync_minutes ) : ''; ?>"
<?php disabled( $data['disabled'], true ); ?>
/>
<?php if ( ! $is_24_hours ) : ?>
<select
class="resync-schedule-field"
name="<?php echo esc_attr( $meridiem_field_key ); ?>"
id="<?php echo esc_attr( $meridiem_field_key ); ?>"
style="<?php echo esc_attr( $data['css'] ); ?>"
<?php disabled( $data['disabled'], true ); ?>>
<option
<?php selected( true, $this->get_scheduled_resync_offset() < 12 * HOUR_IN_SECONDS, true ); ?>
value="am">
<?php esc_html_e( 'am', 'facebook-for-woocommerce' ); ?>
</option>
<option
<?php selected( true, $this->get_scheduled_resync_offset() >= 12 * HOUR_IN_SECONDS || ( ! empty( $resync_meridiem ) && 'pm' === $resync_meridiem ), true ); ?>
value="pm">
<?php esc_html_e( 'pm', 'facebook-for-woocommerce' ); ?>
</option>
</select>
<?php endif; ?>
<br/>
</fieldset>
</td>
</tr>
<?php
return ob_get_clean();
}
/**
* Validates force resync field.
*
* @internal
*
* @since 1.10.0-dev.1
*
* @param string $key field key
* @param string $value posted value
* @return int|string timestamp or empty string
* @throws \Exception
*/
public function validate_resync_schedule_field( $key, $value ) {
$enabled_field_key = $this->get_field_key( 'scheduled_resync_enabled' );
$hours_field_key = $this->get_field_key( 'scheduled_resync_hours' );
$minutes_field_key = $this->get_field_key( 'scheduled_resync_minutes' );
$meridiem_field_key = $this->get_field_key( 'scheduled_resync_meridiem' );
// if not enabled or time is empty, return a blank string
if ( empty( $_POST[ $enabled_field_key ] ) || empty( $_POST[ $hours_field_key ] ) ) {
return '';
}
$posted_hours = (int) sanitize_text_field( wp_unslash( $_POST[ $hours_field_key ] ) );
$posted_minutes = (int) sanitize_text_field( wp_unslash( $_POST[ $minutes_field_key ] ) );
$posted_minutes = str_pad( $posted_minutes, 2, '0', STR_PAD_LEFT );
$posted_meridiem = ! empty( $_POST[ $meridiem_field_key ] ) ? sanitize_text_field( wp_unslash( $_POST[ $meridiem_field_key ] ) ) : '';
// attempts to parse the time (not using date_create_from_format because it considers 30:00 to be a valid time)
$parsed_time = strtotime( "$posted_hours:$posted_minutes $posted_meridiem" );
if ( false === $parsed_time ) {
throw new \Exception( "Invalid resync schedule time: $posted_hours:$posted_minutes $posted_meridiem" );
}
$midnight = ( new DateTime() )->setTimestamp( $parsed_time )->setTime( 0,0,0 );
return $parsed_time - $midnight->getTimestamp();
}
/**
* Gets the "Messenger greeting" field HTML.
*
* @see \WC_Settings_API::generate_textarea_html()
*
* @since 1.10.0-dev.1
*
* @param string|int $key field key or index
* @param array $args associative array of field arguments
* @return string HTML
*/
protected function generate_messenger_greeting_html( $key, array $args = [] ) {
// TODO replace strlen() here with Framework helper method to account for multibyte characters {FN 2020-01-30}
$chars = max( 0, strlen( $this->get_messenger_greeting() ) );
$max_chars = max( 0, $this->get_messenger_greeting_max_characters() );
$field_id = $this->get_field_key( $key );
$counter_class = $field_id . '-characters-count';
wc_enqueue_js( "
jQuery( document ).ready( function( $ ) {
$( 'span." . esc_js( $counter_class ) . "' ).insertAfter( 'textarea#" . esc_js( $field_id ) . "' );
} );
" );
ob_start();
?>
<span
style="display: none; font-family: monospace; font-size: 0.9em;"
class="<?php echo sanitize_html_class( $counter_class ); ?> characters-counter"
><?php echo esc_html( $chars . ' / ' . $max_chars ); ?> <span style="display:none;"><?php echo esc_html( $this->get_messenger_greeting_long_warning_text() ); ?></span></span>
<?php
$counter = ob_get_clean();
return $this->generate_textarea_html( $key, $args ) . $counter;
}
/**
* Validates the Messenger greeting field.
*
* @see \WC_Settings_API::validate_textarea_field()
*
* @since 1.10.0-dev.1
*
* @param string|int $key field key or index
* @param string $value field submitted value
* @throws \Exception on validation errors
* @return string some HTML allowed
*/
protected function validate_messenger_greeting_field( $key, $value ) {
$value = is_string( $value ) ? trim( sanitize_text_field( wp_unslash( $value ) ) ) : '';
$max_chars = $this->get_messenger_greeting_max_characters();
$value_length = function_exists( 'mb_strlen' ) ? mb_strlen( $value, Framework\SV_WC_Helper::MB_ENCODING ) : strlen( $value );
if ( $value_length > $max_chars ) {
throw new Framework\SV_WC_Plugin_Exception( sprintf(
$this->get_messenger_greeting_long_warning_text() . ' %s',
__( "The greeting hasn't been updated.", 'facebook-for-woocommerce' )
) );
}
return $value;
}
/**
* Gets a warning text to be displayed when the Messenger greeting text exceeds the maximum length.
*
* @since 1.10.0-dev.1
*
* @return string
*/
private function get_messenger_greeting_long_warning_text() {
return sprintf(
/* translators: Placeholder: %d - maximum number of allowed characters */
__( 'The Messenger greeting must be %d characters or less.', 'facebook-for-woocommerce' ),
$this->get_messenger_greeting_max_characters()
);
}
/** Getter methods ************************************************************************************************/
/**
* Gets the page access token.
*
* @since 1.10.0-dev.1
*
* @return string
*/
public function get_page_access_token() {
if ( ! is_string( $this->page_access_token ) ) {
$value = get_option( self::OPTION_PAGE_ACCESS_TOKEN, '' );
$this->page_access_token = is_string( $value ) ? $value : '';
}
/**
* Filters the Facebook page access token.
*
* @since 1.10.0-dev.1
*
* @param string $page_access_token Facebook page access token
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (string) apply_filters( 'wc_facebook_page_access_token', $this->page_access_token, $this );
}
/**
* Gets the product catalog ID.
*
* @since 1.10.0-dev.1
*
* @return string
*/
public function get_product_catalog_id() {
if ( ! is_string( $this->product_catalog_id ) ) {
$value = get_option( self::OPTION_PRODUCT_CATALOG_ID, '' );
$this->product_catalog_id = is_string( $value ) ? $value : '';
}
/**
* Filters the Facebook product catalog ID.
*
* @since 1.10.0-dev.1
*
* @param string $product_catalog_id Facebook product catalog ID
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (string) apply_filters( 'wc_facebook_product_catalog_id', $this->product_catalog_id, $this );
}
/**
* Gets the external merchant settings ID.
*
* @since 1.10.0-dev.1
*
* @return string
*/
public function get_external_merchant_settings_id() {
if ( ! is_string( $this->external_merchant_settings_id ) ) {
$value = get_option( self::OPTION_EXTERNAL_MERCHANT_SETTINGS_ID, '' );
$this->external_merchant_settings_id = is_string( $value ) ? $value : '';
}
/**
* Filters the Facebook external merchant settings ID.
*
* @since 1.10.0-dev.1
*
* @param string $external_merchant_settings_id Facebook external merchant settings ID
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (string) apply_filters( 'wc_facebook_external_merchant_settings_id', $this->external_merchant_settings_id, $this );
}
/**
* Gets the feed ID.
*
* @since 1.10.0-dev.1
*
* @return string
*/
public function get_feed_id() {
if ( ! is_string( $this->feed_id ) ) {
$value = get_option( self::OPTION_FEED_ID, '' );
$this->feed_id = is_string( $value ) ? $value : '';
}
/**
* Filters the Facebook feed ID.
*
* @since 1.10.0-dev.1
*
* @param string $feed_id Facebook feed ID
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (string) apply_filters( 'wc_facebook_feed_id', $this->feed_id, $this );
}
/**
* Gets the Facebook pixel install time in UTC seconds.
*
* @since 1.10.0-dev.1
*
* @return int
*/
public function get_pixel_install_time() {
if ( ! (int) $this->pixel_install_time ) {
$value = (int) get_option( self::OPTION_PIXEL_INSTALL_TIME, 0 );
$this->pixel_install_time = $value ?: null;
}
/**
* Filters the Facebook pixel install time.
*
* @since 1.10.0-dev.1
*
* @param string $pixel_install_time Facebook pixel install time in UTC seconds, or null if none set
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (int) apply_filters( 'wc_facebook_pixel_install_time', $this->pixel_install_time, $this );
}
/**
* Gets the configured JS SDK version.
*
* @since 1.10.0-dev.1
*
* @return string
*/
public function get_js_sdk_version() {
if ( ! is_string( $this->js_sdk_version ) ) {
$value = get_option( self::OPTION_JS_SDK_VERSION, '' );
$this->js_sdk_version = is_string( $value ) ? $value : '';
}
/**
* Filters the Facebook JS SDK version.
*
* @since 1.10.0-dev.1
*
* @param string $js_sdk_version Facebook JS SDK version
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (string) apply_filters( 'wc_facebook_js_sdk_version', $this->js_sdk_version, $this );
}
/**
* Gets the configured Facebook page ID.
*
* @since 1.10.0-dev.1
*
* @return string
*/
public function get_facebook_page_id() {
/**
* Filters the configured Facebook page ID.
*
* @since 1.10.0-dev.1
*
* @param string $page_id the configured Facebook page ID
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (string) apply_filters( 'wc_facebook_page_id', $this->get_option( self::SETTING_FACEBOOK_PAGE_ID, '' ), $this );
}
/**
* Gets the configured Facebook pixel ID.
*
* @since 1.10.0-dev.1
*
* @return string
*/
public function get_facebook_pixel_id() {
/**
* Filters the configured Facebook pixel ID.
*
* @since 1.10.0-dev.1
*
* @param string $pixel_id the configured Facebook pixel ID
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (string) apply_filters( 'wc_facebook_pixel_id', $this->get_option( self::SETTING_FACEBOOK_PIXEL_ID, '' ), $this );
}
/**
* Gets the IDs of the categories to be excluded from sync.
*
* @since 1.10.0-dev.1
*
* @return int[]
*/
public function get_excluded_product_category_ids() {
/**
* Filters the configured excluded product category IDs.
*
* @since 1.10.0-dev.1
*
* @param int[] $category_ids the configured excluded product category IDs
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (array) apply_filters( 'wc_facebook_excluded_product_category_ids', $this->get_option( self::SETTING_EXCLUDED_PRODUCT_CATEGORY_IDS, [] ), $this );
}
/**
* Gets the IDs of the tags to be excluded from sync.
*
* @since 1.10.0-dev.1
*
* @return int[]
*/
public function get_excluded_product_tag_ids() {
/**
* Filters the configured excluded product tag IDs.
*
* @since 1.10.0-dev.1
*
* @param int[] $tag_ids the configured excluded product tag IDs
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (array) apply_filters( 'wc_facebook_excluded_product_tag_ids', $this->get_option( self::SETTING_EXCLUDED_PRODUCT_TAG_IDS, [] ), $this );
}
/**
* Gets the configured product description mode.
*
* @since 1.10.0-dev.1
*
* @return string
*/
public function get_product_description_mode() {
/**
* Filters the configured product description mode.
*
* @since 1.10.0-dev.1
*
* @param string $mode the configured product description mode
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
$mode = (string) apply_filters( 'wc_facebook_product_description_mode', $this->get_option( self::SETTING_PRODUCT_DESCRIPTION_MODE, self::PRODUCT_DESCRIPTION_MODE_STANDARD ), $this );
$valid_modes = [
self::PRODUCT_DESCRIPTION_MODE_STANDARD,
self::PRODUCT_DESCRIPTION_MODE_SHORT,
];
if ( ! in_array( $mode, $valid_modes, true ) ) {
$mode = self::PRODUCT_DESCRIPTION_MODE_STANDARD;
}
return $mode;
}
/**
* Gets the configured scheduled re-sync offset in seconds.
*
* Returns null if no offset is configured.
*
* @since 1.10.0-dev.1
*
* @return int|null
*/
public function get_scheduled_resync_offset() {
/**
* Filters the configured scheduled re-sync offset.
*
* @since 1.10.0-dev.1
*
* @param int|null $offset the configured scheduled re-sync offset in seconds
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
$offset = (int) apply_filters( 'wc_facebook_scheduled_resync_offset', $this->get_option( self::SETTING_SCHEDULED_RESYNC_OFFSET, null ), $this );
if ( ! $offset ) {
$offset = null;
}
return $offset;
}
/**
* Gets the configured Facebook messenger locale.
*
* @since 1.10.0-dev.1
*
* @return string
*/
public function get_messenger_locale() {
/**
* Filters the configured Facebook messenger locale.
*
* @since 1.10.0-dev.1
*
* @param string $locale the configured Facebook messenger locale
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (string) apply_filters( 'wc_facebook_messenger_locale', $this->get_option( self::SETTING_MESSENGER_LOCALE, 'en_US' ), $this );
}
/**
* Gets the configured Facebook messenger greeting.
*
* @since 1.10.0-dev.1
*
* @return string
*/
public function get_messenger_greeting() {
/**
* Filters the configured Facebook messenger greeting.
*
* @since 1.10.0-dev.1
*
* @param string $greeting the configured Facebook messenger greeting
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
$greeting = (string) apply_filters( 'wc_facebook_messenger_greeting', $this->get_option( self::SETTING_MESSENGER_GREETING, '' ), $this );
// TODO: update to SV_WC_Helper::str_truncate() when frameworked {CW 2020-01-22}
return substr( $greeting, 0, $this->get_messenger_greeting_max_characters() );
}
/**
* Gets the maximum number of characters allowed in the messenger greeting.
*
* @since 1.10.0-dev.1
*
* @return int
*/
public function get_messenger_greeting_max_characters() {
$default = 80;
/**
* Filters the maximum number of characters allowed in the messenger greeting.
*
* @since 1.10.0-dev.1
*
* @param int $max the maximum number of characters allowed in the messenger greeting
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
$max = (int) apply_filters( 'wc_facebook_messenger_greeting_max_characters', $default, $this );
return $max < 1 ? $default : $max;
}
/**
* Gets the configured Facebook messenger color hex.
*
* This is used to style the messenger UI.
*
* @since 1.10.0-dev.1
*
* @return string
*/
public function get_messenger_color_hex() {
/**
* Filters the configured Facebook messenger color hex.
*
* @since 1.10.0-dev.1
*
* @param string $hex the configured Facebook messenger color hex
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (string) apply_filters( 'wc_facebook_messenger_color_hex', $this->get_option( self::SETTING_MESSENGER_COLOR_HEX, '' ), $this );
}
/** Setter methods ************************************************************************************************/
/**
* Updates the Facebook page access token.
*
* @since 1.10.0-dev.1
*
* @param string $value page access token value
*/
public function update_page_access_token( $value ) {
$this->page_access_token = $this->sanitize_facebook_credential( $value );
update_option( self::OPTION_PAGE_ACCESS_TOKEN, $this->page_access_token );
}
/**
* Updates the Facebook product catalog ID.
*
* @since 1.10.0-dev.1
*
* @param string $value product catalog ID value
*/
public function update_product_catalog_id( $value ) {
$this->product_catalog_id = $this->sanitize_facebook_credential( $value );
update_option( self::OPTION_PRODUCT_CATALOG_ID, $this->product_catalog_id );
}
/**
* Updates the Facebook external merchant settings ID.
*
* @since 1.10.0-dev.1
*
* @param string $value external merchant settings ID value
*/
public function update_external_merchant_settings_id( $value ) {
$this->external_merchant_settings_id = $this->sanitize_facebook_credential( $value );
update_option( self::OPTION_EXTERNAL_MERCHANT_SETTINGS_ID, $this->external_merchant_settings_id );
}
/**
* Updates the Facebook feed ID.
*
* @since 1.10.0-dev.1
*
* @param string $value feed ID value
*/
public function update_feed_id( $value ) {
$this->feed_id = $this->sanitize_facebook_credential( $value );
update_option( self::OPTION_FEED_ID, $this->feed_id );
}
/**
* Updates the Facebook pixel install time.
*
* @since 1.10.0-dev.1
*
* @param int $value pixel install time, in UTC seconds
*/
public function update_pixel_install_time( $value ) {
$value = (int) $value;
$this->pixel_install_time = $value ?: null;
update_option( self::OPTION_PIXEL_INSTALL_TIME, $value ?: '' );
}
/**
* Updates the Facebook JS SDK version.
*
* @since 1.10.0-dev.1
*
* @param string $value JS SDK version
*/
public function update_js_sdk_version( $value ) {
$this->js_sdk_version = $this->sanitize_facebook_credential( $value );
update_option( self::OPTION_JS_SDK_VERSION, $this->js_sdk_version );
}
/**
* Sanitizes a value that's a Facebook credential.
*
* @since 1.10.0-dev.1
*
* @param string $value value to sanitize
* @return string
*/
private function sanitize_facebook_credential( $value ) {
return wc_clean( is_string( $value ) ? $value : '' );
}
/** Conditional methods *******************************************************************************************/
/**
* Determines whether Facebook for WooCommerce is configured.
*
* @since 1.10.0-dev.1
*
* @return bool
*/
public function is_configured() {
return $this->get_page_access_token() && $this->get_facebook_page_id();
}
/**
* Determines whether advanced matching is enabled.
*
* @since 1.10.0-dev.1
*
* @return bool
*/
public function is_advanced_matching_enabled() {
/**
* Filters whether advanced matching is enabled.
*
* @since 1.10.0-dev.1
*
* @param bool $is_enabled whether advanced matching is enabled
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (bool) apply_filters( 'wc_facebook_is_advanced_matching_enabled', 'yes' === $this->get_option( self::SETTING_ENABLE_ADVANCED_MATCHING ), $this );
}
/**
* Determines whether product sync is enabled.
*
* @since 1.10.0-dev.1
*
* @return bool
*/
public function is_product_sync_enabled() {
/**
* Filters whether product sync is enabled.
*
* @since 1.10.0-dev.1
*
* @param bool $is_enabled whether product sync is enabled
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (bool) apply_filters( 'wc_facebook_is_product_sync_enabled', 'yes' === $this->get_option( self::SETTING_ENABLE_PRODUCT_SYNC ), $this );
}
/**
* Determines whether the scheduled re-sync is enabled.
*
* @since 1.10.0-dev.1
*
* @return bool
*/
public function is_scheduled_resync_enabled() {
/**
* Filters whether the scheduled re-sync is enabled.
*
* @since 1.10.0-dev.1
*
* @param bool $is_enabled whether the scheduled re-sync is enabled
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (bool) apply_filters( 'wc_facebook_is_scheduled_resync_enabled', $this->get_scheduled_resync_offset(), $this );
}
/**
* Determines whether the Facebook messenger is enabled.
*
* @since 1.10.0-dev.1
*
* @return bool
*/
public function is_messenger_enabled() {
/**
* Filters whether the Facebook messenger is enabled.
*
* @since 1.10.0-dev.1
*
* @param bool $is_enabled whether the Facebook messenger is enabled
* @param \WC_Facebookcommerce_Integration $integration the integration instance
*/
return (bool) apply_filters( 'wc_facebook_is_messenger_enabled', 'yes' === $this->get_option( self::SETTING_ENABLE_MESSENGER ), $this );
}
/**
* Gets message HTML.
*
* @return string
*/
private function get_message_html( $message, $type = 'error' ) {
ob_start();
?>
<div class="notice is-dismissible notice-<?php echo esc_attr( $type ); ?>">
<p>
<?php
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $message;
?>
</p>
</div>
<?php
return ob_get_clean();
}
/**
* Displays relevant messages to user from transients, clear once displayed.
*/
public function maybe_display_facebook_api_messages() {
$error_msg = get_transient( 'facebook_plugin_api_error' );
if ( $error_msg ) {
$message = sprintf(
__(
'Facebook extension error: %s ',
'facebook-for-woocommerce'
),
$error_msg
);
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $this->get_message_html( $message );
delete_transient( 'facebook_plugin_api_error' );
WC_Facebookcommerce_Utils::fblog(
$error_msg,
[],
true
);
}
$warning_msg = get_transient( 'facebook_plugin_api_warning' );
if ( $warning_msg ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $this->get_message_html( $warning_msg, 'warning' );
delete_transient( 'facebook_plugin_api_warning' );
}
$success_msg = get_transient( 'facebook_plugin_api_success' );
if ( $success_msg ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $this->get_message_html( $success_msg, 'success' );
delete_transient( 'facebook_plugin_api_success' );
}
$info_msg = get_transient( 'facebook_plugin_api_info' );
if ( $info_msg ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $this->get_message_html( $info_msg, 'info' );
delete_transient( 'facebook_plugin_api_info' );
}
$sticky_msg = get_transient( 'facebook_plugin_api_sticky' );
if ( $sticky_msg ) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $this->get_message_html( $sticky_msg, 'info' );
// transient must be deleted elsewhere, or wait for timeout
}
}
/**
* Gets the name of the configured Facebook page.
*
* @return string
*/
public function get_page_name() {
if ( $this->is_configured() ) {
$page_name = $this->fbgraph->get_page_name( $this->get_facebook_page_id(), $this->get_page_access_token() );
} else {
$page_name = '';
}
return is_string( $page_name ) ? $page_name : '';
}
/**
* Gets the Facebook page URL.
*
* @since 1.10.0-dev.1
*
* @return string
*/
public function get_page_url() {
if ( $this->is_configured() ) {
$page_url = $this->fbgraph->get_page_url( $this->get_facebook_page_id(), $this->get_page_access_token() );
} else {
$page_url = '';
}
return is_string( $page_url ) ? $page_url : '';
}
/**
* Gets Messenger or Instagram tooltip message.
*
* @return string
*/
function get_nux_message_ifexist() {
$nux_type_to_elemid_map = [
'messenger_chat' => 'connect_button',
'instagram_shopping' => 'connect_button',
];
$nux_type_to_message_map = [
'messenger_chat' => __( 'Get started with Messenger Customer Chat' ),
'instagram_shopping' => __( 'Get started with Instagram Shopping' ),
];
$message = '';
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( isset( $_GET['nux'] ) ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$nux_type = sanitize_text_field( wp_unslash( $_GET['nux'] ) );
ob_start();
?>
<div class="nux-message" style="display: none;"
data-target="<?php echo esc_attr( $nux_type_to_elemid_map[ $nux_type ] ); ?>">
<div class="nux-message-text">
<?php echo esc_attr( $nux_type_to_message_map[ $nux_type ] ); ?>
</div>
<div class="nux-message-arrow"></div>
<i class="nux-message-close-btn">x</i>
</div>
<script>( function () { fbe_init_nux_messages(); } )();</script>
<?php
$message = ob_get_clean();
}
return $message;
}
/**
* Admin Panel Options
*/
function admin_options() {
$page_name = $this->get_page_name();
$can_manage = current_user_can( 'manage_woocommerce' );
$pre_setup = empty( $this->get_facebook_page_id() ) || empty( $this->get_page_access_token() );
$apikey_invalid = ! $pre_setup && $this->get_page_access_token() && ! $page_name;
$remove_http_active = is_plugin_active( 'remove-http/remove-http.php' );
$https_will_be_stripped = $remove_http_active && ! get_option( 'factmaven_rhttp' )['external'];
if ( $https_will_be_stripped ) {
$this->display_sticky_message(
__( 'You\'re using Remove HTTP which has incompatibilities with our extension. Please disable it, or select the "Ignore external links" option on the Remove HTTP settings page.' )
);
}
?>
<h2><?php esc_html_e( 'Facebook', 'facebook-for-woocommerce' ); ?></h2>
<p>
<?php esc_html_e( 'Control how WooCommerce integrates with your Facebook store.', 'facebook-for-woocommerce' ); ?>
</p>
<div><input type="hidden" name="section" value="<?php echo esc_attr( $this->id ); ?>" /></div>
<div id="fbsetup" <?php echo $this->is_configured() ? 'style="display: none"' : ''; ?>>
<div class="wrapper">
<header>
<div class="help-center">
<a href="https://www.facebook.com/business/help/900699293402826" target="_blank">Help Center <i class="help-center-icon"></i></a>
</div>
</header>
<div class="content">
<h1 id="setup_h1"><?php esc_html_e( 'Grow your business on Facebook', 'facebook-for-woocommerce' ); ?></h1>
<h2>
<?php esc_html_e( 'Use this WooCommerce and Facebook integration to:', 'facebook-for-woocommerce' ); ?>
</h2>
<ul>
<li id="setup_l1"><?php esc_html_e( 'Easily install a tracking pixel', 'facebook-for-woocommerce' ); ?></li>
<li id="setup_l2"><?php esc_html_e( 'Upload your products and create a shop', 'facebook-for-woocommerce' ); ?></li>
<li id="setup_l3"><?php esc_html_e( 'Create dynamic ads with your products and pixel', 'facebook-for-woocommerce' ); ?></li>
</ul>
<span
<?php $external_merchant_settings_id = $this->get_external_merchant_settings_id(); ?>
<?php echo ( ! $can_manage || $apikey_invalid || ! isset( $external_merchant_settings_id ) ) ? ' style="pointer-events: none;"' : ''; ?>>
<a href="#" class="btn pre-setup" onclick="facebookConfig()" id="cta_button">
<?php esc_html_e( 'Get Started', 'facebook-for-woocommerce' ); ?>
</a>
</span>
<?php
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $this->get_nux_message_ifexist();
?>
</div>
</div>
</div>
<div id="integration-settings" <?php echo ! $this->is_configured() ? 'style="display: none"' : ''; ?>>
<table class="form-table"><?php $this->generate_settings_html( $this->get_form_fields() ); ?></table>
</div>
<?php
}
function delete_product_item( $wp_id ) {
$fb_product_item_id = $this->get_product_fbid(
self::FB_PRODUCT_ITEM_ID,
$wp_id
);
if ( $fb_product_item_id ) {
$pi_result =
$this->fbgraph->delete_product_item( $fb_product_item_id );
WC_Facebookcommerce_Utils::log( $pi_result );
}
}
function fb_duplicate_product_reset_meta( $to_delete ) {
array_push( $to_delete, self::FB_PRODUCT_ITEM_ID );
array_push( $to_delete, self::FB_PRODUCT_GROUP_ID );
return $to_delete;
}
/**
* Helper function to update FB visibility.
*/
function update_fb_visibility( $wp_id, $visibility ) {
$woo_product = new WC_Facebook_Product( $wp_id );
if ( ! $woo_product->exists() ) {
// This function can be called for non-woo products.
return;
}
$products = WC_Facebookcommerce_Utils::get_product_array( $woo_product );
foreach ( $products as $item_id ) {
$fb_product_item_id = $this->get_product_fbid(
self::FB_PRODUCT_ITEM_ID,
$item_id
);
if ( ! $fb_product_item_id ) {
WC_Facebookcommerce_Utils::fblog(
$fb_product_item_id . " doesn't exist but underwent a visibility transform.",
array(),
true
);
continue;
}
$result = $this->check_api_result(
$this->fbgraph->update_product_item(
$fb_product_item_id,
array( 'visibility' => $visibility )
)
);
if ( $result ) {
$is_visible = $visibility === self::FB_SHOP_PRODUCT_VISIBLE;
update_post_meta( $item_id, Products::VISIBILITY_META_KEY, wc_bool_to_string( $is_visible ) );
update_post_meta( $wp_id, Products::VISIBILITY_META_KEY, wc_bool_to_string( $is_visible ) );
}
}
}
function on_quick_and_bulk_edit_save( $product ) {
// bail if not a product or product is not enabled for sync
if ( ! $product instanceof \WC_Product || ! Products::is_sync_enabled_for_product( $product ) ) {
return;
}
$wp_id = $product->get_id();
$visibility = get_post_status( $wp_id ) === 'publish'
? self::FB_SHOP_PRODUCT_VISIBLE
: self::FB_SHOP_PRODUCT_HIDDEN;
// case 1: new status is 'publish' regardless of old status, sync to FB
if ( $visibility === self::FB_SHOP_PRODUCT_VISIBLE ) {
$this->on_product_publish( $wp_id );
} else {
// case 2: product never publish to FB, new status is not publish
// case 3: product new status is not publish and published before
$this->update_fb_visibility( $wp_id, $visibility );
}
}
/**
* Gets Facebook product ID from meta or from Facebook API.
*
* @param string $fbid_type ID type (group or item)
* @param int $wp_id post ID
* @param WC_Facebook_Product|null $woo_product product
* @return mixed|void|null
*/
public function get_product_fbid( $fbid_type, $wp_id, $woo_product = null ) {
$fb_id = WC_Facebookcommerce_Utils::get_fbid_post_meta(
$wp_id,
$fbid_type
);
if ( $fb_id ) {
return $fb_id;
}
if ( ! isset( $this->settings['upload_end_time'] ) ) {
return null;
}
if ( ! $woo_product ) {
$woo_product = new WC_Facebook_Product( $wp_id );
}
$products = WC_Facebookcommerce_Utils::get_product_array( $woo_product );
$woo_product = new WC_Facebook_Product( current( $products ) );
// This is a generalized function used elsewhere
// Cannot call is_hidden for VC_Product_Variable Object
if ( $woo_product->is_hidden() ) {
return null;
}
$fb_retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id( $woo_product );
$product_fbid_result = $this->fbgraph->get_facebook_id(
$this->get_product_catalog_id(),
$fb_retailer_id
);
if ( is_wp_error( $product_fbid_result ) ) {
WC_Facebookcommerce_Utils::log( $product_fbid_result->get_error_message() );
$this->display_error_message(
sprintf(
/* translators: Placeholders %1$s - original error message from Facebook API */
esc_html__( 'There was an issue connecting to the Facebook API: %s', 'facebook-for-woocommerce' ),
$product_fbid_result->get_error_message()
)
);
return;
}
if ( $product_fbid_result && isset( $product_fbid_result['body'] ) ) {
$body = WC_Facebookcommerce_Utils::decode_json( $product_fbid_result['body'] );
if ( $body && $body->id ) {
if ( $fbid_type == self::FB_PRODUCT_GROUP_ID ) {
$fb_id = $body->product_group->id;
} else {
$fb_id = $body->id;
}
update_post_meta(
$wp_id,
$fbid_type,
$fb_id
);
update_post_meta( $wp_id, Products::VISIBILITY_META_KEY, true );
return $fb_id;
}
}
return;
}
private function set_default_variant( $product_group_id, $product_item_id ) {
$result = $this->check_api_result(
$this->fbgraph->set_default_variant(
$product_group_id,
array( 'default_product_id' => $product_item_id )
)
);
if ( ! $result ) {
WC_Facebookcommerce_Utils::fblog(
'Fail to set default product item',
array(),
true
);
}
}
private function fb_wp_die() {
if ( ! $this->test_mode ) {
wp_die();
}
}
/**
* Display test result.
**/
function ajax_display_test_result() {
WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'test result', true );
check_ajax_referer( 'wc_facebook_settings_jsx' );
$response = array(
'pass' => 'true',
);
$test_pass = get_option( 'fb_test_pass', null );
if ( ! isset( $test_pass ) ) {
$response['pass'] = 'in progress';
} elseif ( $test_pass == 0 ) {
$response['pass'] = 'false';
$response['debug_info'] = get_transient( 'facebook_plugin_test_fail' );
$response['stack_trace'] =
get_transient( 'facebook_plugin_test_stack_trace' );
$response['stack_trace'] =
preg_replace( "/\n/", '<br>', $response['stack_trace'] );
delete_transient( 'facebook_plugin_test_fail' );
delete_transient( 'facebook_plugin_test_stack_trace' );
}
delete_option( 'fb_test_pass' );
printf( json_encode( $response ) );
wp_die();
}
/**
* Schedules a recurring event to sync products.
*
* @deprecated 1.10.0-dev.1
*/
function ajax_schedule_force_resync() {
wc_deprecated_function( __METHOD__, '1.10.0-dev.1' );
die;
}
function ajax_update_fb_option() {
check_ajax_referer( 'wc_facebook_settings_jsx' );
WC_Facebookcommerce_Utils::check_woo_ajax_permissions( 'update fb options', true );
if ( isset( $_POST ) && ! empty( $_POST['option'] ) && isset( $_POST['option_value'] ) ) {
$option_name = sanitize_text_field( wp_unslash( $_POST['option'] ) );
$option_value = sanitize_text_field( wp_unslash( $_POST['option_value'] ) );
if ( stripos( $option_name, 'fb_' ) === 0 ) {
update_option( $option_name, $option_value );
}
}
wp_die();
}
/**
* Adds an recurring action to sync products.
*
* The action is scheduled using a cron schedule instead of a recurring interval (see https://en.wikipedia.org/wiki/Cron#Overview).
* A cron schedule should allow for the action to run roughly at the same time every day regardless of the duration of the task.
*
* @since 1.10.0-dev.1
*
* @param int $offset number of seconds since the beginning of the daay
*/
public function schedule_resync( $offset ) {
try {
$current_time = new DateTime( 'now', new DateTimeZone( wc_timezone_string() ) );
$first_scheduled_time = new DateTime( "today +{$offset} seconds", new DateTimeZone( wc_timezone_string() ) );
$next_scheduled_time = new DateTime( "today +1 day {$offset} seconds", new DateTimeZone( wc_timezone_string() ) );
} catch ( \Exception $e ) {
// TODO: log an error indicating that it was not possible to schedule a recurring action to sync products {WV 2020-01-28}
return;
}
// unschedule previously scheduled resync actions
$this->unschedule_resync();
$timestamp = $first_scheduled_time >= $current_time ? $first_scheduled_time->getTimestamp() : $next_scheduled_time->getTimestamp();
// TODO: replace 'facebook-for-woocommerce' with the plugin ID once we stat using the Framework {WV 2020-01-30}
as_schedule_single_action( $timestamp, self::ACTION_HOOK_SCHEDULED_RESYNC, [], 'facebook-for-woocommerce' );
}
/**
* Removes the recurring action that syncs products.
*
* @since 1.10.0-dev.1
*/
private function unschedule_resync() {
// TODO: replace 'facebook-for-woocommerce' with the plugin ID once we stat using the Framework {WV 2020-01-30}
as_unschedule_all_actions( self::ACTION_HOOK_SCHEDULED_RESYNC, [], 'facebook-for-woocommerce' );
}
/**
* Determines whether a recurring action to sync products is scheduled and not running.
*
* @see \as_next_scheduled_action()
*
* @since 1.10.0-dev.1
*
* @return bool
*/
public function is_resync_scheduled() {
// TODO: replace 'facebook-for-woocommerce' with the plugin ID once we stat using the Framework {WV 2020-01-30}
return is_int( as_next_scheduled_action( self::ACTION_HOOK_SCHEDULED_RESYNC, [], 'facebook-for-woocommerce' ) );
}
/**
* Handles the scheduled action used to sync products daily.
*
* It will schedule a new action if product sync is enabled and the plugin is configured to resnyc procucts daily.
*
* @internal
*
* @see \WC_Facebookcommerce_Integration::schedule_resync()
*
* @since 1.10.0-dev.1
*/
public function handle_scheduled_resync_action() {
$this->sync_all_fb_products_using_feed();
$resync_offset = $this->get_scheduled_resync_offset();
// manually schedule the next product resync action if possible
if ( null !== $resync_offset && $this->is_product_sync_enabled() && ! $this->is_resync_scheduled() ) {
$this->schedule_resync( $resync_offset );
}
}
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/mirrors_facebook/facebook-for-woocommerce.git
[email protected]:mirrors_facebook/facebook-for-woocommerce.git
mirrors_facebook
facebook-for-woocommerce
facebook-for-woocommerce
add-issue-templates

搜索帮助