Version Description
- 2019-02-26 =
- changing contributor to facebook from facebook4woocommerce, so that woo plugin will be shown under https://profiles.wordpress.org/facebook/#content-plugins
- adding changelog in readme.txt so that notifications will be sent for updates and changelog will be shown under https://wordpress.org/plugins/facebook-for-woocommerce/#developers
- removing debug flags notice under facebook-for-woocommerce.php so that developers will be able to debug with debug logs
Download this release
Release Info
Developer | facebook4woocommerce |
Plugin | Facebook for WooCommerce |
Version | 1.9.11 |
Comparing to | |
See all releases |
Version 1.9.11
- CODE_OF_CONDUCT.md +5 -0
- CONTRIBUTING.md +35 -0
- LICENSE +339 -0
- assets/css/facebook-infobanner.css +56 -0
- assets/css/facebook.css +274 -0
- assets/image/FB-f-Logo__blue_29.png +0 -0
- assets/image/facebook.png +0 -0
- assets/image/help-center.png +0 -0
- assets/image/small-store.png +0 -0
- assets/js/facebook-infobanner.js +42 -0
- assets/js/facebook-metabox.js +67 -0
- assets/js/facebook-products.js +69 -0
- assets/js/facebook-settings.js +882 -0
- changelog.txt +399 -0
- composer.json +16 -0
- facebook-commerce-events-tracker.php +386 -0
- facebook-commerce-messenger-chat.php +87 -0
- facebook-commerce-pixel-event.php +292 -0
- facebook-commerce.php +2541 -0
- facebook-config-warmer.php +20 -0
- facebook-for-woocommerce.php +105 -0
- includes/fbasync.php +42 -0
- includes/fbbackground.php +152 -0
- includes/fbcustomapi.php +63 -0
- includes/fbgraph.php +298 -0
- includes/fbinfobanner.php +241 -0
- includes/fbproduct.php +752 -0
- includes/fbproductfeed.php +367 -0
- includes/fbutils.php +485 -0
- includes/fbwpml.php +137 -0
- includes/test/facebook-integration-test.php +575 -0
- includes/test/fbproductfeed-test.php +40 -0
- readme.txt +58 -0
CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Code of Conduct
|
2 |
+
|
3 |
+
Facebook has adopted a Code of Conduct that we expect project participants to adhere to.
|
4 |
+
Please read the [full text](https://code.fb.com/codeofconduct/)
|
5 |
+
so that you can understand what actions will and will not be tolerated.
|
CONTRIBUTING.md
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Contributing to Facebook for WooCommerce
|
2 |
+
We want to make contributing to this project as easy and transparent as
|
3 |
+
possible.
|
4 |
+
|
5 |
+
## Pull Requests
|
6 |
+
We actively welcome your pull requests.
|
7 |
+
|
8 |
+
1. Fork the repo and create your branch from `master`.
|
9 |
+
2. If you've added code that should be tested, add tests.
|
10 |
+
3. If you've changed APIs, update the documentation.
|
11 |
+
4. Make sure your code lints.
|
12 |
+
5. If you haven't already, complete the Contributor License Agreement ("CLA").
|
13 |
+
|
14 |
+
## Contributor License Agreement ("CLA")
|
15 |
+
In order to accept your pull request, we need you to submit a CLA. You only need
|
16 |
+
to do this once to work on any of Facebook's open source projects.
|
17 |
+
|
18 |
+
Complete your CLA here: <https://code.facebook.com/cla>
|
19 |
+
|
20 |
+
## Issues
|
21 |
+
We use GitHub issues to track public bugs. Please ensure your description is
|
22 |
+
clear and has sufficient instructions to be able to reproduce the issue.
|
23 |
+
|
24 |
+
Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe
|
25 |
+
disclosure of security bugs. In those cases, please go through the process
|
26 |
+
outlined on that page and do not file a public issue.
|
27 |
+
|
28 |
+
## Coding Style
|
29 |
+
* 4 spaces for indentation rather than tabs
|
30 |
+
* 80 character line length
|
31 |
+
|
32 |
+
## License
|
33 |
+
By contributing to Facebook for WooCommerce, you agree that your contributions
|
34 |
+
will be licensed under the LICENSE file in the root directory of
|
35 |
+
this source tree.
|
LICENSE
ADDED
@@ -0,0 +1,339 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
GNU GENERAL PUBLIC LICENSE
|
2 |
+
Version 2, June 1991
|
3 |
+
|
4 |
+
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
5 |
+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
6 |
+
Everyone is permitted to copy and distribute verbatim copies
|
7 |
+
of this license document, but changing it is not allowed.
|
8 |
+
|
9 |
+
Preamble
|
10 |
+
|
11 |
+
The licenses for most software are designed to take away your
|
12 |
+
freedom to share and change it. By contrast, the GNU General Public
|
13 |
+
License is intended to guarantee your freedom to share and change free
|
14 |
+
software--to make sure the software is free for all its users. This
|
15 |
+
General Public License applies to most of the Free Software
|
16 |
+
Foundation's software and to any other program whose authors commit to
|
17 |
+
using it. (Some other Free Software Foundation software is covered by
|
18 |
+
the GNU Lesser General Public License instead.) You can apply it to
|
19 |
+
your programs, too.
|
20 |
+
|
21 |
+
When we speak of free software, we are referring to freedom, not
|
22 |
+
price. Our General Public Licenses are designed to make sure that you
|
23 |
+
have the freedom to distribute copies of free software (and charge for
|
24 |
+
this service if you wish), that you receive source code or can get it
|
25 |
+
if you want it, that you can change the software or use pieces of it
|
26 |
+
in new free programs; and that you know you can do these things.
|
27 |
+
|
28 |
+
To protect your rights, we need to make restrictions that forbid
|
29 |
+
anyone to deny you these rights or to ask you to surrender the rights.
|
30 |
+
These restrictions translate to certain responsibilities for you if you
|
31 |
+
distribute copies of the software, or if you modify it.
|
32 |
+
|
33 |
+
For example, if you distribute copies of such a program, whether
|
34 |
+
gratis or for a fee, you must give the recipients all the rights that
|
35 |
+
you have. You must make sure that they, too, receive or can get the
|
36 |
+
source code. And you must show them these terms so they know their
|
37 |
+
rights.
|
38 |
+
|
39 |
+
We protect your rights with two steps: (1) copyright the software, and
|
40 |
+
(2) offer you this license which gives you legal permission to copy,
|
41 |
+
distribute and/or modify the software.
|
42 |
+
|
43 |
+
Also, for each author's protection and ours, we want to make certain
|
44 |
+
that everyone understands that there is no warranty for this free
|
45 |
+
software. If the software is modified by someone else and passed on, we
|
46 |
+
want its recipients to know that what they have is not the original, so
|
47 |
+
that any problems introduced by others will not reflect on the original
|
48 |
+
authors' reputations.
|
49 |
+
|
50 |
+
Finally, any free program is threatened constantly by software
|
51 |
+
patents. We wish to avoid the danger that redistributors of a free
|
52 |
+
program will individually obtain patent licenses, in effect making the
|
53 |
+
program proprietary. To prevent this, we have made it clear that any
|
54 |
+
patent must be licensed for everyone's free use or not licensed at all.
|
55 |
+
|
56 |
+
The precise terms and conditions for copying, distribution and
|
57 |
+
modification follow.
|
58 |
+
|
59 |
+
GNU GENERAL PUBLIC LICENSE
|
60 |
+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
61 |
+
|
62 |
+
0. This License applies to any program or other work which contains
|
63 |
+
a notice placed by the copyright holder saying it may be distributed
|
64 |
+
under the terms of this General Public License. The "Program", below,
|
65 |
+
refers to any such program or work, and a "work based on the Program"
|
66 |
+
means either the Program or any derivative work under copyright law:
|
67 |
+
that is to say, a work containing the Program or a portion of it,
|
68 |
+
either verbatim or with modifications and/or translated into another
|
69 |
+
language. (Hereinafter, translation is included without limitation in
|
70 |
+
the term "modification".) Each licensee is addressed as "you".
|
71 |
+
|
72 |
+
Activities other than copying, distribution and modification are not
|
73 |
+
covered by this License; they are outside its scope. The act of
|
74 |
+
running the Program is not restricted, and the output from the Program
|
75 |
+
is covered only if its contents constitute a work based on the
|
76 |
+
Program (independent of having been made by running the Program).
|
77 |
+
Whether that is true depends on what the Program does.
|
78 |
+
|
79 |
+
1. You may copy and distribute verbatim copies of the Program's
|
80 |
+
source code as you receive it, in any medium, provided that you
|
81 |
+
conspicuously and appropriately publish on each copy an appropriate
|
82 |
+
copyright notice and disclaimer of warranty; keep intact all the
|
83 |
+
notices that refer to this License and to the absence of any warranty;
|
84 |
+
and give any other recipients of the Program a copy of this License
|
85 |
+
along with the Program.
|
86 |
+
|
87 |
+
You may charge a fee for the physical act of transferring a copy, and
|
88 |
+
you may at your option offer warranty protection in exchange for a fee.
|
89 |
+
|
90 |
+
2. You may modify your copy or copies of the Program or any portion
|
91 |
+
of it, thus forming a work based on the Program, and copy and
|
92 |
+
distribute such modifications or work under the terms of Section 1
|
93 |
+
above, provided that you also meet all of these conditions:
|
94 |
+
|
95 |
+
a) You must cause the modified files to carry prominent notices
|
96 |
+
stating that you changed the files and the date of any change.
|
97 |
+
|
98 |
+
b) You must cause any work that you distribute or publish, that in
|
99 |
+
whole or in part contains or is derived from the Program or any
|
100 |
+
part thereof, to be licensed as a whole at no charge to all third
|
101 |
+
parties under the terms of this License.
|
102 |
+
|
103 |
+
c) If the modified program normally reads commands interactively
|
104 |
+
when run, you must cause it, when started running for such
|
105 |
+
interactive use in the most ordinary way, to print or display an
|
106 |
+
announcement including an appropriate copyright notice and a
|
107 |
+
notice that there is no warranty (or else, saying that you provide
|
108 |
+
a warranty) and that users may redistribute the program under
|
109 |
+
these conditions, and telling the user how to view a copy of this
|
110 |
+
License. (Exception: if the Program itself is interactive but
|
111 |
+
does not normally print such an announcement, your work based on
|
112 |
+
the Program is not required to print an announcement.)
|
113 |
+
|
114 |
+
These requirements apply to the modified work as a whole. If
|
115 |
+
identifiable sections of that work are not derived from the Program,
|
116 |
+
and can be reasonably considered independent and separate works in
|
117 |
+
themselves, then this License, and its terms, do not apply to those
|
118 |
+
sections when you distribute them as separate works. But when you
|
119 |
+
distribute the same sections as part of a whole which is a work based
|
120 |
+
on the Program, the distribution of the whole must be on the terms of
|
121 |
+
this License, whose permissions for other licensees extend to the
|
122 |
+
entire whole, and thus to each and every part regardless of who wrote it.
|
123 |
+
|
124 |
+
Thus, it is not the intent of this section to claim rights or contest
|
125 |
+
your rights to work written entirely by you; rather, the intent is to
|
126 |
+
exercise the right to control the distribution of derivative or
|
127 |
+
collective works based on the Program.
|
128 |
+
|
129 |
+
In addition, mere aggregation of another work not based on the Program
|
130 |
+
with the Program (or with a work based on the Program) on a volume of
|
131 |
+
a storage or distribution medium does not bring the other work under
|
132 |
+
the scope of this License.
|
133 |
+
|
134 |
+
3. You may copy and distribute the Program (or a work based on it,
|
135 |
+
under Section 2) in object code or executable form under the terms of
|
136 |
+
Sections 1 and 2 above provided that you also do one of the following:
|
137 |
+
|
138 |
+
a) Accompany it with the complete corresponding machine-readable
|
139 |
+
source code, which must be distributed under the terms of Sections
|
140 |
+
1 and 2 above on a medium customarily used for software interchange; or,
|
141 |
+
|
142 |
+
b) Accompany it with a written offer, valid for at least three
|
143 |
+
years, to give any third party, for a charge no more than your
|
144 |
+
cost of physically performing source distribution, a complete
|
145 |
+
machine-readable copy of the corresponding source code, to be
|
146 |
+
distributed under the terms of Sections 1 and 2 above on a medium
|
147 |
+
customarily used for software interchange; or,
|
148 |
+
|
149 |
+
c) Accompany it with the information you received as to the offer
|
150 |
+
to distribute corresponding source code. (This alternative is
|
151 |
+
allowed only for noncommercial distribution and only if you
|
152 |
+
received the program in object code or executable form with such
|
153 |
+
an offer, in accord with Subsection b above.)
|
154 |
+
|
155 |
+
The source code for a work means the preferred form of the work for
|
156 |
+
making modifications to it. For an executable work, complete source
|
157 |
+
code means all the source code for all modules it contains, plus any
|
158 |
+
associated interface definition files, plus the scripts used to
|
159 |
+
control compilation and installation of the executable. However, as a
|
160 |
+
special exception, the source code distributed need not include
|
161 |
+
anything that is normally distributed (in either source or binary
|
162 |
+
form) with the major components (compiler, kernel, and so on) of the
|
163 |
+
operating system on which the executable runs, unless that component
|
164 |
+
itself accompanies the executable.
|
165 |
+
|
166 |
+
If distribution of executable or object code is made by offering
|
167 |
+
access to copy from a designated place, then offering equivalent
|
168 |
+
access to copy the source code from the same place counts as
|
169 |
+
distribution of the source code, even though third parties are not
|
170 |
+
compelled to copy the source along with the object code.
|
171 |
+
|
172 |
+
4. You may not copy, modify, sublicense, or distribute the Program
|
173 |
+
except as expressly provided under this License. Any attempt
|
174 |
+
otherwise to copy, modify, sublicense or distribute the Program is
|
175 |
+
void, and will automatically terminate your rights under this License.
|
176 |
+
However, parties who have received copies, or rights, from you under
|
177 |
+
this License will not have their licenses terminated so long as such
|
178 |
+
parties remain in full compliance.
|
179 |
+
|
180 |
+
5. You are not required to accept this License, since you have not
|
181 |
+
signed it. However, nothing else grants you permission to modify or
|
182 |
+
distribute the Program or its derivative works. These actions are
|
183 |
+
prohibited by law if you do not accept this License. Therefore, by
|
184 |
+
modifying or distributing the Program (or any work based on the
|
185 |
+
Program), you indicate your acceptance of this License to do so, and
|
186 |
+
all its terms and conditions for copying, distributing or modifying
|
187 |
+
the Program or works based on it.
|
188 |
+
|
189 |
+
6. Each time you redistribute the Program (or any work based on the
|
190 |
+
Program), the recipient automatically receives a license from the
|
191 |
+
original licensor to copy, distribute or modify the Program subject to
|
192 |
+
these terms and conditions. You may not impose any further
|
193 |
+
restrictions on the recipients' exercise of the rights granted herein.
|
194 |
+
You are not responsible for enforcing compliance by third parties to
|
195 |
+
this License.
|
196 |
+
|
197 |
+
7. If, as a consequence of a court judgment or allegation of patent
|
198 |
+
infringement or for any other reason (not limited to patent issues),
|
199 |
+
conditions are imposed on you (whether by court order, agreement or
|
200 |
+
otherwise) that contradict the conditions of this License, they do not
|
201 |
+
excuse you from the conditions of this License. If you cannot
|
202 |
+
distribute so as to satisfy simultaneously your obligations under this
|
203 |
+
License and any other pertinent obligations, then as a consequence you
|
204 |
+
may not distribute the Program at all. For example, if a patent
|
205 |
+
license would not permit royalty-free redistribution of the Program by
|
206 |
+
all those who receive copies directly or indirectly through you, then
|
207 |
+
the only way you could satisfy both it and this License would be to
|
208 |
+
refrain entirely from distribution of the Program.
|
209 |
+
|
210 |
+
If any portion of this section is held invalid or unenforceable under
|
211 |
+
any particular circumstance, the balance of the section is intended to
|
212 |
+
apply and the section as a whole is intended to apply in other
|
213 |
+
circumstances.
|
214 |
+
|
215 |
+
It is not the purpose of this section to induce you to infringe any
|
216 |
+
patents or other property right claims or to contest validity of any
|
217 |
+
such claims; this section has the sole purpose of protecting the
|
218 |
+
integrity of the free software distribution system, which is
|
219 |
+
implemented by public license practices. Many people have made
|
220 |
+
generous contributions to the wide range of software distributed
|
221 |
+
through that system in reliance on consistent application of that
|
222 |
+
system; it is up to the author/donor to decide if he or she is willing
|
223 |
+
to distribute software through any other system and a licensee cannot
|
224 |
+
impose that choice.
|
225 |
+
|
226 |
+
This section is intended to make thoroughly clear what is believed to
|
227 |
+
be a consequence of the rest of this License.
|
228 |
+
|
229 |
+
8. If the distribution and/or use of the Program is restricted in
|
230 |
+
certain countries either by patents or by copyrighted interfaces, the
|
231 |
+
original copyright holder who places the Program under this License
|
232 |
+
may add an explicit geographical distribution limitation excluding
|
233 |
+
those countries, so that distribution is permitted only in or among
|
234 |
+
countries not thus excluded. In such case, this License incorporates
|
235 |
+
the limitation as if written in the body of this License.
|
236 |
+
|
237 |
+
9. The Free Software Foundation may publish revised and/or new versions
|
238 |
+
of the General Public License from time to time. Such new versions will
|
239 |
+
be similar in spirit to the present version, but may differ in detail to
|
240 |
+
address new problems or concerns.
|
241 |
+
|
242 |
+
Each version is given a distinguishing version number. If the Program
|
243 |
+
specifies a version number of this License which applies to it and "any
|
244 |
+
later version", you have the option of following the terms and conditions
|
245 |
+
either of that version or of any later version published by the Free
|
246 |
+
Software Foundation. If the Program does not specify a version number of
|
247 |
+
this License, you may choose any version ever published by the Free Software
|
248 |
+
Foundation.
|
249 |
+
|
250 |
+
10. If you wish to incorporate parts of the Program into other free
|
251 |
+
programs whose distribution conditions are different, write to the author
|
252 |
+
to ask for permission. For software which is copyrighted by the Free
|
253 |
+
Software Foundation, write to the Free Software Foundation; we sometimes
|
254 |
+
make exceptions for this. Our decision will be guided by the two goals
|
255 |
+
of preserving the free status of all derivatives of our free software and
|
256 |
+
of promoting the sharing and reuse of software generally.
|
257 |
+
|
258 |
+
NO WARRANTY
|
259 |
+
|
260 |
+
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
261 |
+
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
262 |
+
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
263 |
+
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
264 |
+
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
265 |
+
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
266 |
+
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
267 |
+
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
268 |
+
REPAIR OR CORRECTION.
|
269 |
+
|
270 |
+
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
271 |
+
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
272 |
+
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
273 |
+
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
274 |
+
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
275 |
+
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
276 |
+
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
277 |
+
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
278 |
+
POSSIBILITY OF SUCH DAMAGES.
|
279 |
+
|
280 |
+
END OF TERMS AND CONDITIONS
|
281 |
+
|
282 |
+
How to Apply These Terms to Your New Programs
|
283 |
+
|
284 |
+
If you develop a new program, and you want it to be of the greatest
|
285 |
+
possible use to the public, the best way to achieve this is to make it
|
286 |
+
free software which everyone can redistribute and change under these terms.
|
287 |
+
|
288 |
+
To do so, attach the following notices to the program. It is safest
|
289 |
+
to attach them to the start of each source file to most effectively
|
290 |
+
convey the exclusion of warranty; and each file should have at least
|
291 |
+
the "copyright" line and a pointer to where the full notice is found.
|
292 |
+
|
293 |
+
<one line to give the program's name and a brief idea of what it does.>
|
294 |
+
Copyright (C) <year> <name of author>
|
295 |
+
|
296 |
+
This program is free software; you can redistribute it and/or modify
|
297 |
+
it under the terms of the GNU General Public License as published by
|
298 |
+
the Free Software Foundation; either version 2 of the License, or
|
299 |
+
(at your option) any later version.
|
300 |
+
|
301 |
+
This program is distributed in the hope that it will be useful,
|
302 |
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
303 |
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
304 |
+
GNU General Public License for more details.
|
305 |
+
|
306 |
+
You should have received a copy of the GNU General Public License along
|
307 |
+
with this program; if not, write to the Free Software Foundation, Inc.,
|
308 |
+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
309 |
+
|
310 |
+
Also add information on how to contact you by electronic and paper mail.
|
311 |
+
|
312 |
+
If the program is interactive, make it output a short notice like this
|
313 |
+
when it starts in an interactive mode:
|
314 |
+
|
315 |
+
Gnomovision version 69, Copyright (C) year name of author
|
316 |
+
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
317 |
+
This is free software, and you are welcome to redistribute it
|
318 |
+
under certain conditions; type `show c' for details.
|
319 |
+
|
320 |
+
The hypothetical commands `show w' and `show c' should show the appropriate
|
321 |
+
parts of the General Public License. Of course, the commands you use may
|
322 |
+
be called something other than `show w' and `show c'; they could even be
|
323 |
+
mouse-clicks or menu items--whatever suits your program.
|
324 |
+
|
325 |
+
You should also get your employer (if you work as a programmer) or your
|
326 |
+
school, if any, to sign a "copyright disclaimer" for the program, if
|
327 |
+
necessary. Here is a sample; alter the names:
|
328 |
+
|
329 |
+
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
330 |
+
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
331 |
+
|
332 |
+
<signature of Ty Coon>, 1 April 1989
|
333 |
+
Ty Coon, President of Vice
|
334 |
+
|
335 |
+
This General Public License does not permit incorporating your program into
|
336 |
+
proprietary programs. If your program is a subroutine library, you may
|
337 |
+
consider it more useful to permit linking proprietary applications with the
|
338 |
+
library. If this is what you want to do, use the GNU Lesser General
|
339 |
+
Public License instead of this License.
|
assets/css/facebook-infobanner.css
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
3 |
+
*
|
4 |
+
* This source code is licensed under the license found in the
|
5 |
+
* LICENSE file in the root directory of this source tree.
|
6 |
+
*
|
7 |
+
* @package FacebookCommerce
|
8 |
+
*/
|
9 |
+
#fbinfobanner .btn {
|
10 |
+
/* container: */
|
11 |
+
background-color: rgb(86,120,227);
|
12 |
+
border: 1px solid;
|
13 |
+
border-color: #435a8b #3c5488 #334c83;
|
14 |
+
border-radius: 2px;
|
15 |
+
padding: 5px 10px 5px 10px;
|
16 |
+
/* Get started: */
|
17 |
+
font-family: helvetica, arial, sans-serif;
|
18 |
+
font-size: 12px;
|
19 |
+
font-weight: 600;
|
20 |
+
color: #FFFFFF;
|
21 |
+
text-align: center;
|
22 |
+
text-decoration: none;
|
23 |
+
display: inline-block;
|
24 |
+
margin: 5px 10px 1px 1px;
|
25 |
+
}
|
26 |
+
|
27 |
+
#fbinfobanner .btn.dismiss {
|
28 |
+
margin: 5px 10px 5px 1px;
|
29 |
+
}
|
30 |
+
|
31 |
+
#fbinfobanner .btn.grey{
|
32 |
+
background: #f6f7f8;
|
33 |
+
border-color: #e0e1e2;
|
34 |
+
color: #4e5665;
|
35 |
+
}
|
36 |
+
|
37 |
+
#fbinfobanner .iconDetails {
|
38 |
+
margin: 5px 10px 5px 3px;
|
39 |
+
float:left;
|
40 |
+
height:50px;
|
41 |
+
width:50px;
|
42 |
+
}
|
43 |
+
|
44 |
+
#fbinfobanner .tipTitle {
|
45 |
+
padding: 1px 12px 1px 20px;
|
46 |
+
font-size: 14px;
|
47 |
+
}
|
48 |
+
|
49 |
+
#fbinfobanner .tipContent {
|
50 |
+
padding: 1px 12px 1px 20px;
|
51 |
+
font-size: 14px;
|
52 |
+
}
|
53 |
+
|
54 |
+
#fbinfobanner .tipButton {
|
55 |
+
margin-bottom: 0px;
|
56 |
+
}
|
assets/css/facebook.css
ADDED
@@ -0,0 +1,274 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
3 |
+
*
|
4 |
+
* This source code is licensed under the license found in the
|
5 |
+
* LICENSE file in the root directory of this source tree.
|
6 |
+
*
|
7 |
+
* @package FacebookCommerce
|
8 |
+
*/
|
9 |
+
#fbsetup .wrapper {
|
10 |
+
box-shadow: inset 0 0 0 1px rgba(0,0,0,0.2);
|
11 |
+
display: block;
|
12 |
+
width: 600px;
|
13 |
+
background: white url(../image/small-store.png) no-repeat;
|
14 |
+
background-position-x: 92%;
|
15 |
+
background-position-y: 172px;
|
16 |
+
background-size: 180px;
|
17 |
+
}
|
18 |
+
|
19 |
+
#fbsetup .wrapper .help-center {
|
20 |
+
float: right;
|
21 |
+
margin-top: -23px;
|
22 |
+
}
|
23 |
+
|
24 |
+
#fbsetup .wrapper .help-center a {
|
25 |
+
color: white;
|
26 |
+
}
|
27 |
+
|
28 |
+
#fbsetup .wrapper .help-center-icon {
|
29 |
+
display: inline-block;
|
30 |
+
background-image: url(../image/help-center.png);
|
31 |
+
background-size: 38px 38px;
|
32 |
+
background-repeat: no-repeat;
|
33 |
+
width: 38px;
|
34 |
+
height: 38px;
|
35 |
+
zoom: 0.7;
|
36 |
+
top: 12px;
|
37 |
+
position: relative;
|
38 |
+
}
|
39 |
+
|
40 |
+
#fbsetup .content {
|
41 |
+
padding: 10px 30px 20px 30px;
|
42 |
+
}
|
43 |
+
|
44 |
+
#fbsetup .loader {
|
45 |
+
border: 4px solid #f3f3f3; /* Light grey */
|
46 |
+
border-top: 4px solid #3498db; /* Blue */
|
47 |
+
border-radius: 50%;
|
48 |
+
width: 20px;
|
49 |
+
height: 20px;
|
50 |
+
animation: spin 2s linear infinite;
|
51 |
+
}
|
52 |
+
@keyframes spin {
|
53 |
+
0% { transform: rotate(0deg); }
|
54 |
+
100% { transform: rotate(360deg); }
|
55 |
+
}
|
56 |
+
|
57 |
+
#fbsetup .btn {
|
58 |
+
/* container: */
|
59 |
+
background-color: rgb(86,120,227);
|
60 |
+
border: 1px solid;
|
61 |
+
border-color: #435a8b #3c5488 #334c83;
|
62 |
+
border-radius: 2px;
|
63 |
+
padding: 10px 20px;
|
64 |
+
/* Get started: */
|
65 |
+
font-family: helvetica, arial, sans-serif;
|
66 |
+
font-size: 12px;
|
67 |
+
font-weight: 550;
|
68 |
+
color: #FFFFFF;
|
69 |
+
text-align: center;
|
70 |
+
text-decoration: none;
|
71 |
+
display: inline-block;
|
72 |
+
letter-spacing: 0.2px;
|
73 |
+
margin: 0px 12px 10px 0px;
|
74 |
+
width: 60px;
|
75 |
+
}
|
76 |
+
|
77 |
+
#fbsetup .btn.grey{
|
78 |
+
background: #f6f7f8;
|
79 |
+
border-color: #e0e1e2;
|
80 |
+
color: #4e5665;
|
81 |
+
width: 75px;
|
82 |
+
}
|
83 |
+
|
84 |
+
#fbsetup .btn.pre-setup{
|
85 |
+
font-size: 13px;
|
86 |
+
width: 80px;
|
87 |
+
}
|
88 |
+
|
89 |
+
#fbsetup header {
|
90 |
+
display: block;
|
91 |
+
background: url(../image/facebook.png) no-repeat #4267B2;
|
92 |
+
background-position: 20px center;
|
93 |
+
background-size: 100px 19px;
|
94 |
+
padding: 20px;
|
95 |
+
}
|
96 |
+
#fbsetup h1 {
|
97 |
+
/* title: */
|
98 |
+
font-family: arial;
|
99 |
+
font-size: 18px;
|
100 |
+
font-weight: bold;
|
101 |
+
color: #141823;
|
102 |
+
margin-top: 12px;
|
103 |
+
width: 400px;
|
104 |
+
}
|
105 |
+
#fbsetup p {
|
106 |
+
/* copy: */
|
107 |
+
font-family: Helvetica;
|
108 |
+
font-size: 12px;
|
109 |
+
color: #1D2129;
|
110 |
+
margin: 10px 0px 14px 0px;
|
111 |
+
}
|
112 |
+
#fbsetup h2 {
|
113 |
+
color: rgb(0,0,0);
|
114 |
+
font-size: 14px;
|
115 |
+
font-weight: normal;
|
116 |
+
line-height: 18px;
|
117 |
+
margin-bottom: 6px;
|
118 |
+
margin-top: 12px;
|
119 |
+
width: 520px;
|
120 |
+
}
|
121 |
+
#fbsetup ul {
|
122 |
+
list-style-type: disc;
|
123 |
+
margin: 10px 10px 15px 19px;
|
124 |
+
}
|
125 |
+
#fbsetup li {
|
126 |
+
margin: 2px 0px;
|
127 |
+
padding-left: 8px;
|
128 |
+
}
|
129 |
+
#fbsetup .nux-message {
|
130 |
+
box-sizing: border-box;
|
131 |
+
color: #fff;
|
132 |
+
overflow: hidden;
|
133 |
+
padding: 9px;
|
134 |
+
position: absolute;
|
135 |
+
text-align: left;
|
136 |
+
transform: translateZ(0);
|
137 |
+
transition: all 200ms cubic-bezier(.08,.52,.52,1);
|
138 |
+
z-index: 5;
|
139 |
+
}
|
140 |
+
#fbsetup .nux-message .nux-message-text {
|
141 |
+
background: #3578e5;
|
142 |
+
border-radius: 3px;
|
143 |
+
box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
|
144 |
+
box-sizing: border-box;
|
145 |
+
padding: 10px 34px 12px 10px;
|
146 |
+
white-space: normal;
|
147 |
+
min-width: 252px;
|
148 |
+
}
|
149 |
+
#fbsetup .nux-message .nux-message-close-btn {
|
150 |
+
cursor: pointer;
|
151 |
+
position: absolute;
|
152 |
+
right: 19px;
|
153 |
+
top: 19px;
|
154 |
+
height: 12px;
|
155 |
+
width: 12px;
|
156 |
+
font-style: normal;
|
157 |
+
color: #ccc;
|
158 |
+
}
|
159 |
+
#fbsetup .nux-message .nux-message-close-btn:hover {
|
160 |
+
color: #fff;
|
161 |
+
}
|
162 |
+
#fbsetup .nux-message .nux-message-arrow {
|
163 |
+
border-right: 9px solid #3578e5;
|
164 |
+
left: 0;
|
165 |
+
position: absolute;
|
166 |
+
border-bottom: 9px solid transparent;
|
167 |
+
border-top: 9px solid transparent;
|
168 |
+
margin-top: -9px;
|
169 |
+
top: 50%;
|
170 |
+
}
|
171 |
+
#fbsetup .settings-section {
|
172 |
+
display: inline-block;
|
173 |
+
min-width: 45%;
|
174 |
+
max-width: 45%;
|
175 |
+
margin-right: 8px;
|
176 |
+
vertical-align: top;
|
177 |
+
}
|
178 |
+
#fbsetup .settings-section h1 {
|
179 |
+
color: rgb(126,129,136);
|
180 |
+
font-size: 14px;
|
181 |
+
margin-top: 0px;
|
182 |
+
width: auto;
|
183 |
+
}
|
184 |
+
#fbsetup .settings-section h2 {
|
185 |
+
color: rgb(126,129,136);
|
186 |
+
font-size: 14px;
|
187 |
+
margin-top: 2px;
|
188 |
+
width: auto;
|
189 |
+
}
|
190 |
+
#fbsetup .settings-section .btn.small {
|
191 |
+
background: #f6f7f8;
|
192 |
+
border-color: #e0e1e2;
|
193 |
+
color: #4e5665;
|
194 |
+
height: 20px;
|
195 |
+
margin-top: 10px;
|
196 |
+
padding: 2px 6px;
|
197 |
+
width: auto;
|
198 |
+
}
|
199 |
+
.tooltip {
|
200 |
+
position: relative;
|
201 |
+
border-bottom: 1px dotted black;
|
202 |
+
/* container: */
|
203 |
+
border: 1px solid;
|
204 |
+
border-radius: 2px;
|
205 |
+
/* Launch Test: */
|
206 |
+
font-size: 12px;
|
207 |
+
font-weight: 550;
|
208 |
+
color: #FFFFFF;
|
209 |
+
text-align: center;
|
210 |
+
text-decoration: none;
|
211 |
+
display: inline-block;
|
212 |
+
letter-spacing: 0.2px;
|
213 |
+
margin: 0px 12px 10px 0px;
|
214 |
+
background: #f6f7f8;
|
215 |
+
border-color: #e0e1e2;
|
216 |
+
color: #4e5665;
|
217 |
+
height: 20px;
|
218 |
+
margin-top: 10px;
|
219 |
+
padding: 2px 6px;
|
220 |
+
width: auto;
|
221 |
+
}
|
222 |
+
.tooltip .tooltiptext {
|
223 |
+
color: black;
|
224 |
+
font-size: 13px;
|
225 |
+
padding: 5px 0;
|
226 |
+
text-align: justify;
|
227 |
+
visibility: hidden;
|
228 |
+
width: 400px;
|
229 |
+
/* Position the tooltip */
|
230 |
+
position: absolute;
|
231 |
+
z-index: 1;
|
232 |
+
top: 100%;
|
233 |
+
left: 50%;
|
234 |
+
margin-left: -60px;
|
235 |
+
}
|
236 |
+
.tooltip:hover .tooltiptext {
|
237 |
+
visibility: visible;
|
238 |
+
opacity: 1;
|
239 |
+
}
|
240 |
+
#fbsetup a {
|
241 |
+
text-decoration: none;
|
242 |
+
}
|
243 |
+
|
244 |
+
#fbAdvancedOptionsText {
|
245 |
+
color: rgb(69, 114, 169);
|
246 |
+
font-size: 14px;
|
247 |
+
font-family: Helvetica;
|
248 |
+
padding: 8px 0px 8px 0px;
|
249 |
+
}
|
250 |
+
|
251 |
+
#fbAdvancedOptions {
|
252 |
+
color: rgb(126,129,136);
|
253 |
+
font-size: 12px;
|
254 |
+
font-family: Helvetica;
|
255 |
+
display: none;
|
256 |
+
padding: 8px 4px 8px 4px;
|
257 |
+
}
|
258 |
+
|
259 |
+
#fbAdvancedOptions .autosyncSavedNotice {
|
260 |
+
color: green;
|
261 |
+
font-size: 10px;
|
262 |
+
display: none;
|
263 |
+
}
|
264 |
+
|
265 |
+
#fbAdvancedOptions div {
|
266 |
+
padding: 4px 4px 4px 4px;
|
267 |
+
}
|
268 |
+
|
269 |
+
.autosyncTime {
|
270 |
+
padding: 0px;
|
271 |
+
font-family: Helvetica;
|
272 |
+
font-size: 12px;
|
273 |
+
height: 18px;
|
274 |
+
}
|
assets/image/FB-f-Logo__blue_29.png
ADDED
Binary file
|
assets/image/facebook.png
ADDED
Binary file
|
assets/image/help-center.png
ADDED
Binary file
|
assets/image/small-store.png
ADDED
Binary file
|
assets/js/facebook-infobanner.js
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
3 |
+
*
|
4 |
+
* This source code is licensed under the license found in the
|
5 |
+
* LICENSE file in the root directory of this source tree.
|
6 |
+
*
|
7 |
+
* @package FacebookCommerce
|
8 |
+
*/
|
9 |
+
/*
|
10 |
+
* Ajax helper function.
|
11 |
+
* Takes optional payload for POST and optional callback.
|
12 |
+
*/
|
13 |
+
function ajax(action, payload = null, callback = null, failcallback = null) {
|
14 |
+
var data = {
|
15 |
+
'action': action,
|
16 |
+
};
|
17 |
+
if (payload){
|
18 |
+
for (var attrname in payload) { data[attrname] = payload[attrname]; }
|
19 |
+
}
|
20 |
+
|
21 |
+
// Since Wordpress 2.8 ajaxurl is always defined in admin header and
|
22 |
+
// points to admin-ajax.php
|
23 |
+
jQuery.post(ajaxurl, data, function(response) {
|
24 |
+
if(callback) {
|
25 |
+
callback(response);
|
26 |
+
}
|
27 |
+
}).fail(function(errorResponse){
|
28 |
+
if(failcallback) {
|
29 |
+
failcallback(errorResponse);
|
30 |
+
}
|
31 |
+
});
|
32 |
+
}
|
33 |
+
|
34 |
+
function fb_woo_infobanner_post_click(){
|
35 |
+
console.log("Woo infobanner post tip click!");
|
36 |
+
return ajax('ajax_woo_infobanner_post_click');
|
37 |
+
}
|
38 |
+
|
39 |
+
function fb_woo_infobanner_post_xout() {
|
40 |
+
console.log("Woo infobanner post tip xout!");
|
41 |
+
return ajax('ajax_woo_infobanner_post_xout');
|
42 |
+
}
|
assets/js/facebook-metabox.js
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
3 |
+
*
|
4 |
+
* This source code is licensed under the license found in the
|
5 |
+
* LICENSE file in the root directory of this source tree.
|
6 |
+
*
|
7 |
+
* @package FacebookCommerce
|
8 |
+
*/
|
9 |
+
|
10 |
+
/*
|
11 |
+
* Ajax helper function.
|
12 |
+
* Takes optional payload for POST and optional callback.
|
13 |
+
*/
|
14 |
+
function ajax(action, payload = null, cb = null, failcb = null) {
|
15 |
+
var data = {
|
16 |
+
'action': action,
|
17 |
+
};
|
18 |
+
if (payload){
|
19 |
+
for (var attrname in payload) { data[attrname] = payload[attrname]; }
|
20 |
+
}
|
21 |
+
|
22 |
+
// Since Wordpress 2.8 ajaxurl is always defined in admin header and
|
23 |
+
// points to admin-ajax.php
|
24 |
+
jQuery.post(ajaxurl, data, function(response) {
|
25 |
+
if(cb) {
|
26 |
+
cb(response);
|
27 |
+
}
|
28 |
+
}).fail(function(errorResponse){
|
29 |
+
if(failcb) {
|
30 |
+
failcb(errorResponse);
|
31 |
+
}
|
32 |
+
});
|
33 |
+
}
|
34 |
+
|
35 |
+
function fb_reset_product(wp_id) {
|
36 |
+
if(confirm('Resetting Facebook metadata will not remove this product from your shop. ' +
|
37 |
+
'If you have duplicated another product and are trying to publish a new Facebook product, ' +
|
38 |
+
'click OK to proceed. ' +
|
39 |
+
'Otherwise, Facebook metadata will be restored when this product is updated again.')) {
|
40 |
+
var metadata = document.querySelector('#fb_metadata');
|
41 |
+
if(metadata) {
|
42 |
+
metadata.innerHTML =
|
43 |
+
"<b>This product is not yet synced to Facebook.</b>";
|
44 |
+
}
|
45 |
+
return ajax(
|
46 |
+
'ajax_reset_single_fb_product',
|
47 |
+
{'wp_id': wp_id}
|
48 |
+
);
|
49 |
+
}
|
50 |
+
}
|
51 |
+
|
52 |
+
function fb_delete_product(wp_id) {
|
53 |
+
if(confirm('Are you sure you want to delete this product on Facebook? If you only want to "hide" the product, '+
|
54 |
+
'uncheck the "Visible" checkbox and hit "Update". If you delete a product on Facebook and hit "Update" after, ' +
|
55 |
+
'this product will be recreated. To permanently remove this product from Facebook, hit "OK" and close the window.'+
|
56 |
+
'This will not delete the product from WooCommerce.')) {
|
57 |
+
var metadata = document.querySelector('#fb_metadata');
|
58 |
+
if(metadata) {
|
59 |
+
metadata.innerHTML =
|
60 |
+
"<b>This product is not yet synced to Facebook.</b>";
|
61 |
+
}
|
62 |
+
return ajax(
|
63 |
+
'ajax_delete_fb_product',
|
64 |
+
{'wp_id': wp_id}
|
65 |
+
);
|
66 |
+
}
|
67 |
+
}
|
assets/js/facebook-products.js
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
3 |
+
*
|
4 |
+
* This source code is licensed under the license found in the
|
5 |
+
* LICENSE file in the root directory of this source tree.
|
6 |
+
*
|
7 |
+
* @package FacebookCommerce
|
8 |
+
*/
|
9 |
+
|
10 |
+
/*
|
11 |
+
* Ajax helper function.
|
12 |
+
* Takes optional payload for POST and optional callback.
|
13 |
+
*/
|
14 |
+
function ajax(action, payload = null, cb = null, failcb = null) {
|
15 |
+
var data = {
|
16 |
+
'action': action,
|
17 |
+
};
|
18 |
+
if (payload){
|
19 |
+
for (var attrname in payload) { data[attrname] = payload[attrname]; }
|
20 |
+
}
|
21 |
+
|
22 |
+
// Since Wordpress 2.8 ajaxurl is always defined in admin header and
|
23 |
+
// points to admin-ajax.php
|
24 |
+
jQuery.post(ajaxurl, data, function(response) {
|
25 |
+
if(cb) {
|
26 |
+
cb(response);
|
27 |
+
}
|
28 |
+
}).fail(function(errorResponse){
|
29 |
+
if(failcb) {
|
30 |
+
failcb(errorResponse);
|
31 |
+
}
|
32 |
+
});
|
33 |
+
}
|
34 |
+
|
35 |
+
function fb_toggle_visibility(wp_id, published) {
|
36 |
+
var buttonId = document.querySelector("#viz_" + wp_id);
|
37 |
+
var tooltip = document.querySelector("#tip_" + wp_id);
|
38 |
+
|
39 |
+
if(published){
|
40 |
+
tooltip.setAttribute('data-tip',
|
41 |
+
'Product is synced and published (visible) on Facebook.'
|
42 |
+
);
|
43 |
+
buttonId.setAttribute('onclick','fb_toggle_visibility('+wp_id+', false)');
|
44 |
+
buttonId.innerHTML = 'Hide';
|
45 |
+
buttonId.setAttribute('class', 'button');
|
46 |
+
} else {
|
47 |
+
tooltip.setAttribute('data-tip',
|
48 |
+
'Product is synced but not marked as published (visible) on Facebook.'
|
49 |
+
);
|
50 |
+
buttonId.setAttribute('onclick','fb_toggle_visibility('+wp_id+', true)');
|
51 |
+
buttonId.innerHTML = 'Show';
|
52 |
+
buttonId.setAttribute('class', 'button button-primary button-large');
|
53 |
+
}
|
54 |
+
|
55 |
+
//Reset tooltip
|
56 |
+
jQuery(function($) {
|
57 |
+
$('.tips').tipTip({
|
58 |
+
'attribute': 'data-tip',
|
59 |
+
'fadeIn': 50,
|
60 |
+
'fadeOut': 50,
|
61 |
+
'delay': 200
|
62 |
+
});
|
63 |
+
});
|
64 |
+
|
65 |
+
return ajax(
|
66 |
+
'ajax_fb_toggle_visibility',
|
67 |
+
{'wp_id': wp_id, 'published': published}
|
68 |
+
);
|
69 |
+
}
|
assets/js/facebook-settings.js
ADDED
@@ -0,0 +1,882 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
3 |
+
*
|
4 |
+
* This source code is licensed under the license found in the
|
5 |
+
* LICENSE file in the root directory of this source tree.
|
6 |
+
*
|
7 |
+
* @package FacebookCommerce
|
8 |
+
*/
|
9 |
+
|
10 |
+
var fb_sync_no_response_count = 0;
|
11 |
+
var fb_show_advanced_options = false;
|
12 |
+
|
13 |
+
function toggleAdvancedOptions() {
|
14 |
+
var opts = document.getElementById("fbAdvancedOptions");
|
15 |
+
if (!fb_show_advanced_options) {
|
16 |
+
opts.style.display = "block";
|
17 |
+
document.getElementById('fbAdvancedOptionsText').innerHTML = 'Hide Advanced Settings';
|
18 |
+
} else {
|
19 |
+
opts.style.display = "none";
|
20 |
+
document.getElementById('fbAdvancedOptionsText').innerHTML = 'Show Advanced Settings';
|
21 |
+
}
|
22 |
+
fb_show_advanced_options = !fb_show_advanced_options;
|
23 |
+
}
|
24 |
+
|
25 |
+
function openPopup() {
|
26 |
+
var width = 1153;
|
27 |
+
var height = 808;
|
28 |
+
var topPos = screen.height / 2 - height / 2;
|
29 |
+
var leftPos = screen.width / 2 - width / 2;
|
30 |
+
window.originParam = window.location.protocol + '//' + window.location.host;
|
31 |
+
var popupUrl;
|
32 |
+
if(window.facebookAdsToolboxConfig.popupOrigin.includes('staticxx')) {
|
33 |
+
window.facebookAdsToolboxConfig.popupOrigin = 'https://www.facebook.com/';
|
34 |
+
}
|
35 |
+
window.facebookAdsToolboxConfig.popupOrigin = prepend_protocol(
|
36 |
+
window.facebookAdsToolboxConfig.popupOrigin
|
37 |
+
);
|
38 |
+
popupUrl = window.facebookAdsToolboxConfig.popupOrigin;
|
39 |
+
|
40 |
+
var path = '/ads/dia';
|
41 |
+
var page = window.open(popupUrl + '/login.php?display=popup&next=' + encodeURIComponent(popupUrl + path + '?origin=' + window.originParam + ' &merchant_settings_id=' + window.facebookAdsToolboxConfig.diaSettingId), 'DiaWizard', ['toolbar=no', 'location=no', 'directories=no', 'status=no', 'menubar=no', 'scrollbars=no', 'resizable=no', 'copyhistory=no', 'width=' + width, 'height=' + height, 'top=' + topPos, 'left=' + leftPos].join(','));
|
42 |
+
|
43 |
+
return function (type, params) {
|
44 |
+
page.postMessage({
|
45 |
+
type: type,
|
46 |
+
params: params
|
47 |
+
}, window.facebookAdsToolboxConfig.popupOrigin);
|
48 |
+
};
|
49 |
+
}
|
50 |
+
|
51 |
+
function prepend_protocol(url) {
|
52 |
+
// Preprend https if the url begis with //www.
|
53 |
+
if (url.indexOf('//www.') === 0) {
|
54 |
+
url = 'https:' + url;
|
55 |
+
}
|
56 |
+
return url;
|
57 |
+
}
|
58 |
+
|
59 |
+
function get_product_catalog_id_box() {
|
60 |
+
return document.querySelector('#woocommerce_facebookcommerce_fb_product_catalog_id') || null;
|
61 |
+
}
|
62 |
+
function get_pixel_id_box() {
|
63 |
+
return document.querySelector('#woocommerce_facebookcommerce_fb_pixel_id') || null;
|
64 |
+
}
|
65 |
+
function get_pixel_use_pii_id_box() {
|
66 |
+
return document.querySelector('#woocommerce_facebookcommerce_fb_pixel_use_pii') || null;
|
67 |
+
}
|
68 |
+
function get_api_key_box() {
|
69 |
+
return document.querySelector('#woocommerce_facebookcommerce_fb_api_key') || null;
|
70 |
+
}
|
71 |
+
function get_page_id_box() {
|
72 |
+
return document.querySelector('#woocommerce_facebookcommerce_fb_page_id') || null;
|
73 |
+
}
|
74 |
+
function get_ems_id_box() {
|
75 |
+
return document.querySelector('#woocommerce_facebookcommerce_fb_external_merchant_settings_id') || null;
|
76 |
+
}
|
77 |
+
|
78 |
+
/*
|
79 |
+
* Ajax helper function.
|
80 |
+
* Takes optional payload for POST and optional callback.
|
81 |
+
*/
|
82 |
+
function ajax(action, payload = null, callback = null, failcallback = null) {
|
83 |
+
var data = {
|
84 |
+
'action': action,
|
85 |
+
};
|
86 |
+
if (payload){
|
87 |
+
for (var attrname in payload) { data[attrname] = payload[attrname]; }
|
88 |
+
}
|
89 |
+
|
90 |
+
// Since Wordpress 2.8 ajaxurl is always defined in admin header and
|
91 |
+
// points to admin-ajax.php
|
92 |
+
jQuery.post(ajaxurl, data, function(response) {
|
93 |
+
if(callback) {
|
94 |
+
callback(response);
|
95 |
+
}
|
96 |
+
}).fail(function(errorResponse){
|
97 |
+
if(failcallback) {
|
98 |
+
failcallback(errorResponse);
|
99 |
+
}
|
100 |
+
});
|
101 |
+
}
|
102 |
+
|
103 |
+
var settings = {'facebook_for_woocommerce' : 1};
|
104 |
+
var pixel_settings = {'facebook_for_woocommerce' : 1};
|
105 |
+
|
106 |
+
function facebookConfig() {
|
107 |
+
window.sendToFacebook = openPopup();
|
108 |
+
window.diaConfig = { 'clientSetup': window.facebookAdsToolboxConfig };
|
109 |
+
}
|
110 |
+
|
111 |
+
function fb_flush(){
|
112 |
+
console.log("Removing all FBIDs from all products!");
|
113 |
+
return ajax('ajax_reset_all_fb_products');
|
114 |
+
}
|
115 |
+
|
116 |
+
function sync_confirm(verbose = null) {
|
117 |
+
var msg = '';
|
118 |
+
switch (verbose) {
|
119 |
+
case 'fb_force_resync':
|
120 |
+
msg = 'Your products will now be resynced with Facebook, ' +
|
121 |
+
'this may take some time.';
|
122 |
+
break;
|
123 |
+
case 'fb_test_product_sync':
|
124 |
+
msg = 'Launch Test?';
|
125 |
+
break;
|
126 |
+
default:
|
127 |
+
msg = 'Facebook for WooCommerce automatically syncs your products on ' +
|
128 |
+
'create/update. Are you sure you want to force product resync? ' +
|
129 |
+
'This will query all published products and may take some time. ' +
|
130 |
+
'You only need to do this if your products are out of sync ' +
|
131 |
+
'or some of your products did not sync.';
|
132 |
+
}
|
133 |
+
if(confirm(msg)) {
|
134 |
+
sync_all_products(
|
135 |
+
window.facebookAdsToolboxConfig.feed.hasClientSideFeedUpload,
|
136 |
+
verbose == 'fb_test_product_sync'
|
137 |
+
);
|
138 |
+
window.fb_sync_start_time = new Date().getTime();
|
139 |
+
}
|
140 |
+
}
|
141 |
+
|
142 |
+
// Launch the confirm dialog immediately if the param is in the URL.
|
143 |
+
if (window.location.href.includes("fb_force_resync")) {
|
144 |
+
window.onload = function() { sync_confirm("fb_force_resync"); };
|
145 |
+
} else if (window.location.href.includes("fb_test_product_sync")) {
|
146 |
+
// Test products sync by feed.
|
147 |
+
window.is_test = true;
|
148 |
+
window.onload = function() { sync_confirm("fb_test_product_sync"); };
|
149 |
+
}
|
150 |
+
|
151 |
+
function sync_all_products($using_feed = false, $is_test = false) {
|
152 |
+
if (get_product_catalog_id_box() && !get_product_catalog_id_box().value){
|
153 |
+
return;
|
154 |
+
}
|
155 |
+
if (get_api_key_box() && !get_api_key_box().value){
|
156 |
+
return;
|
157 |
+
}
|
158 |
+
console.log('Syncing all products!');
|
159 |
+
window.fb_connected = true;
|
160 |
+
sync_in_progress();
|
161 |
+
if ($using_feed) {
|
162 |
+
window.facebookAdsToolboxConfig.feed.hasClientSideFeedUpload = true;
|
163 |
+
window.feed_upload = true;
|
164 |
+
ping_feed_status_queue();
|
165 |
+
return $is_test ? ajax('ajax_test_sync_products_using_feed')
|
166 |
+
: ajax('ajax_sync_all_fb_products_using_feed');
|
167 |
+
} else {
|
168 |
+
return ajax('ajax_sync_all_fb_products');
|
169 |
+
}
|
170 |
+
}
|
171 |
+
|
172 |
+
// Reset all state
|
173 |
+
function delete_all_settings(callback = null, failcallback = null) {
|
174 |
+
if (get_product_catalog_id_box()) {
|
175 |
+
get_product_catalog_id_box().value = '';
|
176 |
+
}
|
177 |
+
if(get_pixel_id_box()) {
|
178 |
+
get_pixel_id_box().value = '';
|
179 |
+
}
|
180 |
+
if(get_pixel_use_pii_id_box()) {
|
181 |
+
get_pixel_use_pii_id_box().checked = false;
|
182 |
+
}
|
183 |
+
if(get_api_key_box()) {
|
184 |
+
get_api_key_box().value = '';
|
185 |
+
}
|
186 |
+
if(get_page_id_box()) {
|
187 |
+
get_page_id_box().value = '';
|
188 |
+
}
|
189 |
+
if(get_ems_id_box()) {
|
190 |
+
get_ems_id_box().value = '';
|
191 |
+
}
|
192 |
+
|
193 |
+
window.facebookAdsToolboxConfig.pixel.pixelId = '';
|
194 |
+
window.facebookAdsToolboxConfig.diaSettingId = '';
|
195 |
+
|
196 |
+
reset_buttons();
|
197 |
+
window.fb_connected = false;
|
198 |
+
|
199 |
+
console.log('Deleting all settings and removing all FBIDs!');
|
200 |
+
return ajax('ajax_delete_fb_settings', null, callback, failcallback);
|
201 |
+
}
|
202 |
+
|
203 |
+
// save_settings and save_settings_and_sync should only be called once
|
204 |
+
// after all variables are set up in the settings global variable
|
205 |
+
// if called multiple times, race conditions might occur
|
206 |
+
// ---
|
207 |
+
// It's also called again if the pixel id is ever changed or pixel pii is
|
208 |
+
// enabled or disabled.
|
209 |
+
function save_settings(callback = null, failcallback = null, localsettings = null){
|
210 |
+
if (!localsettings) {
|
211 |
+
localsettings = settings;
|
212 |
+
}
|
213 |
+
ajax('ajax_save_fb_settings', localsettings,
|
214 |
+
function(response){
|
215 |
+
if(callback) {
|
216 |
+
callback(response);
|
217 |
+
}
|
218 |
+
},
|
219 |
+
function(errorResponse){
|
220 |
+
if(failcallback) {
|
221 |
+
failcallback(errorResponse);
|
222 |
+
}
|
223 |
+
}
|
224 |
+
);
|
225 |
+
}
|
226 |
+
|
227 |
+
// save_settings wrapper for plugins as we do not need to:
|
228 |
+
// 1. sync products again after plugin is configured
|
229 |
+
// 2. check api_key, which is from facebook and is only necessary
|
230 |
+
// for following sync products
|
231 |
+
function save_settings_for_plugin(callback, failcallback) {
|
232 |
+
save_settings(
|
233 |
+
function(response){
|
234 |
+
if (response && response.includes('settings_saved')){
|
235 |
+
console.log(response);
|
236 |
+
callback(response);
|
237 |
+
} else {
|
238 |
+
console.log('Fail response on save_settings_and_sync');
|
239 |
+
failcallback(response);
|
240 |
+
}
|
241 |
+
},
|
242 |
+
function(errorResponse){
|
243 |
+
console.log('Ajax error while saving settings:' + JSON.stringify(errorResponse));
|
244 |
+
failcallback(errorResponse);
|
245 |
+
});
|
246 |
+
}
|
247 |
+
|
248 |
+
// see comments in save_settings function above
|
249 |
+
function save_settings_and_sync(message) {
|
250 |
+
if ('api_key' in settings){
|
251 |
+
save_settings(
|
252 |
+
function(response){
|
253 |
+
if (response && response.includes('settings_saved')){
|
254 |
+
console.log(response);
|
255 |
+
//Final acks
|
256 |
+
window.sendToFacebook('ack set pixel', message.params);
|
257 |
+
window.sendToFacebook('ack set page access token', message.params);
|
258 |
+
window.sendToFacebook('ack set merchant settings', message.params);
|
259 |
+
sync_all_products(true);
|
260 |
+
} else {
|
261 |
+
window.sendToFacebook('fail save_settings', response);
|
262 |
+
console.log('Fail response on save_settings_and_sync');
|
263 |
+
}
|
264 |
+
},
|
265 |
+
function(errorResponse){
|
266 |
+
console.log('Ajax error while saving settings:' + JSON.stringify(errorResponse));
|
267 |
+
window.sendToFacebook('fail save_settings_ajax', JSON.stringify(errorResponse));
|
268 |
+
}
|
269 |
+
);
|
270 |
+
}
|
271 |
+
}
|
272 |
+
|
273 |
+
//Reset buttons to brand new setup state
|
274 |
+
function reset_buttons(){
|
275 |
+
if(document.querySelector('#settings')){
|
276 |
+
document.querySelector('#settings').style.display = 'none';
|
277 |
+
}
|
278 |
+
if(document.querySelector('#cta_button')){
|
279 |
+
var cta_element = document.querySelector('#cta_button');
|
280 |
+
cta_element.innerHTML = 'Get Started';
|
281 |
+
cta_element.style['font-size'] = '13px';
|
282 |
+
cta_element.style.width = '80px';
|
283 |
+
cta_element.href = '#';
|
284 |
+
cta_element.onclick= function() { facebookConfig(); };
|
285 |
+
}
|
286 |
+
if(document.querySelector('#learnmore_button')){
|
287 |
+
document.querySelector('#learnmore_button').style.display = 'none';
|
288 |
+
}
|
289 |
+
if(document.querySelector('#setup_h1')) {
|
290 |
+
document.querySelector('#setup_h1').innerHTML =
|
291 |
+
'Grow your business on Facebook';
|
292 |
+
}
|
293 |
+
if(document.querySelector('#setup_l1')){
|
294 |
+
document.querySelector('#setup_l1').innerHTML =
|
295 |
+
'Easily install a tracking pixel';
|
296 |
+
}
|
297 |
+
if(document.querySelector('#setup_l2')){
|
298 |
+
document.querySelector('#setup_l2').innerHTML =
|
299 |
+
'Upload your products and create a shop';
|
300 |
+
}
|
301 |
+
if(document.querySelector('#setup_l3')){
|
302 |
+
document.querySelector('#setup_l3').innerHTML =
|
303 |
+
'Create dynamic ads with your products and pixel';
|
304 |
+
}
|
305 |
+
}
|
306 |
+
|
307 |
+
//Remove reset/settings buttons during product sync
|
308 |
+
function sync_in_progress(){
|
309 |
+
if(document.querySelector('#settings')){
|
310 |
+
document.querySelector('#settings').style.display = '';
|
311 |
+
}
|
312 |
+
if(document.querySelector('#connection_status')){
|
313 |
+
document.querySelector('#connection_status').style.display = '';
|
314 |
+
}
|
315 |
+
if(document.querySelector('#sync_complete')){
|
316 |
+
document.querySelector('#sync_complete').style.display = 'none';
|
317 |
+
}
|
318 |
+
//Get rid of all the buttons
|
319 |
+
if(document.querySelector('#setting_button')){
|
320 |
+
document.querySelector('#setting_button').style['pointer-events'] = 'none';
|
321 |
+
}
|
322 |
+
if(document.querySelector('#resync_products')) {
|
323 |
+
document.querySelector('#resync_products').style['pointer-events'] = 'none';
|
324 |
+
}
|
325 |
+
if(document.querySelector('#test_product_sync')) {
|
326 |
+
document.querySelector('#test_product_sync').style.display = 'none';
|
327 |
+
}
|
328 |
+
//Set a product sync status
|
329 |
+
if(document.querySelector('#sync_progress')){
|
330 |
+
document.querySelector('#sync_progress').innerHTML =
|
331 |
+
'Syncing... Keep this browser open <br/>' +
|
332 |
+
'Until sync is complete<br/>' +
|
333 |
+
'<div class="loader"></div>';
|
334 |
+
}
|
335 |
+
}
|
336 |
+
|
337 |
+
function sync_not_in_progress(){
|
338 |
+
// Reset to pre-setup state.
|
339 |
+
if(document.querySelector('#cta_button')){
|
340 |
+
var cta_element = document.querySelector('#cta_button');
|
341 |
+
cta_element.innerHTML = 'Create Ad';
|
342 |
+
cta_element.style['font-size'] = '12px';
|
343 |
+
cta_element.style.width = '60px';
|
344 |
+
if (window.facebookAdsToolboxConfig.diaSettingId) {
|
345 |
+
cta_element.onclick= function() {
|
346 |
+
window.open('https://www.facebook.com/ads/dia/redirect/?settings_id=' +
|
347 |
+
window.facebookAdsToolboxConfig.diaSettingId + '&version=2' +
|
348 |
+
'&entry_point=admin_panel');
|
349 |
+
};
|
350 |
+
} else {
|
351 |
+
cta_element.style['pointer-events'] = 'none';
|
352 |
+
}
|
353 |
+
}
|
354 |
+
if(document.querySelector('#learnmore_button')){
|
355 |
+
var learnmore_element = document.querySelector('#learnmore_button');
|
356 |
+
if (window.facebookAdsToolboxConfig.diaSettingId) {
|
357 |
+
learnmore_element.style.display = '';
|
358 |
+
}
|
359 |
+
}
|
360 |
+
if(document.querySelector('#setup_h1')) {
|
361 |
+
document.querySelector('#setup_h1').innerHTML =
|
362 |
+
'Reach the right people and sell more products';
|
363 |
+
}
|
364 |
+
if(document.querySelector('#setup_l1')){
|
365 |
+
document.querySelector('#setup_l1').innerHTML =
|
366 |
+
'Create an ad in a few steps';
|
367 |
+
}
|
368 |
+
if(document.querySelector('#setup_l2')){
|
369 |
+
document.querySelector('#setup_l2').innerHTML =
|
370 |
+
'Use built-in best practice for online sales';
|
371 |
+
}
|
372 |
+
if(document.querySelector('#setup_l3')){
|
373 |
+
document.querySelector('#setup_l3').innerHTML =
|
374 |
+
'Get reporting on sales and revenue';
|
375 |
+
}
|
376 |
+
if(document.querySelector('#settings')){
|
377 |
+
document.querySelector('#settings').style.display = '';
|
378 |
+
}
|
379 |
+
// Enable buttons.
|
380 |
+
if(document.querySelector('#setting_button')){
|
381 |
+
document.querySelector('#setting_button').style['pointer-events'] = 'auto';
|
382 |
+
}
|
383 |
+
if(document.querySelector('#resync_products')) {
|
384 |
+
document.querySelector('#resync_products').style ['pointer-events'] = 'auto';
|
385 |
+
}
|
386 |
+
// Remove sync progress.
|
387 |
+
if(document.querySelector('#sync_progress')){
|
388 |
+
document.querySelector('#sync_progress').innerHTML = '';
|
389 |
+
}
|
390 |
+
}
|
391 |
+
|
392 |
+
function not_connected(){
|
393 |
+
if(document.querySelector('#connection_status')){
|
394 |
+
document.querySelector('#connection_status').style.display = 'none';
|
395 |
+
}
|
396 |
+
|
397 |
+
if(document.querySelector('#setting_button')){
|
398 |
+
document.querySelector('#setting_button').style['pointer-events'] = 'auto';
|
399 |
+
}
|
400 |
+
if(document.querySelector('#resync_products')) {
|
401 |
+
document.querySelector('#resync_products').style['pointer-events'] = 'none';
|
402 |
+
}
|
403 |
+
if(document.querySelector('#sync_complete')) {
|
404 |
+
document.querySelector('#sync_complete').style.display = 'none';
|
405 |
+
}
|
406 |
+
if(document.querySelector('#sync_progress')){
|
407 |
+
document.querySelector('#sync_progress').innerHTML = '';
|
408 |
+
}
|
409 |
+
}
|
410 |
+
|
411 |
+
function addAnEventListener(obj,evt,func) {
|
412 |
+
if ('addEventListener' in obj){
|
413 |
+
obj.addEventListener(evt,func, false);
|
414 |
+
} else if ('attachEvent' in obj){//IE
|
415 |
+
obj.attachEvent('on'+evt,func);
|
416 |
+
}
|
417 |
+
}
|
418 |
+
|
419 |
+
function setMerchantSettings(message) {
|
420 |
+
if (!message.params.setting_id) {
|
421 |
+
console.error('Facebook Extension Error: got no setting_id', message.params);
|
422 |
+
window.sendToFacebook('fail set merchant settings', message.params);
|
423 |
+
return;
|
424 |
+
}
|
425 |
+
if(get_ems_id_box()){
|
426 |
+
get_ems_id_box().value = message.params.setting_id;
|
427 |
+
}
|
428 |
+
|
429 |
+
settings.external_merchant_settings_id = message.params.setting_id;
|
430 |
+
|
431 |
+
//Immediately set in case button is clicked again
|
432 |
+
window.facebookAdsToolboxConfig.diaSettingId = message.params.setting_id;
|
433 |
+
//Ack merchant settings happens after settings are saved
|
434 |
+
}
|
435 |
+
|
436 |
+
function setCatalog(message) {
|
437 |
+
if (!message.params.catalog_id) {
|
438 |
+
console.error('Facebook Extension Error: got no catalog_id', message.params);
|
439 |
+
window.sendToFacebook('fail set catalog', message.params);
|
440 |
+
return;
|
441 |
+
}
|
442 |
+
if(get_api_key_box()){
|
443 |
+
get_product_catalog_id_box().value = message.params.catalog_id;
|
444 |
+
}
|
445 |
+
|
446 |
+
settings.product_catalog_id = message.params.catalog_id;
|
447 |
+
|
448 |
+
window.sendToFacebook('ack set catalog', message.params);
|
449 |
+
}
|
450 |
+
|
451 |
+
|
452 |
+
function setPixel(message) {
|
453 |
+
if (!message.params.pixel_id) {
|
454 |
+
console.error('Facebook Ads Extension Error: got no pixel_id', message.params);
|
455 |
+
window.sendToFacebook('fail set pixel', message.params);
|
456 |
+
return;
|
457 |
+
}
|
458 |
+
if(get_pixel_id_box()){
|
459 |
+
get_pixel_id_box().value = message.params.pixel_id;
|
460 |
+
}
|
461 |
+
|
462 |
+
settings.pixel_id = message.params.pixel_id;
|
463 |
+
pixel_settings.pixel_id = settings.pixel_id;
|
464 |
+
if (message.params.pixel_use_pii !== undefined) {
|
465 |
+
if(get_pixel_use_pii_id_box()){
|
466 |
+
//!! will explicitly convert truthy/falsy values to a boolean
|
467 |
+
get_pixel_use_pii_id_box().checked = !!message.params.pixel_use_pii;
|
468 |
+
}
|
469 |
+
settings.pixel_use_pii = message.params.pixel_use_pii;
|
470 |
+
pixel_settings.pixel_use_pii = settings.pixel_use_pii;
|
471 |
+
}
|
472 |
+
|
473 |
+
// We need this to support changing the pixel id after setup.
|
474 |
+
save_settings(
|
475 |
+
function(response){
|
476 |
+
if (response && response.includes('settings_saved')){
|
477 |
+
window.sendToFacebook('ack set pixel', message.params);
|
478 |
+
} //may not get settings_saved if we try to save pixel before an API key
|
479 |
+
},
|
480 |
+
function(errorResponse){
|
481 |
+
console.log(errorResponse);
|
482 |
+
window.sendToFacebook('fail set pixel', errorResponse);
|
483 |
+
},
|
484 |
+
pixel_settings
|
485 |
+
);
|
486 |
+
}
|
487 |
+
|
488 |
+
function genFeed(message) {
|
489 |
+
//no-op
|
490 |
+
}
|
491 |
+
|
492 |
+
function setAccessTokenAndPageId(message) {
|
493 |
+
if (!message.params.page_token) {
|
494 |
+
console.error('Facebook Ads Extension Error: got no page_token',
|
495 |
+
message.params);
|
496 |
+
window.sendToFacebook('fail set page access token', message.params);
|
497 |
+
return;
|
498 |
+
}
|
499 |
+
/*
|
500 |
+
Set page_token here
|
501 |
+
*/
|
502 |
+
|
503 |
+
if(get_api_key_box()){
|
504 |
+
get_api_key_box().value = message.params.page_token;
|
505 |
+
}
|
506 |
+
|
507 |
+
if(get_page_id_box()){
|
508 |
+
get_page_id_box().value = message.params.page_id;
|
509 |
+
}
|
510 |
+
|
511 |
+
settings.api_key = message.params.page_token;
|
512 |
+
settings.page_id = message.params.page_id;
|
513 |
+
//Ack token in "save_settings_and_sync" for final ack
|
514 |
+
|
515 |
+
window.facebookAdsToolboxConfig.tokenExpired = false;
|
516 |
+
if(document.querySelector('#token_text')){
|
517 |
+
document.querySelector('#token_text').innerHTML =
|
518 |
+
`<strong>Your API key has been updated.<br />
|
519 |
+
Please refresh the page.</strong>`;
|
520 |
+
}
|
521 |
+
}
|
522 |
+
|
523 |
+
function setMsgerChatSetup(data) {
|
524 |
+
if (data.hasOwnProperty('is_messenger_chat_plugin_enabled')) {
|
525 |
+
settings.is_messenger_chat_plugin_enabled =
|
526 |
+
data.is_messenger_chat_plugin_enabled;
|
527 |
+
}
|
528 |
+
if (data.hasOwnProperty('facebook_jssdk_version')) {
|
529 |
+
settings.facebook_jssdk_version =
|
530 |
+
data.facebook_jssdk_version;
|
531 |
+
}
|
532 |
+
if (data.hasOwnProperty('page_id')) {
|
533 |
+
settings.fb_page_id = data.page_id;
|
534 |
+
}
|
535 |
+
|
536 |
+
if (data.hasOwnProperty('customization')) {
|
537 |
+
var customization = data.customization;
|
538 |
+
|
539 |
+
if (customization.hasOwnProperty('greetingTextCode')) {
|
540 |
+
settings.msger_chat_customization_greeting_text_code =
|
541 |
+
customization.greetingTextCode;
|
542 |
+
}
|
543 |
+
if (customization.hasOwnProperty('locale')) {
|
544 |
+
settings.msger_chat_customization_locale =
|
545 |
+
customization.locale;
|
546 |
+
}
|
547 |
+
if (customization.hasOwnProperty('themeColorCode')) {
|
548 |
+
settings.msger_chat_customization_theme_color_code =
|
549 |
+
customization.themeColorCode;
|
550 |
+
}
|
551 |
+
}
|
552 |
+
}
|
553 |
+
|
554 |
+
function iFrameListener(event) {
|
555 |
+
// Fix for web.facebook.com
|
556 |
+
const origin = event.origin || event.originalEvent.origin;
|
557 |
+
if (origin != window.facebookAdsToolboxConfig.popupOrigin &&
|
558 |
+
urlFromSameDomain(origin, window.facebookAdsToolboxConfig.popupOrigin)) {
|
559 |
+
window.facebookAdsToolboxConfig.popupOrigin = origin;
|
560 |
+
}
|
561 |
+
|
562 |
+
switch (event.data.type) {
|
563 |
+
case 'reset':
|
564 |
+
delete_all_settings(function(res){
|
565 |
+
if(res && event.data.params) {
|
566 |
+
if(res === 'Settings Deleted'){
|
567 |
+
window.sendToFacebook('ack reset', event.data.params);
|
568 |
+
}else{
|
569 |
+
console.log(res);
|
570 |
+
alert(res);
|
571 |
+
}
|
572 |
+
}else {
|
573 |
+
console.log("Got no response from delete_all_settings");
|
574 |
+
}
|
575 |
+
},function(err){
|
576 |
+
console.error(err);
|
577 |
+
});
|
578 |
+
break;
|
579 |
+
case 'get dia settings':
|
580 |
+
window.sendToFacebook('dia settings', window.diaConfig);
|
581 |
+
break;
|
582 |
+
case 'set merchant settings':
|
583 |
+
setMerchantSettings(event.data);
|
584 |
+
break;
|
585 |
+
case 'set catalog':
|
586 |
+
setCatalog(event.data);
|
587 |
+
break;
|
588 |
+
case 'set pixel':
|
589 |
+
setPixel(event.data);
|
590 |
+
break;
|
591 |
+
case 'gen feed':
|
592 |
+
genFeed();
|
593 |
+
break;
|
594 |
+
case 'set page access token':
|
595 |
+
//Should be last message received
|
596 |
+
setAccessTokenAndPageId(event.data);
|
597 |
+
save_settings_and_sync(event.data);
|
598 |
+
break;
|
599 |
+
case 'set msger chat':
|
600 |
+
setMsgerChatSetup(event.data.params);
|
601 |
+
save_settings_for_plugin(
|
602 |
+
function(response) {
|
603 |
+
window.sendToFacebook('ack msger chat', event.data);
|
604 |
+
},
|
605 |
+
function(response) {
|
606 |
+
window.sendToFacebook('fail ack msger chat', event.data);
|
607 |
+
});
|
608 |
+
break;
|
609 |
+
}
|
610 |
+
}
|
611 |
+
|
612 |
+
addAnEventListener(window,'message',iFrameListener);
|
613 |
+
|
614 |
+
function urlFromSameDomain(url1, url2) {
|
615 |
+
if (!url1.startsWith('http') || !url2.startsWith('http')) {
|
616 |
+
return false;
|
617 |
+
}
|
618 |
+
var u1 = parseURL(url1);
|
619 |
+
var u2 = parseURL(url2);
|
620 |
+
var u1host = u1.host.replace(/^\w+\./, 'www.');
|
621 |
+
var u2host = u2.host.replace(/^\w+\./, 'www.');
|
622 |
+
return u1.protocol === u2.protocol && u1host === u2host;
|
623 |
+
}
|
624 |
+
|
625 |
+
function parseURL(url) {
|
626 |
+
var parser = document.createElement('a');
|
627 |
+
parser.href = url;
|
628 |
+
return parser;
|
629 |
+
}
|
630 |
+
|
631 |
+
// Only do pings for supporting older (pre 1.8) setups.
|
632 |
+
window.fb_pings =
|
633 |
+
(window.facebookAdsToolboxConfig.feed.hasClientSideFeedUpload) ?
|
634 |
+
null :
|
635 |
+
setInterval(function(){
|
636 |
+
console.log("Pinging queue...");
|
637 |
+
check_queues();
|
638 |
+
}, 10000);
|
639 |
+
|
640 |
+
function ping_feed_status_queue(count = 0) {
|
641 |
+
window.fb_feed_pings = setInterval(function() {
|
642 |
+
console.log('Pinging feed uploading queue...');
|
643 |
+
check_feed_upload_queue(count);
|
644 |
+
}, 30000*(1 << count));
|
645 |
+
}
|
646 |
+
|
647 |
+
function product_sync_complete(sync_progress_element){
|
648 |
+
sync_not_in_progress();
|
649 |
+
if(document.querySelector('#sync_complete')){
|
650 |
+
document.querySelector('#sync_complete').style.display = '';
|
651 |
+
}
|
652 |
+
if(sync_progress_element) {
|
653 |
+
sync_progress_element.innerHTML = '';
|
654 |
+
}
|
655 |
+
clearInterval(window.fb_pings);
|
656 |
+
}
|
657 |
+
|
658 |
+
function check_queues(){
|
659 |
+
ajax('ajax_fb_background_check_queue',
|
660 |
+
{"request_time": new Date().getTime()}, function(response){
|
661 |
+
if (window.feed_upload) {
|
662 |
+
clearInterval(window.fb_pings);
|
663 |
+
return;
|
664 |
+
}
|
665 |
+
var sync_progress_element = document.querySelector('#sync_progress');
|
666 |
+
var res = parse_response_check_connection(response);
|
667 |
+
if (!res) {
|
668 |
+
if (fb_sync_no_response_count++ > 5) {
|
669 |
+
clearInterval(window.fb_pings);
|
670 |
+
}
|
671 |
+
return;
|
672 |
+
}
|
673 |
+
fb_sync_no_response_count = 0;
|
674 |
+
|
675 |
+
if(res){
|
676 |
+
if(!res.background){
|
677 |
+
console.log("No background sync found, disabling pings");
|
678 |
+
clearInterval(window.fb_pings);
|
679 |
+
}
|
680 |
+
|
681 |
+
var processing = !!res.processing; //explicit boolean conversion
|
682 |
+
var remaining = res.remaining;
|
683 |
+
if(processing) {
|
684 |
+
if(sync_progress_element) {
|
685 |
+
sync_progress_element.innerHTML =
|
686 |
+
'<strong>Progress:</strong> ' + remaining + ' item' +
|
687 |
+
(remaining > 1 ? 's' : '') + ' remaining.';
|
688 |
+
}
|
689 |
+
if(remaining === 0){
|
690 |
+
product_sync_complete(sync_progress_element);
|
691 |
+
}
|
692 |
+
} else {
|
693 |
+
//Not processing, none remaining. Either long complete, or just completed
|
694 |
+
if(window.fb_sync_start_time && res.request_time){
|
695 |
+
var request_time = new Date(parseInt(res.request_time));
|
696 |
+
if(window.fb_sync_start_time > request_time){
|
697 |
+
//Old ping, do nothing.
|
698 |
+
console.log("OLD PING");
|
699 |
+
return;
|
700 |
+
}
|
701 |
+
}
|
702 |
+
|
703 |
+
if(remaining === 0){
|
704 |
+
product_sync_complete(sync_progress_element);
|
705 |
+
}
|
706 |
+
}
|
707 |
+
}
|
708 |
+
});
|
709 |
+
}
|
710 |
+
|
711 |
+
function parse_response_check_connection(res) {
|
712 |
+
if (res) {
|
713 |
+
console.log(res);
|
714 |
+
var response = res.substring(res.indexOf("{")); //Trim leading extra chars (rnrnr)
|
715 |
+
response = JSON.parse(response);
|
716 |
+
if(!response.connected && !window.fb_connected){
|
717 |
+
not_connected();
|
718 |
+
return null;
|
719 |
+
}
|
720 |
+
return response;
|
721 |
+
}
|
722 |
+
return null;
|
723 |
+
}
|
724 |
+
|
725 |
+
function check_feed_upload_queue(check_num) {
|
726 |
+
ajax('ajax_check_feed_upload_status', null, function(response) {
|
727 |
+
var sync_progress_element = document.querySelector('#sync_progress');
|
728 |
+
var res = parse_response_check_connection(response);
|
729 |
+
clearInterval(window.fb_feed_pings);
|
730 |
+
if (res) {
|
731 |
+
var status = res.status;
|
732 |
+
switch (status) {
|
733 |
+
case 'complete':
|
734 |
+
window.feed_upload = false;
|
735 |
+
if (window.is_test) {
|
736 |
+
display_test_result();
|
737 |
+
} else {
|
738 |
+
product_sync_complete(sync_progress_element);
|
739 |
+
}
|
740 |
+
break;
|
741 |
+
case 'in progress':
|
742 |
+
if (sync_progress_element) {
|
743 |
+
sync_progress_element.innerHTML =
|
744 |
+
'Syncing... Keep this browser open <br/>' +
|
745 |
+
'Until sync is complete<br/>';
|
746 |
+
}
|
747 |
+
ping_feed_status_queue(check_num+1);
|
748 |
+
break;
|
749 |
+
default:
|
750 |
+
sync_progress_element.innerHTML =
|
751 |
+
'<strong>Something wrong when uploading, please try again.</strong>';
|
752 |
+
window.feed_upload = false;
|
753 |
+
if (window.is_test) {
|
754 |
+
display_test_result();
|
755 |
+
}
|
756 |
+
}
|
757 |
+
}
|
758 |
+
});
|
759 |
+
}
|
760 |
+
|
761 |
+
function display_test_result() {
|
762 |
+
ajax('ajax_display_test_result', null, function(response) {
|
763 |
+
var sync_complete_element = document.querySelector('#sync_complete');
|
764 |
+
var sync_progress_element = document.querySelector('#sync_progress');
|
765 |
+
var res = parse_response_check_connection(response);
|
766 |
+
if (res) {
|
767 |
+
var status = res.pass;
|
768 |
+
switch (status) {
|
769 |
+
case 'true':
|
770 |
+
sync_not_in_progress();
|
771 |
+
if(sync_complete_element) {
|
772 |
+
sync_complete_element.style.display = '';
|
773 |
+
sync_complete_element.innerHTML =
|
774 |
+
'<strong>Status: </strong>Test Pass.';
|
775 |
+
}
|
776 |
+
if(sync_progress_element) {
|
777 |
+
sync_progress_element.innerHTML = '';
|
778 |
+
}
|
779 |
+
window.is_test = false;
|
780 |
+
break;
|
781 |
+
case 'in progress':
|
782 |
+
if (sync_progress_element) {
|
783 |
+
sync_progress_element.innerHTML =
|
784 |
+
'<strong>Integration test in progress...</strong>';
|
785 |
+
}
|
786 |
+
ping_feed_status_queue();
|
787 |
+
break;
|
788 |
+
default:
|
789 |
+
window.debug_info = res.debug_info + '<br/>' + res.stack_trace;
|
790 |
+
if(sync_complete_element) {
|
791 |
+
sync_complete_element.style.display = '';
|
792 |
+
sync_complete_element.innerHTML =
|
793 |
+
'<strong>Status: </strong>Test Fail.';
|
794 |
+
}
|
795 |
+
if (sync_progress_element) {
|
796 |
+
sync_progress_element.innerHTML = '';
|
797 |
+
}
|
798 |
+
if(document.querySelector('#debug_info')) {
|
799 |
+
document.querySelector('#debug_info').style.display = '';
|
800 |
+
}
|
801 |
+
window.is_test = false;
|
802 |
+
}
|
803 |
+
}
|
804 |
+
});
|
805 |
+
}
|
806 |
+
|
807 |
+
function show_debug_info() {
|
808 |
+
var stack_trace_element = document.querySelector('#stack_trace');
|
809 |
+
if(stack_trace_element) {
|
810 |
+
stack_trace_element.innerHTML = window.debug_info;
|
811 |
+
}
|
812 |
+
if(document.querySelector('#debug_info')) {
|
813 |
+
document.querySelector('#debug_info').style.display = 'none';
|
814 |
+
}
|
815 |
+
window.debug_info = '';
|
816 |
+
}
|
817 |
+
|
818 |
+
function fbe_init_nux_messages() {
|
819 |
+
var jQuery = window.jQuery;
|
820 |
+
jQuery(function() {
|
821 |
+
jQuery.each(jQuery('.nux-message'), function(_index, nux_msg) {
|
822 |
+
var nux_msg_elem = jQuery(nux_msg);
|
823 |
+
var targetid = nux_msg_elem.data('target');
|
824 |
+
var target_elem = jQuery('#' + targetid);
|
825 |
+
var t_pos = target_elem.position();
|
826 |
+
var t_half_height = target_elem.height() / 2;
|
827 |
+
var t_width = target_elem.outerWidth();
|
828 |
+
nux_msg_elem.css({
|
829 |
+
'top': '' + Math.ceil(t_pos.top + t_half_height) + 'px',
|
830 |
+
'left': '' + Math.ceil(t_pos.left + t_width) + 'px',
|
831 |
+
'display': 'block'
|
832 |
+
});
|
833 |
+
jQuery('.nux-message-close-btn', nux_msg_elem).click(function() {
|
834 |
+
jQuery(nux_msg).hide();
|
835 |
+
});
|
836 |
+
});
|
837 |
+
});
|
838 |
+
}
|
839 |
+
|
840 |
+
function saveAutoSyncSchedule() {
|
841 |
+
var isChecked = document.getElementsByClassName('autosyncCheck')[0].checked;
|
842 |
+
var timebox = document.getElementsByClassName('autosyncTime')[0];
|
843 |
+
var button = document.getElementsByClassName('autosyncSaveButton')[0];
|
844 |
+
var saved = document.getElementsByClassName('autosyncSavedNotice')[0];
|
845 |
+
|
846 |
+
if (!isChecked) {
|
847 |
+
timebox.setAttribute('disabled', true);
|
848 |
+
} else {
|
849 |
+
timebox.removeAttribute('disabled');
|
850 |
+
saved.style.transition = '';
|
851 |
+
saved.style.opacity = 1;
|
852 |
+
// Fade out the small 'Saved' after 3 seconds.
|
853 |
+
setTimeout(function() {
|
854 |
+
saved.style.opacity = 0;
|
855 |
+
saved.style.transition = 'opacity 5s';}
|
856 |
+
,3000);
|
857 |
+
}
|
858 |
+
|
859 |
+
ajax('ajax_schedule_force_resync', {"enabled": isChecked ? 1 : 0, "time": timebox.value});
|
860 |
+
}
|
861 |
+
|
862 |
+
function onSetDisableSyncOnDevEnvironment() {
|
863 |
+
var isChecked = document.getElementsByClassName('disableOnDevEnvironment')[0].checked;
|
864 |
+
ajax(
|
865 |
+
'ajax_update_fb_option',
|
866 |
+
{
|
867 |
+
"option": "fb_disable_sync_on_dev_environment",
|
868 |
+
"option_value": isChecked ? 1 : 0
|
869 |
+
}
|
870 |
+
);
|
871 |
+
}
|
872 |
+
|
873 |
+
function syncShortDescription() {
|
874 |
+
var isChecked = document.getElementsByClassName('syncShortDescription')[0].checked;
|
875 |
+
ajax(
|
876 |
+
'ajax_update_fb_option',
|
877 |
+
{
|
878 |
+
"option": "fb_sync_short_description",
|
879 |
+
"option_value": isChecked ? 1 : 0
|
880 |
+
}
|
881 |
+
);
|
882 |
+
}
|
changelog.txt
ADDED
@@ -0,0 +1,399 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
*** Facebook for WooCommerce Changelog ***
|
2 |
+
2019-02-26 - Version 1.9.11
|
3 |
+
* changing contributor to facebook from facebook4woocommerce, so that
|
4 |
+
woo plugin will be shown under https://profiles.wordpress.org/facebook/#content-plugins
|
5 |
+
* adding changelog in readme.txt so that notifications will be sent for updates and
|
6 |
+
changelog will be shown under https://wordpress.org/plugins/facebook-for-woocommerce/#developers
|
7 |
+
* removing debug flags notice under facebook-for-woocommerce.php so that developers will
|
8 |
+
be able to debug with debug logs
|
9 |
+
|
10 |
+
2019-02-11 - Version 1.9.10
|
11 |
+
* Add facebook support link, this will help merchants to reach out to facebook customer service.
|
12 |
+
* Make plugin wordpress compatible by removing woocommerce updater and removing woo_include
|
13 |
+
|
14 |
+
2018-12-30 - Version 1.9.9
|
15 |
+
* Fix issue with missing file in v1.9.8
|
16 |
+
* Remove misleading content relating to Instagram which is not launched yet.
|
17 |
+
|
18 |
+
2018-11-30 - Version 1.9.8
|
19 |
+
* Prevent Show/Hide button auto scroll.
|
20 |
+
* DIY entry field for FB product Image.
|
21 |
+
* Initial Support for Advanced Bulk Edit.
|
22 |
+
* Added Option to Use short description instead of description.
|
23 |
+
* Added checkbox for disable sync with FB for dev environment.
|
24 |
+
* Add New Advanced Option : Auto Force Resync on Schedule.
|
25 |
+
* Don't sync out of stock items to FB depending on Woo setting.
|
26 |
+
* Add custom price.
|
27 |
+
* Fix 502 Bad Gateway Error When Redirect.
|
28 |
+
* Sync composite product with calculating the price based on the price of sub-items.
|
29 |
+
* Advanced Options Toggle in Configuration Screen.
|
30 |
+
|
31 |
+
2018-11-01 - Version 1.9.7
|
32 |
+
* Support messenger chat customization dialog
|
33 |
+
* Add Copyright header.
|
34 |
+
* Fix lowercasting product description.
|
35 |
+
* Fix Connect Woo AYMT Logic Flow and improve Logging.
|
36 |
+
|
37 |
+
2018-09-21 - Version 1.9.6
|
38 |
+
* Update plugin description of new design for WooCommerce.
|
39 |
+
* Remove get_date() in Woocommerce plugin.
|
40 |
+
* Add External Action-to-Make Channel Support.
|
41 |
+
|
42 |
+
2018-08-14 - Version 1.9.5
|
43 |
+
* Fix Subscription Event Injection
|
44 |
+
|
45 |
+
2018-08-10 - Version 1.9.4
|
46 |
+
* Support Lead Gen Event - Contact Form 7
|
47 |
+
* Separate Redirect Entry Point Logging
|
48 |
+
* Fix undefined variable warnings.
|
49 |
+
* Add a Settings link to the plugin config page from the WP Plugins page.
|
50 |
+
* Adding filter to Pixel init for injection of fbq(consent, revoke) (GDPR)
|
51 |
+
|
52 |
+
2018-08-01 - Version 1.9.3
|
53 |
+
* Add Edge Cases for Integration Test.
|
54 |
+
* Fix Undefined PHP Warning.
|
55 |
+
* Add subscribe event.
|
56 |
+
* Fix Unable to Change Pixel after Setup.
|
57 |
+
* Fix Integration Test Confirmation Dialog.
|
58 |
+
|
59 |
+
2018-07-08 - Version 1.9.2
|
60 |
+
* Exclude Virtual Variation and Set Staging.
|
61 |
+
* Add Version Number for Logging.
|
62 |
+
|
63 |
+
2018-06-22 - version 1.9.1
|
64 |
+
* Fix Page Name Extra Space .
|
65 |
+
* Remove Strange Box in Design.
|
66 |
+
|
67 |
+
2018-06-15 - version 1.9.0
|
68 |
+
* Fix Performance Issue by Reusing Existing wp_query Object Contents.
|
69 |
+
* Rename Long Column Which Causes UI Issues for Users in Product Overview.
|
70 |
+
* Update Admin Notice Content.
|
71 |
+
* Fix Weird ID Error Message.
|
72 |
+
* Fix Product ID for Brand Attribute.
|
73 |
+
* Don't use Checkout URL for Unknown Product Types.
|
74 |
+
* Add Beta Integration Test for Syncing Products by Uploading Feed.
|
75 |
+
* Fix Pixel Fired on Related Products.
|
76 |
+
* Redesign Admin Panel.
|
77 |
+
* Add Integration Test Entry Point Button.
|
78 |
+
|
79 |
+
2018-05-02 - version 1.8.7
|
80 |
+
* Fix PHP Error Due to product_brand Taxonomy Not Existing
|
81 |
+
|
82 |
+
2018-04-26 - version 1.8.6
|
83 |
+
* Potential Fix for Compatibility with Enhanced ECommerce Plugin
|
84 |
+
* Reduce Fetch ID API Call for Hidden Products
|
85 |
+
* Support Product Bundles Extension
|
86 |
+
* Basic Support for WP All Import
|
87 |
+
* Fix Trashing and Deletion
|
88 |
+
* Fetch Brand and Support WooCommerce Brand Extension
|
89 |
+
* Remove warning for duplicate SKUs
|
90 |
+
* Remove upsell header on config page
|
91 |
+
* Update Info Banner Content
|
92 |
+
* Fix Warning When Deleting NON PRODUCT post
|
93 |
+
* WPML Support : Language Selector
|
94 |
+
|
95 |
+
2018-04-17 - version 1.8.5
|
96 |
+
* Fix ViewContent event incorrectly firing with content_type 'product'
|
97 |
+
* Fix product group retailer id not matching ViewContent content id.
|
98 |
+
* Added logging and defensive code to debug #348
|
99 |
+
* Warning about Cart URL changes now clears with Force Resync
|
100 |
+
|
101 |
+
2018-04-06 - version 1.8.4
|
102 |
+
* Fire AddToCart on Cart Viewed for shops which redirect to cart.
|
103 |
+
* Fix HTML AJAX comment inside script breaking HTML optimizers.
|
104 |
+
* Log product sync speed as a performance metric.
|
105 |
+
|
106 |
+
2018-03-30 - version 1.8.3
|
107 |
+
* Fix Hidden Product Showing Up in Shop after Initial Sync.
|
108 |
+
* Hiding a variable product now hides the product on FB.
|
109 |
+
* Fix Variable Subscription Products Not Syncing Variants.
|
110 |
+
* Support Default Variant in Plugin via Graph API: Create and Update.
|
111 |
+
* Set Default Variant as Default Product in Feed.
|
112 |
+
|
113 |
+
2018-03-20 - version 1.8.2
|
114 |
+
* Fix category column in feed.
|
115 |
+
* Update Force Resync to use a feed if feed was used for initial sync. (~15x faster)
|
116 |
+
* Fix all caps description products being rejected from feed.
|
117 |
+
* Fix gallery images for variable products not showing in feed.
|
118 |
+
* Fix multiple category error in feed upload.
|
119 |
+
|
120 |
+
2018-02-27 - version 1.8.1
|
121 |
+
* Fix Upgrade TagName For Beta Version
|
122 |
+
|
123 |
+
2018-02-14 - version 1.8.0
|
124 |
+
* Up to 15x Performance Improvement of Initial Product Sync by using a feed upload.
|
125 |
+
* Fix Subscription Product Bug Due to API Change.
|
126 |
+
* Fix Undefined Index Notices.
|
127 |
+
* Ensure jQuery is Loaded before Using it.
|
128 |
+
* Moved apply_filters from Constructor to wp_head Action.
|
129 |
+
* Remove Atlernative Pixel Basecode Fetching.
|
130 |
+
* [WordPress]Separate the Plugins to Different Directories
|
131 |
+
|
132 |
+
2018-02-01 - version 1.7.11
|
133 |
+
* Fix permission error due to difference in data format between graphapi and feed uploading.
|
134 |
+
|
135 |
+
2018-01-31 - version 1.7.10
|
136 |
+
* Disable Alternate Pixel Basecode fetching due to issues for some stores.
|
137 |
+
* Fix variation carry main description if variant description is empty.
|
138 |
+
* Add version check information.
|
139 |
+
|
140 |
+
2018-01-30 - version 1.7.9
|
141 |
+
* Add Filter hook for other plugins to override pixel behavior.
|
142 |
+
* Fix 500 errors when saving settings.
|
143 |
+
|
144 |
+
2018-01-25 - version 1.7.8
|
145 |
+
* Fixes WC_Facebookcommerce_Pixel reference error
|
146 |
+
|
147 |
+
2018-01-22 - version 1.7.7
|
148 |
+
* Fix purchase event not firing for Stripe.
|
149 |
+
* Fix duplicate pixel issue for Initiate Checkout and Purchase events
|
150 |
+
* Pixel basecode is fetched from Facebook when setup is completed.
|
151 |
+
* Pixel proxy endpoints added for cases where pixel script cannot be loaded.
|
152 |
+
|
153 |
+
2018-01-03 - version 1.7.6
|
154 |
+
* WordPress only plugin notice on missing Pixel ID.
|
155 |
+
* WordPress only plugin direct link to settings page from plugin page.
|
156 |
+
|
157 |
+
2018-01-11 - version 1.7.5
|
158 |
+
* Fix auto-updater to upgrade facebook-for-woocommerce only.
|
159 |
+
|
160 |
+
2018-01-05 - version 1.7.4
|
161 |
+
* Fix purchase event not firing for some payment types.
|
162 |
+
* Provide functionality to refresh API token when invalid.
|
163 |
+
* Added Quick Edit Compatibility.
|
164 |
+
* Fix Incompatibility with remove HTTP extension.
|
165 |
+
* Added Bulk Edit Compatibility.
|
166 |
+
* WPML compatibility: products in non-default language set to staging
|
167 |
+
* Add support for variable subscription products.
|
168 |
+
|
169 |
+
2017-12-13 - version 1.7.3
|
170 |
+
* Fix security hole that would allow a logged in user without
|
171 |
+
manage_woocommerce permissions to toggle the fb_visibility of products.
|
172 |
+
* Handle shortcodes.
|
173 |
+
* Support product duplication.
|
174 |
+
|
175 |
+
2017-11-30 - version 1.7.2
|
176 |
+
* Fix issue with get_plugin_data being called before it was loaded.
|
177 |
+
* Improve perf of info dialog.
|
178 |
+
* Move class loading and DB read into admin gate to fix site slowdown.
|
179 |
+
|
180 |
+
2017-11-29 - version 1.7.0
|
181 |
+
* Enable auto-upgrading.
|
182 |
+
* Remove the extra content IDs to fix match rate issue.
|
183 |
+
* Only show 'Any' in attribute value string and show attribute name.
|
184 |
+
* Fix trash/restore products visibility on Facebook Shop.
|
185 |
+
* Fix deprecated function for variant description.
|
186 |
+
* Add hook and fix checkout url for external products.
|
187 |
+
* Clean HTML tag in product title.
|
188 |
+
* Add info dialog on WooCommerce report, settings and status page.
|
189 |
+
|
190 |
+
2017-11-03 - version 1.6.6
|
191 |
+
* Solve race condition to avoid minus remaining number in syncing process.
|
192 |
+
* Fix sync of variant product description.
|
193 |
+
* Enable line breaks in product main description.
|
194 |
+
* Enable upsell message and redirect link visible for upgrading users.
|
195 |
+
* Improve upsell message content and style.
|
196 |
+
|
197 |
+
2017-10-19 - version 1.6.5
|
198 |
+
* Fix unterminated div tag. Thanks @pwag42
|
199 |
+
* After 7 days, show a link to a new ads interface on the settings page.
|
200 |
+
|
201 |
+
2017-10-04 - version 1.6.4
|
202 |
+
* Default to variant specific image as primary image for FB.
|
203 |
+
* Add a Checkbox to allow override to use the parent product image.
|
204 |
+
* Don't sync items which have zero price.
|
205 |
+
* Existing 0 price items will be marked invisible after Force-Resync.
|
206 |
+
* Support Syncing Bookable Items with 'Display Cost' set.
|
207 |
+
* Fix 'Update' and 'Publish' for Bookable Items.
|
208 |
+
|
209 |
+
|
210 |
+
2017-10-03 - version 1.6.3
|
211 |
+
* Use Bookable price when regular price doesn't exist.
|
212 |
+
* Support Default Variations.
|
213 |
+
* Fix warning when generating attribute names.
|
214 |
+
|
215 |
+
2017-09-28 - version 1.6.2
|
216 |
+
* Fix "Invalid Parameter" API error caused by invalid sale dates.
|
217 |
+
* Fix variable product unable to sync gallery images.
|
218 |
+
* Cache gallery image urls for variable products to reduce DB load.
|
219 |
+
* Fix exception during generation of some ViewCategory events.
|
220 |
+
|
221 |
+
2017-09-21 - version 1.6.1
|
222 |
+
* Prevent save settings button for other WooCommerce plugins erasing FB settings.
|
223 |
+
|
224 |
+
2017-09-15 - version 1.6.0
|
225 |
+
* Support sale start and end dates
|
226 |
+
* Include tax on sale price as needed
|
227 |
+
* Fix visibility toggle on the product page
|
228 |
+
* No longer publish hidden products
|
229 |
+
* Use variable products' attribute name, instead of slug name
|
230 |
+
* Fix encoding issue in Variation names
|
231 |
+
* Resolve W3 validation error caused by pixel code
|
232 |
+
* Correctly sync variants where the attribute is not specified or set to 'any'.
|
233 |
+
* Save Changes button no longer disappears in other settings pages
|
234 |
+
* Fix a JS warning caused by ViewCategory events by switching to trackCustom
|
235 |
+
* Fix quoted strings having unneeded slashes in the FB Description
|
236 |
+
* Fix Unicode encoding in Category Names. Thanks @jancinert.
|
237 |
+
|
238 |
+
2017-09-05 - version 1.5.1
|
239 |
+
* Fix critical issue with ViewContent events not matching products.
|
240 |
+
|
241 |
+
2017-09-05 - version 1.5.0
|
242 |
+
* Added support for generic WordPress installations (without WooCommerce)
|
243 |
+
* Added Search events
|
244 |
+
* Setting page for Pixel ID and for enabling advanced measurement
|
245 |
+
* Use featured image as primary image for variants, and variant images as additional images.
|
246 |
+
|
247 |
+
2017-08-25 - version 1.4.6
|
248 |
+
* Fix issue where prices were rounded incorrectly
|
249 |
+
|
250 |
+
2017-08-15 - version 1.4.5
|
251 |
+
* Prevent printed output from breaking the popupOrigin
|
252 |
+
* Add composer.json file
|
253 |
+
|
254 |
+
2017-07-25 - version 1.4.4
|
255 |
+
* Remove duplicate and blank content ids in pixel fires.
|
256 |
+
* Fix warning when sale price is malformed.
|
257 |
+
|
258 |
+
2017-07-26 - version 1.4.3
|
259 |
+
* Remove search event for admin panel searches (fix JS error preventing quick edit)
|
260 |
+
* Prevent search event from firing twice
|
261 |
+
* Add categories to items
|
262 |
+
* Add re-configure button back if merchant is locked out (e.g. due to password change)
|
263 |
+
|
264 |
+
2017-06-20 - version 1.4.2
|
265 |
+
* Add custom Facebook description box
|
266 |
+
* Less yelling during product sync
|
267 |
+
* Remove deprecation warnings
|
268 |
+
* Fix RTL issue for JS loading (thanks Ariel!)
|
269 |
+
* Validate gender param to FB enum
|
270 |
+
* Fix product deletion for sub variants
|
271 |
+
|
272 |
+
2017-06-15 - version 1.4.1
|
273 |
+
* Fix OOM/whitescreen of death error (constrain product count query to IDs)
|
274 |
+
|
275 |
+
2017-06-05 - version 1.4.0
|
276 |
+
* Background sync! Products will now sync in the background if you keep your settings page open.
|
277 |
+
* Fix purchase pixel fires for product variants
|
278 |
+
* Fix state on hide/show buttons
|
279 |
+
* Fix issue with incorrect price rounding
|
280 |
+
* Delete FB metadata from product items as well as groups
|
281 |
+
* Add GPL license to repo
|
282 |
+
|
283 |
+
2017-05-30 - version 1.3.3
|
284 |
+
* Use display prices for regular prices (includes VAT if used)
|
285 |
+
* Switch from checkboxes to buttons on Products page
|
286 |
+
* Add "Reset Facebook metadata" option for merchants that duplicate products
|
287 |
+
* Add "Delete on Facebook" option for products
|
288 |
+
* Only delete products when actually deleted (vs trashed)
|
289 |
+
* Allow pixel resetting after setup completion
|
290 |
+
* Disable plugin if WP_DEBUG_DISPLAY is detected (prevent PHP notice injection)
|
291 |
+
|
292 |
+
2017-05-25 - version 1.3.2
|
293 |
+
* Allow Force Resync button to resume stalled product syncs.
|
294 |
+
* Handle WP_Error errors
|
295 |
+
|
296 |
+
2017-05-23 - version 1.3.1
|
297 |
+
* Only use checkout urls if valid cart url is detected
|
298 |
+
* Set priority and namespacing for all ajax functions
|
299 |
+
* Workaround for staticxx domain redirects
|
300 |
+
|
301 |
+
2017-05-15 - version 1.3.0
|
302 |
+
* Improve memory usage and remove OOM for large product catalogs
|
303 |
+
* Split variant methods for product groups and product items (fix mismatches)
|
304 |
+
* Make re-sync button work for updates as well as creates
|
305 |
+
* Fix issue with newly added variants breaking out of their product group
|
306 |
+
* Use default form values for variants if present
|
307 |
+
* Strip registered shortcodes from descriptions via strip_shortcodes
|
308 |
+
* Remove PHP warning about variable passed by reference
|
309 |
+
* Capture and save existing FBIDs on "Duplicate Retailer ID" error
|
310 |
+
|
311 |
+
2017-05-11 - version 1.2.6
|
312 |
+
* Remove use of post guid for product URLs
|
313 |
+
|
314 |
+
2017-05-06 - version 1.2.5
|
315 |
+
* Ajax lockdown: transmit fb-specific code and set priority on save_settings
|
316 |
+
|
317 |
+
2017-05-05 - version 1.2.4
|
318 |
+
* Fix issue with pixel selection step freezing due to missing site url.
|
319 |
+
|
320 |
+
2017-05-04 - version 1.2.3
|
321 |
+
* Workaround for servers that send additional characters on ajax responses
|
322 |
+
|
323 |
+
2017-05-02 - version 1.2.2
|
324 |
+
* Fix issues with non-english variant labels
|
325 |
+
* Support gallery image urls
|
326 |
+
|
327 |
+
2017-05-02 - version 1.2.1
|
328 |
+
* Use wc_get_cart_url to support custom cart URLs
|
329 |
+
* Fix logging bug for users without manage_woocommerce attempting to set up
|
330 |
+
|
331 |
+
2017-04-27 - version 1.2.0
|
332 |
+
* Allow regular admins with manage_woocommerce permission to use plugin
|
333 |
+
* Lock settings during product sync
|
334 |
+
* Do not allow product sync during an existing product sync
|
335 |
+
* Validate catalog ID before product sync
|
336 |
+
* Fix issue with resync products button
|
337 |
+
* Fix issue with plugin breaking product search
|
338 |
+
|
339 |
+
2017-04-25 - version 1.1.0
|
340 |
+
* Support duplicate SKUs via better retailer ID logic
|
341 |
+
* Add "resync products" button
|
342 |
+
* Update FB logging to support objects as well as strings
|
343 |
+
* Add support for regular and sale pricing
|
344 |
+
|
345 |
+
2017-04-20 - version 1.0.3
|
346 |
+
* Remove delete button (use Advanced Settings tab instead)
|
347 |
+
* Cleaned up admin messages, prepended them with "Facebook for WooCommerce"
|
348 |
+
|
349 |
+
2017-04-13 - version 1.0.2
|
350 |
+
* Remove Save Settings button when not in debug mode
|
351 |
+
|
352 |
+
2017-04-11 - version 1.0.1
|
353 |
+
* Prevent blank item descriptions
|
354 |
+
* Update copy on setup page
|
355 |
+
* Sanitize settings before sending via fblog
|
356 |
+
* Show better warnings for duplicate SKUs
|
357 |
+
|
358 |
+
2017-04-10 - version 1.0.0
|
359 |
+
* First release! Woo!
|
360 |
+
|
361 |
+
2017-04-09 - version 0.8.0
|
362 |
+
* Include status messages during background sync
|
363 |
+
* Fix several PHP warnings and various WooCommerce 3.0 compatibility issues
|
364 |
+
|
365 |
+
2017-04-07 - version 0.7.2
|
366 |
+
* Fix issue where products were not being created due to blank descriptions
|
367 |
+
|
368 |
+
2017-04-04 - version 0.7.1
|
369 |
+
* Clean up output for checkboxes via printf
|
370 |
+
* Bug fixes and performance improvements
|
371 |
+
|
372 |
+
2017-04-03 - version 0.7.0
|
373 |
+
* Add new product visibility checkbox toggle for Facebook Shops on Product list
|
374 |
+
* Official rename: Facebook for WooCommerce
|
375 |
+
|
376 |
+
2017-03-30 - version 0.6.0
|
377 |
+
* Revert back to minor revisions for versioning
|
378 |
+
* Add ability to reset all settings to start over
|
379 |
+
|
380 |
+
2017-03-20 - version 0.0.5
|
381 |
+
* Fix "Get Started" button bug
|
382 |
+
* Fix issue preventing "Use Advanced Matching" setting from properly saving
|
383 |
+
* Various security fixes
|
384 |
+
|
385 |
+
2017-03-01 - version 0.0.4
|
386 |
+
|
387 |
+
* Add Facebook metabox on product page with products' FB ID and a Visibility toggle (publish/unpublish).
|
388 |
+
|
389 |
+
2017-02-15 - version 0.0.3
|
390 |
+
|
391 |
+
* Add a change log!
|
392 |
+
* Modify plugin to check for both local and network installs of WooCommerce.
|
393 |
+
* Disable plugin hooks that were triggering before setup was complete.
|
394 |
+
* Escape HTML tags in product descriptions.
|
395 |
+
* Use attribute label name instead of first-letter-capitalized slug.
|
396 |
+
|
397 |
+
2017-02-02 - version 0.0.2
|
398 |
+
|
399 |
+
* Fix breaking when debug mode is disabled (null check in facebook-settings.js)
|
composer.json
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "facebookincubator/facebook-for-woocommerce",
|
3 |
+
"description": "Grow your business with Facebook for WooCommerce! This plugin will install a Facebook Pixel and optionally create a shop on your Facebook page.",
|
4 |
+
"type": "wordpress-plugin",
|
5 |
+
"license": "GPL-2.0+",
|
6 |
+
"require": {
|
7 |
+
"composer/installers": "~1.0",
|
8 |
+
"php": ">=5.6"
|
9 |
+
},
|
10 |
+
"repositories": [
|
11 |
+
{
|
12 |
+
"type": "git",
|
13 |
+
"url" : "git@github.com:facebookincubator/facebook-for-woocommerce.git"
|
14 |
+
}
|
15 |
+
]
|
16 |
+
}
|
facebook-commerce-events-tracker.php
ADDED
@@ -0,0 +1,386 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* @package FacebookCommerce
|
9 |
+
*/
|
10 |
+
|
11 |
+
if (!class_exists('WC_Facebookcommerce_EventsTracker')) :
|
12 |
+
|
13 |
+
if (!class_exists('WC_Facebookcommerce_Utils')) {
|
14 |
+
include_once 'includes/fbutils.php';
|
15 |
+
}
|
16 |
+
|
17 |
+
if (!class_exists('WC_Facebookcommerce_Pixel')) {
|
18 |
+
include_once 'facebook-commerce-pixel-event.php';
|
19 |
+
}
|
20 |
+
|
21 |
+
class WC_Facebookcommerce_EventsTracker {
|
22 |
+
private $pixel;
|
23 |
+
private static $isEnabled = true;
|
24 |
+
const FB_PRIORITY_HIGH = 2;
|
25 |
+
const FB_PRIORITY_LOW = 11;
|
26 |
+
|
27 |
+
public function __construct($user_info) {
|
28 |
+
$this->pixel = new WC_Facebookcommerce_Pixel($user_info);
|
29 |
+
|
30 |
+
add_action('wp_head', array($this, 'apply_filters'));
|
31 |
+
|
32 |
+
// Pixel Tracking Hooks
|
33 |
+
add_action('wp_head',
|
34 |
+
array($this, 'inject_base_pixel'));
|
35 |
+
add_action('wp_footer',
|
36 |
+
array($this, 'inject_base_pixel_noscript'));
|
37 |
+
add_action('woocommerce_after_single_product',
|
38 |
+
array($this, 'inject_view_content_event'), self::FB_PRIORITY_HIGH);
|
39 |
+
add_action('woocommerce_after_shop_loop',
|
40 |
+
array($this, 'inject_view_category_event'));
|
41 |
+
add_action('pre_get_posts',
|
42 |
+
array($this, 'inject_search_event'));
|
43 |
+
add_action('woocommerce_after_cart',
|
44 |
+
array($this, 'inject_add_to_cart_redirect_event'));
|
45 |
+
add_action('woocommerce_add_to_cart',
|
46 |
+
array($this, 'inject_add_to_cart_event'), self::FB_PRIORITY_HIGH);
|
47 |
+
add_action('wc_ajax_fb_inject_add_to_cart_event',
|
48 |
+
array($this, 'inject_ajax_add_to_cart_event' ), self::FB_PRIORITY_HIGH);
|
49 |
+
add_action('woocommerce_after_checkout_form',
|
50 |
+
array($this, 'inject_initiate_checkout_event'));
|
51 |
+
add_action('woocommerce_thankyou',
|
52 |
+
array($this, 'inject_gateway_purchase_event'), self::FB_PRIORITY_HIGH);
|
53 |
+
add_action('woocommerce_payment_complete',
|
54 |
+
array($this, 'inject_purchase_event'), self::FB_PRIORITY_HIGH);
|
55 |
+
add_action('wpcf7_contact_form',
|
56 |
+
array($this, 'inject_lead_event_hook'), self::FB_PRIORITY_LOW);
|
57 |
+
|
58 |
+
}
|
59 |
+
|
60 |
+
public function apply_filters() {
|
61 |
+
self::$isEnabled = apply_filters(
|
62 |
+
"facebook_for_woocommerce_integration_pixel_enabled",
|
63 |
+
self::$isEnabled);
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Base pixel code to be injected on page head. Because of this, it's better
|
68 |
+
* to echo the return value than using
|
69 |
+
* WC_Facebookcommerce_Utils::wc_enqueue_js() in this case
|
70 |
+
*/
|
71 |
+
public function inject_base_pixel() {
|
72 |
+
if (self::$isEnabled) {
|
73 |
+
echo $this->pixel->pixel_base_code();
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Base pixel noscript to be injected on page body. This is to avoid W3
|
79 |
+
* validation error.
|
80 |
+
*/
|
81 |
+
public function inject_base_pixel_noscript() {
|
82 |
+
if (self::$isEnabled) {
|
83 |
+
echo $this->pixel->pixel_base_code_noscript();
|
84 |
+
}
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Triggers ViewCategory for product category listings
|
89 |
+
*/
|
90 |
+
public function inject_view_category_event() {
|
91 |
+
global $wp_query;
|
92 |
+
if (!self::$isEnabled) {
|
93 |
+
return;
|
94 |
+
}
|
95 |
+
|
96 |
+
$products = array_values(array_map(function($item) {
|
97 |
+
return wc_get_product($item->ID);
|
98 |
+
},
|
99 |
+
$wp_query->posts));
|
100 |
+
|
101 |
+
// if any product is a variant, fire the pixel with
|
102 |
+
// content_type: product_group
|
103 |
+
$content_type = 'product';
|
104 |
+
$product_ids = array();
|
105 |
+
foreach ($products as $product) {
|
106 |
+
if (!$product) {
|
107 |
+
continue;
|
108 |
+
}
|
109 |
+
$product_ids = array_merge(
|
110 |
+
$product_ids,
|
111 |
+
WC_Facebookcommerce_Utils::get_fb_content_ids($product));
|
112 |
+
if (WC_Facebookcommerce_Utils::is_variable_type($product->get_type())) {
|
113 |
+
$content_type = 'product_group';
|
114 |
+
}
|
115 |
+
}
|
116 |
+
|
117 |
+
$categories =
|
118 |
+
WC_Facebookcommerce_Utils::get_product_categories(get_the_ID());
|
119 |
+
|
120 |
+
$this->pixel->inject_event(
|
121 |
+
'ViewCategory',
|
122 |
+
array(
|
123 |
+
'content_name' => $categories['name'],
|
124 |
+
'content_category' => $categories['categories'],
|
125 |
+
'content_ids' => json_encode(array_slice($product_ids, 0, 10)),
|
126 |
+
'content_type' => $content_type
|
127 |
+
),
|
128 |
+
'trackCustom');
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Triggers Search for result pages (deduped)
|
133 |
+
*/
|
134 |
+
public function inject_search_event() {
|
135 |
+
if (!self::$isEnabled) {
|
136 |
+
return;
|
137 |
+
}
|
138 |
+
|
139 |
+
if (!is_admin() && is_search() && get_search_query() !== '') {
|
140 |
+
if ($this->pixel->check_last_event('Search')) {
|
141 |
+
return;
|
142 |
+
}
|
143 |
+
|
144 |
+
if (WC_Facebookcommerce_Utils::isWoocommerceIntegration()) {
|
145 |
+
$this->actually_inject_search_event();
|
146 |
+
} else {
|
147 |
+
add_action('wp_head', array($this, 'actually_inject_search_event'), 11);
|
148 |
+
}
|
149 |
+
}
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Triggers Search for result pages
|
154 |
+
*/
|
155 |
+
public function actually_inject_search_event() {
|
156 |
+
if (!self::$isEnabled) {
|
157 |
+
return;
|
158 |
+
}
|
159 |
+
|
160 |
+
$this->pixel->inject_event(
|
161 |
+
'Search',
|
162 |
+
array(
|
163 |
+
'search_string' => get_search_query()
|
164 |
+
));
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Helper function to iterate through a cart and gather all content ids
|
169 |
+
*/
|
170 |
+
private function get_content_ids_from_cart($cart) {
|
171 |
+
$product_ids = array();
|
172 |
+
foreach ($cart as $item) {
|
173 |
+
$product_ids = array_merge(
|
174 |
+
$product_ids,
|
175 |
+
WC_Facebookcommerce_Utils::get_fb_content_ids($item['data']));
|
176 |
+
}
|
177 |
+
return $product_ids;
|
178 |
+
}
|
179 |
+
|
180 |
+
/**
|
181 |
+
* Triggers ViewContent product pages
|
182 |
+
*/
|
183 |
+
public function inject_view_content_event() {
|
184 |
+
if (!self::$isEnabled) {
|
185 |
+
return;
|
186 |
+
}
|
187 |
+
global $post;
|
188 |
+
$product = wc_get_product($post->ID);
|
189 |
+
$content_type = 'product_group';
|
190 |
+
if (!$product) {
|
191 |
+
return;
|
192 |
+
}
|
193 |
+
|
194 |
+
// if product is a variant, fire the pixel with content_type: product_group
|
195 |
+
if (WC_Facebookcommerce_Utils::is_variation_type($product->get_type())) {
|
196 |
+
$content_type = 'product';
|
197 |
+
}
|
198 |
+
|
199 |
+
$content_ids = WC_Facebookcommerce_Utils::get_fb_content_ids($product);
|
200 |
+
$this->pixel->inject_event(
|
201 |
+
'ViewContent',
|
202 |
+
array(
|
203 |
+
'content_name' => $product->get_title(),
|
204 |
+
'content_ids' => json_encode($content_ids),
|
205 |
+
'content_type' => $content_type,
|
206 |
+
'value' => $product->get_price(),
|
207 |
+
'currency' => get_woocommerce_currency()
|
208 |
+
));
|
209 |
+
}
|
210 |
+
|
211 |
+
/**
|
212 |
+
* Triggers AddToCart for cart page and add_to_cart button clicks
|
213 |
+
*/
|
214 |
+
public function inject_add_to_cart_event() {
|
215 |
+
if (!self::$isEnabled) {
|
216 |
+
return;
|
217 |
+
}
|
218 |
+
|
219 |
+
$product_ids = $this->get_content_ids_from_cart(WC()->cart->get_cart());
|
220 |
+
|
221 |
+
$this->pixel->inject_event(
|
222 |
+
'AddToCart',
|
223 |
+
array(
|
224 |
+
'content_ids' => json_encode($product_ids),
|
225 |
+
'content_type' => 'product',
|
226 |
+
'value' => WC()->cart->total,
|
227 |
+
'currency' => get_woocommerce_currency()
|
228 |
+
));
|
229 |
+
}
|
230 |
+
|
231 |
+
/**
|
232 |
+
* Triggered by add_to_cart jquery trigger
|
233 |
+
*/
|
234 |
+
public function inject_ajax_add_to_cart_event() {
|
235 |
+
if (!self::$isEnabled) {
|
236 |
+
return;
|
237 |
+
}
|
238 |
+
|
239 |
+
ob_start();
|
240 |
+
|
241 |
+
echo '<script>';
|
242 |
+
|
243 |
+
$product_ids = $this->get_content_ids_from_cart(WC()->cart->get_cart());
|
244 |
+
|
245 |
+
echo $this->pixel->build_event(
|
246 |
+
'AddToCart',
|
247 |
+
array(
|
248 |
+
'content_ids' => json_encode($product_ids),
|
249 |
+
'content_type' => 'product',
|
250 |
+
'value' => WC()->cart->total,
|
251 |
+
'currency' => get_woocommerce_currency()
|
252 |
+
));
|
253 |
+
echo '</script>';
|
254 |
+
|
255 |
+
$pixel = ob_get_clean();
|
256 |
+
|
257 |
+
wp_send_json($pixel);
|
258 |
+
}
|
259 |
+
|
260 |
+
/**
|
261 |
+
* Trigger AddToCart for cart page and woocommerce_after_cart hook.
|
262 |
+
* When set 'redirect to cart', ajax call for button click and
|
263 |
+
* woocommerce_add_to_cart will be skipped.
|
264 |
+
*/
|
265 |
+
public function inject_add_to_cart_redirect_event() {
|
266 |
+
if (!self::$isEnabled) {
|
267 |
+
return;
|
268 |
+
}
|
269 |
+
$redirect_checked = get_option('woocommerce_cart_redirect_after_add', 'no');
|
270 |
+
if ($redirect_checked == 'yes') {
|
271 |
+
$this->inject_add_to_cart_event();
|
272 |
+
}
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* Triggers InitiateCheckout for checkout page
|
277 |
+
*/
|
278 |
+
public function inject_initiate_checkout_event() {
|
279 |
+
if (!self::$isEnabled ||
|
280 |
+
$this->pixel->check_last_event('InitiateCheckout')) {
|
281 |
+
return;
|
282 |
+
}
|
283 |
+
|
284 |
+
$product_ids = $this->get_content_ids_from_cart(WC()->cart->get_cart());
|
285 |
+
|
286 |
+
$this->pixel->inject_event(
|
287 |
+
'InitiateCheckout',
|
288 |
+
array(
|
289 |
+
'num_items' => WC()->cart->get_cart_contents_count(),
|
290 |
+
'content_ids' => json_encode($product_ids),
|
291 |
+
'content_type' => 'product',
|
292 |
+
'value' => WC()->cart->total,
|
293 |
+
'currency' => get_woocommerce_currency()
|
294 |
+
));
|
295 |
+
}
|
296 |
+
|
297 |
+
/**
|
298 |
+
* Triggers Purchase for payment transaction complete and for the thank you
|
299 |
+
* page in cases of delayed payment.
|
300 |
+
*/
|
301 |
+
public function inject_purchase_event($order_id) {
|
302 |
+
if (!self::$isEnabled ||
|
303 |
+
$this->pixel->check_last_event('Purchase')) {
|
304 |
+
return;
|
305 |
+
}
|
306 |
+
|
307 |
+
$this->inject_subscribe_event($order_id);
|
308 |
+
|
309 |
+
$order = new WC_Order($order_id);
|
310 |
+
$content_type = 'product';
|
311 |
+
$product_ids = array();
|
312 |
+
foreach ($order->get_items() as $item) {
|
313 |
+
$product = wc_get_product($item['product_id']);
|
314 |
+
$product_ids = array_merge(
|
315 |
+
$product_ids,
|
316 |
+
WC_Facebookcommerce_Utils::get_fb_content_ids($product));
|
317 |
+
if (WC_Facebookcommerce_Utils::is_variable_type($product->get_type())) {
|
318 |
+
$content_type = 'product_group';
|
319 |
+
}
|
320 |
+
}
|
321 |
+
|
322 |
+
$this->pixel->inject_event(
|
323 |
+
'Purchase',
|
324 |
+
array(
|
325 |
+
'content_ids' => json_encode($product_ids),
|
326 |
+
'content_type' => $content_type,
|
327 |
+
'value' => $order->get_total(),
|
328 |
+
'currency' => get_woocommerce_currency()
|
329 |
+
));
|
330 |
+
}
|
331 |
+
|
332 |
+
/**
|
333 |
+
* Triggers Subscribe for payment transaction complete of purchase with
|
334 |
+
* subscription.
|
335 |
+
*/
|
336 |
+
public function inject_subscribe_event($order_id) {
|
337 |
+
if (!function_exists("wcs_get_subscriptions_for_order")) {
|
338 |
+
return;
|
339 |
+
}
|
340 |
+
|
341 |
+
$subscription_ids = wcs_get_subscriptions_for_order($order_id);
|
342 |
+
foreach ($subscription_ids as $subscription_id) {
|
343 |
+
$subscription = new WC_Subscription($subscription_id);
|
344 |
+
$this->pixel->inject_event(
|
345 |
+
'Subscribe',
|
346 |
+
array(
|
347 |
+
'sign_up_fee' => $subscription->get_sign_up_fee(),
|
348 |
+
'value' => $subscription->get_total(),
|
349 |
+
'currency' => get_woocommerce_currency()
|
350 |
+
));
|
351 |
+
}
|
352 |
+
}
|
353 |
+
|
354 |
+
/**
|
355 |
+
* Triggers Purchase for thank you page for COD, BACS CHEQUE payment
|
356 |
+
* which won't invoke woocommerce_payment_complete.
|
357 |
+
*/
|
358 |
+
public function inject_gateway_purchase_event($order_id) {
|
359 |
+
if (!self::$isEnabled ||
|
360 |
+
$this->pixel->check_last_event('Purchase')) {
|
361 |
+
return;
|
362 |
+
}
|
363 |
+
|
364 |
+
$order = new WC_Order($order_id);
|
365 |
+
$payment = $order->get_payment_method();
|
366 |
+
$this->inject_purchase_event($order_id);
|
367 |
+
$this->inject_subscribe_event($order_id);
|
368 |
+
}
|
369 |
+
|
370 |
+
/** Contact Form 7 Support **/
|
371 |
+
public function inject_lead_event_hook() {
|
372 |
+
add_action('wp_footer', array($this, 'inject_lead_event'), 11);
|
373 |
+
}
|
374 |
+
|
375 |
+
public function inject_lead_event() {
|
376 |
+
if (!is_admin()) {
|
377 |
+
$this->pixel->inject_conditional_event(
|
378 |
+
'Lead',
|
379 |
+
array(),
|
380 |
+
'wpcf7submit',
|
381 |
+
'{ em: event.detail.inputs.filter(ele => ele.name.includes("email"))[0].value }');
|
382 |
+
}
|
383 |
+
}
|
384 |
+
}
|
385 |
+
|
386 |
+
endif;
|
facebook-commerce-messenger-chat.php
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* @package FacebookCommerce
|
9 |
+
*/
|
10 |
+
|
11 |
+
if (!class_exists('WC_Facebookcommerce_MessengerChat')) :
|
12 |
+
|
13 |
+
if (!class_exists('WC_Facebookcommerce_Utils')) {
|
14 |
+
include_once 'includes/fbutils.php';
|
15 |
+
}
|
16 |
+
|
17 |
+
class WC_Facebookcommerce_MessengerChat {
|
18 |
+
|
19 |
+
public function __construct($settings) {
|
20 |
+
$this->enabled = isset($settings['is_messenger_chat_plugin_enabled'])
|
21 |
+
? $settings['is_messenger_chat_plugin_enabled']
|
22 |
+
: 'no';
|
23 |
+
|
24 |
+
$this->page_id = isset($settings['fb_page_id'])
|
25 |
+
? $settings['fb_page_id']
|
26 |
+
: '';
|
27 |
+
|
28 |
+
$this->jssdk_version = isset($settings['facebook_jssdk_version'])
|
29 |
+
? $settings['facebook_jssdk_version']
|
30 |
+
: '';
|
31 |
+
|
32 |
+
$this->greeting_text_code = isset($settings['msger_chat_customization_greeting_text_code'])
|
33 |
+
? $settings['msger_chat_customization_greeting_text_code']
|
34 |
+
: null;
|
35 |
+
|
36 |
+
$this->locale = isset($settings['msger_chat_customization_locale'])
|
37 |
+
? $settings['msger_chat_customization_locale']
|
38 |
+
: null;
|
39 |
+
|
40 |
+
$this->theme_color_code = isset($settings['msger_chat_customization_theme_color_code'])
|
41 |
+
? $settings['msger_chat_customization_theme_color_code']
|
42 |
+
: null;
|
43 |
+
|
44 |
+
add_action('wp_footer', array($this, 'inject_messenger_chat_plugin'));
|
45 |
+
}
|
46 |
+
|
47 |
+
public function inject_messenger_chat_plugin() {
|
48 |
+
if ($this->enabled === 'yes') {
|
49 |
+
echo sprintf("<div
|
50 |
+
attribution=\"fbe_woocommerce\"
|
51 |
+
class=\"fb-customerchat\"
|
52 |
+
page_id=\"%s\"
|
53 |
+
%s
|
54 |
+
%s
|
55 |
+
%s /></div>
|
56 |
+
<!-- Facebook JSSDK -->
|
57 |
+
<script>
|
58 |
+
window.fbAsyncInit = function() {
|
59 |
+
FB.init({
|
60 |
+
appId : '',
|
61 |
+
autoLogAppEvents : true,
|
62 |
+
xfbml : true,
|
63 |
+
version : '%s'
|
64 |
+
});
|
65 |
+
};
|
66 |
+
|
67 |
+
(function(d, s, id){
|
68 |
+
var js, fjs = d.getElementsByTagName(s)[0];
|
69 |
+
if (d.getElementById(id)) {return;}
|
70 |
+
js = d.createElement(s); js.id = id;
|
71 |
+
js.src = 'https://connect.facebook.net/%s/sdk/xfbml.customerchat.js';
|
72 |
+
fjs.parentNode.insertBefore(js, fjs);
|
73 |
+
}(document, 'script', 'facebook-jssdk'));
|
74 |
+
</script>
|
75 |
+
<div></div>",
|
76 |
+
$this->page_id,
|
77 |
+
$this->theme_color_code ? sprintf('theme_color="%s"', $this->theme_color_code) : '',
|
78 |
+
$this->greeting_text_code ? sprintf('logged_in_greeting="%s"', $this->greeting_text_code) : '',
|
79 |
+
$this->greeting_text_code ? sprintf('logged_out_greeting="%s"', $this->greeting_text_code) : '',
|
80 |
+
$this->jssdk_version,
|
81 |
+
$this->locale ? $this->locale : 'en_US');
|
82 |
+
}
|
83 |
+
}
|
84 |
+
|
85 |
+
}
|
86 |
+
|
87 |
+
endif;
|
facebook-commerce-pixel-event.php
ADDED
@@ -0,0 +1,292 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* @package FacebookCommerce
|
9 |
+
*/
|
10 |
+
|
11 |
+
if (!class_exists('WC_Facebookcommerce_Pixel')) :
|
12 |
+
|
13 |
+
|
14 |
+
class WC_Facebookcommerce_Pixel {
|
15 |
+
const SETTINGS_KEY = 'facebook_config';
|
16 |
+
const PIXEL_ID_KEY = 'pixel_id';
|
17 |
+
const USE_PII_KEY = 'use_pii';
|
18 |
+
|
19 |
+
const PIXEL_RENDER = 'pixel_render';
|
20 |
+
const NO_SCRIPT_RENDER = 'no_script_render';
|
21 |
+
|
22 |
+
private $user_info;
|
23 |
+
private $last_event;
|
24 |
+
static $render_cache = array();
|
25 |
+
|
26 |
+
static $default_pixel_basecode = "
|
27 |
+
<script type='text/javascript'>
|
28 |
+
!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
29 |
+
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
|
30 |
+
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
|
31 |
+
t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
|
32 |
+
document,'script','https://connect.facebook.net/en_US/fbevents.js');
|
33 |
+
</script>
|
34 |
+
";
|
35 |
+
|
36 |
+
public function __construct($user_info=array()) {
|
37 |
+
$this->user_info = $user_info;
|
38 |
+
$this->last_event = '';
|
39 |
+
}
|
40 |
+
|
41 |
+
public static function initialize() {
|
42 |
+
if (!is_admin()) {
|
43 |
+
return;
|
44 |
+
}
|
45 |
+
|
46 |
+
// Initialize PixelID in storage - this will only need to happen when the
|
47 |
+
// use is an admin
|
48 |
+
$pixel_id = self::get_pixel_id();
|
49 |
+
if (!WC_Facebookcommerce_Utils::is_valid_id($pixel_id) &&
|
50 |
+
class_exists('WC_Facebookcommerce_WarmConfig')) {
|
51 |
+
$fb_warm_pixel_id = WC_Facebookcommerce_WarmConfig::$fb_warm_pixel_id;
|
52 |
+
|
53 |
+
if (WC_Facebookcommerce_Utils::is_valid_id($fb_warm_pixel_id) &&
|
54 |
+
(int)$fb_warm_pixel_id == $fb_warm_pixel_id) {
|
55 |
+
$fb_warm_pixel_id = (string)$fb_warm_pixel_id;
|
56 |
+
self::set_pixel_id($fb_warm_pixel_id);
|
57 |
+
}
|
58 |
+
}
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Returns FB pixel code script part
|
63 |
+
*/
|
64 |
+
public function pixel_base_code() {
|
65 |
+
$pixel_id = self::get_pixel_id();
|
66 |
+
if (
|
67 |
+
(
|
68 |
+
isset(self::$render_cache[self::PIXEL_RENDER]) &&
|
69 |
+
self::$render_cache[self::PIXEL_RENDER] === true
|
70 |
+
) ||
|
71 |
+
!isset($pixel_id) ||
|
72 |
+
$pixel_id === 0
|
73 |
+
) {
|
74 |
+
return;
|
75 |
+
}
|
76 |
+
|
77 |
+
self::$render_cache[self::PIXEL_RENDER] = true;
|
78 |
+
$params = self::add_version_info();
|
79 |
+
|
80 |
+
return sprintf("
|
81 |
+
<!-- %s Facebook Integration Begin -->
|
82 |
+
%s
|
83 |
+
<script>
|
84 |
+
%s
|
85 |
+
fbq('track', 'PageView', %s);
|
86 |
+
|
87 |
+
document.addEventListener('DOMContentLoaded', function() {
|
88 |
+
jQuery && jQuery(function($){
|
89 |
+
$('body').on('added_to_cart', function(event) {
|
90 |
+
// Ajax action.
|
91 |
+
$.get('?wc-ajax=fb_inject_add_to_cart_event', function(data) {
|
92 |
+
$('head').append(data);
|
93 |
+
});
|
94 |
+
});
|
95 |
+
});
|
96 |
+
}, false);
|
97 |
+
|
98 |
+
</script>
|
99 |
+
<!-- DO NOT MODIFY -->
|
100 |
+
<!-- %s Facebook Integration end -->
|
101 |
+
",
|
102 |
+
WC_Facebookcommerce_Utils::getIntegrationName(),
|
103 |
+
self::get_basecode(),
|
104 |
+
$this->pixel_init_code(),
|
105 |
+
json_encode($params, JSON_PRETTY_PRINT | JSON_FORCE_OBJECT),
|
106 |
+
WC_Facebookcommerce_Utils::getIntegrationName());
|
107 |
+
}
|
108 |
+
|
109 |
+
/**
|
110 |
+
* Prevent double-fires by checking the last event
|
111 |
+
*/
|
112 |
+
public function check_last_event($event_name) {
|
113 |
+
return $event_name === $this->last_event;
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Preferred method to inject events in a page, normally you should use this
|
118 |
+
* instead of WC_Facebookcommerce_Pixel::build_event()
|
119 |
+
*/
|
120 |
+
public function inject_event($event_name, $params, $method='track') {
|
121 |
+
$code = self::build_event($event_name, $params, $method);
|
122 |
+
$this->last_event = $event_name;
|
123 |
+
|
124 |
+
if (WC_Facebookcommerce_Utils::isWoocommerceIntegration()) {
|
125 |
+
WC_Facebookcommerce_Utils::wc_enqueue_js($code);
|
126 |
+
} else {
|
127 |
+
printf("
|
128 |
+
<!-- Facebook Pixel Event Code -->
|
129 |
+
<script>
|
130 |
+
%s
|
131 |
+
</script>
|
132 |
+
<!-- End Facebook Pixel Event Code -->
|
133 |
+
",
|
134 |
+
$code);
|
135 |
+
}
|
136 |
+
}
|
137 |
+
|
138 |
+
public function inject_conditional_event(
|
139 |
+
$event_name, $params, $listener, $jsonified_pii = '') {
|
140 |
+
$code = self::build_event($event_name, $params, 'track');
|
141 |
+
$this->last_event = $event_name;
|
142 |
+
|
143 |
+
// Prepends fbq(...) with pii information to the injected code.
|
144 |
+
if ($jsonified_pii && get_option(self::SETTINGS_KEY)[self::USE_PII_KEY]) {
|
145 |
+
$this->user_info = '%s';
|
146 |
+
$code =
|
147 |
+
sprintf($this->pixel_init_code(), '" || '.$jsonified_pii.' || "').$code;
|
148 |
+
}
|
149 |
+
|
150 |
+
printf("
|
151 |
+
<!-- Facebook Pixel Event Code -->
|
152 |
+
<script>
|
153 |
+
document.addEventListener('%s', function (event) {
|
154 |
+
%s
|
155 |
+
}, false );
|
156 |
+
</script>
|
157 |
+
<!-- End Facebook Pixel Event Code -->
|
158 |
+
",
|
159 |
+
$listener,
|
160 |
+
$code);
|
161 |
+
}
|
162 |
+
|
163 |
+
/**
|
164 |
+
* Returns FB pixel code noscript part to avoid W3 validation error
|
165 |
+
*/
|
166 |
+
public function pixel_base_code_noscript() {
|
167 |
+
$pixel_id = self::get_pixel_id();
|
168 |
+
if (
|
169 |
+
(
|
170 |
+
isset(self::$render_cache[self::NO_SCRIPT_RENDER]) &&
|
171 |
+
self::$render_cache[self::NO_SCRIPT_RENDER] === true
|
172 |
+
) ||
|
173 |
+
!isset($pixel_id) ||
|
174 |
+
$pixel_id === 0
|
175 |
+
) {
|
176 |
+
return;
|
177 |
+
}
|
178 |
+
|
179 |
+
self::$render_cache[self::NO_SCRIPT_RENDER] = true;
|
180 |
+
|
181 |
+
return sprintf("
|
182 |
+
<!-- Facebook Pixel Code -->
|
183 |
+
<noscript>
|
184 |
+
<img height=\"1\" width=\"1\" style=\"display:none\" alt=\"fbpx\"
|
185 |
+
src=\"https://www.facebook.com/tr?id=%s&ev=PageView&noscript=1\"/>
|
186 |
+
</noscript>
|
187 |
+
<!-- DO NOT MODIFY -->
|
188 |
+
<!-- End Facebook Pixel Code -->
|
189 |
+
",
|
190 |
+
esc_js($pixel_id));
|
191 |
+
}
|
192 |
+
|
193 |
+
/**
|
194 |
+
* You probably should use WC_Facebookcommerce_Pixel::inject_event() but
|
195 |
+
* this method is available if you need to modify the JS code somehow
|
196 |
+
*/
|
197 |
+
public static function build_event($event_name, $params, $method='track') {
|
198 |
+
$params = self::add_version_info($params);
|
199 |
+
return sprintf(
|
200 |
+
"/* %s Facebook Integration Event Tracking */\n".
|
201 |
+
"fbq('%s', '%s', %s);",
|
202 |
+
WC_Facebookcommerce_Utils::getIntegrationName(),
|
203 |
+
$method,
|
204 |
+
$event_name,
|
205 |
+
json_encode($params, JSON_PRETTY_PRINT | JSON_FORCE_OBJECT));
|
206 |
+
}
|
207 |
+
|
208 |
+
public static function get_pixel_id() {
|
209 |
+
$fb_options = self::get_options();
|
210 |
+
if (!$fb_options) {
|
211 |
+
return '';
|
212 |
+
}
|
213 |
+
return isset($fb_options[self::PIXEL_ID_KEY]) ?
|
214 |
+
$fb_options[self::PIXEL_ID_KEY] : '';
|
215 |
+
}
|
216 |
+
|
217 |
+
public static function set_pixel_id($pixel_id) {
|
218 |
+
$fb_options = self::get_options();
|
219 |
+
|
220 |
+
if (isset($fb_options[self::PIXEL_ID_KEY])
|
221 |
+
&& $fb_options[self::PIXEL_ID_KEY] == $pixel_id) {
|
222 |
+
return;
|
223 |
+
}
|
224 |
+
|
225 |
+
$fb_options[self::PIXEL_ID_KEY] = $pixel_id;
|
226 |
+
update_option(self::SETTINGS_KEY, $fb_options);
|
227 |
+
}
|
228 |
+
|
229 |
+
public static function get_basecode() {
|
230 |
+
return self::$default_pixel_basecode;
|
231 |
+
}
|
232 |
+
|
233 |
+
private static function get_version_info() {
|
234 |
+
global $wp_version;
|
235 |
+
|
236 |
+
if (WC_Facebookcommerce_Utils::isWoocommerceIntegration()) {
|
237 |
+
return array(
|
238 |
+
'source' => 'woocommerce',
|
239 |
+
'version' => WC()->version,
|
240 |
+
'pluginVersion' => WC_Facebookcommerce_Utils::PLUGIN_VERSION
|
241 |
+
);
|
242 |
+
}
|
243 |
+
|
244 |
+
return array(
|
245 |
+
'source' => 'wordpress',
|
246 |
+
'version' => $wp_version,
|
247 |
+
'pluginVersion' => WC_Facebookcommerce_Utils::PLUGIN_VERSION
|
248 |
+
);
|
249 |
+
}
|
250 |
+
|
251 |
+
public static function get_options() {
|
252 |
+
return get_option(self::SETTINGS_KEY, array(
|
253 |
+
self::PIXEL_ID_KEY => '0',
|
254 |
+
self::USE_PII_KEY => 0,
|
255 |
+
));
|
256 |
+
}
|
257 |
+
|
258 |
+
/**
|
259 |
+
* Returns an array with version_info for pixel fires. Parameters provided by
|
260 |
+
* users should not be overwritten by this function
|
261 |
+
*/
|
262 |
+
private static function add_version_info($params=array()) {
|
263 |
+
// if any parameter is passed in the pixel, do not overwrite it
|
264 |
+
return array_replace(self::get_version_info(), $params);
|
265 |
+
}
|
266 |
+
|
267 |
+
/**
|
268 |
+
* Init code might contain additional information to help matching website
|
269 |
+
* users with facebook users. Information is hashed in JS side using SHA256
|
270 |
+
* before sending to Facebook.
|
271 |
+
*/
|
272 |
+
private function pixel_init_code() {
|
273 |
+
$version_info = self::get_version_info();
|
274 |
+
$agent_string = sprintf(
|
275 |
+
'%s-%s-%s',
|
276 |
+
$version_info['source'],
|
277 |
+
$version_info['version'],
|
278 |
+
$version_info['pluginVersion']);
|
279 |
+
|
280 |
+
$params = array(
|
281 |
+
'agent' => $agent_string);
|
282 |
+
|
283 |
+
return apply_filters('facebook_woocommerce_pixel_init', sprintf(
|
284 |
+
"fbq('init', '%s', %s, %s);\n",
|
285 |
+
esc_js(self::get_pixel_id()),
|
286 |
+
json_encode($this->user_info, JSON_PRETTY_PRINT | JSON_FORCE_OBJECT),
|
287 |
+
json_encode($params, JSON_PRETTY_PRINT | JSON_FORCE_OBJECT)));
|
288 |
+
}
|
289 |
+
|
290 |
+
}
|
291 |
+
|
292 |
+
endif;
|
facebook-commerce.php
ADDED
@@ -0,0 +1,2541 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* @package FacebookCommerce
|
9 |
+
*/
|
10 |
+
|
11 |
+
if (!defined('ABSPATH')) exit; // Exit if accessed directly
|
12 |
+
|
13 |
+
include_once('facebook-config-warmer.php');
|
14 |
+
include_once('includes/fbproduct.php');
|
15 |
+
include_once 'facebook-commerce-pixel-event.php';
|
16 |
+
|
17 |
+
class WC_Facebookcommerce_Integration extends WC_Integration {
|
18 |
+
|
19 |
+
const FB_PRODUCT_GROUP_ID = 'fb_product_group_id';
|
20 |
+
const FB_PRODUCT_ITEM_ID = 'fb_product_item_id';
|
21 |
+
const FB_PRODUCT_DESCRIPTION = 'fb_product_description';
|
22 |
+
|
23 |
+
const FB_VISIBILITY = 'fb_visibility';
|
24 |
+
|
25 |
+
const FB_CART_URL = 'fb_cart_url';
|
26 |
+
|
27 |
+
const FB_MESSAGE_DISPLAY_TIME = 180;
|
28 |
+
|
29 |
+
// Number of days to query tip.
|
30 |
+
const FB_TIP_QUERY = 1;
|
31 |
+
|
32 |
+
const FB_VARIANT_IMAGE = 'fb_image';
|
33 |
+
|
34 |
+
const FB_ADMIN_MESSAGE_PREPEND = '<b>Facebook for WooCommerce</b><br/>';
|
35 |
+
|
36 |
+
const FB_SYNC_IN_PROGRESS = 'fb_sync_in_progress';
|
37 |
+
const FB_SYNC_REMAINING = 'fb_sync_remaining';
|
38 |
+
const FB_SYNC_TIMEOUT = 30;
|
39 |
+
const FB_PRIORITY_MID = 9;
|
40 |
+
|
41 |
+
private $test_mode = false;
|
42 |
+
|
43 |
+
public function init_settings() {
|
44 |
+
parent::init_settings();
|
45 |
+
}
|
46 |
+
|
47 |
+
public function init_pixel() {
|
48 |
+
WC_Facebookcommerce_Pixel::initialize();
|
49 |
+
|
50 |
+
// Migrate WC customer pixel_id from WC settings to WP options.
|
51 |
+
// This is part of a larger effort to consolidate all the FB-specific
|
52 |
+
// settings for all plugin integrations.
|
53 |
+
if (is_admin()) {
|
54 |
+
$pixel_id = WC_Facebookcommerce_Pixel::get_pixel_id();
|
55 |
+
$settings_pixel_id = isset($this->settings['fb_pixel_id']) ?
|
56 |
+
(string)$this->settings['fb_pixel_id'] : null;
|
57 |
+
if (
|
58 |
+
WC_Facebookcommerce_Utils::is_valid_id($settings_pixel_id) &&
|
59 |
+
(!WC_Facebookcommerce_Utils::is_valid_id($pixel_id) ||
|
60 |
+
$pixel_id != $settings_pixel_id
|
61 |
+
)
|
62 |
+
) {
|
63 |
+
WC_Facebookcommerce_Pixel::set_pixel_id($settings_pixel_id);
|
64 |
+
}
|
65 |
+
}
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Init and hook in the integration.
|
70 |
+
*
|
71 |
+
* @access public
|
72 |
+
* @return void
|
73 |
+
*/
|
74 |
+
public function __construct() {
|
75 |
+
if (!class_exists('WC_Facebookcommerce_REST_Controller')) {
|
76 |
+
include_once( 'includes/fbcustomapi.php' );
|
77 |
+
$this->customapi = new WC_Facebookcommerce_REST_Controller();
|
78 |
+
}
|
79 |
+
|
80 |
+
if (!class_exists('WC_Facebookcommerce_EventsTracker')) {
|
81 |
+
include_once 'facebook-commerce-events-tracker.php';
|
82 |
+
}
|
83 |
+
|
84 |
+
$this->id = 'facebookcommerce';
|
85 |
+
$this->method_title = __(
|
86 |
+
'Facebook for WooCommerce',
|
87 |
+
'facebook-for-commerce');
|
88 |
+
$this->method_description = __(
|
89 |
+
'Facebook Commerce and Dynamic Ads (Pixel) Extension',
|
90 |
+
'facebook-for-commerce');
|
91 |
+
|
92 |
+
// Load the settings.
|
93 |
+
$this->init_settings();
|
94 |
+
|
95 |
+
$this->page_id = isset($this->settings['fb_page_id'])
|
96 |
+
? $this->settings['fb_page_id']
|
97 |
+
: '';
|
98 |
+
|
99 |
+
$this->api_key = isset($this->settings['fb_api_key'])
|
100 |
+
? $this->settings['fb_api_key']
|
101 |
+
: '';
|
102 |
+
|
103 |
+
$pixel_id = WC_Facebookcommerce_Pixel::get_pixel_id();
|
104 |
+
if (!$pixel_id) {
|
105 |
+
$pixel_id = isset($this->settings['fb_pixel_id']) ?
|
106 |
+
$this->settings['fb_pixel_id'] : '';
|
107 |
+
}
|
108 |
+
$this->pixel_id = isset($pixel_id)
|
109 |
+
? $pixel_id
|
110 |
+
: '';
|
111 |
+
|
112 |
+
$this->pixel_install_time = isset($this->settings['pixel_install_time'])
|
113 |
+
? $this->settings['pixel_install_time']
|
114 |
+
: '';
|
115 |
+
|
116 |
+
$this->use_pii = isset($this->settings['fb_pixel_use_pii'])
|
117 |
+
&& $this->settings['fb_pixel_use_pii'] === 'yes'
|
118 |
+
? true
|
119 |
+
: false;
|
120 |
+
|
121 |
+
$this->product_catalog_id = isset($this->settings['fb_product_catalog_id'])
|
122 |
+
? $this->settings['fb_product_catalog_id']
|
123 |
+
: '';
|
124 |
+
|
125 |
+
$this->external_merchant_settings_id =
|
126 |
+
isset($this->settings['fb_external_merchant_settings_id'])
|
127 |
+
? $this->settings['fb_external_merchant_settings_id']
|
128 |
+
: '';
|
129 |
+
|
130 |
+
if (!class_exists('WC_Facebookcommerce_Utils')) {
|
131 |
+
include_once 'includes/fbutils.php';
|
132 |
+
}
|
133 |
+
|
134 |
+
WC_Facebookcommerce_Utils::$ems = $this->external_merchant_settings_id;
|
135 |
+
|
136 |
+
if (!class_exists('WC_Facebookcommerce_Graph_API')) {
|
137 |
+
include_once 'includes/fbgraph.php';
|
138 |
+
$this->fbgraph = new WC_Facebookcommerce_Graph_API($this->api_key);
|
139 |
+
}
|
140 |
+
|
141 |
+
WC_Facebookcommerce_Utils::$fbgraph = $this->fbgraph;
|
142 |
+
$this->feed_id = isset($this->settings['fb_feed_id'])
|
143 |
+
? $this->settings['fb_feed_id']
|
144 |
+
: '';
|
145 |
+
|
146 |
+
// Hooks
|
147 |
+
if (is_admin()) {
|
148 |
+
$this->init_pixel();
|
149 |
+
$this->init_form_fields();
|
150 |
+
// Display an info banner for eligible pixel and user.
|
151 |
+
if ($this->external_merchant_settings_id
|
152 |
+
&& $this->pixel_id
|
153 |
+
&& $this->pixel_install_time) {
|
154 |
+
$should_query_tip =
|
155 |
+
WC_Facebookcommerce_Utils::check_time_cap(
|
156 |
+
get_option('fb_info_banner_last_query_time', ''),
|
157 |
+
self::FB_TIP_QUERY);
|
158 |
+
$last_tip_info = WC_Facebookcommerce_Utils::get_cached_best_tip();
|
159 |
+
|
160 |
+
if ($should_query_tip || $last_tip_info) {
|
161 |
+
if (!class_exists('WC_Facebookcommerce_Info_Banner')) {
|
162 |
+
include_once 'includes/fbinfobanner.php';
|
163 |
+
}
|
164 |
+
WC_Facebookcommerce_Info_Banner::get_instance(
|
165 |
+
$this->external_merchant_settings_id,
|
166 |
+
$this->fbgraph,
|
167 |
+
$should_query_tip);
|
168 |
+
}
|
169 |
+
}
|
170 |
+
|
171 |
+
if (!class_exists('WC_Facebook_Integration_Test')) {
|
172 |
+
include_once 'includes/test/facebook-integration-test.php';
|
173 |
+
}
|
174 |
+
$integration_test = WC_Facebook_Integration_Test::get_instance($this);
|
175 |
+
$integration_test::$fbgraph = $this->fbgraph;
|
176 |
+
|
177 |
+
if (!$this->pixel_install_time && $this->pixel_id) {
|
178 |
+
$this->pixel_install_time = current_time('mysql');
|
179 |
+
$this->settings['pixel_install_time'] = $this->pixel_install_time;
|
180 |
+
update_option(
|
181 |
+
$this->get_option_key(),
|
182 |
+
apply_filters(
|
183 |
+
'woocommerce_settings_api_sanitized_fields_' . $this->id,
|
184 |
+
$this->settings));
|
185 |
+
}
|
186 |
+
add_action('admin_notices', array( $this, 'checks' ));
|
187 |
+
add_action('woocommerce_update_options_integration_facebookcommerce',
|
188 |
+
array($this, 'process_admin_options'));
|
189 |
+
add_action('admin_enqueue_scripts', array( $this, 'load_assets'));
|
190 |
+
|
191 |
+
add_action('wp_ajax_ajax_save_fb_settings',
|
192 |
+
array($this, 'ajax_save_fb_settings'), self::FB_PRIORITY_MID);
|
193 |
+
|
194 |
+
add_action('wp_ajax_ajax_delete_fb_settings',
|
195 |
+
array($this, 'ajax_delete_fb_settings'), self::FB_PRIORITY_MID);
|
196 |
+
|
197 |
+
add_action('wp_ajax_ajax_sync_all_fb_products',
|
198 |
+
array($this, 'ajax_sync_all_fb_products'), self::FB_PRIORITY_MID);
|
199 |
+
|
200 |
+
add_action('wp_ajax_ajax_sync_all_fb_products_using_feed',
|
201 |
+
array($this, 'ajax_sync_all_fb_products_using_feed'),
|
202 |
+
self::FB_PRIORITY_MID);
|
203 |
+
|
204 |
+
add_action('wp_ajax_ajax_check_feed_upload_status',
|
205 |
+
array($this, 'ajax_check_feed_upload_status'),
|
206 |
+
self::FB_PRIORITY_MID);
|
207 |
+
|
208 |
+
add_action('wp_ajax_ajax_reset_all_fb_products',
|
209 |
+
array($this, 'ajax_reset_all_fb_products'),
|
210 |
+
self::FB_PRIORITY_MID);
|
211 |
+
add_action('wp_ajax_ajax_display_test_result',
|
212 |
+
array($this, 'ajax_display_test_result'));
|
213 |
+
|
214 |
+
add_action('wp_ajax_ajax_schedule_force_resync',
|
215 |
+
array($this, 'ajax_schedule_force_resync'), self::FB_PRIORITY_MID);
|
216 |
+
|
217 |
+
add_action('wp_ajax_ajax_update_fb_option',
|
218 |
+
array($this, 'ajax_update_fb_option'), self::FB_PRIORITY_MID);
|
219 |
+
|
220 |
+
// Only load product processing hooks if we have completed setup.
|
221 |
+
if ($this->api_key && $this->product_catalog_id) {
|
222 |
+
add_action(
|
223 |
+
'woocommerce_process_product_meta_simple',
|
224 |
+
array($this, 'on_simple_product_publish'),
|
225 |
+
10, // Action priority
|
226 |
+
1 // Args passed to on_product_publish (should be 'id')
|
227 |
+
);
|
228 |
+
|
229 |
+
add_action(
|
230 |
+
'woocommerce_process_product_meta_variable',
|
231 |
+
array($this, 'on_variable_product_publish'),
|
232 |
+
10, // Action priority
|
233 |
+
1 // Args passed to on_product_publish (should be 'id')
|
234 |
+
);
|
235 |
+
|
236 |
+
add_action(
|
237 |
+
'woocommerce_process_product_meta_booking',
|
238 |
+
array($this, 'on_simple_product_publish'),
|
239 |
+
10, // Action priority
|
240 |
+
1 // Args passed to on_product_publish (should be 'id')
|
241 |
+
);
|
242 |
+
|
243 |
+
add_action(
|
244 |
+
'woocommerce_process_product_meta_external',
|
245 |
+
array($this, 'on_simple_product_publish'),
|
246 |
+
10, // Action priority
|
247 |
+
1 // Args passed to on_product_publish (should be 'id')
|
248 |
+
);
|
249 |
+
|
250 |
+
add_action(
|
251 |
+
'woocommerce_process_product_meta_subscription',
|
252 |
+
array($this, 'on_product_publish'),
|
253 |
+
10, // Action priority
|
254 |
+
1 // Args passed to on_product_publish (should be 'id')
|
255 |
+
);
|
256 |
+
|
257 |
+
add_action(
|
258 |
+
'woocommerce_process_product_meta_variable-subscription',
|
259 |
+
array($this, 'on_product_publish'),
|
260 |
+
10, // Action priority
|
261 |
+
1 // Args passed to on_product_publish (should be 'id')
|
262 |
+
);
|
263 |
+
|
264 |
+
add_action('woocommerce_process_product_meta_bundle',
|
265 |
+
array($this, 'on_product_publish'),
|
266 |
+
10, // Action priority
|
267 |
+
1 // Args passed to on_product_publish (should be 'id')
|
268 |
+
);
|
269 |
+
|
270 |
+
add_action(
|
271 |
+
'woocommerce_product_quick_edit_save',
|
272 |
+
array($this, 'on_quick_and_bulk_edit_save'),
|
273 |
+
10, // Action priority
|
274 |
+
1 // Args passed to on_quick_and_bulk_edit_save ('product')
|
275 |
+
);
|
276 |
+
|
277 |
+
add_action(
|
278 |
+
'woocommerce_product_bulk_edit_save',
|
279 |
+
array($this, 'on_quick_and_bulk_edit_save'),
|
280 |
+
10, // Action priority
|
281 |
+
1 // Args passed to on_quick_and_bulk_edit_save ('product')
|
282 |
+
);
|
283 |
+
|
284 |
+
add_action(
|
285 |
+
'before_delete_post',
|
286 |
+
array($this, 'on_product_delete'),
|
287 |
+
10,
|
288 |
+
1);
|
289 |
+
|
290 |
+
add_action('add_meta_boxes', array($this, 'fb_product_metabox'), 10, 1);
|
291 |
+
|
292 |
+
add_filter('manage_product_posts_columns',
|
293 |
+
array($this, 'fb_product_columns'));
|
294 |
+
add_action('manage_product_posts_custom_column',
|
295 |
+
array( $this, 'fb_render_product_columns' ), 2);
|
296 |
+
add_action('transition_post_status',
|
297 |
+
array($this, 'fb_change_product_published_status'), 10, 3);
|
298 |
+
|
299 |
+
// Product data tab
|
300 |
+
add_filter('woocommerce_product_data_tabs',
|
301 |
+
array($this, 'fb_new_product_tab'));
|
302 |
+
|
303 |
+
add_action('woocommerce_product_data_panels',
|
304 |
+
array($this, 'fb_new_product_tab_content' ));
|
305 |
+
|
306 |
+
add_action('wp_ajax_ajax_fb_toggle_visibility',
|
307 |
+
array($this, 'ajax_toggle_visibility'));
|
308 |
+
|
309 |
+
add_action('wp_ajax_ajax_reset_single_fb_product',
|
310 |
+
array($this, 'ajax_reset_single_fb_product'));
|
311 |
+
|
312 |
+
add_action('wp_ajax_ajax_delete_fb_product',
|
313 |
+
array($this, 'ajax_delete_fb_product'));
|
314 |
+
|
315 |
+
add_filter('woocommerce_duplicate_product_exclude_meta',
|
316 |
+
array($this, 'fb_duplicate_product_reset_meta'));
|
317 |
+
|
318 |
+
add_action('pmxi_after_xml_import',
|
319 |
+
array($this, 'wp_all_import_compat'));
|
320 |
+
|
321 |
+
add_action('wp_ajax_wpmelon_adv_bulk_edit',
|
322 |
+
array($this, 'ajax_woo_adv_bulk_edit_compat'), self::FB_PRIORITY_MID);
|
323 |
+
|
324 |
+
// Used to remove the 'you need to resync' message.
|
325 |
+
if (isset($_GET['remove_sticky'])) {
|
326 |
+
$this->remove_sticky_message();
|
327 |
+
}
|
328 |
+
|
329 |
+
if (defined('ICL_LANGUAGE_CODE')) {
|
330 |
+
include_once('includes/fbwpml.php');
|
331 |
+
new WC_Facebook_WPML_Injector();
|
332 |
+
}
|
333 |
+
|
334 |
+
}
|
335 |
+
$this->load_background_sync_process();
|
336 |
+
}
|
337 |
+
// Must be outside of admin for cron to schedule correctly.
|
338 |
+
add_action('sync_all_fb_products_using_feed',
|
339 |
+
array($this, 'sync_all_fb_products_using_feed'),
|
340 |
+
self::FB_PRIORITY_MID);
|
341 |
+
|
342 |
+
if ($this->pixel_id) {
|
343 |
+
$user_info = WC_Facebookcommerce_Utils::get_user_info($this->use_pii);
|
344 |
+
$this->events_tracker = new WC_Facebookcommerce_EventsTracker($user_info);
|
345 |
+
}
|
346 |
+
|
347 |
+
if (isset($this->settings['is_messenger_chat_plugin_enabled']) &&
|
348 |
+
$this->settings['is_messenger_chat_plugin_enabled'] === 'yes') {
|
349 |
+
if (!class_exists('WC_Facebookcommerce_MessengerChat')) {
|
350 |
+
include_once 'facebook-commerce-messenger-chat.php';
|
351 |
+
}
|
352 |
+
$this->messenger_chat = new WC_Facebookcommerce_MessengerChat($this->settings);
|
353 |
+
}
|
354 |
+
}
|
355 |
+
|
356 |
+
public function load_background_sync_process() {
|
357 |
+
// Attempt to load background processing (Woo 3.x.x only)
|
358 |
+
include_once('includes/fbbackground.php');
|
359 |
+
if (class_exists('WC_Facebookcommerce_Background_Process')) {
|
360 |
+
if (!isset($this->background_processor)) {
|
361 |
+
$this->background_processor =
|
362 |
+
new WC_Facebookcommerce_Background_Process($this);
|
363 |
+
}
|
364 |
+
}
|
365 |
+
add_action('wp_ajax_ajax_fb_background_check_queue',
|
366 |
+
array($this, 'ajax_fb_background_check_queue'));
|
367 |
+
}
|
368 |
+
|
369 |
+
public function ajax_fb_background_check_queue() {
|
370 |
+
WC_Facebookcommerce_Utils::check_woo_ajax_permissions('background check queue', true);
|
371 |
+
$request_time = null;
|
372 |
+
if (isset($_POST['request_time'])) {
|
373 |
+
$request_time = esc_js(sanitize_text_field($_POST['request_time']));
|
374 |
+
}
|
375 |
+
if ($this->settings['fb_api_key']) {
|
376 |
+
if (isset($this->background_processor)) {
|
377 |
+
$is_processing = $this->background_processor->handle_cron_healthcheck();
|
378 |
+
$remaining = $this->background_processor->get_item_count();
|
379 |
+
$response = array(
|
380 |
+
'connected' => true,
|
381 |
+
'background' => true,
|
382 |
+
'processing' => $is_processing,
|
383 |
+
'remaining' => $remaining,
|
384 |
+
'request_time' => $request_time,
|
385 |
+
);
|
386 |
+
} else {
|
387 |
+
$response = array(
|
388 |
+
'connected' => true,
|
389 |
+
'background' => false,
|
390 |
+
);
|
391 |
+
}
|
392 |
+
} else {
|
393 |
+
$response = array(
|
394 |
+
'connected' => false,
|
395 |
+
'background' => false,
|
396 |
+
);
|
397 |
+
}
|
398 |
+
printf(json_encode($response));
|
399 |
+
wp_die();
|
400 |
+
}
|
401 |
+
|
402 |
+
public function fb_new_product_tab($tabs) {
|
403 |
+
$tabs['fb_commerce_tab'] = array(
|
404 |
+
'label' => __('Facebook', 'facebook-for-woocommerce'),
|
405 |
+
'target' => 'facebook_options',
|
406 |
+
'class' => array( 'show_if_simple', 'show_if_variable' ),
|
407 |
+
);
|
408 |
+
return $tabs;
|
409 |
+
}
|
410 |
+
|
411 |
+
public function fb_new_product_tab_content() {
|
412 |
+
global $post;
|
413 |
+
$woo_product = new WC_Facebook_Product($post->ID);
|
414 |
+
$description = get_post_meta(
|
415 |
+
$post->ID,
|
416 |
+
self::FB_PRODUCT_DESCRIPTION,
|
417 |
+
true);
|
418 |
+
|
419 |
+
$price = get_post_meta(
|
420 |
+
$post->ID,
|
421 |
+
WC_Facebook_Product::FB_PRODUCT_PRICE,
|
422 |
+
true);
|
423 |
+
|
424 |
+
$image = get_post_meta(
|
425 |
+
$post->ID,
|
426 |
+
WC_Facebook_Product::FB_PRODUCT_IMAGE,
|
427 |
+
true);
|
428 |
+
|
429 |
+
$image_setting = null;
|
430 |
+
if (WC_Facebookcommerce_Utils::is_variable_type($woo_product->get_type())) {
|
431 |
+
$image_setting = $woo_product->get_use_parent_image();
|
432 |
+
}
|
433 |
+
|
434 |
+
// 'id' attribute needs to match the 'target' parameter set above
|
435 |
+
?><div id='facebook_options' class='panel woocommerce_options_panel'><?php
|
436 |
+
?><div class='options_group'><?php
|
437 |
+
woocommerce_wp_textarea_input(
|
438 |
+
array(
|
439 |
+
'id' => self::FB_PRODUCT_DESCRIPTION,
|
440 |
+
'label' => __('Facebook Description', 'facebook-for-woocommerce'),
|
441 |
+
'desc_tip' => 'true',
|
442 |
+
'description' => __(
|
443 |
+
'Custom (plain-text only) description for product on Facebook. '.
|
444 |
+
'If blank, product description will be used. ' .
|
445 |
+
'If product description is blank, shortname will be used.',
|
446 |
+
'facebook-for-woocommerce'),
|
447 |
+
'cols' => 40,
|
448 |
+
'rows' => 20,
|
449 |
+
'value' => $description,
|
450 |
+
));
|
451 |
+
woocommerce_wp_textarea_input(
|
452 |
+
array(
|
453 |
+
'id' => WC_Facebook_Product::FB_PRODUCT_IMAGE,
|
454 |
+
'label' => __('Facebook Product Image', 'facebook-for-woocommerce'),
|
455 |
+
'desc_tip' => 'true',
|
456 |
+
'description' => __(
|
457 |
+
'Image URL for product on Facebook. Must be an absolute URL '.
|
458 |
+
'e.g. https://...'.
|
459 |
+
'This can be used to override the primary image that will be '.
|
460 |
+
'used on Facebook for this product. If blank, the primary '.
|
461 |
+
'product image in Woo will be used as the primary image on FB.',
|
462 |
+
'facebook-for-woocommerce'),
|
463 |
+
'cols' => 40,
|
464 |
+
'rows' => 10,
|
465 |
+
'value' => $image,
|
466 |
+
));
|
467 |
+
woocommerce_wp_text_input(
|
468 |
+
array(
|
469 |
+
'id' => WC_Facebook_Product::FB_PRODUCT_PRICE,
|
470 |
+
'label' => __('Facebook Price (' .
|
471 |
+
get_woocommerce_currency_symbol() . ')', 'facebook-for-woocommerce'),
|
472 |
+
'desc_tip' => 'true',
|
473 |
+
'description' => __(
|
474 |
+
'Custom price for product on Facebook. '.
|
475 |
+
'Please enter in monetary decimal (.) format without thousand '.
|
476 |
+
'separators and currency symbols. '.
|
477 |
+
'If blank, product price will be used. ',
|
478 |
+
'facebook-for-woocommerce'),
|
479 |
+
'cols' => 40,
|
480 |
+
'rows' => 60,
|
481 |
+
'value' => $price,
|
482 |
+
));
|
483 |
+
if ($image_setting !== null) {
|
484 |
+
woocommerce_wp_checkbox(array(
|
485 |
+
'id' => self::FB_VARIANT_IMAGE,
|
486 |
+
'label' => __('Use Parent Image', 'facebook-for-woocommerce'),
|
487 |
+
'required' => false,
|
488 |
+
'desc_tip' => 'true',
|
489 |
+
'description' => __(
|
490 |
+
' By default, the primary image uploaded to Facebook is the image'.
|
491 |
+
' specified in each variant, if provided. '.
|
492 |
+
' However, if you enable this setting, the '.
|
493 |
+
' image of the parent will be used as the primary image'.
|
494 |
+
' for this product and all its variants instead.'),
|
495 |
+
'value' => $image_setting ? 'yes' : 'no',
|
496 |
+
));
|
497 |
+
}
|
498 |
+
?>
|
499 |
+
</div>
|
500 |
+
</div><?php
|
501 |
+
}
|
502 |
+
|
503 |
+
public function fb_product_columns($existing_columns) {
|
504 |
+
if (empty($existing_columns) && ! is_array($existing_columns)) {
|
505 |
+
$existing_columns = array();
|
506 |
+
}
|
507 |
+
|
508 |
+
$columns = array();
|
509 |
+
$columns['fb'] = __('FB Shop', 'facebook-for-woocommerce');
|
510 |
+
|
511 |
+
// Verify that cart URL hasn't changed. We do it here because this page
|
512 |
+
// is most likely to be visited (so it's a handy place to make the check)
|
513 |
+
$cart_url = get_option(self::FB_CART_URL);
|
514 |
+
if (!empty($cart_url) && (wc_get_cart_url() !== $cart_url)) {
|
515 |
+
$this->display_warning_message('One or more of your products is using a
|
516 |
+
checkout URL that may be different than your shop checkout URL.
|
517 |
+
<a href="' . WOOCOMMERCE_FACEBOOK_PLUGIN_SETTINGS_URL . '">
|
518 |
+
Re-sync your products to update checkout URLs on Facebook.</a>');
|
519 |
+
}
|
520 |
+
|
521 |
+
return array_merge($columns, $existing_columns);
|
522 |
+
}
|
523 |
+
|
524 |
+
public function fb_render_product_columns($column) {
|
525 |
+
global $post, $the_product;
|
526 |
+
|
527 |
+
wp_enqueue_script('wc_facebook_jsx', plugins_url(
|
528 |
+
'/assets/js/facebook-products.js?ts=' . time(), __FILE__));
|
529 |
+
|
530 |
+
if (empty($the_product) || $the_product->get_id() != $post->ID) {
|
531 |
+
$the_product = new WC_Facebook_Product($post);
|
532 |
+
}
|
533 |
+
|
534 |
+
if ($column === 'fb') {
|
535 |
+
$fb_product_group_id = $this->get_product_fbid(
|
536 |
+
self::FB_PRODUCT_GROUP_ID,
|
537 |
+
$post->ID,
|
538 |
+
$the_product);
|
539 |
+
if (!$fb_product_group_id) {
|
540 |
+
printf('<span>Not Synced</span>');
|
541 |
+
} else {
|
542 |
+
$viz_value = get_post_meta($post->ID, self::FB_VISIBILITY, true);
|
543 |
+
$data_tip = $viz_value === '' ?
|
544 |
+
'Product is synced but not marked as published (visible)
|
545 |
+
on Facebook.' :
|
546 |
+
'Product is synced and published (visible) on Facebook.';
|
547 |
+
|
548 |
+
printf('<span class="tips" id="tip_%1$s" data-tip="%2$s">',
|
549 |
+
$post->ID,
|
550 |
+
$data_tip);
|
551 |
+
|
552 |
+
if ($viz_value === '') {
|
553 |
+
printf(
|
554 |
+
'<a id="viz_%1$s" class="button button-primary button-large"
|
555 |
+
href="javascript:;" onclick="fb_toggle_visibility(%1$s, true)">Show</a>',
|
556 |
+
$post->ID);
|
557 |
+
} else {
|
558 |
+
printf(
|
559 |
+
'<a id="viz_%1$s" class="button" href="javascript:;"
|
560 |
+
onclick="fb_toggle_visibility(%1$s, false)">Hide</a>',
|
561 |
+
$post->ID);
|
562 |
+
}
|
563 |
+
}
|
564 |
+
}
|
565 |
+
}
|
566 |
+
|
567 |
+
public function fb_product_metabox() {
|
568 |
+
wp_enqueue_script('wc_facebook_jsx', plugins_url(
|
569 |
+
'/assets/js/facebook-metabox.js?ts=' . time(), __FILE__));
|
570 |
+
|
571 |
+
add_meta_box(
|
572 |
+
'facebook_metabox', // Meta box ID
|
573 |
+
'Facebook', // Meta box Title
|
574 |
+
array($this, 'fb_product_meta_box_html'), // Callback
|
575 |
+
'product', // Screen to which to add the meta box
|
576 |
+
'side' // Context
|
577 |
+
);
|
578 |
+
}
|
579 |
+
|
580 |
+
public function fb_product_meta_box_html() {
|
581 |
+
global $post;
|
582 |
+
$woo_product = new WC_Facebook_Product($post->ID);
|
583 |
+
$fb_product_group_id = $this->get_product_fbid(
|
584 |
+
self::FB_PRODUCT_GROUP_ID,
|
585 |
+
$post->ID,
|
586 |
+
$woo_product);
|
587 |
+
printf('<span id="fb_metadata">');
|
588 |
+
if ($fb_product_group_id) {
|
589 |
+
printf('Facebook ID: <a href="https://facebook.com/'.
|
590 |
+
$fb_product_group_id . '" target="_blank">' .
|
591 |
+
$fb_product_group_id . '</a><p/>');
|
592 |
+
if (WC_Facebookcommerce_Utils::is_variable_type($woo_product->get_type())) {
|
593 |
+
printf('<p>Variant IDs:<br/>');
|
594 |
+
$children = $woo_product->get_children();
|
595 |
+
foreach ($children as $child_id) {
|
596 |
+
$fb_product_item_id = $this->get_product_fbid(
|
597 |
+
self::FB_PRODUCT_ITEM_ID,
|
598 |
+
$child_id);
|
599 |
+
printf($child_id .' : <a href="https://facebook.com/'.
|
600 |
+
$fb_product_item_id . '" target="_blank">' .
|
601 |
+
$fb_product_item_id . '</a><br/>');
|
602 |
+
}
|
603 |
+
printf('</p>');
|
604 |
+
}
|
605 |
+
|
606 |
+
$checkbox_value = get_post_meta($post->ID, self::FB_VISIBILITY, true);
|
607 |
+
|
608 |
+
printf('Visible: <input name="%1$s" type="checkbox" value="1" %2$s/>',
|
609 |
+
self::FB_VISIBILITY,
|
610 |
+
$checkbox_value === '' ? '' : 'checked');
|
611 |
+
printf('<p/><input name="is_product_page" type="hidden" value="1"');
|
612 |
+
|
613 |
+
printf(
|
614 |
+
'<p/><a href="#" onclick="fb_reset_product(%1$s)">
|
615 |
+
Reset Facebook metadata</a>',
|
616 |
+
$post->ID);
|
617 |
+
|
618 |
+
printf(
|
619 |
+
'<p/><a href="#" onclick="fb_delete_product(%1$s)">
|
620 |
+
Delete product(s) on Facebook</a>',
|
621 |
+
$post->ID);
|
622 |
+
} else {
|
623 |
+
printf("<b>This product is not yet synced to Facebook.</b>");
|
624 |
+
}
|
625 |
+
printf('</span>');
|
626 |
+
}
|
627 |
+
|
628 |
+
private function get_product_count() {
|
629 |
+
$args = array(
|
630 |
+
'post_type' => 'product',
|
631 |
+
'post_status' => 'publish',
|
632 |
+
'posts_per_page' => -1,
|
633 |
+
'fields' => 'ids'
|
634 |
+
);
|
635 |
+
$products = new WP_Query($args);
|
636 |
+
|
637 |
+
wp_reset_postdata();
|
638 |
+
|
639 |
+
echo $products->found_posts;
|
640 |
+
}
|
641 |
+
|
642 |
+
/**
|
643 |
+
* Load DIA specific JS Data
|
644 |
+
*/
|
645 |
+
public function load_assets() {
|
646 |
+
$screen = get_current_screen();
|
647 |
+
|
648 |
+
// load banner assets
|
649 |
+
wp_enqueue_script('wc_facebook_infobanner_jsx', plugins_url(
|
650 |
+
'/assets/js/facebook-infobanner.js?ts=' . time(), __FILE__));
|
651 |
+
|
652 |
+
wp_enqueue_style('wc_facebook_infobanner_css', plugins_url(
|
653 |
+
'/assets/css/facebook-infobanner.css', __FILE__));
|
654 |
+
|
655 |
+
if (strpos($screen->id , "page_wc-settings") == 0) {
|
656 |
+
return;
|
657 |
+
}
|
658 |
+
|
659 |
+
if (empty($_GET['tab'])) {
|
660 |
+
return;
|
661 |
+
}
|
662 |
+
|
663 |
+
if ('integration' !== $_GET['tab']) {
|
664 |
+
return;
|
665 |
+
}
|
666 |
+
|
667 |
+
?>
|
668 |
+
<script>
|
669 |
+
window.facebookAdsToolboxConfig = {
|
670 |
+
hasGzipSupport:
|
671 |
+
'<?php echo extension_loaded('zlib') ? 'true' : 'false' ?>'
|
672 |
+
,enabledPlugins: ['MESSENGER_CHAT','INSTAGRAM_SHOP']
|
673 |
+
,enableSubscription:
|
674 |
+
'<?php echo class_exists('WC_Subscriptions') ? 'true' : 'false' ?>'
|
675 |
+
,popupOrigin: '<?php echo isset($_GET['url']) ? esc_js($_GET['url']) :
|
676 |
+
'https://www.facebook.com/' ?>'
|
677 |
+
,feedWasDisabled: 'true'
|
678 |
+
,platform: 'WooCommerce'
|
679 |
+
,pixel: {
|
680 |
+
pixelId: '<?php echo $this->pixel_id ?: '' ?>'
|
681 |
+
,advanced_matching_supported: true
|
682 |
+
}
|
683 |
+
,diaSettingId: '<?php echo $this->external_merchant_settings_id ?: '' ?>'
|
684 |
+
,store: {
|
685 |
+
baseUrl: window.location.protocol + '//' + window.location.host
|
686 |
+
,baseCurrency:
|
687 |
+
'<?php echo esc_js(
|
688 |
+
WC_Admin_Settings::get_option('woocommerce_currency'))?>'
|
689 |
+
,timezoneId: '<?php echo date('Z') ?>'
|
690 |
+
,storeName:
|
691 |
+
'<?php echo esc_js(WC_Facebookcommerce_Utils::get_store_name()); ?>'
|
692 |
+
,version: '<?php echo WC()->version ?>'
|
693 |
+
,php_version: '<?php echo PHP_VERSION ?>'
|
694 |
+
,plugin_version:
|
695 |
+
'<?php echo WC_Facebookcommerce_Utils::PLUGIN_VERSION ?>'
|
696 |
+
}
|
697 |
+
,feed: {
|
698 |
+
totalVisibleProducts: '<?php echo $this->get_product_count() ?>'
|
699 |
+
,hasClientSideFeedUpload: '<?php echo !!$this->feed_id ?>'
|
700 |
+
}
|
701 |
+
,feedPrepared: {
|
702 |
+
feedUrl: ''
|
703 |
+
,feedPingUrl: ''
|
704 |
+
,samples: <?php echo $this->get_sample_product_feed()?>
|
705 |
+
}
|
706 |
+
,tokenExpired: '<?php echo $this->settings['fb_api_key'] &&
|
707 |
+
!$this->get_page_name()?>'
|
708 |
+
};
|
709 |
+
</script>
|
710 |
+
<?php
|
711 |
+
wp_enqueue_script('wc_facebook_jsx', plugins_url(
|
712 |
+
'/assets/js/facebook-settings.js?ts=' . time(), __FILE__));
|
713 |
+
wp_enqueue_style('wc_facebook_css', plugins_url(
|
714 |
+
'/assets/css/facebook.css', __FILE__));
|
715 |
+
}
|
716 |
+
|
717 |
+
function on_product_delete($wp_id) {
|
718 |
+
$woo_product = new WC_Facebook_Product($wp_id);
|
719 |
+
if (!$woo_product->exists()) {
|
720 |
+
// This happens when the wp_id is not a product or it's already
|
721 |
+
// been deleted.
|
722 |
+
return;
|
723 |
+
}
|
724 |
+
$fb_product_group_id = $this->get_product_fbid(
|
725 |
+
self::FB_PRODUCT_GROUP_ID,
|
726 |
+
$wp_id,
|
727 |
+
$woo_product);
|
728 |
+
$fb_product_item_id = $this->get_product_fbid(
|
729 |
+
self::FB_PRODUCT_ITEM_ID,
|
730 |
+
$wp_id,
|
731 |
+
$woo_product);
|
732 |
+
if (! ($fb_product_group_id || $fb_product_item_id ) ) {
|
733 |
+
return; // No synced product, no-op.
|
734 |
+
}
|
735 |
+
$products = array($wp_id);
|
736 |
+
if (WC_Facebookcommerce_Utils::is_variable_type($woo_product->get_type())) {
|
737 |
+
$children = $woo_product->get_children();
|
738 |
+
$products = array_merge($products, $children);
|
739 |
+
}
|
740 |
+
foreach ($products as $item_id) {
|
741 |
+
$this->delete_product_item($item_id);
|
742 |
+
}
|
743 |
+
if ($fb_product_group_id) {
|
744 |
+
$pg_result = $this->fbgraph->delete_product_group($fb_product_group_id);
|
745 |
+
WC_Facebookcommerce_Utils::log($pg_result);
|
746 |
+
}
|
747 |
+
}
|
748 |
+
|
749 |
+
/**
|
750 |
+
* Update FB visibility for trashing and restore.
|
751 |
+
*/
|
752 |
+
function fb_change_product_published_status($new_status, $old_status, $post) {
|
753 |
+
global $post;
|
754 |
+
$visibility = $new_status == 'publish' ? 'published' : 'staging';
|
755 |
+
|
756 |
+
// change from publish status -> unpublish status, e.g. trash, draft, etc.
|
757 |
+
// change from trash status -> publish status
|
758 |
+
// no need to update for change from trash <-> unpublish status
|
759 |
+
if (($old_status == 'publish' && $new_status != 'publish') ||
|
760 |
+
($old_status == 'trash' && $new_status == 'publish')) {
|
761 |
+
$this->update_fb_visibility($post->ID, $visibility);
|
762 |
+
}
|
763 |
+
}
|
764 |
+
|
765 |
+
/**
|
766 |
+
* Generic function for use with any product publishing.
|
767 |
+
* Will determine product type (simple or variable) and delegate to
|
768 |
+
* appropriate handler.
|
769 |
+
*/
|
770 |
+
function on_product_publish($wp_id) {
|
771 |
+
if (get_post_status($wp_id) != 'publish') {
|
772 |
+
return;
|
773 |
+
}
|
774 |
+
|
775 |
+
$woo_product = new WC_Facebook_Product($wp_id);
|
776 |
+
$product_type = $woo_product->get_type();
|
777 |
+
if (WC_Facebookcommerce_Utils::is_variable_type($woo_product->get_type())) {
|
778 |
+
$this->on_variable_product_publish($wp_id, $woo_product);
|
779 |
+
} else {
|
780 |
+
$this->on_simple_product_publish($wp_id, $woo_product);
|
781 |
+
}
|
782 |
+
}
|
783 |
+
|
784 |
+
/**
|
785 |
+
* If the user has opt-in to remove products that are out of stock,
|
786 |
+
* this function will delete the product from FB Page as well.
|
787 |
+
*/
|
788 |
+
function delete_on_out_of_stock($wp_id, $woo_product) {
|
789 |
+
if (get_option('woocommerce_hide_out_of_stock_items') === 'yes' &&
|
790 |
+
!$woo_product->is_in_stock()) {
|
791 |
+
$this->delete_product_item($wp_id);
|
792 |
+
return true;
|
793 |
+
}
|
794 |
+
return false;
|
795 |
+
}
|
796 |
+
|
797 |
+
function on_variable_product_publish($wp_id, $woo_product = null) {
|
798 |
+
if (get_option('fb_disable_sync_on_dev_environment', false)) {
|
799 |
+
return;
|
800 |
+
}
|
801 |
+
|
802 |
+
if (get_post_status($wp_id) != 'publish') {
|
803 |
+
return;
|
804 |
+
}
|
805 |
+
// Check if product group has been published to FB. If not, it's new.
|
806 |
+
// If yes, loop through variants and see if product items are published.
|
807 |
+
if (!$woo_product) {
|
808 |
+
$woo_product = new WC_Facebook_Product($wp_id);
|
809 |
+
}
|
810 |
+
|
811 |
+
if ($this->delete_on_out_of_stock($wp_id, $woo_product)) {
|
812 |
+
return;
|
813 |
+
}
|
814 |
+
|
815 |
+
if (isset($_POST[self::FB_PRODUCT_DESCRIPTION])) {
|
816 |
+
$woo_product->set_description($_POST[self::FB_PRODUCT_DESCRIPTION]);
|
817 |
+
}
|
818 |
+
if (isset($_POST[WC_Facebook_Product::FB_PRODUCT_PRICE])) {
|
819 |
+
$woo_product->set_price($_POST[WC_Facebook_Product::FB_PRODUCT_PRICE]);
|
820 |
+
}
|
821 |
+
if (isset($_POST[WC_Facebook_Product::FB_PRODUCT_IMAGE])) {
|
822 |
+
$woo_product->set_product_image($_POST[WC_Facebook_Product::FB_PRODUCT_IMAGE]);
|
823 |
+
}
|
824 |
+
|
825 |
+
$woo_product->set_use_parent_image(
|
826 |
+
(isset($_POST[self::FB_VARIANT_IMAGE])) ?
|
827 |
+
$_POST[self::FB_VARIANT_IMAGE] :
|
828 |
+
null);
|
829 |
+
$fb_product_group_id = $this->get_product_fbid(
|
830 |
+
self::FB_PRODUCT_GROUP_ID,
|
831 |
+
$wp_id,
|
832 |
+
$woo_product);
|
833 |
+
|
834 |
+
if ($fb_product_group_id) {
|
835 |
+
$woo_product->update_visibility(
|
836 |
+
isset($_POST['is_product_page']),
|
837 |
+
isset($_POST[self::FB_VISIBILITY]));
|
838 |
+
$this->update_product_group($woo_product);
|
839 |
+
$child_products = $woo_product->get_children();
|
840 |
+
$variation_id = $woo_product->find_matching_product_variation();
|
841 |
+
// check if item_id is default variation. If yes, update in the end.
|
842 |
+
// If default variation value is to update, delete old fb_product_item_id
|
843 |
+
// and create new one in order to make it order correctly.
|
844 |
+
foreach ($child_products as $item_id) {
|
845 |
+
$fb_product_item_id =
|
846 |
+
$this->on_simple_product_publish($item_id, null, $woo_product);
|
847 |
+
if ($item_id == $variation_id && $fb_product_item_id) {
|
848 |
+
$this->set_default_variant($fb_product_group_id, $fb_product_item_id);
|
849 |
+
}
|
850 |
+
}
|
851 |
+
} else {
|
852 |
+
$this->create_product_variable($woo_product);
|
853 |
+
}
|
854 |
+
}
|
855 |
+
|
856 |
+
function on_simple_product_publish(
|
857 |
+
$wp_id,
|
858 |
+
$woo_product = null,
|
859 |
+
&$parent_product = null) {
|
860 |
+
if (get_option('fb_disable_sync_on_dev_environment', false)) {
|
861 |
+
return;
|
862 |
+
}
|
863 |
+
|
864 |
+
if (get_post_status($wp_id) != 'publish') {
|
865 |
+
return;
|
866 |
+
}
|
867 |
+
|
868 |
+
if (!$woo_product) {
|
869 |
+
$woo_product = new WC_Facebook_Product($wp_id, $parent_product);
|
870 |
+
}
|
871 |
+
|
872 |
+
if ($this->delete_on_out_of_stock($wp_id, $woo_product)) {
|
873 |
+
return;
|
874 |
+
}
|
875 |
+
|
876 |
+
if (isset($_POST[self::FB_PRODUCT_DESCRIPTION])) {
|
877 |
+
$woo_product->set_description($_POST[self::FB_PRODUCT_DESCRIPTION]);
|
878 |
+
}
|
879 |
+
|
880 |
+
if (isset($_POST[WC_Facebook_Product::FB_PRODUCT_PRICE])) {
|
881 |
+
$woo_product->set_price($_POST[WC_Facebook_Product::FB_PRODUCT_PRICE]);
|
882 |
+
}
|
883 |
+
|
884 |
+
if (isset($_POST[WC_Facebook_Product::FB_PRODUCT_IMAGE])) {
|
885 |
+
$woo_product->set_product_image($_POST[WC_Facebook_Product::FB_PRODUCT_IMAGE]);
|
886 |
+
}
|
887 |
+
|
888 |
+
// Check if this product has already been published to FB.
|
889 |
+
// If not, it's new!
|
890 |
+
$fb_product_item_id = $this->get_product_fbid(
|
891 |
+
self::FB_PRODUCT_ITEM_ID,
|
892 |
+
$wp_id,
|
893 |
+
$woo_product);
|
894 |
+
|
895 |
+
if ($fb_product_item_id) {
|
896 |
+
$woo_product->update_visibility(
|
897 |
+
isset($_POST['is_product_page']),
|
898 |
+
isset($_POST[self::FB_VISIBILITY]));
|
899 |
+
$this->update_product_item($woo_product, $fb_product_item_id);
|
900 |
+
return $fb_product_item_id;
|
901 |
+
} else {
|
902 |
+
// Check if this is a new product item for an existing product group
|
903 |
+
if ($woo_product->get_parent_id()) {
|
904 |
+
$fb_product_group_id = $this->get_product_fbid(
|
905 |
+
self::FB_PRODUCT_GROUP_ID,
|
906 |
+
$woo_product->get_parent_id(),
|
907 |
+
$woo_product);
|
908 |
+
|
909 |
+
// New variant added
|
910 |
+
if ($fb_product_group_id) {
|
911 |
+
return
|
912 |
+
$this->create_product_simple($woo_product, $fb_product_group_id);
|
913 |
+
} else {
|
914 |
+
WC_Facebookcommerce_Utils::fblog(
|
915 |
+
"Wrong! simple_product_publish called without group ID for
|
916 |
+
a variable product!", array(), true);
|
917 |
+
}
|
918 |
+
} else {
|
919 |
+
return $this->create_product_simple($woo_product); // new product
|
920 |
+
}
|
921 |
+
}
|
922 |
+
}
|
923 |
+
|
924 |
+
function create_product_variable($woo_product) {
|
925 |
+
$retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id($woo_product);
|
926 |
+
|
927 |
+
$fb_product_group_id = $this->create_product_group(
|
928 |
+
$woo_product,
|
929 |
+
$retailer_id,
|
930 |
+
true);
|
931 |
+
|
932 |
+
if ($fb_product_group_id) {
|
933 |
+
$child_products = $woo_product->get_children();
|
934 |
+
$variation_id = $woo_product->find_matching_product_variation();
|
935 |
+
foreach ($child_products as $item_id) {
|
936 |
+
$child_product = new WC_Facebook_Product($item_id, $woo_product);
|
937 |
+
$retailer_id =
|
938 |
+
WC_Facebookcommerce_Utils::get_fb_retailer_id($child_product);
|
939 |
+
$fb_product_item_id = $this->create_product_item(
|
940 |
+
$child_product,
|
941 |
+
$retailer_id,
|
942 |
+
$fb_product_group_id);
|
943 |
+
if ($item_id == $variation_id && $fb_product_item_id) {
|
944 |
+
$this->set_default_variant($fb_product_group_id, $fb_product_item_id);
|
945 |
+
}
|
946 |
+
}
|
947 |
+
}
|
948 |
+
}
|
949 |
+
|
950 |
+
/**
|
951 |
+
* Create product group and product, store fb-specific info
|
952 |
+
**/
|
953 |
+
function create_product_simple($woo_product, $fb_product_group_id = null) {
|
954 |
+
$retailer_id = WC_Facebookcommerce_Utils::get_fb_retailer_id($woo_product);
|
955 |
+
|
956 |
+
if (!$fb_product_group_id) {
|
957 |
+
$fb_product_group_id = $this->create_product_group(
|
958 |
+
$woo_product,
|
959 |
+
$retailer_id);
|
960 |
+
}
|
961 |
+
|
962 |
+
if ($fb_product_group_id) {
|
963 |
+
$fb_product_item_id = $this->create_product_item(
|
964 |
+
$woo_product,
|
965 |
+
$retailer_id,
|
966 |
+
$fb_product_group_id);
|
967 |
+
return $fb_product_item_id;
|
968 |
+
}
|
969 |
+
}
|
970 |
+
|
971 |
+
function create_product_group($woo_product, $retailer_id, $variants = false) {
|
972 |
+
|
973 |
+
$product_group_data = array(
|
974 |
+
'retailer_id' => $retailer_id,
|
975 |
+
);
|
976 |
+
|
977 |
+
// Default visibility on create = published
|
978 |
+
$woo_product->fb_visibility = true;
|
979 |
+
update_post_meta($woo_product->get_id(), self::FB_VISIBILITY, true);
|
980 |
+
|
981 |
+
if ($variants) {
|
982 |
+
$product_group_data['variants'] =
|
983 |
+
$woo_product->prepare_variants_for_group();
|
984 |
+
}
|
985 |
+
|
986 |
+
$create_product_group_result = $this->check_api_result(
|
987 |
+
$this->fbgraph->create_product_group(
|
988 |
+
$this->product_catalog_id,
|
989 |
+
$product_group_data),
|
990 |
+
$product_group_data,
|
991 |
+
$woo_product->get_id());
|
992 |
+
|
993 |
+
// New variant added
|
994 |
+
if ($create_product_group_result) {
|
995 |
+
$decode_result = WC_Facebookcommerce_Utils::decode_json($create_product_group_result['body']);
|
996 |
+
$fb_product_group_id = $decode_result->id;
|
997 |
+
// update_post_meta is actually more of a create_or_update
|
998 |
+
update_post_meta(
|
999 |
+
$woo_product->get_id(),
|
1000 |
+
self::FB_PRODUCT_GROUP_ID,
|
1001 |
+
$fb_product_group_id);
|
1002 |
+
|
1003 |
+
$this->display_success_message(
|
1004 |
+
'Created product group <a href="https://facebook.com/'.
|
1005 |
+
$fb_product_group_id . '" target="_blank">' .
|
1006 |
+
$fb_product_group_id . '</a> on Facebook.');
|
1007 |
+
|
1008 |
+
return $fb_product_group_id;
|
1009 |
+
}
|
1010 |
+
}
|
1011 |
+
|
1012 |
+
function create_product_item($woo_product, $retailer_id, $product_group_id) {
|
1013 |
+
// Default visibility on create = published
|
1014 |
+
$woo_product->fb_visibility = true;
|
1015 |
+
$product_data = $woo_product->prepare_product($retailer_id);
|
1016 |
+
if (!$product_data['price']) {
|
1017 |
+
return 0;
|
1018 |
+
}
|
1019 |
+
|
1020 |
+
update_post_meta($woo_product->get_id(), self::FB_VISIBILITY, true);
|
1021 |
+
|
1022 |
+
$product_result = $this->check_api_result(
|
1023 |
+
$this->fbgraph->create_product_item(
|
1024 |
+
$product_group_id,
|
1025 |
+
$product_data),
|
1026 |
+
$product_data,
|
1027 |
+
$woo_product->get_id());
|
1028 |
+
|
1029 |
+
if ($product_result) {
|
1030 |
+
$decode_result = WC_Facebookcommerce_Utils::decode_json($product_result['body']);
|
1031 |
+
$fb_product_item_id = $decode_result->id;
|
1032 |
+
|
1033 |
+
update_post_meta($woo_product->get_id(),
|
1034 |
+
self::FB_PRODUCT_ITEM_ID, $fb_product_item_id);
|
1035 |
+
|
1036 |
+
$this->display_success_message(
|
1037 |
+
'Created product item <a href="https://facebook.com/'.
|
1038 |
+
$fb_product_item_id . '" target="_blank">' .
|
1039 |
+
$fb_product_item_id . '</a> on Facebook.');
|
1040 |
+
|
1041 |
+
return $fb_product_item_id;
|
1042 |
+
}
|
1043 |
+
}
|
1044 |
+
|
1045 |
+
|
1046 |
+
/**
|
1047 |
+
* Update existing product group (variant data only)
|
1048 |
+
**/
|
1049 |
+
function update_product_group($woo_product) {
|
1050 |
+
$fb_product_group_id = $this->get_product_fbid(
|
1051 |
+
self::FB_PRODUCT_GROUP_ID,
|
1052 |
+
$woo_product->get_id(),
|
1053 |
+
$woo_product);
|
1054 |
+
|
1055 |
+
if (!$fb_product_group_id) {
|
1056 |
+
return;
|
1057 |
+
}
|
1058 |
+
|
1059 |
+
$variants = $woo_product->prepare_variants_for_group();
|
1060 |
+
|
1061 |
+
if (!$variants) {
|
1062 |
+
WC_Facebookcommerce_Utils::log(
|
1063 |
+
sprintf(__('Nothing to update for product group for %1$s',
|
1064 |
+
'facebook-for-woocommerce'),
|
1065 |
+
$fb_product_group_id));
|
1066 |
+
return;
|
1067 |
+
}
|
1068 |
+
|
1069 |
+
$product_group_data = array(
|
1070 |
+
'variants' => $variants
|
1071 |
+
);
|
1072 |
+
|
1073 |
+
$result = $this->check_api_result(
|
1074 |
+
$this->fbgraph->update_product_group(
|
1075 |
+
$fb_product_group_id,
|
1076 |
+
$product_group_data));
|
1077 |
+
|
1078 |
+
if ($result) {
|
1079 |
+
$this->display_success_message(
|
1080 |
+
'Updated product group <a href="https://facebook.com/'.
|
1081 |
+
$fb_product_group_id .'" target="_blank">' . $fb_product_group_id .
|
1082 |
+
'</a> on Facebook.');
|
1083 |
+
}
|
1084 |
+
}
|
1085 |
+
|
1086 |
+
/**
|
1087 |
+
* Update existing product
|
1088 |
+
**/
|
1089 |
+
function update_product_item($woo_product, $fb_product_item_id) {
|
1090 |
+
$product_data = $woo_product->prepare_product();
|
1091 |
+
|
1092 |
+
$result = $this->check_api_result(
|
1093 |
+
$this->fbgraph->update_product_item(
|
1094 |
+
$fb_product_item_id,
|
1095 |
+
$product_data));
|
1096 |
+
|
1097 |
+
if ($result) {
|
1098 |
+
$this->display_success_message(
|
1099 |
+
'Updated product <a href="https://facebook.com/'. $fb_product_item_id .
|
1100 |
+
'" target="_blank">' . $fb_product_item_id . '</a> on Facebook.');
|
1101 |
+
}
|
1102 |
+
}
|
1103 |
+
|
1104 |
+
/**
|
1105 |
+
* Save settings via AJAX (to preserve window context for onboarding)
|
1106 |
+
**/
|
1107 |
+
function ajax_save_fb_settings() {
|
1108 |
+
WC_Facebookcommerce_Utils::check_woo_ajax_permissions('save settings', true);
|
1109 |
+
|
1110 |
+
if (isset($_REQUEST)) {
|
1111 |
+
if (!isset($_REQUEST['facebook_for_woocommerce'])) {
|
1112 |
+
// This is not a request from our plugin,
|
1113 |
+
// some other handler or plugin probably
|
1114 |
+
// wants to handle it and wp_die() after.
|
1115 |
+
return;
|
1116 |
+
}
|
1117 |
+
|
1118 |
+
if (isset($_REQUEST['api_key']) && ctype_alnum($_REQUEST['api_key'])) {
|
1119 |
+
$this->settings['fb_api_key'] = $_REQUEST['api_key'];
|
1120 |
+
}
|
1121 |
+
if (isset($_REQUEST['product_catalog_id']) &&
|
1122 |
+
ctype_digit($_REQUEST['product_catalog_id'])) {
|
1123 |
+
|
1124 |
+
if ($this->product_catalog_id != '' &&
|
1125 |
+
$this->product_catalog_id != $_REQUEST['product_catalog_id']) {
|
1126 |
+
$this->reset_all_products();
|
1127 |
+
}
|
1128 |
+
$this->settings['fb_product_catalog_id'] =
|
1129 |
+
$_REQUEST['product_catalog_id'];
|
1130 |
+
}
|
1131 |
+
if (isset($_REQUEST['pixel_id']) && ctype_digit($_REQUEST['pixel_id'])) {
|
1132 |
+
// To prevent race conditions with pixel-only settings,
|
1133 |
+
// only save a pixel if we already have an API key.
|
1134 |
+
if ($this->settings['fb_api_key']) {
|
1135 |
+
$this->settings['fb_pixel_id'] = $_REQUEST['pixel_id'];
|
1136 |
+
if ($this->pixel_id != $_REQUEST['pixel_id']) {
|
1137 |
+
$this->settings['pixel_install_time'] = current_time('mysql');
|
1138 |
+
}
|
1139 |
+
} else {
|
1140 |
+
WC_Facebookcommerce_Utils::log(
|
1141 |
+
"Got pixel-only settings, doing nothing");
|
1142 |
+
echo "Not saving pixel-only settings";
|
1143 |
+
wp_die();
|
1144 |
+
}
|
1145 |
+
}
|
1146 |
+
if (isset($_REQUEST['pixel_use_pii'])) {
|
1147 |
+
$this->settings['fb_pixel_use_pii'] =
|
1148 |
+
($_REQUEST['pixel_use_pii'] === 'true' ||
|
1149 |
+
$_REQUEST['pixel_use_pii'] === true) ? 'yes' : 'no';
|
1150 |
+
}
|
1151 |
+
if (isset($_REQUEST['page_id']) &&
|
1152 |
+
ctype_digit($_REQUEST['page_id'])) {
|
1153 |
+
$this->settings['fb_page_id'] = $_REQUEST['page_id'];
|
1154 |
+
}
|
1155 |
+
if (isset($_REQUEST['external_merchant_settings_id']) &&
|
1156 |
+
ctype_digit($_REQUEST['external_merchant_settings_id'])) {
|
1157 |
+
$this->settings['fb_external_merchant_settings_id'] =
|
1158 |
+
$_REQUEST['external_merchant_settings_id'];
|
1159 |
+
}
|
1160 |
+
if (isset($_REQUEST['is_messenger_chat_plugin_enabled'])) {
|
1161 |
+
$this->settings['is_messenger_chat_plugin_enabled'] =
|
1162 |
+
($_REQUEST['is_messenger_chat_plugin_enabled'] === 'true' ||
|
1163 |
+
$_REQUEST['is_messenger_chat_plugin_enabled'] === true) ? 'yes' : 'no';
|
1164 |
+
}
|
1165 |
+
if (isset($_REQUEST['facebook_jssdk_version'])) {
|
1166 |
+
$this->settings['facebook_jssdk_version'] =
|
1167 |
+
sanitize_text_field($_REQUEST['facebook_jssdk_version']);
|
1168 |
+
}
|
1169 |
+
if (isset($_REQUEST['msger_chat_customization_greeting_text_code'])
|
1170 |
+
&& ctype_digit($_REQUEST['msger_chat_customization_greeting_text_code'])) {
|
1171 |
+
$this->settings['msger_chat_customization_greeting_text_code'] =
|
1172 |
+
$_REQUEST['msger_chat_customization_greeting_text_code'];
|
1173 |
+
}
|
1174 |
+
if (isset($_REQUEST['msger_chat_customization_locale'])) {
|
1175 |
+
$this->settings['msger_chat_customization_locale'] =
|
1176 |
+
sanitize_text_field($_REQUEST['msger_chat_customization_locale']);
|
1177 |
+
}
|
1178 |
+
if (isset($_REQUEST['msger_chat_customization_theme_color_code']) &&
|
1179 |
+
ctype_digit($_REQUEST['msger_chat_customization_theme_color_code'])) {
|
1180 |
+
$this->settings['msger_chat_customization_theme_color_code'] =
|
1181 |
+
$_REQUEST['msger_chat_customization_theme_color_code'];
|
1182 |
+
}
|
1183 |
+
|
1184 |
+
update_option(
|
1185 |
+
$this->get_option_key(),
|
1186 |
+
apply_filters(
|
1187 |
+
'woocommerce_settings_api_sanitized_fields_' . $this->id,
|
1188 |
+
$this->settings));
|
1189 |
+
|
1190 |
+
WC_Facebookcommerce_Utils::log("Settings saved!");
|
1191 |
+
echo "settings_saved";
|
1192 |
+
} else {
|
1193 |
+
echo "No Request";
|
1194 |
+
}
|
1195 |
+
|
1196 |
+
wp_die();
|
1197 |
+
}
|
1198 |
+
|
1199 |
+
/**
|
1200 |
+
* Delete all settings via AJAX
|
1201 |
+
**/
|
1202 |
+
function ajax_delete_fb_settings() {
|
1203 |
+
if (!WC_Facebookcommerce_Utils::check_woo_ajax_permissions('delete settings', false)) {
|
1204 |
+
return;
|
1205 |
+
}
|
1206 |
+
|
1207 |
+
// Do not allow reset in the middle of product sync
|
1208 |
+
$currently_syncing = get_transient(self::FB_SYNC_IN_PROGRESS);
|
1209 |
+
if ($currently_syncing) {
|
1210 |
+
wp_send_json('A Facebook product sync is currently in progress.
|
1211 |
+
Deleting settings during product sync may cause errors.');
|
1212 |
+
return;
|
1213 |
+
}
|
1214 |
+
|
1215 |
+
if (isset($_REQUEST)) {
|
1216 |
+
$ems = $this->settings['fb_external_merchant_settings_id'];
|
1217 |
+
if ($ems) {
|
1218 |
+
WC_Facebookcommerce_Utils::fblog(
|
1219 |
+
"Deleted all settings!",
|
1220 |
+
array(),
|
1221 |
+
false,
|
1222 |
+
$ems);
|
1223 |
+
}
|
1224 |
+
|
1225 |
+
$this->init_settings();
|
1226 |
+
$this->settings['fb_api_key'] = '';
|
1227 |
+
$this->settings['fb_product_catalog_id'] = '';
|
1228 |
+
|
1229 |
+
$this->settings['fb_pixel_id'] = '';
|
1230 |
+
$this->settings['fb_pixel_use_pii'] = 'no';
|
1231 |
+
|
1232 |
+
$this->settings['fb_page_id'] = '';
|
1233 |
+
$this->settings['fb_external_merchant_settings_id'] = '';
|
1234 |
+
$this->settings['pixel_install_time'] = '';
|
1235 |
+
$this->settings['fb_feed_id'] = '';
|
1236 |
+
$this->settings['fb_upload_id'] = '';
|
1237 |
+
$this->settings['upload_end_time'] = '';
|
1238 |
+
|
1239 |
+
WC_Facebookcommerce_Pixel::set_pixel_id(0);
|
1240 |
+
|
1241 |
+
update_option(
|
1242 |
+
$this->get_option_key(),
|
1243 |
+
apply_filters(
|
1244 |
+
'woocommerce_settings_api_sanitized_fields_' . $this->id,
|
1245 |
+
$this->settings));
|
1246 |
+
|
1247 |
+
// Clean up old messages
|
1248 |
+
delete_transient('facebook_plugin_api_error');
|
1249 |
+
delete_transient('facebook_plugin_api_success');
|
1250 |
+
delete_transient('facebook_plugin_api_warning');
|
1251 |
+
delete_transient('facebook_plugin_api_info');
|
1252 |
+
delete_transient('facebook_plugin_api_sticky');
|
1253 |
+
|
1254 |
+
$this->reset_all_products();
|
1255 |
+
|
1256 |
+
WC_Facebookcommerce_Utils::log("Settings deleted");
|
1257 |
+
echo "Settings Deleted";
|
1258 |
+
|
1259 |
+
}
|
1260 |
+
|
1261 |
+
wp_die();
|
1262 |
+
}
|
1263 |
+
|
1264 |
+
/**
|
1265 |
+
* Check Feed Upload Status
|
1266 |
+
**/
|
1267 |
+
function ajax_check_feed_upload_status() {
|
1268 |
+
WC_Facebookcommerce_Utils::check_woo_ajax_permissions('check feed upload status', true);
|
1269 |
+
if ($this->settings['fb_api_key']) {
|
1270 |
+
$response = array(
|
1271 |
+
'connected' => true,
|
1272 |
+
'status' => 'in progress',
|
1273 |
+
);
|
1274 |
+
if ($this->settings['fb_upload_id']) {
|
1275 |
+
if (!isset($this->fbproductfeed)) {
|
1276 |
+
if (!class_exists('WC_Facebook_Product_Feed')) {
|
1277 |
+
include_once 'includes/fbproductfeed.php';
|
1278 |
+
}
|
1279 |
+
$this->fbproductfeed = new WC_Facebook_Product_Feed(
|
1280 |
+
$this->product_catalog_id, $this->fbgraph);
|
1281 |
+
}
|
1282 |
+
$status = $this->fbproductfeed->is_upload_complete($this->settings);
|
1283 |
+
|
1284 |
+
$response['status'] = $status;
|
1285 |
+
} else {
|
1286 |
+
$response = array(
|
1287 |
+
'connected' => true,
|
1288 |
+
'status' => 'error',
|
1289 |
+
);
|
1290 |
+
}
|
1291 |
+
if ($response['status'] == 'complete') {
|
1292 |
+
update_option(
|
1293 |
+
$this->get_option_key(),
|
1294 |
+
apply_filters(
|
1295 |
+
'woocommerce_settings_api_sanitized_fields_' . $this->id,
|
1296 |
+
$this->settings));
|
1297 |
+
}
|
1298 |
+
} else {
|
1299 |
+
$response = array(
|
1300 |
+
'connected' => false,
|
1301 |
+
);
|
1302 |
+
}
|
1303 |
+
printf(json_encode($response));
|
1304 |
+
wp_die();
|
1305 |
+
}
|
1306 |
+
|
1307 |
+
/**
|
1308 |
+
* Display custom success message (sugar)
|
1309 |
+
**/
|
1310 |
+
function display_success_message($msg) {
|
1311 |
+
$msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
|
1312 |
+
set_transient('facebook_plugin_api_success', $msg,
|
1313 |
+
self::FB_MESSAGE_DISPLAY_TIME);
|
1314 |
+
}
|
1315 |
+
|
1316 |
+
/**
|
1317 |
+
* Display custom warning message (sugar)
|
1318 |
+
**/
|
1319 |
+
function display_warning_message($msg) {
|
1320 |
+
$msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
|
1321 |
+
set_transient('facebook_plugin_api_warning', $msg,
|
1322 |
+
self::FB_MESSAGE_DISPLAY_TIME);
|
1323 |
+
}
|
1324 |
+
|
1325 |
+
/**
|
1326 |
+
* Display custom info message (sugar)
|
1327 |
+
**/
|
1328 |
+
function display_info_message($msg) {
|
1329 |
+
$msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
|
1330 |
+
set_transient('facebook_plugin_api_info', $msg,
|
1331 |
+
self::FB_MESSAGE_DISPLAY_TIME);
|
1332 |
+
}
|
1333 |
+
|
1334 |
+
/**
|
1335 |
+
* Display custom "sticky" info message.
|
1336 |
+
* Call remove_sticky_message or wait for time out.
|
1337 |
+
**/
|
1338 |
+
function display_sticky_message($msg) {
|
1339 |
+
$msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
|
1340 |
+
set_transient('facebook_plugin_api_sticky', $msg,
|
1341 |
+
self::FB_MESSAGE_DISPLAY_TIME);
|
1342 |
+
}
|
1343 |
+
|
1344 |
+
/**
|
1345 |
+
* Remove custom "sticky" info message
|
1346 |
+
**/
|
1347 |
+
function remove_sticky_message() {
|
1348 |
+
delete_transient('facebook_plugin_api_sticky');
|
1349 |
+
}
|
1350 |
+
|
1351 |
+
function remove_resync_message() {
|
1352 |
+
$msg = get_transient('facebook_plugin_api_sticky');
|
1353 |
+
if ($msg && strpos($msg, 'Sync') !== false) {
|
1354 |
+
delete_transient('facebook_plugin_resync_sticky');
|
1355 |
+
}
|
1356 |
+
}
|
1357 |
+
|
1358 |
+
/**
|
1359 |
+
* Display custom error message (sugar)
|
1360 |
+
**/
|
1361 |
+
function display_error_message($msg) {
|
1362 |
+
$msg = self::FB_ADMIN_MESSAGE_PREPEND . $msg;
|
1363 |
+
WC_Facebookcommerce_Utils::log($msg);
|
1364 |
+
set_transient('facebook_plugin_api_error', $msg,
|
1365 |
+
self::FB_MESSAGE_DISPLAY_TIME);
|
1366 |
+
}
|
1367 |
+
|
1368 |
+
/**
|
1369 |
+
* Display error message from API result (sugar)
|
1370 |
+
**/
|
1371 |
+
function display_error_message_from_result($result) {
|
1372 |
+
$msg = json_decode($result['body'])->error->message;
|
1373 |
+
$this->display_error_message($msg);
|
1374 |
+
}
|
1375 |
+
|
1376 |
+
/**
|
1377 |
+
* Deal with FB API responses, display error if FB API returns error
|
1378 |
+
*
|
1379 |
+
* @return result if response is 200, null otherwise
|
1380 |
+
**/
|
1381 |
+
function check_api_result($result, $logdata = null, $wpid = null) {
|
1382 |
+
if (is_wp_error($result)) {
|
1383 |
+
WC_Facebookcommerce_Utils::log($result->get_error_message());
|
1384 |
+
$this->display_error_message(
|
1385 |
+
"There was an issue connecting to the Facebook API: ".
|
1386 |
+
$result->get_error_message());
|
1387 |
+
return;
|
1388 |
+
}
|
1389 |
+
if ($result['response']['code'] != '200') {
|
1390 |
+
// Catch 10800 fb error code ("Duplicate retailer ID") and capture FBID
|
1391 |
+
// if possible, otherwise let user know we found dupe SKUs
|
1392 |
+
$body = WC_Facebookcommerce_Utils::decode_json($result['body']);
|
1393 |
+
if ($body && $body->error->code == '10800') {
|
1394 |
+
$error_data = $body->error->error_data; // error_data may contain FBIDs
|
1395 |
+
if ($error_data && $wpid) {
|
1396 |
+
$existing_id = $this->get_existing_fbid($error_data, $wpid);
|
1397 |
+
if ($existing_id) {
|
1398 |
+
// Add "existing_id" ID to result
|
1399 |
+
$body->id = $existing_id;
|
1400 |
+
$result['body'] = json_encode($body);
|
1401 |
+
return $result;
|
1402 |
+
}
|
1403 |
+
}
|
1404 |
+
} else {
|
1405 |
+
$this->display_error_message_from_result($result);
|
1406 |
+
}
|
1407 |
+
|
1408 |
+
WC_Facebookcommerce_Utils::log($result);
|
1409 |
+
$data = array(
|
1410 |
+
'result' => $result,
|
1411 |
+
'data' => $logdata,
|
1412 |
+
);
|
1413 |
+
WC_Facebookcommerce_Utils::fblog(
|
1414 |
+
'Non-200 error code from FB',
|
1415 |
+
$data,
|
1416 |
+
true);
|
1417 |
+
return null;
|
1418 |
+
}
|
1419 |
+
return $result;
|
1420 |
+
}
|
1421 |
+
|
1422 |
+
function ajax_woo_adv_bulk_edit_compat($import_id) {
|
1423 |
+
if (!WC_Facebookcommerce_Utils::check_woo_ajax_permissions('adv bulk edit', false)) {
|
1424 |
+
return;
|
1425 |
+
}
|
1426 |
+
$type = isset($_POST["type"]) ? $_POST["type"] : "";
|
1427 |
+
if (strpos($type, 'product') !== false && strpos($type, 'load') === false) {
|
1428 |
+
$this->display_out_of_sync_message("advanced bulk edit");
|
1429 |
+
}
|
1430 |
+
}
|
1431 |
+
|
1432 |
+
function wp_all_import_compat($import_id) {
|
1433 |
+
$import = new PMXI_Import_Record();
|
1434 |
+
$import->getById($import_id);
|
1435 |
+
if (!$import->isEmpty() && in_array($import->options['custom_type'], array('product', 'product_variation'))) {
|
1436 |
+
$this->display_out_of_sync_message("import");
|
1437 |
+
}
|
1438 |
+
}
|
1439 |
+
|
1440 |
+
function display_out_of_sync_message($action_name) {
|
1441 |
+
$this->display_sticky_message(
|
1442 |
+
sprintf(
|
1443 |
+
'Products may be out of Sync with Facebook due to your recent '.$action_name.'.'.
|
1444 |
+
' <a href="%s&fb_force_resync=true&remove_sticky=true">Re-Sync them with FB.</a>',
|
1445 |
+
WOOCOMMERCE_FACEBOOK_PLUGIN_SETTINGS_URL));
|
1446 |
+
}
|
1447 |
+
|
1448 |
+
/**
|
1449 |
+
* If we get a product group ID or product item ID back for a dupe retailer
|
1450 |
+
* id error, update existing ID.
|
1451 |
+
*
|
1452 |
+
* @return null
|
1453 |
+
**/
|
1454 |
+
function get_existing_fbid($error_data, $wpid) {
|
1455 |
+
if (isset($error_data->product_group_id)) {
|
1456 |
+
update_post_meta(
|
1457 |
+
$wpid,
|
1458 |
+
self::FB_PRODUCT_GROUP_ID,
|
1459 |
+
(string)$error_data->product_group_id);
|
1460 |
+
return $error_data->product_group_id;
|
1461 |
+
}
|
1462 |
+
else if (isset($error_data->product_item_id)) {
|
1463 |
+
update_post_meta(
|
1464 |
+
$wpid,
|
1465 |
+
self::FB_PRODUCT_ITEM_ID,
|
1466 |
+
(string)$error_data->product_item_id);
|
1467 |
+
return $error_data->product_item_id;
|
1468 |
+
} else {
|
1469 |
+
return;
|
1470 |
+
}
|
1471 |
+
}
|
1472 |
+
|
1473 |
+
/**
|
1474 |
+
* Check for api key and any other API errors
|
1475 |
+
**/
|
1476 |
+
function checks() {
|
1477 |
+
// Check required fields
|
1478 |
+
|
1479 |
+
if (!$this->api_key || !$this->product_catalog_id) {
|
1480 |
+
echo $this->get_message_html(sprintf(__('%1$sFacebook for WooCommerce
|
1481 |
+
is almost ready.%2$s To complete your configuration, %3$scomplete the
|
1482 |
+
setup steps%4$s.',
|
1483 |
+
'facebook-for-woocommerce'), '<strong>', '</strong>',
|
1484 |
+
|
1485 |
+
'<a href="' . esc_url(WOOCOMMERCE_FACEBOOK_PLUGIN_SETTINGS_URL) . '">',
|
1486 |
+
'</a>'), 'info');
|
1487 |
+
}
|
1488 |
+
|
1489 |
+
// WooCommerce 2.x upgrade nag
|
1490 |
+
if ($this->api_key && (!isset($this->background_processor))) {
|
1491 |
+
echo $this->get_message_html(sprintf(__(
|
1492 |
+
'Facebook product sync may not work correctly in WooCommerce version
|
1493 |
+
%1$s. Please upgrade to WooCommerce 3.',
|
1494 |
+
'facebook-for-woocommerce'), WC()->version), 'warning');
|
1495 |
+
}
|
1496 |
+
|
1497 |
+
$this->maybe_display_facebook_api_messages();
|
1498 |
+
}
|
1499 |
+
|
1500 |
+
function get_sample_product_feed() {
|
1501 |
+
ob_start();
|
1502 |
+
|
1503 |
+
// Get up to 12 published posts that are products
|
1504 |
+
$args = array(
|
1505 |
+
'post_type' => 'product',
|
1506 |
+
'post_status' => 'publish',
|
1507 |
+
'posts_per_page' => 12,
|
1508 |
+
'fields' => 'ids'
|
1509 |
+
);
|
1510 |
+
|
1511 |
+
$post_ids = get_posts($args);
|
1512 |
+
$items = array();
|
1513 |
+
|
1514 |
+
foreach ($post_ids as $post_id) {
|
1515 |
+
|
1516 |
+
$woo_product = new WC_Facebook_Product($post_id);
|
1517 |
+
$product_data = $woo_product->prepare_product();
|
1518 |
+
|
1519 |
+
$feed_item = array(
|
1520 |
+
'title' => strip_tags($product_data['name']),
|
1521 |
+
'availability' => $woo_product->is_in_stock() ? 'in stock' :
|
1522 |
+
'out of stock',
|
1523 |
+
'description' => strip_tags($product_data['description']),
|
1524 |
+
'id' => $product_data['retailer_id'],
|
1525 |
+
'image_link' => $product_data['image_url'],
|
1526 |
+
'brand' => strip_tags(WC_Facebookcommerce_Utils::get_store_name()),
|
1527 |
+
'link' => $product_data['url'],
|
1528 |
+
'price' => $product_data['price'] . ' ' . get_woocommerce_currency(),
|
1529 |
+
);
|
1530 |
+
|
1531 |
+
array_push($items, $feed_item);
|
1532 |
+
}
|
1533 |
+
// https://codex.wordpress.org/Function_Reference/wp_reset_postdata
|
1534 |
+
wp_reset_postdata();
|
1535 |
+
ob_end_clean();
|
1536 |
+
return json_encode(array($items));
|
1537 |
+
}
|
1538 |
+
|
1539 |
+
/**
|
1540 |
+
* Loop through array of WPIDs to remove metadata.
|
1541 |
+
**/
|
1542 |
+
function delete_post_meta_loop($products) {
|
1543 |
+
foreach ($products as $product_id) {
|
1544 |
+
delete_post_meta($product_id, self::FB_PRODUCT_GROUP_ID);
|
1545 |
+
delete_post_meta($product_id, self::FB_PRODUCT_ITEM_ID);
|
1546 |
+
delete_post_meta($product_id, self::FB_VISIBILITY);
|
1547 |
+
}
|
1548 |
+
}
|
1549 |
+
|
1550 |
+
/**
|
1551 |
+
* Remove FBIDs from all products when resetting store.
|
1552 |
+
**/
|
1553 |
+
function reset_all_products() {
|
1554 |
+
if (!is_admin()) {
|
1555 |
+
WC_Facebookcommerce_Utils::log("Not resetting any FBIDs from products,
|
1556 |
+
must call reset from admin context.");
|
1557 |
+
return false;
|
1558 |
+
}
|
1559 |
+
|
1560 |
+
$test_instance = WC_Facebook_Integration_Test::get_instance($this);
|
1561 |
+
$this->test_mode = $test_instance::$test_mode;
|
1562 |
+
|
1563 |
+
// Include draft products (omit 'post_status' => 'publish')
|
1564 |
+
WC_Facebookcommerce_Utils::log("Removing FBIDs from all products");
|
1565 |
+
|
1566 |
+
$post_ids = get_posts(array(
|
1567 |
+
'post_type' => 'product',
|
1568 |
+
'posts_per_page' => -1,
|
1569 |
+
'fields' => 'ids'
|
1570 |
+
));
|
1571 |
+
|
1572 |
+
$children = array();
|
1573 |
+
foreach ($post_ids as $post_id) {
|
1574 |
+
$children = array_merge(get_posts(array(
|
1575 |
+
'post_type' => 'product_variation',
|
1576 |
+
'posts_per_page' => -1,
|
1577 |
+
'post_parent' => $post_id,
|
1578 |
+
'fields' => 'ids'
|
1579 |
+
)), $children);
|
1580 |
+
}
|
1581 |
+
$post_ids = array_merge($post_ids, $children);
|
1582 |
+
$this->delete_post_meta_loop($post_ids);
|
1583 |
+
|
1584 |
+
WC_Facebookcommerce_Utils::log("Product FBIDs deleted");
|
1585 |
+
return true;
|
1586 |
+
}
|
1587 |
+
|
1588 |
+
/**
|
1589 |
+
* Remove FBIDs from a single WC product
|
1590 |
+
**/
|
1591 |
+
function reset_single_product($wp_id) {
|
1592 |
+
$woo_product = new WC_Facebook_Product($wp_id);
|
1593 |
+
$products = array($woo_product->get_id());
|
1594 |
+
if (WC_Facebookcommerce_Utils::is_variable_type($woo_product->get_type())) {
|
1595 |
+
$products = array_merge($products, $woo_product->get_children());
|
1596 |
+
}
|
1597 |
+
|
1598 |
+
$this->delete_post_meta_loop($products);
|
1599 |
+
|
1600 |
+
WC_Facebookcommerce_Utils::log("Deleted FB Metadata for product " . $wp_id);
|
1601 |
+
}
|
1602 |
+
|
1603 |
+
function ajax_reset_all_fb_products() {
|
1604 |
+
WC_Facebookcommerce_Utils::check_woo_ajax_permissions('reset products', true);
|
1605 |
+
$this->reset_all_products();
|
1606 |
+
wp_reset_postdata();
|
1607 |
+
wp_die();
|
1608 |
+
}
|
1609 |
+
|
1610 |
+
function ajax_reset_single_fb_product() {
|
1611 |
+
WC_Facebookcommerce_Utils::check_woo_ajax_permissions('reset single product', true);
|
1612 |
+
if (!isset($_POST['wp_id'])) {
|
1613 |
+
wp_die();
|
1614 |
+
}
|
1615 |
+
|
1616 |
+
$wp_id = sanitize_text_field($_POST['wp_id']);
|
1617 |
+
$woo_product = new WC_Facebook_Product($wp_id);
|
1618 |
+
if ($woo_product) {
|
1619 |
+
$this->reset_single_product($wp_id);
|
1620 |
+
}
|
1621 |
+
|
1622 |
+
wp_reset_postdata();
|
1623 |
+
wp_die();
|
1624 |
+
}
|
1625 |
+
|
1626 |
+
function ajax_delete_fb_product() {
|
1627 |
+
WC_Facebookcommerce_Utils::check_woo_ajax_permissions('delete single product', true);
|
1628 |
+
if (!isset($_POST['wp_id'])) {
|
1629 |
+
wp_die();
|
1630 |
+
}
|
1631 |
+
|
1632 |
+
$wp_id = sanitize_text_field($_POST['wp_id']);
|
1633 |
+
$this->on_product_delete($wp_id);
|
1634 |
+
$this->reset_single_product($wp_id);
|
1635 |
+
wp_reset_postdata();
|
1636 |
+
wp_die();
|
1637 |
+
}
|
1638 |
+
|
1639 |
+
/**
|
1640 |
+
* Special function to run all visible products through on_product_publish
|
1641 |
+
**/
|
1642 |
+
function ajax_sync_all_fb_products() {
|
1643 |
+
WC_Facebookcommerce_Utils::check_woo_ajax_permissions('syncall products', true);
|
1644 |
+
if (get_option('fb_disable_sync_on_dev_environment', false)) {
|
1645 |
+
WC_Facebookcommerce_Utils::log(
|
1646 |
+
'Sync to FB Page is not allowed in Dev Environment');
|
1647 |
+
wp_die();
|
1648 |
+
return;
|
1649 |
+
}
|
1650 |
+
|
1651 |
+
if (!$this->api_key || !$this->product_catalog_id) {
|
1652 |
+
WC_Facebookcommerce_Utils::log("No API key or catalog ID: " .
|
1653 |
+
$this->api_key . ' and ' . $this->product_catalog_id);
|
1654 |
+
wp_die();
|
1655 |
+
return;
|
1656 |
+
}
|
1657 |
+
$this->remove_resync_message();
|
1658 |
+
|
1659 |
+
$currently_syncing = get_transient(self::FB_SYNC_IN_PROGRESS);
|
1660 |
+
|
1661 |
+
if (isset($this->background_processor)) {
|
1662 |
+
if ($this->background_processor->is_updating()) {
|
1663 |
+
$this->background_processor->handle_cron_healthcheck();
|
1664 |
+
$currently_syncing = 1;
|
1665 |
+
}
|
1666 |
+
}
|
1667 |
+
|
1668 |
+
if ($currently_syncing) {
|
1669 |
+
WC_Facebookcommerce_Utils::log('Not syncing, sync in progress');
|
1670 |
+
WC_Facebookcommerce_Utils::fblog(
|
1671 |
+
'Tried to sync during an in-progress sync!', array(), true);
|
1672 |
+
$this->display_warning_message('A product sync is in progress.
|
1673 |
+
Please wait until the sync finishes before starting a new one.');
|
1674 |
+
wp_die();
|
1675 |
+
return;
|
1676 |
+
}
|
1677 |
+
|
1678 |
+
$is_valid_product_catalog =
|
1679 |
+
$this->fbgraph->validate_product_catalog($this->product_catalog_id);
|
1680 |
+
|
1681 |
+
if (!$is_valid_product_catalog) {
|
1682 |
+
WC_Facebookcommerce_Utils::log('Not syncing, invalid product catalog!');
|
1683 |
+
WC_Facebookcommerce_Utils::fblog(
|
1684 |
+
'Tried to sync with an invalid product catalog!', array(), true);
|
1685 |
+
$this->display_warning_message('We\'ve detected that your
|
1686 |
+
Facebook Product Catalog is no longer valid. This may happen if it was
|
1687 |
+
deleted, or this may be a transient error.
|
1688 |
+
If this error persists please delete your settings via
|
1689 |
+
"Re-configure Facebook Settings > Advanced Settings > Delete Settings"
|
1690 |
+
and try setup again');
|
1691 |
+
wp_die();
|
1692 |
+
return;
|
1693 |
+
}
|
1694 |
+
|
1695 |
+
// Cache the cart URL to display a warning in case it changes later
|
1696 |
+
$cart_url = get_option(self::FB_CART_URL);
|
1697 |
+
if ($cart_url != wc_get_cart_url()) {
|
1698 |
+
update_option(self::FB_CART_URL, wc_get_cart_url());
|
1699 |
+
}
|
1700 |
+
|
1701 |
+
$sanitized_settings = $this->settings;
|
1702 |
+
unset($sanitized_settings['fb_api_key']);
|
1703 |
+
|
1704 |
+
// Get all published posts. First unsynced then already-synced.
|
1705 |
+
$post_ids_new = WC_Facebookcommerce_Utils::get_wp_posts(
|
1706 |
+
self::FB_PRODUCT_GROUP_ID, 'NOT EXISTS');
|
1707 |
+
$post_ids_old = WC_Facebookcommerce_Utils::get_wp_posts(
|
1708 |
+
self::FB_PRODUCT_GROUP_ID, 'EXISTS');
|
1709 |
+
|
1710 |
+
$total_new = count($post_ids_new);
|
1711 |
+
$total_old = count($post_ids_old);
|
1712 |
+
$post_ids = array_merge($post_ids_new, $post_ids_old);
|
1713 |
+
$total = count($post_ids);
|
1714 |
+
|
1715 |
+
WC_Facebookcommerce_Utils::fblog(
|
1716 |
+
'Attempting to sync ' . $total . ' ( ' .
|
1717 |
+
$total_new . ' new) products with settings: ',
|
1718 |
+
$sanitized_settings,
|
1719 |
+
false);
|
1720 |
+
|
1721 |
+
// Check for background processing (Woo 3.x.x)
|
1722 |
+
if (isset($this->background_processor)) {
|
1723 |
+
$starting_message = sprintf(
|
1724 |
+
'Starting background sync to Facebook: %d products...',
|
1725 |
+
$total);
|
1726 |
+
|
1727 |
+
set_transient(
|
1728 |
+
self::FB_SYNC_IN_PROGRESS,
|
1729 |
+
true,
|
1730 |
+
self::FB_SYNC_TIMEOUT);
|
1731 |
+
|
1732 |
+
set_transient(
|
1733 |
+
self::FB_SYNC_REMAINING,
|
1734 |
+
(int)$total);
|
1735 |
+
|
1736 |
+
$this->display_info_message($starting_message);
|
1737 |
+
WC_Facebookcommerce_Utils::log($starting_message);
|
1738 |
+
|
1739 |
+
foreach ($post_ids as $post_id) {
|
1740 |
+
WC_Facebookcommerce_Utils::log("Pushing post to queue: " . $post_id);
|
1741 |
+
$this->background_processor->push_to_queue($post_id);
|
1742 |
+
}
|
1743 |
+
|
1744 |
+
$this->background_processor->save()->dispatch();
|
1745 |
+
// reset FB_SYNC_REMAINING to avoid race condition
|
1746 |
+
set_transient(
|
1747 |
+
self::FB_SYNC_REMAINING,
|
1748 |
+
(int)$total);
|
1749 |
+
// handle_cron_healthcheck must be called
|
1750 |
+
// https://github.com/A5hleyRich/wp-background-processing/issues/34
|
1751 |
+
$this->background_processor->handle_cron_healthcheck();
|
1752 |
+
} else {
|
1753 |
+
// Oldschool sync for WooCommerce 2.x
|
1754 |
+
$count = ($total_old === $total) ? 0 : $total_old;
|
1755 |
+
foreach ($post_ids as $post_id) {
|
1756 |
+
// Repeatedly overwrite sync total while in actual sync loop
|
1757 |
+
set_transient(
|
1758 |
+
self::FB_SYNC_IN_PROGRESS,
|
1759 |
+
true,
|
1760 |
+
self::FB_SYNC_TIMEOUT);
|
1761 |
+
|
1762 |
+
$this->display_sticky_message(
|
1763 |
+
sprintf(
|
1764 |
+
'Syncing products to Facebook: %d out of %d...',
|
1765 |
+
// Display different # when resuming to avoid confusion.
|
1766 |
+
min($count, $total),
|
1767 |
+
$total),
|
1768 |
+
true);
|
1769 |
+
|
1770 |
+
$this->on_product_publish($post_id);
|
1771 |
+
$count++;
|
1772 |
+
}
|
1773 |
+
WC_Facebookcommerce_Utils::log('Synced ' . $count . ' products');
|
1774 |
+
$this->remove_sticky_message();
|
1775 |
+
$this->display_info_message('Facebook product sync complete!');
|
1776 |
+
delete_transient(self::FB_SYNC_IN_PROGRESS);
|
1777 |
+
WC_Facebookcommerce_Utils::fblog(
|
1778 |
+
'Product sync complete. Total products synced: ' . $count);
|
1779 |
+
}
|
1780 |
+
|
1781 |
+
// https://codex.wordpress.org/Function_Reference/wp_reset_postdata
|
1782 |
+
wp_reset_postdata();
|
1783 |
+
|
1784 |
+
// This is important, for some reason.
|
1785 |
+
// See https://codex.wordpress.org/AJAX_in_Plugins
|
1786 |
+
wp_die();
|
1787 |
+
}
|
1788 |
+
|
1789 |
+
/**
|
1790 |
+
* Special function to run all visible products by uploading feed.
|
1791 |
+
**/
|
1792 |
+
function ajax_sync_all_fb_products_using_feed() {
|
1793 |
+
WC_Facebookcommerce_Utils::check_woo_ajax_permissions(
|
1794 |
+
'syncall products using feed', !$this->test_mode);
|
1795 |
+
return $this->sync_all_fb_products_using_feed();
|
1796 |
+
}
|
1797 |
+
|
1798 |
+
// Separate entry point that bypasses permission check for use in cron.
|
1799 |
+
function sync_all_fb_products_using_feed() {
|
1800 |
+
if (get_option('fb_disable_sync_on_dev_environment', false)) {
|
1801 |
+
WC_Facebookcommerce_Utils::log(
|
1802 |
+
'Sync to FB Page is not allowed in Dev Environment');
|
1803 |
+
$this->fb_wp_die();
|
1804 |
+
return false;
|
1805 |
+
}
|
1806 |
+
|
1807 |
+
if (!$this->api_key || !$this->product_catalog_id) {
|
1808 |
+
self::log("No API key or catalog ID: " . $this->api_key .
|
1809 |
+
' and ' . $this->product_catalog_id);
|
1810 |
+
$this->fb_wp_die();
|
1811 |
+
return false;
|
1812 |
+
}
|
1813 |
+
$this->remove_resync_message();
|
1814 |
+
$is_valid_product_catalog =
|
1815 |
+
$this->fbgraph->validate_product_catalog($this->product_catalog_id);
|
1816 |
+
|
1817 |
+
if (!$is_valid_product_catalog) {
|
1818 |
+
WC_Facebookcommerce_Utils::log('Not syncing, invalid product catalog!');
|
1819 |
+
WC_Facebookcommerce_Utils::fblog(
|
1820 |
+
'Tried to sync with an invalid product catalog!', array(), true);
|
1821 |
+
$this->display_warning_message('We\'ve detected that your
|
1822 |
+
Facebook Product Catalog is no longer valid. This may happen if it was
|
1823 |
+
deleted, or this may be a transient error.
|
1824 |
+
If this error persists please delete your settings via
|
1825 |
+
"Re-configure Facebook Settings > Advanced Settings > Delete Settings"
|
1826 |
+
and try setup again');
|
1827 |
+
$this->fb_wp_die();
|
1828 |
+
return false;
|
1829 |
+
}
|
1830 |
+
|
1831 |
+
// Cache the cart URL to display a warning in case it changes later
|
1832 |
+
$cart_url = get_option(self::FB_CART_URL);
|
1833 |
+
if ($cart_url != wc_get_cart_url()) {
|
1834 |
+
update_option(self::FB_CART_URL, wc_get_cart_url());
|
1835 |
+
}
|
1836 |
+
|
1837 |
+
if (!class_exists('WC_Facebook_Product_Feed')) {
|
1838 |
+
include_once 'includes/fbproductfeed.php';
|
1839 |
+
}
|
1840 |
+
if ($this->test_mode) {
|
1841 |
+
$this->fbproductfeed = new WC_Facebook_Product_Feed_Test_Mock(
|
1842 |
+
$this->product_catalog_id, $this->fbgraph, $this->feed_id);
|
1843 |
+
} else {
|
1844 |
+
$this->fbproductfeed = new WC_Facebook_Product_Feed(
|
1845 |
+
$this->product_catalog_id, $this->fbgraph, $this->feed_id);
|
1846 |
+
}
|
1847 |
+
|
1848 |
+
$upload_success = $this->fbproductfeed->sync_all_products_using_feed();
|
1849 |
+
if ($upload_success) {
|
1850 |
+
$this->settings['fb_feed_id'] = $this->fbproductfeed->feed_id;
|
1851 |
+
$this->settings['fb_upload_id'] = $this->fbproductfeed->upload_id;
|
1852 |
+
update_option($this->get_option_key(),
|
1853 |
+
apply_filters('woocommerce_settings_api_sanitized_fields_' .
|
1854 |
+
$this->id, $this->settings));
|
1855 |
+
wp_reset_postdata();
|
1856 |
+
$this->fb_wp_die();
|
1857 |
+
return true;
|
1858 |
+
} else if (!$this->test_mode) {
|
1859 |
+
// curl failed, roll back to original sync approach.
|
1860 |
+
WC_Facebookcommerce_Utils::fblog(
|
1861 |
+
'Sync all products using feed, curl failed', array(), true);
|
1862 |
+
$this->sync_all_products();
|
1863 |
+
}
|
1864 |
+
return false;
|
1865 |
+
}
|
1866 |
+
|
1867 |
+
/**
|
1868 |
+
* Toggles product visibility via AJAX (checks current viz and flips it)
|
1869 |
+
**/
|
1870 |
+
function ajax_toggle_visibility() {
|
1871 |
+
WC_Facebookcommerce_Utils::check_woo_ajax_permissions('toggle visibility', true);
|
1872 |
+
if (!isset($_POST['wp_id']) || ! isset($_POST['published'])) {
|
1873 |
+
wp_die();
|
1874 |
+
}
|
1875 |
+
|
1876 |
+
$wp_id = sanitize_text_field($_POST['wp_id']);
|
1877 |
+
$published = ($_POST['published']) === 'true' ? true : false;
|
1878 |
+
|
1879 |
+
$woo_product = new WC_Facebook_Product($wp_id);
|
1880 |
+
$products = WC_Facebookcommerce_Utils::get_product_array($woo_product);
|
1881 |
+
|
1882 |
+
// Loop through product items and flip visibility
|
1883 |
+
foreach ($products as $item_id) {
|
1884 |
+
$fb_product_item_id = $this->get_product_fbid(
|
1885 |
+
self::FB_PRODUCT_ITEM_ID,
|
1886 |
+
$item_id);
|
1887 |
+
$data = array(
|
1888 |
+
'visibility' => $published ? 'published' : 'staging'
|
1889 |
+
);
|
1890 |
+
|
1891 |
+
$result = $this->check_api_result(
|
1892 |
+
$this->fbgraph->update_product_item(
|
1893 |
+
$fb_product_item_id,
|
1894 |
+
$data));
|
1895 |
+
|
1896 |
+
if ($result) {
|
1897 |
+
update_post_meta($item_id, self::FB_VISIBILITY, $published);
|
1898 |
+
update_post_meta($wp_id, self::FB_VISIBILITY, $published);
|
1899 |
+
}
|
1900 |
+
}
|
1901 |
+
wp_die();
|
1902 |
+
}
|
1903 |
+
|
1904 |
+
/**
|
1905 |
+
* Initialize Settings Form Fields
|
1906 |
+
*
|
1907 |
+
* @access public
|
1908 |
+
* @return void
|
1909 |
+
*/
|
1910 |
+
function init_form_fields() {
|
1911 |
+
$this->form_fields = array(
|
1912 |
+
'fb_settings_heading' => array(
|
1913 |
+
'title' => __('Debug Mode', 'facebook-for-woocommerce'),
|
1914 |
+
'type' => 'title',
|
1915 |
+
'description' => '',
|
1916 |
+
'default' => ''
|
1917 |
+
),
|
1918 |
+
'fb_page_id' => array(
|
1919 |
+
'title' => __('Facebook Page ID', 'facebook-for-woocommerce'),
|
1920 |
+
'type' => 'text',
|
1921 |
+
'description' => __('The unique identifier for your Facebook page.',
|
1922 |
+
'facebook-for-woocommerce'),
|
1923 |
+
'default' => '',
|
1924 |
+
),
|
1925 |
+
'fb_product_catalog_id' => array(
|
1926 |
+
'title' => __('Product Catalog ID', 'facebook-for-woocommerce'),
|
1927 |
+
'type' => 'text',
|
1928 |
+
'description' => __('The unique identifier for your product catalog,
|
1929 |
+
on Facebook.', 'facebook-for-woocommerce'),
|
1930 |
+
'default' => ''
|
1931 |
+
),
|
1932 |
+
'fb_pixel_id' => array(
|
1933 |
+
'title' => __('Pixel ID', 'facebook-for-woocommerce'),
|
1934 |
+
'type' => 'text',
|
1935 |
+
'description' => __('The unique identifier for your Facebook pixel',
|
1936 |
+
'facebook-for-woocommerce'),
|
1937 |
+
'default' => ''
|
1938 |
+
),
|
1939 |
+
'fb_pixel_use_pii' => array(
|
1940 |
+
'title' => __('Use Advanced Matching on pixel?',
|
1941 |
+
'facebook-for-woocommerce'),
|
1942 |
+
'type' => 'checkbox',
|
1943 |
+
'description' => __('Enabling Advanced Matching
|
1944 |
+
improves audience building.', 'facebook-for-woocommerce'),
|
1945 |
+
'default' => 'yes'
|
1946 |
+
),
|
1947 |
+
'fb_external_merchant_settings_id' => array(
|
1948 |
+
'title' => __('External Merchant Settings ID',
|
1949 |
+
'facebook-for-woocommerce'),
|
1950 |
+
'type' => 'text',
|
1951 |
+
'description' => __('The unique identifier for your external merchant
|
1952 |
+
settings, on Facebook.', 'facebook-for-woocommerce'),
|
1953 |
+
'default' => ''
|
1954 |
+
),
|
1955 |
+
'fb_api_key' => array(
|
1956 |
+
'title' => __('API Key', 'facebook-for-woocommerce'),
|
1957 |
+
'type' => 'text',
|
1958 |
+
'description' => sprintf(__('A non-expiring Page Token with
|
1959 |
+
%1$smanage_pages%2$s permissions.', 'facebook-for-woocommerce'),
|
1960 |
+
'<code>', '</code>'),
|
1961 |
+
'default' => ''
|
1962 |
+
),
|
1963 |
+
);
|
1964 |
+
|
1965 |
+
if (!class_exists('WC_Facebookcommerce_EventsTracker')) {
|
1966 |
+
include_once 'includes/fbutils.php';
|
1967 |
+
}
|
1968 |
+
} // End init_form_fields()
|
1969 |
+
|
1970 |
+
|
1971 |
+
/**
|
1972 |
+
* Get message
|
1973 |
+
* @return string Error
|
1974 |
+
*/
|
1975 |
+
private function get_message_html($message, $type = 'error') {
|
1976 |
+
ob_start();
|
1977 |
+
|
1978 |
+
?>
|
1979 |
+
<div class="notice is-dismissible notice-<?php echo $type ?>">
|
1980 |
+
<p><?php echo $message ?></p>
|
1981 |
+
</div>
|
1982 |
+
<?php
|
1983 |
+
return ob_get_clean();
|
1984 |
+
}
|
1985 |
+
|
1986 |
+
/**
|
1987 |
+
* Display relevant messages to user from transients, clear once displayed
|
1988 |
+
*
|
1989 |
+
* @param void
|
1990 |
+
*/
|
1991 |
+
public function maybe_display_facebook_api_messages() {
|
1992 |
+
$error_msg = get_transient('facebook_plugin_api_error');
|
1993 |
+
|
1994 |
+
if ($error_msg) {
|
1995 |
+
echo $this->get_message_html(sprintf(__('Facebook extension error: %s ',
|
1996 |
+
'facebook-for-woocommerce'), $error_msg));
|
1997 |
+
delete_transient('facebook_plugin_api_error');
|
1998 |
+
|
1999 |
+
WC_Facebookcommerce_Utils::fblog(
|
2000 |
+
$error_msg,
|
2001 |
+
array(),
|
2002 |
+
true);
|
2003 |
+
}
|
2004 |
+
|
2005 |
+
$warning_msg = get_transient('facebook_plugin_api_warning');
|
2006 |
+
|
2007 |
+
if ($warning_msg) {
|
2008 |
+
echo $this->get_message_html(__($warning_msg, 'facebook-for-woocommerce'),
|
2009 |
+
'warning');
|
2010 |
+
delete_transient('facebook_plugin_api_warning');
|
2011 |
+
}
|
2012 |
+
|
2013 |
+
$success_msg = get_transient('facebook_plugin_api_success');
|
2014 |
+
|
2015 |
+
if ($success_msg) {
|
2016 |
+
echo $this->get_message_html(__($success_msg, 'facebook-for-woocommerce'),
|
2017 |
+
'success');
|
2018 |
+
delete_transient('facebook_plugin_api_success');
|
2019 |
+
}
|
2020 |
+
|
2021 |
+
$info_msg = get_transient('facebook_plugin_api_info');
|
2022 |
+
|
2023 |
+
if ($info_msg) {
|
2024 |
+
echo $this->get_message_html(__($info_msg, 'facebook-for-woocommerce'),
|
2025 |
+
'info');
|
2026 |
+
delete_transient('facebook_plugin_api_info');
|
2027 |
+
}
|
2028 |
+
|
2029 |
+
$sticky_msg = get_transient('facebook_plugin_api_sticky');
|
2030 |
+
|
2031 |
+
if ($sticky_msg) {
|
2032 |
+
echo $this->get_message_html(__($sticky_msg, 'facebook-for-woocommerce'),
|
2033 |
+
'info');
|
2034 |
+
// Transient must be deleted elsewhere, or wait for timeout
|
2035 |
+
}
|
2036 |
+
|
2037 |
+
}
|
2038 |
+
|
2039 |
+
function get_page_name() {
|
2040 |
+
$page_name = '';
|
2041 |
+
if (!empty($this->settings['fb_page_id']) &&
|
2042 |
+
!empty($this->settings['fb_api_key']) ) {
|
2043 |
+
|
2044 |
+
$page_name = $this->fbgraph->get_page_name($this->settings['fb_page_id'],
|
2045 |
+
$this->settings['fb_api_key']);
|
2046 |
+
}
|
2047 |
+
return $page_name;
|
2048 |
+
}
|
2049 |
+
|
2050 |
+
function get_nux_message_ifexist() {
|
2051 |
+
$nux_type_to_elemid_map = array(
|
2052 |
+
'messenger_chat' => 'connect_button',
|
2053 |
+
'instagram_shopping' => 'connect_button'
|
2054 |
+
);
|
2055 |
+
$nux_type_to_message_map = array(
|
2056 |
+
'messenger_chat' => __('Get started with Messenger Customer Chat'),
|
2057 |
+
'instagram_shopping' => __('Get started with Instagram Shopping')
|
2058 |
+
);
|
2059 |
+
if (isset($_GET['nux'])) {
|
2060 |
+
return sprintf('<div class="nux-message" style="display: none;" data-target="%s">
|
2061 |
+
<div class="nux-message-text">%s</div>
|
2062 |
+
<div class="nux-message-arrow"></div>
|
2063 |
+
<i class="nux-message-close-btn">x</i>
|
2064 |
+
</div>
|
2065 |
+
<script>(function() { fbe_init_nux_messages(); })();</script>',
|
2066 |
+
$nux_type_to_elemid_map[sanitize_text_field($_GET['nux'])],
|
2067 |
+
$nux_type_to_message_map[sanitize_text_field($_GET['nux'])]);
|
2068 |
+
} else {
|
2069 |
+
return '';
|
2070 |
+
}
|
2071 |
+
}
|
2072 |
+
|
2073 |
+
/**
|
2074 |
+
* Admin Panel Options
|
2075 |
+
*/
|
2076 |
+
function admin_options() {
|
2077 |
+
$domain = 'facebook-for-woocommerce';
|
2078 |
+
$cta_button_text = __('Get Started', $domain);
|
2079 |
+
$page_name = $this->get_page_name();
|
2080 |
+
|
2081 |
+
$can_manage = current_user_can('manage_woocommerce');
|
2082 |
+
$pre_setup = empty($this->settings['fb_page_id']) ||
|
2083 |
+
empty($this->settings['fb_api_key']);
|
2084 |
+
$apikey_invalid = !$pre_setup && $this->settings['fb_api_key'] && !$page_name;
|
2085 |
+
|
2086 |
+
$redirect_uri = '';
|
2087 |
+
$remove_http_active = is_plugin_active('remove-http/remove-http.php');
|
2088 |
+
$https_will_be_stripped = $remove_http_active &&
|
2089 |
+
!get_option('factmaven_rhttp')['external'];
|
2090 |
+
if ($https_will_be_stripped) {
|
2091 |
+
$this->display_sticky_message(__('You\'re using Remove HTTP which has
|
2092 |
+
incompatibilities with our extension. Please disable it, or select the
|
2093 |
+
"Ignore external links" option on the Remove HTTP settings page.'));
|
2094 |
+
}
|
2095 |
+
|
2096 |
+
if (!$pre_setup) {
|
2097 |
+
$cta_button_text = __('Create Ad', $domain);
|
2098 |
+
$redirect_uri = 'https://www.facebook.com/ads/dia/redirect/?settings_id='
|
2099 |
+
. $this->external_merchant_settings_id . '&version=2' .
|
2100 |
+
'&entry_point=admin_panel';
|
2101 |
+
}
|
2102 |
+
$currently_syncing = get_transient(self::FB_SYNC_IN_PROGRESS);
|
2103 |
+
$connected = ($page_name != '');
|
2104 |
+
$hide_test = ($connected && $currently_syncing) || !defined('WP_DEBUG') ||
|
2105 |
+
WP_DEBUG !== true;
|
2106 |
+
$nux_message = $this->get_nux_message_ifexist();
|
2107 |
+
?>
|
2108 |
+
<h2><?php _e('Facebook', $domain); ?></h2>
|
2109 |
+
<p><?php _e('Control how WooCommerce integrates with your Facebook store.',
|
2110 |
+
$domain);?>
|
2111 |
+
</p>
|
2112 |
+
<hr/>
|
2113 |
+
|
2114 |
+
<div id="fbsetup">
|
2115 |
+
<div class="wrapper">
|
2116 |
+
<header>
|
2117 |
+
<div class="help-center">
|
2118 |
+
<a href="https://www.facebook.com/business/help/900699293402826" target="_blank">Help Center <i class="help-center-icon"></i></a>
|
2119 |
+
</div>
|
2120 |
+
</header>
|
2121 |
+
<div class="content">
|
2122 |
+
<h1 id="setup_h1">
|
2123 |
+
<?php
|
2124 |
+
$pre_setup
|
2125 |
+
? _e('Grow your business on Facebook', $domain)
|
2126 |
+
: _e('Reach The Right People and Sell More Online', $domain);
|
2127 |
+
?>
|
2128 |
+
</h1>
|
2129 |
+
<h2>
|
2130 |
+
<?php _e('Use this WooCommerce and Facebook integration to:',
|
2131 |
+
$domain); ?>
|
2132 |
+
</h2>
|
2133 |
+
<ul>
|
2134 |
+
<li id="setup_l1">
|
2135 |
+
<?php
|
2136 |
+
$pre_setup
|
2137 |
+
? _e('Easily install a tracking pixel', $domain)
|
2138 |
+
: _e('Create an ad in a few steps', $domain);
|
2139 |
+
?>
|
2140 |
+
</li>
|
2141 |
+
<li id="setup_l2">
|
2142 |
+
<?php
|
2143 |
+
$pre_setup
|
2144 |
+
? _e('Upload your products and create a shop', $domain)
|
2145 |
+
: _e('Use built-in best practices for online sales', $domain);
|
2146 |
+
?>
|
2147 |
+
</li>
|
2148 |
+
<li id="setup_l3">
|
2149 |
+
<?php
|
2150 |
+
$pre_setup
|
2151 |
+
? _e('Create dynamic ads with your products and pixel', $domain)
|
2152 |
+
: _e('Get reporting on sales and revenue', $domain);
|
2153 |
+
?>
|
2154 |
+
</li>
|
2155 |
+
</ul>
|
2156 |
+
<span
|
2157 |
+
<?php
|
2158 |
+
if ($pre_setup) {
|
2159 |
+
if (!$can_manage) {
|
2160 |
+
echo ' style="pointer-events: none;"';
|
2161 |
+
}
|
2162 |
+
echo '><a href="#" class="btn pre-setup" onclick="facebookConfig()"
|
2163 |
+
id="cta_button">' . esc_html($cta_button_text) . '</a></span>';
|
2164 |
+
} else {
|
2165 |
+
if (!$can_manage || $apikey_invalid ||
|
2166 |
+
!isset($this->external_merchant_settings_id)) {
|
2167 |
+
echo ' style="pointer-events: none;"';
|
2168 |
+
}
|
2169 |
+
echo (
|
2170 |
+
'><a href='.$redirect_uri.' class="btn" id="cta_button">' .
|
2171 |
+
esc_html($cta_button_text) . '</a>' .
|
2172 |
+
'<a href="https://www.facebook.com/business/m/drive-more-online-sales"
|
2173 |
+
class="btn grey" id="learnmore_button">' . __("Learn More") .
|
2174 |
+
'</a></span>'
|
2175 |
+
);
|
2176 |
+
}
|
2177 |
+
?>
|
2178 |
+
<hr />
|
2179 |
+
<div class="settings-container">
|
2180 |
+
<div id="plugins" class="settings-section"
|
2181 |
+
<?php echo ($pre_setup && $can_manage) ? ' style="display:none;"' : ''; ?>
|
2182 |
+
>
|
2183 |
+
<h1><?php echo __('Add Ways for People to Shop'); ?></h1>
|
2184 |
+
<h2><?php echo __('Connect your business with features such as Messenger and more.'); ?></h2>
|
2185 |
+
<a href="#" class="btn small" onclick="facebookConfig()" id="connect_button">
|
2186 |
+
<?php echo __('Add Features'); ?>
|
2187 |
+
</a>
|
2188 |
+
</div>
|
2189 |
+
<div id="settings" class="settings-section"
|
2190 |
+
<?php
|
2191 |
+
if ($pre_setup && $can_manage) {
|
2192 |
+
echo ' style="display:none;"';
|
2193 |
+
}
|
2194 |
+
echo '><h1>' . esc_html__('Settings', $domain) . '</h1>';
|
2195 |
+
if ($apikey_invalid) {
|
2196 |
+
// API key is set, but no page name.
|
2197 |
+
echo '<h2 id="token_text" style="color:red;">' .
|
2198 |
+
__('Your API key is no longer valid. Please click "Settings >
|
2199 |
+
Advanced Options > Update Token".', $domain) . '</h2>
|
2200 |
+
|
2201 |
+
<span><a href="#" class="btn small" onclick="facebookConfig()"
|
2202 |
+
id="setting_button">' . __('Settings', $domain) . '</a>
|
2203 |
+
</span>';
|
2204 |
+
} else {
|
2205 |
+
if (!$can_manage) {
|
2206 |
+
echo '<h2 style="color:red;">' . __('You must have
|
2207 |
+
"manage_woocommerce" permissions to use this plugin.', $domain) .
|
2208 |
+
'</h2>';
|
2209 |
+
} else {
|
2210 |
+
echo '<h2><span id="connection_status"';
|
2211 |
+
if (!$connected) {
|
2212 |
+
echo ' style="display: none;"';
|
2213 |
+
}
|
2214 |
+
echo '>';
|
2215 |
+
echo __('Your WooCommerce store is connected to ', $domain) .
|
2216 |
+
(($page_name != '')
|
2217 |
+
? sprintf(
|
2218 |
+
__('the Facebook page <a target="_blank" href="https://www.facebook.com/%1$s">%2$s</a></span>', $domain),
|
2219 |
+
$this->settings['fb_page_id'],
|
2220 |
+
esc_html($page_name))
|
2221 |
+
: sprintf(
|
2222 |
+
__('<a target="_blank" href="https://www.facebook.com/%1$s">your Facebook page</a></span>', $domain),
|
2223 |
+
$this->settings['fb_page_id'])
|
2224 |
+
) .
|
2225 |
+
'.<span id="sync_complete" style="margin-left: 5px;';
|
2226 |
+
if (!$connected || $currently_syncing) {
|
2227 |
+
echo ' display: none;';
|
2228 |
+
}
|
2229 |
+
echo '">' . __('Status', $domain) . ': '
|
2230 |
+
. __('Products are synced to Facebook.', $domain) . '</span>'.
|
2231 |
+
sprintf(__('<span><a href="#" onclick="show_debug_info()"
|
2232 |
+
id="debug_info" style="display:none;" > More Info </a></span>',
|
2233 |
+
$domain)) . '</span></h2>
|
2234 |
+
<span><a href="#" class="btn small" onclick="facebookConfig()"
|
2235 |
+
id="setting_button"';
|
2236 |
+
|
2237 |
+
if ($currently_syncing) {
|
2238 |
+
echo ' style="pointer-events: none;" ';
|
2239 |
+
}
|
2240 |
+
echo '>' . __('Manage Settings', $domain) . '</a></span>
|
2241 |
+
|
2242 |
+
<span><a href="#" class="btn small" onclick="sync_confirm()"
|
2243 |
+
id="resync_products"';
|
2244 |
+
|
2245 |
+
if ($connected && $currently_syncing) {
|
2246 |
+
echo ' style="pointer-events: none;" ';
|
2247 |
+
}
|
2248 |
+
echo '>' . __('Sync Products', $domain) . '</a></span>
|
2249 |
+
|
2250 |
+
<p id="sync_progress">';
|
2251 |
+
if ($connected && $currently_syncing) {
|
2252 |
+
echo '<hr/>';
|
2253 |
+
echo __('Syncing... Keep this browser open', $domain);
|
2254 |
+
echo '<br/>';
|
2255 |
+
echo __('Until sync is complete', $domain);
|
2256 |
+
}
|
2257 |
+
echo '</p>';
|
2258 |
+
}
|
2259 |
+
} ?>
|
2260 |
+
</div>
|
2261 |
+
<hr />
|
2262 |
+
</div>
|
2263 |
+
<?php echo $nux_message; ?>
|
2264 |
+
|
2265 |
+
<div>
|
2266 |
+
<div id='fbAdvancedOptionsText' onclick="toggleAdvancedOptions();">
|
2267 |
+
Show Advanced Settings
|
2268 |
+
</div>
|
2269 |
+
<div id='fbAdvancedOptions'>
|
2270 |
+
<div class='autosync' title="This experimental feature will call force resync at the specified time using wordpress cron scheduling.">
|
2271 |
+
<input type="checkbox"
|
2272 |
+
onclick="saveAutoSyncSchedule()"
|
2273 |
+
class="autosyncCheck"
|
2274 |
+
<?php echo get_option('woocommerce_fb_autosync_time', false) ? 'checked' : 'unchecked'; ?>>
|
2275 |
+
Automatically Force Resync of Products At
|
2276 |
+
|
2277 |
+
<input
|
2278 |
+
type="time"
|
2279 |
+
value="<?php echo get_option('woocommerce_fb_autosync_time', '23:00'); ?>"
|
2280 |
+
class="autosyncTime"
|
2281 |
+
onfocusout="saveAutoSyncSchedule()"
|
2282 |
+
<?php echo get_option('woocommerce_fb_autosync_time', 0) ? '' : 'disabled'; ?> />
|
2283 |
+
Every Day.
|
2284 |
+
<span class="autosyncSavedNotice" disabled> Saved </span>
|
2285 |
+
</div>
|
2286 |
+
<div title="This option is meant for development and testing environments.">
|
2287 |
+
<input type="checkbox"
|
2288 |
+
onclick="onSetDisableSyncOnDevEnvironment()"
|
2289 |
+
class="disableOnDevEnvironment"
|
2290 |
+
<?php echo get_option('fb_disable_sync_on_dev_environment', false)
|
2291 |
+
? 'checked'
|
2292 |
+
: 'unchecked'; ?> />
|
2293 |
+
Disable Product Sync with FB
|
2294 |
+
</div>
|
2295 |
+
<div class='shortdescr' title="This experimental feature will import short description instead of description for all products.">
|
2296 |
+
<input type="checkbox"
|
2297 |
+
onclick="syncShortDescription()"
|
2298 |
+
class="syncShortDescription"
|
2299 |
+
<?php echo get_option('fb_sync_short_description', false)
|
2300 |
+
? 'checked'
|
2301 |
+
: 'unchecked'; ?> />
|
2302 |
+
Sync Short Description Instead of Description
|
2303 |
+
</div>
|
2304 |
+
</div>
|
2305 |
+
</div>
|
2306 |
+
</div>
|
2307 |
+
</div>
|
2308 |
+
<div <?php echo ($hide_test) ? ' style="display:none;" ' : ''; ?> >
|
2309 |
+
<p class="tooltip" id="test_product_sync">
|
2310 |
+
<?php
|
2311 |
+
// WP_DEBUG mode: button to launch test
|
2312 |
+
echo sprintf(__('<a href="%s&fb_test_product_sync=true"', $domain),
|
2313 |
+
WOOCOMMERCE_FACEBOOK_PLUGIN_SETTINGS_URL);
|
2314 |
+
echo '>' . esc_html__('Launch Test', $domain);
|
2315 |
+
?>
|
2316 |
+
<span class='tooltiptext'>
|
2317 |
+
<?php
|
2318 |
+
_e('This button will run an integration test suite verifying the
|
2319 |
+
extension. Note that this will reset your products and resync them
|
2320 |
+
to Facebook. Not recommended to use unless you are changing the
|
2321 |
+
extension code and want to test your changes.', $domain);
|
2322 |
+
?>
|
2323 |
+
</span>
|
2324 |
+
<?php
|
2325 |
+
echo '</a>';
|
2326 |
+
?>
|
2327 |
+
</p>
|
2328 |
+
<p id="stack_trace"></p>
|
2329 |
+
</div>
|
2330 |
+
<br/><hr/><br/>
|
2331 |
+
<?php
|
2332 |
+
|
2333 |
+
$GLOBALS['hide_save_button'] = true;
|
2334 |
+
if (defined('WP_DEBUG') && true === WP_DEBUG) {
|
2335 |
+
$GLOBALS['hide_save_button'] = false;
|
2336 |
+
?>
|
2337 |
+
<table class="form-table">
|
2338 |
+
<?php $this->generate_settings_html(); ?>
|
2339 |
+
</table><!--/.form-table-->
|
2340 |
+
<?php
|
2341 |
+
}
|
2342 |
+
}
|
2343 |
+
|
2344 |
+
function delete_product_item($wp_id) {
|
2345 |
+
$fb_product_item_id = $this->get_product_fbid(
|
2346 |
+
self::FB_PRODUCT_ITEM_ID,
|
2347 |
+
$wp_id);
|
2348 |
+
if ($fb_product_item_id) {
|
2349 |
+
$pi_result =
|
2350 |
+
$this->fbgraph->delete_product_item($fb_product_item_id);
|
2351 |
+
WC_Facebookcommerce_Utils::log($pi_result);
|
2352 |
+
}
|
2353 |
+
}
|
2354 |
+
|
2355 |
+
function fb_duplicate_product_reset_meta($to_delete) {
|
2356 |
+
array_push($to_delete, self::FB_PRODUCT_ITEM_ID);
|
2357 |
+
array_push($to_delete, self::FB_PRODUCT_GROUP_ID);
|
2358 |
+
return $to_delete;
|
2359 |
+
}
|
2360 |
+
|
2361 |
+
/**
|
2362 |
+
* Helper function to update FB visibility.
|
2363 |
+
*/
|
2364 |
+
function update_fb_visibility($wp_id, $visibility) {
|
2365 |
+
$woo_product = new WC_Facebook_Product($wp_id);
|
2366 |
+
if (!$woo_product->exists()) {
|
2367 |
+
// This function can be called for non-woo products.
|
2368 |
+
return;
|
2369 |
+
}
|
2370 |
+
|
2371 |
+
$products = WC_Facebookcommerce_Utils::get_product_array($woo_product);
|
2372 |
+
foreach ($products as $item_id) {
|
2373 |
+
$fb_product_item_id = $this->get_product_fbid(
|
2374 |
+
self::FB_PRODUCT_ITEM_ID,
|
2375 |
+
$item_id);
|
2376 |
+
|
2377 |
+
if (!$fb_product_item_id) {
|
2378 |
+
WC_Facebookcommerce_Utils::fblog(
|
2379 |
+
$fb_product_item_id." doesn't exist but underwent a visibility transform.",
|
2380 |
+
array(),
|
2381 |
+
true);
|
2382 |
+
continue;
|
2383 |
+
}
|
2384 |
+
$result = $this->check_api_result(
|
2385 |
+
$this->fbgraph->update_product_item(
|
2386 |
+
$fb_product_item_id,
|
2387 |
+
array('visibility' => $visibility)));
|
2388 |
+
if ($result) {
|
2389 |
+
update_post_meta($item_id, self::FB_VISIBILITY, $visibility);
|
2390 |
+
update_post_meta($wp_id, self::FB_VISIBILITY, $visibility);
|
2391 |
+
}
|
2392 |
+
}
|
2393 |
+
}
|
2394 |
+
|
2395 |
+
function on_quick_and_bulk_edit_save($product) {
|
2396 |
+
$wp_id = $product->get_id();
|
2397 |
+
$visibility = get_post_status($wp_id) == 'publish'
|
2398 |
+
? 'published'
|
2399 |
+
: 'staging';
|
2400 |
+
// case 1: new status is 'publish' regardless of old status, sync to FB
|
2401 |
+
if ($visibility == 'published') {
|
2402 |
+
$this->on_product_publish($wp_id);
|
2403 |
+
} else {
|
2404 |
+
// case 2: product never publish to FB, new status is not publish
|
2405 |
+
// case 3: product new status is not publish and published before
|
2406 |
+
$this->update_fb_visibility($wp_id, $visibility);
|
2407 |
+
}
|
2408 |
+
}
|
2409 |
+
|
2410 |
+
private function get_product_fbid($fbid_type, $wp_id, $woo_product = null) {
|
2411 |
+
$fb_id = WC_Facebookcommerce_Utils::get_fbid_post_meta(
|
2412 |
+
$wp_id,
|
2413 |
+
$fbid_type);
|
2414 |
+
if ($fb_id) {
|
2415 |
+
return $fb_id;
|
2416 |
+
}
|
2417 |
+
if (!isset($this->settings['upload_end_time'])) {
|
2418 |
+
return null;
|
2419 |
+
}
|
2420 |
+
if (!$woo_product) {
|
2421 |
+
$woo_product = new WC_Facebook_Product($wp_id);
|
2422 |
+
}
|
2423 |
+
$products = WC_Facebookcommerce_Utils::get_product_array($woo_product);
|
2424 |
+
$woo_product = new WC_Facebook_Product(current($products));
|
2425 |
+
// This is a generalized function used elsewhere
|
2426 |
+
// Cannot call is_hidden for VC_Product_Variable Object
|
2427 |
+
if ($woo_product->is_hidden()) {
|
2428 |
+
return null;
|
2429 |
+
}
|
2430 |
+
$fb_retailer_id =
|
2431 |
+
WC_Facebookcommerce_Utils::get_fb_retailer_id($woo_product);
|
2432 |
+
|
2433 |
+
$product_fbid_result = $this->fbgraph->get_facebook_id(
|
2434 |
+
$this->product_catalog_id,
|
2435 |
+
$fb_retailer_id);
|
2436 |
+
if (is_wp_error($product_fbid_result)) {
|
2437 |
+
WC_Facebookcommerce_Utils::log($product_fbid_result->get_error_message());
|
2438 |
+
$this->display_error_message(
|
2439 |
+
"There was an issue connecting to the Facebook API: ".
|
2440 |
+
$product_fbid_result->get_error_message());
|
2441 |
+
return;
|
2442 |
+
}
|
2443 |
+
|
2444 |
+
if ($product_fbid_result && isset($product_fbid_result['body'])) {
|
2445 |
+
$body = WC_Facebookcommerce_Utils::decode_json($product_fbid_result['body']);
|
2446 |
+
if ($body && $body->id) {
|
2447 |
+
if ($fbid_type == self::FB_PRODUCT_GROUP_ID) {
|
2448 |
+
$fb_id = $body->product_group->id;
|
2449 |
+
} else {
|
2450 |
+
$fb_id = $body->id;
|
2451 |
+
}
|
2452 |
+
update_post_meta(
|
2453 |
+
$wp_id,
|
2454 |
+
$fbid_type,
|
2455 |
+
$fb_id);
|
2456 |
+
update_post_meta($wp_id, self::FB_VISIBILITY, true);
|
2457 |
+
return $fb_id;
|
2458 |
+
}
|
2459 |
+
}
|
2460 |
+
return;
|
2461 |
+
}
|
2462 |
+
|
2463 |
+
private function set_default_variant($product_group_id, $product_item_id) {
|
2464 |
+
$result = $this->check_api_result(
|
2465 |
+
$this->fbgraph->set_default_variant(
|
2466 |
+
$product_group_id,
|
2467 |
+
array('default_product_id' => $product_item_id)));
|
2468 |
+
if (!$result) {
|
2469 |
+
WC_Facebookcommerce_Utils::fblog(
|
2470 |
+
'Fail to set default product item',
|
2471 |
+
array(),
|
2472 |
+
true);
|
2473 |
+
}
|
2474 |
+
}
|
2475 |
+
|
2476 |
+
private function fb_wp_die() {
|
2477 |
+
if (!$this->test_mode) {
|
2478 |
+
wp_die();
|
2479 |
+
}
|
2480 |
+
}
|
2481 |
+
|
2482 |
+
/**
|
2483 |
+
* Display test result.
|
2484 |
+
**/
|
2485 |
+
function ajax_display_test_result() {
|
2486 |
+
WC_Facebookcommerce_Utils::check_woo_ajax_permissions('test result', true);
|
2487 |
+
$response = array(
|
2488 |
+
'pass' => 'true',
|
2489 |
+
);
|
2490 |
+
$test_pass = get_option('fb_test_pass', null);
|
2491 |
+
if (!isset($test_pass)) {
|
2492 |
+
$response['pass'] = 'in progress';
|
2493 |
+
} else if ($test_pass == 0) {
|
2494 |
+
$response['pass'] = 'false';
|
2495 |
+
$response['debug_info'] = get_transient('facebook_plugin_test_fail');
|
2496 |
+
$response['stack_trace'] =
|
2497 |
+
get_transient('facebook_plugin_test_stack_trace');
|
2498 |
+
$response['stack_trace'] =
|
2499 |
+
preg_replace("/\n/", '<br>', $response['stack_trace']);
|
2500 |
+
delete_transient('facebook_plugin_test_fail');
|
2501 |
+
delete_transient('facebook_plugin_test_stack_trace');
|
2502 |
+
}
|
2503 |
+
delete_option('fb_test_pass');
|
2504 |
+
printf(json_encode($response));
|
2505 |
+
wp_die();
|
2506 |
+
}
|
2507 |
+
|
2508 |
+
/**
|
2509 |
+
* Schedule Force Resync
|
2510 |
+
*/
|
2511 |
+
function ajax_schedule_force_resync() {
|
2512 |
+
WC_Facebookcommerce_Utils::check_woo_ajax_permissions('resync schedule', true);
|
2513 |
+
if (isset($_POST) && isset($_POST['enabled'])) {
|
2514 |
+
if (isset($_POST['time']) && $_POST['enabled']) { // Enabled
|
2515 |
+
$time = sanitize_text_field($_POST['time']);
|
2516 |
+
wp_clear_scheduled_hook('sync_all_fb_products_using_feed');
|
2517 |
+
wp_schedule_event(
|
2518 |
+
strtotime($time),
|
2519 |
+
'daily',
|
2520 |
+
'sync_all_fb_products_using_feed');
|
2521 |
+
WC_Facebookcommerce_Utils::fblog('Scheduled autosync for '.$time);
|
2522 |
+
update_option('woocommerce_fb_autosync_time', $time);
|
2523 |
+
} else if (!$_POST['enabled']) { // Disabled
|
2524 |
+
wp_clear_scheduled_hook('sync_all_fb_products_using_feed');
|
2525 |
+
WC_Facebookcommerce_Utils::fblog('Autosync disabled');
|
2526 |
+
delete_option('woocommerce_fb_autosync_time');
|
2527 |
+
}
|
2528 |
+
} else {
|
2529 |
+
WC_Facebookcommerce_Utils::fblog('Autosync AJAX Problem', $_POST, true);
|
2530 |
+
}
|
2531 |
+
wp_die();
|
2532 |
+
}
|
2533 |
+
|
2534 |
+
function ajax_update_fb_option() {
|
2535 |
+
WC_Facebookcommerce_Utils::check_woo_ajax_permissions('update fb options', true);
|
2536 |
+
if (isset($_POST) && stripos($_POST['option'], 'fb_') === 0) {
|
2537 |
+
update_option(sanitize_text_field($_POST['option']), sanitize_text_field($_POST['option_value']));
|
2538 |
+
}
|
2539 |
+
wp_die();
|
2540 |
+
}
|
2541 |
+
}
|
facebook-config-warmer.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* @package FacebookCommerce
|
9 |
+
*/
|
10 |
+
|
11 |
+
if (!defined('ABSPATH')) exit; // Exit if accessed directly
|
12 |
+
|
13 |
+
|
14 |
+
if (!class_exists('WC_Facebookcommerce_WarmConfig')) :
|
15 |
+
|
16 |
+
class WC_Facebookcommerce_WarmConfig {
|
17 |
+
static $fb_warm_pixel_id = null;
|
18 |
+
}
|
19 |
+
|
20 |
+
endif;
|
facebook-for-woocommerce.php
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* Plugin Name: Facebook for WooCommerce
|
9 |
+
* Plugin URI: https://github.com/facebookincubator/facebook-for-woocommerce/
|
10 |
+
* Description: Grow your business on Facebook! Use this official plugin to help sell more of your products using Facebook. After completing the setup, you'll be ready to create ads that promote your products and you can also create a shop section on your Page where customers can browse your products on Facebook.
|
11 |
+
* Author: Facebook
|
12 |
+
* Author URI: https://www.facebook.com/
|
13 |
+
* Version: 1.9.11
|
14 |
+
* Woo: 2127297:0ea4fe4c2d7ca6338f8a322fb3e4e187
|
15 |
+
* Text Domain: facebook-for-woocommerce
|
16 |
+
* WC requires at least: 3.0.0
|
17 |
+
* WC tested up to: 3.3.5
|
18 |
+
*
|
19 |
+
* @package FacebookCommerce
|
20 |
+
*/
|
21 |
+
|
22 |
+
|
23 |
+
if (!class_exists('WC_Facebookcommerce')) :
|
24 |
+
include_once 'includes/fbutils.php';
|
25 |
+
|
26 |
+
class WC_Facebookcommerce {
|
27 |
+
|
28 |
+
// Change it above as well
|
29 |
+
const PLUGIN_VERSION = WC_Facebookcommerce_Utils::PLUGIN_VERSION;
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Construct the plugin.
|
33 |
+
*/
|
34 |
+
public function __construct() {
|
35 |
+
add_action('plugins_loaded', array( $this, 'init'));
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Initialize the plugin.
|
40 |
+
*/
|
41 |
+
public function init() {
|
42 |
+
if (is_admin()) {
|
43 |
+
add_filter('plugin_action_links_'.plugin_basename(__FILE__),
|
44 |
+
array($this, 'add_settings_link'));
|
45 |
+
}
|
46 |
+
|
47 |
+
if (WC_Facebookcommerce_Utils::isWoocommerceIntegration()) {
|
48 |
+
if (!defined('WOOCOMMERCE_FACEBOOK_PLUGIN_SETTINGS_URL')) {
|
49 |
+
define(
|
50 |
+
'WOOCOMMERCE_FACEBOOK_PLUGIN_SETTINGS_URL',
|
51 |
+
get_admin_url()
|
52 |
+
.'/admin.php?page=wc-settings&tab=integration'
|
53 |
+
.'§ion=facebookcommerce');
|
54 |
+
}
|
55 |
+
include_once 'facebook-commerce.php';
|
56 |
+
|
57 |
+
// Register WooCommerce integration.
|
58 |
+
add_filter('woocommerce_integrations', array(
|
59 |
+
$this,
|
60 |
+
'add_woocommerce_integration'
|
61 |
+
));
|
62 |
+
}
|
63 |
+
}
|
64 |
+
|
65 |
+
public function add_settings_link($links) {
|
66 |
+
$settings = array(
|
67 |
+
'settings' => sprintf(
|
68 |
+
'<a href="%s">%s</a>',
|
69 |
+
admin_url('admin.php?page=wc-settings&tab=integration§ion=facebookcommerce'),
|
70 |
+
'Settings')
|
71 |
+
);
|
72 |
+
return array_merge($settings, $links);
|
73 |
+
}
|
74 |
+
|
75 |
+
public function wp_debug_display_error() {
|
76 |
+
?>
|
77 |
+
<div class="error below-h3">
|
78 |
+
<p>
|
79 |
+
<?php
|
80 |
+
printf(__('To use Facebook for WooCommerce,
|
81 |
+
please disable WP_DEBUG_DISPLAY in your wp-config.php file.
|
82 |
+
Contact your server administrator for more assistance.',
|
83 |
+
'facebook-for-woocommerce'));
|
84 |
+
?>
|
85 |
+
</p>
|
86 |
+
</div>
|
87 |
+
<?php
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Add a new integration to WooCommerce.
|
92 |
+
*/
|
93 |
+
public function add_woocommerce_integration($integrations) {
|
94 |
+
$integrations[] = 'WC_Facebookcommerce_Integration';
|
95 |
+
return $integrations;
|
96 |
+
}
|
97 |
+
|
98 |
+
public function add_wordpress_integration() {
|
99 |
+
new WP_Facebook_Integration();
|
100 |
+
}
|
101 |
+
}
|
102 |
+
|
103 |
+
$WC_Facebookcommerce = new WC_Facebookcommerce(__FILE__);
|
104 |
+
|
105 |
+
endif;
|
includes/fbasync.php
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* @package FacebookCommerce
|
9 |
+
*/
|
10 |
+
|
11 |
+
if (!defined('ABSPATH')) {
|
12 |
+
exit;
|
13 |
+
}
|
14 |
+
|
15 |
+
if (!class_exists('WP_Async_Request', false) ) {
|
16 |
+
// Do not attempt to create this class without WP_Async_Request
|
17 |
+
return;
|
18 |
+
}
|
19 |
+
|
20 |
+
if (!class_exists('WC_Facebookcommerce_Async_Request')) :
|
21 |
+
|
22 |
+
/**
|
23 |
+
* FB Graph API async request
|
24 |
+
*
|
25 |
+
*/
|
26 |
+
class WC_Facebookcommerce_Async_Request extends WP_Async_Request {
|
27 |
+
|
28 |
+
protected $action = 'wc_facebook_async_request';
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Handle
|
32 |
+
*
|
33 |
+
* Override this method to perform any actions required
|
34 |
+
* during the async request.
|
35 |
+
*/
|
36 |
+
protected function handle() {
|
37 |
+
// Actions to perform
|
38 |
+
}
|
39 |
+
|
40 |
+
}
|
41 |
+
|
42 |
+
endif;
|
includes/fbbackground.php
ADDED
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* @package FacebookCommerce
|
9 |
+
*/
|
10 |
+
|
11 |
+
if (! defined('ABSPATH')) {
|
12 |
+
exit;
|
13 |
+
}
|
14 |
+
|
15 |
+
if (!class_exists('WP_Background_Process', false) ) {
|
16 |
+
// Do not attempt to create this class without WP_Background_Process
|
17 |
+
return;
|
18 |
+
}
|
19 |
+
|
20 |
+
if (! class_exists('WC_Facebookcommerce_Background_Process')) :
|
21 |
+
|
22 |
+
class WC_Facebookcommerce_Background_Process extends WP_Background_Process {
|
23 |
+
|
24 |
+
public function __construct($commerce) {
|
25 |
+
$this->commerce = $commerce; // Full WC_Facebookcommerce_Integration obj
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var string
|
30 |
+
*/
|
31 |
+
protected $action = 'fb_commerce_background_process';
|
32 |
+
|
33 |
+
public function dispatch() {
|
34 |
+
$commerce = $this->commerce;
|
35 |
+
$dispatched = parent::dispatch();
|
36 |
+
|
37 |
+
if (is_wp_error($dispatched)) {
|
38 |
+
WC_Facebookcommerce_Utils::log(
|
39 |
+
sprintf('Unable to dispatch FB Background processor: %s',
|
40 |
+
$dispatched->get_error_message()),
|
41 |
+
array( 'source' => 'wc_facebook_background_process' ));
|
42 |
+
}
|
43 |
+
}
|
44 |
+
|
45 |
+
public function get_item_count() {
|
46 |
+
$commerce = $this->commerce;
|
47 |
+
return (int)get_transient($commerce::FB_SYNC_REMAINING);
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Handle cron healthcheck
|
52 |
+
*
|
53 |
+
* Restart the background process if not already running
|
54 |
+
* and data exists in the queue.
|
55 |
+
*/
|
56 |
+
public function handle_cron_healthcheck() {
|
57 |
+
$commerce = $this->commerce;
|
58 |
+
if ($this->is_process_running()) {
|
59 |
+
// Background process already running, no-op
|
60 |
+
return true; // Return "is running? status"
|
61 |
+
}
|
62 |
+
|
63 |
+
if ($this->is_queue_empty()) {
|
64 |
+
// No data to process.
|
65 |
+
$this->clear_scheduled_event();
|
66 |
+
delete_transient($commerce::FB_SYNC_REMAINING);
|
67 |
+
return;
|
68 |
+
}
|
69 |
+
|
70 |
+
$this->handle();
|
71 |
+
return true;
|
72 |
+
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Schedule fallback event.
|
77 |
+
*/
|
78 |
+
protected function schedule_event() {
|
79 |
+
if (!wp_next_scheduled($this->cron_hook_identifier)) {
|
80 |
+
wp_schedule_event(time() + 10, $this->cron_interval_identifier,
|
81 |
+
$this->cron_hook_identifier);
|
82 |
+
}
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Is the processor updating?
|
87 |
+
* @return boolean
|
88 |
+
*/
|
89 |
+
public function is_updating() {
|
90 |
+
return false === $this->is_queue_empty();
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Is the processor running?
|
95 |
+
* @return boolean
|
96 |
+
*/
|
97 |
+
public function is_running() {
|
98 |
+
return $this->is_process_running();
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Process individual product
|
103 |
+
*
|
104 |
+
* Returns false to remove the item from the queue
|
105 |
+
* (would return item if it needed additional processing).
|
106 |
+
*
|
107 |
+
* @param mixed $item Queue item to iterate over
|
108 |
+
*
|
109 |
+
* @return mixed
|
110 |
+
*/
|
111 |
+
protected function task($item) {
|
112 |
+
$commerce = $this->commerce; // PHP5 compatibility for static access
|
113 |
+
$remaining = $this->get_item_count();
|
114 |
+
$count_message = sprintf(
|
115 |
+
'Background syncing products to Facebook. Products remaining: %1$d',
|
116 |
+
$remaining);
|
117 |
+
|
118 |
+
$this->commerce->display_sticky_message($count_message, true);
|
119 |
+
|
120 |
+
$this->commerce->on_product_publish($item);
|
121 |
+
$remaining--;
|
122 |
+
set_transient(
|
123 |
+
$commerce::FB_SYNC_IN_PROGRESS,
|
124 |
+
true,
|
125 |
+
$commerce::FB_SYNC_TIMEOUT);
|
126 |
+
set_transient(
|
127 |
+
$commerce::FB_SYNC_REMAINING,
|
128 |
+
$remaining);
|
129 |
+
|
130 |
+
return false;
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Complete
|
135 |
+
*
|
136 |
+
* Override if applicable, but ensure that the below actions are
|
137 |
+
* performed, or, call parent::complete().
|
138 |
+
*/
|
139 |
+
protected function complete() {
|
140 |
+
$commerce = $this->commerce; // PHP5 compatibility for static access
|
141 |
+
delete_transient($commerce::FB_SYNC_IN_PROGRESS);
|
142 |
+
delete_transient($commerce::FB_SYNC_REMAINING);
|
143 |
+
WC_Facebookcommerce_Utils::log("Background sync complete!");
|
144 |
+
WC_Facebookcommerce_Utils::fblog("Background sync complete!");
|
145 |
+
$this->commerce->remove_sticky_message();
|
146 |
+
$this->commerce->display_info_message('Facebook product sync complete!');
|
147 |
+
parent::complete();
|
148 |
+
}
|
149 |
+
|
150 |
+
}
|
151 |
+
|
152 |
+
endif;
|
includes/fbcustomapi.php
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* @package FacebookCommerce
|
9 |
+
*/
|
10 |
+
|
11 |
+
if (! defined('ABSPATH')) {
|
12 |
+
exit;
|
13 |
+
}
|
14 |
+
|
15 |
+
if (! class_exists('WC_Facebookcommerce_REST_Controller')) :
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Custom API REST path class
|
19 |
+
*
|
20 |
+
*/
|
21 |
+
class WC_Facebookcommerce_REST_Controller extends WP_REST_Controller {
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Init and hook in the integration.
|
25 |
+
*/
|
26 |
+
public function __construct() {
|
27 |
+
global $woocommerce;
|
28 |
+
add_action('rest_api_init', array($this, 'register_routes'));
|
29 |
+
|
30 |
+
|
31 |
+
// TODO: wp_woocommerce_api_keys
|
32 |
+
// http://stackoverflow.com/questions/31327994/woocommerce-rest-client-api-
|
33 |
+
// programmatically-get-consumer-key-and-secret
|
34 |
+
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Function to define custom routes
|
39 |
+
*/
|
40 |
+
public function register_routes() {
|
41 |
+
register_rest_route('facebook/v1', '/version' ,
|
42 |
+
array(
|
43 |
+
'methods' => WP_REST_Server::READABLE,
|
44 |
+
'callback' => array($this, 'fb_test_function'),
|
45 |
+
));
|
46 |
+
}
|
47 |
+
|
48 |
+
public function fb_test_function(WP_REST_Request $request) {
|
49 |
+
$parameters = $request->get_params();
|
50 |
+
// Create the response object
|
51 |
+
$res = new WP_REST_Response(WC_Facebookcommerce_Utils::PLUGIN_VERSION);
|
52 |
+
|
53 |
+
// Add a custom status code
|
54 |
+
$res->set_status(200);
|
55 |
+
$res->jsonSerialize();
|
56 |
+
|
57 |
+
return $res;
|
58 |
+
}
|
59 |
+
|
60 |
+
|
61 |
+
}
|
62 |
+
|
63 |
+
endif;
|
includes/fbgraph.php
ADDED
@@ -0,0 +1,298 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* @package FacebookCommerce
|
9 |
+
*/
|
10 |
+
|
11 |
+
if (!defined('ABSPATH')) {
|
12 |
+
exit;
|
13 |
+
}
|
14 |
+
|
15 |
+
if (!class_exists('WC_Facebookcommerce_Graph_API')) :
|
16 |
+
|
17 |
+
if (!class_exists('WC_Facebookcommerce_Async_Request')) {
|
18 |
+
include_once 'fbasync.php';
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* FB Graph API helper functions
|
23 |
+
*
|
24 |
+
*/
|
25 |
+
class WC_Facebookcommerce_Graph_API {
|
26 |
+
const GRAPH_API_URL = 'https://graph.facebook.com/v2.9/';
|
27 |
+
const CURL_TIMEOUT = 500;
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Cache the api_key
|
31 |
+
*/
|
32 |
+
var $api_key;
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Init
|
36 |
+
*/
|
37 |
+
public function __construct($api_key) {
|
38 |
+
$this->api_key = $api_key;
|
39 |
+
}
|
40 |
+
|
41 |
+
public function _get($url, $api_key = '') {
|
42 |
+
$api_key = $api_key ?: $this->api_key;
|
43 |
+
return wp_remote_get($url, array(
|
44 |
+
'headers' => array(
|
45 |
+
'Authorization' => 'Bearer ' . $api_key,
|
46 |
+
),
|
47 |
+
'timeout' => self::CURL_TIMEOUT,
|
48 |
+
));
|
49 |
+
}
|
50 |
+
|
51 |
+
public function _post($url, $data, $api_key = '') {
|
52 |
+
if (class_exists('WC_Facebookcommerce_Async_Request')) {
|
53 |
+
return self::_post_async($url, $data);
|
54 |
+
} else {
|
55 |
+
return self::_post_sync($url, $data);
|
56 |
+
}
|
57 |
+
}
|
58 |
+
|
59 |
+
public function _post_sync($url, $data, $api_key = '') {
|
60 |
+
$api_key = $api_key ?: $this->api_key;
|
61 |
+
return wp_remote_post($url, array(
|
62 |
+
'body' => $data,
|
63 |
+
'headers' => array(
|
64 |
+
'Authorization' => 'Bearer ' . $api_key,
|
65 |
+
),
|
66 |
+
'timeout' => self::CURL_TIMEOUT,
|
67 |
+
));
|
68 |
+
}
|
69 |
+
|
70 |
+
public function _post_async($url, $data, $api_key = '') {
|
71 |
+
if (!class_exists('WC_Facebookcommerce_Async_Request')) {
|
72 |
+
return;
|
73 |
+
}
|
74 |
+
|
75 |
+
$api_key = $api_key ?: $this->api_key;
|
76 |
+
$fbasync = new WC_Facebookcommerce_Async_Request();
|
77 |
+
|
78 |
+
$fbasync->query_url = $url;
|
79 |
+
$fbasync->query_args = array();
|
80 |
+
$fbasync->post_args = array(
|
81 |
+
'body' => $data,
|
82 |
+
'headers' => array(
|
83 |
+
'Authorization' => 'Bearer ' . $api_key,
|
84 |
+
),
|
85 |
+
'timeout' => self::CURL_TIMEOUT,
|
86 |
+
);
|
87 |
+
|
88 |
+
return $fbasync->dispatch();
|
89 |
+
}
|
90 |
+
|
91 |
+
public function _delete($url, $api_key = '') {
|
92 |
+
$api_key = $api_key ?: $this->api_key;
|
93 |
+
|
94 |
+
return wp_remote_request($url, array(
|
95 |
+
'headers' => array(
|
96 |
+
'Authorization' => 'Bearer ' . $api_key,
|
97 |
+
),
|
98 |
+
'timeout' => self::CURL_TIMEOUT,
|
99 |
+
'method' => 'DELETE',
|
100 |
+
));
|
101 |
+
}
|
102 |
+
|
103 |
+
// GET https://graph.facebook.com/vX.X/{page-id}/?fields=name
|
104 |
+
public function get_page_name($page_id, $api_key = '') {
|
105 |
+
$api_key = $api_key ?: $this->api_key;
|
106 |
+
$url = $this->build_url($page_id, '/?fields=name');
|
107 |
+
$response = self::_get($url, $api_key);
|
108 |
+
if (is_wp_error($response)) {
|
109 |
+
WC_Facebookcommerce_Utils::log($response->get_error_message());
|
110 |
+
return;
|
111 |
+
}
|
112 |
+
if ($response['response']['code'] != '200') {
|
113 |
+
return;
|
114 |
+
}
|
115 |
+
|
116 |
+
$response_body = wp_remote_retrieve_body($response);
|
117 |
+
|
118 |
+
return json_decode($response_body)->name;
|
119 |
+
}
|
120 |
+
|
121 |
+
public function validate_product_catalog($product_catalog_id) {
|
122 |
+
$url = $this->build_url($product_catalog_id);
|
123 |
+
$response = self::_get($url);
|
124 |
+
if (is_wp_error($response)) {
|
125 |
+
WC_Facebookcommerce_Utils::log($response->get_error_message());
|
126 |
+
return;
|
127 |
+
}
|
128 |
+
return $response['response']['code'] == '200';
|
129 |
+
}
|
130 |
+
|
131 |
+
// POST https://graph.facebook.com/vX.X/{product-catalog-id}/product_groups
|
132 |
+
public function create_product_group($product_catalog_id, $data) {
|
133 |
+
$url = $this->build_url($product_catalog_id, '/product_groups');
|
134 |
+
return self::_post($url, $data);
|
135 |
+
}
|
136 |
+
|
137 |
+
// POST https://graph.facebook.com/vX.X/{product-group-id}/products
|
138 |
+
public function create_product_item($product_group_id, $data) {
|
139 |
+
$url = $this->build_url($product_group_id, '/products');
|
140 |
+
return self::_post($url, $data);
|
141 |
+
}
|
142 |
+
|
143 |
+
public function update_product_group($product_catalog_id, $data) {
|
144 |
+
$url = $this->build_url($product_catalog_id);
|
145 |
+
return self::_post($url, $data);
|
146 |
+
}
|
147 |
+
|
148 |
+
public function update_product_item($product_id, $data) {
|
149 |
+
$url = $this->build_url($product_id);
|
150 |
+
return self::_post($url, $data);
|
151 |
+
}
|
152 |
+
|
153 |
+
public function delete_product_item($product_item_id) {
|
154 |
+
$product_item_url = $this->build_url($product_item_id);
|
155 |
+
return self::_delete($product_item_url);
|
156 |
+
}
|
157 |
+
|
158 |
+
public function delete_product_group($product_group_id) {
|
159 |
+
$product_group_url = $this->build_url($product_group_id);
|
160 |
+
return self::_delete($product_group_url);
|
161 |
+
}
|
162 |
+
|
163 |
+
public function log($ems_id, $message, $error) {
|
164 |
+
$log_url = $this->build_url($ems_id, '/log_events');
|
165 |
+
|
166 |
+
$data = array(
|
167 |
+
'message'=> $message,
|
168 |
+
'error' => $error
|
169 |
+
);
|
170 |
+
|
171 |
+
self::_post($log_url, $data);
|
172 |
+
}
|
173 |
+
|
174 |
+
public function log_tip_event($tip_id, $channel_id, $event) {
|
175 |
+
$tip_event_log_url = $this->build_url('', '/log_tip_events');
|
176 |
+
|
177 |
+
$data = array(
|
178 |
+
'tip_id' => $tip_id,
|
179 |
+
'channel_id' => $channel_id,
|
180 |
+
'event' => $event
|
181 |
+
);
|
182 |
+
|
183 |
+
self::_post($tip_event_log_url, $data);
|
184 |
+
}
|
185 |
+
|
186 |
+
public function create_upload($facebook_feed_id, $path_to_feed_file) {
|
187 |
+
$url = $this->build_url(
|
188 |
+
$facebook_feed_id,
|
189 |
+
'/uploads?access_token=' . $this->api_key);
|
190 |
+
$data = array(
|
191 |
+
'file' => new CurlFile($path_to_feed_file, 'text/csv')
|
192 |
+
);
|
193 |
+
$curl = curl_init();
|
194 |
+
curl_setopt_array(
|
195 |
+
$curl,
|
196 |
+
array(
|
197 |
+
CURLOPT_URL => $url,
|
198 |
+
CURLOPT_POST => 1,
|
199 |
+
CURLOPT_POSTFIELDS => $data,
|
200 |
+
CURLOPT_RETURNTRANSFER => 1));
|
201 |
+
$response = curl_exec($curl);
|
202 |
+
if (curl_errno($curl)) {
|
203 |
+
WC_Facebookcommerce_Utils::fblog($response);
|
204 |
+
return null;
|
205 |
+
}
|
206 |
+
return WC_Facebookcommerce_Utils::decode_json($response, true);
|
207 |
+
}
|
208 |
+
|
209 |
+
public function create_feed($facebook_catalog_id, $data) {
|
210 |
+
$url = $this->build_url($facebook_catalog_id, '/product_feeds');
|
211 |
+
// success API call will return {id: <product feed id>}
|
212 |
+
// failure API will return {error: <error message>}
|
213 |
+
return self::_post($url, $data);
|
214 |
+
}
|
215 |
+
|
216 |
+
public function get_upload_status($facebook_upload_id) {
|
217 |
+
$url = $this->build_url($facebook_upload_id, '/?fields=end_time');
|
218 |
+
// success API call will return
|
219 |
+
// {id: <upload id>, end_time: <time when upload completes>}
|
220 |
+
// failure API will return {error: <error message>}
|
221 |
+
return self::_get($url);
|
222 |
+
}
|
223 |
+
|
224 |
+
// success API call will return a JSON of tip info
|
225 |
+
public function get_tip_info($external_merchant_settings_id) {
|
226 |
+
$url = $this->build_url($external_merchant_settings_id, '/?fields=connect_woo');
|
227 |
+
$response = self::_get($url, $this->api_key);
|
228 |
+
$data = array(
|
229 |
+
'response' => $response,
|
230 |
+
);
|
231 |
+
if (is_wp_error($response)) {
|
232 |
+
$data['error_type'] = 'is_wp_error';
|
233 |
+
WC_Facebookcommerce_Utils::fblog(
|
234 |
+
'Failed to get AYMT tip info via API.',
|
235 |
+
$data,
|
236 |
+
true);
|
237 |
+
return;
|
238 |
+
}
|
239 |
+
if ($response['response']['code'] != '200') {
|
240 |
+
$data['error_type'] = 'Non-200 error code from FB';
|
241 |
+
WC_Facebookcommerce_Utils::fblog(
|
242 |
+
'Failed to get AYMT tip info via API.',
|
243 |
+
$data,
|
244 |
+
true);
|
245 |
+
return;
|
246 |
+
}
|
247 |
+
|
248 |
+
$response_body = wp_remote_retrieve_body($response);
|
249 |
+
$connect_woo =
|
250 |
+
WC_Facebookcommerce_Utils::decode_json($response_body)->connect_woo;
|
251 |
+
if (!isset($connect_woo)) {
|
252 |
+
$data['error_type'] = 'Response body not set';
|
253 |
+
WC_Facebookcommerce_Utils::fblog(
|
254 |
+
"Failed to get AYMT tip info via API.",
|
255 |
+
$data,
|
256 |
+
true);
|
257 |
+
}
|
258 |
+
return $connect_woo;
|
259 |
+
}
|
260 |
+
|
261 |
+
public function get_facebook_id($facebook_catalog_id, $product_id) {
|
262 |
+
$param = 'catalog:' . (string)$facebook_catalog_id . ':' .
|
263 |
+
base64_encode($product_id) . '/?fields=id,product_group{id}';
|
264 |
+
$url = $this->build_url('', $param);
|
265 |
+
// success API call will return
|
266 |
+
// {id: <fb product id>, product_group{id} <fb product group id>}
|
267 |
+
// failure API will return {error: <error message>}
|
268 |
+
return self::_get($url);
|
269 |
+
}
|
270 |
+
|
271 |
+
public function check_product_info($facebook_catalog_id, $product_id, $pr_v) {
|
272 |
+
$param = 'catalog:' . (string)$facebook_catalog_id . ':' .
|
273 |
+
base64_encode($product_id) . '/?fields=id,name,description,price,' .
|
274 |
+
'sale_price,sale_price_start_date,sale_price_end_date,image_url,' .
|
275 |
+
'visibility';
|
276 |
+
if ($pr_v) {
|
277 |
+
$param = $param . ',additional_variant_attributes{value}';
|
278 |
+
}
|
279 |
+
$url = $this->build_url('', $param);
|
280 |
+
// success API call will return
|
281 |
+
// {id: <fb product id>, name,description,price,sale_price,sale_price_start_date
|
282 |
+
// sale_price_end_date
|
283 |
+
// failure API will return {error: <error message>}
|
284 |
+
return self::_get($url);
|
285 |
+
}
|
286 |
+
|
287 |
+
public function set_default_variant($product_group_id, $data) {
|
288 |
+
$url = $this->build_url($product_group_id);
|
289 |
+
return self::_post($url, $data);
|
290 |
+
}
|
291 |
+
|
292 |
+
private function build_url($field_id, $param ='') {
|
293 |
+
return self::GRAPH_API_URL . (string)$field_id . $param;
|
294 |
+
}
|
295 |
+
|
296 |
+
}
|
297 |
+
|
298 |
+
endif;
|
includes/fbinfobanner.php
ADDED
@@ -0,0 +1,241 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* @package FacebookCommerce
|
9 |
+
*/
|
10 |
+
|
11 |
+
if (! defined('ABSPATH')) {
|
12 |
+
exit;
|
13 |
+
}
|
14 |
+
|
15 |
+
if (!class_exists('WC_Facebookcommerce_Utils')) {
|
16 |
+
include_once 'includes/fbutils.php';
|
17 |
+
}
|
18 |
+
|
19 |
+
if (! class_exists('WC_Facebookcommerce_Info_Banner')) :
|
20 |
+
|
21 |
+
/**
|
22 |
+
* FB Info Banner class
|
23 |
+
*/
|
24 |
+
class WC_Facebookcommerce_Info_Banner {
|
25 |
+
|
26 |
+
const FB_NO_TIP_EXISTS = 'No Tip Exist!';
|
27 |
+
const DEFAULT_TIP_IMG_URL_PREFIX = 'https://www.facebook.com';
|
28 |
+
const CHANNEL_ID = 2087541767986590;
|
29 |
+
|
30 |
+
/** @var object Class Instance */
|
31 |
+
private static $instance;
|
32 |
+
|
33 |
+
/** @var string If the banner has been dismissed */
|
34 |
+
private $external_merchant_settings_id;
|
35 |
+
private $fbgraph;
|
36 |
+
private $should_query_tip;
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Get the class instance
|
40 |
+
*/
|
41 |
+
public static function get_instance(
|
42 |
+
$external_merchant_settings_id,
|
43 |
+
$fbgraph,
|
44 |
+
$should_query_tip = false) {
|
45 |
+
return null === self::$instance
|
46 |
+
? (self::$instance = new self(
|
47 |
+
$external_merchant_settings_id,
|
48 |
+
$fbgraph,
|
49 |
+
$should_query_tip))
|
50 |
+
: self::$instance;
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Constructor
|
55 |
+
*/
|
56 |
+
public function __construct(
|
57 |
+
$external_merchant_settings_id,
|
58 |
+
$fbgraph,
|
59 |
+
$should_query_tip = false) {
|
60 |
+
$this->should_query_tip = $should_query_tip;
|
61 |
+
$this->external_merchant_settings_id = $external_merchant_settings_id;
|
62 |
+
$this->fbgraph = $fbgraph;
|
63 |
+
add_action('wp_ajax_ajax_woo_infobanner_post_click', array($this, 'ajax_woo_infobanner_post_click'));
|
64 |
+
add_action('wp_ajax_ajax_woo_infobanner_post_xout', array($this, 'ajax_woo_infobanner_post_xout'));
|
65 |
+
add_action('admin_notices', array($this, 'banner'));
|
66 |
+
add_action('admin_init', array($this, 'dismiss_banner'));
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Post click event when hit primary button.
|
71 |
+
*/
|
72 |
+
function ajax_woo_infobanner_post_click() {
|
73 |
+
WC_Facebookcommerce_Utils::check_woo_ajax_permissions(
|
74 |
+
'post tip click event',
|
75 |
+
true);
|
76 |
+
$tip_info = WC_Facebookcommerce_Utils::get_cached_best_tip();
|
77 |
+
$tip_id = isset($tip_info->tip_id)
|
78 |
+
? $tip_info->tip_id
|
79 |
+
: null;
|
80 |
+
if ($tip_id == null) {
|
81 |
+
WC_Facebookcommerce_Utils::fblog(
|
82 |
+
'Do not have tip id when click, sth went wrong',
|
83 |
+
array('tip_info' => $tip_info),
|
84 |
+
true);
|
85 |
+
} else {
|
86 |
+
WC_Facebookcommerce_Utils::tip_events_log(
|
87 |
+
$tip_id,
|
88 |
+
self::CHANNEL_ID,
|
89 |
+
'click');
|
90 |
+
}
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Post xout event when hit dismiss button.
|
95 |
+
*/
|
96 |
+
function ajax_woo_infobanner_post_xout() {
|
97 |
+
WC_Facebookcommerce_Utils::check_woo_ajax_permissions(
|
98 |
+
'post tip xout event',
|
99 |
+
true);
|
100 |
+
$tip_info = WC_Facebookcommerce_Utils::get_cached_best_tip();
|
101 |
+
$tip_id = isset($tip_info->tip_id)
|
102 |
+
? $tip_info->tip_id
|
103 |
+
: null;
|
104 |
+
// Delete cached tip if xout.
|
105 |
+
update_option('fb_info_banner_last_best_tip', '');
|
106 |
+
if ($tip_id == null) {
|
107 |
+
WC_Facebookcommerce_Utils::fblog(
|
108 |
+
'Do not have tip id when xout, sth went wrong',
|
109 |
+
array('tip_info' => $tip_info),
|
110 |
+
true);
|
111 |
+
} else {
|
112 |
+
WC_Facebookcommerce_Utils::tip_events_log(
|
113 |
+
$tip_id,
|
114 |
+
self::CHANNEL_ID,
|
115 |
+
'xout');
|
116 |
+
}
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Display a info banner on Woocommerce pages.
|
121 |
+
*/
|
122 |
+
public function banner() {
|
123 |
+
$screen = get_current_screen();
|
124 |
+
if (!in_array($screen->base, array('woocommerce_page_wc-reports',
|
125 |
+
'woocommerce_page_wc-settings', 'woocommerce_page_wc-status')) ||
|
126 |
+
$screen->is_network || $screen->action) {
|
127 |
+
return;
|
128 |
+
}
|
129 |
+
|
130 |
+
$tip_info = null;
|
131 |
+
if (!$this->should_query_tip) {
|
132 |
+
// If last query is less than 1 day, either has last best tip or default
|
133 |
+
// tip pass time cap.
|
134 |
+
$tip_info = WC_Facebookcommerce_Utils::get_cached_best_tip();
|
135 |
+
} else {
|
136 |
+
$tip_info = $this->fbgraph->get_tip_info(
|
137 |
+
$this->external_merchant_settings_id);
|
138 |
+
update_option('fb_info_banner_last_query_time', current_time('mysql'));
|
139 |
+
}
|
140 |
+
|
141 |
+
// Not render if no cached best tip, or no best tip returned from FB.
|
142 |
+
if (!$tip_info || ($tip_info === self::FB_NO_TIP_EXISTS)) {
|
143 |
+
// Delete cached tip if should query and get no tip.
|
144 |
+
delete_option('fb_info_banner_last_best_tip');
|
145 |
+
return;
|
146 |
+
} else {
|
147 |
+
// Get tip creatives via API
|
148 |
+
if (is_string($tip_info)) {
|
149 |
+
$tip_info = WC_Facebookcommerce_Utils::decode_json($tip_info);
|
150 |
+
}
|
151 |
+
$tip_title = isset($tip_info->tip_title->__html)
|
152 |
+
? $tip_info->tip_title->__html
|
153 |
+
: null;
|
154 |
+
|
155 |
+
$tip_body = isset($tip_info->tip_body->__html)
|
156 |
+
? $tip_info->tip_body->__html
|
157 |
+
: null;
|
158 |
+
|
159 |
+
$tip_action_link = isset($tip_info->tip_action_link)
|
160 |
+
? $tip_info->tip_action_link
|
161 |
+
: null;
|
162 |
+
|
163 |
+
$tip_action = isset($tip_info->tip_action->__html)
|
164 |
+
? $tip_info->tip_action->__html
|
165 |
+
: null;
|
166 |
+
|
167 |
+
$tip_img_url = isset($tip_info->tip_img_url)
|
168 |
+
? self::DEFAULT_TIP_IMG_URL_PREFIX . $tip_info->tip_img_url
|
169 |
+
: null;
|
170 |
+
|
171 |
+
if ($tip_title == null || $tip_body == null || $tip_action_link == null
|
172 |
+
|| $tip_action == null || $tip_action == null) {
|
173 |
+
WC_Facebookcommerce_Utils::fblog(
|
174 |
+
'Unexpected response from FB for tip info.',
|
175 |
+
array('tip_info' => $tip_info),
|
176 |
+
true);
|
177 |
+
return;
|
178 |
+
}
|
179 |
+
update_option('fb_info_banner_last_best_tip',
|
180 |
+
is_object($tip_info) || is_array($tip_info)
|
181 |
+
? json_encode($tip_info) : $tip_info);
|
182 |
+
}
|
183 |
+
|
184 |
+
$dismiss_url = $this->dismiss_url();
|
185 |
+
echo '<div class="updated fade"><div id="fbinfobanner"><div><img src="'. $tip_img_url .
|
186 |
+
'" class="iconDetails"></div><p class = "tipTitle">' .
|
187 |
+
__('<strong>' . $tip_title . '</strong>', 'facebook-for-woocommerce') . "\n";
|
188 |
+
echo '<p class = "tipContent">'.
|
189 |
+
__($tip_body, 'facebook-for-woocommerce') . '</p>';
|
190 |
+
echo '<p class = "tipButton"><a href="' . $tip_action_link . '" class = "btn" onclick="fb_woo_infobanner_post_click(); return true;" title="' .
|
191 |
+
__('Click and redirect.', 'facebook-for-woocommerce').
|
192 |
+
'"> ' . __($tip_action, 'facebook-for-woocommerce') . '</a>' .
|
193 |
+
'<a href="' . esc_url($dismiss_url). '" class = "btn dismiss grey" onclick="fb_woo_infobanner_post_xout(); return true;" title="' .
|
194 |
+
__('Dismiss this notice.', 'facebook-for-woocommerce').
|
195 |
+
'"> ' . __('Dismiss', 'facebook-for-woocommerce') . '</a></p></div></div>';
|
196 |
+
}
|
197 |
+
|
198 |
+
/**
|
199 |
+
* Returns the url that the user clicks to remove the info banner
|
200 |
+
* @return (string)
|
201 |
+
*/
|
202 |
+
private function dismiss_url() {
|
203 |
+
$url = admin_url('admin.php');
|
204 |
+
|
205 |
+
$url = add_query_arg(array(
|
206 |
+
'page' => 'wc-settings',
|
207 |
+
'tab' => 'integration',
|
208 |
+
'wc-notice' => 'dismiss-fb-info-banner',
|
209 |
+
), $url);
|
210 |
+
|
211 |
+
return wp_nonce_url($url, 'woocommerce_info_banner_dismiss');
|
212 |
+
}
|
213 |
+
|
214 |
+
/**
|
215 |
+
* Handles the dismiss action so that the banner can be permanently hidden
|
216 |
+
* during time threshold
|
217 |
+
*/
|
218 |
+
public function dismiss_banner() {
|
219 |
+
if (!isset($_GET['wc-notice'])) {
|
220 |
+
return;
|
221 |
+
}
|
222 |
+
|
223 |
+
if ('dismiss-fb-info-banner' !== $_GET['wc-notice']) {
|
224 |
+
return;
|
225 |
+
}
|
226 |
+
|
227 |
+
if (!check_admin_referer('woocommerce_info_banner_dismiss')) {
|
228 |
+
return;
|
229 |
+
}
|
230 |
+
|
231 |
+
// Delete cached tip if xout.
|
232 |
+
delete_option('fb_info_banner_last_best_tip');
|
233 |
+
if (wp_get_referer()) {
|
234 |
+
wp_safe_redirect(wp_get_referer());
|
235 |
+
} else {
|
236 |
+
wp_safe_redirect(admin_url('admin.php?page=wc-settings&tab=integration'));
|
237 |
+
}
|
238 |
+
}
|
239 |
+
}
|
240 |
+
|
241 |
+
endif;
|
includes/fbproduct.php
ADDED
@@ -0,0 +1,752 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* @package FacebookCommerce
|
9 |
+
*/
|
10 |
+
|
11 |
+
if (! defined('ABSPATH')) {
|
12 |
+
exit;
|
13 |
+
}
|
14 |
+
|
15 |
+
if (!class_exists('WC_Facebookcommerce_Utils')) {
|
16 |
+
include_once 'includes/fbutils.php';
|
17 |
+
}
|
18 |
+
|
19 |
+
if (! class_exists('WC_Facebook_Product')) :
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Custom FB Product proxy class
|
23 |
+
*/
|
24 |
+
class WC_Facebook_Product {
|
25 |
+
|
26 |
+
// Should match facebook-commerce.php while we migrate that code over
|
27 |
+
// to this object.
|
28 |
+
const FB_PRODUCT_DESCRIPTION = 'fb_product_description';
|
29 |
+
const FB_PRODUCT_PRICE = 'fb_product_price';
|
30 |
+
const FB_PRODUCT_IMAGE = 'fb_product_image';
|
31 |
+
const FB_VARIANT_IMAGE = 'fb_image';
|
32 |
+
const FB_VISIBILITY = 'fb_visibility';
|
33 |
+
|
34 |
+
const MIN_DATE_1 = '1970-01-29';
|
35 |
+
const MIN_DATE_2 = '1970-01-30';
|
36 |
+
const MAX_DATE = '2038-01-17';
|
37 |
+
const MAX_TIME = 'T23:59+00:00';
|
38 |
+
const MIN_TIME = 'T00:00+00:00';
|
39 |
+
|
40 |
+
static $use_checkout_url = array('simple' => 1, 'variable' => 1, 'variation' => 1);
|
41 |
+
|
42 |
+
public function __construct(
|
43 |
+
$wpid, $parent_product = null) {
|
44 |
+
$this->id = $wpid;
|
45 |
+
$this->fb_description = '';
|
46 |
+
$this->fb_visibility = get_post_meta($wpid, self::FB_VISIBILITY, true);
|
47 |
+
$this->woo_product = wc_get_product($wpid);
|
48 |
+
$this->gallery_urls = null;
|
49 |
+
$this->fb_use_parent_image = null;
|
50 |
+
$this->fb_price = 0;
|
51 |
+
$this->main_description = '';
|
52 |
+
$this->sync_short_description = get_option('fb_sync_short_description', false);
|
53 |
+
|
54 |
+
// Variable products should use some data from the parent_product
|
55 |
+
// For performance reasons, that data shouldn't be regenerated every time.
|
56 |
+
if ($parent_product) {
|
57 |
+
$this->gallery_urls = $parent_product->get_gallery_urls();
|
58 |
+
$this->fb_use_parent_image = $parent_product->get_use_parent_image();
|
59 |
+
$this->main_description = WC_Facebookcommerce_Utils::clean_string(
|
60 |
+
$parent_product->get_description());
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
public function exists() {
|
65 |
+
return ($this->woo_product !== null && $this->woo_product !== false);
|
66 |
+
}
|
67 |
+
|
68 |
+
// Fall back to calling method on $woo_product
|
69 |
+
public function __call($function, $args) {
|
70 |
+
if ($this->woo_product) {
|
71 |
+
return call_user_func_array(array($this->woo_product, $function), $args);
|
72 |
+
} else {
|
73 |
+
$e = new Exception();
|
74 |
+
$backtrace = var_export($e->getTraceAsString(), true);
|
75 |
+
WC_Facebookcommerce_Utils::fblog(
|
76 |
+
"Calling $function on Null Woo Object. Trace:\n".$backtrace,
|
77 |
+
array(),
|
78 |
+
true);
|
79 |
+
return null;
|
80 |
+
}
|
81 |
+
}
|
82 |
+
|
83 |
+
public function get_gallery_urls() {
|
84 |
+
if ($this->gallery_urls === null) {
|
85 |
+
if (is_callable(array($this, 'get_gallery_image_ids'))) {
|
86 |
+
$image_ids = $this->get_gallery_image_ids();
|
87 |
+
} else {
|
88 |
+
$image_ids = $this->get_gallery_attachment_ids();
|
89 |
+
}
|
90 |
+
$gallery_urls = array();
|
91 |
+
foreach ($image_ids as $image_id) {
|
92 |
+
$image_url = wp_get_attachment_url($image_id);
|
93 |
+
if (!empty($image_url)) {
|
94 |
+
array_push($gallery_urls,
|
95 |
+
WC_Facebookcommerce_Utils::make_url($image_url));
|
96 |
+
}
|
97 |
+
}
|
98 |
+
$this->gallery_urls = array_filter($gallery_urls);
|
99 |
+
}
|
100 |
+
|
101 |
+
return $this->gallery_urls;
|
102 |
+
}
|
103 |
+
|
104 |
+
public function get_post_data() {
|
105 |
+
if (is_callable('get_post')) {
|
106 |
+
return get_post($this->id);
|
107 |
+
} else {
|
108 |
+
return $this->get_post_data();
|
109 |
+
}
|
110 |
+
}
|
111 |
+
|
112 |
+
public function get_fb_price() {
|
113 |
+
// Cache the price in this object in case of multiple calls.
|
114 |
+
if ($this->fb_price) {
|
115 |
+
return $this->fb_price;
|
116 |
+
}
|
117 |
+
|
118 |
+
$price = get_post_meta(
|
119 |
+
$this->id,
|
120 |
+
self::FB_PRODUCT_PRICE,
|
121 |
+
true);
|
122 |
+
|
123 |
+
if (is_numeric($price)) {
|
124 |
+
return intval(round($price * 100));
|
125 |
+
}
|
126 |
+
|
127 |
+
// If product is composite product, we rely on their pricing.
|
128 |
+
if (class_exists('WC_Product_Composite')
|
129 |
+
&& $this->woo_product->get_type() === 'composite') {
|
130 |
+
$price = get_option('woocommerce_tax_display_shop') === 'incl'
|
131 |
+
? $this->woo_product->get_composite_price_including_tax()
|
132 |
+
: $this->woo_product->get_composite_price();
|
133 |
+
$this->fb_price = intval(round($price * 100));
|
134 |
+
return $this->fb_price;
|
135 |
+
}
|
136 |
+
|
137 |
+
// Get regular price: regular price doesn't include sales
|
138 |
+
$regular_price = floatval($this->get_regular_price());
|
139 |
+
|
140 |
+
// If it's a bookable product, the normal price is null/0.
|
141 |
+
if (!$regular_price &&
|
142 |
+
class_exists('WC_Product_Booking') &&
|
143 |
+
is_wc_booking_product($this)) {
|
144 |
+
$product = new WC_Product_Booking($this->woo_product);
|
145 |
+
$regular_price = $product->get_display_cost();
|
146 |
+
}
|
147 |
+
|
148 |
+
// Get regular price plus tax, if it's set to display and taxable
|
149 |
+
// whether price includes tax is based on 'woocommerce_tax_display_shop'
|
150 |
+
$price = $this->get_price_plus_tax($regular_price);
|
151 |
+
$this->fb_price = intval(round($price * 100));
|
152 |
+
return $this->fb_price;
|
153 |
+
}
|
154 |
+
|
155 |
+
public function get_all_image_urls() {
|
156 |
+
$image_urls = array();
|
157 |
+
$parent_image_id = $this->get_parent_image_id();
|
158 |
+
$image_url = wp_get_attachment_url(
|
159 |
+
($parent_image_id) ?: $this->woo_product->get_image_id());
|
160 |
+
|
161 |
+
if ($image_url) {
|
162 |
+
$image_url = WC_Facebookcommerce_Utils::make_url($image_url);
|
163 |
+
array_push($image_urls, $image_url);
|
164 |
+
}
|
165 |
+
|
166 |
+
// For variable products, add the variation specific image.
|
167 |
+
if ($parent_image_id) {
|
168 |
+
$image_url2 = wp_get_attachment_url($this->woo_product->get_image_id());
|
169 |
+
$image_url2 = WC_Facebookcommerce_Utils::make_url($image_url2);
|
170 |
+
if ($image_url != $image_url2) {
|
171 |
+
// A Checkbox toggles which image is primary.
|
172 |
+
// Default to variant specific image as primary.
|
173 |
+
if ($this->fb_use_parent_image) {
|
174 |
+
array_push($image_urls, $image_url2);
|
175 |
+
} else {
|
176 |
+
array_unshift($image_urls, $image_url2);
|
177 |
+
}
|
178 |
+
}
|
179 |
+
}
|
180 |
+
|
181 |
+
$gallery_urls = $this->get_gallery_urls();
|
182 |
+
$image_urls = array_merge($image_urls, $gallery_urls);
|
183 |
+
$image_urls = array_filter($image_urls);
|
184 |
+
|
185 |
+
// If there are no images, create a placeholder image.
|
186 |
+
if (empty($image_urls)) {
|
187 |
+
$name = urlencode(strip_tags($this->woo_product->get_title()));
|
188 |
+
$image_url = 'https://placeholdit.imgix.net/~text?txtsize=33&txt='
|
189 |
+
. $name . '&w=530&h=530'; // TODO: BETTER PLACEHOLDER
|
190 |
+
return array($image_url);
|
191 |
+
}
|
192 |
+
|
193 |
+
$image_override = get_post_meta($this->id, self::FB_PRODUCT_IMAGE, true);
|
194 |
+
if ($image_override) {
|
195 |
+
array_unshift($image_urls, $image_override);
|
196 |
+
$image_urls = array_unique($image_urls);
|
197 |
+
}
|
198 |
+
|
199 |
+
return $image_urls;
|
200 |
+
}
|
201 |
+
|
202 |
+
// Returns the parent image id for variable products only.
|
203 |
+
public function get_parent_image_id() {
|
204 |
+
if (WC_Facebookcommerce_Utils::is_variation_type($this->woo_product->get_type())) {
|
205 |
+
$parent_data = $this->get_parent_data();
|
206 |
+
return $parent_data['image_id'];
|
207 |
+
}
|
208 |
+
return null;
|
209 |
+
}
|
210 |
+
|
211 |
+
public function set_description($description) {
|
212 |
+
$description = stripslashes(
|
213 |
+
WC_Facebookcommerce_Utils::clean_string($description));
|
214 |
+
$this->fb_description = $description;
|
215 |
+
update_post_meta(
|
216 |
+
$this->id,
|
217 |
+
self::FB_PRODUCT_DESCRIPTION,
|
218 |
+
$description);
|
219 |
+
}
|
220 |
+
|
221 |
+
public function set_product_image($image) {
|
222 |
+
if ($image !== null && strlen($image) !== 0) {
|
223 |
+
$image = WC_Facebookcommerce_Utils::clean_string($image);
|
224 |
+
$image = WC_Facebookcommerce_Utils::make_url($image);
|
225 |
+
update_post_meta(
|
226 |
+
$this->id,
|
227 |
+
self::FB_PRODUCT_IMAGE,
|
228 |
+
$image);
|
229 |
+
}
|
230 |
+
}
|
231 |
+
|
232 |
+
public function set_price($price) {
|
233 |
+
if (is_numeric($price)) {
|
234 |
+
$this->fb_price = intval(round($price * 100));
|
235 |
+
update_post_meta(
|
236 |
+
$this->id,
|
237 |
+
self::FB_PRODUCT_PRICE,
|
238 |
+
$price);
|
239 |
+
} else {
|
240 |
+
delete_post_meta(
|
241 |
+
$this->id,
|
242 |
+
self::FB_PRODUCT_PRICE);
|
243 |
+
}
|
244 |
+
}
|
245 |
+
|
246 |
+
public function get_use_parent_image() {
|
247 |
+
if ($this->fb_use_parent_image === null) {
|
248 |
+
$variant_image_setting =
|
249 |
+
get_post_meta($this->id, self::FB_VARIANT_IMAGE, true);
|
250 |
+
$this->fb_use_parent_image = ($variant_image_setting) ? true : false;
|
251 |
+
}
|
252 |
+
return $this->fb_use_parent_image;
|
253 |
+
}
|
254 |
+
|
255 |
+
public function set_use_parent_image($setting) {
|
256 |
+
$this->fb_use_parent_image = ($setting == 'yes');
|
257 |
+
update_post_meta(
|
258 |
+
$this->id,
|
259 |
+
self::FB_VARIANT_IMAGE,
|
260 |
+
$this->fb_use_parent_image);
|
261 |
+
}
|
262 |
+
|
263 |
+
public function get_fb_description() {
|
264 |
+
if ($this->fb_description) {
|
265 |
+
return $this->fb_description;
|
266 |
+
}
|
267 |
+
|
268 |
+
$description = get_post_meta(
|
269 |
+
$this->id,
|
270 |
+
self::FB_PRODUCT_DESCRIPTION,
|
271 |
+
true);
|
272 |
+
|
273 |
+
if ($description) {
|
274 |
+
return $description;
|
275 |
+
}
|
276 |
+
|
277 |
+
if (WC_Facebookcommerce_Utils::is_variation_type($this->woo_product->get_type())) {
|
278 |
+
$description = WC_Facebookcommerce_Utils::clean_string(
|
279 |
+
$this->get_description());
|
280 |
+
if ($description) {
|
281 |
+
return $description;
|
282 |
+
}
|
283 |
+
if ($this->main_description) {
|
284 |
+
return $this->main_description;
|
285 |
+
}
|
286 |
+
}
|
287 |
+
|
288 |
+
$post = $this->get_post_data();
|
289 |
+
|
290 |
+
$post_content = WC_Facebookcommerce_Utils::clean_string(
|
291 |
+
$post->post_content);
|
292 |
+
$post_excerpt = WC_Facebookcommerce_Utils::clean_string(
|
293 |
+
$post->post_excerpt);
|
294 |
+
$post_title = WC_Facebookcommerce_Utils::clean_string(
|
295 |
+
$post->post_title);
|
296 |
+
|
297 |
+
// Sanitize description
|
298 |
+
if ($post_content) {
|
299 |
+
$description = $post_content;
|
300 |
+
}
|
301 |
+
if ($this->sync_short_description || ($description == '' && $post_excerpt)) {
|
302 |
+
$description = $post_excerpt;
|
303 |
+
}
|
304 |
+
if ($description == '') {
|
305 |
+
$description = $post_title;
|
306 |
+
}
|
307 |
+
|
308 |
+
return $description;
|
309 |
+
}
|
310 |
+
|
311 |
+
public function add_sale_price($product_data) {
|
312 |
+
// initialize sale date and sale_price
|
313 |
+
$product_data['sale_price_start_date'] = self::MIN_DATE_1 . self::MIN_TIME;
|
314 |
+
$product_data['sale_price_end_date'] = self::MIN_DATE_2 . self::MAX_TIME;
|
315 |
+
$product_data['sale_price'] = $product_data['price'];
|
316 |
+
|
317 |
+
$sale_price = $this->woo_product->get_sale_price();
|
318 |
+
// check if sale exist
|
319 |
+
if (!is_numeric($sale_price)) {
|
320 |
+
return $product_data;
|
321 |
+
}
|
322 |
+
$sale_price =
|
323 |
+
intval(round($this->get_price_plus_tax($sale_price) * 100));
|
324 |
+
|
325 |
+
$sale_start =
|
326 |
+
($date = get_post_meta($this->id, '_sale_price_dates_from', true))
|
327 |
+
? date_i18n('Y-m-d', $date) . self::MIN_TIME
|
328 |
+
: self::MIN_DATE_1 . self::MIN_TIME;
|
329 |
+
|
330 |
+
$sale_end =
|
331 |
+
($date = get_post_meta($this->id, '_sale_price_dates_to', true))
|
332 |
+
? date_i18n('Y-m-d', $date) . self::MAX_TIME
|
333 |
+
: self::MAX_DATE . self::MAX_TIME;
|
334 |
+
|
335 |
+
// check if sale is expired and sale time range is valid
|
336 |
+
$product_data['sale_price_start_date'] = $sale_start;
|
337 |
+
$product_data['sale_price_end_date'] = $sale_end;
|
338 |
+
$product_data['sale_price'] = $sale_price;
|
339 |
+
return $product_data;
|
340 |
+
}
|
341 |
+
|
342 |
+
public function is_hidden() {
|
343 |
+
$wpid = $this->id;
|
344 |
+
if (WC_Facebookcommerce_Utils::is_variation_type($this->get_type())) {
|
345 |
+
$wpid = $this->get_parent_id();
|
346 |
+
}
|
347 |
+
$hidden_from_catalog = has_term(
|
348 |
+
'exclude-from-catalog',
|
349 |
+
'product_visibility',
|
350 |
+
$wpid);
|
351 |
+
$hidden_from_search = has_term(
|
352 |
+
'exclude-from-search',
|
353 |
+
'product_visibility',
|
354 |
+
$wpid);
|
355 |
+
// fb_visibility === '': after initial sync by feed
|
356 |
+
// fb_visibility === false: set hidden on FB metadata
|
357 |
+
// Explicitly check whether flip 'hide' before.
|
358 |
+
return ($hidden_from_catalog && $hidden_from_search) ||
|
359 |
+
$this->fb_visibility === false || !$this->get_fb_price();
|
360 |
+
}
|
361 |
+
|
362 |
+
public function get_price_plus_tax($price) {
|
363 |
+
$woo_product = $this->woo_product;
|
364 |
+
// // wc_get_price_including_tax exist for Woo > 2.7
|
365 |
+
if (function_exists('wc_get_price_including_tax')) {
|
366 |
+
$args = array( 'qty' => 1, 'price' => $price);
|
367 |
+
return get_option('woocommerce_tax_display_shop') === 'incl'
|
368 |
+
? wc_get_price_including_tax($woo_product, $args)
|
369 |
+
: wc_get_price_excluding_tax($woo_product, $args);
|
370 |
+
} else {
|
371 |
+
return get_option('woocommerce_tax_display_shop') === 'incl'
|
372 |
+
? $woo_product->get_price_including_tax(1, $price)
|
373 |
+
: $woo_product->get_price_excluding_tax(1, $price);
|
374 |
+
}
|
375 |
+
}
|
376 |
+
|
377 |
+
public function get_grouped_product_option_names($key, $option_values) {
|
378 |
+
// Convert all slug_names in $option_values into the visible names that
|
379 |
+
// advertisers have set to be the display names for a given attribute value
|
380 |
+
$terms = get_the_terms($this->id, $key);
|
381 |
+
return array_map(
|
382 |
+
function ($slug_name) use ($terms) {
|
383 |
+
foreach ($terms as $term) {
|
384 |
+
if ($term->slug === $slug_name) {
|
385 |
+
return $term->name;
|
386 |
+
}
|
387 |
+
}
|
388 |
+
return $slug_name;
|
389 |
+
},
|
390 |
+
$option_values);
|
391 |
+
}
|
392 |
+
|
393 |
+
public function get_variant_option_name($label, $default_value) {
|
394 |
+
// For the given label, get the Visible name rather than the slug
|
395 |
+
$meta = get_post_meta($this->id, $label, true);
|
396 |
+
$attribute_name = str_replace('attribute_', '', $label);
|
397 |
+
$term = get_term_by('slug', $meta, $attribute_name);
|
398 |
+
return $term && $term->name ? $term->name : $default_value;
|
399 |
+
}
|
400 |
+
|
401 |
+
public function update_visibility($is_product_page, $visible_box_checked) {
|
402 |
+
$visibility = get_post_meta($this->id, self::FB_VISIBILITY, true);
|
403 |
+
if ($visibility && !$is_product_page) {
|
404 |
+
// If the product was previously set to visible, keep it as visible
|
405 |
+
// (unless we're on the product page)
|
406 |
+
$this->fb_visibility = $visibility;
|
407 |
+
} else {
|
408 |
+
// If the product is not visible OR we're on the product page,
|
409 |
+
// then update the visibility as needed.
|
410 |
+
$this->fb_visibility = $visible_box_checked ? true : false;
|
411 |
+
update_post_meta($this->id, self::FB_VISIBILITY, $this->fb_visibility);
|
412 |
+
}
|
413 |
+
}
|
414 |
+
|
415 |
+
// wrapper function to find item_id for default variation
|
416 |
+
function find_matching_product_variation() {
|
417 |
+
if (is_callable(array($this, 'get_default_attributes'))) {
|
418 |
+
$default_attributes = $this->get_default_attributes();
|
419 |
+
} else {
|
420 |
+
$default_attributes = $this->get_variation_default_attributes();
|
421 |
+
}
|
422 |
+
|
423 |
+
if (!$default_attributes) {
|
424 |
+
return;
|
425 |
+
}
|
426 |
+
foreach ($default_attributes as $key => $value) {
|
427 |
+
if (strncmp($key, 'attribute_', strlen('attribute_')) === 0) {
|
428 |
+
continue;
|
429 |
+
}
|
430 |
+
unset($default_attributes[$key]);
|
431 |
+
$default_attributes[sprintf('attribute_%s', $key)] = $value;
|
432 |
+
}
|
433 |
+
if (class_exists('WC_Data_Store')) {
|
434 |
+
// for >= woo 3.0.0
|
435 |
+
$data_store = WC_Data_Store::load('product');
|
436 |
+
return
|
437 |
+
$data_store->find_matching_product_variation(
|
438 |
+
$this,
|
439 |
+
$default_attributes);
|
440 |
+
} else {
|
441 |
+
return $this->get_matching_variation($default_attributes);
|
442 |
+
}
|
443 |
+
}
|
444 |
+
|
445 |
+
/**
|
446 |
+
* Assemble product payload for POST
|
447 |
+
**/
|
448 |
+
function prepare_product(
|
449 |
+
$retailer_id = null,
|
450 |
+
$prepare_for_product_feed = false) {
|
451 |
+
if (!$retailer_id) {
|
452 |
+
$retailer_id =
|
453 |
+
WC_Facebookcommerce_Utils::get_fb_retailer_id($this);
|
454 |
+
}
|
455 |
+
$image_urls = $this->get_all_image_urls();
|
456 |
+
|
457 |
+
// Replace Wordpress sanitization's ampersand with a real ampersand.
|
458 |
+
$product_url = str_replace(
|
459 |
+
'&%3B',
|
460 |
+
'&',
|
461 |
+
html_entity_decode($this->get_permalink()));
|
462 |
+
|
463 |
+
// Use product_url for external/bundle product setting.
|
464 |
+
$product_type = $this->get_type();
|
465 |
+
if (!$product_type || !isset(self::$use_checkout_url[$product_type])) {
|
466 |
+
$checkout_url = $product_url;
|
467 |
+
} else if (wc_get_cart_url()) {
|
468 |
+
$char = '?';
|
469 |
+
// Some merchant cart pages are actually a querystring
|
470 |
+
if (strpos(wc_get_cart_url(), '?') !== false) {
|
471 |
+
$char = '&';
|
472 |
+
}
|
473 |
+
|
474 |
+
$checkout_url = WC_Facebookcommerce_Utils::make_url(
|
475 |
+
wc_get_cart_url() . $char);
|
476 |
+
|
477 |
+
if (WC_Facebookcommerce_Utils::is_variation_type($this->get_type())) {
|
478 |
+
$query_data = array(
|
479 |
+
'add-to-cart' => $this->get_parent_id(),
|
480 |
+
'variation_id' => $this->get_id()
|
481 |
+
);
|
482 |
+
|
483 |
+
$query_data = array_merge(
|
484 |
+
$query_data,
|
485 |
+
$this->get_variation_attributes());
|
486 |
+
|
487 |
+
} else {
|
488 |
+
$query_data = array(
|
489 |
+
'add-to-cart' => $this->get_id()
|
490 |
+
);
|
491 |
+
}
|
492 |
+
|
493 |
+
$checkout_url = $checkout_url . http_build_query($query_data);
|
494 |
+
|
495 |
+
} else {
|
496 |
+
$checkout_url = null;
|
497 |
+
}
|
498 |
+
|
499 |
+
$id = $this->get_id();
|
500 |
+
if (WC_Facebookcommerce_Utils::is_variation_type($this->get_type())) {
|
501 |
+
$id = $this->get_parent_id();
|
502 |
+
}
|
503 |
+
$categories =
|
504 |
+
WC_Facebookcommerce_Utils::get_product_categories($id);
|
505 |
+
$brand = get_the_term_list($id, 'product_brand', '', ', ');
|
506 |
+
$brand = is_wp_error($brand) || !$brand
|
507 |
+
? WC_Facebookcommerce_Utils::get_store_name()
|
508 |
+
: WC_Facebookcommerce_Utils::clean_string($brand);
|
509 |
+
|
510 |
+
$product_data = array(
|
511 |
+
'name' => WC_Facebookcommerce_Utils::clean_string(
|
512 |
+
$this->get_title()),
|
513 |
+
'description' => $this->get_fb_description(),
|
514 |
+
'image_url' => $image_urls[0], // The array can't be empty.
|
515 |
+
'additional_image_urls' => array_filter($image_urls),
|
516 |
+
'url'=> $product_url,
|
517 |
+
'category' => $categories['categories'],
|
518 |
+
'brand' => $brand,
|
519 |
+
'retailer_id' => $retailer_id,
|
520 |
+
'price' => $this->get_fb_price(),
|
521 |
+
'currency' => get_woocommerce_currency(),
|
522 |
+
'availability' => $this->is_in_stock() ? 'in stock' :
|
523 |
+
'out of stock',
|
524 |
+
'visibility' => !$this->is_hidden()
|
525 |
+
? 'published'
|
526 |
+
: 'staging'
|
527 |
+
);
|
528 |
+
|
529 |
+
// Only use checkout URLs if they exist.
|
530 |
+
if ($checkout_url) {
|
531 |
+
$product_data['checkout_url'] = $checkout_url;
|
532 |
+
}
|
533 |
+
|
534 |
+
$product_data = $this->add_sale_price($product_data);
|
535 |
+
|
536 |
+
// IF using WPML, set the product to staging unless it is in the
|
537 |
+
// default language. WPML >= 3.2 Supported.
|
538 |
+
if (defined('ICL_LANGUAGE_CODE')) {
|
539 |
+
if (class_exists('WC_Facebook_WPML_Injector') && WC_Facebook_WPML_Injector::should_hide($id)) {
|
540 |
+
$product_data['visibility'] = 'staging';
|
541 |
+
}
|
542 |
+
}
|
543 |
+
|
544 |
+
// Exclude variations that are "virtual" products from export to Facebook &&
|
545 |
+
// No Visibility Option for Variations
|
546 |
+
if (true === $this->get_virtual()) {
|
547 |
+
$product_data['visibility'] = 'staging';
|
548 |
+
}
|
549 |
+
|
550 |
+
if (!$prepare_for_product_feed) {
|
551 |
+
$this->prepare_variants_for_item($product_data);
|
552 |
+
} else if (
|
553 |
+
WC_Facebookcommerce_Utils::is_all_caps($product_data['description'])
|
554 |
+
) {
|
555 |
+
$product_data['description'] =
|
556 |
+
mb_strtolower($product_data['description']);
|
557 |
+
}
|
558 |
+
|
559 |
+
/**
|
560 |
+
* Filters the generated product data.
|
561 |
+
*
|
562 |
+
* @param int $id Woocommerce product id
|
563 |
+
* @param array $product_data An array of product data
|
564 |
+
*/
|
565 |
+
return apply_filters(
|
566 |
+
"facebook_for_woocommerce_integration_prepare_product",
|
567 |
+
$product_data,
|
568 |
+
$id);
|
569 |
+
}
|
570 |
+
|
571 |
+
|
572 |
+
/**
|
573 |
+
* Modify Woo variant/taxonomies to be FB compatible
|
574 |
+
**/
|
575 |
+
public function prepare_variants_for_item(&$product_data) {
|
576 |
+
if (!WC_Facebookcommerce_Utils::is_variation_type(
|
577 |
+
$this->get_type())) {
|
578 |
+
return;
|
579 |
+
}
|
580 |
+
|
581 |
+
$attributes = $this->get_variation_attributes();
|
582 |
+
if (!$attributes) {
|
583 |
+
return;
|
584 |
+
}
|
585 |
+
|
586 |
+
$variant_names = array_keys($attributes);
|
587 |
+
$variant_array = array();
|
588 |
+
|
589 |
+
// Loop through variants (size, color, etc) if they exist
|
590 |
+
// For each product field type, pull the single variant
|
591 |
+
foreach ($variant_names as $orig_name) {
|
592 |
+
// Retrieve label name for attribute
|
593 |
+
$label = wc_attribute_label($orig_name, $this);
|
594 |
+
|
595 |
+
// Clean up variant name (e.g. pa_color should be color)
|
596 |
+
// Replace "custom_data:foo" with just "foo" so we can use the key
|
597 |
+
// Product item API expects "custom_data" instead of "custom_data:foo"
|
598 |
+
$new_name = str_replace(
|
599 |
+
'custom_data:',
|
600 |
+
'',
|
601 |
+
WC_Facebookcommerce_Utils::sanitize_variant_name($orig_name));
|
602 |
+
|
603 |
+
// Sometimes WC returns an array, sometimes it's an assoc array, depending
|
604 |
+
// on what type of taxonomy it's using. array_values will guarantee we
|
605 |
+
// only get a flat array of values.
|
606 |
+
$options = $this->get_variant_option_name(
|
607 |
+
$label,
|
608 |
+
$attributes[$orig_name]);
|
609 |
+
if (isset($options)) {
|
610 |
+
if (is_array($options)) {
|
611 |
+
$option_values = array_values($options);
|
612 |
+
} else {
|
613 |
+
$option_values = array($options);
|
614 |
+
// If this attribute has value 'any', options will be empty strings
|
615 |
+
// Redirect to product page to select variants.
|
616 |
+
// Reset checkout url since checkout_url (build from query data will
|
617 |
+
// be invalid in this case.
|
618 |
+
if (count($option_values) === 1 && empty($option_values[0])) {
|
619 |
+
$option_values[0] = 'any';
|
620 |
+
$product_data['checkout_url'] = $product_data['url'];
|
621 |
+
}
|
622 |
+
}
|
623 |
+
if ($new_name === WC_Facebookcommerce_Utils::FB_VARIANT_GENDER) {
|
624 |
+
// If we can't validate the gender, this will be null.
|
625 |
+
$product_data[$new_name] =
|
626 |
+
WC_Facebookcommerce_Utils::validateGender($option_values[0]);
|
627 |
+
}
|
628 |
+
|
629 |
+
switch ($new_name) {
|
630 |
+
case WC_Facebookcommerce_Utils::FB_VARIANT_SIZE:
|
631 |
+
case WC_Facebookcommerce_Utils::FB_VARIANT_COLOR:
|
632 |
+
case WC_Facebookcommerce_Utils::FB_VARIANT_PATTERN:
|
633 |
+
array_push($variant_array, array(
|
634 |
+
'product_field' => $new_name,
|
635 |
+
'label' => $label,
|
636 |
+
'options' => $option_values,
|
637 |
+
));
|
638 |
+
$product_data[$new_name] = $option_values[0];
|
639 |
+
break;
|
640 |
+
case WC_Facebookcommerce_Utils::FB_VARIANT_GENDER:
|
641 |
+
// If we can't validate the GENDER field, we'll fall through to the
|
642 |
+
// default case and set the gender into custom data.
|
643 |
+
if ($product_data[$new_name]) {
|
644 |
+
array_push($variant_array, array(
|
645 |
+
'product_field' => $new_name,
|
646 |
+
'label' => $label,
|
647 |
+
'options' => $option_values,
|
648 |
+
));
|
649 |
+
break;
|
650 |
+
}
|
651 |
+
|
652 |
+
default:
|
653 |
+
// This is for any custom_data.
|
654 |
+
if (!isset($product_data['custom_data'])) {
|
655 |
+
$product_data['custom_data'] = array();
|
656 |
+
}
|
657 |
+
$product_data['custom_data'][$new_name]
|
658 |
+
= urldecode($option_values[0]);
|
659 |
+
break;
|
660 |
+
}
|
661 |
+
} else {
|
662 |
+
WC_Facebookcommerce_Utils::log(
|
663 |
+
$this->get_id() . ": No options for " . $orig_name);
|
664 |
+
continue;
|
665 |
+
}
|
666 |
+
}
|
667 |
+
return $variant_array;
|
668 |
+
}
|
669 |
+
|
670 |
+
/**
|
671 |
+
* Modify Woo variant/taxonomies for variable products to be FB compatible
|
672 |
+
**/
|
673 |
+
public function prepare_variants_for_group($feed_data = false) {
|
674 |
+
if (!WC_Facebookcommerce_Utils::is_variable_type(
|
675 |
+
$this->get_type())) {
|
676 |
+
WC_Facebookcommerce_Utils::fblog(
|
677 |
+
"prepare_variants_for_group called on non-variable product");
|
678 |
+
return;
|
679 |
+
}
|
680 |
+
|
681 |
+
$variation_attributes = $this->get_variation_attributes();
|
682 |
+
if (!$variation_attributes) {
|
683 |
+
return;
|
684 |
+
}
|
685 |
+
$final_variants = array();
|
686 |
+
|
687 |
+
$attrs = array_keys($this->get_attributes());
|
688 |
+
foreach ($attrs as $name) {
|
689 |
+
$label = wc_attribute_label($name, $this);
|
690 |
+
|
691 |
+
if (taxonomy_is_product_attribute($name)) {
|
692 |
+
$key = $name;
|
693 |
+
} else {
|
694 |
+
// variation_attributes keys are labels for custom attrs for some reason
|
695 |
+
$key = $label;
|
696 |
+
}
|
697 |
+
|
698 |
+
if (!$key) {
|
699 |
+
WC_Facebookcommerce_Utils::fblog(
|
700 |
+
"Critical error: can't get attribute name or label!");
|
701 |
+
return;
|
702 |
+
}
|
703 |
+
|
704 |
+
if (isset($variation_attributes[$key])) {
|
705 |
+
// Array of the options (e.g. small, medium, large)
|
706 |
+
$option_values = $variation_attributes[$key];
|
707 |
+
} else {
|
708 |
+
WC_Facebookcommerce_Utils::log(
|
709 |
+
$this->get_id() . ": No options for " . $name);
|
710 |
+
continue; // Skip variations without valid attribute options
|
711 |
+
}
|
712 |
+
|
713 |
+
// If this is a wc_product_variable, check default attrib.
|
714 |
+
// If it's being used, show it as the first option on Facebook.
|
715 |
+
$first_option = $this->get_variation_default_attribute($key);
|
716 |
+
if ($first_option) {
|
717 |
+
$idx = array_search($first_option, $option_values);
|
718 |
+
unset($option_values[$idx]);
|
719 |
+
array_unshift($option_values, $first_option);
|
720 |
+
}
|
721 |
+
|
722 |
+
if (
|
723 |
+
function_exists('taxonomy_is_product_attribute') &&
|
724 |
+
taxonomy_is_product_attribute($name)
|
725 |
+
) {
|
726 |
+
$option_values = $this->get_grouped_product_option_names(
|
727 |
+
$key,
|
728 |
+
$option_values);
|
729 |
+
}
|
730 |
+
|
731 |
+
// https://developers.facebook.com/docs/marketing-api/reference/product-variant/
|
732 |
+
// For API approach, product_field need to start with 'custom_data:'
|
733 |
+
// Clean up variant name (e.g. pa_color should be color)
|
734 |
+
$name = WC_Facebookcommerce_Utils::sanitize_variant_name($name);
|
735 |
+
|
736 |
+
// For feed uploading, product field should remove prefix 'custom_data:'
|
737 |
+
if ($feed_data) {
|
738 |
+
$name = str_replace('custom_data:', '', $name);
|
739 |
+
}
|
740 |
+
array_push($final_variants, array(
|
741 |
+
'product_field' => $name,
|
742 |
+
'label' => $label,
|
743 |
+
'options' => $option_values,
|
744 |
+
));
|
745 |
+
}
|
746 |
+
|
747 |
+
return $final_variants;
|
748 |
+
|
749 |
+
}
|
750 |
+
}
|
751 |
+
|
752 |
+
endif;
|
includes/fbproductfeed.php
ADDED
@@ -0,0 +1,367 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* @package FacebookCommerce
|
9 |
+
*/
|
10 |
+
|
11 |
+
if (! defined('ABSPATH')) {
|
12 |
+
exit;
|
13 |
+
}
|
14 |
+
|
15 |
+
if (! class_exists('WC_Facebook_Product_Feed')) :
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Initial Sync by Facebook feed class
|
19 |
+
*/
|
20 |
+
class WC_Facebook_Product_Feed {
|
21 |
+
|
22 |
+
const FACEBOOK_CATALOG_FEED_FILENAME = 'fae_product_catalog.csv';
|
23 |
+
const FB_ADDITIONAL_IMAGES_FOR_FEED = 5;
|
24 |
+
const FEED_NAME = 'Initial product sync from WooCommerce. DO NOT DELETE.';
|
25 |
+
const FB_PRODUCT_GROUP_ID = 'fb_product_group_id';
|
26 |
+
const FB_VISIBILITY = 'fb_visibility';
|
27 |
+
|
28 |
+
private $has_default_product_count = 0;
|
29 |
+
private $no_default_product_count = 0;
|
30 |
+
|
31 |
+
public function __construct(
|
32 |
+
$facebook_catalog_id,
|
33 |
+
$fbgraph,
|
34 |
+
$feed_id = null) {
|
35 |
+
$this->facebook_catalog_id = $facebook_catalog_id;
|
36 |
+
$this->fbgraph = $fbgraph;
|
37 |
+
$this->feed_id = $feed_id;
|
38 |
+
}
|
39 |
+
|
40 |
+
public function sync_all_products_using_feed() {
|
41 |
+
$start_time = microtime(true);
|
42 |
+
$this->log_feed_progress('Sync all products using feed');
|
43 |
+
|
44 |
+
if (!is_writable(dirname(__FILE__))) {
|
45 |
+
$this->log_feed_progress(
|
46 |
+
'Failure - Sync all products using feed, folder is not writable');
|
47 |
+
return false;
|
48 |
+
}
|
49 |
+
|
50 |
+
if (!$this->generate_productfeed_file()) {
|
51 |
+
$this->log_feed_progress(
|
52 |
+
'Failure - Sync all products using feed, feed file not generated');
|
53 |
+
return false;
|
54 |
+
}
|
55 |
+
$this->log_feed_progress('Sync all products using feed, feed file generated');
|
56 |
+
|
57 |
+
if (!$this->feed_id) {
|
58 |
+
$this->feed_id = $this->create_feed();
|
59 |
+
if (!$this->feed_id) {
|
60 |
+
$this->log_feed_progress(
|
61 |
+
'Failure - Sync all products using feed, facebook feed not created');
|
62 |
+
return false;
|
63 |
+
}
|
64 |
+
$this->log_feed_progress(
|
65 |
+
'Sync all products using feed, facebook feed created');
|
66 |
+
} else {
|
67 |
+
$this->log_feed_progress(
|
68 |
+
'Sync all products using feed, facebook feed already exists.');
|
69 |
+
}
|
70 |
+
|
71 |
+
|
72 |
+
$this->upload_id = $this->create_upload($this->feed_id);
|
73 |
+
if (!$this->upload_id) {
|
74 |
+
$this->log_feed_progress(
|
75 |
+
'Failure - Sync all products using feed, facebook upload not created');
|
76 |
+
return false;
|
77 |
+
}
|
78 |
+
$this->log_feed_progress(
|
79 |
+
'Sync all products using feed, facebook upload created');
|
80 |
+
|
81 |
+
unlink(dirname(__FILE__) .
|
82 |
+
DIRECTORY_SEPARATOR . (self::FACEBOOK_CATALOG_FEED_FILENAME));
|
83 |
+
|
84 |
+
$total_product_count =
|
85 |
+
$this->has_default_product_count + $this->no_default_product_count;
|
86 |
+
$default_product_percentage =
|
87 |
+
($total_product_count == 0 || $this->has_default_product_count == 0)
|
88 |
+
? 0
|
89 |
+
: $this->has_default_product_count / $total_product_count * 100;
|
90 |
+
$time_spent = microtime(true) - $start_time;
|
91 |
+
$data = array();
|
92 |
+
// Only log performance if this store has products in order to get average
|
93 |
+
// performance.
|
94 |
+
if ($total_product_count != 0) {
|
95 |
+
$data = array(
|
96 |
+
'sync_time' => $time_spent,
|
97 |
+
'total' => $total_product_count,
|
98 |
+
'default_product_percentage' => $default_product_percentage,
|
99 |
+
);
|
100 |
+
}
|
101 |
+
$this->log_feed_progress('Complete - Sync all products using feed.', $data);
|
102 |
+
return true;
|
103 |
+
}
|
104 |
+
|
105 |
+
public function generate_productfeed_file() {
|
106 |
+
$this->log_feed_progress('Generating product feed file');
|
107 |
+
$post_ids = $this->get_product_wpid();
|
108 |
+
$all_parent_product = array_map(function($post_id) {
|
109 |
+
if (get_post_type($post_id) == 'product_variation') {
|
110 |
+
return wp_get_post_parent_id($post_id);
|
111 |
+
}
|
112 |
+
}, $post_ids);
|
113 |
+
$all_parent_product = array_filter(array_unique($all_parent_product));
|
114 |
+
$product_ids = array_diff($post_ids, $all_parent_product);
|
115 |
+
return $this->write_product_feed_file($product_ids);
|
116 |
+
}
|
117 |
+
|
118 |
+
public function write_product_feed_file($wp_ids) {
|
119 |
+
try {
|
120 |
+
$feed_file =
|
121 |
+
fopen(dirname(__FILE__) . DIRECTORY_SEPARATOR .
|
122 |
+
(self::FACEBOOK_CATALOG_FEED_FILENAME), "w");
|
123 |
+
fwrite($feed_file, $this->get_product_feed_header_row());
|
124 |
+
|
125 |
+
$product_group_attribute_variants = array();
|
126 |
+
foreach ($wp_ids as $wp_id) {
|
127 |
+
$woo_product = new WC_Facebook_Product($wp_id);
|
128 |
+
if ($woo_product->is_hidden()) {
|
129 |
+
continue;
|
130 |
+
}
|
131 |
+
if (get_option('woocommerce_hide_out_of_stock_items') === 'yes' &&
|
132 |
+
!$woo_product->is_in_stock()) {
|
133 |
+
continue;
|
134 |
+
}
|
135 |
+
$product_data_as_feed_row = $this->prepare_product_for_feed(
|
136 |
+
$woo_product, $product_group_attribute_variants);
|
137 |
+
fwrite($feed_file, $product_data_as_feed_row);
|
138 |
+
}
|
139 |
+
fclose($feed_file);
|
140 |
+
wp_reset_postdata();
|
141 |
+
return true;
|
142 |
+
} catch (Exception $e) {
|
143 |
+
WC_Facebookcommerce_Utils::log(json_encode($e->getMessage()));
|
144 |
+
return false;
|
145 |
+
}
|
146 |
+
}
|
147 |
+
|
148 |
+
public function get_product_feed_header_row() {
|
149 |
+
return 'id,title,description,image_link,link,product_type,' .
|
150 |
+
'brand,price,availability,item_group_id,checkout_url,' .
|
151 |
+
'additional_image_link,sale_price_effective_date,sale_price,condition,' .
|
152 |
+
'visibility,default_product,variant' . PHP_EOL;
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* Assemble product payload in feed upload for initial sync.
|
157 |
+
**/
|
158 |
+
private function prepare_product_for_feed(
|
159 |
+
$woo_product,
|
160 |
+
&$attribute_variants) {
|
161 |
+
$product_data = $woo_product->prepare_product(null, true);
|
162 |
+
$item_group_id = $product_data['retailer_id'];
|
163 |
+
// prepare variant column for variable products
|
164 |
+
$product_data['variant'] = '';
|
165 |
+
if (
|
166 |
+
WC_Facebookcommerce_Utils::is_variation_type($woo_product->get_type())
|
167 |
+
) {
|
168 |
+
$parent_id = $woo_product->get_parent_id();
|
169 |
+
|
170 |
+
if (!isset($attribute_variants[$parent_id])) {
|
171 |
+
$parent_product = new WC_Facebook_Product($parent_id);
|
172 |
+
|
173 |
+
$gallery_urls = array_filter($parent_product->get_gallery_urls());
|
174 |
+
$variation_id = $parent_product->find_matching_product_variation();
|
175 |
+
$variants_for_group = $parent_product->prepare_variants_for_group(true);
|
176 |
+
$parent_attribute_values = array();
|
177 |
+
$parent_attribute_values['gallery_urls'] = $gallery_urls;
|
178 |
+
$parent_attribute_values['default_variant_id'] = $variation_id;
|
179 |
+
$parent_attribute_values['item_group_id'] =
|
180 |
+
WC_Facebookcommerce_Utils::get_fb_retailer_id($parent_product);
|
181 |
+
foreach ($variants_for_group as $variant) {
|
182 |
+
$parent_attribute_values[$variant['product_field']] =
|
183 |
+
$variant['options'];
|
184 |
+
}
|
185 |
+
// cache product group variants
|
186 |
+
$attribute_variants[$parent_id] = $parent_attribute_values;
|
187 |
+
}
|
188 |
+
$parent_attribute_values = $attribute_variants[$parent_id];
|
189 |
+
$variants_for_item =
|
190 |
+
$woo_product->prepare_variants_for_item($product_data);
|
191 |
+
$variant_feed_column = array();
|
192 |
+
foreach ($variants_for_item as $variant_array) {
|
193 |
+
static::format_variant_for_feed(
|
194 |
+
$variant_array['product_field'],
|
195 |
+
$variant_array['options'][0],
|
196 |
+
$parent_attribute_values,
|
197 |
+
$variant_feed_column);
|
198 |
+
}
|
199 |
+
if (isset($product_data['custom_data'])) {
|
200 |
+
foreach ($product_data['custom_data'] as $product_field => $value) {
|
201 |
+
static::format_variant_for_feed(
|
202 |
+
$product_field,
|
203 |
+
$value,
|
204 |
+
$parent_attribute_values,
|
205 |
+
$variant_feed_column);
|
206 |
+
}
|
207 |
+
}
|
208 |
+
if (!empty($variant_feed_column)) {
|
209 |
+
$product_data['variant'] =
|
210 |
+
"\"" . implode(',', $variant_feed_column) . "\"";
|
211 |
+
}
|
212 |
+
if (isset($parent_attribute_values['gallery_urls'])) {
|
213 |
+
$product_data['additional_image_urls'] =
|
214 |
+
array_merge($product_data['additional_image_urls'],
|
215 |
+
$parent_attribute_values['gallery_urls']);
|
216 |
+
}
|
217 |
+
if (isset($parent_attribute_values['item_group_id'])) {
|
218 |
+
$item_group_id = $parent_attribute_values['item_group_id'];
|
219 |
+
}
|
220 |
+
|
221 |
+
$product_data['default_product'] =
|
222 |
+
$parent_attribute_values['default_variant_id'] == $woo_product->id
|
223 |
+
? 'default'
|
224 |
+
:'';
|
225 |
+
|
226 |
+
// If this group has default variant value, log this product item
|
227 |
+
if (isset($parent_attribute_values['default_variant_id']) &&
|
228 |
+
!empty($parent_attribute_values['default_variant_id'])) {
|
229 |
+
$this->has_default_product_count++;
|
230 |
+
} else {
|
231 |
+
$this->no_default_product_count++;
|
232 |
+
}
|
233 |
+
}
|
234 |
+
|
235 |
+
// log simple product
|
236 |
+
if (!isset($product_data['default_product'])) {
|
237 |
+
$this->no_default_product_count++;
|
238 |
+
$product_data['default_product'];
|
239 |
+
}
|
240 |
+
|
241 |
+
return
|
242 |
+
$product_data['retailer_id'] . ',' .
|
243 |
+
static::format_string_for_feed($product_data['name']) . ',' .
|
244 |
+
static::format_string_for_feed($product_data['description']) . ',' .
|
245 |
+
$product_data['image_url'] . ',' .
|
246 |
+
$product_data['url'] . ',' .
|
247 |
+
static::format_string_for_feed($product_data['category']) . ',' .
|
248 |
+
static::format_string_for_feed($product_data['brand']) . ',' .
|
249 |
+
static::format_price_for_feed(
|
250 |
+
$product_data['price'], $product_data['currency']) . ',' .
|
251 |
+
$product_data['availability'] . ',' .
|
252 |
+
$item_group_id . ',' .
|
253 |
+
$product_data['checkout_url'] . ',' .
|
254 |
+
static::format_additional_image_url(
|
255 |
+
$product_data['additional_image_urls']) . ',' .
|
256 |
+
$product_data['sale_price_start_date'] . '/' .
|
257 |
+
$product_data['sale_price_end_date']. ',' .
|
258 |
+
static::format_price_for_feed(
|
259 |
+
$product_data['sale_price'], $product_data['currency']) . ',' .
|
260 |
+
'new' . ',' .
|
261 |
+
$product_data['visibility'] . ',' .
|
262 |
+
$product_data['default_product'] . ',' .
|
263 |
+
$product_data['variant'] . PHP_EOL;
|
264 |
+
}
|
265 |
+
|
266 |
+
private function create_feed() {
|
267 |
+
$result = $this->fbgraph->create_feed(
|
268 |
+
$this->facebook_catalog_id, array('name' => self::FEED_NAME));
|
269 |
+
if (is_wp_error($result) || !isset($result['body'])) {
|
270 |
+
$this->log_feed_progress(json_encode($result));
|
271 |
+
return null;
|
272 |
+
}
|
273 |
+
$decode_result = WC_Facebookcommerce_Utils::decode_json($result['body']);
|
274 |
+
$feed_id = $decode_result->id;
|
275 |
+
if (!$feed_id) {
|
276 |
+
$this->log_feed_progress(
|
277 |
+
'Response from creating feed not return feed id!');
|
278 |
+
return null;
|
279 |
+
}
|
280 |
+
return $feed_id;
|
281 |
+
}
|
282 |
+
|
283 |
+
private function create_upload($facebook_feed_id) {
|
284 |
+
$result = $this->fbgraph->create_upload(
|
285 |
+
$facebook_feed_id, dirname(__FILE__) . DIRECTORY_SEPARATOR .
|
286 |
+
(self::FACEBOOK_CATALOG_FEED_FILENAME));
|
287 |
+
if (is_null($result) || !isset($result['id']) || !$result['id']) {
|
288 |
+
$this->log_feed_progress(json_encode($result));
|
289 |
+
return null;
|
290 |
+
}
|
291 |
+
$upload_id = $result['id'];
|
292 |
+
return $upload_id;
|
293 |
+
}
|
294 |
+
|
295 |
+
private static function format_additional_image_url($product_image_urls) {
|
296 |
+
// returns the top 10 additional image urls separated by a comma
|
297 |
+
// according to feed api rules
|
298 |
+
$product_image_urls = array_slice(
|
299 |
+
$product_image_urls,
|
300 |
+
0,
|
301 |
+
self::FB_ADDITIONAL_IMAGES_FOR_FEED);
|
302 |
+
if ($product_image_urls) {
|
303 |
+
return "\"" . implode(',', $product_image_urls) . "\"";
|
304 |
+
} else {
|
305 |
+
return '';
|
306 |
+
}
|
307 |
+
}
|
308 |
+
|
309 |
+
private static function format_string_for_feed($text) {
|
310 |
+
if ((bool)$text) {
|
311 |
+
return "\"" . str_replace('"', "'", $text) . "\"";
|
312 |
+
} else {
|
313 |
+
return '';
|
314 |
+
}
|
315 |
+
}
|
316 |
+
|
317 |
+
private static function format_price_for_feed($value, $currency) {
|
318 |
+
return (string)(round($value / (float) 100, 2)) . $currency;
|
319 |
+
}
|
320 |
+
|
321 |
+
private static function format_variant_for_feed(
|
322 |
+
$product_field,
|
323 |
+
$value,
|
324 |
+
$parent_attribute_values,
|
325 |
+
&$variant_feed_column) {
|
326 |
+
if (!array_key_exists($product_field, $parent_attribute_values)) {
|
327 |
+
return;
|
328 |
+
}
|
329 |
+
array_push($variant_feed_column,
|
330 |
+
$product_field . ':' .
|
331 |
+
implode('/', $parent_attribute_values[$product_field]) . ':' .
|
332 |
+
$value);
|
333 |
+
}
|
334 |
+
|
335 |
+
public function is_upload_complete(&$settings) {
|
336 |
+
$result = $this->fbgraph->get_upload_status($settings['fb_upload_id']);
|
337 |
+
if (is_wp_error($result) || !isset($result['body'])) {
|
338 |
+
$this->log_feed_progress(json_encode($result));
|
339 |
+
return 'error';
|
340 |
+
}
|
341 |
+
$decode_result = WC_Facebookcommerce_Utils::decode_json($result['body'], true);
|
342 |
+
$end_time = $decode_result['end_time'];
|
343 |
+
if (isset($end_time)) {
|
344 |
+
$settings['upload_end_time'] = $end_time;
|
345 |
+
return 'complete';
|
346 |
+
} else {
|
347 |
+
return 'in progress';
|
348 |
+
}
|
349 |
+
}
|
350 |
+
|
351 |
+
// Log progress in local log file and FB.
|
352 |
+
public function log_feed_progress($msg, $object = array()) {
|
353 |
+
WC_Facebookcommerce_Utils::fblog($msg, $object);
|
354 |
+
$msg = empty($object) ? $msg : $msg . json_encode($object);
|
355 |
+
WC_Facebookcommerce_Utils::log($msg);
|
356 |
+
}
|
357 |
+
|
358 |
+
public function get_product_wpid() {
|
359 |
+
$post_ids = WC_Facebookcommerce_Utils::get_wp_posts(
|
360 |
+
null,
|
361 |
+
null,
|
362 |
+
array('product', 'product_variation'));
|
363 |
+
return $post_ids;
|
364 |
+
}
|
365 |
+
}
|
366 |
+
|
367 |
+
endif;
|
includes/fbutils.php
ADDED
@@ -0,0 +1,485 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* @package FacebookCommerce
|
9 |
+
*/
|
10 |
+
|
11 |
+
if (!defined('ABSPATH')) {
|
12 |
+
exit;
|
13 |
+
}
|
14 |
+
|
15 |
+
if (!class_exists('WC_Facebookcommerce_Utils')) :
|
16 |
+
|
17 |
+
/**
|
18 |
+
* FB Graph API helper functions
|
19 |
+
*
|
20 |
+
*/
|
21 |
+
class WC_Facebookcommerce_Utils {
|
22 |
+
|
23 |
+
const FB_RETAILER_ID_PREFIX = 'wc_post_id_';
|
24 |
+
const PLUGIN_VERSION = '1.9.11'; // Change it in `facebook-for-*.php` also
|
25 |
+
|
26 |
+
const FB_VARIANT_IMAGE = 'fb_image';
|
27 |
+
const FB_VARIANT_SIZE = 'size';
|
28 |
+
const FB_VARIANT_COLOR = 'color';
|
29 |
+
const FB_VARIANT_COLOUR = 'colour';
|
30 |
+
const FB_VARIANT_PATTERN = 'pattern';
|
31 |
+
const FB_VARIANT_GENDER = 'gender';
|
32 |
+
|
33 |
+
public static $ems = null;
|
34 |
+
public static $fbgraph = null;
|
35 |
+
public static $store_name = null;
|
36 |
+
|
37 |
+
public static $validGenderArray =
|
38 |
+
array("male" => 1, "female" => 1, "unisex" => 1);
|
39 |
+
/**
|
40 |
+
* WooCommerce 2.1 support for wc_enqueue_js
|
41 |
+
*
|
42 |
+
* @since 1.2.1
|
43 |
+
*
|
44 |
+
* @access public
|
45 |
+
* @param string $code
|
46 |
+
* @return void
|
47 |
+
*/
|
48 |
+
public static function wc_enqueue_js($code) {
|
49 |
+
global $wc_queued_js;
|
50 |
+
|
51 |
+
if (function_exists('wc_enqueue_js') && empty($wc_queued_js)) {
|
52 |
+
wc_enqueue_js($code);
|
53 |
+
} else {
|
54 |
+
$wc_queued_js = $code."\n".$wc_queued_js;
|
55 |
+
}
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Validate URLs, make relative URLs absolute
|
60 |
+
*
|
61 |
+
* @access public
|
62 |
+
* @param string $url
|
63 |
+
* @return string
|
64 |
+
*/
|
65 |
+
public static function make_url($url) {
|
66 |
+
if (
|
67 |
+
// The first check incorrectly fails for URLs with special chars.
|
68 |
+
!filter_var($url , FILTER_VALIDATE_URL) &&
|
69 |
+
substr($url, 0, 4) !== 'http'
|
70 |
+
) {
|
71 |
+
return get_site_url() . $url ;
|
72 |
+
} else {
|
73 |
+
return $url;
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Product ID for Dynamic Ads on Facebook can be SKU or wc_post_id_123
|
79 |
+
* This function should be used to get retailer_id based on a WC_Product
|
80 |
+
* from WooCommerce
|
81 |
+
*
|
82 |
+
* @access public
|
83 |
+
* @param WC_Product $woo_product
|
84 |
+
* @return string
|
85 |
+
*/
|
86 |
+
public static function get_fb_retailer_id($woo_product) {
|
87 |
+
$woo_id = $woo_product->get_id();
|
88 |
+
|
89 |
+
// Call $woo_product->get_id() instead of ->id to account for Variable
|
90 |
+
// products, which have their own variant_ids.
|
91 |
+
return $woo_product->get_sku() ? $woo_product->get_sku() . '_' .
|
92 |
+
$woo_id : self::FB_RETAILER_ID_PREFIX . $woo_id;
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Return categories for products/pixel
|
97 |
+
*
|
98 |
+
* @access public
|
99 |
+
* @param String $id
|
100 |
+
* @return Array
|
101 |
+
*/
|
102 |
+
public static function get_product_categories($wpid) {
|
103 |
+
$category_path = wp_get_post_terms(
|
104 |
+
$wpid,
|
105 |
+
'product_cat',
|
106 |
+
array('fields' => 'all'));
|
107 |
+
$content_category = array_values(
|
108 |
+
array_map(
|
109 |
+
function($item) {
|
110 |
+
return $item->name;
|
111 |
+
},
|
112 |
+
$category_path));
|
113 |
+
$content_category_slice = array_slice($content_category, -1);
|
114 |
+
$categories =
|
115 |
+
empty($content_category) ? '""' : implode(', ', $content_category);
|
116 |
+
return array(
|
117 |
+
'name' => array_pop($content_category_slice),
|
118 |
+
'categories' => $categories
|
119 |
+
);
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* Returns content id to match on for Pixel fires.
|
124 |
+
*
|
125 |
+
* @access public
|
126 |
+
* @param WC_Product $woo_product
|
127 |
+
* @return array
|
128 |
+
*/
|
129 |
+
public static function get_fb_content_ids($woo_product) {
|
130 |
+
return array(self::get_fb_retailer_id($woo_product));
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* Clean up strings for FB Graph POSTing.
|
135 |
+
* This function should will:
|
136 |
+
* 1. Replace newlines chars/nbsp with a real space
|
137 |
+
* 2. strip_tags()
|
138 |
+
* 3. trim()
|
139 |
+
*
|
140 |
+
* @access public
|
141 |
+
* @param String string
|
142 |
+
* @return string
|
143 |
+
*/
|
144 |
+
public static function clean_string($string) {
|
145 |
+
$string = do_shortcode($string);
|
146 |
+
$string = str_replace(array('&%3B', '&'), '&', $string);
|
147 |
+
$string = str_replace(array("\r", " ", "\t"), ' ', $string);
|
148 |
+
$string = wp_strip_all_tags($string, false); // true == remove line breaks
|
149 |
+
return $string;
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* Returns flat array of woo IDs for variable products, or
|
154 |
+
* an array with a single woo ID for simple products.
|
155 |
+
*
|
156 |
+
* @access public
|
157 |
+
* @param WC_Product $woo_product
|
158 |
+
* @return array
|
159 |
+
*/
|
160 |
+
public static function get_product_array($woo_product) {
|
161 |
+
$result = array();
|
162 |
+
if (WC_Facebookcommerce_Utils::is_variable_type($woo_product->get_type())) {
|
163 |
+
foreach ($woo_product->get_children() as $item_id) {
|
164 |
+
array_push($result, $item_id);
|
165 |
+
}
|
166 |
+
return $result;
|
167 |
+
} else {
|
168 |
+
return array($woo_product->get_id());
|
169 |
+
}
|
170 |
+
}
|
171 |
+
|
172 |
+
/**
|
173 |
+
* Returns true if WooCommerce plugin found.
|
174 |
+
*
|
175 |
+
* @access public
|
176 |
+
* @return bool
|
177 |
+
*/
|
178 |
+
public static function isWoocommerceIntegration() {
|
179 |
+
return class_exists('WooCommerce');
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* Returns integration dependent name.
|
184 |
+
*
|
185 |
+
* @access public
|
186 |
+
* @return string
|
187 |
+
*/
|
188 |
+
public static function getIntegrationName() {
|
189 |
+
if (WC_Facebookcommerce_Utils::isWoocommerceIntegration()) {
|
190 |
+
return 'WooCommerce';
|
191 |
+
} else {
|
192 |
+
return 'WordPress';
|
193 |
+
}
|
194 |
+
}
|
195 |
+
|
196 |
+
/**
|
197 |
+
* Returns user info for the current WP user.
|
198 |
+
*
|
199 |
+
* @access public
|
200 |
+
* @param boolean $use_pii
|
201 |
+
* @return array
|
202 |
+
*/
|
203 |
+
public static function get_user_info($use_pii) {
|
204 |
+
$current_user = wp_get_current_user();
|
205 |
+
if (0 === $current_user->ID || $use_pii === false) {
|
206 |
+
// User not logged in or admin chose not to send PII.
|
207 |
+
return array();
|
208 |
+
} else {
|
209 |
+
return array_filter(
|
210 |
+
array(
|
211 |
+
// Keys documented in
|
212 |
+
// https://developers.facebook.com/docs/facebook-pixel/pixel-with-ads/
|
213 |
+
// /conversion-tracking#advanced_match
|
214 |
+
'em' => $current_user->user_email,
|
215 |
+
'fn' => $current_user->user_firstname,
|
216 |
+
'ln' => $current_user->user_lastname
|
217 |
+
),
|
218 |
+
function ($value) { return $value !== null && $value !== ''; });
|
219 |
+
}
|
220 |
+
}
|
221 |
+
|
222 |
+
/**
|
223 |
+
* Utility function for development logging.
|
224 |
+
*/
|
225 |
+
public static function fblog(
|
226 |
+
$message,
|
227 |
+
$object = array(),
|
228 |
+
$error = false,
|
229 |
+
$ems = '') {
|
230 |
+
if ($error) {
|
231 |
+
$object['plugin_version'] = self::PLUGIN_VERSION;
|
232 |
+
$object['php_version'] = phpversion();
|
233 |
+
}
|
234 |
+
$message = json_encode(array(
|
235 |
+
'message' => $message,
|
236 |
+
'object' => $object
|
237 |
+
));
|
238 |
+
$ems = $ems ?: self::$ems;
|
239 |
+
if ($ems) {
|
240 |
+
self::$fbgraph->log(
|
241 |
+
$ems,
|
242 |
+
$message,
|
243 |
+
$error);
|
244 |
+
} else {
|
245 |
+
error_log('external merchant setting is null, something wrong here: ' .
|
246 |
+
$message);
|
247 |
+
}
|
248 |
+
}
|
249 |
+
|
250 |
+
/**
|
251 |
+
* Utility function for development Tip Events logging.
|
252 |
+
*/
|
253 |
+
public static function tip_events_log(
|
254 |
+
$tip_id,
|
255 |
+
$channel_id,
|
256 |
+
$event,
|
257 |
+
$ems = '') {
|
258 |
+
|
259 |
+
$ems = $ems ?: self::$ems;
|
260 |
+
if ($ems) {
|
261 |
+
self::$fbgraph->log_tip_event(
|
262 |
+
$tip_id,
|
263 |
+
$channel_id,
|
264 |
+
$event);
|
265 |
+
} else {
|
266 |
+
error_log('external merchant setting is null');
|
267 |
+
}
|
268 |
+
}
|
269 |
+
|
270 |
+
public static function is_variation_type($type) {
|
271 |
+
return $type == 'variation' || $type == 'subscription_variation';
|
272 |
+
}
|
273 |
+
|
274 |
+
public static function is_variable_type($type) {
|
275 |
+
return $type == 'variable' || $type == 'variable-subscription';
|
276 |
+
}
|
277 |
+
|
278 |
+
public static function check_woo_ajax_permissions($action_text, $die) {
|
279 |
+
if (!current_user_can('manage_woocommerce')) {
|
280 |
+
self::log(
|
281 |
+
'Non manage_woocommerce user attempting to'.$action_text.'!',
|
282 |
+
array(),
|
283 |
+
true);
|
284 |
+
if ($die) {
|
285 |
+
wp_die();
|
286 |
+
}
|
287 |
+
return false;
|
288 |
+
}
|
289 |
+
return true;
|
290 |
+
}
|
291 |
+
|
292 |
+
/**
|
293 |
+
* Returns true if id is a positive non-zero integer
|
294 |
+
*
|
295 |
+
* @access public
|
296 |
+
* @param string $pixel_id
|
297 |
+
* @return bool
|
298 |
+
*/
|
299 |
+
public static function is_valid_id($pixel_id) {
|
300 |
+
return isset($pixel_id) && is_numeric($pixel_id) && (int)$pixel_id > 0;
|
301 |
+
}
|
302 |
+
|
303 |
+
/**
|
304 |
+
* Helper function to query posts.
|
305 |
+
*/
|
306 |
+
public static function get_wp_posts(
|
307 |
+
$product_group_id = null,
|
308 |
+
$compare_condition = null,
|
309 |
+
$post_type = 'product') {
|
310 |
+
$args = array(
|
311 |
+
'fields' => 'ids',
|
312 |
+
'meta_query' => array(
|
313 |
+
(($product_group_id) ?
|
314 |
+
array(
|
315 |
+
'key' => $product_group_id,
|
316 |
+
'compare' => $compare_condition,
|
317 |
+
) : array()
|
318 |
+
),
|
319 |
+
),
|
320 |
+
'post_status' => 'publish',
|
321 |
+
'post_type' => $post_type,
|
322 |
+
'posts_per_page' => -1,
|
323 |
+
);
|
324 |
+
return get_posts($args);
|
325 |
+
}
|
326 |
+
|
327 |
+
/**
|
328 |
+
* Helper log function for debugging
|
329 |
+
*/
|
330 |
+
public static function log($message) {
|
331 |
+
if (WP_DEBUG === true) {
|
332 |
+
if (is_array($message) || is_object($message)) {
|
333 |
+
error_log(json_encode($message));
|
334 |
+
}
|
335 |
+
else {
|
336 |
+
error_log(sanitize_textarea_field($message));
|
337 |
+
}
|
338 |
+
}
|
339 |
+
}
|
340 |
+
|
341 |
+
// Return store name with sanitized apostrophe
|
342 |
+
public static function get_store_name() {
|
343 |
+
if (self::$store_name) {
|
344 |
+
return self::$store_name;
|
345 |
+
}
|
346 |
+
$name = trim(str_replace(
|
347 |
+
"'",
|
348 |
+
"\u{2019}",
|
349 |
+
html_entity_decode(
|
350 |
+
get_bloginfo('name'),
|
351 |
+
ENT_QUOTES,
|
352 |
+
'UTF-8')));
|
353 |
+
if ($name) {
|
354 |
+
self::$store_name = $name;
|
355 |
+
return $name;
|
356 |
+
}
|
357 |
+
// Fallback to site url
|
358 |
+
$url = get_site_url();
|
359 |
+
if ($url) {
|
360 |
+
self::$store_name = parse_url($url, PHP_URL_HOST);
|
361 |
+
return self::$store_name;
|
362 |
+
}
|
363 |
+
// If site url doesn't exist, fall back to http host.
|
364 |
+
if ($_SERVER['HTTP_HOST']) {
|
365 |
+
self::$store_name = $_SERVER['HTTP_HOST'];
|
366 |
+
return self::$store_name;
|
367 |
+
}
|
368 |
+
|
369 |
+
// If http host doesn't exist, fall back to local host name.
|
370 |
+
$url = gethostname();
|
371 |
+
self::$store_name = $url;
|
372 |
+
return (self::$store_name) ? (self::$store_name) : 'A Store Has No Name';
|
373 |
+
}
|
374 |
+
|
375 |
+
/*
|
376 |
+
* Change variant product field name from Woo taxonomy to FB name
|
377 |
+
*/
|
378 |
+
public static function sanitize_variant_name($name) {
|
379 |
+
$name = str_replace(array('attribute_', 'pa_'), '', strtolower($name));
|
380 |
+
|
381 |
+
// British spelling
|
382 |
+
if ($name === self::FB_VARIANT_COLOUR) {
|
383 |
+
$name = self::FB_VARIANT_COLOR;
|
384 |
+
}
|
385 |
+
|
386 |
+
switch ($name) {
|
387 |
+
case self::FB_VARIANT_SIZE:
|
388 |
+
case self::FB_VARIANT_COLOR:
|
389 |
+
case self::FB_VARIANT_GENDER:
|
390 |
+
case self::FB_VARIANT_PATTERN:
|
391 |
+
break;
|
392 |
+
default:
|
393 |
+
$name = 'custom_data:' . strtolower($name);
|
394 |
+
break;
|
395 |
+
}
|
396 |
+
|
397 |
+
return $name;
|
398 |
+
}
|
399 |
+
|
400 |
+
public static function validateGender($gender) {
|
401 |
+
if ($gender && !isset(self::$validGenderArray[$gender])) {
|
402 |
+
$first_char = strtolower(substr($gender, 0, 1));
|
403 |
+
// Men, Man, Boys
|
404 |
+
if ($first_char === 'm' || $first_char === 'b') {
|
405 |
+
return "male";
|
406 |
+
}
|
407 |
+
// Women, Woman, Female, Ladies
|
408 |
+
if ($first_char === 'w' || $first_char === 'f' || $first_char === 'l') {
|
409 |
+
return "female";
|
410 |
+
}
|
411 |
+
if ($first_char === 'u') {
|
412 |
+
return "unisex";
|
413 |
+
}
|
414 |
+
if (strlen($gender) >= 3) {
|
415 |
+
$gender = strtolower(substr($gender, 0, 3));
|
416 |
+
if ($gender === 'gir' || $gender === 'her') {
|
417 |
+
return "female";
|
418 |
+
}
|
419 |
+
if ($gender === 'him' || $gender === 'his' || $gender == 'guy') {
|
420 |
+
return "male";
|
421 |
+
}
|
422 |
+
}
|
423 |
+
return null;
|
424 |
+
}
|
425 |
+
return $gender;
|
426 |
+
}
|
427 |
+
|
428 |
+
public static function get_fbid_post_meta($wp_id, $fbid_type) {
|
429 |
+
return get_post_meta($wp_id, $fbid_type, true);
|
430 |
+
}
|
431 |
+
|
432 |
+
public static function is_all_caps($value) {
|
433 |
+
if ($value === null || $value === '') {
|
434 |
+
return true;
|
435 |
+
}
|
436 |
+
if (preg_match('/[^\\p{Common}\\p{Latin}]/u', $value)) {
|
437 |
+
// Contains non-western characters
|
438 |
+
// So, it can't be all uppercase
|
439 |
+
return false;
|
440 |
+
}
|
441 |
+
$latin_string = preg_replace('/[^\\p{Latin}]/u', '', $value);
|
442 |
+
if ($latin_string === '') {
|
443 |
+
// Symbols only
|
444 |
+
return true;
|
445 |
+
}
|
446 |
+
return strtoupper($latin_string) === $latin_string;
|
447 |
+
}
|
448 |
+
|
449 |
+
public static function decode_json($json_string, $assoc = false) {
|
450 |
+
// Plugin requires 5.6.0 but for some user use 5.5.9 JSON_BIGINT_AS_STRING
|
451 |
+
// will cause 502 issue when redirect.
|
452 |
+
return version_compare(phpversion(), '5.6.0') >= 0
|
453 |
+
? json_decode($json_string, $assoc, 512, JSON_BIGINT_AS_STRING)
|
454 |
+
: json_decode($json_string, $assoc, 512);
|
455 |
+
}
|
456 |
+
|
457 |
+
public static function set_test_fail_reason($msg, $trace) {
|
458 |
+
$reason_msg = get_transient('facebook_plugin_test_fail');
|
459 |
+
if ($reason_msg) {
|
460 |
+
$msg = $reason_msg . PHP_EOL . $msg;
|
461 |
+
}
|
462 |
+
set_transient('facebook_plugin_test_fail', $msg);
|
463 |
+
set_transient('facebook_plugin_test_stack_trace', $trace);
|
464 |
+
}
|
465 |
+
|
466 |
+
/**
|
467 |
+
* Helper function to check time cap.
|
468 |
+
*/
|
469 |
+
public static function check_time_cap($from, $date_cap) {
|
470 |
+
if ($from == null) {
|
471 |
+
return true;
|
472 |
+
}
|
473 |
+
$now = new DateTime(current_time('mysql'));
|
474 |
+
$diff_in_day = $now->diff(new DateTime($from))->format('%a');
|
475 |
+
return is_numeric($diff_in_day) && (int)$diff_in_day > $date_cap;
|
476 |
+
}
|
477 |
+
|
478 |
+
public static function get_cached_best_tip() {
|
479 |
+
$cached_best_tip = WC_Facebookcommerce_Utils::decode_json(
|
480 |
+
get_option('fb_info_banner_last_best_tip', ''));
|
481 |
+
return $cached_best_tip;
|
482 |
+
}
|
483 |
+
}
|
484 |
+
|
485 |
+
endif;
|
includes/fbwpml.php
ADDED
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* @package FacebookCommerce
|
9 |
+
*/
|
10 |
+
|
11 |
+
if (!defined('ABSPATH')) {
|
12 |
+
exit;
|
13 |
+
}
|
14 |
+
|
15 |
+
if (!class_exists('WC_Facebook_WPML_Injector')) :
|
16 |
+
|
17 |
+
class FB_WPML_Language_Status {
|
18 |
+
const VISIBLE = 1;
|
19 |
+
const HIDDEN = 2;
|
20 |
+
const NOT_SYNCED = 0;
|
21 |
+
}
|
22 |
+
|
23 |
+
class WC_Facebook_WPML_Injector {
|
24 |
+
public static $settings = null;
|
25 |
+
public static $default_lang = null;
|
26 |
+
const OPTION = 'fb_wmpl_language_visibility';
|
27 |
+
|
28 |
+
public function __construct() {
|
29 |
+
add_action('icl_menu_footer', array($this, 'wpml_support'));
|
30 |
+
add_action('icl_ajx_custom_call', array($this, 'wpml_ajax_support'), 10, 2);
|
31 |
+
self::$settings = get_option(self::OPTION);
|
32 |
+
self::$default_lang = apply_filters('wpml_default_language', null);
|
33 |
+
}
|
34 |
+
|
35 |
+
public static function should_hide($wp_id) {
|
36 |
+
$product_lang = apply_filters('wpml_post_language_details', null, $wp_id);
|
37 |
+
$settings = self::$settings;
|
38 |
+
if ($product_lang && isset($product_lang['language_code'])) {
|
39 |
+
$product_lang = $product_lang['language_code'];
|
40 |
+
}
|
41 |
+
|
42 |
+
// Option doesn't exist : Backwards Compatibility
|
43 |
+
if (!$settings) {
|
44 |
+
return ($product_lang && self::$default_lang !== $product_lang);
|
45 |
+
}
|
46 |
+
// Hide products from non-active languages.
|
47 |
+
if (!isset($settings[$product_lang])) {
|
48 |
+
return true;
|
49 |
+
}
|
50 |
+
return $settings[$product_lang] !== FB_WPML_Language_Status::VISIBLE;
|
51 |
+
}
|
52 |
+
|
53 |
+
public function wpml_ajax_support($call, $REQUEST) {
|
54 |
+
global $sitepress;
|
55 |
+
if (isset($REQUEST['icl_ajx_action'])) {
|
56 |
+
$call = $REQUEST['icl_ajx_action'];
|
57 |
+
}
|
58 |
+
if ($call === "icl_fb_woo") {
|
59 |
+
$active_languages = array_keys($sitepress->get_active_languages());
|
60 |
+
$settings = array();
|
61 |
+
foreach ($active_languages as $lang) {
|
62 |
+
$settings[$lang] = $REQUEST[$lang] === 'on' ?
|
63 |
+
FB_WPML_Language_Status::VISIBLE : FB_WPML_Language_Status::HIDDEN;
|
64 |
+
}
|
65 |
+
|
66 |
+
update_option('fb_wmpl_language_visibility', $settings, false);
|
67 |
+
self::$settings = $settings;
|
68 |
+
}
|
69 |
+
}
|
70 |
+
|
71 |
+
public function wpml_support() {
|
72 |
+
global $sitepress;
|
73 |
+
if (strpos($_GET['page'], 'languages.php')) {
|
74 |
+
$active_languages = $sitepress->get_active_languages();
|
75 |
+
$settings = get_option(self::OPTION);
|
76 |
+
|
77 |
+
// Default setting is only show default lang.
|
78 |
+
if (!$settings) {
|
79 |
+
$settings = array_fill_keys(
|
80 |
+
array_keys($active_languages), FB_WPML_Language_Status::HIDDEN);
|
81 |
+
$settings[self::$default_lang] = FB_WPML_Language_Status::VISIBLE;
|
82 |
+
}
|
83 |
+
$ajax_response = sprintf(
|
84 |
+
'Saved. You should now '.
|
85 |
+
' <a href="%s&fb_force_resync=true">Re-Sync</a>'.
|
86 |
+
' your products with Facebook. ',
|
87 |
+
WOOCOMMERCE_FACEBOOK_PLUGIN_SETTINGS_URL);
|
88 |
+
|
89 |
+
?><div id="lang-sec-fb" class="wpml-section wpml-section-languages">
|
90 |
+
<div class="wpml-section-header">
|
91 |
+
<h3><?php _e('Facebook Visibility', 'sitepress') ?></h3>
|
92 |
+
</div>
|
93 |
+
<div class="wpml-section-content">
|
94 |
+
WooCommerce Products with languages that are selected
|
95 |
+
here will be visible to customers who see your Facebook Shop.
|
96 |
+
|
97 |
+
<div class="wpml-section-content-inner">
|
98 |
+
<form id="icl_fb_woo" name="icl_fb_woo" action="">
|
99 |
+
<?php
|
100 |
+
foreach ($settings as $language => $set) {
|
101 |
+
$is_checked = $set === FB_WPML_Language_Status::VISIBLE ?
|
102 |
+
'checked' : '';
|
103 |
+
$str = '
|
104 |
+
<p><label>
|
105 |
+
<input type="checkbox" id="icl_fb_woo_chk" name="'.$language.'" '.$is_checked.'>
|
106 |
+
'.$active_languages[$language]['native_name'].'
|
107 |
+
</label></p>
|
108 |
+
';
|
109 |
+
echo $str;
|
110 |
+
}
|
111 |
+
?>
|
112 |
+
<p class="buttons-wrap">
|
113 |
+
<span class="icl_ajx_response_fb" id="icl_ajx_response_fb" hidden="true">
|
114 |
+
<?php echo $ajax_response ?>
|
115 |
+
</span>
|
116 |
+
<input class="button button-primary"
|
117 |
+
name="save"
|
118 |
+
value="<?php _e('Save', 'sitepress') ?>"
|
119 |
+
type="submit" />
|
120 |
+
</p>
|
121 |
+
</form>
|
122 |
+
<script type="text/javascript">
|
123 |
+
addLoadEvent(function(){
|
124 |
+
jQuery('#icl_fb_woo').submit(iclSaveForm);
|
125 |
+
jQuery('#icl_fb_woo').submit(function(){
|
126 |
+
jQuery('#icl_ajx_response_fb').show();
|
127 |
+
});
|
128 |
+
});
|
129 |
+
</script>
|
130 |
+
</div>
|
131 |
+
</div>
|
132 |
+
</div><?php
|
133 |
+
}
|
134 |
+
}
|
135 |
+
}
|
136 |
+
|
137 |
+
endif;
|
includes/test/facebook-integration-test.php
ADDED
@@ -0,0 +1,575 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* @package FacebookCommerce
|
9 |
+
*
|
10 |
+
* usage:
|
11 |
+
* 1. set WP_DEBUG = true and WP_DEBUG_DISPLAY = false
|
12 |
+
* 2. append "&fb_test_product_sync=true" to the url when you are on facebook-for-woocommerce setting pages
|
13 |
+
* 3. refresh the page to launch test
|
14 |
+
* https://codex.wordpress.org/WP_DEBUG
|
15 |
+
*/
|
16 |
+
|
17 |
+
if (!defined('ABSPATH')) {
|
18 |
+
exit;
|
19 |
+
}
|
20 |
+
|
21 |
+
include_once(dirname(__DIR__) . '/fbutils.php');
|
22 |
+
include_once('fbproductfeed-test.php');
|
23 |
+
|
24 |
+
if (!class_exists('WC_Facebook_Integration_Test')) :
|
25 |
+
|
26 |
+
/**
|
27 |
+
* This tests the upload of test objects into Facebook using the plugin's
|
28 |
+
* infrastructure and checks to see if the product field have been correctly
|
29 |
+
* uploaded into FB.
|
30 |
+
*/
|
31 |
+
class WC_Facebook_Integration_Test {
|
32 |
+
|
33 |
+
const FB_PRODUCT_GROUP_ID = 'fb_product_group_id';
|
34 |
+
const FB_PRODUCT_ITEM_ID = 'fb_product_item_id';
|
35 |
+
const MAX_SLEEP_IN_SEC = 90;
|
36 |
+
const MAX_TIME = 'T23:59+00:00';
|
37 |
+
const MIN_TIME = 'T00:00+00:00';
|
38 |
+
/** Class Instance */
|
39 |
+
private static $instance;
|
40 |
+
|
41 |
+
public static $commerce = null; // Full WC_Facebookcommerce_Integration obj
|
42 |
+
public static $fbgraph = null;
|
43 |
+
public static $test_mode = false;
|
44 |
+
|
45 |
+
// simple products' id and variable products' parent_id
|
46 |
+
public static $wp_post_ids = array();
|
47 |
+
// FB product item retailer id.
|
48 |
+
public static $retailer_ids = array();
|
49 |
+
// product and product_variation post id for test
|
50 |
+
public $product_post_wpid = null;
|
51 |
+
public static $test_pass = 1;
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Get the class instance
|
55 |
+
*/
|
56 |
+
public static function get_instance($commerce) {
|
57 |
+
return null === self::$instance
|
58 |
+
? (self::$instance = new self($commerce))
|
59 |
+
: self::$instance;
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Constructor
|
64 |
+
*/
|
65 |
+
public function __construct($commerce) {
|
66 |
+
|
67 |
+
self::$commerce = $commerce;
|
68 |
+
|
69 |
+
add_action('wp_ajax_ajax_test_sync_products_using_feed',
|
70 |
+
array($this, 'ajax_test_sync_products_using_feed'));
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Test visible products by uploading feed.
|
75 |
+
**/
|
76 |
+
function ajax_test_sync_products_using_feed() {
|
77 |
+
self::$test_mode = true;
|
78 |
+
// test ajax reset all products in db
|
79 |
+
$reset = self::$commerce->reset_all_products();
|
80 |
+
if ($reset) {
|
81 |
+
WC_Facebookcommerce_Utils::log('Test - Removing FBIDs from all products');
|
82 |
+
$this->product_post_wpid = $this->create_data();
|
83 |
+
if (empty($this->product_post_wpid)) {
|
84 |
+
self::$test_pass = 0;
|
85 |
+
WC_Facebookcommerce_Utils::log(
|
86 |
+
'Test - Fail to create test product by inserting posts.');
|
87 |
+
WC_Facebookcommerce_Utils::set_test_fail_reason(
|
88 |
+
'Fail to create test products by inserting posts.',
|
89 |
+
(new Exception)->getTraceAsString());
|
90 |
+
update_option('fb_test_pass', false);
|
91 |
+
wp_die();
|
92 |
+
return;
|
93 |
+
}
|
94 |
+
$this->set_product_wpid($this->product_post_wpid);
|
95 |
+
$upload_success =
|
96 |
+
self::$commerce->ajax_sync_all_fb_products_using_feed(true);
|
97 |
+
if ($upload_success) {
|
98 |
+
// verification Step.
|
99 |
+
// Wait till FB finish backend creation to prevent race condition.
|
100 |
+
$time_start = microtime(true);
|
101 |
+
while ((microtime(true) - $time_start) < self::MAX_SLEEP_IN_SEC) {
|
102 |
+
$complete = self::$commerce->fbproductfeed->is_upload_complete(
|
103 |
+
self::$commerce->settings);
|
104 |
+
if ($complete) {
|
105 |
+
break;
|
106 |
+
} else {
|
107 |
+
$this->sleep_til_upload_complete(10);
|
108 |
+
}
|
109 |
+
}
|
110 |
+
$this->sleep_til_upload_complete(60);
|
111 |
+
$check_product_create = $this->check_product_create();
|
112 |
+
if (!$check_product_create) {
|
113 |
+
self::$test_pass = 0;
|
114 |
+
} else {
|
115 |
+
WC_Facebookcommerce_Utils::log(
|
116 |
+
'Test - Products create successfully.');
|
117 |
+
}
|
118 |
+
// Clean up whatever has been created.
|
119 |
+
// Test on_product_delete API hook.
|
120 |
+
$clean_up = $this->clean_up();
|
121 |
+
if (!$clean_up) {
|
122 |
+
self::$test_pass = 0;
|
123 |
+
WC_Facebookcommerce_Utils::log(
|
124 |
+
'Test - Fail to delete product from FB');
|
125 |
+
WC_Facebookcommerce_Utils::set_test_fail_reason(
|
126 |
+
'Fail to delete product from FB',
|
127 |
+
(new Exception)->getTraceAsString());
|
128 |
+
} else {
|
129 |
+
WC_Facebookcommerce_Utils::log(
|
130 |
+
'Test - Delete product from FB successfully');
|
131 |
+
}
|
132 |
+
} else {
|
133 |
+
self::$test_pass = 0;
|
134 |
+
WC_Facebookcommerce_Utils::log(
|
135 |
+
'Test - Sync all products using feed, curl failed.');
|
136 |
+
WC_Facebookcommerce_Utils::set_test_fail_reason(
|
137 |
+
'Sync all products using feed, curl failed',
|
138 |
+
(new Exception)->getTraceAsString());
|
139 |
+
}
|
140 |
+
|
141 |
+
} else {
|
142 |
+
self::$test_pass = 0;
|
143 |
+
WC_Facebookcommerce_Utils::log(
|
144 |
+
'Test - Fail to remove FBIDs from local DB');
|
145 |
+
WC_Facebookcommerce_Utils::set_test_fail_reason(
|
146 |
+
'Fail to remove FBIDs from local DB',
|
147 |
+
(new Exception)->getTraceAsString());
|
148 |
+
}
|
149 |
+
update_option('fb_test_pass', self::$test_pass);
|
150 |
+
wp_die();
|
151 |
+
return;
|
152 |
+
}
|
153 |
+
|
154 |
+
function check_product_create() {
|
155 |
+
if (count(self::$retailer_ids) < 3) {
|
156 |
+
WC_Facebookcommerce_Utils::log('Test - Failed to create 3 product items.');
|
157 |
+
WC_Facebookcommerce_Utils::set_test_fail_reason(
|
158 |
+
'Failed to create 3 product items.',
|
159 |
+
(new Exception)->getTraceAsString());
|
160 |
+
return false;
|
161 |
+
}
|
162 |
+
|
163 |
+
if (count(self::$retailer_ids) > 3) {
|
164 |
+
WC_Facebookcommerce_Utils::log(
|
165 |
+
'Test - Failed to skip invisible products.');
|
166 |
+
WC_Facebookcommerce_Utils::set_test_fail_reason(
|
167 |
+
'Failed to skip invisible products.',
|
168 |
+
(new Exception)->getTraceAsString());
|
169 |
+
return false;
|
170 |
+
}
|
171 |
+
|
172 |
+
// Check 3 products have been created.
|
173 |
+
for ($i = 0; $i < 3; $i++) {
|
174 |
+
$product_type = $i == 0? 'Simple' : 'Variable';
|
175 |
+
$retailer_id = self::$retailer_ids[$i];
|
176 |
+
$item_fbid =
|
177 |
+
$this->check_fbid_api(self::FB_PRODUCT_ITEM_ID, $retailer_id);
|
178 |
+
$group_fbid =
|
179 |
+
$this->check_fbid_api(self::FB_PRODUCT_GROUP_ID, $retailer_id);
|
180 |
+
if (!$item_fbid || !$group_fbid) {
|
181 |
+
WC_Facebookcommerce_Utils::log('Test - ' . $product_type .
|
182 |
+
' product failed to create.');
|
183 |
+
WC_Facebookcommerce_Utils::set_test_fail_reason($product_type .
|
184 |
+
' product failed to create.', (new Exception)->getTraceAsString());
|
185 |
+
return false;
|
186 |
+
}
|
187 |
+
}
|
188 |
+
|
189 |
+
// Check product detailed as expected.
|
190 |
+
$data = array(
|
191 |
+
'name' => 'a simple product for test',
|
192 |
+
'price' => '20.00',
|
193 |
+
'description' => 'This is to test a simple product.',
|
194 |
+
'sale_price' => '10.00',
|
195 |
+
'sale_price_dates_from' =>
|
196 |
+
date_i18n('Y-m-d', strtotime('now')) . self::MIN_TIME,
|
197 |
+
'sale_price_dates_to' =>
|
198 |
+
date_i18n('Y-m-d', strtotime('+10 day')) . self::MAX_TIME,
|
199 |
+
'visibility' => 'published',
|
200 |
+
);
|
201 |
+
$simple_product_result =
|
202 |
+
$this->check_product_info(self::$retailer_ids[0], false, $data);
|
203 |
+
if (!$simple_product_result) {
|
204 |
+
WC_Facebookcommerce_Utils::log('Test - Simple product failed to match ' .
|
205 |
+
'product details.');
|
206 |
+
WC_Facebookcommerce_Utils::set_test_fail_reason('Simple product failed to'
|
207 |
+
. ' match product details.', (new Exception)->getTraceAsString());
|
208 |
+
return false;
|
209 |
+
}
|
210 |
+
|
211 |
+
$data = array(
|
212 |
+
'name' => 'a variable product for test',
|
213 |
+
'price' => '30.00',
|
214 |
+
'description' => 'This is to test a variable product. - Red',
|
215 |
+
'additional_variant_attributes' => array('value' => 'Red'),
|
216 |
+
'visibility' => 'published',
|
217 |
+
);
|
218 |
+
$variable_product_result =
|
219 |
+
$this->check_product_info(self::$retailer_ids[1], true, $data);
|
220 |
+
if (!$variable_product_result) {
|
221 |
+
WC_Facebookcommerce_Utils::log(
|
222 |
+
'Test - Variable product failed to match product details.');
|
223 |
+
WC_Facebookcommerce_Utils::set_test_fail_reason(
|
224 |
+
'Variable product failed to match product details.',
|
225 |
+
(new Exception)->getTraceAsString());
|
226 |
+
return false;
|
227 |
+
}
|
228 |
+
return true;
|
229 |
+
}
|
230 |
+
|
231 |
+
function check_fbid_api($fbid_type, $fb_retailer_id) {
|
232 |
+
$product_fbid_result = self::$fbgraph->get_facebook_id(
|
233 |
+
self::$commerce->product_catalog_id,
|
234 |
+
$fb_retailer_id,
|
235 |
+
true);
|
236 |
+
|
237 |
+
if (is_wp_error($product_fbid_result)) {
|
238 |
+
WC_Facebookcommerce_Utils::log(
|
239 |
+
'Test - ' . $product_fbid_result->get_error_message());
|
240 |
+
WC_Facebookcommerce_Utils::set_test_fail_reason(
|
241 |
+
'There was an issue connecting to the Facebook API: '.
|
242 |
+
$product_fbid_result->get_error_message(),
|
243 |
+
(new Exception)->getTraceAsString());
|
244 |
+
return false;
|
245 |
+
}
|
246 |
+
|
247 |
+
if ($product_fbid_result && isset($product_fbid_result['body'])) {
|
248 |
+
$body = WC_Facebookcommerce_Utils::decode_json(
|
249 |
+
$product_fbid_result['body'], true);
|
250 |
+
if ($body && isset($body['id'])) {
|
251 |
+
if ($fbid_type == self::FB_PRODUCT_GROUP_ID) {
|
252 |
+
$fb_id =
|
253 |
+
isset($body['product_group'])
|
254 |
+
? $body['product_group']['id']
|
255 |
+
: false;
|
256 |
+
} else {
|
257 |
+
$fb_id = $body['id'];
|
258 |
+
}
|
259 |
+
return $fb_id;
|
260 |
+
}
|
261 |
+
}
|
262 |
+
|
263 |
+
return false;
|
264 |
+
}
|
265 |
+
|
266 |
+
function check_product_info($retailer_id, $has_variant, $data) {
|
267 |
+
$prod_info_result = self::$fbgraph->check_product_info(
|
268 |
+
self::$commerce->product_catalog_id,
|
269 |
+
$retailer_id,
|
270 |
+
$has_variant);
|
271 |
+
if (is_wp_error($prod_info_result)) {
|
272 |
+
WC_Facebookcommerce_Utils::log(
|
273 |
+
'Test - ' . $prod_info_result->get_error_message());
|
274 |
+
WC_Facebookcommerce_Utils::set_test_fail_reason(
|
275 |
+
'There was an issue connecting to the Facebook API: '.
|
276 |
+
$prod_info_result->get_error_message(),
|
277 |
+
(new Exception)->getTraceAsString());
|
278 |
+
return false;
|
279 |
+
}
|
280 |
+
|
281 |
+
$match = true;
|
282 |
+
if ($prod_info_result && isset($prod_info_result['body'])) {
|
283 |
+
$body = WC_Facebookcommerce_Utils::decode_json(
|
284 |
+
$prod_info_result['body'], true);
|
285 |
+
if (!$body) {
|
286 |
+
return false;
|
287 |
+
}
|
288 |
+
if ($body['name'] != $data['name']) {
|
289 |
+
WC_Facebookcommerce_Utils::log(
|
290 |
+
'Test - ' . $retailer_id . " doesn\'t match name.");
|
291 |
+
$match = false;
|
292 |
+
}
|
293 |
+
|
294 |
+
if ($body['description'] != $data['description']) {
|
295 |
+
WC_Facebookcommerce_Utils::log(
|
296 |
+
'Test - ' . $retailer_id . " doesn\'t match description.");
|
297 |
+
$match = false;
|
298 |
+
}
|
299 |
+
// Woo doesn't have API to return currency symbol.
|
300 |
+
// FB graph API only support to response with a currency symbol price.
|
301 |
+
// No php built-in function to support cast html number to symbol.
|
302 |
+
// Compare numeric price only.
|
303 |
+
$price = floatval(preg_replace('/[^\d\.]+/', '', $body['price']));
|
304 |
+
if ($price != $data['price']) {
|
305 |
+
WC_Facebookcommerce_Utils::log(
|
306 |
+
'Test - ' . $retailer_id . " doesn\'t match price.");
|
307 |
+
$match = false;
|
308 |
+
}
|
309 |
+
// Check sale price and dates.
|
310 |
+
if (isset($data['sale_price'])) {
|
311 |
+
$sale_price = floatval(
|
312 |
+
preg_replace('/[^\d\.]+/', '', $body['sale_price']));
|
313 |
+
if ($sale_price != $data['sale_price']) {
|
314 |
+
WC_Facebookcommerce_Utils::log(
|
315 |
+
'Test - ' . $retailer_id . " doesn\'t match sale price.");
|
316 |
+
$match = false;
|
317 |
+
}
|
318 |
+
if ($body['sale_price_start_date'] != $data['sale_price_dates_from']) {
|
319 |
+
WC_Facebookcommerce_Utils::log(
|
320 |
+
'Test - ' . $retailer_id . " doesn\'t match sale price start date");
|
321 |
+
$match = false;
|
322 |
+
}
|
323 |
+
if ($body['sale_price_end_date'] != $data['sale_price_dates_to']) {
|
324 |
+
WC_Facebookcommerce_Utils::log(
|
325 |
+
'Test - ' . $retailer_id . " doesn\'t match sale price end date.");
|
326 |
+
$match = false;
|
327 |
+
}
|
328 |
+
}
|
329 |
+
|
330 |
+
if ($body['visibility'] != $data['visibility']) {
|
331 |
+
WC_Facebookcommerce_Utils::log(
|
332 |
+
'Test - ' . $retailer_id . " doesn\'t match visibility.");
|
333 |
+
$match = false;
|
334 |
+
}
|
335 |
+
|
336 |
+
if ($has_variant &&
|
337 |
+
(!isset($body['additional_variant_attributes']) ||
|
338 |
+
$body['additional_variant_attributes'][0]['value'] !=
|
339 |
+
$data['additional_variant_attributes']['value'])) {
|
340 |
+
|
341 |
+
WC_Facebookcommerce_Utils::log(
|
342 |
+
'Test - ' . $retailer_id . " doesn\'t match variation.");
|
343 |
+
$match = false;
|
344 |
+
}
|
345 |
+
}
|
346 |
+
return $match;
|
347 |
+
}
|
348 |
+
|
349 |
+
// Don't early return to prevent haunting product id.
|
350 |
+
function clean_up() {
|
351 |
+
$failure = false;
|
352 |
+
foreach (self::$wp_post_ids as $post_id) {
|
353 |
+
$delete_post_result = wp_delete_post($post_id);
|
354 |
+
// return false or null if failed.
|
355 |
+
if (!$delete_post_result) {
|
356 |
+
WC_Facebookcommerce_Utils::log('Test - Fail to delete post ' . $post_id);
|
357 |
+
WC_Facebookcommerce_Utils::set_test_fail_reason(
|
358 |
+
'Fail to delete post ' . $post_id, (new Exception)->getTraceAsString());
|
359 |
+
$failure = true;
|
360 |
+
}
|
361 |
+
}
|
362 |
+
self::$wp_post_ids = array();
|
363 |
+
|
364 |
+
$this->sleep_til_upload_complete(60);
|
365 |
+
foreach (self::$retailer_ids as $retailer_id) {
|
366 |
+
$item_fbid =
|
367 |
+
$this->check_fbid_api(self::FB_PRODUCT_ITEM_ID, $retailer_id);
|
368 |
+
$group_fbid =
|
369 |
+
$this->check_fbid_api(self::FB_PRODUCT_GROUP_ID, $retailer_id);
|
370 |
+
if ($item_fbid || $group_fbid) {
|
371 |
+
WC_Facebookcommerce_Utils::log('Test - Failed to delete product ' .
|
372 |
+
$retailer_id . ' via plugin deletion hook.');
|
373 |
+
WC_Facebookcommerce_Utils::set_test_fail_reason(
|
374 |
+
'Failed to delete product ' . $retailer_id .
|
375 |
+
' via plugin deletion hook.',
|
376 |
+
(new Exception)->getTraceAsString());
|
377 |
+
$failure = true;
|
378 |
+
}
|
379 |
+
}
|
380 |
+
self::$retailer_ids = array();
|
381 |
+
|
382 |
+
return !$failure;
|
383 |
+
}
|
384 |
+
|
385 |
+
function create_data() {
|
386 |
+
$prod_and_variant_wpid = array();
|
387 |
+
// Gets term object from Accessories in the database.
|
388 |
+
$term = get_term_by('name', 'Accessories', 'product_cat');
|
389 |
+
// Accessories should be a default category.
|
390 |
+
// If not exist, set categories term first.
|
391 |
+
if (!$term) {
|
392 |
+
$term = wp_insert_term(
|
393 |
+
'Accessories', // the term
|
394 |
+
'product_cat', // the taxonomy
|
395 |
+
array(
|
396 |
+
'slug' => 'accessories'
|
397 |
+
));
|
398 |
+
}
|
399 |
+
$data = array (
|
400 |
+
'post_content' => 'This is to test a simple product.',
|
401 |
+
'post_title' => 'a simple product for test',
|
402 |
+
'post_status' => 'publish',
|
403 |
+
'post_type' => 'product',
|
404 |
+
'term' => $term,
|
405 |
+
'price' => 20,
|
406 |
+
'sale_price' => 10,
|
407 |
+
'sale_price_dates_from' => strtotime('now'),
|
408 |
+
'sale_price_dates_to' => strtotime('+10 day'),
|
409 |
+
);
|
410 |
+
$simple_product_result =
|
411 |
+
$this->create_test_simple_product($data, $prod_and_variant_wpid);
|
412 |
+
|
413 |
+
if (!$simple_product_result) {
|
414 |
+
return false;
|
415 |
+
}
|
416 |
+
|
417 |
+
// Test an invisible product - invisible products won't be synced by feed.
|
418 |
+
$data['visibility'] = false;
|
419 |
+
$simple_product_result =
|
420 |
+
$this->create_test_simple_product($data, $prod_and_variant_wpid);
|
421 |
+
|
422 |
+
if (!$simple_product_result) {
|
423 |
+
return false;
|
424 |
+
}
|
425 |
+
|
426 |
+
$data['post_content'] = 'This is to test a variable product.';
|
427 |
+
$data['post_title'] = 'a variable product for test';
|
428 |
+
$data['price'] = 30;
|
429 |
+
|
430 |
+
// Test variable products.
|
431 |
+
$variable_product_result =
|
432 |
+
$this->create_test_variable_product($data, $prod_and_variant_wpid);
|
433 |
+
if (!$variable_product_result) {
|
434 |
+
return false;
|
435 |
+
}
|
436 |
+
return $prod_and_variant_wpid;
|
437 |
+
}
|
438 |
+
|
439 |
+
function create_test_simple_product($data, &$prod_and_variant_wpid) {
|
440 |
+
$post_id = $this->fb_insert_post($data, 'Simple');
|
441 |
+
if (!$post_id) {
|
442 |
+
return false;
|
443 |
+
}
|
444 |
+
array_push($prod_and_variant_wpid, $post_id);
|
445 |
+
update_post_meta($post_id, '_regular_price', $data['price']);
|
446 |
+
update_post_meta($post_id, '_sale_price', $data['sale_price']);
|
447 |
+
update_post_meta($post_id, '_sale_price_dates_from', $data['sale_price_dates_from']);
|
448 |
+
update_post_meta($post_id, '_sale_price_dates_to', $data['sale_price_dates_to']);
|
449 |
+
|
450 |
+
wp_set_object_terms($post_id, 'simple', 'product_type');
|
451 |
+
// Invisible products won't be synced by feed.
|
452 |
+
if (isset($data['visibility'])) {
|
453 |
+
$terms = array('exclude-from-catalog', 'exclude-from-search');
|
454 |
+
wp_set_object_terms($post_id, $terms, 'product_visibility');
|
455 |
+
} else {
|
456 |
+
array_push(self::$wp_post_ids, $post_id);
|
457 |
+
array_push(self::$retailer_ids, 'wc_post_id_' . $post_id);
|
458 |
+
}
|
459 |
+
|
460 |
+
$product = wc_get_product($post_id);
|
461 |
+
$product->set_stock_status('instock');
|
462 |
+
wp_set_object_terms($post_id, $data['term']->term_id, 'product_cat');
|
463 |
+
return true;
|
464 |
+
}
|
465 |
+
|
466 |
+
function create_test_variable_product($data, &$prod_and_variant_wpid) {
|
467 |
+
$post_id = $this->fb_insert_post($data, 'Variable');
|
468 |
+
if (!$post_id) {
|
469 |
+
return false;
|
470 |
+
}
|
471 |
+
|
472 |
+
wp_set_object_terms($post_id, 'variable', 'product_type');
|
473 |
+
array_push($prod_and_variant_wpid, $post_id);
|
474 |
+
array_push(self::$wp_post_ids, $post_id);
|
475 |
+
// Gets term object from Accessories in the database.
|
476 |
+
$term = get_term_by('name', 'Accessories', 'product_cat');
|
477 |
+
wp_set_object_terms($post_id, $term->term_id, 'product_cat');
|
478 |
+
|
479 |
+
// Set up attributes.
|
480 |
+
$avail_attribute_values = array(
|
481 |
+
'Red',
|
482 |
+
'Blue'
|
483 |
+
);
|
484 |
+
wp_set_object_terms($post_id, $avail_attribute_values, 'pa_color');
|
485 |
+
$thedata = array(
|
486 |
+
'pa_color' => array(
|
487 |
+
'name' => 'pa_color',
|
488 |
+
'value' => '',
|
489 |
+
'is_visible' => '1',
|
490 |
+
'is_variation' => '1',
|
491 |
+
'is_taxonomy' => '1'
|
492 |
+
)
|
493 |
+
);
|
494 |
+
update_post_meta($post_id, '_product_attributes', $thedata);
|
495 |
+
|
496 |
+
// Insert variations.
|
497 |
+
$variation_data = array(
|
498 |
+
'post_content' => 'This is to test a variable product. - Red',
|
499 |
+
'post_status' => 'publish',
|
500 |
+
'post_type' => 'product_variation',
|
501 |
+
'post_parent' => $post_id,
|
502 |
+
'price' => 30,
|
503 |
+
);
|
504 |
+
$variation_red = $this->fb_insert_post($variation_data, 'Variation');
|
505 |
+
if (!$variation_red) {
|
506 |
+
return;
|
507 |
+
}
|
508 |
+
|
509 |
+
$this->fb_update_variation_meta(
|
510 |
+
$prod_and_variant_wpid, $variation_red, 'Red', $variation_data);
|
511 |
+
|
512 |
+
$variation_data['post_content'] = 'a variable product for test - Blue';
|
513 |
+
$variation_blue = $this->fb_insert_post($variation_data, 'Variatoin');
|
514 |
+
if (!$variation_blue) {
|
515 |
+
return false;
|
516 |
+
}
|
517 |
+
$this->fb_update_variation_meta(
|
518 |
+
$prod_and_variant_wpid, $variation_blue, 'Blue', $variation_data);
|
519 |
+
$product = wc_get_product($variation_blue);
|
520 |
+
$product->set_stock_status('instock');
|
521 |
+
wp_set_object_terms($variation_blue, 'variation', 'product_type');
|
522 |
+
return true;
|
523 |
+
}
|
524 |
+
|
525 |
+
function fb_update_variation_meta(
|
526 |
+
&$prod_and_variant_wpid,
|
527 |
+
$variation_id,
|
528 |
+
$value,
|
529 |
+
$data) {
|
530 |
+
array_push($prod_and_variant_wpid, $variation_id);
|
531 |
+
array_push(self::$retailer_ids, 'wc_post_id_' . $variation_id);
|
532 |
+
|
533 |
+
$attribute_term = get_term_by('name', $value, 'pa_color');
|
534 |
+
|
535 |
+
update_post_meta($variation_id, 'attribute_pa_color', $attribute_term->slug);
|
536 |
+
update_post_meta($variation_id, '_price', $data['price']);
|
537 |
+
update_post_meta($variation_id, '_regular_price', $data['price']);
|
538 |
+
wp_set_object_terms($variation_id, 'variation', 'product_type');
|
539 |
+
$product = wc_get_product($variation_id);
|
540 |
+
$product->set_stock_status('instock');
|
541 |
+
}
|
542 |
+
|
543 |
+
function fb_insert_post($data, $p_type) {
|
544 |
+
$postarr = array_intersect_key(
|
545 |
+
$data,
|
546 |
+
array_flip(array(
|
547 |
+
'post_content',
|
548 |
+
'post_title',
|
549 |
+
'post_status',
|
550 |
+
'post_type',
|
551 |
+
'post_parent',
|
552 |
+
)));
|
553 |
+
$post_id = wp_insert_post($postarr);
|
554 |
+
if (is_wp_error($post_id)) {
|
555 |
+
WC_Facebookcommerce_Utils::log('Test - ' . $p_type .
|
556 |
+
' product wp_insert_post' . 'failed: ' . json_encode($post_id));
|
557 |
+
return false;
|
558 |
+
} else {
|
559 |
+
return $post_id;
|
560 |
+
}
|
561 |
+
}
|
562 |
+
|
563 |
+
/**
|
564 |
+
* IMPORTANT! Wait for Ents creation and prevent race condition.
|
565 |
+
**/
|
566 |
+
function sleep_til_upload_complete($sec) {
|
567 |
+
sleep($sec);
|
568 |
+
}
|
569 |
+
|
570 |
+
function set_product_wpid($product_post_wpid) {
|
571 |
+
WC_Facebook_Product_Feed_Test_Mock::$product_post_wpid = $product_post_wpid;
|
572 |
+
}
|
573 |
+
}
|
574 |
+
|
575 |
+
endif;
|
includes/test/fbproductfeed-test.php
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
4 |
+
*
|
5 |
+
* This source code is licensed under the license found in the
|
6 |
+
* LICENSE file in the root directory of this source tree.
|
7 |
+
*
|
8 |
+
* @package FacebookCommerce
|
9 |
+
*/
|
10 |
+
|
11 |
+
if (! defined('ABSPATH')) {
|
12 |
+
exit;
|
13 |
+
}
|
14 |
+
|
15 |
+
include_once(dirname(__DIR__) . '/fbproductfeed.php');
|
16 |
+
include_once(dirname(__DIR__) . '/fbutils.php');
|
17 |
+
|
18 |
+
if (! class_exists('WC_Facebook_Product_Feed_Test')) :
|
19 |
+
/**
|
20 |
+
* Mock for Facebook feed class
|
21 |
+
*/
|
22 |
+
class WC_Facebook_Product_Feed_Test_Mock extends WC_Facebook_Product_Feed {
|
23 |
+
|
24 |
+
public static $product_post_wpid = null;
|
25 |
+
|
26 |
+
// Return test product post id.
|
27 |
+
// Don't mess up actual products.
|
28 |
+
public function get_product_wpid() {
|
29 |
+
return self::$product_post_wpid;
|
30 |
+
}
|
31 |
+
|
32 |
+
// Log progress in local log file for testing.
|
33 |
+
// Not to overwhelm DB log to track important signals.
|
34 |
+
public function log_feed_progress($msg, $object = array()) {
|
35 |
+
$msg = empty($object) ? $msg : $msg . json_encode($object);
|
36 |
+
WC_Facebookcommerce_Utils::log('Test - ' . $msg);
|
37 |
+
}
|
38 |
+
}
|
39 |
+
|
40 |
+
endif;
|
readme.txt
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
=== Facebook for WooCommerce ===
|
2 |
+
Contributors: facebook
|
3 |
+
Tags: facebook, shop, catalog, advertise, pixel, product
|
4 |
+
Requires at least: 4.4
|
5 |
+
Tested up to: 4.9.8
|
6 |
+
Stable tag: 1.9.5
|
7 |
+
Requires PHP: 5.6
|
8 |
+
MySQL: 5.6 or greater
|
9 |
+
License: GPLv2 or later
|
10 |
+
License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
11 |
+
|
12 |
+
Get the Official Facebook for WooCommerce plugin for two powerful ways to help grow your business, including an ads extension and shops tab for your page.
|
13 |
+
|
14 |
+
== Description ==
|
15 |
+
|
16 |
+
This is the official Facebook for WooCommerce plugin that connects your WooCommerce website to Facebook. With this plugin, you can install the Facebook pixel, upload your online store catalog, and create a shop on your Facebook page, enabling you to easily run dynamic ads.
|
17 |
+
|
18 |
+
Marketing on Facebook helps your business build lasting relationships with people, find new customers, and increase sales for your online store. With this Facebook ad extension, reaching the people who matter most to your business is simple. This extension will track the results of your advertising across devices. It will also help you:
|
19 |
+
|
20 |
+
* Maximize your campaign performance. By setting up the Facebook pixel and building your audience, you will optimize your ads for people likely to buy your products, and reach people with relevant ads on Facebook after they’ve visited your website.
|
21 |
+
* Find more customers. Connecting your product catalog automatically creates carousel ads that showcase the products you sell and attract more shoppers to your website.
|
22 |
+
* Generate sales among your website visitors. When you set up the Facebook pixel and connect your product catalog, you can use dynamic ads to reach shoppers when they’re on Facebook with ads for the products they viewed on your website. This will be included in a future release of Facebook for WooCommerce.
|
23 |
+
|
24 |
+
== Installation ==
|
25 |
+
|
26 |
+
Visit the Facebook Help Center [here](https://www.facebook.com/business/help/900699293402826).
|
27 |
+
|
28 |
+
== Support ==
|
29 |
+
|
30 |
+
If you believe you have found a security vulnerability on Facebook, we encourage you to let us know right away. We investigate all legitimate reports and do our best to quickly fix the problem. Before reporting, please review [this page](https://www.facebook.com/whitehat), which includes our responsible disclosure policy and reward guideline. You can submit bugs [here](https://github.com/facebookincubator/facebook-for-woocommerce/issues) or contact advertising support [here](https://www.facebook.com/business/help/900699293402826).
|
31 |
+
|
32 |
+
When opening a bug on GitHub, please give us as many details as possible.
|
33 |
+
|
34 |
+
* Symptoms of your problem
|
35 |
+
* Screenshot, if possible
|
36 |
+
* Your Facebook page URL
|
37 |
+
* Your website URL
|
38 |
+
* Current version of Facebook-for-WooCommerce, WooCommerce, Wordpress, PHP
|
39 |
+
|
40 |
+
== Changelog ==
|
41 |
+
|
42 |
+
= 1.9.11 - 2019-02-26 =
|
43 |
+
* changing contributor to facebook from facebook4woocommerce, so that
|
44 |
+
woo plugin will be shown under
|
45 |
+
https://profiles.wordpress.org/facebook/#content-plugins
|
46 |
+
* adding changelog in readme.txt so that notifications will be sent for
|
47 |
+
updates and changelog will be shown under
|
48 |
+
https://wordpress.org/plugins/facebook-for-woocommerce/#developers
|
49 |
+
* removing debug flags notice under facebook-for-woocommerce.php so that
|
50 |
+
developers will be able to debug with debug logs
|
51 |
+
|
52 |
+
= 1.9.10 - 2019-02-11 =
|
53 |
+
* Add facebook support link, this will help merchants to reach out to facebook customer service.
|
54 |
+
* Make plugin wordpress compatible by removing woocommerce updater and removing woo_include
|
55 |
+
|
56 |
+
= 1.9.9 - 2018-12-30 =
|
57 |
+
* Fix issue with missing file in v1.9.8
|
58 |
+
* Remove misleading content relating to Instagram which is not launched yet.
|