Version Description
Download this release
Release Info
Developer | MailChimp |
Plugin | MailChimp for WooCommerce |
Version | 1.0.0 |
Comparing to | |
See all releases |
Version 1.0.0
- LICENSE.txt +339 -0
- README.md +97 -0
- README.txt +13 -0
- admin/class-mailchimp-woocommerce-admin.php +631 -0
- admin/css/mailchimp-woocommerce-admin.css +4 -0
- admin/index.php +1 -0
- admin/js/mailchimp-woocommerce-admin.js +32 -0
- admin/partials/mailchimp-woocommerce-admin-tabs.php +90 -0
- admin/partials/tabs/api_key.php +46 -0
- admin/partials/tabs/campaign_defaults.php +73 -0
- admin/partials/tabs/errors/missing_api_key.php +3 -0
- admin/partials/tabs/errors/missing_campaign_defaults.php +3 -0
- admin/partials/tabs/errors/missing_list.php +3 -0
- admin/partials/tabs/errors/missing_store.php +3 -0
- admin/partials/tabs/errors/not_ready_for_sync.php +3 -0
- admin/partials/tabs/newsletter_settings.php +89 -0
- admin/partials/tabs/notices.php +8 -0
- admin/partials/tabs/store_info.php +148 -0
- admin/partials/tabs/store_sync.php +30 -0
- admin/partials/tabs/success/re-sync-started.php +3 -0
- changelog.md +17 -0
- includes/api/assets/class-mailchimp-address.php +337 -0
- includes/api/assets/class-mailchimp-cart.php +275 -0
- includes/api/assets/class-mailchimp-customer.php +257 -0
- includes/api/assets/class-mailchimp-line-item.php +180 -0
- includes/api/assets/class-mailchimp-order.php +405 -0
- includes/api/assets/class-mailchimp-product-variation.php +250 -0
- includes/api/assets/class-mailchimp-product.php +281 -0
- includes/api/assets/class-mailchimp-store.php +320 -0
- includes/api/class-mailchimp-api.php +1003 -0
- includes/api/class-mailchimp-woocommerce-api.php +100 -0
- includes/api/class-mailchimp-woocommerce-create-list-submission.php +162 -0
- includes/api/class-mailchimp-woocommerce-transform-orders.php +398 -0
- includes/api/class-mailchimp-woocommerce-transform-products.php +193 -0
- includes/api/errors/class-mailchimp-error.php +14 -0
- includes/api/errors/class-mailchimp-server-error.php +14 -0
- includes/api/helpers/class-mailchimp-woocommerce-api-currency-codes.php +133 -0
- includes/api/helpers/class-mailchimp-woocommerce-api-locales.php +469 -0
- includes/class-mailchimp-woocommerce-activator.php +117 -0
- includes/class-mailchimp-woocommerce-deactivator.php +39 -0
- includes/class-mailchimp-woocommerce-i18n.php +47 -0
- includes/class-mailchimp-woocommerce-loader.php +129 -0
- includes/class-mailchimp-woocommerce-newsletter.php +83 -0
- includes/class-mailchimp-woocommerce-options.php +238 -0
- includes/class-mailchimp-woocommerce-service.php +492 -0
- includes/class-mailchimp-woocommerce.php +398 -0
- includes/index.php +1 -0
- includes/plugin-update-checker/README.md +105 -0
- includes/plugin-update-checker/composer.json +19 -0
- includes/plugin-update-checker/css/puc-debug-bar.css +62 -0
- includes/plugin-update-checker/debug-bar-panel.php +146 -0
- includes/plugin-update-checker/debug-bar-plugin.php +102 -0
- includes/plugin-update-checker/github-checker.php +458 -0
- includes/plugin-update-checker/js/debug-bar.js +52 -0
- includes/plugin-update-checker/languages/plugin-update-checker-fr_FR.mo +0 -0
- includes/plugin-update-checker/languages/plugin-update-checker-fr_FR.po +38 -0
- includes/plugin-update-checker/languages/plugin-update-checker-hu_HU.mo +0 -0
- includes/plugin-update-checker/languages/plugin-update-checker-hu_HU.po +41 -0
- includes/plugin-update-checker/languages/plugin-update-checker.pot +39 -0
- includes/plugin-update-checker/license.txt +7 -0
- includes/plugin-update-checker/plugin-update-checker.php +1641 -0
- includes/plugin-update-checker/vendor/Parsedown.php +1538 -0
- includes/plugin-update-checker/vendor/ParsedownLegacy.php +1535 -0
- includes/plugin-update-checker/vendor/readme-parser.php +331 -0
- includes/processes/class-mailchimp-woocommerce-abstract-sync.php +323 -0
- includes/processes/class-mailchimp-woocommerce-cart-update.php +176 -0
- includes/processes/class-mailchimp-woocommerce-process-orders.php +86 -0
- includes/processes/class-mailchimp-woocommerce-process-products.php +74 -0
- includes/processes/class-mailchimp-woocommerce-single-order.php +144 -0
- includes/processes/class-mailchimp-woocommerce-single-product.php +105 -0
- includes/slack/Contracts/Http/Interactor.php +34 -0
- includes/slack/Contracts/Http/Response.php +26 -0
- includes/slack/Contracts/Http/ResponseFactory.php +15 -0
- includes/slack/Core/Commander.php +508 -0
- includes/slack/Http/CurlInteractor.php +106 -0
- includes/slack/Http/SlackResponse.php +84 -0
- includes/slack/Http/SlackResponseFactory.php +13 -0
- includes/slack/Logger.php +93 -0
- includes/vendor/queue.php +43 -0
- includes/vendor/queue/classes/cli/queue-command.php +120 -0
- includes/vendor/queue/classes/worker/wp-http-worker.php +323 -0
- includes/vendor/queue/classes/worker/wp-worker.php +86 -0
- includes/vendor/queue/classes/wp-job.php +103 -0
- includes/vendor/queue/classes/wp-queue.php +231 -0
- index.php +1 -0
- languages/mailchimp-woocommerce.pot +0 -0
- mailchimp-woocommerce.php +274 -0
- public/class-mailchimp-woocommerce-public.php +109 -0
- public/css/mailchimp-woocommerce-public.css +4 -0
- public/index.php +1 -0
- public/js/mailchimp-woocommerce-public.js +312 -0
- public/partials/mailchimp-woocommerce-public-display.php +16 -0
- uninstall.php +44 -0
LICENSE.txt
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.
|
README.md
ADDED
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# MailChimp for Woocommerce Integration
|
2 |
+
|
3 |
+
# Updated 9.23.16 with a fix to delete carts for completed orders.
|
4 |
+
MailChimp for WooCommerce is a plugin that adds customers and their order information to your MailChimp list. Use WooCommerce data to create targeted campaigns, track sales, recommend products, send Abandoned Cart emails, and more.
|
5 |
+
|
6 |
+
In this article, you’ll learn how to connect MailChimp for WooCommerce.
|
7 |
+
|
8 |
+
#Before You Start#
|
9 |
+
|
10 |
+
**Here are some things to know before you begin this process.**
|
11 |
+
|
12 |
+
- This tutorial is intended for use with a beta version of MailChimp for WooCommerce.
|
13 |
+
- We recommend you use this plugin in a staging environment before installing on production servers.
|
14 |
+
- This process requires an API Key from your MailChimp account. If you aren’t sure how to generate a MailChimp API Key, read [About API Keys.](http://kb.mailchimp.com/integrations/api-integrations/about-api-keys)
|
15 |
+
- This plugin supports MailChimp’s [Abandoned Cart Automation](http://kb.mailchimp.com/automation/create-an-abandoned-cart-workflow) feature.
|
16 |
+
WooCommerce customers who haven't signed up for marketing emails will appear in the Transactional portion of your list, and cannot be exported. See [View or Export a List.](http://kb.mailchimp.com/lists/managing-subscribers/view-or-export-a-list)
|
17 |
+
- To switch lists or accounts, you must deactivate and delete the plugin, then re-install it.
|
18 |
+
- In e-commerce reports and on subscriber profile pages, product variants will display as the parent product.
|
19 |
+
- This beta version of MailChimp for WooCommerce may have issues displaying grouped orders.
|
20 |
+
|
21 |
+
#Task Roadmap#
|
22 |
+
**Here’s a brief overview of this multi-step process.**
|
23 |
+
|
24 |
+
- Install the plugin on your WordPress Admin site.
|
25 |
+
- Connect the plugin with your MailChimp API Key, and configure your list settings to complete the data sync process.
|
26 |
+
|
27 |
+
#Install the Plugin#
|
28 |
+
**To install the plugin, follow these steps.**
|
29 |
+
|
30 |
+
1) Log in to your WordPress admin panel.
|
31 |
+
2) In the left navigation panel, click **Plugins**, and choose **Add New**.
|
32 |
+
|
33 |
+
![Add new] (https://cloud.githubusercontent.com/assets/6547700/18677991/a7622bcc-7f28-11e6-8e8c-9bbdfa9861c7.png)
|
34 |
+
|
35 |
+
3) Click **Upload Plugin**.
|
36 |
+
|
37 |
+
![Upload] (https://cloud.githubusercontent.com/assets/6547700/18677997/a76dab82-7f28-11e6-98e4-4309739cd840.png)
|
38 |
+
|
39 |
+
4) Click **Choose File** to select the ZIP file for the plugin, then click **Install Now**.
|
40 |
+
|
41 |
+
![Install Now](https://cloud.githubusercontent.com/assets/6547700/18677988/a760949c-7f28-11e6-9e13-13c23d044ad4.png)
|
42 |
+
|
43 |
+
5) Click **Activate Plugin**.
|
44 |
+
|
45 |
+
![Activate plugin](https://cloud.githubusercontent.com/assets/6547700/18677990/a760d7c2-7f28-11e6-8741-12c1efa7a991.png)
|
46 |
+
|
47 |
+
After you activate the plugin, you’ll be taken to the **Settings** page, where you will add your API key and configure your list settings.
|
48 |
+
|
49 |
+
#Configure and Sync#
|
50 |
+
**To configure your MailChimp settings for WooCommerce customers and sync them to MailChimp, follow these steps.**
|
51 |
+
|
52 |
+
1) On the **Connect** tab, paste your MailChimp API key into the field, choose whether or not you want to send debugging logs to MailChimp, and click **Save all changes**.
|
53 |
+
|
54 |
+
![API key] (https://cloud.githubusercontent.com/assets/19805049/18877771/3fca90e8-849c-11e6-9e3a-161a7b3936dd.png)
|
55 |
+
|
56 |
+
2) Navigate to the **Store Settings** tab.
|
57 |
+
|
58 |
+
![Store Settings](https://cloud.githubusercontent.com/assets/6547700/18677998/a76e5640-7f28-11e6-9fd3-d66949fa1413.png)
|
59 |
+
|
60 |
+
3) Enter the contact and location details for your WooCommerce Store, and click **Save all changes**.
|
61 |
+
|
62 |
+
![Save all changes] (https://cloud.githubusercontent.com/assets/6547700/18677996/a76d126c-7f28-11e6-9150-4b289d20f057.png)
|
63 |
+
|
64 |
+
4) Navigate to the **List Settings** tab.
|
65 |
+
|
66 |
+
![List Settings tab] (https://cloud.githubusercontent.com/assets/19805049/18878446/961221d0-849e-11e6-99bb-175c22bf921e.png)
|
67 |
+
|
68 |
+
5) Choose the list you want to sync, decide whether or not you want to auto-subscribe existing customers, set the subscribe message you want customers to see at checkout, and click **Save all changes**.
|
69 |
+
|
70 |
+
![Save all changes](https://cloud.githubusercontent.com/assets/19805049/18877772/3fd24162-849c-11e6-8442-79ec4550b8ac.png)
|
71 |
+
|
72 |
+
All set! When you click **Save all changes**, we’ll start syncing your WooCommerce customers to MailChimp. To view progress, check the **Sync Status** tab.
|
73 |
+
|
74 |
+
If you have no lists in your MailChimp account, you will be given the option to create a new list on the **List Defaults** tab. To create a new list, set your list defaults, and click **Save all Changes** when you’re done. We’ll create a MailChimp list for you, and begin the data sync.
|
75 |
+
|
76 |
+
![List Defaults tab](https://cloud.githubusercontent.com/assets/19805049/18956260/cffd3926-8628-11e6-9c68-9fe3c964c75c.png)
|
77 |
+
|
78 |
+
#Next Steps#
|
79 |
+
After you connect, you can do a lot with the the data you collect, like build segments, send Automation workflows, track purchases, and view results.
|
80 |
+
|
81 |
+
Find out everything MailChimp has to offer in our article, [How to Use MailChimp for E-Commerce](http://kb.mailchimp.com/integrations/e-commerce/how-to-use-mailchimp-for-e-commerce).
|
82 |
+
|
83 |
+
#Deactivate or Delete the Plugin#
|
84 |
+
When you deactivate MailChimp for WooCommerce, it stops the sync but doesn’t remove the plugin. You can always re-activate the sync, which will backfill data at a later point in time.
|
85 |
+
To deactivate MailChimp for WooCommerce, follow these steps.
|
86 |
+
|
87 |
+
1) Log in to your WordPress admin panel.
|
88 |
+
|
89 |
+
2) In the left navigation panel, click **Plugins**, and choose **Installed Plugins**.
|
90 |
+
|
91 |
+
![Installed Plugins](https://cloud.githubusercontent.com/assets/6547700/18677993/a76542ee-7f28-11e6-99dd-cfd6c1f5c24a.png)
|
92 |
+
|
93 |
+
3) Click the box next to the MailChimp for WooCommerce plugin, and click **Deactivate**.
|
94 |
+
|
95 |
+
![Deactivate](https://cloud.githubusercontent.com/assets/6547700/18677992/a762b844-7f28-11e6-9679-8d6c6a1d731d.png)
|
96 |
+
|
97 |
+
After you deactivate the plugin, you will have the option to **Delete** it. If you delete the plugin, you will retain customers’ email addresses in your list, but remove all associated e-commerce data.
|
README.txt
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
=== vextras-woocommerce ===
|
2 |
+
Contributors: ryanhungate
|
3 |
+
Tags: ecommerce,email,workflows,mailchimp
|
4 |
+
Donate link: https://mailchimp.com
|
5 |
+
Requires at least: 4.3
|
6 |
+
Tested up to: 4.4.2
|
7 |
+
Stable tag: 4.4.2
|
8 |
+
License: GPLv2 or later
|
9 |
+
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
10 |
+
|
11 |
+
MailChimp - WooCommerce integration.
|
12 |
+
|
13 |
+
== Installation ==
|
admin/class-mailchimp-woocommerce-admin.php
ADDED
@@ -0,0 +1,631 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* The admin-specific functionality of the plugin.
|
5 |
+
*
|
6 |
+
* @link https://mailchimp.com
|
7 |
+
* @since 1.0.1
|
8 |
+
*
|
9 |
+
* @package MailChimp_Woocommerce
|
10 |
+
* @subpackage MailChimp_Woocommerce/admin
|
11 |
+
*/
|
12 |
+
|
13 |
+
/**
|
14 |
+
* The admin-specific functionality of the plugin.
|
15 |
+
*
|
16 |
+
* Defines the plugin name, version, and two examples hooks for how to
|
17 |
+
* enqueue the admin-specific stylesheet and JavaScript.
|
18 |
+
*
|
19 |
+
* @package MailChimp_Woocommerce
|
20 |
+
* @subpackage MailChimp_Woocommerce/admin
|
21 |
+
* @author Ryan Hungate <ryan@mailchimp.com>
|
22 |
+
*/
|
23 |
+
class MailChimp_Woocommerce_Admin extends MailChimp_Woocommerce_Options {
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @return MailChimp_Woocommerce_Admin
|
27 |
+
*/
|
28 |
+
public static function connect()
|
29 |
+
{
|
30 |
+
$env = mailchimp_environment_variables();
|
31 |
+
|
32 |
+
return new self('mailchimp-woocommerce', $env->version);
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Initialize the class and set its properties.
|
37 |
+
*
|
38 |
+
* @since 1.0.0
|
39 |
+
* @param string $plugin_name The name of this plugin.
|
40 |
+
* @param string $version The version of this plugin.
|
41 |
+
*/
|
42 |
+
public function __construct( $plugin_name, $version ) {
|
43 |
+
$this->plugin_name = $plugin_name;
|
44 |
+
$this->version = $version;
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Register the stylesheets for the admin area.
|
49 |
+
*
|
50 |
+
* @since 1.0.0
|
51 |
+
*/
|
52 |
+
public function enqueue_styles() {
|
53 |
+
wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/mailchimp-woocommerce-admin.css', array(), $this->version, 'all' );
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Register the JavaScript for the admin area.
|
58 |
+
*
|
59 |
+
* @since 1.0.0
|
60 |
+
*/
|
61 |
+
public function enqueue_scripts() {
|
62 |
+
wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/mailchimp-woocommerce-admin.js', array( 'jquery' ), $this->version, false );
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Register the administration menu for this plugin into the WordPress Dashboard menu.
|
67 |
+
*
|
68 |
+
* @since 1.0.0
|
69 |
+
*/
|
70 |
+
|
71 |
+
public function add_plugin_admin_menu() {
|
72 |
+
/*
|
73 |
+
* Documentation : http://codex.wordpress.org/Administration_Menus
|
74 |
+
*/
|
75 |
+
add_options_page( 'MailChimp - WooCommerce Setup', 'MailChimp', 'manage_options', $this->plugin_name, array($this, 'display_plugin_setup_page'));
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Add settings action link to the plugins page.
|
80 |
+
*
|
81 |
+
* @since 1.0.0
|
82 |
+
*/
|
83 |
+
public function add_action_links($links) {
|
84 |
+
/*
|
85 |
+
* Documentation : https://codex.wordpress.org/Plugin_API/Filter_Reference/plugin_action_links_(plugin_file_name)
|
86 |
+
*/
|
87 |
+
$settings_link = array(
|
88 |
+
'<a href="' . admin_url( 'options-general.php?page=' . $this->plugin_name ) . '">' . __('Settings', $this->plugin_name) . '</a>',
|
89 |
+
);
|
90 |
+
|
91 |
+
return array_merge($settings_link, $links);
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Admin bar
|
96 |
+
*
|
97 |
+
* @param WP_Admin_Bar $wp_admin_bar
|
98 |
+
*/
|
99 |
+
public function admin_bar( $wp_admin_bar ) {
|
100 |
+
if ( ! current_user_can( 'manage_options' ) ) {
|
101 |
+
return;
|
102 |
+
}
|
103 |
+
$wp_admin_bar->add_menu( array(
|
104 |
+
'id' => 'mailchimp-woocommerce',
|
105 |
+
'title' => __('MailChimp', 'mailchimp-woocommerce' ),
|
106 |
+
'href' => '#',
|
107 |
+
));
|
108 |
+
$wp_admin_bar->add_menu( array(
|
109 |
+
'parent' => 'mailchimp-woocommerce',
|
110 |
+
'id' => 'mailchimp-woocommerce-api-key',
|
111 |
+
'title' => __('API Key', 'mailchimp-woocommerce' ),
|
112 |
+
'href' => wp_nonce_url(admin_url('options-general.php?page=mailchimp-woocommerce&tab=api_key'), 'mc-api-key'),
|
113 |
+
));
|
114 |
+
$wp_admin_bar->add_menu( array(
|
115 |
+
'parent' => 'mailchimp-woocommerce',
|
116 |
+
'id' => 'mailchimp-woocommerce-store-info',
|
117 |
+
'title' => __('Store Info', 'mailchimp-woocommerce' ),
|
118 |
+
'href' => wp_nonce_url(admin_url('options-general.php?page=mailchimp-woocommerce&tab=store_info'), 'mc-store-info'),
|
119 |
+
));
|
120 |
+
$wp_admin_bar->add_menu( array(
|
121 |
+
'parent' => 'mailchimp-woocommerce',
|
122 |
+
'id' => 'mailchimp-woocommerce-campaign-defaults',
|
123 |
+
'title' => __('Campaign Defaults', 'mailchimp-woocommerce' ),
|
124 |
+
'href' => wp_nonce_url(admin_url('options-general.php?page=mailchimp-woocommerce&tab=campaign_defaults'), 'mc-campaign-defaults'),
|
125 |
+
));
|
126 |
+
$wp_admin_bar->add_menu( array(
|
127 |
+
'parent' => 'mailchimp-woocommerce',
|
128 |
+
'id' => 'mailchimp-woocommerce-newsletter-settings',
|
129 |
+
'title' => __('Newsletter Settings', 'mailchimp-woocommerce' ),
|
130 |
+
'href' => wp_nonce_url(admin_url('options-general.php?page=mailchimp-woocommerce&tab=newsletter_settings'), 'mc-newsletter-settings'),
|
131 |
+
));
|
132 |
+
|
133 |
+
// only display this button if the data is not syncing and we have a valid api key
|
134 |
+
if ((bool) $this->getOption('mailchimp_list', false) && (bool) $this->getData('sync.syncing', false) === false) {
|
135 |
+
$wp_admin_bar->add_menu( array(
|
136 |
+
'parent' => 'mailchimp-woocommerce',
|
137 |
+
'id' => 'mailchimp-woocommerce-sync',
|
138 |
+
'title' => __('Sync', 'mailchimp-woocommerce'),
|
139 |
+
'href' => wp_nonce_url(admin_url('?mailchimp-woocommerce[action]=sync&mailchimp-woocommerce[action]=sync'), 'mc-sync'),
|
140 |
+
));
|
141 |
+
}
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Render the settings page for this plugin.
|
146 |
+
*
|
147 |
+
* @since 1.0.0
|
148 |
+
*/
|
149 |
+
public function display_plugin_setup_page() {
|
150 |
+
include_once( 'partials/mailchimp-woocommerce-admin-tabs.php' );
|
151 |
+
}
|
152 |
+
|
153 |
+
/**
|
154 |
+
*
|
155 |
+
*/
|
156 |
+
public function options_update() {
|
157 |
+
register_setting($this->plugin_name, $this->plugin_name, array($this, 'validate'));
|
158 |
+
}
|
159 |
+
|
160 |
+
/**
|
161 |
+
* @param $input
|
162 |
+
* @return array
|
163 |
+
*/
|
164 |
+
public function validate($input) {
|
165 |
+
|
166 |
+
$active_tab = isset($input['mailchimp_active_tab']) ? $input['mailchimp_active_tab'] : null;
|
167 |
+
|
168 |
+
if (empty($active_tab)) {
|
169 |
+
return $this->getOptions();
|
170 |
+
}
|
171 |
+
|
172 |
+
switch ($active_tab) {
|
173 |
+
|
174 |
+
case 'api_key':
|
175 |
+
$data = $this->validatePostApiKey($input);
|
176 |
+
break;
|
177 |
+
|
178 |
+
case 'store_info':
|
179 |
+
$data = $this->validatePostStoreInfo($input);
|
180 |
+
break;
|
181 |
+
|
182 |
+
case 'campaign_defaults' :
|
183 |
+
$data = $this->validatePostCampaignDefaults($input);
|
184 |
+
break;
|
185 |
+
|
186 |
+
case 'newsletter_settings':
|
187 |
+
$data = $this->validatePostNewsletterSettings($input);
|
188 |
+
break;
|
189 |
+
}
|
190 |
+
|
191 |
+
return (isset($data) && is_array($data)) ? array_merge($this->getOptions(), $data) : $this->getOptions();
|
192 |
+
}
|
193 |
+
|
194 |
+
/**
|
195 |
+
* STEP 1.
|
196 |
+
*
|
197 |
+
* Handle the 'api_key' tab post.
|
198 |
+
*
|
199 |
+
* @param $input
|
200 |
+
* @return array
|
201 |
+
*/
|
202 |
+
protected function validatePostApiKey($input)
|
203 |
+
{
|
204 |
+
$data = array(
|
205 |
+
'mailchimp_api_key' => isset($input['mailchimp_api_key']) ? $input['mailchimp_api_key'] : false,
|
206 |
+
'mailchimp_debugging' => isset($input['mailchimp_debugging']) ? $input['mailchimp_debugging'] : false,
|
207 |
+
'mailchimp_account_info_id' => null,
|
208 |
+
'mailchimp_account_info_username' => null,
|
209 |
+
);
|
210 |
+
|
211 |
+
$api = new MailChimpApi($data['mailchimp_api_key']);
|
212 |
+
|
213 |
+
$valid = true;
|
214 |
+
|
215 |
+
if (empty($data['mailchimp_api_key']) || !($profile = $api->ping(true))) {
|
216 |
+
unset($data['mailchimp_api_key']);
|
217 |
+
$valid = false;
|
218 |
+
}
|
219 |
+
|
220 |
+
// tell our reporting system whether or not we had a valid ping.
|
221 |
+
$this->setData('validation.api.ping', $valid);
|
222 |
+
|
223 |
+
if ($valid && isset($profile) && is_array($profile) && array_key_exists('account_id', $profile)) {
|
224 |
+
$data['mailchimp_account_info_id'] = $profile['account_id'];
|
225 |
+
$data['mailchimp_account_info_username'] = $profile['username'];
|
226 |
+
}
|
227 |
+
|
228 |
+
return $data;
|
229 |
+
}
|
230 |
+
|
231 |
+
/**
|
232 |
+
* STEP 2.
|
233 |
+
*
|
234 |
+
* Handle the 'store_info' tab post.
|
235 |
+
*
|
236 |
+
* @param $input
|
237 |
+
* @return array
|
238 |
+
*/
|
239 |
+
protected function validatePostStoreInfo($input)
|
240 |
+
{
|
241 |
+
$data = array(
|
242 |
+
|
243 |
+
// store basics
|
244 |
+
'store_name' => isset($input['store_name']) ? $input['store_name'] : get_option('blogname'),
|
245 |
+
'store_street' => isset($input['store_street']) ? $input['store_street'] : false,
|
246 |
+
'store_city' => isset($input['store_city']) ? $input['store_city'] : false,
|
247 |
+
'store_state' => isset($input['store_state']) ? $input['store_state'] : false,
|
248 |
+
'store_postal_code' => isset($input['store_postal_code']) ? $input['store_postal_code'] : false,
|
249 |
+
'store_country' => isset($input['store_country']) ? $input['store_country'] : false,
|
250 |
+
'store_phone' => isset($input['store_phone']) ? $input['store_phone'] : false,
|
251 |
+
|
252 |
+
// locale info
|
253 |
+
'store_locale' => isset($input['store_locale']) ? $input['store_locale'] : false,
|
254 |
+
'store_timezone' => isset($input['store_timezone']) ? $input['store_timezone'] : false,
|
255 |
+
'store_currency_code' => isset($input['store_currency_code']) ? $input['store_currency_code'] : false,
|
256 |
+
|
257 |
+
'admin_email' => isset($input['admin_email']) && is_email($input['admin_email']) ? $input['admin_email'] : $this->getOption('admin_email', false),
|
258 |
+
);
|
259 |
+
|
260 |
+
if (!$this->hasValidStoreInfo($data)) {
|
261 |
+
|
262 |
+
if (empty($data['admin_email']) || empty($data['store_city']) || empty($data['store_state']) || empty($data['store_postal_code']) || empty($data['store_country']) || empty($data['store_street'])) {
|
263 |
+
add_settings_error('mailchimp_store_settings', '', 'As part of the MailChimp Terms of Use, we require a contact email and a physical mailing address.');
|
264 |
+
}
|
265 |
+
|
266 |
+
$this->setData('validation.store_info', false);
|
267 |
+
return array();
|
268 |
+
}
|
269 |
+
|
270 |
+
$this->setData('validation.store_info', true);
|
271 |
+
|
272 |
+
if ($this->hasValidMailChimpList()) {
|
273 |
+
$this->syncStore(array_merge($this->getOptions(), $data));
|
274 |
+
}
|
275 |
+
|
276 |
+
return $data;
|
277 |
+
}
|
278 |
+
|
279 |
+
/**
|
280 |
+
* STEP 3.
|
281 |
+
*
|
282 |
+
* Handle the 'campaign_defaults' tab post.
|
283 |
+
*
|
284 |
+
* @param $input
|
285 |
+
* @return array
|
286 |
+
*/
|
287 |
+
protected function validatePostCampaignDefaults($input)
|
288 |
+
{
|
289 |
+
$data = array(
|
290 |
+
'campaign_from_name' => isset($input['campaign_from_name']) ? $input['campaign_from_name'] : false,
|
291 |
+
'campaign_from_email' => isset($input['campaign_from_email']) && is_email($input['campaign_from_email']) ? $input['campaign_from_email'] : false,
|
292 |
+
'campaign_subject' => isset($input['campaign_subject']) ? $input['campaign_subject'] : get_option('blogname'),
|
293 |
+
'campaign_language' => isset($input['campaign_language']) ? $input['campaign_language'] : 'en',
|
294 |
+
'campaign_permission_reminder' => isset($input['campaign_permission_reminder']) ? $input['campaign_permission_reminder'] : 'You were subscribed to the newsletter from '.get_option('blogname'),
|
295 |
+
);
|
296 |
+
|
297 |
+
if (!$this->hasValidCampaignDefaults($data)) {
|
298 |
+
$this->setData('validation.campaign_defaults', false);
|
299 |
+
return array();
|
300 |
+
}
|
301 |
+
|
302 |
+
$this->setData('validation.campaign_defaults', true);
|
303 |
+
|
304 |
+
return $data;
|
305 |
+
}
|
306 |
+
|
307 |
+
/**
|
308 |
+
* STEP 4.
|
309 |
+
*
|
310 |
+
* Handle the 'newsletter_settings' tab post.
|
311 |
+
*
|
312 |
+
* @param $input
|
313 |
+
* @return array
|
314 |
+
*/
|
315 |
+
protected function validatePostNewsletterSettings($input)
|
316 |
+
{
|
317 |
+
$data = array(
|
318 |
+
'mailchimp_list' => isset($input['mailchimp_list']) ? $input['mailchimp_list'] : $this->getOption('mailchimp_list', ''),
|
319 |
+
'newsletter_label' => isset($input['newsletter_label']) ? $input['newsletter_label'] : $this->getOption('newsletter_label', 'Subscribe to our newsletter'),
|
320 |
+
'mailchimp_auto_subscribe' => isset($input['mailchimp_auto_subscribe']) ? $input['mailchimp_auto_subscribe'] : $this->getOption('mailchimp_auto_subscribe', '0'),
|
321 |
+
);
|
322 |
+
|
323 |
+
if ($data['mailchimp_list'] === 'create_new') {
|
324 |
+
$data['mailchimp_list'] = $this->createMailChimpList(array_merge($this->getOptions(), $data));
|
325 |
+
}
|
326 |
+
|
327 |
+
// as long as we have a list set, and it's currently in MC as a valid list, let's sync the store.
|
328 |
+
if (!empty($data['mailchimp_list']) && $this->api()->hasList($data['mailchimp_list'])) {
|
329 |
+
|
330 |
+
// sync the store with MC
|
331 |
+
$this->syncStore(array_merge($this->getOptions(), $data));
|
332 |
+
|
333 |
+
// start the sync automatically if the sync is false
|
334 |
+
if ((bool) $this->getData('sync.started_at', false) === false) {
|
335 |
+
$job = new MailChimp_WooCommerce_Process_Products();
|
336 |
+
wp_queue($job);
|
337 |
+
$job->flagStartSync();
|
338 |
+
}
|
339 |
+
}
|
340 |
+
|
341 |
+
return $data;
|
342 |
+
}
|
343 |
+
|
344 |
+
/**
|
345 |
+
* @param null|array $data
|
346 |
+
* @return bool
|
347 |
+
*/
|
348 |
+
public function hasValidStoreInfo($data = null)
|
349 |
+
{
|
350 |
+
return $this->validateOptions(array(
|
351 |
+
'store_name', 'store_street', 'store_city', 'store_state',
|
352 |
+
'store_postal_code', 'store_country', 'store_phone',
|
353 |
+
'store_locale', 'store_timezone', 'store_currency_code',
|
354 |
+
'store_phone',
|
355 |
+
), $data);
|
356 |
+
}
|
357 |
+
|
358 |
+
/**
|
359 |
+
* @param null|array $data
|
360 |
+
* @return bool
|
361 |
+
*/
|
362 |
+
public function hasValidCampaignDefaults($data = null)
|
363 |
+
{
|
364 |
+
return $this->validateOptions(array(
|
365 |
+
'campaign_from_name', 'campaign_from_email', 'campaign_subject', 'campaign_language',
|
366 |
+
'campaign_permission_reminder'
|
367 |
+
), $data);
|
368 |
+
}
|
369 |
+
|
370 |
+
/**
|
371 |
+
* @param null|array $data
|
372 |
+
* @return bool
|
373 |
+
*/
|
374 |
+
public function hasValidApiKey($data = null)
|
375 |
+
{
|
376 |
+
if (!$this->validateOptions(array('mailchimp_api_key'), $data)) {
|
377 |
+
return false;
|
378 |
+
}
|
379 |
+
|
380 |
+
if (($pinged = $this->getCached('api-ping-check', null)) === null) {
|
381 |
+
if (($pinged = $this->api()->ping())) {
|
382 |
+
$this->setCached('api-ping-check', true, 120);
|
383 |
+
}
|
384 |
+
return $pinged;
|
385 |
+
}
|
386 |
+
return $pinged;
|
387 |
+
}
|
388 |
+
|
389 |
+
/**
|
390 |
+
* @return bool
|
391 |
+
*/
|
392 |
+
public function hasValidMailChimpList()
|
393 |
+
{
|
394 |
+
if (!$this->hasValidApiKey()) {
|
395 |
+
add_settings_error('mailchimp_api_key', '', 'You must supply your MailChimp API key to pull the lists.');
|
396 |
+
return false;
|
397 |
+
}
|
398 |
+
|
399 |
+
if (!($this->validateOptions(array('mailchimp_list')))) {
|
400 |
+
return $this->api()->getLists(true);
|
401 |
+
}
|
402 |
+
|
403 |
+
return $this->api()->hasList($this->getOption('mailchimp_list'));
|
404 |
+
}
|
405 |
+
|
406 |
+
/**
|
407 |
+
* @return array|bool
|
408 |
+
*/
|
409 |
+
public function getMailChimpLists()
|
410 |
+
{
|
411 |
+
if (!$this->hasValidApiKey()) {
|
412 |
+
return false;
|
413 |
+
}
|
414 |
+
|
415 |
+
try {
|
416 |
+
if (($pinged = $this->getCached('api-lists', null)) === null) {
|
417 |
+
$pinged = $this->api()->getLists(true);
|
418 |
+
if ($pinged) {
|
419 |
+
$this->setCached('api-lists', $pinged, 120);
|
420 |
+
}
|
421 |
+
return $pinged;
|
422 |
+
}
|
423 |
+
return $pinged;
|
424 |
+
} catch (\Exception $e) {
|
425 |
+
return array();
|
426 |
+
}
|
427 |
+
}
|
428 |
+
|
429 |
+
/**
|
430 |
+
* @return bool
|
431 |
+
*/
|
432 |
+
public function isReadyForSync()
|
433 |
+
{
|
434 |
+
if (!$this->hasValidApiKey()) {
|
435 |
+
return false;
|
436 |
+
}
|
437 |
+
|
438 |
+
if (!$this->getOption('mailchimp_list', false)) {
|
439 |
+
return false;
|
440 |
+
}
|
441 |
+
|
442 |
+
if (!$this->api()->hasList($this->getOption('mailchimp_list'))) {
|
443 |
+
return false;
|
444 |
+
}
|
445 |
+
|
446 |
+
if (!$this->api()->getStore($this->getUniqueStoreID())) {
|
447 |
+
return false;
|
448 |
+
}
|
449 |
+
|
450 |
+
return true;
|
451 |
+
}
|
452 |
+
|
453 |
+
/**
|
454 |
+
* @param null|array $data
|
455 |
+
* @return bool|string
|
456 |
+
*/
|
457 |
+
private function createMailChimpList($data = null)
|
458 |
+
{
|
459 |
+
if (empty($data)) {
|
460 |
+
$data = $this->getOptions();
|
461 |
+
}
|
462 |
+
|
463 |
+
$required = array(
|
464 |
+
'store_name', 'store_street', 'store_city', 'store_state',
|
465 |
+
'store_postal_code', 'store_country', 'campaign_from_name',
|
466 |
+
'campaign_from_email', 'campaign_subject', 'campaign_permission_reminder',
|
467 |
+
);
|
468 |
+
|
469 |
+
foreach ($required as $requirement) {
|
470 |
+
if (!isset($data[$requirement]) || empty($data[$requirement])) {
|
471 |
+
return false;
|
472 |
+
}
|
473 |
+
}
|
474 |
+
|
475 |
+
$submission = new MailChimp_CreateListSubmission();
|
476 |
+
|
477 |
+
// allow the subscribers to choose preferred email type (html or text).
|
478 |
+
$submission->setEmailTypeOption(true);
|
479 |
+
|
480 |
+
// set the store name
|
481 |
+
$submission->setName($data['store_name']);
|
482 |
+
|
483 |
+
// set the campaign defaults
|
484 |
+
$submission->setCampaignDefaults(
|
485 |
+
$data['campaign_from_name'],
|
486 |
+
$data['campaign_from_email'],
|
487 |
+
$data['campaign_subject'],
|
488 |
+
$data['campaign_language']
|
489 |
+
);
|
490 |
+
|
491 |
+
// set the permission reminder message.
|
492 |
+
$submission->setPermissionReminder($data['campaign_permission_reminder']);
|
493 |
+
|
494 |
+
if (isset($data['admin_email']) && !empty($data['admin_email'])) {
|
495 |
+
$submission->setNotifyOnSubscribe($data['admin_email']);
|
496 |
+
$submission->setNotifyOnUnSubscribe($data['admin_email']);
|
497 |
+
}
|
498 |
+
|
499 |
+
$submission->setContact($this->address($data));
|
500 |
+
|
501 |
+
try {
|
502 |
+
$response = $this->api()->createList($submission);
|
503 |
+
|
504 |
+
$list_id = array_key_exists('id', $response) ? $response['id'] : false;
|
505 |
+
|
506 |
+
$this->setData('errors.mailchimp_list', false);
|
507 |
+
|
508 |
+
return $list_id;
|
509 |
+
|
510 |
+
} catch (MailChimp_Error $e) {
|
511 |
+
$this->setData('errors.mailchimp_list', $e->getMessage());
|
512 |
+
return false;
|
513 |
+
}
|
514 |
+
}
|
515 |
+
|
516 |
+
/**
|
517 |
+
* @param null $data
|
518 |
+
* @return bool
|
519 |
+
*/
|
520 |
+
private function syncStore($data = null)
|
521 |
+
{
|
522 |
+
if (empty($data)) {
|
523 |
+
$data = $this->getOptions();
|
524 |
+
}
|
525 |
+
|
526 |
+
$site_url = $this->getUniqueStoreID();
|
527 |
+
|
528 |
+
$new = false;
|
529 |
+
|
530 |
+
if (!($store = $this->api()->getStore($site_url))) {
|
531 |
+
$new = true;
|
532 |
+
$store = new MailChimp_Store();
|
533 |
+
}
|
534 |
+
|
535 |
+
$list_id = $this->array_get($data, 'mailchimp_list', false);
|
536 |
+
$call = $new ? 'addStore' : 'updateStore';
|
537 |
+
$time_key = $new ? 'store_created_at' : 'store_updated_at';
|
538 |
+
|
539 |
+
$store->setId($site_url);
|
540 |
+
$store->setPlatform('woocommerce');
|
541 |
+
|
542 |
+
// set the locale data
|
543 |
+
$store->setPrimaryLocale($this->array_get($data, 'store_locale', 'en'));
|
544 |
+
$store->setTimezone($this->array_get($data, 'store_timezone', 'America\New_York'));
|
545 |
+
$store->setCurrencyCode($this->array_get($data, 'store_currency_code', 'USD'));
|
546 |
+
|
547 |
+
// set the basics
|
548 |
+
$store->setName($this->array_get($data, 'store_name'));
|
549 |
+
$store->setDomain(get_option('siteurl'));
|
550 |
+
$store->setEmailAddress($this->array_get($data, 'campaign_from_email'));
|
551 |
+
$store->setAddress($this->address($data));
|
552 |
+
$store->setPhone($this->array_get($data, 'store_phone'));
|
553 |
+
$store->setListId($list_id);
|
554 |
+
|
555 |
+
try {
|
556 |
+
// let's create a new store for this user through the API
|
557 |
+
$this->api()->$call($store);
|
558 |
+
|
559 |
+
// apply extra meta for store created at
|
560 |
+
$this->setData('errors.store_info', false);
|
561 |
+
$this->setData($time_key, time());
|
562 |
+
|
563 |
+
return true;
|
564 |
+
|
565 |
+
} catch (\Exception $e) {
|
566 |
+
$this->setData('errors.store_info', $e->getMessage());
|
567 |
+
}
|
568 |
+
|
569 |
+
return false;
|
570 |
+
}
|
571 |
+
|
572 |
+
/**
|
573 |
+
* @param array $data
|
574 |
+
* @return MailChimp_Address
|
575 |
+
*/
|
576 |
+
private function address(array $data)
|
577 |
+
{
|
578 |
+
$address = new MailChimp_Address();
|
579 |
+
|
580 |
+
if (isset($data['store_street']) && $data['store_street']) {
|
581 |
+
$address->setAddress1($data['store_street']);
|
582 |
+
}
|
583 |
+
|
584 |
+
if (isset($data['store_city']) && $data['store_city']) {
|
585 |
+
$address->setCity($data['store_city']);
|
586 |
+
}
|
587 |
+
|
588 |
+
if (isset($data['store_state']) && $data['store_state']) {
|
589 |
+
$address->setProvince($data['store_state']);
|
590 |
+
}
|
591 |
+
|
592 |
+
if (isset($data['store_country']) && $data['store_country']) {
|
593 |
+
$address->setCountry($data['store_country']);
|
594 |
+
}
|
595 |
+
|
596 |
+
if (isset($data['store_postal_code']) && $data['store_postal_code']) {
|
597 |
+
$address->setPostalCode($data['store_postal_code']);
|
598 |
+
}
|
599 |
+
|
600 |
+
if (isset($data['store_name']) && $data['store_name']) {
|
601 |
+
$address->setCompany($data['store_name']);
|
602 |
+
}
|
603 |
+
|
604 |
+
if (isset($data['store_phone']) && $data['store_phone']) {
|
605 |
+
$address->setPhone($data['store_phone']);
|
606 |
+
}
|
607 |
+
|
608 |
+
$address->setCountryCode($this->array_get($data, 'store_currency_code', 'USD'));
|
609 |
+
|
610 |
+
return $address;
|
611 |
+
}
|
612 |
+
|
613 |
+
/**
|
614 |
+
* @param array $required
|
615 |
+
* @param null $options
|
616 |
+
* @return bool
|
617 |
+
*/
|
618 |
+
private function validateOptions(array $required, $options = null)
|
619 |
+
{
|
620 |
+
$options = is_array($options) ? $options : $this->getOptions();
|
621 |
+
|
622 |
+
foreach ($required as $requirement) {
|
623 |
+
if (!isset($options[$requirement]) || empty($options[$requirement])) {
|
624 |
+
return false;
|
625 |
+
}
|
626 |
+
}
|
627 |
+
|
628 |
+
return true;
|
629 |
+
}
|
630 |
+
|
631 |
+
}
|
admin/css/mailchimp-woocommerce-admin.css
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* All of the CSS for your admin-specific functionality should be
|
3 |
+
* included in this file.
|
4 |
+
*/
|
admin/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden
|
admin/js/mailchimp-woocommerce-admin.js
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function( $ ) {
|
2 |
+
'use strict';
|
3 |
+
|
4 |
+
/**
|
5 |
+
* All of the code for your admin-facing JavaScript source
|
6 |
+
* should reside in this file.
|
7 |
+
*
|
8 |
+
* Note: It has been assumed you will write jQuery code here, so the
|
9 |
+
* $ function reference has been prepared for usage within the scope
|
10 |
+
* of this function.
|
11 |
+
*
|
12 |
+
* This enables you to define handlers, for when the DOM is ready:
|
13 |
+
*
|
14 |
+
* $(function() {
|
15 |
+
*
|
16 |
+
* });
|
17 |
+
*
|
18 |
+
* When the window is loaded:
|
19 |
+
*
|
20 |
+
* $( window ).load(function() {
|
21 |
+
*
|
22 |
+
* });
|
23 |
+
*
|
24 |
+
* ...and/or other possibilities.
|
25 |
+
*
|
26 |
+
* Ideally, it is not considered best practise to attach more than a
|
27 |
+
* single DOM-ready or window-load handler for a particular page.
|
28 |
+
* Although scripts in the WordPress core, Plugins and Themes may be
|
29 |
+
* practising this, we should strive to set a better example in our own work.
|
30 |
+
*/
|
31 |
+
|
32 |
+
})( jQuery );
|
admin/partials/mailchimp-woocommerce-admin-tabs.php
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
$active_tab = isset($_GET['tab']) ? $_GET['tab'] : 'api_key';
|
3 |
+
|
4 |
+
$handler = MailChimp_Woocommerce_Admin::connect();
|
5 |
+
|
6 |
+
//Grab all options for this particular tab we're viewing.
|
7 |
+
$options = get_option($this->plugin_name, array());
|
8 |
+
|
9 |
+
$show_sync_tab = false;
|
10 |
+
$show_campaign_defaults = true;
|
11 |
+
$has_valid_api_key = false;
|
12 |
+
$allow_new_list = true;
|
13 |
+
|
14 |
+
if (isset($options['mailchimp_api_key']) && $handler->hasValidApiKey()) {
|
15 |
+
$has_valid_api_key = true;
|
16 |
+
// if we don't have a valid api key we need to redirect back to the 'api_key' tab.
|
17 |
+
if (($mailchimp_lists = $handler->getMailChimpLists()) && is_array($mailchimp_lists)) {
|
18 |
+
$show_campaign_defaults = false;
|
19 |
+
$allow_new_list = false;
|
20 |
+
}
|
21 |
+
|
22 |
+
// only display this button if the data is not syncing and we have a valid api key
|
23 |
+
if ((bool) $this->getData('sync.started_at', false)) {
|
24 |
+
$show_sync_tab = true;
|
25 |
+
}
|
26 |
+
}
|
27 |
+
|
28 |
+
?>
|
29 |
+
|
30 |
+
<!-- Create a header in the default WordPress 'wrap' container -->
|
31 |
+
<div class="wrap">
|
32 |
+
<div id="icon-themes" class="icon32"></div>
|
33 |
+
<h2>MailChimp Settings</h2>
|
34 |
+
|
35 |
+
<h2 class="nav-tab-wrapper">
|
36 |
+
<a href="?page=mailchimp-woocommerce&tab=api_key" class="nav-tab <?php echo $active_tab == 'api_key' ? 'nav-tab-active' : ''; ?>">Connect</a>
|
37 |
+
<?php if($has_valid_api_key): ?>
|
38 |
+
<a href="?page=mailchimp-woocommerce&tab=store_info" class="nav-tab <?php echo $active_tab == 'store_info' ? 'nav-tab-active' : ''; ?>">Store Settings</a>
|
39 |
+
<?php if ($handler->hasValidStoreInfo()) : ?>
|
40 |
+
<?php if($show_campaign_defaults): ?>
|
41 |
+
<a href="?page=mailchimp-woocommerce&tab=campaign_defaults" class="nav-tab <?php echo $active_tab == 'campaign_defaults' ? 'nav-tab-active' : ''; ?>">List Defaults</a>
|
42 |
+
<?php endif; ?>
|
43 |
+
<a href="?page=mailchimp-woocommerce&tab=newsletter_settings" class="nav-tab <?php echo $active_tab == 'newsletter_settings' ? 'nav-tab-active' : ''; ?>">List Settings</a>
|
44 |
+
<?php if($show_sync_tab): ?>
|
45 |
+
<a href="?page=mailchimp-woocommerce&tab=sync" class="nav-tab <?php echo $active_tab == 'sync' ? 'nav-tab-active' : ''; ?>">Sync Status</a>
|
46 |
+
<?php endif; ?>
|
47 |
+
<?php endif;?>
|
48 |
+
<?php endif; ?>
|
49 |
+
</h2>
|
50 |
+
|
51 |
+
<form method="post" name="cleanup_options" action="options.php">
|
52 |
+
|
53 |
+
<?php
|
54 |
+
settings_fields($this->plugin_name);
|
55 |
+
do_settings_sections($this->plugin_name);
|
56 |
+
//settings_errors();
|
57 |
+
include('tabs/notices.php');
|
58 |
+
?>
|
59 |
+
|
60 |
+
<input type="hidden" name="<?php echo $this->plugin_name; ?>[mailchimp_active_tab]" value="<?php echo $active_tab; ?>"/>
|
61 |
+
|
62 |
+
<?php if( $active_tab == 'api_key' ): ?>
|
63 |
+
<?php include_once 'tabs/api_key.php'; ?>
|
64 |
+
<?php endif; ?>
|
65 |
+
|
66 |
+
<?php if( $active_tab == 'store_info' && $has_valid_api_key): ?>
|
67 |
+
<?php include_once 'tabs/store_info.php'; ?>
|
68 |
+
<?php endif; ?>
|
69 |
+
|
70 |
+
<?php if( $active_tab == 'campaign_defaults' ): ?>
|
71 |
+
<?php include_once 'tabs/campaign_defaults.php'; ?>
|
72 |
+
<?php endif; ?>
|
73 |
+
|
74 |
+
<?php if( $active_tab == 'newsletter_settings' ): ?>
|
75 |
+
<?php include_once 'tabs/newsletter_settings.php'; ?>
|
76 |
+
<?php endif; ?>
|
77 |
+
|
78 |
+
<?php if( $active_tab == 'sync' && $show_sync_tab): ?>
|
79 |
+
<?php include_once 'tabs/store_sync.php'; ?>
|
80 |
+
<?php endif; ?>
|
81 |
+
|
82 |
+
<?php if ($active_tab !== 'sync') submit_button('Save all changes', 'primary','submit', TRUE); ?>
|
83 |
+
|
84 |
+
<?php if($show_sync_tab && isset($_GET['show_sync']) && $_GET['show_sync'] === '1'): ?>
|
85 |
+
<p><a style="float:left;" class="btn" href="/?mailchimp_woocommerce[action]=sync">Re-Sync</a></p>
|
86 |
+
<?php endif; ?>
|
87 |
+
|
88 |
+
</form>
|
89 |
+
|
90 |
+
</div><!-- /.wrap -->
|
admin/partials/tabs/api_key.php
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
|
3 |
+
|
4 |
+
<?php
|
5 |
+
|
6 |
+
if (isset($options['mailchimp_api_key']) && !$handler->hasValidApiKey()) {
|
7 |
+
include_once __DIR__.'/errors/missing_api_key.php';
|
8 |
+
}
|
9 |
+
|
10 |
+
?>
|
11 |
+
|
12 |
+
<h2 style="padding-top: 1em;">API Information</h2>
|
13 |
+
<p>To find your MailChimp API key, log into your account settings > Extras > API keys. From there, either grab an existing key or generate a new one for your WooCommerce store. </p>
|
14 |
+
|
15 |
+
<!-- remove some meta and generators from the <head> -->
|
16 |
+
<fieldset>
|
17 |
+
<legend class="screen-reader-text">
|
18 |
+
<span>MailChimp API Key</span>
|
19 |
+
</legend>
|
20 |
+
<label for="<?php echo $this->plugin_name; ?>-mailchimp-api-key">
|
21 |
+
<input style="width: 30%;" type="password" id="<?php echo $this->plugin_name; ?>-mailchimp-api-key" name="<?php echo $this->plugin_name; ?>[mailchimp_api_key]" value="<?php echo isset($options['mailchimp_api_key']) ? $options['mailchimp_api_key'] : '' ?>" />
|
22 |
+
<span><?php esc_attr_e('Enter your MailChimp API key.', $this->plugin_name); ?></span>
|
23 |
+
</label>
|
24 |
+
</fieldset>
|
25 |
+
|
26 |
+
<fieldset>
|
27 |
+
<legend class="screen-reader-text">
|
28 |
+
<span>Enable Debugging</span>
|
29 |
+
</legend>
|
30 |
+
<label for="<?php echo $this->plugin_name; ?>-mailchimp-debugging">
|
31 |
+
<select name="<?php echo $this->plugin_name; ?>[mailchimp_debugging]" style="width:30%">
|
32 |
+
|
33 |
+
<?php
|
34 |
+
|
35 |
+
$enable_mailchimp_debugging = (array_key_exists('mailchimp_debugging', $options) && !is_null($options['mailchimp_debugging'])) ? $options['mailchimp_debugging'] : '1';
|
36 |
+
|
37 |
+
foreach (['0' => 'No', '1' => 'Yes'] as $key => $value ) {
|
38 |
+
echo '<option value="' . esc_attr($key) . '" ' . selected($key == $enable_mailchimp_debugging, true, false ) . '>' . esc_html( $value ) . '</option>';
|
39 |
+
}
|
40 |
+
?>
|
41 |
+
|
42 |
+
</select>
|
43 |
+
<span><?php esc_attr_e('Enable debugging logs to be sent to MailChimp.', $this->plugin_name); ?></span>
|
44 |
+
</label>
|
45 |
+
</fieldset>
|
46 |
+
|
admin/partials/tabs/campaign_defaults.php
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
$handler = MailChimp_Woocommerce_Admin::connect();
|
4 |
+
|
5 |
+
// if we don't have valid campaign defaults we need to redirect back to the 'campaign_defaults' tab.
|
6 |
+
if (!$handler->hasValidApiKey()) {
|
7 |
+
wp_redirect('options-general.php?page=mailchimp-woocommerce&tab=api_key&error_notice=missing_api_key');
|
8 |
+
}
|
9 |
+
if (!$handler->hasValidStoreInfo()) {
|
10 |
+
wp_redirect('options-general.php?page=mailchimp-woocommerce&tab=store_info&error_notice=missing_store');
|
11 |
+
}
|
12 |
+
|
13 |
+
?>
|
14 |
+
|
15 |
+
<h2 style="padding-top: 1em;">List Defaults</h2>
|
16 |
+
<p>Please fill out the default campaign information.</p>
|
17 |
+
|
18 |
+
<fieldset>
|
19 |
+
<legend class="screen-reader-text">
|
20 |
+
<span>Contact Name</span>
|
21 |
+
</legend>
|
22 |
+
<label for="<?php echo $this->plugin_name; ?>-campaign-from-name-label">
|
23 |
+
<input style="width: 30%;" type="text" id="<?php echo $this->plugin_name; ?>-campaign-from-name-label" name="<?php echo $this->plugin_name; ?>[campaign_from_name]" value="<?php echo isset($options['campaign_from_name']) ? $options['campaign_from_name'] : '' ?>" />
|
24 |
+
<span><?php esc_attr_e('Default from name', $this->plugin_name); ?></span>
|
25 |
+
</label>
|
26 |
+
</fieldset>
|
27 |
+
|
28 |
+
<fieldset>
|
29 |
+
<legend class="screen-reader-text">
|
30 |
+
<span>From Email</span>
|
31 |
+
</legend>
|
32 |
+
<label for="<?php echo $this->plugin_name; ?>-campaign-from-email-label">
|
33 |
+
<input style="width: 30%;" type="text" id="<?php echo $this->plugin_name; ?>-campaign-from-email-label" name="<?php echo $this->plugin_name; ?>[campaign_from_email]" value="<?php echo isset($options['campaign_from_email']) ? $options['campaign_from_email'] : get_option('admin_email') ?>" />
|
34 |
+
<span><?php esc_attr_e('Default from email', $this->plugin_name); ?></span>
|
35 |
+
</label>
|
36 |
+
</fieldset>
|
37 |
+
|
38 |
+
<fieldset>
|
39 |
+
<legend class="screen-reader-text">
|
40 |
+
<span>Default Subject</span>
|
41 |
+
</legend>
|
42 |
+
<label for="<?php echo $this->plugin_name; ?>-campaign-subject-label">
|
43 |
+
<input style="width: 30%;" type="text" id="<?php echo $this->plugin_name; ?>-campaign-subject-label" name="<?php echo $this->plugin_name; ?>[campaign_subject]" value="<?php echo isset($options['campaign_subject']) ? $options['campaign_subject'] : get_option('blogname') ?>" />
|
44 |
+
<span><?php esc_attr_e('Default subject', $this->plugin_name); ?></span>
|
45 |
+
</label>
|
46 |
+
</fieldset>
|
47 |
+
|
48 |
+
<fieldset>
|
49 |
+
<legend class="screen-reader-text">
|
50 |
+
<span>Default Language</span>
|
51 |
+
</legend>
|
52 |
+
<label for="<?php echo $this->plugin_name; ?>-campaign-language-label">
|
53 |
+
<select id="<?php echo $this->plugin_name; ?>-campaign-language-label" name="<?php echo $this->plugin_name; ?>[campaign_language]" style="width:30%" required>
|
54 |
+
<?php $selected_locale = isset($options['campaign_language']) && !empty($options['campaign_language']) ? $options['campaign_language'] : 'en'; ?>
|
55 |
+
<?php
|
56 |
+
foreach(MailChimp_Api_Locales::simple() as $locale_key => $local_value) {
|
57 |
+
echo '<option value="' . esc_attr( $locale_key ) . '" ' . selected($locale_key === $selected_locale, true, false ) . '>' . esc_html( $local_value ) . '</option>';
|
58 |
+
}
|
59 |
+
?>
|
60 |
+
</select>
|
61 |
+
<span><?php esc_attr_e('Default language', $this->plugin_name); ?></span>
|
62 |
+
</label>
|
63 |
+
</fieldset>
|
64 |
+
|
65 |
+
<fieldset>
|
66 |
+
<legend class="screen-reader-text">
|
67 |
+
<span>Permission Reminder</span>
|
68 |
+
</legend>
|
69 |
+
<label for="<?php echo $this->plugin_name; ?>-campaign-permission-reminder-label">
|
70 |
+
<textarea style="width: 30%;" id="<?php echo $this->plugin_name; ?>-campaign-permission-reminder-label" name="<?php echo $this->plugin_name; ?>[campaign_permission_reminder]"><?php echo isset($options['campaign_permission_reminder']) ? $options['campaign_permission_reminder'] : 'You were subscribed to the newsletter from '.get_option('blogname') ?></textarea>
|
71 |
+
<span><?php esc_attr_e('Permission reminder message', $this->plugin_name); ?></span>
|
72 |
+
</label>
|
73 |
+
</fieldset>
|
admin/partials/tabs/errors/missing_api_key.php
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<div class="error notice is-dismissable">
|
2 |
+
<p><?php _e('MailChimp says: You must enter in a valid API key.', 'mailchimp-woocommerce'); ?></p>
|
3 |
+
</div>
|
admin/partials/tabs/errors/missing_campaign_defaults.php
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<div class="error notice is-dismissable">
|
2 |
+
<p><?php _e('MailChimp says: Sorry you must set up your campaign defaults before you proceed!', 'mailchimp-woocommerce'); ?></p>
|
3 |
+
</div>
|
admin/partials/tabs/errors/missing_list.php
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<div class="error notice is-dismissable">
|
2 |
+
<p><?php _e('MailChimp says: You must select a marketing list.', 'mailchimp-woocommerce'); ?></p>
|
3 |
+
</div>
|
admin/partials/tabs/errors/missing_store.php
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<div class="error notice is-dismissable">
|
2 |
+
<p><?php _e('MailChimp says: Sorry you must set up your store before you proceed!', 'mailchimp-woocommerce'); ?></p>
|
3 |
+
</div>
|
admin/partials/tabs/errors/not_ready_for_sync.php
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<div class="error notice is-dismissable">
|
2 |
+
<p><?php _e('MailChimp says: You are not fully ready to run the Store Sync, please verify your settings before proceeding.', 'mailchimp-woocommerce'); ?></p>
|
3 |
+
</div>
|
admin/partials/tabs/newsletter_settings.php
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
// if we don't have valid campaign defaults we need to redirect back to the 'campaign_defaults' tab.
|
3 |
+
if (!$handler->hasValidApiKey()) {
|
4 |
+
wp_redirect('options-general.php?page=mailchimp-woocommerce&tab=api_key&error_notice=missing_api_key');
|
5 |
+
}
|
6 |
+
|
7 |
+
// if we don't have valid store information, we need to redirect back to the 'store_info' tab.
|
8 |
+
if (!$handler->hasValidStoreInfo()) {
|
9 |
+
wp_redirect('options-general.php?page=mailchimp-woocommerce&tab=store_info&error_notice=missing_store');
|
10 |
+
}
|
11 |
+
|
12 |
+
// if we don't have a valid api key we need to redirect back to the 'api_key' tab.
|
13 |
+
if (!isset($mailchimp_lists) && ($mailchimp_lists = $handler->getMailChimpLists()) === false) {
|
14 |
+
wp_redirect('options-general.php?page=mailchimp-woocommerce&tab=api_key&error_notice=missing_api_key');
|
15 |
+
}
|
16 |
+
|
17 |
+
// if we don't have valid campaign defaults we need to redirect back to the 'campaign_defaults' tab.
|
18 |
+
if (empty($mailchimp_lists) && !$handler->hasValidCampaignDefaults()) {
|
19 |
+
wp_redirect('options-general.php?page=mailchimp-woocommerce&tab=campaign_defaults&error_notice=missing_campaign_defaults');
|
20 |
+
}
|
21 |
+
|
22 |
+
$list_is_configured = isset($options['mailchimp_list']) && (!empty($options['mailchimp_list'])) && array_key_exists($options['mailchimp_list'], $mailchimp_lists);
|
23 |
+
?>
|
24 |
+
|
25 |
+
<?php if(($newsletter_settings_error = $this->getData('errors.mailchimp_list', false))) : ?>
|
26 |
+
<div class="error notice is-dismissable">
|
27 |
+
<p><?php _e($newsletter_settings_error, 'mailchimp-woocommerce'); ?></p>
|
28 |
+
</div>
|
29 |
+
<?php endif; ?>
|
30 |
+
|
31 |
+
<h2 style="padding-top: 1em;">List Settings</h2>
|
32 |
+
<p>Please apply your list settings. If you don't have a list, you can choose to create one.</p>
|
33 |
+
|
34 |
+
<fieldset>
|
35 |
+
<legend class="screen-reader-text">
|
36 |
+
<span>List Name</span>
|
37 |
+
</legend>
|
38 |
+
<label for="<?php echo $this->plugin_name; ?>-mailchimp-list-label">
|
39 |
+
<select name="<?php echo $this->plugin_name; ?>[mailchimp_list]" style="width:30%" required <?php if($list_is_configured): ?> disabled <?php endif; ?>>
|
40 |
+
|
41 |
+
<?php if(!isset($allow_new_list) || $allow_new_list === true): ?>
|
42 |
+
<option value="create_new">Create New List</option>
|
43 |
+
<?php endif ?>
|
44 |
+
|
45 |
+
<?php if(isset($allow_new_list) && $allow_new_list === false): ?>
|
46 |
+
<option value="">-- Select List --</option>
|
47 |
+
<?php endif; ?>
|
48 |
+
|
49 |
+
<?php
|
50 |
+
if (is_array($mailchimp_lists)) {
|
51 |
+
foreach ($mailchimp_lists as $key => $value ) {
|
52 |
+
echo '<option value="' . esc_attr( $key ) . '" ' . selected( $key === $options['mailchimp_list'], true, false ) . '>' . esc_html( $value ) . '</option>';
|
53 |
+
}
|
54 |
+
}
|
55 |
+
?>
|
56 |
+
</select>
|
57 |
+
<span><?php esc_attr_e('Choose a list to sync with your store.', $this->plugin_name); ?></span>
|
58 |
+
</label>
|
59 |
+
</fieldset>
|
60 |
+
|
61 |
+
<fieldset>
|
62 |
+
<legend class="screen-reader-text">
|
63 |
+
<span>Auto Subscribe On Initial Sync</span>
|
64 |
+
</legend>
|
65 |
+
<label for="<?php echo $this->plugin_name; ?>-mailchimp-auto-subscribe">
|
66 |
+
<select name="<?php echo $this->plugin_name; ?>[mailchimp_auto_subscribe]" style="width:30%" required <?php if($list_is_configured): ?> disabled <?php endif; ?>>
|
67 |
+
|
68 |
+
<?php
|
69 |
+
$enable_auto_subscribe = (array_key_exists('mailchimp_auto_subscribe', $options) && !is_null($options['mailchimp_auto_subscribe'])) ? $options['mailchimp_auto_subscribe'] : '1';
|
70 |
+
|
71 |
+
foreach (['0' => 'No', '1' => 'Yes'] as $key => $value ) {
|
72 |
+
echo '<option value="' . esc_attr( $key ) . '" ' . selected($key == $enable_auto_subscribe, true, false ) . '>' . esc_html( $value ) . '</option>';
|
73 |
+
}
|
74 |
+
?>
|
75 |
+
|
76 |
+
</select>
|
77 |
+
<span><?php esc_attr_e('During initial sync, auto subscribe the existing customers.', $this->plugin_name); ?></span>
|
78 |
+
</label>
|
79 |
+
</fieldset>
|
80 |
+
|
81 |
+
<fieldset>
|
82 |
+
<legend class="screen-reader-text">
|
83 |
+
<span>MailChimp Newsletter Label</span>
|
84 |
+
</legend>
|
85 |
+
<label for="<?php echo $this->plugin_name; ?>-newsletter-checkbox-label">
|
86 |
+
<input style="width: 30%;" type="text" id="<?php echo $this->plugin_name; ?>-newsletter-checkbox-label" name="<?php echo $this->plugin_name; ?>[newsletter_label]" value="<?php echo isset($options['newsletter_label']) ? $options['newsletter_label'] : 'Subscribe to our newsletter' ?>" />
|
87 |
+
<span><?php esc_attr_e('Write a subscribe message for customers at checkout.', $this->plugin_name); ?></span>
|
88 |
+
</label>
|
89 |
+
</fieldset>
|
admin/partials/tabs/notices.php
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if(isset($_GET['error_notice']) && file_exists(__DIR__.'/errors/'.$_GET['error_notice'].'.php')): ?>
|
2 |
+
<?php include(__DIR__.'/errors/'.$_GET['error_notice'].'.php'); ?>
|
3 |
+
<?php endif; ?>
|
4 |
+
|
5 |
+
<?php if(isset($_GET['success_notice']) && file_exists(__DIR__.'/success/'.$_GET['success_notice'].'.php')): ?>
|
6 |
+
<?php include(__DIR__.'/success/'.$_GET['success_notice'].'.php'); ?>
|
7 |
+
<?php endif; ?>
|
8 |
+
|
admin/partials/tabs/store_info.php
ADDED
@@ -0,0 +1,148 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
$handler = MailChimp_Woocommerce_Admin::connect();
|
4 |
+
|
5 |
+
// if we don't have valid campaign defaults we need to redirect back to the 'campaign_defaults' tab.
|
6 |
+
if (!$handler->hasValidApiKey()) {
|
7 |
+
wp_redirect('options-general.php?page=mailchimp-woocommerce&tab=api_key&error_notice=missing_api_key');
|
8 |
+
}
|
9 |
+
|
10 |
+
?>
|
11 |
+
|
12 |
+
<h2 style="padding-top: 1em;">Store Settings</h2>
|
13 |
+
<p>Please provide the following information about your WooCommerce store.</p>
|
14 |
+
|
15 |
+
<fieldset>
|
16 |
+
<legend class="screen-reader-text">
|
17 |
+
<span>Store Name</span>
|
18 |
+
</legend>
|
19 |
+
<label for="<?php echo $this->plugin_name; ?>-store-name-label">
|
20 |
+
<input style="width: 30%;" type="text" id="<?php echo $this->plugin_name; ?>-store-name-label" name="<?php echo $this->plugin_name; ?>[store_name]" value="<?php echo isset($options['store_name']) ? $options['store_name'] : get_option('blogname') ?>" />
|
21 |
+
<span><?php esc_attr_e('Name', $this->plugin_name); ?></span>
|
22 |
+
</label>
|
23 |
+
</fieldset>
|
24 |
+
|
25 |
+
<fieldset>
|
26 |
+
<legend class="screen-reader-text">
|
27 |
+
<span>Email</span>
|
28 |
+
</legend>
|
29 |
+
<label for="<?php echo $this->plugin_name; ?>-admin-email-label">
|
30 |
+
<input style="width: 30%;" type="email" id="<?php echo $this->plugin_name; ?>-admin-email-label" name="<?php echo $this->plugin_name; ?>[admin_email]" value="<?php echo isset($options['admin_email']) ? $options['admin_email'] : get_option('admin_email') ?>" />
|
31 |
+
<span><?php esc_attr_e('Email', $this->plugin_name); ?></span>
|
32 |
+
</label>
|
33 |
+
</fieldset>
|
34 |
+
|
35 |
+
<fieldset>
|
36 |
+
<legend class="screen-reader-text">
|
37 |
+
<span>Street Address</span>
|
38 |
+
</legend>
|
39 |
+
<label for="<?php echo $this->plugin_name; ?>-store-address-label">
|
40 |
+
<input style="width: 30%;" type="text" id="<?php echo $this->plugin_name; ?>-store-address-label" name="<?php echo $this->plugin_name; ?>[store_street]" value="<?php echo isset($options['store_street']) ? $options['store_street'] : '' ?>" />
|
41 |
+
<span><?php esc_attr_e('Street address', $this->plugin_name); ?></span>
|
42 |
+
</label>
|
43 |
+
</fieldset>
|
44 |
+
|
45 |
+
<fieldset>
|
46 |
+
<legend class="screen-reader-text">
|
47 |
+
<span>City</span>
|
48 |
+
</legend>
|
49 |
+
<label for="<?php echo $this->plugin_name; ?>-store-city-label">
|
50 |
+
<input style="width: 30%;" type="text" id="<?php echo $this->plugin_name; ?>-store-city-label" name="<?php echo $this->plugin_name; ?>[store_city]" value="<?php echo isset($options['store_city']) ? $options['store_city'] : '' ?>" />
|
51 |
+
<span><?php esc_attr_e('City', $this->plugin_name); ?></span>
|
52 |
+
</label>
|
53 |
+
</fieldset>
|
54 |
+
|
55 |
+
<fieldset>
|
56 |
+
<legend class="screen-reader-text">
|
57 |
+
<span>State</span>
|
58 |
+
</legend>
|
59 |
+
<label for="<?php echo $this->plugin_name; ?>-store-state-label">
|
60 |
+
<input style="width: 30%;" type="text" id="<?php echo $this->plugin_name; ?>-store-state-label" name="<?php echo $this->plugin_name; ?>[store_state]" value="<?php echo isset($options['store_state']) ? $options['store_state'] : '' ?>" />
|
61 |
+
<span><?php esc_attr_e('State', $this->plugin_name); ?></span>
|
62 |
+
</label>
|
63 |
+
</fieldset>
|
64 |
+
|
65 |
+
<fieldset>
|
66 |
+
<legend class="screen-reader-text">
|
67 |
+
<span>Postal Code</span>
|
68 |
+
</legend>
|
69 |
+
<label for="<?php echo $this->plugin_name; ?>-store-state-label">
|
70 |
+
<input style="width: 30%;" type="text" id="<?php echo $this->plugin_name; ?>-store-postal-code-label" name="<?php echo $this->plugin_name; ?>[store_postal_code]" value="<?php echo isset($options['store_postal_code']) ? $options['store_postal_code'] : '' ?>" />
|
71 |
+
<span><?php esc_attr_e('Postal Code', $this->plugin_name); ?></span>
|
72 |
+
</label>
|
73 |
+
</fieldset>
|
74 |
+
|
75 |
+
<fieldset>
|
76 |
+
<legend class="screen-reader-text">
|
77 |
+
<span>Country</span>
|
78 |
+
</legend>
|
79 |
+
<label for="<?php echo $this->plugin_name; ?>-store-country-label">
|
80 |
+
<input style="width: 30%;" type="text" id="<?php echo $this->plugin_name; ?>-store-country-label" name="<?php echo $this->plugin_name; ?>[store_country]" value="<?php echo isset($options['store_country']) ? $options['store_country'] : 'US' ?>" />
|
81 |
+
<span><?php esc_attr_e('Country', $this->plugin_name); ?></span>
|
82 |
+
</label>
|
83 |
+
</fieldset>
|
84 |
+
|
85 |
+
<fieldset>
|
86 |
+
<legend class="screen-reader-text">
|
87 |
+
<span>Phone</span>
|
88 |
+
</legend>
|
89 |
+
<label for="<?php echo $this->plugin_name; ?>-store-phone-label">
|
90 |
+
<input style="width: 30%;" type="text" id="<?php echo $this->plugin_name; ?>-store-phone-label" name="<?php echo $this->plugin_name; ?>[store_phone]" value="<?php echo isset($options['store_phone']) ? $options['store_phone'] : '' ?>" />
|
91 |
+
<span><?php esc_attr_e('Phone Number', $this->plugin_name); ?></span>
|
92 |
+
</label>
|
93 |
+
</fieldset>
|
94 |
+
|
95 |
+
<h2 style="padding-top: 1em;">Locale Settings</h2>
|
96 |
+
|
97 |
+
<p>Please apply your locale settings. If you're unsure about these, use the defaults.</p>
|
98 |
+
|
99 |
+
<fieldset>
|
100 |
+
<legend class="screen-reader-text">
|
101 |
+
<span>Locale</span>
|
102 |
+
</legend>
|
103 |
+
<label for="<?php echo $this->plugin_name; ?>-store-locale-label">
|
104 |
+
<select name="<?php echo $this->plugin_name; ?>[store_locale]" style="width:30%" required>
|
105 |
+
<?php $selected_locale = isset($options['store_locale']) && !empty($options['store_locale']) ? $options['store_locale'] : 'en'; ?>
|
106 |
+
<?php
|
107 |
+
foreach(MailChimp_Api_Locales::simple() as $locale_key => $local_value) {
|
108 |
+
echo '<option value="' . esc_attr( $locale_key ) . '" ' . selected($locale_key === $selected_locale, true, false ) . '>' . esc_html( $local_value ) . '</option>';
|
109 |
+
}
|
110 |
+
?>
|
111 |
+
</select>
|
112 |
+
<span><?php esc_attr_e('Locale', $this->plugin_name); ?></span>
|
113 |
+
</label>
|
114 |
+
</fieldset>
|
115 |
+
|
116 |
+
<fieldset>
|
117 |
+
<legend class="screen-reader-text">
|
118 |
+
<span>Currency Code</span>
|
119 |
+
</legend>
|
120 |
+
<label for="<?php echo $this->plugin_name; ?>-store-currency-code-label">
|
121 |
+
<select name="<?php echo $this->plugin_name; ?>[store_currency_code]" style="width:30%" required>
|
122 |
+
<?php
|
123 |
+
$selected_currency_code = isset($options['store_currency_code']) && !empty($options['store_currency_code']) ? $options['store_currency_code'] : 'USD';
|
124 |
+
foreach (MailChimp_Api_CurrencyCodes::lists() as $key => $value ) {
|
125 |
+
echo '<option value="' . esc_attr( $key ) . '" ' . selected($key === $selected_currency_code, true, false ) . '>' . esc_html( $value ) . '</option>';
|
126 |
+
}
|
127 |
+
?>
|
128 |
+
</select>
|
129 |
+
<span><?php esc_attr_e('Currency', $this->plugin_name); ?></span>
|
130 |
+
</label>
|
131 |
+
</fieldset>
|
132 |
+
|
133 |
+
<fieldset>
|
134 |
+
<legend class="screen-reader-text">
|
135 |
+
<span>Timezone</span>
|
136 |
+
</legend>
|
137 |
+
<label for="<?php echo $this->plugin_name; ?>-store-timezone-label">
|
138 |
+
<select name="<?php echo $this->plugin_name; ?>[store_timezone]" style="width:30%" required>
|
139 |
+
<?php $selected_timezone = isset($options['store_timezone']) && !empty($options['store_timezone']) ? $options['store_timezone'] : 'America/New_York'; ?>
|
140 |
+
<?php
|
141 |
+
foreach(mailchimp_get_timezone_list() as $t) {
|
142 |
+
echo '<option value="' . esc_attr( $t['zone'] ) . '" ' . selected($t['zone'] === $selected_timezone, true, false ) . '>' . esc_html( $t['diff_from_GMT'] . ' - ' . $t['zone'] ) . '</option>';
|
143 |
+
}
|
144 |
+
?>
|
145 |
+
</select>
|
146 |
+
<span><?php esc_attr_e('Timezone', $this->plugin_name); ?></span>
|
147 |
+
</label>
|
148 |
+
</fieldset>
|
admin/partials/tabs/store_sync.php
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
$handler = MailChimp_Woocommerce_Admin::connect();
|
4 |
+
|
5 |
+
// if we're not ready for a sync, we need to redirect out of this page like now.
|
6 |
+
//if (!$handler->isReadyForSync()) {
|
7 |
+
// wp_redirect('options-general.php?page=mailchimp-woocommerce&tab=api_key&error_notice=not_ready_for_sync');
|
8 |
+
//}
|
9 |
+
|
10 |
+
if (($sync_started_at = $this->getData('sync.started_at', false))) {
|
11 |
+
$date = mailchimp_date_local(date("c", $sync_started_at));
|
12 |
+
$sync_started_at = $date->format('D, M j, Y g:i A');
|
13 |
+
} else {
|
14 |
+
$sync_started_at = 'N/A';
|
15 |
+
}
|
16 |
+
|
17 |
+
if (($sync_complete_at = $this->getData('sync.completed_at', false))) {
|
18 |
+
$date = mailchimp_date_local(date("c", $sync_complete_at));
|
19 |
+
$sync_complete_at = $date->format('D, M j, Y g:i A');
|
20 |
+
} else {
|
21 |
+
$sync_complete_at = $sync_started_at !== 'N/A' ? 'In Progress' : 'N/A';
|
22 |
+
}
|
23 |
+
|
24 |
+
?>
|
25 |
+
|
26 |
+
<h2 style="padding-top: 1em;">Sync Timeline</h2>
|
27 |
+
|
28 |
+
<p>Sync Started: <?php echo $sync_started_at; ?></p>
|
29 |
+
<p>Sync Completed: <?php echo $sync_complete_at; ?></p>
|
30 |
+
|
admin/partials/tabs/success/re-sync-started.php
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
<div class="success notice is-dismissable">
|
2 |
+
<p><?php _e('MailChimp says: Your re-sync has been started!', 'mailchimp-woocommerce'); ?></p>
|
3 |
+
</div>
|
changelog.md
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
** 0.1.11 **
|
2 |
+
* removed an extra debug log that was not needed
|
3 |
+
|
4 |
+
** 0.1.10 **
|
5 |
+
* altered debug logging and fixed store settings validation requirements
|
6 |
+
|
7 |
+
** 0.1.9 **
|
8 |
+
* using fallback to stream context during failed patch requests
|
9 |
+
|
10 |
+
** 0.1.8 **
|
11 |
+
* fixing http request header for larger patch requests
|
12 |
+
|
13 |
+
** 0.1.7 **
|
14 |
+
* fixing various bugs with the sync and product issues.
|
15 |
+
|
16 |
+
** 0.1.2 **
|
17 |
+
* fixed admin order update hook.
|
includes/api/assets/class-mailchimp-address.php
ADDED
@@ -0,0 +1,337 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 3/8/16
|
9 |
+
* Time: 2:22 PM
|
10 |
+
*/
|
11 |
+
class MailChimp_Address
|
12 |
+
{
|
13 |
+
protected $type;
|
14 |
+
protected $name;
|
15 |
+
protected $address1;
|
16 |
+
protected $address2;
|
17 |
+
protected $city;
|
18 |
+
protected $province;
|
19 |
+
protected $province_code;
|
20 |
+
protected $postal_code;
|
21 |
+
protected $country;
|
22 |
+
protected $country_code;
|
23 |
+
protected $longitude;
|
24 |
+
protected $latitude;
|
25 |
+
protected $phone;
|
26 |
+
protected $company;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @return array
|
30 |
+
*/
|
31 |
+
public function getValidation()
|
32 |
+
{
|
33 |
+
return array(
|
34 |
+
'address1' => 'string',
|
35 |
+
'address2' => 'string',
|
36 |
+
'city' => 'string',
|
37 |
+
'province' => 'string',
|
38 |
+
'province_code' => 'string|digits:2',
|
39 |
+
'postal_code' => 'string',
|
40 |
+
'country' => 'string',
|
41 |
+
'country_code' => 'string|digits:2',
|
42 |
+
'latitude' => 'numeric',
|
43 |
+
'longitude' => 'numeric',
|
44 |
+
);
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* @return mixed
|
49 |
+
*/
|
50 |
+
public function getName()
|
51 |
+
{
|
52 |
+
return $this->name;
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* @param mixed $name
|
57 |
+
* @return MailChimp_Address
|
58 |
+
*/
|
59 |
+
public function setName($name)
|
60 |
+
{
|
61 |
+
$this->name = $name;
|
62 |
+
|
63 |
+
return $this;
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* @return mixed
|
68 |
+
*/
|
69 |
+
public function getAddress1()
|
70 |
+
{
|
71 |
+
return $this->address1;
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* @param mixed $address1
|
76 |
+
* @return MailChimp_Address
|
77 |
+
*/
|
78 |
+
public function setAddress1($address1)
|
79 |
+
{
|
80 |
+
$this->address1 = $address1;
|
81 |
+
|
82 |
+
return $this;
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* @return mixed
|
87 |
+
*/
|
88 |
+
public function getAddress2()
|
89 |
+
{
|
90 |
+
return $this->address2;
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* @param mixed $address2
|
95 |
+
* @return MailChimp_Address
|
96 |
+
*/
|
97 |
+
public function setAddress2($address2)
|
98 |
+
{
|
99 |
+
$this->address2 = $address2;
|
100 |
+
|
101 |
+
return $this;
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* @return mixed
|
106 |
+
*/
|
107 |
+
public function getCity()
|
108 |
+
{
|
109 |
+
return $this->city;
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* @param mixed $city
|
114 |
+
* @return MailChimp_Address
|
115 |
+
*/
|
116 |
+
public function setCity($city)
|
117 |
+
{
|
118 |
+
$this->city = $city;
|
119 |
+
|
120 |
+
return $this;
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* @return mixed
|
125 |
+
*/
|
126 |
+
public function getProvince()
|
127 |
+
{
|
128 |
+
return $this->province;
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* @param mixed $province
|
133 |
+
* @return MailChimp_Address
|
134 |
+
*/
|
135 |
+
public function setProvince($province)
|
136 |
+
{
|
137 |
+
$this->province = $province;
|
138 |
+
|
139 |
+
return $this;
|
140 |
+
}
|
141 |
+
|
142 |
+
/**
|
143 |
+
* @return mixed
|
144 |
+
*/
|
145 |
+
public function getProvinceCode()
|
146 |
+
{
|
147 |
+
return $this->province_code;
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* @param mixed $province_code
|
152 |
+
* @return MailChimp_Address
|
153 |
+
*/
|
154 |
+
public function setProvinceCode($province_code)
|
155 |
+
{
|
156 |
+
$this->province_code = $province_code;
|
157 |
+
|
158 |
+
return $this;
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* @return mixed
|
163 |
+
*/
|
164 |
+
public function getPostalCode()
|
165 |
+
{
|
166 |
+
return $this->postal_code;
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* @param mixed $postal_code
|
171 |
+
* @return MailChimp_Address
|
172 |
+
*/
|
173 |
+
public function setPostalCode($postal_code)
|
174 |
+
{
|
175 |
+
$this->postal_code = $postal_code;
|
176 |
+
|
177 |
+
return $this;
|
178 |
+
}
|
179 |
+
|
180 |
+
/**
|
181 |
+
* @return mixed
|
182 |
+
*/
|
183 |
+
public function getCountry()
|
184 |
+
{
|
185 |
+
return $this->country;
|
186 |
+
}
|
187 |
+
|
188 |
+
/**
|
189 |
+
* @param mixed $country
|
190 |
+
* @return MailChimp_Address
|
191 |
+
*/
|
192 |
+
public function setCountry($country)
|
193 |
+
{
|
194 |
+
$this->country = $country;
|
195 |
+
|
196 |
+
return $this;
|
197 |
+
}
|
198 |
+
|
199 |
+
/**
|
200 |
+
* @return mixed
|
201 |
+
*/
|
202 |
+
public function getCountryCode()
|
203 |
+
{
|
204 |
+
return $this->country_code;
|
205 |
+
}
|
206 |
+
|
207 |
+
/**
|
208 |
+
* @param mixed $country_code
|
209 |
+
* @return MailChimp_Address
|
210 |
+
*/
|
211 |
+
public function setCountryCode($country_code)
|
212 |
+
{
|
213 |
+
$this->country_code = $country_code;
|
214 |
+
|
215 |
+
return $this;
|
216 |
+
}
|
217 |
+
|
218 |
+
/**
|
219 |
+
* @return mixed
|
220 |
+
*/
|
221 |
+
public function getLongitude()
|
222 |
+
{
|
223 |
+
return $this->longitude;
|
224 |
+
}
|
225 |
+
|
226 |
+
/**
|
227 |
+
* @param mixed $longitude
|
228 |
+
* @return MailChimp_Address
|
229 |
+
*/
|
230 |
+
public function setLongitude($longitude)
|
231 |
+
{
|
232 |
+
$this->longitude = $longitude;
|
233 |
+
|
234 |
+
return $this;
|
235 |
+
}
|
236 |
+
|
237 |
+
/**
|
238 |
+
* @return mixed
|
239 |
+
*/
|
240 |
+
public function getLatitude()
|
241 |
+
{
|
242 |
+
return $this->latitude;
|
243 |
+
}
|
244 |
+
|
245 |
+
/**
|
246 |
+
* @param mixed $latitude
|
247 |
+
* @return MailChimp_Address
|
248 |
+
*/
|
249 |
+
public function setLatitude($latitude)
|
250 |
+
{
|
251 |
+
$this->latitude = $latitude;
|
252 |
+
|
253 |
+
return $this;
|
254 |
+
}
|
255 |
+
|
256 |
+
/**
|
257 |
+
* @return mixed
|
258 |
+
*/
|
259 |
+
public function getPhone()
|
260 |
+
{
|
261 |
+
return $this->phone;
|
262 |
+
}
|
263 |
+
|
264 |
+
/**
|
265 |
+
* @param mixed $phone
|
266 |
+
* @return MailChimp_Address
|
267 |
+
*/
|
268 |
+
public function setPhone($phone)
|
269 |
+
{
|
270 |
+
$this->phone = $phone;
|
271 |
+
|
272 |
+
return $this;
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* @return mixed
|
277 |
+
*/
|
278 |
+
public function getCompany()
|
279 |
+
{
|
280 |
+
return $this->company;
|
281 |
+
}
|
282 |
+
|
283 |
+
/**
|
284 |
+
* @param mixed $company
|
285 |
+
* @return MailChimp_Address
|
286 |
+
*/
|
287 |
+
public function setCompany($company)
|
288 |
+
{
|
289 |
+
$this->company = $company;
|
290 |
+
|
291 |
+
return $this;
|
292 |
+
}
|
293 |
+
|
294 |
+
/**
|
295 |
+
* @return array
|
296 |
+
*/
|
297 |
+
public function toArray()
|
298 |
+
{
|
299 |
+
return mailchimp_array_remove_empty(array(
|
300 |
+
'name' => (string) $this->name,
|
301 |
+
'address1' => (string) $this->address1,
|
302 |
+
'address2' => (string) $this->address2,
|
303 |
+
'city' => (string) $this->city,
|
304 |
+
'province' => (string) $this->province,
|
305 |
+
'province_code' => (string) $this->province_code,
|
306 |
+
'postal_code' => (string) $this->postal_code,
|
307 |
+
'country' => (string) $this->country,
|
308 |
+
'country_code' => (string) $this->country_code,
|
309 |
+
'longitude' => $this->longitude ? (int) $this->longitude : null,
|
310 |
+
'latitude' => $this->latitude ? (int) $this->latitude : null,
|
311 |
+
'phone' => (string) $this->phone,
|
312 |
+
'company' => (string) $this->company,
|
313 |
+
));
|
314 |
+
}
|
315 |
+
|
316 |
+
/**
|
317 |
+
* @param array $data
|
318 |
+
* @return MailChimp_Address
|
319 |
+
*/
|
320 |
+
public function fromArray(array $data)
|
321 |
+
{
|
322 |
+
$singles = array(
|
323 |
+
'name', 'address1', 'address2', 'city',
|
324 |
+
'province', 'province_code', 'postal_code',
|
325 |
+
'country', 'country_code', 'longitude',
|
326 |
+
'phone', 'company',
|
327 |
+
);
|
328 |
+
|
329 |
+
foreach ($singles as $key) {
|
330 |
+
if (array_key_exists($key, $data)) {
|
331 |
+
$this->$key = $data[$key];
|
332 |
+
}
|
333 |
+
}
|
334 |
+
|
335 |
+
return $this;
|
336 |
+
}
|
337 |
+
}
|
includes/api/assets/class-mailchimp-cart.php
ADDED
@@ -0,0 +1,275 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 7/15/16
|
9 |
+
* Time: 1:26 PM
|
10 |
+
*/
|
11 |
+
class MailChimp_Cart
|
12 |
+
{
|
13 |
+
protected $store_id;
|
14 |
+
protected $id;
|
15 |
+
protected $customer;
|
16 |
+
protected $campaign_id;
|
17 |
+
protected $checkout_url;
|
18 |
+
protected $currency_code;
|
19 |
+
protected $order_total;
|
20 |
+
protected $tax_total;
|
21 |
+
protected $lines = array();
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @param $unique_id
|
25 |
+
* @return $this
|
26 |
+
*/
|
27 |
+
public function setId($unique_id)
|
28 |
+
{
|
29 |
+
$this->id = $unique_id;
|
30 |
+
|
31 |
+
return $this;
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* @return mixed
|
36 |
+
*/
|
37 |
+
public function getId()
|
38 |
+
{
|
39 |
+
return $this->id;
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* @param $store_id
|
44 |
+
* @return $this
|
45 |
+
*/
|
46 |
+
public function setStoreID($store_id)
|
47 |
+
{
|
48 |
+
$this->store_id = $store_id;
|
49 |
+
|
50 |
+
return $this;
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* @return mixed
|
55 |
+
*/
|
56 |
+
public function getStoreID()
|
57 |
+
{
|
58 |
+
if (empty($this->store_id)) {
|
59 |
+
$this->store_id = mailchimp_get_store_id();
|
60 |
+
}
|
61 |
+
|
62 |
+
return $this->store_id;
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* @param MailChimp_Customer $customer
|
67 |
+
* @return $this
|
68 |
+
*/
|
69 |
+
public function setCustomer(MailChimp_Customer $customer)
|
70 |
+
{
|
71 |
+
$this->customer = $customer;
|
72 |
+
|
73 |
+
return $this;
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* @return MailChimp_Customer
|
78 |
+
*/
|
79 |
+
public function getCustomer()
|
80 |
+
{
|
81 |
+
if (empty($this->customer)) {
|
82 |
+
$this->customer = new MailChimp_Customer();
|
83 |
+
}
|
84 |
+
|
85 |
+
return $this->customer;
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* @param $id
|
90 |
+
* @return $this
|
91 |
+
*/
|
92 |
+
public function setCampaignID($id)
|
93 |
+
{
|
94 |
+
$this->campaign_id = $id;
|
95 |
+
|
96 |
+
return $this;
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* @return mixed
|
101 |
+
*/
|
102 |
+
public function getCampaignID()
|
103 |
+
{
|
104 |
+
return $this->campaign_id;
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* @param $url
|
109 |
+
* @return $this
|
110 |
+
*/
|
111 |
+
public function setCheckoutUrl($url)
|
112 |
+
{
|
113 |
+
$this->checkout_url = $url;
|
114 |
+
|
115 |
+
return $this;
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* @return string
|
120 |
+
*/
|
121 |
+
public function getCheckoutURL()
|
122 |
+
{
|
123 |
+
if (empty($this->checkout_url)) {
|
124 |
+
$this->checkout_url = wc_get_checkout_url();
|
125 |
+
}
|
126 |
+
|
127 |
+
return $this->checkout_url;
|
128 |
+
}
|
129 |
+
|
130 |
+
/**
|
131 |
+
* @param $code
|
132 |
+
* @return $this
|
133 |
+
*/
|
134 |
+
public function setCurrencyCode($code)
|
135 |
+
{
|
136 |
+
$this->currency_code = $code;
|
137 |
+
|
138 |
+
return $this;
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* @return string
|
143 |
+
*/
|
144 |
+
public function getCurrencyCode()
|
145 |
+
{
|
146 |
+
if (empty($this->currency_code)) {
|
147 |
+
$options = get_option('mailchimp-woocommerce', array());
|
148 |
+
$this->currency_code = isset($options['store_currency_code']) ? $options['store_currency_code'] : 'USD';
|
149 |
+
}
|
150 |
+
|
151 |
+
return $this->currency_code;
|
152 |
+
}
|
153 |
+
|
154 |
+
/**
|
155 |
+
* @param $total
|
156 |
+
* @return $this
|
157 |
+
*/
|
158 |
+
public function setOrderTotal($total)
|
159 |
+
{
|
160 |
+
$this->order_total = number_format($total, 2);
|
161 |
+
|
162 |
+
return $this;
|
163 |
+
}
|
164 |
+
|
165 |
+
/**
|
166 |
+
* @return float
|
167 |
+
*/
|
168 |
+
public function getOrderTotal()
|
169 |
+
{
|
170 |
+
return $this->order_total;
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* @param $total
|
175 |
+
* @return $this
|
176 |
+
*/
|
177 |
+
public function setTaxTotal($total)
|
178 |
+
{
|
179 |
+
$this->tax_total = number_format($total, 2);
|
180 |
+
|
181 |
+
return $this;
|
182 |
+
}
|
183 |
+
|
184 |
+
/**
|
185 |
+
* @return float
|
186 |
+
*/
|
187 |
+
public function getTaxTotal()
|
188 |
+
{
|
189 |
+
return $this->tax_total;
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* @param MailChimp_LineItem $item
|
194 |
+
* @return $this
|
195 |
+
*/
|
196 |
+
public function addItem(MailChimp_LineItem $item)
|
197 |
+
{
|
198 |
+
$this->lines[] = $item;
|
199 |
+
return $this;
|
200 |
+
}
|
201 |
+
|
202 |
+
/**
|
203 |
+
* @return array
|
204 |
+
*/
|
205 |
+
public function items()
|
206 |
+
{
|
207 |
+
return $this->lines;
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* @return mixed
|
212 |
+
*/
|
213 |
+
public function toArray()
|
214 |
+
{
|
215 |
+
return mailchimp_array_remove_empty(array(
|
216 |
+
'id' => (string) $this->getId(),
|
217 |
+
'customer' => $this->getCustomer()->toArray(),
|
218 |
+
'campaign_id' => (string) $this->getCampaignID(),
|
219 |
+
'checkout_url' => (string) $this->getCheckoutURL(),
|
220 |
+
'currency_code' => (string) $this->getCurrencyCode(),
|
221 |
+
'order_total' => $this->getOrderTotal(),
|
222 |
+
'tax_total' => $this->getTaxTotal() > 0 ? $this->getTaxTotal() : null,
|
223 |
+
'lines' => array_map(function($item) {
|
224 |
+
return $item->toArray();
|
225 |
+
}, $this->items()),
|
226 |
+
));
|
227 |
+
}
|
228 |
+
|
229 |
+
/**
|
230 |
+
* @return array
|
231 |
+
*/
|
232 |
+
public function toArrayForUpdate()
|
233 |
+
{
|
234 |
+
return mailchimp_array_remove_empty(array(
|
235 |
+
'campaign_id' => (string) $this->getCampaignID(),
|
236 |
+
'checkout_url' => (string) $this->getCheckoutURL(),
|
237 |
+
'currency_code' => (string) $this->getCurrencyCode(),
|
238 |
+
'order_total' => $this->getOrderTotal(),
|
239 |
+
'tax_total' => $this->getTaxTotal() > 0 ? $this->getTaxTotal() : null,
|
240 |
+
'lines' => array_map(function($item) {
|
241 |
+
return $item->toArray();
|
242 |
+
}, $this->items()),
|
243 |
+
));
|
244 |
+
}
|
245 |
+
|
246 |
+
/**
|
247 |
+
* @param array $data
|
248 |
+
* @return MailChimp_Cart
|
249 |
+
*/
|
250 |
+
public function fromArray(array $data)
|
251 |
+
{
|
252 |
+
$singles = [
|
253 |
+
'store_id', 'id', 'campaign_id', 'checkout_url',
|
254 |
+
'currency_code', 'order_total', 'tax_total',
|
255 |
+
];
|
256 |
+
|
257 |
+
foreach ($singles as $key) {
|
258 |
+
if (array_key_exists($key, $data)) {
|
259 |
+
$this->$key = $data[$key];
|
260 |
+
}
|
261 |
+
}
|
262 |
+
|
263 |
+
if (array_key_exists('customer', $data) && is_array($data['customer'])) {
|
264 |
+
$this->customer = (new MailChimp_Customer())->fromArray($data['customer']);
|
265 |
+
}
|
266 |
+
|
267 |
+
if (array_key_exists('lines', $data) && is_array($data['lines'])) {
|
268 |
+
foreach ($data['lines'] as $line_item) {
|
269 |
+
$this->lines[] = (new MailChimp_LineItem)->fromArray($line_item);
|
270 |
+
}
|
271 |
+
}
|
272 |
+
|
273 |
+
return $this;
|
274 |
+
}
|
275 |
+
}
|
includes/api/assets/class-mailchimp-customer.php
ADDED
@@ -0,0 +1,257 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 3/8/16
|
9 |
+
* Time: 2:16 PM
|
10 |
+
*/
|
11 |
+
class MailChimp_Customer
|
12 |
+
{
|
13 |
+
protected $id = null;
|
14 |
+
protected $email_address = null;
|
15 |
+
protected $opt_in_status = null;
|
16 |
+
protected $company = null;
|
17 |
+
protected $first_name = null;
|
18 |
+
protected $last_name = null;
|
19 |
+
protected $orders_count = null;
|
20 |
+
protected $total_spent = null;
|
21 |
+
protected $address;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @return array
|
25 |
+
*/
|
26 |
+
public function getValidation()
|
27 |
+
{
|
28 |
+
return array(
|
29 |
+
'id' => 'required',
|
30 |
+
'email_address' => 'required|email',
|
31 |
+
'opt_in_status' => 'required|boolean',
|
32 |
+
'company' => 'string',
|
33 |
+
'first_name' => 'string',
|
34 |
+
'last_name' => 'string',
|
35 |
+
'orders_count' => 'integer',
|
36 |
+
'total_spent' => 'integer',
|
37 |
+
);
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @return null
|
42 |
+
*/
|
43 |
+
public function getId()
|
44 |
+
{
|
45 |
+
return $this->id;
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* @param null $id
|
50 |
+
* @return MailChimp_Customer
|
51 |
+
*/
|
52 |
+
public function setId($id)
|
53 |
+
{
|
54 |
+
$this->id = $id;
|
55 |
+
|
56 |
+
return $this;
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* @return null
|
61 |
+
*/
|
62 |
+
public function getEmailAddress()
|
63 |
+
{
|
64 |
+
return $this->email_address;
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* @param null $email_address
|
69 |
+
* @return MailChimp_Customer
|
70 |
+
*/
|
71 |
+
public function setEmailAddress($email_address)
|
72 |
+
{
|
73 |
+
$this->email_address = $email_address;
|
74 |
+
|
75 |
+
return $this;
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* @return null
|
80 |
+
*/
|
81 |
+
public function getOptInStatus()
|
82 |
+
{
|
83 |
+
return $this->opt_in_status;
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* @param null $opt_in_status
|
88 |
+
* @return MailChimp_Customer
|
89 |
+
*/
|
90 |
+
public function setOptInStatus($opt_in_status)
|
91 |
+
{
|
92 |
+
$this->opt_in_status = $opt_in_status;
|
93 |
+
|
94 |
+
return $this;
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* @return null
|
99 |
+
*/
|
100 |
+
public function getCompany()
|
101 |
+
{
|
102 |
+
return $this->company;
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* @param null $company
|
107 |
+
* @return MailChimp_Customer
|
108 |
+
*/
|
109 |
+
public function setCompany($company)
|
110 |
+
{
|
111 |
+
$this->company = $company;
|
112 |
+
|
113 |
+
return $this;
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* @return null
|
118 |
+
*/
|
119 |
+
public function getFirstName()
|
120 |
+
{
|
121 |
+
return $this->first_name;
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* @param null $first_name
|
126 |
+
* @return MailChimp_Customer
|
127 |
+
*/
|
128 |
+
public function setFirstName($first_name)
|
129 |
+
{
|
130 |
+
$this->first_name = $first_name;
|
131 |
+
|
132 |
+
return $this;
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* @return null
|
137 |
+
*/
|
138 |
+
public function getLastName()
|
139 |
+
{
|
140 |
+
return $this->last_name;
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* @param null $last_name
|
145 |
+
* @return MailChimp_Customer
|
146 |
+
*/
|
147 |
+
public function setLastName($last_name)
|
148 |
+
{
|
149 |
+
$this->last_name = $last_name;
|
150 |
+
|
151 |
+
return $this;
|
152 |
+
}
|
153 |
+
|
154 |
+
/**
|
155 |
+
* @return null
|
156 |
+
*/
|
157 |
+
public function getOrdersCount()
|
158 |
+
{
|
159 |
+
return $this->orders_count;
|
160 |
+
}
|
161 |
+
|
162 |
+
/**
|
163 |
+
* @param null $orders_count
|
164 |
+
* @return MailChimp_Customer
|
165 |
+
*/
|
166 |
+
public function setOrdersCount($orders_count)
|
167 |
+
{
|
168 |
+
$this->orders_count = $orders_count;
|
169 |
+
|
170 |
+
return $this;
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* @return null
|
175 |
+
*/
|
176 |
+
public function getTotalSpent()
|
177 |
+
{
|
178 |
+
return $this->total_spent;
|
179 |
+
}
|
180 |
+
|
181 |
+
/**
|
182 |
+
* @param null $total_spent
|
183 |
+
* @return MailChimp_Customer
|
184 |
+
*/
|
185 |
+
public function setTotalSpent($total_spent)
|
186 |
+
{
|
187 |
+
$this->total_spent = $total_spent;
|
188 |
+
|
189 |
+
return $this;
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* @return MailChimp_Address
|
194 |
+
*/
|
195 |
+
public function getAddress()
|
196 |
+
{
|
197 |
+
if (empty($this->address)) {
|
198 |
+
$this->address = new MailChimp_Address();
|
199 |
+
}
|
200 |
+
return $this->address;
|
201 |
+
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* @param MailChimp_Address $address
|
205 |
+
* @return MailChimp_Customer
|
206 |
+
*/
|
207 |
+
public function setAddress(MailChimp_Address $address)
|
208 |
+
{
|
209 |
+
$this->address = $address;
|
210 |
+
|
211 |
+
return $this;
|
212 |
+
}
|
213 |
+
|
214 |
+
/**
|
215 |
+
* @return array
|
216 |
+
*/
|
217 |
+
public function toArray()
|
218 |
+
{
|
219 |
+
$address = $this->getAddress()->toArray();
|
220 |
+
|
221 |
+
return mailchimp_array_remove_empty(array(
|
222 |
+
'id' => (string) $this->getId(),
|
223 |
+
'email_address' => (string) $this->getEmailAddress(),
|
224 |
+
'opt_in_status' => $this->getOptInStatus(),
|
225 |
+
'company' => (string) $this->getCompany(),
|
226 |
+
'first_name' => (string) $this->getFirstName(),
|
227 |
+
'last_name' => (string) $this->getLastName(),
|
228 |
+
'orders_count' => (int) $this->getOrdersCount(),
|
229 |
+
'total_spent' => number_format($this->getTotalSpent(), 2),
|
230 |
+
'address' => empty($address) ? null : $address,
|
231 |
+
));
|
232 |
+
}
|
233 |
+
|
234 |
+
/**
|
235 |
+
* @param array $data
|
236 |
+
* @return MailChimp_Customer
|
237 |
+
*/
|
238 |
+
public function fromArray(array $data)
|
239 |
+
{
|
240 |
+
$singles = [
|
241 |
+
'id', 'email_address', 'opt_in_status', 'company',
|
242 |
+
'first_name', 'last_name', 'orders_count', 'total_spent',
|
243 |
+
];
|
244 |
+
|
245 |
+
foreach ($singles as $key) {
|
246 |
+
if (array_key_exists($key, $data)) {
|
247 |
+
$this->$key = $data[$key];
|
248 |
+
}
|
249 |
+
}
|
250 |
+
|
251 |
+
if (array_key_exists('address', $data) && is_array($data['address'])) {
|
252 |
+
$this->address = (new MailChimp_Address())->fromArray($data['address']);
|
253 |
+
}
|
254 |
+
|
255 |
+
return $this;
|
256 |
+
}
|
257 |
+
}
|
includes/api/assets/class-mailchimp-line-item.php
ADDED
@@ -0,0 +1,180 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 3/8/16
|
9 |
+
* Time: 2:16 PM
|
10 |
+
*/
|
11 |
+
class MailChimp_LineItem
|
12 |
+
{
|
13 |
+
protected $id;
|
14 |
+
protected $product_id;
|
15 |
+
protected $product_variant_id;
|
16 |
+
protected $quantity;
|
17 |
+
protected $price;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @return array
|
21 |
+
*/
|
22 |
+
public function getValidation()
|
23 |
+
{
|
24 |
+
return array(
|
25 |
+
'id' => 'required|string',
|
26 |
+
'product_id' => 'required|string',
|
27 |
+
'product_variant_id' => 'required|string',
|
28 |
+
'quantity' => 'required|integer',
|
29 |
+
'price' => 'required|numeric',
|
30 |
+
);
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* @param $id
|
35 |
+
* @param $product_id
|
36 |
+
* @param $variant_id
|
37 |
+
* @param $quantity
|
38 |
+
* @param $price
|
39 |
+
* @return MailChimp_LineItem
|
40 |
+
*/
|
41 |
+
public static function make($id, $product_id, $variant_id, $quantity, $price)
|
42 |
+
{
|
43 |
+
$item = new MailChimp_LineItem();
|
44 |
+
$item->id = $id;
|
45 |
+
$item->product_id = $product_id;
|
46 |
+
$item->product_variant_id = $variant_id;
|
47 |
+
$item->quantity = $quantity;
|
48 |
+
$item->price = $price;
|
49 |
+
|
50 |
+
return $item;
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* @return mixed
|
55 |
+
*/
|
56 |
+
public function getId()
|
57 |
+
{
|
58 |
+
return $this->id;
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* @param mixed $id
|
63 |
+
* @return MailChimp_LineItem
|
64 |
+
*/
|
65 |
+
public function setId($id)
|
66 |
+
{
|
67 |
+
$this->id = $id;
|
68 |
+
|
69 |
+
return $this;
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* @return mixed
|
74 |
+
*/
|
75 |
+
public function getProductId()
|
76 |
+
{
|
77 |
+
return $this->product_id;
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* @param mixed $product_id
|
82 |
+
* @return MailChimp_LineItem
|
83 |
+
*/
|
84 |
+
public function setProductId($product_id)
|
85 |
+
{
|
86 |
+
$this->product_id = $product_id;
|
87 |
+
|
88 |
+
return $this;
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* @return mixed
|
93 |
+
*/
|
94 |
+
public function getProductVariantId()
|
95 |
+
{
|
96 |
+
return $this->product_variant_id;
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* @param mixed $product_variant_id
|
101 |
+
* @return MailChimp_LineItem
|
102 |
+
*/
|
103 |
+
public function setProductVariantId($product_variant_id)
|
104 |
+
{
|
105 |
+
$this->product_variant_id = $product_variant_id;
|
106 |
+
|
107 |
+
return $this;
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* @return mixed
|
112 |
+
*/
|
113 |
+
public function getQuantity()
|
114 |
+
{
|
115 |
+
return $this->quantity;
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* @param mixed $quantity
|
120 |
+
* @return MailChimp_LineItem
|
121 |
+
*/
|
122 |
+
public function setQuantity($quantity)
|
123 |
+
{
|
124 |
+
$this->quantity = $quantity;
|
125 |
+
|
126 |
+
return $this;
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* @return mixed
|
131 |
+
*/
|
132 |
+
public function getPrice()
|
133 |
+
{
|
134 |
+
return $this->price;
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* @param mixed $price
|
139 |
+
* @return MailChimp_LineItem
|
140 |
+
*/
|
141 |
+
public function setPrice($price)
|
142 |
+
{
|
143 |
+
$this->price = $price;
|
144 |
+
|
145 |
+
return $this;
|
146 |
+
}
|
147 |
+
|
148 |
+
/**
|
149 |
+
* @return array
|
150 |
+
*/
|
151 |
+
public function toArray()
|
152 |
+
{
|
153 |
+
return mailchimp_array_remove_empty(array(
|
154 |
+
'id' => (string) $this->id,
|
155 |
+
'product_id' => (string) $this->product_id,
|
156 |
+
'product_variant_id' => (string) $this->product_variant_id,
|
157 |
+
'quantity' => (int) $this->quantity,
|
158 |
+
'price' => (string) $this->price,
|
159 |
+
));
|
160 |
+
}
|
161 |
+
|
162 |
+
/**
|
163 |
+
* @param array $data
|
164 |
+
* @return MailChimp_LineItem
|
165 |
+
*/
|
166 |
+
public function fromArray(array $data)
|
167 |
+
{
|
168 |
+
$singles = array(
|
169 |
+
'id', 'product_id', 'product_variant_id', 'quantity', 'price',
|
170 |
+
);
|
171 |
+
|
172 |
+
foreach ($singles as $key) {
|
173 |
+
if (array_key_exists($key, $data)) {
|
174 |
+
$this->$key = $data[$key];
|
175 |
+
}
|
176 |
+
}
|
177 |
+
|
178 |
+
return $this;
|
179 |
+
}
|
180 |
+
}
|
includes/api/assets/class-mailchimp-order.php
ADDED
@@ -0,0 +1,405 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 3/8/16
|
9 |
+
* Time: 2:16 PM
|
10 |
+
*/
|
11 |
+
class MailChimp_Order
|
12 |
+
{
|
13 |
+
protected $id = null;
|
14 |
+
protected $customer = null;
|
15 |
+
protected $campaign_id = null;
|
16 |
+
protected $financial_status = null;
|
17 |
+
protected $fulfillment_status = null;
|
18 |
+
protected $currency_code = null;
|
19 |
+
protected $order_total = null;
|
20 |
+
protected $tax_total = null;
|
21 |
+
protected $shipping_total = null;
|
22 |
+
protected $updated_at_foreign = null;
|
23 |
+
protected $processed_at_foreign = null;
|
24 |
+
protected $cancelled_at_foreign = null;
|
25 |
+
protected $shipping_address = null;
|
26 |
+
protected $billing_address = null;
|
27 |
+
protected $lines = [];
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @return array
|
31 |
+
*/
|
32 |
+
public function getValidation()
|
33 |
+
{
|
34 |
+
return array(
|
35 |
+
'id' => 'required|string',
|
36 |
+
'customer' => 'required',
|
37 |
+
'campaign_id' => 'string',
|
38 |
+
'financial_status' => 'string',
|
39 |
+
'fulfillment_status' => 'string',
|
40 |
+
'currency_code' => 'required|currency_code',
|
41 |
+
'order_total' => 'required|numeric',
|
42 |
+
'tax_total' => 'numeric',
|
43 |
+
'processed_at_foreign' => 'date',
|
44 |
+
'updated_at_foreign' => 'date',
|
45 |
+
'cancelled_at_foreign' => 'date',
|
46 |
+
'lines' => 'required|array',
|
47 |
+
);
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* @param $id
|
52 |
+
* @return MailChimp_Order
|
53 |
+
*/
|
54 |
+
public function setId($id)
|
55 |
+
{
|
56 |
+
$this->id = $id;
|
57 |
+
|
58 |
+
return $this;
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* @return null|string
|
63 |
+
*/
|
64 |
+
public function getId()
|
65 |
+
{
|
66 |
+
return $this->id;
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* @param MailChimp_Customer $customer
|
71 |
+
* @return MailChimp_Order
|
72 |
+
*/
|
73 |
+
public function setCustomer(MailChimp_Customer $customer)
|
74 |
+
{
|
75 |
+
$this->customer = $customer;
|
76 |
+
|
77 |
+
return $this;
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* @return null|MailChimp_Customer
|
82 |
+
*/
|
83 |
+
public function getCustomer()
|
84 |
+
{
|
85 |
+
if (empty($this->customer)) {
|
86 |
+
$this->customer = new MailChimp_Customer();
|
87 |
+
}
|
88 |
+
return $this->customer;
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* @param MailChimp_LineItem $item
|
93 |
+
* @return $this
|
94 |
+
*/
|
95 |
+
public function addItem(MailChimp_LineItem $item)
|
96 |
+
{
|
97 |
+
$this->lines[] = $item;
|
98 |
+
return $this;
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* @return array
|
103 |
+
*/
|
104 |
+
public function items()
|
105 |
+
{
|
106 |
+
return $this->lines;
|
107 |
+
}
|
108 |
+
|
109 |
+
/**
|
110 |
+
* @return null
|
111 |
+
*/
|
112 |
+
public function getCampaignId()
|
113 |
+
{
|
114 |
+
return $this->campaign_id;
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* @param null $campaign_id
|
119 |
+
* @return MailChimp_Order
|
120 |
+
*/
|
121 |
+
public function setCampaignId($campaign_id)
|
122 |
+
{
|
123 |
+
$this->campaign_id = $campaign_id;
|
124 |
+
|
125 |
+
return $this;
|
126 |
+
}
|
127 |
+
|
128 |
+
/**
|
129 |
+
* @return null
|
130 |
+
*/
|
131 |
+
public function getFinancialStatus()
|
132 |
+
{
|
133 |
+
return $this->financial_status;
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* @param null $financial_status
|
138 |
+
* @return Order
|
139 |
+
*/
|
140 |
+
public function setFinancialStatus($financial_status)
|
141 |
+
{
|
142 |
+
$this->financial_status = $financial_status;
|
143 |
+
|
144 |
+
return $this;
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* @return null
|
149 |
+
*/
|
150 |
+
public function getFulfillmentStatus()
|
151 |
+
{
|
152 |
+
return $this->fulfillment_status;
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* @param null $fulfillment_status
|
157 |
+
* @return MailChimp_Order
|
158 |
+
*/
|
159 |
+
public function setFulfillmentStatus($fulfillment_status)
|
160 |
+
{
|
161 |
+
$this->fulfillment_status = $fulfillment_status;
|
162 |
+
|
163 |
+
return $this;
|
164 |
+
}
|
165 |
+
|
166 |
+
/**
|
167 |
+
* @return null
|
168 |
+
*/
|
169 |
+
public function getCurrencyCode()
|
170 |
+
{
|
171 |
+
return $this->currency_code;
|
172 |
+
}
|
173 |
+
|
174 |
+
/**
|
175 |
+
* @param null $currency_code
|
176 |
+
* @return MailChimp_Order
|
177 |
+
*/
|
178 |
+
public function setCurrencyCode($currency_code)
|
179 |
+
{
|
180 |
+
$this->currency_code = $currency_code;
|
181 |
+
|
182 |
+
return $this;
|
183 |
+
}
|
184 |
+
|
185 |
+
/**
|
186 |
+
* @return mixed
|
187 |
+
*/
|
188 |
+
public function getOrderTotal()
|
189 |
+
{
|
190 |
+
return $this->order_total;
|
191 |
+
}
|
192 |
+
|
193 |
+
/**
|
194 |
+
* @param mixed $order_total
|
195 |
+
* @return MailChimp_Order
|
196 |
+
*/
|
197 |
+
public function setOrderTotal($order_total)
|
198 |
+
{
|
199 |
+
$this->order_total = $order_total;
|
200 |
+
|
201 |
+
return $this;
|
202 |
+
}
|
203 |
+
|
204 |
+
/**
|
205 |
+
* @return mixed
|
206 |
+
*/
|
207 |
+
public function getTaxTotal()
|
208 |
+
{
|
209 |
+
return $this->tax_total;
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* @param mixed $tax_total
|
214 |
+
* @return MailChimp_Order
|
215 |
+
*/
|
216 |
+
public function setTaxTotal($tax_total)
|
217 |
+
{
|
218 |
+
$this->tax_total = $tax_total;
|
219 |
+
|
220 |
+
return $this;
|
221 |
+
}
|
222 |
+
|
223 |
+
/**
|
224 |
+
* @return mixed
|
225 |
+
*/
|
226 |
+
public function getShippingTotal()
|
227 |
+
{
|
228 |
+
return $this->shipping_total;
|
229 |
+
}
|
230 |
+
|
231 |
+
/**
|
232 |
+
* @param mixed $shipping_total
|
233 |
+
* @return MailChimp_Order
|
234 |
+
*/
|
235 |
+
public function setShippingTotal($shipping_total)
|
236 |
+
{
|
237 |
+
$this->shipping_total = $shipping_total;
|
238 |
+
|
239 |
+
return $this;
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* @param \DateTime $time
|
244 |
+
* @return $this
|
245 |
+
*/
|
246 |
+
public function setProcessedAt(\DateTime $time)
|
247 |
+
{
|
248 |
+
$this->processed_at_foreign = $time->format('Y-m-d H:i:s');
|
249 |
+
|
250 |
+
return $this;
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* @return null
|
255 |
+
*/
|
256 |
+
public function getProcessedAt()
|
257 |
+
{
|
258 |
+
return $this->processed_at_foreign;
|
259 |
+
}
|
260 |
+
|
261 |
+
/**
|
262 |
+
* @param \DateTime $time
|
263 |
+
* @return $this
|
264 |
+
*/
|
265 |
+
public function setCancelledAt(\DateTime $time)
|
266 |
+
{
|
267 |
+
$this->cancelled_at_foreign = $time->format('Y-m-d H:i:s');
|
268 |
+
|
269 |
+
return $this;
|
270 |
+
}
|
271 |
+
|
272 |
+
/**
|
273 |
+
* @return null
|
274 |
+
*/
|
275 |
+
public function getCancelledAt()
|
276 |
+
{
|
277 |
+
return $this->cancelled_at_foreign;
|
278 |
+
}
|
279 |
+
|
280 |
+
/**
|
281 |
+
* @param \DateTime $time
|
282 |
+
* @return $this
|
283 |
+
*/
|
284 |
+
public function setUpdatedAt(\DateTime $time)
|
285 |
+
{
|
286 |
+
$this->updated_at_foreign = $time->format('Y-m-d H:i:s');
|
287 |
+
|
288 |
+
return $this;
|
289 |
+
}
|
290 |
+
|
291 |
+
/**
|
292 |
+
* @return null
|
293 |
+
*/
|
294 |
+
public function getUpdatedAt()
|
295 |
+
{
|
296 |
+
return $this->updated_at_foreign;
|
297 |
+
}
|
298 |
+
|
299 |
+
/**
|
300 |
+
* @param MailChimp_Address $address
|
301 |
+
* @return $this
|
302 |
+
*/
|
303 |
+
public function setShippingAddress(MailChimp_Address $address)
|
304 |
+
{
|
305 |
+
$this->shipping_address = $address;
|
306 |
+
|
307 |
+
return $this;
|
308 |
+
}
|
309 |
+
|
310 |
+
/**
|
311 |
+
* @return MailChimp_Address
|
312 |
+
*/
|
313 |
+
public function getShippingAddress()
|
314 |
+
{
|
315 |
+
if (empty($this->shipping_address)) {
|
316 |
+
$this->shipping_address = new MailChimp_Address('shipping');
|
317 |
+
}
|
318 |
+
return $this->shipping_address;
|
319 |
+
}
|
320 |
+
|
321 |
+
/**
|
322 |
+
* @param MailChimp_Address $address
|
323 |
+
* @return $this
|
324 |
+
*/
|
325 |
+
public function setBillingAddress(MailChimp_Address $address)
|
326 |
+
{
|
327 |
+
$this->billing_address = $address;
|
328 |
+
|
329 |
+
return $this;
|
330 |
+
}
|
331 |
+
|
332 |
+
/**
|
333 |
+
* @return MailChimp_Address
|
334 |
+
*/
|
335 |
+
public function getBillingAddress()
|
336 |
+
{
|
337 |
+
if (empty($this->billing_address)) {
|
338 |
+
$this->billing_address = new MailChimp_Address('billing');
|
339 |
+
}
|
340 |
+
return $this->billing_address;
|
341 |
+
}
|
342 |
+
|
343 |
+
/**
|
344 |
+
* @return array
|
345 |
+
*/
|
346 |
+
public function toArray()
|
347 |
+
{
|
348 |
+
return mailchimp_array_remove_empty(array_filter(array(
|
349 |
+
'id' => (string) $this->getId(),
|
350 |
+
'customer' => $this->getCustomer()->toArray(),
|
351 |
+
'campaign_id' => (string) $this->getCampaignId(),
|
352 |
+
'financial_status' => (string) $this->getFinancialStatus(),
|
353 |
+
'fulfillment_status' => (string) $this->getFulfillmentStatus(),
|
354 |
+
'currency_code' => (string) $this->getCurrencyCode(),
|
355 |
+
'order_total' => floatval($this->getOrderTotal()),
|
356 |
+
'tax_total' => floatval($this->getTaxTotal()),
|
357 |
+
'shipping_total' => floatval($this->getShippingTotal()),
|
358 |
+
'processed_at_foreign' => (string) $this->getProcessedAt(),
|
359 |
+
'cancelled_at_foreign' => (string) $this->getCancelledAt(),
|
360 |
+
'updated_at_foreign' => (string) $this->getUpdatedAt(),
|
361 |
+
'shipping_address' => $this->getShippingAddress()->toArray(),
|
362 |
+
'billing_address' => $this->getBillingAddress()->toArray(),
|
363 |
+
'lines' => array_map(function ($item) {
|
364 |
+
/** @var MailChimp_LineItem $item */
|
365 |
+
return $item->toArray();
|
366 |
+
}, $this->items()),
|
367 |
+
)));
|
368 |
+
}
|
369 |
+
|
370 |
+
/**
|
371 |
+
* @param array $data
|
372 |
+
* @return MailChimp_Order
|
373 |
+
*/
|
374 |
+
public function fromArray(array $data)
|
375 |
+
{
|
376 |
+
$singles = array(
|
377 |
+
'id', 'campaign_id', 'financial_status', 'fulfillment_status',
|
378 |
+
'currency_code', 'order_total', 'tax_total', 'processed_at_foreign',
|
379 |
+
'cancelled_at_foreign', 'updated_at_foreign'
|
380 |
+
);
|
381 |
+
|
382 |
+
foreach ($singles as $key) {
|
383 |
+
if (array_key_exists($key, $data)) {
|
384 |
+
$this->$key = $data[$key];
|
385 |
+
}
|
386 |
+
}
|
387 |
+
|
388 |
+
if (array_key_exists('shipping_address', $data) && is_array($data['shipping_address'])) {
|
389 |
+
$this->shipping_address = (new MailChimp_Address())->fromArray($data['shipping_address']);
|
390 |
+
}
|
391 |
+
|
392 |
+
if (array_key_exists('billing_address', $data) && is_array($data['billing_address'])) {
|
393 |
+
$this->billing_address = (new MailChimp_Address())->fromArray($data['billing_address']);
|
394 |
+
}
|
395 |
+
|
396 |
+
if (array_key_exists('lines', $data) && is_array($data['lines'])) {
|
397 |
+
$this->lines = [];
|
398 |
+
foreach ($data['lines'] as $line_item) {
|
399 |
+
$this->lines[] = (new MailChimp_LineItem())->fromArray($line_item);
|
400 |
+
}
|
401 |
+
}
|
402 |
+
|
403 |
+
return $this;
|
404 |
+
}
|
405 |
+
}
|
includes/api/assets/class-mailchimp-product-variation.php
ADDED
@@ -0,0 +1,250 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 3/8/16
|
9 |
+
* Time: 2:17 PM
|
10 |
+
*/
|
11 |
+
class MailChimp_ProductVariation
|
12 |
+
{
|
13 |
+
protected $id = null;
|
14 |
+
protected $title = null;
|
15 |
+
protected $url = null;
|
16 |
+
protected $sku = null;
|
17 |
+
protected $price = null;
|
18 |
+
protected $inventory_quantity = null;
|
19 |
+
protected $image_url = null;
|
20 |
+
protected $backorders = null;
|
21 |
+
protected $visibility = null;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @return array
|
25 |
+
*/
|
26 |
+
public function getValidation()
|
27 |
+
{
|
28 |
+
return array(
|
29 |
+
'id' => 'required|string',
|
30 |
+
'title' => 'required|string',
|
31 |
+
'url' => 'url',
|
32 |
+
'sku' => 'string',
|
33 |
+
'price' => 'numeric',
|
34 |
+
'inventory_quantity' => 'integer',
|
35 |
+
'image_url' => 'url',
|
36 |
+
'backorders' => 'string',
|
37 |
+
'visibility' => 'string',
|
38 |
+
);
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* @return null
|
43 |
+
*/
|
44 |
+
public function getId()
|
45 |
+
{
|
46 |
+
return $this->id;
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* @param null $id
|
51 |
+
* @return MailChimp_ProductVariation
|
52 |
+
*/
|
53 |
+
public function setId($id)
|
54 |
+
{
|
55 |
+
$this->id = $id;
|
56 |
+
|
57 |
+
return $this;
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* @return null
|
62 |
+
*/
|
63 |
+
public function getTitle()
|
64 |
+
{
|
65 |
+
return $this->title;
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* @param null $title
|
70 |
+
* @return MailChimp_ProductVariation
|
71 |
+
*/
|
72 |
+
public function setTitle($title)
|
73 |
+
{
|
74 |
+
$this->title = $title;
|
75 |
+
|
76 |
+
return $this;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* @return null
|
81 |
+
*/
|
82 |
+
public function getUrl()
|
83 |
+
{
|
84 |
+
return $this->url;
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* @param null $url
|
89 |
+
* @return MailChimp_ProductVariation
|
90 |
+
*/
|
91 |
+
public function setUrl($url)
|
92 |
+
{
|
93 |
+
$this->url = $url;
|
94 |
+
|
95 |
+
return $this;
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* @return null
|
100 |
+
*/
|
101 |
+
public function getSku()
|
102 |
+
{
|
103 |
+
return $this->sku;
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* @param null $sku
|
108 |
+
* @return MailChimp_ProductVariation
|
109 |
+
*/
|
110 |
+
public function setSku($sku)
|
111 |
+
{
|
112 |
+
$this->sku = $sku;
|
113 |
+
|
114 |
+
return $this;
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* @return null
|
119 |
+
*/
|
120 |
+
public function getPrice()
|
121 |
+
{
|
122 |
+
return $this->price;
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* @param null $price
|
127 |
+
* @return MailChimp_ProductVariation
|
128 |
+
*/
|
129 |
+
public function setPrice($price)
|
130 |
+
{
|
131 |
+
$this->price = $price;
|
132 |
+
|
133 |
+
return $this;
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* @return null
|
138 |
+
*/
|
139 |
+
public function getInventoryQuantity()
|
140 |
+
{
|
141 |
+
return $this->inventory_quantity;
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* @param null $inventory_quantity
|
146 |
+
* @return MailChimp_ProductVariation
|
147 |
+
*/
|
148 |
+
public function setInventoryQuantity($inventory_quantity)
|
149 |
+
{
|
150 |
+
$this->inventory_quantity = $inventory_quantity;
|
151 |
+
|
152 |
+
return $this;
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* @return null
|
157 |
+
*/
|
158 |
+
public function getImageUrl()
|
159 |
+
{
|
160 |
+
return !empty($this->image_url) ? $this->image_url : null;
|
161 |
+
}
|
162 |
+
|
163 |
+
/**
|
164 |
+
* @param null $image_url
|
165 |
+
* @return MailChimp_ProductVariation
|
166 |
+
*/
|
167 |
+
public function setImageUrl($image_url)
|
168 |
+
{
|
169 |
+
$this->image_url = $image_url;
|
170 |
+
|
171 |
+
return $this;
|
172 |
+
}
|
173 |
+
|
174 |
+
/**
|
175 |
+
* @return null
|
176 |
+
*/
|
177 |
+
public function getBackorders()
|
178 |
+
{
|
179 |
+
return $this->backorders;
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* @param null $backorders
|
184 |
+
* @return MailChimp_ProductVariation
|
185 |
+
*/
|
186 |
+
public function setBackorders($backorders)
|
187 |
+
{
|
188 |
+
$this->backorders = $backorders;
|
189 |
+
|
190 |
+
return $this;
|
191 |
+
}
|
192 |
+
|
193 |
+
/**
|
194 |
+
* @return null
|
195 |
+
*/
|
196 |
+
public function getVisibility()
|
197 |
+
{
|
198 |
+
return $this->visibility;
|
199 |
+
}
|
200 |
+
|
201 |
+
/**
|
202 |
+
* @param null $visibility
|
203 |
+
* @return MailChimp_ProductVariation
|
204 |
+
*/
|
205 |
+
public function setVisibility($visibility)
|
206 |
+
{
|
207 |
+
$this->visibility = $visibility;
|
208 |
+
|
209 |
+
return $this;
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* @return array
|
214 |
+
*/
|
215 |
+
public function toArray()
|
216 |
+
{
|
217 |
+
return mailchimp_array_remove_empty(array(
|
218 |
+
'id' => (string) $this->getId(),
|
219 |
+
'title' => $this->getTitle(),
|
220 |
+
'url' => (string) $this->getUrl(),
|
221 |
+
'sku' => (string) $this->getSku(),
|
222 |
+
'price' => $this->getPrice(),
|
223 |
+
'inventory_quantity' => (int) $this->getInventoryQuantity(),
|
224 |
+
'image_url' => (string) $this->getImageUrl(),
|
225 |
+
'backorders' => (string) $this->getBackorders(),
|
226 |
+
'visibility' => (string) $this->getVisibility(),
|
227 |
+
));
|
228 |
+
}
|
229 |
+
|
230 |
+
/**
|
231 |
+
* @param array $data
|
232 |
+
* @return MailChimp_ProductVariation
|
233 |
+
*/
|
234 |
+
public function fromArray(array $data)
|
235 |
+
{
|
236 |
+
$singles = array(
|
237 |
+
'id', 'title', 'url', 'sku',
|
238 |
+
'price', 'inventory_quantity', 'image_url', 'backorders',
|
239 |
+
'visibility',
|
240 |
+
);
|
241 |
+
|
242 |
+
foreach ($singles as $key) {
|
243 |
+
if (array_key_exists($key, $data)) {
|
244 |
+
$this->$key = $data[$key];
|
245 |
+
}
|
246 |
+
}
|
247 |
+
|
248 |
+
return $this;
|
249 |
+
}
|
250 |
+
}
|
includes/api/assets/class-mailchimp-product.php
ADDED
@@ -0,0 +1,281 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 3/8/16
|
9 |
+
* Time: 2:17 PM
|
10 |
+
*/
|
11 |
+
class MailChimp_Product
|
12 |
+
{
|
13 |
+
protected $id;
|
14 |
+
protected $title;
|
15 |
+
protected $handle = null;
|
16 |
+
protected $url = null;
|
17 |
+
protected $description = null;
|
18 |
+
protected $type = null;
|
19 |
+
protected $vendor = null;
|
20 |
+
protected $image_url = null;
|
21 |
+
protected $variants = array();
|
22 |
+
protected $published_at_foreign = null;
|
23 |
+
|
24 |
+
/**
|
25 |
+
* @return array
|
26 |
+
*/
|
27 |
+
public function getValidation()
|
28 |
+
{
|
29 |
+
return array(
|
30 |
+
'id' => 'required|string',
|
31 |
+
'title' => 'required|string',
|
32 |
+
'handle' => 'string',
|
33 |
+
'url' => 'url',
|
34 |
+
'description' => 'string',
|
35 |
+
'type' => 'string',
|
36 |
+
'vendor' => 'string',
|
37 |
+
'image_url' => 'url',
|
38 |
+
'variants' => 'required|array',
|
39 |
+
'published_at_foreign' => 'date',
|
40 |
+
);
|
41 |
+
}
|
42 |
+
|
43 |
+
/**
|
44 |
+
* @return mixed
|
45 |
+
*/
|
46 |
+
public function getId()
|
47 |
+
{
|
48 |
+
return $this->id;
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @param mixed $id
|
53 |
+
* @return MailChimp_Product
|
54 |
+
*/
|
55 |
+
public function setId($id)
|
56 |
+
{
|
57 |
+
$this->id = $id;
|
58 |
+
|
59 |
+
return $this;
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* @return mixed
|
64 |
+
*/
|
65 |
+
public function getTitle()
|
66 |
+
{
|
67 |
+
return $this->title;
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* @param mixed $title
|
72 |
+
* @return MailChimp_Product
|
73 |
+
*/
|
74 |
+
public function setTitle($title)
|
75 |
+
{
|
76 |
+
$this->title = $title;
|
77 |
+
|
78 |
+
return $this;
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* @return null
|
83 |
+
*/
|
84 |
+
public function getHandle()
|
85 |
+
{
|
86 |
+
return $this->handle;
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* @param null $handle
|
91 |
+
* @return MailChimp_Product
|
92 |
+
*/
|
93 |
+
public function setHandle($handle)
|
94 |
+
{
|
95 |
+
$this->handle = $handle;
|
96 |
+
|
97 |
+
return $this;
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* @return null
|
102 |
+
*/
|
103 |
+
public function getUrl()
|
104 |
+
{
|
105 |
+
return $this->url;
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* @param null $url
|
110 |
+
* @return MailChimp_Product
|
111 |
+
*/
|
112 |
+
public function setUrl($url)
|
113 |
+
{
|
114 |
+
$this->url = $url;
|
115 |
+
|
116 |
+
return $this;
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* @return null
|
121 |
+
*/
|
122 |
+
public function getDescription()
|
123 |
+
{
|
124 |
+
return $this->description;
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* @param null $description
|
129 |
+
* @return MailChimp_Product
|
130 |
+
*/
|
131 |
+
public function setDescription($description)
|
132 |
+
{
|
133 |
+
$this->description = $description;
|
134 |
+
|
135 |
+
return $this;
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* @return null
|
140 |
+
*/
|
141 |
+
public function getType()
|
142 |
+
{
|
143 |
+
return $this->type;
|
144 |
+
}
|
145 |
+
|
146 |
+
/**
|
147 |
+
* @param null $type
|
148 |
+
* @return MailChimp_Product
|
149 |
+
*/
|
150 |
+
public function setType($type)
|
151 |
+
{
|
152 |
+
$this->type = $type;
|
153 |
+
|
154 |
+
return $this;
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* @return null
|
159 |
+
*/
|
160 |
+
public function getVendor()
|
161 |
+
{
|
162 |
+
return $this->vendor;
|
163 |
+
}
|
164 |
+
|
165 |
+
/**
|
166 |
+
* @param null $vendor
|
167 |
+
* @return MailChimp_Product
|
168 |
+
*/
|
169 |
+
public function setVendor($vendor)
|
170 |
+
{
|
171 |
+
$this->vendor = $vendor;
|
172 |
+
|
173 |
+
return $this;
|
174 |
+
}
|
175 |
+
|
176 |
+
/**
|
177 |
+
* @return null
|
178 |
+
*/
|
179 |
+
public function getImageUrl()
|
180 |
+
{
|
181 |
+
return $this->image_url;
|
182 |
+
}
|
183 |
+
|
184 |
+
/**
|
185 |
+
* @param null $image_url
|
186 |
+
* @return MailChimp_Product
|
187 |
+
*/
|
188 |
+
public function setImageUrl($image_url)
|
189 |
+
{
|
190 |
+
$this->image_url = $image_url;
|
191 |
+
|
192 |
+
return $this;
|
193 |
+
}
|
194 |
+
|
195 |
+
/**
|
196 |
+
* @return array
|
197 |
+
*/
|
198 |
+
public function getVariations()
|
199 |
+
{
|
200 |
+
return $this->variants;
|
201 |
+
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* @param MailChimp_ProductVariation $variation
|
205 |
+
* @return MailChimp_Product
|
206 |
+
*/
|
207 |
+
public function addVariant(MailChimp_ProductVariation $variation)
|
208 |
+
{
|
209 |
+
$this->variants[] = $variation;
|
210 |
+
|
211 |
+
return $this;
|
212 |
+
}
|
213 |
+
|
214 |
+
/**
|
215 |
+
* @return string
|
216 |
+
*/
|
217 |
+
public function getPublishedAtForeign()
|
218 |
+
{
|
219 |
+
return $this->published_at_foreign;
|
220 |
+
}
|
221 |
+
|
222 |
+
/**
|
223 |
+
* @param \DateTime $time
|
224 |
+
* @return MailChimp_Product
|
225 |
+
*/
|
226 |
+
public function setPublishedAtForeign(\DateTime $time)
|
227 |
+
{
|
228 |
+
$this->published_at_foreign = $time->format('Y-m-d H:i:s');
|
229 |
+
|
230 |
+
return $this;
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* @return array
|
235 |
+
*/
|
236 |
+
public function toArray()
|
237 |
+
{
|
238 |
+
return mailchimp_array_remove_empty(array(
|
239 |
+
'id' => (string) $this->getId(),
|
240 |
+
'title' => $this->getTitle(),
|
241 |
+
'handle' => (string) $this->getHandle(),
|
242 |
+
'url' => (string) $this->getUrl(),
|
243 |
+
'description' => (string) $this->getDescription(),
|
244 |
+
'type' => (string) $this->getType(),
|
245 |
+
'vendor' => (string) $this->getVendor(),
|
246 |
+
'image_url' => (string) $this->getImageUrl(),
|
247 |
+
'variants' => array_map(function ($item) {
|
248 |
+
return $item->toArray();
|
249 |
+
}, $this->getVariations()),
|
250 |
+
'published_at_foreign' => (string) $this->getPublishedAtForeign(),
|
251 |
+
));
|
252 |
+
}
|
253 |
+
|
254 |
+
/**
|
255 |
+
* @param array $data
|
256 |
+
* @return MailChimp_Product
|
257 |
+
*/
|
258 |
+
public function fromArray(array $data)
|
259 |
+
{
|
260 |
+
$singles = array(
|
261 |
+
'id', 'title', 'handle', 'url',
|
262 |
+
'description', 'type', 'vendor', 'image_url',
|
263 |
+
'published_at_foreign',
|
264 |
+
);
|
265 |
+
|
266 |
+
foreach ($singles as $key) {
|
267 |
+
if (array_key_exists($key, $data)) {
|
268 |
+
$this->$key = $data[$key];
|
269 |
+
}
|
270 |
+
}
|
271 |
+
|
272 |
+
if (array_key_exists('variants', $data) && is_array($data['variants'])) {
|
273 |
+
$this->variants = array();
|
274 |
+
foreach ($data['variants'] as $variant) {
|
275 |
+
$this->variants[] = (new MailChimp_ProductVariation())->fromArray($variant);
|
276 |
+
}
|
277 |
+
}
|
278 |
+
|
279 |
+
return $this;
|
280 |
+
}
|
281 |
+
}
|
includes/api/assets/class-mailchimp-store.php
ADDED
@@ -0,0 +1,320 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 3/8/16
|
9 |
+
* Time: 3:13 PM
|
10 |
+
*/
|
11 |
+
class MailChimp_Store
|
12 |
+
{
|
13 |
+
protected $id = null;
|
14 |
+
protected $list_id = null;
|
15 |
+
protected $name = null;
|
16 |
+
protected $domain = null;
|
17 |
+
protected $email_address = null;
|
18 |
+
protected $currency_code = null;
|
19 |
+
protected $money_format = null;
|
20 |
+
protected $primary_locale = null;
|
21 |
+
protected $timezone = null;
|
22 |
+
protected $phone = null;
|
23 |
+
protected $address = null;
|
24 |
+
protected $platform = null;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @return array
|
28 |
+
*/
|
29 |
+
public function getValidation()
|
30 |
+
{
|
31 |
+
return array(
|
32 |
+
'id' => 'required|string',
|
33 |
+
'list_id' => 'required|string',
|
34 |
+
'name' => 'required|string',
|
35 |
+
'domain' => 'string',
|
36 |
+
'email_address' => 'email',
|
37 |
+
'currency_code' => 'required|currency_code',
|
38 |
+
'primary_locale' => 'locale_basic',
|
39 |
+
'timezone' => 'timezone',
|
40 |
+
'phone' => 'string',
|
41 |
+
);
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* @return null
|
46 |
+
*/
|
47 |
+
public function getId()
|
48 |
+
{
|
49 |
+
return $this->id;
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* @param null $id
|
54 |
+
* @return MailChimp_Store
|
55 |
+
*/
|
56 |
+
public function setId($id)
|
57 |
+
{
|
58 |
+
$this->id = $id;
|
59 |
+
|
60 |
+
return $this;
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* @return null
|
65 |
+
*/
|
66 |
+
public function getListId()
|
67 |
+
{
|
68 |
+
return $this->list_id;
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* @param null $list_id
|
73 |
+
* @return MailChimp_Store
|
74 |
+
*/
|
75 |
+
public function setListId($list_id)
|
76 |
+
{
|
77 |
+
$this->list_id = $list_id;
|
78 |
+
|
79 |
+
return $this;
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* @return null
|
84 |
+
*/
|
85 |
+
public function getName()
|
86 |
+
{
|
87 |
+
return $this->name;
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* @param null $name
|
92 |
+
* @return MailChimp_Store;
|
93 |
+
*/
|
94 |
+
public function setName($name)
|
95 |
+
{
|
96 |
+
$this->name = $name;
|
97 |
+
|
98 |
+
return $this;
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* @return null
|
103 |
+
*/
|
104 |
+
public function getDomain()
|
105 |
+
{
|
106 |
+
return $this->domain;
|
107 |
+
}
|
108 |
+
|
109 |
+
/**
|
110 |
+
* @param null $domain
|
111 |
+
* @return MailChimp_Store;
|
112 |
+
*/
|
113 |
+
public function setDomain($domain)
|
114 |
+
{
|
115 |
+
$this->domain = $domain;
|
116 |
+
|
117 |
+
return $this;
|
118 |
+
}
|
119 |
+
|
120 |
+
/**
|
121 |
+
* @return null
|
122 |
+
*/
|
123 |
+
public function getEmailAddress()
|
124 |
+
{
|
125 |
+
return $this->email_address;
|
126 |
+
}
|
127 |
+
|
128 |
+
/**
|
129 |
+
* @param null $email_address
|
130 |
+
* @return MailChimp_Store;
|
131 |
+
*/
|
132 |
+
public function setEmailAddress($email_address)
|
133 |
+
{
|
134 |
+
$this->email_address = $email_address;
|
135 |
+
|
136 |
+
return $this;
|
137 |
+
}
|
138 |
+
|
139 |
+
/**
|
140 |
+
* @return null
|
141 |
+
*/
|
142 |
+
public function getCurrencyCode()
|
143 |
+
{
|
144 |
+
return $this->currency_code;
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* @param null $currency_code
|
149 |
+
* @return MailChimp_Store;
|
150 |
+
*/
|
151 |
+
public function setCurrencyCode($currency_code)
|
152 |
+
{
|
153 |
+
$this->currency_code = $currency_code;
|
154 |
+
|
155 |
+
return $this;
|
156 |
+
}
|
157 |
+
|
158 |
+
/**
|
159 |
+
* @return null
|
160 |
+
*/
|
161 |
+
public function getMoneyFormat()
|
162 |
+
{
|
163 |
+
return $this->money_format;
|
164 |
+
}
|
165 |
+
|
166 |
+
/**
|
167 |
+
* @param null $money_format
|
168 |
+
* @return MailChimp_Store;
|
169 |
+
*/
|
170 |
+
public function setMoneyFormat($money_format)
|
171 |
+
{
|
172 |
+
$this->money_format = $money_format;
|
173 |
+
|
174 |
+
return $this;
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* @return null
|
179 |
+
*/
|
180 |
+
public function getPrimaryLocale()
|
181 |
+
{
|
182 |
+
return $this->primary_locale;
|
183 |
+
}
|
184 |
+
|
185 |
+
/**
|
186 |
+
* @param null $primary_locale
|
187 |
+
* @return MailChimp_Store;
|
188 |
+
*/
|
189 |
+
public function setPrimaryLocale($primary_locale)
|
190 |
+
{
|
191 |
+
$this->primary_locale = $primary_locale;
|
192 |
+
|
193 |
+
return $this;
|
194 |
+
}
|
195 |
+
|
196 |
+
/**
|
197 |
+
* @return null
|
198 |
+
*/
|
199 |
+
public function getTimezone()
|
200 |
+
{
|
201 |
+
return $this->timezone;
|
202 |
+
}
|
203 |
+
|
204 |
+
/**
|
205 |
+
* @param null $timezone
|
206 |
+
* @return MailChimp_Store;
|
207 |
+
*/
|
208 |
+
public function setTimezone($timezone)
|
209 |
+
{
|
210 |
+
$this->timezone = $timezone;
|
211 |
+
|
212 |
+
return $this;
|
213 |
+
}
|
214 |
+
|
215 |
+
/**
|
216 |
+
* @return null
|
217 |
+
*/
|
218 |
+
public function getPhone()
|
219 |
+
{
|
220 |
+
return $this->phone;
|
221 |
+
}
|
222 |
+
|
223 |
+
/**
|
224 |
+
* @param null $phone
|
225 |
+
* @return MailChimp_Store;
|
226 |
+
*/
|
227 |
+
public function setPhone($phone)
|
228 |
+
{
|
229 |
+
$this->phone = $phone;
|
230 |
+
|
231 |
+
return $this;
|
232 |
+
}
|
233 |
+
|
234 |
+
/**
|
235 |
+
* @param $platform
|
236 |
+
* @return $this
|
237 |
+
*/
|
238 |
+
public function setPlatform($platform)
|
239 |
+
{
|
240 |
+
$this->platform = $platform;
|
241 |
+
|
242 |
+
return $this;
|
243 |
+
}
|
244 |
+
|
245 |
+
/**
|
246 |
+
* @return string
|
247 |
+
*/
|
248 |
+
public function getPlatform()
|
249 |
+
{
|
250 |
+
return $this->platform;
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* @return MailChimp_Address
|
255 |
+
*/
|
256 |
+
public function getAddress()
|
257 |
+
{
|
258 |
+
if (empty($this->address)) {
|
259 |
+
$this->address = new MailChimp_Address();
|
260 |
+
}
|
261 |
+
return $this->address;
|
262 |
+
}
|
263 |
+
|
264 |
+
/**
|
265 |
+
* @param MailChimp_Address $address
|
266 |
+
* @return Store;
|
267 |
+
*/
|
268 |
+
public function setAddress(MailChimp_Address $address)
|
269 |
+
{
|
270 |
+
$this->address = $address;
|
271 |
+
|
272 |
+
return $this;
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* @return array
|
277 |
+
*/
|
278 |
+
public function toArray()
|
279 |
+
{
|
280 |
+
return mailchimp_array_remove_empty(array(
|
281 |
+
'id' => $this->getId(),
|
282 |
+
'platform' => $this->getPlatform(),
|
283 |
+
'list_id' => $this->getListId(),
|
284 |
+
'name' => $this->getName(),
|
285 |
+
'domain' => $this->getDomain(),
|
286 |
+
'email_address' => $this->getEmailAddress(),
|
287 |
+
'currency_code' => $this->getCurrencyCode(),
|
288 |
+
'money_format' => $this->getMoneyFormat(),
|
289 |
+
'primary_locale' => $this->getPrimaryLocale(),
|
290 |
+
'timezone' => $this->getTimezone(),
|
291 |
+
'phone' => $this->getPhone(),
|
292 |
+
'address' => $this->getAddress()->toArray(),
|
293 |
+
));
|
294 |
+
}
|
295 |
+
|
296 |
+
/**
|
297 |
+
* @param array $data
|
298 |
+
* @return MailChimp_Store
|
299 |
+
*/
|
300 |
+
public function fromArray(array $data)
|
301 |
+
{
|
302 |
+
$singles = array(
|
303 |
+
'id', 'list_id', 'name', 'domain',
|
304 |
+
'email_address', 'currency_code', 'money_format',
|
305 |
+
'primary_locale', 'timezone', 'phone', 'platform',
|
306 |
+
);
|
307 |
+
|
308 |
+
foreach ($singles as $key) {
|
309 |
+
if (array_key_exists($key, $data)) {
|
310 |
+
$this->$key = $data[$key];
|
311 |
+
}
|
312 |
+
}
|
313 |
+
|
314 |
+
if (array_key_exists('address', $data)) {
|
315 |
+
$this->address = (new MailChimp_Address())->fromArray($data['address']);
|
316 |
+
}
|
317 |
+
|
318 |
+
return $this;
|
319 |
+
}
|
320 |
+
}
|
includes/api/class-mailchimp-api.php
ADDED
@@ -0,0 +1,1003 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by PhpStorm.
|
5 |
+
*
|
6 |
+
* User: kingpin
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 11/4/15
|
9 |
+
* Time: 3:35 PM
|
10 |
+
*/
|
11 |
+
class MailChimpApi
|
12 |
+
{
|
13 |
+
protected $version = '3.0';
|
14 |
+
protected $data_center = 'us2';
|
15 |
+
protected $api_key = null;
|
16 |
+
protected $auth_type = 'key';
|
17 |
+
|
18 |
+
/**
|
19 |
+
* MailChimpService constructor.
|
20 |
+
* @param null $api_key
|
21 |
+
*/
|
22 |
+
public function __construct($api_key = null)
|
23 |
+
{
|
24 |
+
if (!empty($api_key)) {
|
25 |
+
$this->setApiKey($api_key);
|
26 |
+
}
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @param $key
|
31 |
+
* @return $this
|
32 |
+
*/
|
33 |
+
public function setApiKey($key)
|
34 |
+
{
|
35 |
+
$parts = str_getcsv($key, '-');
|
36 |
+
|
37 |
+
if (count($parts) == 2) {
|
38 |
+
$this->data_center = $parts[1];
|
39 |
+
}
|
40 |
+
|
41 |
+
$this->api_key = $parts[0];
|
42 |
+
|
43 |
+
return $this;
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* @param $dc
|
48 |
+
* @return $this
|
49 |
+
*/
|
50 |
+
public function setDataCenter($dc)
|
51 |
+
{
|
52 |
+
$this->data_center = $dc;
|
53 |
+
|
54 |
+
return $this;
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* @param $version
|
59 |
+
* @return $this
|
60 |
+
*/
|
61 |
+
public function setVersion($version)
|
62 |
+
{
|
63 |
+
$this->version = $version;
|
64 |
+
|
65 |
+
return $this;
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* @param bool $return_profile
|
70 |
+
* @return array|bool
|
71 |
+
*/
|
72 |
+
public function ping($return_profile = false)
|
73 |
+
{
|
74 |
+
try {
|
75 |
+
$profile = $this->get('/');
|
76 |
+
return $return_profile ? $profile : true;
|
77 |
+
} catch (MailChimp_Error $e) {
|
78 |
+
return false;
|
79 |
+
}
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* @return array
|
84 |
+
*/
|
85 |
+
public function getProfile()
|
86 |
+
{
|
87 |
+
return $this->get('/');
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* @return array|bool
|
92 |
+
*/
|
93 |
+
public function getAuthorizedApps()
|
94 |
+
{
|
95 |
+
return $this->get('authorized-apps');
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* @return array|bool
|
100 |
+
*/
|
101 |
+
public function getAuthorizedAppDetails($id)
|
102 |
+
{
|
103 |
+
return $this->get("authorized-apps/$id");
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Returns an array of ['access_token' => '', 'viewer_token' => '']
|
108 |
+
*
|
109 |
+
* @param $client_id
|
110 |
+
* @param $client_secret
|
111 |
+
* @return array|bool
|
112 |
+
*/
|
113 |
+
public function linkAuthorizedApp($client_id, $client_secret)
|
114 |
+
{
|
115 |
+
return $this->post('authorized-apps', array('client_id' => $client_id, 'client_secret' => $client_secret));
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* @param $list_id
|
120 |
+
* @param $email
|
121 |
+
* @return array|bool
|
122 |
+
*/
|
123 |
+
public function member($list_id, $email)
|
124 |
+
{
|
125 |
+
$hash = md5(strtolower($email));
|
126 |
+
return $this->get("lists/$list_id/members/$hash", array());
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* @param $list_id
|
131 |
+
* @return array|bool
|
132 |
+
*/
|
133 |
+
public function members($list_id)
|
134 |
+
{
|
135 |
+
return $this->get("lists/$list_id/members");
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* @param $list_id
|
140 |
+
* @param $email
|
141 |
+
* @param bool $subscribed
|
142 |
+
* @param array $merge_fields
|
143 |
+
* @param array $list_interests
|
144 |
+
* @return array|bool
|
145 |
+
*/
|
146 |
+
public function subscribe($list_id, $email, $subscribed = true, $merge_fields = array(), $list_interests = array())
|
147 |
+
{
|
148 |
+
$data = array(
|
149 |
+
'email_type' => 'html',
|
150 |
+
'email_address' => $email,
|
151 |
+
'status' => $subscribed === true ? 'subscribed' : 'pending',
|
152 |
+
'merge_fields' => $merge_fields,
|
153 |
+
'interests' => $list_interests,
|
154 |
+
);
|
155 |
+
|
156 |
+
if (empty($data['merge_fields'])) {
|
157 |
+
unset($data['merge_fields']);
|
158 |
+
}
|
159 |
+
|
160 |
+
if (empty($data['interests'])) {
|
161 |
+
unset($data['interests']);
|
162 |
+
}
|
163 |
+
|
164 |
+
return $this->post("lists/$list_id/members", $data);
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* @param $list_id
|
169 |
+
* @param $email
|
170 |
+
* @param bool $subscribed
|
171 |
+
* @param array $merge_fields
|
172 |
+
* @param array $list_interests
|
173 |
+
* @return array|bool
|
174 |
+
*/
|
175 |
+
public function update($list_id, $email, $subscribed = true, $merge_fields = array(), $list_interests = array())
|
176 |
+
{
|
177 |
+
$hash = md5(strtolower($email));
|
178 |
+
|
179 |
+
$data = array(
|
180 |
+
'email_address' => $email,
|
181 |
+
'status' => ($subscribed === null ? 'cleaned' : ($subscribed === true ? 'subscribed' : 'unsubscribed')),
|
182 |
+
'merge_fields' => $merge_fields,
|
183 |
+
'interests' => $list_interests,
|
184 |
+
);
|
185 |
+
|
186 |
+
if (empty($data['merge_fields'])) {
|
187 |
+
unset($data['merge_fields']);
|
188 |
+
}
|
189 |
+
|
190 |
+
|
191 |
+
if (empty($data['interests'])) {
|
192 |
+
unset($data['interests']);
|
193 |
+
}
|
194 |
+
|
195 |
+
return $this->patch("lists/$list_id/members/$hash", $data);
|
196 |
+
}
|
197 |
+
|
198 |
+
/**
|
199 |
+
* @param $list_id
|
200 |
+
* @param $email
|
201 |
+
* @param bool $subscribed
|
202 |
+
* @param array $merge_fields
|
203 |
+
* @param array $list_interests
|
204 |
+
* @return array|bool
|
205 |
+
*/
|
206 |
+
public function updateOrCreate($list_id, $email, $subscribed = true, $merge_fields = array(), $list_interests = array())
|
207 |
+
{
|
208 |
+
$hash = md5(strtolower($email));
|
209 |
+
|
210 |
+
$data = array(
|
211 |
+
'email_address' => $email,
|
212 |
+
'status' => ($subscribed === null ? 'cleaned' : ($subscribed === true ? 'subscribed' : 'unsubscribed')),
|
213 |
+
'status_if_new' => $subscribed === true ? 'subscribed' : 'pending',
|
214 |
+
'merge_fields' => $merge_fields,
|
215 |
+
'interests' => $list_interests,
|
216 |
+
);
|
217 |
+
|
218 |
+
if (empty($data['merge_fields'])) {
|
219 |
+
unset($data['merge_fields']);
|
220 |
+
}
|
221 |
+
|
222 |
+
if (empty($data['interests'])) {
|
223 |
+
unset($data['interests']);
|
224 |
+
}
|
225 |
+
|
226 |
+
return $this->put("lists/$list_id/members/$hash", $data);
|
227 |
+
}
|
228 |
+
|
229 |
+
/**
|
230 |
+
* @param MailChimp_CreateListSubmission $submission
|
231 |
+
* @return array|bool
|
232 |
+
*/
|
233 |
+
public function createList(MailChimp_CreateListSubmission $submission)
|
234 |
+
{
|
235 |
+
return $this->post('lists', $submission->getSubmission());
|
236 |
+
}
|
237 |
+
|
238 |
+
/**
|
239 |
+
* @param bool $as_list
|
240 |
+
* @param int $count
|
241 |
+
* @return array|mixed
|
242 |
+
*/
|
243 |
+
public function getLists($as_list = false, $count = 100)
|
244 |
+
{
|
245 |
+
$result = $this->get('lists', array('count' => $count));
|
246 |
+
|
247 |
+
if ($as_list) {
|
248 |
+
$lists = array();
|
249 |
+
if ($result) {
|
250 |
+
$result = (object)$result;
|
251 |
+
if (isset($result->lists) && is_array($result->lists)) {
|
252 |
+
foreach ($result->lists as $list) {
|
253 |
+
$list = (object)$list;
|
254 |
+
$lists[$list->id] = $list->name;
|
255 |
+
}
|
256 |
+
}
|
257 |
+
}
|
258 |
+
|
259 |
+
return $lists;
|
260 |
+
}
|
261 |
+
|
262 |
+
return $result;
|
263 |
+
}
|
264 |
+
|
265 |
+
/**
|
266 |
+
* @param $id
|
267 |
+
* @return bool
|
268 |
+
*/
|
269 |
+
public function hasList($id)
|
270 |
+
{
|
271 |
+
try {
|
272 |
+
return (bool) $this->getList($id);
|
273 |
+
} catch (\Exception $e) {
|
274 |
+
return false;
|
275 |
+
}
|
276 |
+
}
|
277 |
+
/**
|
278 |
+
* @param $id
|
279 |
+
* @return mixed
|
280 |
+
*/
|
281 |
+
public function getList($id)
|
282 |
+
{
|
283 |
+
return $this->get('lists/' . $id);
|
284 |
+
}
|
285 |
+
|
286 |
+
/**
|
287 |
+
* @param $id
|
288 |
+
* @return array|bool
|
289 |
+
*/
|
290 |
+
public function deleteList($id)
|
291 |
+
{
|
292 |
+
return $this->delete('lists/'.$id);
|
293 |
+
}
|
294 |
+
|
295 |
+
/**
|
296 |
+
* @return array|mixed
|
297 |
+
*/
|
298 |
+
public function getListsWithMergeFields()
|
299 |
+
{
|
300 |
+
$lists = $this->getLists(true);
|
301 |
+
foreach ($lists as $id => $name) {
|
302 |
+
$lists[$id] = $this->mergeFields($id, 100);
|
303 |
+
}
|
304 |
+
|
305 |
+
return $lists;
|
306 |
+
}
|
307 |
+
|
308 |
+
/**
|
309 |
+
* @param $list_id
|
310 |
+
* @param int $count
|
311 |
+
* @return array|bool
|
312 |
+
*/
|
313 |
+
public function mergeFields($list_id, $count = 10)
|
314 |
+
{
|
315 |
+
$result = $this->get("lists/$list_id/merge-fields", array('count' => $count,));
|
316 |
+
|
317 |
+
return $result;
|
318 |
+
}
|
319 |
+
|
320 |
+
/**
|
321 |
+
* @param $list_id
|
322 |
+
* @return array|bool
|
323 |
+
*/
|
324 |
+
public function getInterestGroups($list_id)
|
325 |
+
{
|
326 |
+
if (empty($list_id)) {
|
327 |
+
return array();
|
328 |
+
}
|
329 |
+
$result = $this->get("lists/$list_id/interest-categories");
|
330 |
+
|
331 |
+
return $result;
|
332 |
+
}
|
333 |
+
|
334 |
+
/**
|
335 |
+
* @param $list_id
|
336 |
+
* @param $group_id
|
337 |
+
* @return array|bool
|
338 |
+
*/
|
339 |
+
public function getInterestGroupOptions($list_id, $group_id)
|
340 |
+
{
|
341 |
+
if (empty($list_id) || empty($group_id)) {
|
342 |
+
return array();
|
343 |
+
}
|
344 |
+
$result = $this->get("lists/$list_id/interest-categories/$group_id/interests");
|
345 |
+
|
346 |
+
return $result;
|
347 |
+
}
|
348 |
+
|
349 |
+
/**
|
350 |
+
* @param $store_id
|
351 |
+
* @param int $page
|
352 |
+
* @param int $count
|
353 |
+
* @param DateTime|null $since
|
354 |
+
* @param null $campaign_id
|
355 |
+
* @return array|bool
|
356 |
+
*/
|
357 |
+
public function orders($store_id, $page = 1, $count = 10, \DateTime $since = null, $campaign_id = null)
|
358 |
+
{
|
359 |
+
$result = $this->get('ecommerce/stores/'.$store_id.'/orders', array(
|
360 |
+
'start' => $page,
|
361 |
+
'count' => $count,
|
362 |
+
'offset' => ($page * $count),
|
363 |
+
'since' => $since ? $since->format('Y-m-d H:i:s') : null,
|
364 |
+
'cid' => $campaign_id,
|
365 |
+
));
|
366 |
+
|
367 |
+
return $result;
|
368 |
+
}
|
369 |
+
|
370 |
+
/**
|
371 |
+
* @param $store_id
|
372 |
+
* @return MailChimp_Store|bool
|
373 |
+
*/
|
374 |
+
public function getStore($store_id)
|
375 |
+
{
|
376 |
+
try {
|
377 |
+
$data = $this->get("ecommerce/stores/$store_id");
|
378 |
+
if (!isset($data['id']) || !isset($data['name'])) {
|
379 |
+
return false;
|
380 |
+
}
|
381 |
+
return (new MailChimp_Store)->fromArray($data);
|
382 |
+
} catch (MailChimp_Error $e) {
|
383 |
+
return false;
|
384 |
+
}
|
385 |
+
}
|
386 |
+
|
387 |
+
/**
|
388 |
+
* @return array|bool
|
389 |
+
*/
|
390 |
+
public function stores()
|
391 |
+
{
|
392 |
+
try {
|
393 |
+
$data = $this->get("ecommerce/stores");
|
394 |
+
|
395 |
+
if (!isset($data['stores']) || empty($data['stores'])) {
|
396 |
+
return array();
|
397 |
+
}
|
398 |
+
|
399 |
+
$response = array();
|
400 |
+
|
401 |
+
foreach ($data['stores'] as $store_data) {
|
402 |
+
$response[] = (new MailChimp_Store)->fromArray($store_data);
|
403 |
+
}
|
404 |
+
|
405 |
+
return $response;
|
406 |
+
} catch (MailChimp_Error $e) {
|
407 |
+
return false;
|
408 |
+
}
|
409 |
+
}
|
410 |
+
|
411 |
+
/**
|
412 |
+
* @param MailChimp_Store $store
|
413 |
+
* @param bool $silent
|
414 |
+
* @return bool|MailChimp_Store
|
415 |
+
* @throws Exception
|
416 |
+
*/
|
417 |
+
public function addStore(MailChimp_Store $store, $silent = true)
|
418 |
+
{
|
419 |
+
try {
|
420 |
+
$this->validateStoreSubmission($store);
|
421 |
+
$data = $this->post("ecommerce/stores", $store->toArray());
|
422 |
+
return (new MailChimp_Store)->fromArray($data);
|
423 |
+
} catch (\Exception $e) {
|
424 |
+
if (!$silent) throw $e;
|
425 |
+
return false;
|
426 |
+
}
|
427 |
+
}
|
428 |
+
|
429 |
+
/**
|
430 |
+
* @param MailChimp_Store $store
|
431 |
+
* @param bool $silent
|
432 |
+
* @return bool|MailChimp_Store
|
433 |
+
* @throws Exception
|
434 |
+
*/
|
435 |
+
public function updateStore(MailChimp_Store $store, $silent = true)
|
436 |
+
{
|
437 |
+
try {
|
438 |
+
$this->validateStoreSubmission($store);
|
439 |
+
$data = $this->patch("ecommerce/stores/{$store->getId()}", $store->toArray());
|
440 |
+
return (new MailChimp_Store)->fromArray($data);
|
441 |
+
} catch (\Exception $e) {
|
442 |
+
if (!$silent) throw $e;
|
443 |
+
return false;
|
444 |
+
}
|
445 |
+
}
|
446 |
+
|
447 |
+
/**
|
448 |
+
* @param $store_id
|
449 |
+
* @return bool
|
450 |
+
*/
|
451 |
+
public function deleteStore($store_id)
|
452 |
+
{
|
453 |
+
try {
|
454 |
+
$this->delete("ecommerce/stores/$store_id");
|
455 |
+
return true;
|
456 |
+
} catch (MailChimp_Error $e) {
|
457 |
+
return false;
|
458 |
+
}
|
459 |
+
}
|
460 |
+
|
461 |
+
/**
|
462 |
+
* @param $store_id
|
463 |
+
* @param string $customer_id
|
464 |
+
* @return MailChimp_Customer|bool
|
465 |
+
*/
|
466 |
+
public function getCustomer($store_id, $customer_id)
|
467 |
+
{
|
468 |
+
try {
|
469 |
+
$data = $this->get("ecommerce/stores/$store_id/customers/$customer_id");
|
470 |
+
return (new MailChimp_Customer)->fromArray($data);
|
471 |
+
} catch (MailChimp_Error $e) {
|
472 |
+
return false;
|
473 |
+
}
|
474 |
+
}
|
475 |
+
|
476 |
+
/**
|
477 |
+
* @param MailChimp_Customer $store
|
478 |
+
* @return MailChimp_Customer
|
479 |
+
* @throws MailChimp_Error
|
480 |
+
*/
|
481 |
+
public function addCustomer(MailChimp_Customer $store)
|
482 |
+
{
|
483 |
+
$this->validateStoreSubmission($store);
|
484 |
+
$data = $this->post("ecommerce/stores", $store->toArray());
|
485 |
+
return (new MailChimp_Customer)->fromArray($data);
|
486 |
+
}
|
487 |
+
|
488 |
+
/**
|
489 |
+
* @param $store_id
|
490 |
+
* @param int $page
|
491 |
+
* @param int $count
|
492 |
+
* @return array|bool
|
493 |
+
*/
|
494 |
+
public function carts($store_id, $page = 1, $count = 10)
|
495 |
+
{
|
496 |
+
$result = $this->get('ecommerce/stores/'.$store_id.'/carts', array(
|
497 |
+
'start' => $page,
|
498 |
+
'count' => $count,
|
499 |
+
'offset' => ($page * $count),
|
500 |
+
));
|
501 |
+
|
502 |
+
return $result;
|
503 |
+
}
|
504 |
+
|
505 |
+
/**
|
506 |
+
* @param $store_id
|
507 |
+
* @param MailChimp_Cart $cart
|
508 |
+
* @param bool $silent
|
509 |
+
* @return bool|MailChimp_Cart
|
510 |
+
* @throws MailChimp_Error
|
511 |
+
*/
|
512 |
+
public function addCart($store_id, MailChimp_Cart $cart, $silent = true)
|
513 |
+
{
|
514 |
+
try {
|
515 |
+
$data = $this->post("ecommerce/stores/$store_id/carts", $cart->toArray());
|
516 |
+
return (new MailChimp_Cart)->setStoreID($store_id)->fromArray($data);
|
517 |
+
} catch (MailChimp_Error $e) {
|
518 |
+
if (!$silent) throw $e;
|
519 |
+
mailchimp_log('api.addCart', $e->getMessage());
|
520 |
+
return false;
|
521 |
+
}
|
522 |
+
}
|
523 |
+
|
524 |
+
/**
|
525 |
+
* @param $store_id
|
526 |
+
* @param MailChimp_Cart $cart
|
527 |
+
* @param bool $silent
|
528 |
+
* @return bool|MailChimp_Cart
|
529 |
+
* @throws MailChimp_Error
|
530 |
+
*/
|
531 |
+
public function updateCart($store_id, MailChimp_Cart $cart, $silent = true)
|
532 |
+
{
|
533 |
+
try {
|
534 |
+
$data = $this->patch("ecommerce/stores/$store_id/carts/{$cart->getId()}", $cart->toArrayForUpdate());
|
535 |
+
return (new MailChimp_Cart)->setStoreID($store_id)->fromArray($data);
|
536 |
+
} catch (MailChimp_Error $e) {
|
537 |
+
if (!$silent) throw $e;
|
538 |
+
mailchimp_log('api.updateCart', $e->getMessage());
|
539 |
+
return false;
|
540 |
+
}
|
541 |
+
}
|
542 |
+
|
543 |
+
/**
|
544 |
+
* @param $store_id
|
545 |
+
* @param $id
|
546 |
+
* @return bool|MailChimp_Cart
|
547 |
+
*/
|
548 |
+
public function getCart($store_id, $id)
|
549 |
+
{
|
550 |
+
try {
|
551 |
+
$data = $this->get("ecommerce/stores/$store_id/carts/$id");
|
552 |
+
return (new MailChimp_Cart)->setStoreID($store_id)->fromArray($data);
|
553 |
+
} catch (MailChimp_Error $e) {
|
554 |
+
return false;
|
555 |
+
}
|
556 |
+
}
|
557 |
+
|
558 |
+
/**
|
559 |
+
* @param $store_id
|
560 |
+
* @param $id
|
561 |
+
* @return bool
|
562 |
+
*/
|
563 |
+
public function deleteCartByID($store_id, $id)
|
564 |
+
{
|
565 |
+
try {
|
566 |
+
$this->delete("ecommerce/stores/$store_id/carts/$id");
|
567 |
+
return true;
|
568 |
+
} catch (MailChimp_Error $e) {
|
569 |
+
return false;
|
570 |
+
}
|
571 |
+
}
|
572 |
+
|
573 |
+
/**
|
574 |
+
* @param $store_id
|
575 |
+
* @param MailChimp_Customer $customer
|
576 |
+
* @param bool $silent
|
577 |
+
* @return bool|MailChimp_Customer
|
578 |
+
* @throws MailChimp_Error
|
579 |
+
*/
|
580 |
+
public function updateCustomer($store_id, MailChimp_Customer $customer, $silent = true)
|
581 |
+
{
|
582 |
+
try {
|
583 |
+
$this->validateStoreSubmission($customer);
|
584 |
+
$data = $this->patch("ecommerce/stores/$store_id/customers/{$customer->getId()}", $customer->toArray());
|
585 |
+
return (new MailChimp_Customer)->fromArray($data);
|
586 |
+
} catch (MailChimp_Error $e) {
|
587 |
+
if (!$silent) throw $e;
|
588 |
+
return false;
|
589 |
+
}
|
590 |
+
}
|
591 |
+
|
592 |
+
/**
|
593 |
+
* @param $store_id
|
594 |
+
* @param $customer_id
|
595 |
+
* @return bool
|
596 |
+
*/
|
597 |
+
public function deleteCustomer($store_id, $customer_id)
|
598 |
+
{
|
599 |
+
try {
|
600 |
+
$this->delete("ecommerce/stores/$store_id/customers/$customer_id");
|
601 |
+
return true;
|
602 |
+
} catch (MailChimp_Error $e) {
|
603 |
+
return false;
|
604 |
+
}
|
605 |
+
}
|
606 |
+
|
607 |
+
/**
|
608 |
+
* @param $store_id
|
609 |
+
* @param MailChimp_Order $order
|
610 |
+
* @param bool $silent
|
611 |
+
* @return bool|MailChimp_Order
|
612 |
+
* @throws Exception
|
613 |
+
*/
|
614 |
+
public function addStoreOrder($store_id, MailChimp_Order $order, $silent = true)
|
615 |
+
{
|
616 |
+
try {
|
617 |
+
$this->validateStoreSubmission($order);
|
618 |
+
$data = $this->post("ecommerce/stores/$store_id/orders", $order->toArray());
|
619 |
+
return (new MailChimp_Order)->fromArray($data);
|
620 |
+
} catch (\Exception $e) {
|
621 |
+
if (!$silent) throw $e;
|
622 |
+
mailchimp_log('api.add_order.error', $e->getMessage(), array('submission' => $order->toArray()));
|
623 |
+
return false;
|
624 |
+
}
|
625 |
+
}
|
626 |
+
|
627 |
+
/**
|
628 |
+
* @param $store_id
|
629 |
+
* @param MailChimp_Order $order
|
630 |
+
* @param bool $silent
|
631 |
+
* @return bool|MailChimp_Order
|
632 |
+
* @throws Exception
|
633 |
+
*/
|
634 |
+
public function updateStoreOrder($store_id, MailChimp_Order $order, $silent = true)
|
635 |
+
{
|
636 |
+
try {
|
637 |
+
$this->validateStoreSubmission($order);
|
638 |
+
$id = $order->getId();
|
639 |
+
$data = $this->patch("ecommerce/stores/$store_id/orders/$id", $order->toArray());
|
640 |
+
return (new MailChimp_Order)->fromArray($data);
|
641 |
+
} catch (\Exception $e) {
|
642 |
+
if (!$silent) throw $e;
|
643 |
+
mailchimp_log('api.update_order.error', $e->getMessage(), array('submission' => $order->toArray()));
|
644 |
+
return false;
|
645 |
+
}
|
646 |
+
}
|
647 |
+
|
648 |
+
/**
|
649 |
+
* @param $store_id
|
650 |
+
* @param $order_id
|
651 |
+
* @return MailChimp_Order|bool
|
652 |
+
*/
|
653 |
+
public function getStoreOrder($store_id, $order_id)
|
654 |
+
{
|
655 |
+
try {
|
656 |
+
$data = $this->get("ecommerce/stores/$store_id/orders/$order_id");
|
657 |
+
return (new MailChimp_Order)->fromArray($data);
|
658 |
+
} catch (MailChimp_Error $e) {
|
659 |
+
return false;
|
660 |
+
}
|
661 |
+
}
|
662 |
+
|
663 |
+
/**
|
664 |
+
* @param $store_id
|
665 |
+
* @param $order_id
|
666 |
+
* @return bool
|
667 |
+
*/
|
668 |
+
public function deleteStoreOrder($store_id, $order_id)
|
669 |
+
{
|
670 |
+
try {
|
671 |
+
$this->delete("ecommerce/stores/$store_id/orders/$order_id");
|
672 |
+
return true;
|
673 |
+
} catch (MailChimp_Error $e) {
|
674 |
+
return false;
|
675 |
+
}
|
676 |
+
}
|
677 |
+
|
678 |
+
/**
|
679 |
+
* @param $store_id
|
680 |
+
* @param $product_id
|
681 |
+
* @return MailChimp_Product|bool
|
682 |
+
*/
|
683 |
+
public function getStoreProduct($store_id, $product_id)
|
684 |
+
{
|
685 |
+
try {
|
686 |
+
$data = $this->get("ecommerce/stores/$store_id/products/$product_id");
|
687 |
+
return (new MailChimp_Product)->fromArray($data);
|
688 |
+
} catch (MailChimp_Error $e) {
|
689 |
+
return false;
|
690 |
+
}
|
691 |
+
}
|
692 |
+
|
693 |
+
/**
|
694 |
+
* @param $store_id
|
695 |
+
* @param int $page
|
696 |
+
* @param int $count
|
697 |
+
* @return array|bool
|
698 |
+
*/
|
699 |
+
public function products($store_id, $page = 1, $count = 10)
|
700 |
+
{
|
701 |
+
$result = $this->get('ecommerce/stores/'.$store_id.'/products', array(
|
702 |
+
'start' => $page,
|
703 |
+
'count' => $count,
|
704 |
+
'offset' => ($page * $count),
|
705 |
+
));
|
706 |
+
|
707 |
+
return $result;
|
708 |
+
}
|
709 |
+
|
710 |
+
/**
|
711 |
+
* @param $store_id
|
712 |
+
* @param MailChimp_Product $product
|
713 |
+
* @param bool $silent
|
714 |
+
* @return bool|MailChimp_Product
|
715 |
+
* @throws Exception
|
716 |
+
*/
|
717 |
+
public function addStoreProduct($store_id, MailChimp_Product $product, $silent = true)
|
718 |
+
{
|
719 |
+
try {
|
720 |
+
$this->validateStoreSubmission($product);
|
721 |
+
$data = $this->post("ecommerce/stores/$store_id/products", $product->toArray());
|
722 |
+
return (new MailChimp_Product)->fromArray($data);
|
723 |
+
} catch (\Exception $e) {
|
724 |
+
if (!$silent) throw $e;
|
725 |
+
mailchimp_log('api.add_product.error', $e->getMessage(), array('submission' => $product->toArray()));
|
726 |
+
return false;
|
727 |
+
}
|
728 |
+
}
|
729 |
+
|
730 |
+
/**
|
731 |
+
* @param $store_id
|
732 |
+
* @param $product_id
|
733 |
+
* @return bool
|
734 |
+
*/
|
735 |
+
public function deleteStoreProduct($store_id, $product_id)
|
736 |
+
{
|
737 |
+
try {
|
738 |
+
$this->delete("ecommerce/stores/$store_id/products/$product_id");
|
739 |
+
return true;
|
740 |
+
} catch (MailChimp_Error $e) {
|
741 |
+
return false;
|
742 |
+
}
|
743 |
+
}
|
744 |
+
|
745 |
+
/**
|
746 |
+
* @param MailChimp_Store|MailChimp_Order|MailChimp_Product|MailChimp_Customer $target
|
747 |
+
* @return bool
|
748 |
+
* @throws MailChimp_Error
|
749 |
+
*/
|
750 |
+
protected function validateStoreSubmission($target)
|
751 |
+
{
|
752 |
+
return true;
|
753 |
+
}
|
754 |
+
|
755 |
+
/**
|
756 |
+
* @param $url
|
757 |
+
* @param null $params
|
758 |
+
* @return array|bool
|
759 |
+
* @throws MailChimp_Error
|
760 |
+
*/
|
761 |
+
protected function delete($url, $params = null)
|
762 |
+
{
|
763 |
+
$curl = curl_init();
|
764 |
+
|
765 |
+
$options = $this->applyCurlOptions('DELETE', $url, $params);
|
766 |
+
|
767 |
+
curl_setopt_array($curl, $options);
|
768 |
+
|
769 |
+
return $this->processCurlResponse($curl);
|
770 |
+
}
|
771 |
+
|
772 |
+
/**
|
773 |
+
* @param $url
|
774 |
+
* @param null $params
|
775 |
+
* @return array|bool
|
776 |
+
* @throws MailChimp_Error
|
777 |
+
*/
|
778 |
+
protected function get($url, $params = null)
|
779 |
+
{
|
780 |
+
$curl = curl_init();
|
781 |
+
|
782 |
+
$options = $this->applyCurlOptions('GET', $url, $params);
|
783 |
+
|
784 |
+
curl_setopt_array($curl, $options);
|
785 |
+
|
786 |
+
return $this->processCurlResponse($curl);
|
787 |
+
}
|
788 |
+
|
789 |
+
/**
|
790 |
+
* @param $url
|
791 |
+
* @param $body
|
792 |
+
* @return array|mixed|null|object
|
793 |
+
* @throws Exception
|
794 |
+
* @throws MailChimp_Error
|
795 |
+
*/
|
796 |
+
protected function patch($url, $body)
|
797 |
+
{
|
798 |
+
try {
|
799 |
+
// process the patch request the normal way
|
800 |
+
$curl = curl_init();
|
801 |
+
|
802 |
+
$options = $this->applyCurlOptions('PATCH', $url, array());
|
803 |
+
$options[CURLOPT_POSTFIELDS] = json_encode($body);
|
804 |
+
|
805 |
+
curl_setopt_array($curl, $options);
|
806 |
+
|
807 |
+
return $this->processCurlResponse($curl);
|
808 |
+
|
809 |
+
} catch (\Exception $e) {
|
810 |
+
|
811 |
+
// if the error that we get is not the json parsing error, throw it.
|
812 |
+
if (strpos(strtolower($e->getMessage()), 'json parsing error') === false) {
|
813 |
+
throw $e;
|
814 |
+
}
|
815 |
+
|
816 |
+
// ah snap, gotta try the file get contents fallback.
|
817 |
+
mailchimp_log('api.patch.fallback', 'stream', array('curl_version' => curl_version()));
|
818 |
+
|
819 |
+
$context = stream_context_create([
|
820 |
+
'http' => [
|
821 |
+
'method' => 'PATCH',
|
822 |
+
'header' => [
|
823 |
+
'Authorization: Basic '.base64_encode('mailchimp:'.$this->api_key),
|
824 |
+
'Accept: application/json',
|
825 |
+
'Content-Type: application/json'
|
826 |
+
],
|
827 |
+
'content' => json_encode($body)
|
828 |
+
]
|
829 |
+
]);
|
830 |
+
|
831 |
+
$response = file_get_contents($this->url($url), FALSE, $context);
|
832 |
+
|
833 |
+
if ($response === false) {
|
834 |
+
throw new MailChimp_Error('Invalid patch request');
|
835 |
+
}
|
836 |
+
|
837 |
+
return json_decode($response, true);
|
838 |
+
}
|
839 |
+
}
|
840 |
+
|
841 |
+
/**
|
842 |
+
* @param $url
|
843 |
+
* @param $body
|
844 |
+
* @return array|bool
|
845 |
+
* @throws MailChimp_Error
|
846 |
+
*/
|
847 |
+
protected function post($url, $body)
|
848 |
+
{
|
849 |
+
$curl = curl_init();
|
850 |
+
|
851 |
+
$options = $this->applyCurlOptions('POST', $url, array());
|
852 |
+
$options[CURLOPT_POSTFIELDS] = json_encode($body);
|
853 |
+
|
854 |
+
curl_setopt_array($curl, $options);
|
855 |
+
|
856 |
+
return $this->processCurlResponse($curl);
|
857 |
+
}
|
858 |
+
|
859 |
+
/**
|
860 |
+
* @param $url
|
861 |
+
* @param $body
|
862 |
+
* @return array|bool
|
863 |
+
* @throws MailChimp_Error
|
864 |
+
*/
|
865 |
+
protected function put($url, $body)
|
866 |
+
{
|
867 |
+
$curl = curl_init();
|
868 |
+
|
869 |
+
$options = $this->applyCurlOptions('PUT', $url, array());
|
870 |
+
$options[CURLOPT_POSTFIELDS] = json_encode($body);
|
871 |
+
|
872 |
+
curl_setopt_array($curl, $options);
|
873 |
+
|
874 |
+
return $this->processCurlResponse($curl);
|
875 |
+
}
|
876 |
+
|
877 |
+
/**
|
878 |
+
* @param string $extra
|
879 |
+
* @param null|array $params
|
880 |
+
* @return string
|
881 |
+
*/
|
882 |
+
protected function url($extra = '', $params = null)
|
883 |
+
{
|
884 |
+
$url = "https://{$this->data_center}.api.mailchimp.com/{$this->version}/";
|
885 |
+
|
886 |
+
if (!empty($extra)) {
|
887 |
+
$url .= $extra;
|
888 |
+
}
|
889 |
+
|
890 |
+
if (!empty($params)) {
|
891 |
+
$url .= '?'.(is_array($params) ? http_build_query($params) : $params);
|
892 |
+
}
|
893 |
+
|
894 |
+
return $url;
|
895 |
+
}
|
896 |
+
|
897 |
+
/**
|
898 |
+
* @param $method
|
899 |
+
* @param $url
|
900 |
+
* @param $body
|
901 |
+
* @return array|WP_Error
|
902 |
+
*/
|
903 |
+
protected function sendWithHttpClient($method, $url, $body)
|
904 |
+
{
|
905 |
+
return _wp_http_get_object()->request($this->url($url), array(
|
906 |
+
'method' => strtoupper($method),
|
907 |
+
'headers' => array(
|
908 |
+
'Authorization' => 'Basic ' . base64_encode('mailchimp:'.$this->api_key),
|
909 |
+
'Content-Type' => 'application/json',
|
910 |
+
),
|
911 |
+
'body' => json_encode($body),
|
912 |
+
));
|
913 |
+
}
|
914 |
+
|
915 |
+
/**
|
916 |
+
* @param $method
|
917 |
+
* @param $url
|
918 |
+
* @param array $params
|
919 |
+
* @return array
|
920 |
+
*/
|
921 |
+
protected function applyCurlOptions($method, $url, $params = array())
|
922 |
+
{
|
923 |
+
return array(
|
924 |
+
CURLOPT_USERPWD => "mailchimp:{$this->api_key}",
|
925 |
+
CURLOPT_CUSTOMREQUEST => strtoupper($method),
|
926 |
+
CURLOPT_URL => $this->url($url, $params),
|
927 |
+
CURLOPT_RETURNTRANSFER => true,
|
928 |
+
CURLOPT_ENCODING => "",
|
929 |
+
CURLOPT_MAXREDIRS => 10,
|
930 |
+
CURLOPT_TIMEOUT => 30,
|
931 |
+
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
|
932 |
+
CURLINFO_HEADER_OUT => true,
|
933 |
+
CURLOPT_HTTPHEADER => ['content-type: application/json']
|
934 |
+
);
|
935 |
+
}
|
936 |
+
|
937 |
+
/**
|
938 |
+
* @param $curl
|
939 |
+
* @return array|mixed|null|object
|
940 |
+
* @throws Exception
|
941 |
+
* @throws MailChimp_Error
|
942 |
+
* @throws MailChimp_ServerError
|
943 |
+
*/
|
944 |
+
protected function processCurlResponse($curl)
|
945 |
+
{
|
946 |
+
$response = curl_exec($curl);
|
947 |
+
|
948 |
+
$err = curl_error($curl);
|
949 |
+
$info = curl_getinfo($curl);
|
950 |
+
curl_close($curl);
|
951 |
+
|
952 |
+
if ($err) {
|
953 |
+
throw new MailChimp_Error('CURL error :: '.$err, '500');
|
954 |
+
}
|
955 |
+
|
956 |
+
$data = json_decode($response, true);
|
957 |
+
|
958 |
+
if (empty($info) || ($info['http_code'] >= 200 && $info['http_code'] <= 400)) {
|
959 |
+
if (is_array($data)) {
|
960 |
+
try {
|
961 |
+
$this->checkForErrors($data);
|
962 |
+
} catch (\Exception $e) {
|
963 |
+
throw $e;
|
964 |
+
}
|
965 |
+
}
|
966 |
+
return $data;
|
967 |
+
}
|
968 |
+
|
969 |
+
if ($info['http_code'] >= 400 && $info['http_code'] <= 500) {
|
970 |
+
throw new MailChimp_Error($data['title'] .' :: '.$data['detail'], $data['status']);
|
971 |
+
}
|
972 |
+
|
973 |
+
if ($info['http_code'] >= 500) {
|
974 |
+
throw new MailChimp_ServerError($data['detail'], $data['status']);
|
975 |
+
}
|
976 |
+
|
977 |
+
return null;
|
978 |
+
}
|
979 |
+
|
980 |
+
/**
|
981 |
+
* @param array $data
|
982 |
+
* @return bool
|
983 |
+
* @throws MailChimp_Error
|
984 |
+
*/
|
985 |
+
protected function checkForErrors(array $data)
|
986 |
+
{
|
987 |
+
// if we have an array of error data push it into a message
|
988 |
+
if (isset($data['errors'])) {
|
989 |
+
$message = '';
|
990 |
+
foreach ($data['errors'] as $error) {
|
991 |
+
$message .= '<p>'.$error['field'].': '.$error['message'].'</p>';
|
992 |
+
}
|
993 |
+
throw new MailChimp_Error($message, $data['status']);
|
994 |
+
}
|
995 |
+
|
996 |
+
// make sure the response is correct from the data in the response array
|
997 |
+
if (isset($data['status']) && $data['status'] >= 400) {
|
998 |
+
throw new MailChimp_Error($data['detail'], $data['status']);
|
999 |
+
}
|
1000 |
+
|
1001 |
+
return false;
|
1002 |
+
}
|
1003 |
+
}
|
includes/api/class-mailchimp-woocommerce-api.php
ADDED
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 7/13/16
|
9 |
+
* Time: 2:32 PM
|
10 |
+
*/
|
11 |
+
class MailChimp_WooCommerce_Api
|
12 |
+
{
|
13 |
+
protected static $filterable_actions = array(
|
14 |
+
'paginate-resource',
|
15 |
+
);
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @param int $default_page
|
19 |
+
* @param int $default_per
|
20 |
+
* @return array
|
21 |
+
*/
|
22 |
+
public static function filter($default_page = null, $default_per = null)
|
23 |
+
{
|
24 |
+
if (isset($_GET['mailchimp-woocommerce']) && isset($_GET['mailchimp-woocommerce']['action'])) {
|
25 |
+
if (in_array($_GET['mailchimp-woocommerce']['action'], static::$filterable_actions)) {
|
26 |
+
if (empty($default_page)) {
|
27 |
+
$page = isset($_GET['page']) ? (int) $_GET['page'] : null;
|
28 |
+
}
|
29 |
+
if (empty($default_per)) {
|
30 |
+
$per = isset($_GET['per']) ? (int) $_GET['per'] : null;
|
31 |
+
}
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
if (empty($page)) $page = 1;
|
36 |
+
if (empty($per)) $per = 10;
|
37 |
+
|
38 |
+
return array($page, $per);
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* @param null $page
|
43 |
+
* @param null $per
|
44 |
+
* @return object|stdClass
|
45 |
+
*/
|
46 |
+
public function paginateProducts($page = null, $per = null)
|
47 |
+
{
|
48 |
+
return $this->paginate('products', $page, $per);
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @param null $page
|
53 |
+
* @param null $per
|
54 |
+
* @return object|stdClass
|
55 |
+
*/
|
56 |
+
public function paginateOrders($page = null, $per = null)
|
57 |
+
{
|
58 |
+
return $this->paginate('orders', $page, $per);
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* @param $resource
|
63 |
+
* @param int $page
|
64 |
+
* @param int $per
|
65 |
+
* @return object|stdClass
|
66 |
+
*/
|
67 |
+
public function paginate($resource, $page = 1, $per = 10)
|
68 |
+
{
|
69 |
+
if (($sync = $this->engine($resource))) {
|
70 |
+
return $sync->compile($page, $per);
|
71 |
+
}
|
72 |
+
|
73 |
+
return (object) array(
|
74 |
+
'endpoint' => $resource,
|
75 |
+
'page' => $page,
|
76 |
+
'count' => 0,
|
77 |
+
'stuffed' => false,
|
78 |
+
'items' => array(),
|
79 |
+
);
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* @param $resource
|
84 |
+
* @return bool|MailChimp_WooCommerce_Transform_Orders|MailChimp_WooCommerce_Transform_Products
|
85 |
+
*/
|
86 |
+
public function engine($resource)
|
87 |
+
{
|
88 |
+
switch ($resource) {
|
89 |
+
case 'products' :
|
90 |
+
return new MailChimp_WooCommerce_Transform_Products();
|
91 |
+
break;
|
92 |
+
case 'orders' :
|
93 |
+
return new MailChimp_WooCommerce_Transform_Orders();
|
94 |
+
break;
|
95 |
+
|
96 |
+
default:
|
97 |
+
return false;
|
98 |
+
}
|
99 |
+
}
|
100 |
+
}
|
includes/api/class-mailchimp-woocommerce-create-list-submission.php
ADDED
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 7/8/16
|
9 |
+
* Time: 4:16 PM
|
10 |
+
*/
|
11 |
+
class MailChimp_CreateListSubmission
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* @var array
|
15 |
+
*/
|
16 |
+
protected $props = array();
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @param $name
|
20 |
+
* @return $this
|
21 |
+
*/
|
22 |
+
public function setName($name)
|
23 |
+
{
|
24 |
+
$this->props['name'] = $name;
|
25 |
+
|
26 |
+
return $this;
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @param $bool
|
31 |
+
* @return $this
|
32 |
+
*/
|
33 |
+
public function setUseArchiveBar($bool)
|
34 |
+
{
|
35 |
+
$this->props['use_archive_bar'] = (bool) $bool;
|
36 |
+
|
37 |
+
return $this;
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @param $reminder
|
42 |
+
* @return $this
|
43 |
+
*/
|
44 |
+
public function setPermissionReminder($reminder)
|
45 |
+
{
|
46 |
+
$this->props['permission_reminder'] = $reminder;
|
47 |
+
|
48 |
+
return $this;
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @param $email
|
53 |
+
* @return $this
|
54 |
+
*/
|
55 |
+
public function setNotifyOnSubscribe($email)
|
56 |
+
{
|
57 |
+
$this->props['notify_on_subscribe'] = $email;
|
58 |
+
|
59 |
+
return $this;
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* @param string $email
|
64 |
+
* @return $this
|
65 |
+
*/
|
66 |
+
public function setNotifyOnUnSubscribe($email)
|
67 |
+
{
|
68 |
+
$this->props['notify_on_unsubscribe'] = $email;
|
69 |
+
|
70 |
+
return $this;
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* @param $bool
|
75 |
+
* @return $this
|
76 |
+
*/
|
77 |
+
public function setEmailTypeOption($bool)
|
78 |
+
{
|
79 |
+
$this->props['email_type_option'] = (bool) $bool;
|
80 |
+
|
81 |
+
return $this;
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* @param bool $public
|
86 |
+
* @return $this
|
87 |
+
*/
|
88 |
+
public function setVisibility($public = true)
|
89 |
+
{
|
90 |
+
$this->props['visibility'] = $public ? 'pub' : 'prv';
|
91 |
+
|
92 |
+
return $this;
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* @param $name
|
97 |
+
* @param $email
|
98 |
+
* @param $subject
|
99 |
+
* @param string $language
|
100 |
+
* @return $this
|
101 |
+
*/
|
102 |
+
public function setCampaignDefaults($name, $email, $subject, $language = 'en')
|
103 |
+
{
|
104 |
+
$this->props['campaign_defaults'] = array(
|
105 |
+
'from_name' => $name,
|
106 |
+
'from_email' => $email,
|
107 |
+
'subject' => $subject,
|
108 |
+
'language' => $language,
|
109 |
+
);
|
110 |
+
|
111 |
+
return $this;
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* @param MailChimp_Address $address
|
116 |
+
* @return $this
|
117 |
+
*/
|
118 |
+
public function setContact(MailChimp_Address $address)
|
119 |
+
{
|
120 |
+
$data = array();
|
121 |
+
|
122 |
+
if (($company = $address->getCompany()) && !empty($company)) {
|
123 |
+
$data['company'] = $company;
|
124 |
+
}
|
125 |
+
|
126 |
+
if (($street = $address->getAddress1()) && !empty($address)) {
|
127 |
+
$data['address1'] = $street;
|
128 |
+
}
|
129 |
+
|
130 |
+
if (($city = $address->getCity()) && !empty($city)) {
|
131 |
+
$data['city'] = $city;
|
132 |
+
}
|
133 |
+
|
134 |
+
if (($state = $address->getProvince()) && !empty($state)) {
|
135 |
+
$data['state'] = $state;
|
136 |
+
}
|
137 |
+
|
138 |
+
if (($zip = $address->getPostalCode()) && !empty($zip)) {
|
139 |
+
$data['zip'] = $zip;
|
140 |
+
}
|
141 |
+
|
142 |
+
if (($country = $address->getCountry()) && !empty($country)) {
|
143 |
+
$data['country'] = $country;
|
144 |
+
}
|
145 |
+
|
146 |
+
if (($phone = $address->getPhone()) && !empty($phone)) {
|
147 |
+
$data['phone'] = $phone;
|
148 |
+
}
|
149 |
+
|
150 |
+
$this->props['contact'] = $data;
|
151 |
+
|
152 |
+
return $this;
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* @return array
|
157 |
+
*/
|
158 |
+
public function getSubmission()
|
159 |
+
{
|
160 |
+
return $this->props;
|
161 |
+
}
|
162 |
+
}
|
includes/api/class-mailchimp-woocommerce-transform-orders.php
ADDED
@@ -0,0 +1,398 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 7/13/16
|
9 |
+
* Time: 8:29 AM
|
10 |
+
*/
|
11 |
+
class MailChimp_WooCommerce_Transform_Orders
|
12 |
+
{
|
13 |
+
public $campaign_id = null;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @param int $page
|
17 |
+
* @param int $limit
|
18 |
+
* @return \stdClass
|
19 |
+
*/
|
20 |
+
public function compile($page = 1, $limit = 10)
|
21 |
+
{
|
22 |
+
$response = (object) array(
|
23 |
+
'endpoint' => 'orders',
|
24 |
+
'page' => $page,
|
25 |
+
'limit' => (int) $limit,
|
26 |
+
'count' => 0,
|
27 |
+
'valid' => 0,
|
28 |
+
'drafts' => 0,
|
29 |
+
'stuffed' => false,
|
30 |
+
'items' => array(),
|
31 |
+
);
|
32 |
+
|
33 |
+
if ((($orders = $this->getOrderPosts($page, $limit)) && !empty($orders))) {
|
34 |
+
foreach ($orders as $post) {
|
35 |
+
$response->count++;
|
36 |
+
if ($post->post_status === 'auto-draft') {
|
37 |
+
$response->drafts++;
|
38 |
+
continue;
|
39 |
+
}
|
40 |
+
|
41 |
+
$response->valid++;
|
42 |
+
$response->items[] = $this->transform($post);
|
43 |
+
}
|
44 |
+
}
|
45 |
+
|
46 |
+
$response->stuffed = ($response->count > 0 && (int) $response->count === (int) $limit) ? true : false;
|
47 |
+
|
48 |
+
return $response;
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* @param WP_Post $post
|
53 |
+
* @return MailChimp_Order
|
54 |
+
*/
|
55 |
+
public function transform(WP_Post $post)
|
56 |
+
{
|
57 |
+
$woo = new WC_Order($post);
|
58 |
+
|
59 |
+
$order = new MailChimp_Order();
|
60 |
+
|
61 |
+
$order->setId($woo->id);
|
62 |
+
|
63 |
+
// if we have a campaign id let's set it now.
|
64 |
+
if (!empty($this->campaign_id)) {
|
65 |
+
$order->setCampaignId($this->campaign_id);
|
66 |
+
}
|
67 |
+
|
68 |
+
$order->setFulfillmentStatus($woo->get_status());
|
69 |
+
$order->setProcessedAt(mailchimp_date_utc($woo->order_date));
|
70 |
+
|
71 |
+
if ($woo->get_status() === 'cancelled') {
|
72 |
+
$order->setCancelledAt(mailchimp_date_utc($woo->modified_date));
|
73 |
+
}
|
74 |
+
|
75 |
+
$order->setCurrencyCode($woo->get_order_currency());
|
76 |
+
$order->setFinancialStatus($woo->is_paid() ? 'paid' : 'pending');
|
77 |
+
|
78 |
+
$order->setOrderTotal($woo->get_total());
|
79 |
+
|
80 |
+
// if we have any tax
|
81 |
+
$order->setTaxTotal($woo->get_total_tax());
|
82 |
+
|
83 |
+
// if we have shipping.
|
84 |
+
$order->setShippingTotal($woo->get_total_shipping());
|
85 |
+
|
86 |
+
// set the customer
|
87 |
+
$order->setCustomer($this->buildCustomerFromOrder($woo));
|
88 |
+
|
89 |
+
// apply the addresses to the order
|
90 |
+
$addresses = $this->getOrderAddresses($woo);
|
91 |
+
$order->setShippingAddress($addresses->shipping);
|
92 |
+
$order->setBillingAddress($addresses->billing);
|
93 |
+
|
94 |
+
// loop through all the order items
|
95 |
+
foreach ($woo->get_items() as $key => $order_detail) {
|
96 |
+
|
97 |
+
// add it into the order item container.
|
98 |
+
$item = $this->buildLineItem($key, $order_detail);
|
99 |
+
|
100 |
+
// if we don't have a product post with this id, we need to add a deleted product to the MC side
|
101 |
+
if (!($product_post = get_post($item->getProductId()))) {
|
102 |
+
|
103 |
+
// check if it exists, otherwise create a new one.
|
104 |
+
if (($deleted_product = MailChimp_WooCommerce_Transform_Products::deleted($item->getProductId()))) {
|
105 |
+
|
106 |
+
$deleted_product_id = "deleted_{$item->getProductId()}";
|
107 |
+
|
108 |
+
// swap out the old item id and product variant id with the deleted version.
|
109 |
+
$item->setProductId($deleted_product_id);
|
110 |
+
$item->setProductVariantId($deleted_product_id);
|
111 |
+
|
112 |
+
// add the item and continue on the loop.
|
113 |
+
$order->addItem($item);
|
114 |
+
continue;
|
115 |
+
}
|
116 |
+
|
117 |
+
mailchimp_log('order.items.error', "Order #{$woo->id} :: Product {$item->getProductId()} does not exist!");
|
118 |
+
continue;
|
119 |
+
}
|
120 |
+
|
121 |
+
$order->addItem($item);
|
122 |
+
}
|
123 |
+
|
124 |
+
return $order;
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* @param WC_Order $order
|
129 |
+
* @return MailChimp_Customer
|
130 |
+
*/
|
131 |
+
public function buildCustomerFromOrder(WC_Order $order)
|
132 |
+
{
|
133 |
+
$customer = new MailChimp_Customer();
|
134 |
+
|
135 |
+
$customer->setId(md5(trim(strtolower($order->billing_email))));
|
136 |
+
$customer->setCompany($order->billing_company);
|
137 |
+
$customer->setEmailAddress(trim($order->billing_email));
|
138 |
+
$customer->setFirstName($order->billing_first_name);
|
139 |
+
$customer->setLastName($order->billing_last_name);
|
140 |
+
$customer->setOrdersCount(1);
|
141 |
+
$customer->setTotalSpent($order->get_total());
|
142 |
+
|
143 |
+
// we are saving the post meta for subscribers on each order... so if they have subscribed on checkout
|
144 |
+
$subscriber_meta = get_post_meta($order->id, 'mailchimp_woocommerce_is_subscribed', true);
|
145 |
+
$subscribed_on_order = $subscriber_meta === '' ? false : (bool) $subscriber_meta;
|
146 |
+
|
147 |
+
$customer->setOptInStatus($subscribed_on_order);
|
148 |
+
|
149 |
+
// use the info from the order to compile an address.
|
150 |
+
$address = new MailChimp_Address();
|
151 |
+
$address->setAddress1($order->billing_address_1);
|
152 |
+
$address->setAddress2($order->billing_address_2);
|
153 |
+
$address->setCity($order->billing_city);
|
154 |
+
$address->setProvince($order->billing_state);
|
155 |
+
$address->setPostalCode($order->billing_postcode);
|
156 |
+
$address->setCountry($order->billing_country);
|
157 |
+
$address->setPhone($order->billing_phone);
|
158 |
+
$address->setName('billing');
|
159 |
+
|
160 |
+
$customer->setAddress($address);
|
161 |
+
|
162 |
+
if (($user = get_userdata($order->customer_user))) {
|
163 |
+
|
164 |
+
/** IF we wanted to use the user data instead we would do it here.
|
165 |
+
* but we discussed using the billing address instead.
|
166 |
+
*/
|
167 |
+
|
168 |
+
/*
|
169 |
+
$customer->setId($user->ID);
|
170 |
+
$customer->setEmailAddress($user->user_email);
|
171 |
+
$customer->setFirstName($user->first_name);
|
172 |
+
$customer->setLastName($user->last_name);
|
173 |
+
|
174 |
+
if (($address = $this->getUserAddress($user->ID))) {
|
175 |
+
if (count($address->toArray()) > 3) {
|
176 |
+
$customer->setAddress($address);
|
177 |
+
}
|
178 |
+
}
|
179 |
+
*/
|
180 |
+
|
181 |
+
if (!($stats = $this->getCustomerOrderTotals($order->customer_user))) {
|
182 |
+
$stats = (object) array('count' => 0, 'total' => 0);
|
183 |
+
}
|
184 |
+
|
185 |
+
$customer->setOrdersCount($stats->count);
|
186 |
+
$customer->setTotalSpent($stats->total);
|
187 |
+
}
|
188 |
+
|
189 |
+
return $customer;
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* @param $key
|
194 |
+
* @param $order_detail
|
195 |
+
* @return MailChimp_LineItem
|
196 |
+
*/
|
197 |
+
protected function buildLineItem($key, $order_detail)
|
198 |
+
{
|
199 |
+
// fire up a new MC line item
|
200 |
+
$item = new MailChimp_LineItem();
|
201 |
+
$item->setId($key);
|
202 |
+
|
203 |
+
if (isset($order_detail['item_meta']) && is_array($order_detail['item_meta'])) {
|
204 |
+
|
205 |
+
foreach ($order_detail['item_meta'] as $meta_key => $meta_data_array) {
|
206 |
+
|
207 |
+
if (!isset($meta_data_array[0])) {
|
208 |
+
continue;
|
209 |
+
}
|
210 |
+
|
211 |
+
switch ($meta_key) {
|
212 |
+
|
213 |
+
case '_line_subtotal':
|
214 |
+
$item->setPrice($meta_data_array[0]);
|
215 |
+
break;
|
216 |
+
|
217 |
+
case '_product_id':
|
218 |
+
$item->setProductId($meta_data_array[0]);
|
219 |
+
break;
|
220 |
+
|
221 |
+
case '_variation_id':
|
222 |
+
$item->setProductVariantId($meta_data_array[0]);
|
223 |
+
break;
|
224 |
+
|
225 |
+
case '_qty':
|
226 |
+
$item->setQuantity($meta_data_array[0]);
|
227 |
+
break;
|
228 |
+
|
229 |
+
}
|
230 |
+
}
|
231 |
+
|
232 |
+
if ($item->getProductVariantId() <= 0) {
|
233 |
+
$item->setProductVariantId($item->getProductId());
|
234 |
+
}
|
235 |
+
|
236 |
+
} elseif (isset($order_detail['item_meta_array']) && is_array($order_detail['item_meta_array'])) {
|
237 |
+
|
238 |
+
/// Some users have the newer version of the item meta.
|
239 |
+
|
240 |
+
foreach ($order_detail['item_meta_array'] as $meta_id => $object) {
|
241 |
+
|
242 |
+
if (!isset($object->key)) {
|
243 |
+
continue;
|
244 |
+
}
|
245 |
+
|
246 |
+
switch ($object->key) {
|
247 |
+
|
248 |
+
case '_line_subtotal':
|
249 |
+
$item->setPrice($object->value);
|
250 |
+
break;
|
251 |
+
|
252 |
+
case '_product_id':
|
253 |
+
$item->setProductId($object->value);
|
254 |
+
break;
|
255 |
+
|
256 |
+
case '_variation_id':
|
257 |
+
$item->setProductVariantId($object->value);
|
258 |
+
break;
|
259 |
+
|
260 |
+
case '_qty':
|
261 |
+
$item->setQuantity($object->value);
|
262 |
+
break;
|
263 |
+
}
|
264 |
+
}
|
265 |
+
|
266 |
+
if ($item->getProductVariantId() <= 0) {
|
267 |
+
$item->setProductVariantId($item->getProductId());
|
268 |
+
}
|
269 |
+
}
|
270 |
+
|
271 |
+
if ($item->getQuantity() > 1) {
|
272 |
+
$current_price = $item->getPrice();
|
273 |
+
$price = ($current_price/$item->getQuantity());
|
274 |
+
$item->setPrice($price);
|
275 |
+
}
|
276 |
+
|
277 |
+
return $item;
|
278 |
+
}
|
279 |
+
|
280 |
+
/**
|
281 |
+
* @param int $page
|
282 |
+
* @param int $posts
|
283 |
+
* @return array|bool
|
284 |
+
*/
|
285 |
+
public function getOrderPosts($page = 1, $posts = 10)
|
286 |
+
{
|
287 |
+
$orders = get_posts(array(
|
288 |
+
'post_type' => 'shop_order',
|
289 |
+
//'post_status' => 'publish',
|
290 |
+
'posts_per_page' => $posts,
|
291 |
+
'paged' => $page,
|
292 |
+
'orderby' => 'id',
|
293 |
+
'order' => 'ASC'
|
294 |
+
));
|
295 |
+
|
296 |
+
if (empty($orders)) {
|
297 |
+
return false;
|
298 |
+
}
|
299 |
+
|
300 |
+
return $orders;
|
301 |
+
}
|
302 |
+
|
303 |
+
/**
|
304 |
+
* returns an object with a 'total' and a 'count'.
|
305 |
+
*
|
306 |
+
* @param $user_id
|
307 |
+
* @return object
|
308 |
+
*/
|
309 |
+
public function getCustomerOrderTotals($user_id)
|
310 |
+
{
|
311 |
+
$stats = (object) array('count' => 0, 'total' => 0);
|
312 |
+
|
313 |
+
if (!empty($user_id)) {
|
314 |
+
$orders = get_posts(apply_filters('woocommerce_my_account_my_orders_query', array(
|
315 |
+
'numberposts' => -1,
|
316 |
+
'meta_key' => '_customer_user',
|
317 |
+
'meta_value' => $user_id,
|
318 |
+
'post_type' => 'shop_order',
|
319 |
+
'post_status' => 'publish'
|
320 |
+
)));
|
321 |
+
|
322 |
+
foreach ($orders as $order) {
|
323 |
+
$woo = new WC_Order($order);
|
324 |
+
$stats->total += $woo->get_total();
|
325 |
+
$stats->count++;
|
326 |
+
}
|
327 |
+
return $stats;
|
328 |
+
}
|
329 |
+
|
330 |
+
return false;
|
331 |
+
}
|
332 |
+
|
333 |
+
/**
|
334 |
+
* @param WC_Order $order
|
335 |
+
* @return object
|
336 |
+
*/
|
337 |
+
public function getOrderAddresses(WC_Order $order)
|
338 |
+
{
|
339 |
+
// use the info from the order to compile an address.
|
340 |
+
$billing = new MailChimp_Address();
|
341 |
+
$billing->setAddress1($order->billing_address_1);
|
342 |
+
$billing->setAddress2($order->billing_address_2);
|
343 |
+
$billing->setCity($order->billing_city);
|
344 |
+
$billing->setProvince($order->billing_state);
|
345 |
+
$billing->setPostalCode($order->billing_postcode);
|
346 |
+
$billing->setCountry($order->billing_country);
|
347 |
+
$billing->setPhone($order->billing_phone);
|
348 |
+
$billing->setName('billing');
|
349 |
+
|
350 |
+
$shipping = new MailChimp_Address();
|
351 |
+
$shipping->setAddress1($order->shipping_address_1);
|
352 |
+
$shipping->setAddress2($order->shipping_address_2);
|
353 |
+
$shipping->setCity($order->shipping_city);
|
354 |
+
$shipping->setProvince($order->shipping_state);
|
355 |
+
$shipping->setPostalCode($order->shipping_postcode);
|
356 |
+
$shipping->setCountry($order->shipping_country);
|
357 |
+
if (isset($order->shipping_phone)) {
|
358 |
+
$shipping->setPhone($order->shipping_phone);
|
359 |
+
}
|
360 |
+
$shipping->setName('shipping');
|
361 |
+
|
362 |
+
return (object) array('billing' => $billing, 'shipping' => $shipping);
|
363 |
+
}
|
364 |
+
|
365 |
+
/**
|
366 |
+
* @param $user_id
|
367 |
+
* @param string $type
|
368 |
+
* @return MailChimp_Address
|
369 |
+
*/
|
370 |
+
public function getUserAddress($user_id, $type = 'billing')
|
371 |
+
{
|
372 |
+
$address = new MailChimp_Address();
|
373 |
+
|
374 |
+
// pull all the meta for this user.
|
375 |
+
$meta = get_user_meta($user_id);
|
376 |
+
|
377 |
+
// loop through all the possible address properties, and if we have on on the user, set the property
|
378 |
+
// because it's more up to date.
|
379 |
+
$address_props = array(
|
380 |
+
$type.'_address_1' => 'setAddress1',
|
381 |
+
$type.'_address_2' => 'setAddress2',
|
382 |
+
$type.'_city' => 'setCity',
|
383 |
+
$type.'_state' => 'setProvince',
|
384 |
+
$type.'_postcode' => 'setPostalCode',
|
385 |
+
$type.'_country' => 'setCountry',
|
386 |
+
$type.'_phone' => 'setPhone',
|
387 |
+
);
|
388 |
+
|
389 |
+
// loop through all the address properties and set the values if we have one.
|
390 |
+
foreach ($address_props as $address_key => $address_call) {
|
391 |
+
if (isset($meta[$address_key]) && !empty($meta[$address_key]) && isset($meta[$address_key][0])) {
|
392 |
+
$address->$address_call($meta[$address_key][0]);
|
393 |
+
}
|
394 |
+
}
|
395 |
+
|
396 |
+
return $address;
|
397 |
+
}
|
398 |
+
}
|
includes/api/class-mailchimp-woocommerce-transform-products.php
ADDED
@@ -0,0 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 7/13/16
|
9 |
+
* Time: 8:29 AM
|
10 |
+
*/
|
11 |
+
class MailChimp_WooCommerce_Transform_Products
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* @param int $page
|
15 |
+
* @param int $limit
|
16 |
+
* @return \stdClass
|
17 |
+
*/
|
18 |
+
public function compile($page = 1, $limit = 10)
|
19 |
+
{
|
20 |
+
$response = (object) array(
|
21 |
+
'endpoint' => 'products',
|
22 |
+
'page' => $page,
|
23 |
+
'limit' => (int) $limit,
|
24 |
+
'count' => 0,
|
25 |
+
'stuffed' => false,
|
26 |
+
'items' => array(),
|
27 |
+
);
|
28 |
+
|
29 |
+
if ((($products = $this->getProductPosts($page, $limit)) && !empty($products))) {
|
30 |
+
foreach ($products as $post) {
|
31 |
+
$response->items[] = $this->transform($post);
|
32 |
+
$response->count++;
|
33 |
+
}
|
34 |
+
}
|
35 |
+
|
36 |
+
$response->stuffed = ($response->count > 0 && (int) $response->count === (int) $limit) ? true : false;
|
37 |
+
|
38 |
+
return $response;
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* @param WP_Post $post
|
43 |
+
* @return MailChimp_Product
|
44 |
+
*/
|
45 |
+
public function transform(WP_Post $post)
|
46 |
+
{
|
47 |
+
$woo = new WC_Product($post);
|
48 |
+
|
49 |
+
$variant_posts = $this->getProductVariantPosts($post->ID);
|
50 |
+
|
51 |
+
$variants = $variant_posts ? array_merge(array($woo), $variant_posts) : array($woo);
|
52 |
+
|
53 |
+
$is_variant = count($variants) > 1;
|
54 |
+
|
55 |
+
$product = new MailChimp_Product();
|
56 |
+
|
57 |
+
$product->setId($woo->get_id());
|
58 |
+
$product->setHandle($post->post_name);
|
59 |
+
$product->setImageUrl(get_the_post_thumbnail_url($post));
|
60 |
+
$product->setDescription($post->post_content);
|
61 |
+
$product->setPublishedAtForeign(mailchimp_date_utc($post->post_date));
|
62 |
+
$product->setTitle($woo->get_title());
|
63 |
+
$product->setUrl($woo->get_permalink());
|
64 |
+
|
65 |
+
foreach ($variants as $variant) {
|
66 |
+
|
67 |
+
$product_variant = $this->variant($is_variant, $variant);
|
68 |
+
|
69 |
+
$product_variant_title = $product_variant->getTitle();
|
70 |
+
|
71 |
+
if (empty($product_variant_title)) {
|
72 |
+
$product_variant->setTitle($woo->get_title());
|
73 |
+
}
|
74 |
+
|
75 |
+
$product_variant_image = $product_variant->getImageUrl();
|
76 |
+
|
77 |
+
if (empty($product_variant_image)) {
|
78 |
+
$product_variant->setImageUrl($product->getImageUrl());
|
79 |
+
}
|
80 |
+
|
81 |
+
$product->addVariant($product_variant);
|
82 |
+
}
|
83 |
+
|
84 |
+
return $product;
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* @param $is_variant
|
89 |
+
* @param WP_Post $post
|
90 |
+
* @return MailChimp_ProductVariation
|
91 |
+
*/
|
92 |
+
public function variant($is_variant, $post)
|
93 |
+
{
|
94 |
+
if ($post instanceof WC_Product || $post instanceof WC_Product_Variation) {
|
95 |
+
$woo = $post;
|
96 |
+
} else {
|
97 |
+
if (isset($post->post_type) && $post->post_type === 'product_variation') {
|
98 |
+
$woo = new WC_Product_Variation($post->ID);
|
99 |
+
} else {
|
100 |
+
$woo = new WC_Product($post);
|
101 |
+
}
|
102 |
+
}
|
103 |
+
|
104 |
+
$variant = new MailChimp_ProductVariation();
|
105 |
+
|
106 |
+
$variant->setId($woo->get_id());
|
107 |
+
$variant->setUrl($woo->get_permalink());
|
108 |
+
$variant->setTitle($woo->get_title());
|
109 |
+
$variant->setBackorders($woo->backorders_allowed());
|
110 |
+
$variant->setImageUrl(get_the_post_thumbnail_url($post));
|
111 |
+
$variant->setInventoryQuantity(($woo->managing_stock() ? $woo->get_stock_quantity() : 0));
|
112 |
+
$variant->setPrice($woo->get_price());
|
113 |
+
$variant->setSku($woo->get_sku());
|
114 |
+
|
115 |
+
if ($woo instanceof WC_Product_Variation) {
|
116 |
+
$variant->setVisibility(($woo->variation_is_visible() ? 'visible' : ''));
|
117 |
+
} else {
|
118 |
+
$variant->setVisibility(($woo->is_visible() ? 'visible' : ''));
|
119 |
+
}
|
120 |
+
|
121 |
+
return $variant;
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* @param int $page
|
126 |
+
* @param int $posts
|
127 |
+
* @return array|bool
|
128 |
+
*/
|
129 |
+
public function getProductPosts($page = 1, $posts = 10)
|
130 |
+
{
|
131 |
+
$products = get_posts(array(
|
132 |
+
'post_type' => array('product'),
|
133 |
+
'posts_per_page' => $posts,
|
134 |
+
'paged' => $page,
|
135 |
+
'orderby' => 'ID',
|
136 |
+
'order' => 'ASC',
|
137 |
+
));
|
138 |
+
|
139 |
+
if (empty($products)) {
|
140 |
+
return false;
|
141 |
+
}
|
142 |
+
|
143 |
+
return $products;
|
144 |
+
}
|
145 |
+
|
146 |
+
/**
|
147 |
+
* @param $id
|
148 |
+
* @return array|bool
|
149 |
+
*/
|
150 |
+
public function getProductVariantPosts($id)
|
151 |
+
{
|
152 |
+
$variants = get_posts(array(
|
153 |
+
'numberposts' => 99999,
|
154 |
+
'order' => 'ASC',
|
155 |
+
'orderby' => 'ID',
|
156 |
+
'post_type' => 'product_variation',
|
157 |
+
'post_parent' => $id,
|
158 |
+
));
|
159 |
+
|
160 |
+
if (empty($variants)) {
|
161 |
+
return false;
|
162 |
+
}
|
163 |
+
|
164 |
+
return $variants;
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* @param $id
|
169 |
+
* @return MailChimp_Product
|
170 |
+
*/
|
171 |
+
public static function deleted($id)
|
172 |
+
{
|
173 |
+
$store_id = mailchimp_get_store_id();
|
174 |
+
$api = mailchimp_get_api();
|
175 |
+
|
176 |
+
if (!($product = $api->getStoreProduct($store_id, "deleted_{$id}"))) {
|
177 |
+
$product = new MailChimp_Product();
|
178 |
+
|
179 |
+
$product->setId("deleted_{$id}");
|
180 |
+
$product->setTitle("deleted_{$id}");
|
181 |
+
|
182 |
+
$variant = new MailChimp_ProductVariation();
|
183 |
+
$variant->setId("deleted_{$id}");
|
184 |
+
$variant->setTitle("deleted_{$id}");
|
185 |
+
|
186 |
+
$product->addVariant($variant);
|
187 |
+
|
188 |
+
return $api->addStoreProduct($store_id, $product);
|
189 |
+
}
|
190 |
+
|
191 |
+
return $product;
|
192 |
+
}
|
193 |
+
}
|
includes/api/errors/class-mailchimp-error.php
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by PhpStorm.
|
5 |
+
*
|
6 |
+
* User: kingpin
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 6/18/15
|
9 |
+
* Time: 11:13 AM
|
10 |
+
*/
|
11 |
+
class MailChimp_Error extends \Exception
|
12 |
+
{
|
13 |
+
|
14 |
+
}
|
includes/api/errors/class-mailchimp-server-error.php
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by PhpStorm.
|
5 |
+
*
|
6 |
+
* User: kingpin
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 6/18/15
|
9 |
+
* Time: 11:13 AM
|
10 |
+
*/
|
11 |
+
class MailChimp_ServerError extends \Exception
|
12 |
+
{
|
13 |
+
|
14 |
+
}
|
includes/api/helpers/class-mailchimp-woocommerce-api-currency-codes.php
ADDED
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 7/12/16
|
9 |
+
* Time: 1:38 PM
|
10 |
+
*/
|
11 |
+
class MailChimp_Api_CurrencyCodes
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* @return array
|
15 |
+
*/
|
16 |
+
public static function all()
|
17 |
+
{
|
18 |
+
return array(
|
19 |
+
'AFA' => array('Afghan Afghani', '971'),
|
20 |
+
'AWG' => array('Aruban Florin', '533'),
|
21 |
+
'AUD' => array('Australian Dollars', '036'),
|
22 |
+
'ARS' => array('Argentine Pes', '032'),
|
23 |
+
'AZN' => array('Azerbaijanian Manat', '944'),
|
24 |
+
'BSD' => array('Bahamian Dollar', '044'),
|
25 |
+
'BDT' => array('Bangladeshi Taka', '050'),
|
26 |
+
'BBD' => array('Barbados Dollar', '052'),
|
27 |
+
'BYR' => array('Belarussian Rouble', '974'),
|
28 |
+
'BOB' => array('Bolivian Boliviano', '068'),
|
29 |
+
'BRL' => array('Brazilian Real', '986'),
|
30 |
+
'GBP' => array('British Pounds Sterling', '826'),
|
31 |
+
'BGN' => array('Bulgarian Lev', '975'),
|
32 |
+
'KHR' => array('Cambodia Riel', '116'),
|
33 |
+
'CAD' => array('Canadian Dollars', '124'),
|
34 |
+
'KYD' => array('Cayman Islands Dollar', '136'),
|
35 |
+
'CLP' => array('Chilean Peso', '152'),
|
36 |
+
'CNY' => array('Chinese Renminbi Yuan', '156'),
|
37 |
+
'COP' => array('Colombian Peso', '170'),
|
38 |
+
'CRC' => array('Costa Rican Colon', '188'),
|
39 |
+
'HRK' => array('Croatia Kuna', '191'),
|
40 |
+
'CPY' => array('Cypriot Pounds', '196'),
|
41 |
+
'CZK' => array('Czech Koruna', '203'),
|
42 |
+
'DKK' => array('Danish Krone', '208'),
|
43 |
+
'DOP' => array('Dominican Republic Peso', '214'),
|
44 |
+
'XCD' => array('East Caribbean Dollar', '951'),
|
45 |
+
'EGP' => array('Egyptian Pound', '818'),
|
46 |
+
'ERN' => array('Eritrean Nakfa', '232'),
|
47 |
+
'EEK' => array('Estonia Kroon', '233'),
|
48 |
+
'EUR' => array('Euro', '978'),
|
49 |
+
'GEL' => array('Georgian Lari', '981'),
|
50 |
+
'GHC' => array('Ghana Cedi', '288'),
|
51 |
+
'GIP' => array('Gibraltar Pound', '292'),
|
52 |
+
'GTQ' => array('Guatemala Quetzal', '320'),
|
53 |
+
'HNL' => array('Honduras Lempira', '340'),
|
54 |
+
'HKD' => array('Hong Kong Dollars', '344'),
|
55 |
+
'HUF' => array('Hungary Forint', '348'),
|
56 |
+
'ISK' => array('Icelandic Krona', '352'),
|
57 |
+
'INR' => array('Indian Rupee', '356'),
|
58 |
+
'IDR' => array('Indonesia Rupiah', '360'),
|
59 |
+
'ILS' => array('Israel Shekel', '376'),
|
60 |
+
'JMD' => array('Jamaican Dollar', '388'),
|
61 |
+
'JPY' => array('Japanese yen', '392'),
|
62 |
+
'KZT' => array('Kazakhstan Tenge', '368'),
|
63 |
+
'KES' => array('Kenyan Shilling', '404'),
|
64 |
+
'KWD' => array('Kuwaiti Dinar', '414'),
|
65 |
+
'LVL' => array('Latvia Lat', '428'),
|
66 |
+
'LBP' => array('Lebanese Pound', '422'),
|
67 |
+
'LTL' => array('Lithuania Litas', '440'),
|
68 |
+
'MOP' => array('Macau Pataca', '446'),
|
69 |
+
'MKD' => array('Macedonian Denar', '807'),
|
70 |
+
'MGA' => array('Malagascy Ariary', '969'),
|
71 |
+
'MYR' => array('Malaysian Ringgit', '458'),
|
72 |
+
'MTL' => array('Maltese Lira', '470'),
|
73 |
+
'BAM' => array('Marka', '977'),
|
74 |
+
'MUR' => array('Mauritius Rupee', '480'),
|
75 |
+
'MXN' => array('Mexican Pesos', '484'),
|
76 |
+
'MZM' => array('Mozambique Metical', '508'),
|
77 |
+
'NPR' => array('Nepalese Rupee', '524'),
|
78 |
+
'ANG' => array('Netherlands Antilles Guilder', '532'),
|
79 |
+
'TWD' => array('New Taiwanese Dollars', '901'),
|
80 |
+
'NZD' => array('New Zealand Dollars', '554'),
|
81 |
+
'NIO' => array('Nicaragua Cordoba', '558'),
|
82 |
+
'NGN' => array('Nigeria Naira', '566'),
|
83 |
+
'KPW' => array('North Korean Won', '408'),
|
84 |
+
'NOK' => array('Norwegian Krone', '578'),
|
85 |
+
'OMR' => array('Omani Riyal', '512'),
|
86 |
+
'PKR' => array('Pakistani Rupee', '586'),
|
87 |
+
'PYG' => array('Paraguay Guarani', '600'),
|
88 |
+
'PEN' => array('Peru New Sol', '604'),
|
89 |
+
'PHP' => array('Philippine Pesos', '608'),
|
90 |
+
'QAR' => array('Qatari Riyal', '634'),
|
91 |
+
'RON' => array('Romanian New Leu', '946'),
|
92 |
+
'RUB' => array('Russian Federation Ruble', '643'),
|
93 |
+
'SAR' => array('Saudi Riyal', '682'),
|
94 |
+
'CSD' => array('Serbian Dinar', '891'),
|
95 |
+
'SCR' => array('Seychelles Rupee', '690'),
|
96 |
+
'SGD' => array('Singapore Dollars', '702'),
|
97 |
+
'SKK' => array('Slovak Koruna', '703'),
|
98 |
+
'SIT' => array('Slovenia Tolar', '705'),
|
99 |
+
'ZAR' => array('South African Rand', '710'),
|
100 |
+
'KRW' => array('South Korean Won', '410'),
|
101 |
+
'LKR' => array('Sri Lankan Rupee', '144'),
|
102 |
+
'SRD' => array('Surinam Dollar', '968'),
|
103 |
+
'SEK' => array('Swedish Krona', '752'),
|
104 |
+
'CHF' => array('Swiss Francs', '756'),
|
105 |
+
'TZS' => array('Tanzanian Shilling', '834'),
|
106 |
+
'THB' => array('Thai Baht', '764'),
|
107 |
+
'TTD' => array('Trinidad and Tobago Dollar', '780'),
|
108 |
+
'TRY' => array('Turkish New Lira', '949'),
|
109 |
+
'AED' => array('UAE Dirham', '784'),
|
110 |
+
'USD' => array('US Dollars', '840'),
|
111 |
+
'UGX' => array('Ugandian Shilling', '800'),
|
112 |
+
'UAH' => array('Ukraine Hryvna', '980'),
|
113 |
+
'UYU' => array('Uruguayan Peso', '858'),
|
114 |
+
'UZS' => array('Uzbekistani Som', '860'),
|
115 |
+
'VEB' => array('Venezuela Bolivar', '862'),
|
116 |
+
'VND' => array('Vietnam Dong', '704'),
|
117 |
+
'AMK' => array('Zambian Kwacha', '894'),
|
118 |
+
'ZWD' => array('Zimbabwe Dollar', '716'),
|
119 |
+
);
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* @return array
|
124 |
+
*/
|
125 |
+
public static function lists()
|
126 |
+
{
|
127 |
+
$response = array();
|
128 |
+
foreach (static::all() as $key => $data) {
|
129 |
+
$response[$key] = $data[0];
|
130 |
+
}
|
131 |
+
return $response;
|
132 |
+
}
|
133 |
+
}
|
includes/api/helpers/class-mailchimp-woocommerce-api-locales.php
ADDED
@@ -0,0 +1,469 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 7/12/16
|
9 |
+
* Time: 2:07 PM
|
10 |
+
*/
|
11 |
+
class MailChimp_Api_Locales
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* @return array
|
15 |
+
*/
|
16 |
+
public function all()
|
17 |
+
{
|
18 |
+
return [
|
19 |
+
"af_NA"=> "Afrikaans (Namibia)",
|
20 |
+
"af_ZA"=> "Afrikaans (South Africa)",
|
21 |
+
"af"=> "Afrikaans",
|
22 |
+
"ak_GH"=> "Akan (Ghana)",
|
23 |
+
"ak"=> "Akan",
|
24 |
+
"sq_AL"=> "Albanian (Albania)",
|
25 |
+
"sq"=> "Albanian",
|
26 |
+
"am_ET"=> "Amharic (Ethiopia)",
|
27 |
+
"am"=> "Amharic",
|
28 |
+
"ar_DZ"=> "Arabic (Algeria)",
|
29 |
+
"ar_BH"=> "Arabic (Bahrain)",
|
30 |
+
"ar_EG"=> "Arabic (Egypt)",
|
31 |
+
"ar_IQ"=> "Arabic (Iraq)",
|
32 |
+
"ar_JO"=> "Arabic (Jordan)",
|
33 |
+
"ar_KW"=> "Arabic (Kuwait)",
|
34 |
+
"ar_LB"=> "Arabic (Lebanon)",
|
35 |
+
"ar_LY"=> "Arabic (Libya)",
|
36 |
+
"ar_MA"=> "Arabic (Morocco)",
|
37 |
+
"ar_OM"=> "Arabic (Oman)",
|
38 |
+
"ar_QA"=> "Arabic (Qatar)",
|
39 |
+
"ar_SA"=> "Arabic (Saudi Arabia)",
|
40 |
+
"ar_SD"=> "Arabic (Sudan)",
|
41 |
+
"ar_SY"=> "Arabic (Syria)",
|
42 |
+
"ar_TN"=> "Arabic (Tunisia)",
|
43 |
+
"ar_AE"=> "Arabic (United Arab Emirates)",
|
44 |
+
"ar_YE"=> "Arabic (Yemen)",
|
45 |
+
"ar"=> "Arabic",
|
46 |
+
"hy_AM"=> "Armenian (Armenia)",
|
47 |
+
"hy"=> "Armenian",
|
48 |
+
"as_IN"=> "Assamese (India)",
|
49 |
+
"as"=> "Assamese",
|
50 |
+
"asa_TZ"=> "Asu (Tanzania)",
|
51 |
+
"asa"=> "Asu",
|
52 |
+
"az_Cyrl"=> "Azerbaijani (Cyrillic)",
|
53 |
+
"az_Cyrl_AZ"=> "Azerbaijani (Cyrillic, Azerbaijan)",
|
54 |
+
"az_Latn"=> "Azerbaijani (Latin)",
|
55 |
+
"az_Latn_AZ"=> "Azerbaijani (Latin, Azerbaijan)",
|
56 |
+
"az"=> "Azerbaijani",
|
57 |
+
"bm_ML"=> "Bambara (Mali)",
|
58 |
+
"bm"=> "Bambara",
|
59 |
+
"eu_ES"=> "Basque (Spain)",
|
60 |
+
"eu"=> "Basque",
|
61 |
+
"be_BY"=> "Belarusian (Belarus)",
|
62 |
+
"be"=> "Belarusian",
|
63 |
+
"bem_ZM"=> "Bemba (Zambia)",
|
64 |
+
"bem"=> "Bemba",
|
65 |
+
"bez_TZ"=> "Bena (Tanzania)",
|
66 |
+
"bez"=> "Bena",
|
67 |
+
"bn_BD"=> "Bengali (Bangladesh)",
|
68 |
+
"bn_IN"=> "Bengali (India)",
|
69 |
+
"bn"=> "Bengali",
|
70 |
+
"bs_BA"=> "Bosnian (Bosnia and Herzegovina)",
|
71 |
+
"bs"=> "Bosnian",
|
72 |
+
"bg_BG"=> "Bulgarian (Bulgaria)",
|
73 |
+
"bg"=> "Bulgarian",
|
74 |
+
"my_MM"=> "Burmese (Myanmar [Burma])",
|
75 |
+
"my"=> "Burmese",
|
76 |
+
"ca_ES"=> "Catalan (Spain)",
|
77 |
+
"ca"=> "Catalan",
|
78 |
+
"tzm_Latn"=> "Central Morocco Tamazight (Latin)",
|
79 |
+
"tzm_Latn_MA"=> "Central Morocco Tamazight (Latin, Morocco)",
|
80 |
+
"tzm"=> "Central Morocco Tamazight",
|
81 |
+
"chr_US"=> "Cherokee (United States)",
|
82 |
+
"chr"=> "Cherokee",
|
83 |
+
"cgg_UG"=> "Chiga (Uganda)",
|
84 |
+
"cgg"=> "Chiga",
|
85 |
+
"zh_Hans"=> "Chinese (Simplified Han)",
|
86 |
+
"zh_Hans_CN"=> "Chinese (Simplified Han, China)",
|
87 |
+
"zh_Hans_HK"=> "Chinese (Simplified Han, Hong Kong SAR China)",
|
88 |
+
"zh_Hans_MO"=> "Chinese (Simplified Han, Macau SAR China)",
|
89 |
+
"zh_Hans_SG"=> "Chinese (Simplified Han, Singapore)",
|
90 |
+
"zh_Hant"=> "Chinese (Traditional Han)",
|
91 |
+
"zh_Hant_HK"=> "Chinese (Traditional Han, Hong Kong SAR China)",
|
92 |
+
"zh_Hant_MO"=> "Chinese (Traditional Han, Macau SAR China)",
|
93 |
+
"zh_Hant_TW"=> "Chinese (Traditional Han, Taiwan)",
|
94 |
+
"zh"=> "Chinese",
|
95 |
+
"kw_GB"=> "Cornish (United Kingdom)",
|
96 |
+
"kw"=> "Cornish",
|
97 |
+
"hr_HR"=> "Croatian (Croatia)",
|
98 |
+
"hr"=> "Croatian",
|
99 |
+
"cs_CZ"=> "Czech (Czech Republic)",
|
100 |
+
"cs"=> "Czech",
|
101 |
+
"da_DK"=> "Danish (Denmark)",
|
102 |
+
"da"=> "Danish",
|
103 |
+
"nl_BE"=> "Dutch (Belgium)",
|
104 |
+
"nl_NL"=> "Dutch (Netherlands)",
|
105 |
+
"nl"=> "Dutch",
|
106 |
+
"ebu_KE"=> "Embu (Kenya)",
|
107 |
+
"ebu"=> "Embu",
|
108 |
+
"en_AS"=> "English (American Samoa)",
|
109 |
+
"en_AU"=> "English (Australia)",
|
110 |
+
"en_BE"=> "English (Belgium)",
|
111 |
+
"en_BZ"=> "English (Belize)",
|
112 |
+
"en_BW"=> "English (Botswana)",
|
113 |
+
"en_CA"=> "English (Canada)",
|
114 |
+
"en_GU"=> "English (Guam)",
|
115 |
+
"en_HK"=> "English (Hong Kong SAR China)",
|
116 |
+
"en_IN"=> "English (India)",
|
117 |
+
"en_IE"=> "English (Ireland)",
|
118 |
+
"en_JM"=> "English (Jamaica)",
|
119 |
+
"en_MT"=> "English (Malta)",
|
120 |
+
"en_MH"=> "English (Marshall Islands)",
|
121 |
+
"en_MU"=> "English (Mauritius)",
|
122 |
+
"en_NA"=> "English (Namibia)",
|
123 |
+
"en_NZ"=> "English (New Zealand)",
|
124 |
+
"en_MP"=> "English (Northern Mariana Islands)",
|
125 |
+
"en_PK"=> "English (Pakistan)",
|
126 |
+
"en_PH"=> "English (Philippines)",
|
127 |
+
"en_SG"=> "English (Singapore)",
|
128 |
+
"en_ZA"=> "English (South Africa)",
|
129 |
+
"en_TT"=> "English (Trinidad and Tobago)",
|
130 |
+
"en_UM"=> "English (U.S. Minor Outlying Islands)",
|
131 |
+
"en_VI"=> "English (U.S. Virgin Islands)",
|
132 |
+
"en_GB"=> "English (United Kingdom)",
|
133 |
+
"en_US"=> "English (United States)",
|
134 |
+
"en_ZW"=> "English (Zimbabwe)",
|
135 |
+
"en"=> "English",
|
136 |
+
"eo"=> "Esperanto",
|
137 |
+
"et_EE"=> "Estonian (Estonia)",
|
138 |
+
"et"=> "Estonian",
|
139 |
+
"ee_GH"=> "Ewe (Ghana)",
|
140 |
+
"ee_TG"=> "Ewe (Togo)",
|
141 |
+
"ee"=> "Ewe",
|
142 |
+
"fo_FO"=> "Faroese (Faroe Islands)",
|
143 |
+
"fo"=> "Faroese",
|
144 |
+
"fil_PH"=> "Filipino (Philippines)",
|
145 |
+
"fil"=> "Filipino",
|
146 |
+
"fi_FI"=> "Finnish (Finland)",
|
147 |
+
"fi"=> "Finnish",
|
148 |
+
"fr_BE"=> "French (Belgium)",
|
149 |
+
"fr_BJ"=> "French (Benin)",
|
150 |
+
"fr_BF"=> "French (Burkina Faso)",
|
151 |
+
"fr_BI"=> "French (Burundi)",
|
152 |
+
"fr_CM"=> "French (Cameroon)",
|
153 |
+
"fr_CA"=> "French (Canada)",
|
154 |
+
"fr_CF"=> "French (Central African Republic)",
|
155 |
+
"fr_TD"=> "French (Chad)",
|
156 |
+
"fr_KM"=> "French (Comoros)",
|
157 |
+
"fr_CG"=> "French (Congo - Brazzaville)",
|
158 |
+
"fr_CD"=> "French (Congo - Kinshasa)",
|
159 |
+
"fr_CI"=> "French (Côte d’Ivoire)",
|
160 |
+
"fr_DJ"=> "French (Djibouti)",
|
161 |
+
"fr_GQ"=> "French (Equatorial Guinea)",
|
162 |
+
"fr_FR"=> "French (France)",
|
163 |
+
"fr_GA"=> "French (Gabon)",
|
164 |
+
"fr_GP"=> "French (Guadeloupe)",
|
165 |
+
"fr_GN"=> "French (Guinea)",
|
166 |
+
"fr_LU"=> "French (Luxembourg)",
|
167 |
+
"fr_MG"=> "French (Madagascar)",
|
168 |
+
"fr_ML"=> "French (Mali)",
|
169 |
+
"fr_MQ"=> "French (Martinique)",
|
170 |
+
"fr_MC"=> "French (Monaco)",
|
171 |
+
"fr_NE"=> "French (Niger)",
|
172 |
+
"fr_RW"=> "French (Rwanda)",
|
173 |
+
"fr_RE"=> "French (Réunion)",
|
174 |
+
"fr_BL"=> "French (Saint Barthélemy)",
|
175 |
+
"fr_MF"=> "French (Saint Martin)",
|
176 |
+
"fr_SN"=> "French (Senegal)",
|
177 |
+
"fr_CH"=> "French (Switzerland)",
|
178 |
+
"fr_TG"=> "French (Togo)",
|
179 |
+
"fr"=> "French",
|
180 |
+
"ff_SN"=> "Fulah (Senegal)",
|
181 |
+
"ff"=> "Fulah",
|
182 |
+
"gl_ES"=> "Galician (Spain)",
|
183 |
+
"gl"=> "Galician",
|
184 |
+
"lg_UG"=> "Ganda (Uganda)",
|
185 |
+
"lg"=> "Ganda",
|
186 |
+
"ka_GE"=> "Georgian (Georgia)",
|
187 |
+
"ka"=> "Georgian",
|
188 |
+
"de_AT"=> "German (Austria)",
|
189 |
+
"de_BE"=> "German (Belgium)",
|
190 |
+
"de_DE"=> "German (Germany)",
|
191 |
+
"de_LI"=> "German (Liechtenstein)",
|
192 |
+
"de_LU"=> "German (Luxembourg)",
|
193 |
+
"de_CH"=> "German (Switzerland)",
|
194 |
+
"de"=> "German",
|
195 |
+
"el_CY"=> "Greek (Cyprus)",
|
196 |
+
"el_GR"=> "Greek (Greece)",
|
197 |
+
"el"=> "Greek",
|
198 |
+
"gu_IN"=> "Gujarati (India)",
|
199 |
+
"gu"=> "Gujarati",
|
200 |
+
"guz_KE"=> "Gusii (Kenya)",
|
201 |
+
"guz"=> "Gusii",
|
202 |
+
"ha_Latn"=> "Hausa (Latin)",
|
203 |
+
"ha_Latn_GH"=> "Hausa (Latin, Ghana)",
|
204 |
+
"ha_Latn_NE"=> "Hausa (Latin, Niger)",
|
205 |
+
"ha_Latn_NG"=> "Hausa (Latin, Nigeria)",
|
206 |
+
"ha"=> "Hausa",
|
207 |
+
"haw_US"=> "Hawaiian (United States)",
|
208 |
+
"haw"=> "Hawaiian",
|
209 |
+
"he_IL"=> "Hebrew (Israel)",
|
210 |
+
"he"=> "Hebrew",
|
211 |
+
"hi_IN"=> "Hindi (India)",
|
212 |
+
"hi"=> "Hindi",
|
213 |
+
"hu_HU"=> "Hungarian (Hungary)",
|
214 |
+
"hu"=> "Hungarian",
|
215 |
+
"is_IS"=> "Icelandic (Iceland)",
|
216 |
+
"is"=> "Icelandic",
|
217 |
+
"ig_NG"=> "Igbo (Nigeria)",
|
218 |
+
"ig"=> "Igbo",
|
219 |
+
"id_ID"=> "Indonesian (Indonesia)",
|
220 |
+
"id"=> "Indonesian",
|
221 |
+
"ga_IE"=> "Irish (Ireland)",
|
222 |
+
"ga"=> "Irish",
|
223 |
+
"it_IT"=> "Italian (Italy)",
|
224 |
+
"it_CH"=> "Italian (Switzerland)",
|
225 |
+
"it"=> "Italian",
|
226 |
+
"ja_JP"=> "Japanese (Japan)",
|
227 |
+
"ja"=> "Japanese",
|
228 |
+
"kea_CV"=> "Kabuverdianu (Cape Verde)",
|
229 |
+
"kea"=> "Kabuverdianu",
|
230 |
+
"kab_DZ"=> "Kabyle (Algeria)",
|
231 |
+
"kab"=> "Kabyle",
|
232 |
+
"kl_GL"=> "Kalaallisut (Greenland)",
|
233 |
+
"kl"=> "Kalaallisut",
|
234 |
+
"kln_KE"=> "Kalenjin (Kenya)",
|
235 |
+
"kln"=> "Kalenjin",
|
236 |
+
"kam_KE"=> "Kamba (Kenya)",
|
237 |
+
"kam"=> "Kamba",
|
238 |
+
"kn_IN"=> "Kannada (India)",
|
239 |
+
"kn"=> "Kannada",
|
240 |
+
"kk_Cyrl"=> "Kazakh (Cyrillic)",
|
241 |
+
"kk_Cyrl_KZ"=> "Kazakh (Cyrillic, Kazakhstan)",
|
242 |
+
"kk"=> "Kazakh",
|
243 |
+
"km_KH"=> "Khmer (Cambodia)",
|
244 |
+
"km"=> "Khmer",
|
245 |
+
"ki_KE"=> "Kikuyu (Kenya)",
|
246 |
+
"ki"=> "Kikuyu",
|
247 |
+
"rw_RW"=> "Kinyarwanda (Rwanda)",
|
248 |
+
"rw"=> "Kinyarwanda",
|
249 |
+
"kok_IN"=> "Konkani (India)",
|
250 |
+
"kok"=> "Konkani",
|
251 |
+
"ko_KR"=> "Korean (South Korea)",
|
252 |
+
"ko"=> "Korean",
|
253 |
+
"khq_ML"=> "Koyra Chiini (Mali)",
|
254 |
+
"khq"=> "Koyra Chiini",
|
255 |
+
"ses_ML"=> "Koyraboro Senni (Mali)",
|
256 |
+
"ses"=> "Koyraboro Senni",
|
257 |
+
"lag_TZ"=> "Langi (Tanzania)",
|
258 |
+
"lag"=> "Langi",
|
259 |
+
"lv_LV"=> "Latvian (Latvia)",
|
260 |
+
"lv"=> "Latvian",
|
261 |
+
"lt_LT"=> "Lithuanian (Lithuania)",
|
262 |
+
"lt"=> "Lithuanian",
|
263 |
+
"luo_KE"=> "Luo (Kenya)",
|
264 |
+
"luo"=> "Luo",
|
265 |
+
"luy_KE"=> "Luyia (Kenya)",
|
266 |
+
"luy"=> "Luyia",
|
267 |
+
"mk_MK"=> "Macedonian (Macedonia)",
|
268 |
+
"mk"=> "Macedonian",
|
269 |
+
"jmc_TZ"=> "Machame (Tanzania)",
|
270 |
+
"jmc"=> "Machame",
|
271 |
+
"kde_TZ"=> "Makonde (Tanzania)",
|
272 |
+
"kde"=> "Makonde",
|
273 |
+
"mg_MG"=> "Malagasy (Madagascar)",
|
274 |
+
"mg"=> "Malagasy",
|
275 |
+
"ms_BN"=> "Malay (Brunei)",
|
276 |
+
"ms_MY"=> "Malay (Malaysia)",
|
277 |
+
"ms"=> "Malay",
|
278 |
+
"ml_IN"=> "Malayalam (India)",
|
279 |
+
"ml"=> "Malayalam",
|
280 |
+
"mt_MT"=> "Maltese (Malta)",
|
281 |
+
"mt"=> "Maltese",
|
282 |
+
"gv_GB"=> "Manx (United Kingdom)",
|
283 |
+
"gv"=> "Manx",
|
284 |
+
"mr_IN"=> "Marathi (India)",
|
285 |
+
"mr"=> "Marathi",
|
286 |
+
"mas_KE"=> "Masai (Kenya)",
|
287 |
+
"mas_TZ"=> "Masai (Tanzania)",
|
288 |
+
"mas"=> "Masai",
|
289 |
+
"mer_KE"=> "Meru (Kenya)",
|
290 |
+
"mer"=> "Meru",
|
291 |
+
"mfe_MU"=> "Morisyen (Mauritius)",
|
292 |
+
"mfe"=> "Morisyen",
|
293 |
+
"naq_NA"=> "Nama (Namibia)",
|
294 |
+
"naq"=> "Nama",
|
295 |
+
"ne_IN"=> "Nepali (India)",
|
296 |
+
"ne_NP"=> "Nepali (Nepal)",
|
297 |
+
"ne"=> "Nepali",
|
298 |
+
"nd_ZW"=> "North Ndebele (Zimbabwe)",
|
299 |
+
"nd"=> "North Ndebele",
|
300 |
+
"nb_NO"=> "Norwegian Bokmål (Norway)",
|
301 |
+
"nb"=> "Norwegian Bokmål",
|
302 |
+
"nn_NO"=> "Norwegian Nynorsk (Norway)",
|
303 |
+
"nn"=> "Norwegian Nynorsk",
|
304 |
+
"nyn_UG"=> "Nyankole (Uganda)",
|
305 |
+
"nyn"=> "Nyankole",
|
306 |
+
"or_IN"=> "Oriya (India)",
|
307 |
+
"or"=> "Oriya",
|
308 |
+
"om_ET"=> "Oromo (Ethiopia)",
|
309 |
+
"om_KE"=> "Oromo (Kenya)",
|
310 |
+
"om"=> "Oromo",
|
311 |
+
"ps_AF"=> "Pashto (Afghanistan)",
|
312 |
+
"ps"=> "Pashto",
|
313 |
+
"fa_AF"=> "Persian (Afghanistan)",
|
314 |
+
"fa_IR"=> "Persian (Iran)",
|
315 |
+
"fa"=> "Persian",
|
316 |
+
"pl_PL"=> "Polish (Poland)",
|
317 |
+
"pl"=> "Polish",
|
318 |
+
"pt_BR"=> "Portuguese (Brazil)",
|
319 |
+
"pt_GW"=> "Portuguese (Guinea-Bissau)",
|
320 |
+
"pt_MZ"=> "Portuguese (Mozambique)",
|
321 |
+
"pt_PT"=> "Portuguese (Portugal)",
|
322 |
+
"pt"=> "Portuguese",
|
323 |
+
"pa_Arab"=> "Punjabi (Arabic)",
|
324 |
+
"pa_Arab_PK"=> "Punjabi (Arabic, Pakistan)",
|
325 |
+
"pa_Guru"=> "Punjabi (Gurmukhi)",
|
326 |
+
"pa_Guru_IN"=> "Punjabi (Gurmukhi, India)",
|
327 |
+
"pa"=> "Punjabi",
|
328 |
+
"ro_MD"=> "Romanian (Moldova)",
|
329 |
+
"ro_RO"=> "Romanian (Romania)",
|
330 |
+
"ro"=> "Romanian",
|
331 |
+
"rm_CH"=> "Romansh (Switzerland)",
|
332 |
+
"rm"=> "Romansh",
|
333 |
+
"rof_TZ"=> "Rombo (Tanzania)",
|
334 |
+
"rof"=> "Rombo",
|
335 |
+
"ru_MD"=> "Russian (Moldova)",
|
336 |
+
"ru_RU"=> "Russian (Russia)",
|
337 |
+
"ru_UA"=> "Russian (Ukraine)",
|
338 |
+
"ru"=> "Russian",
|
339 |
+
"rwk_TZ"=> "Rwa (Tanzania)",
|
340 |
+
"rwk"=> "Rwa",
|
341 |
+
"saq_KE"=> "Samburu (Kenya)",
|
342 |
+
"saq"=> "Samburu",
|
343 |
+
"sg_CF"=> "Sango (Central African Republic)",
|
344 |
+
"sg"=> "Sango",
|
345 |
+
"seh_MZ"=> "Sena (Mozambique)",
|
346 |
+
"seh"=> "Sena",
|
347 |
+
"sr_Cyrl"=> "Serbian (Cyrillic)",
|
348 |
+
"sr_Cyrl_BA"=> "Serbian (Cyrillic, Bosnia and Herzegovina)",
|
349 |
+
"sr_Cyrl_ME"=> "Serbian (Cyrillic, Montenegro)",
|
350 |
+
"sr_Cyrl_RS"=> "Serbian (Cyrillic, Serbia)",
|
351 |
+
"sr_Latn"=> "Serbian (Latin)",
|
352 |
+
"sr_Latn_BA"=> "Serbian (Latin, Bosnia and Herzegovina)",
|
353 |
+
"sr_Latn_ME"=> "Serbian (Latin, Montenegro)",
|
354 |
+
"sr_Latn_RS"=> "Serbian (Latin, Serbia)",
|
355 |
+
"sr"=> "Serbian",
|
356 |
+
"sn_ZW"=> "Shona (Zimbabwe)",
|
357 |
+
"sn"=> "Shona",
|
358 |
+
"ii_CN"=> "Sichuan Yi (China)",
|
359 |
+
"ii"=> "Sichuan Yi",
|
360 |
+
"si_LK"=> "Sinhala (Sri Lanka)",
|
361 |
+
"si"=> "Sinhala",
|
362 |
+
"sk_SK"=> "Slovak (Slovakia)",
|
363 |
+
"sk"=> "Slovak",
|
364 |
+
"sl_SI"=> "Slovenian (Slovenia)",
|
365 |
+
"sl"=> "Slovenian",
|
366 |
+
"xog_UG"=> "Soga (Uganda)",
|
367 |
+
"xog"=> "Soga",
|
368 |
+
"so_DJ"=> "Somali (Djibouti)",
|
369 |
+
"so_ET"=> "Somali (Ethiopia)",
|
370 |
+
"so_KE"=> "Somali (Kenya)",
|
371 |
+
"so_SO"=> "Somali (Somalia)",
|
372 |
+
"so"=> "Somali",
|
373 |
+
"es_AR"=> "Spanish (Argentina)",
|
374 |
+
"es_BO"=> "Spanish (Bolivia)",
|
375 |
+
"es_CL"=> "Spanish (Chile)",
|
376 |
+
"es_CO"=> "Spanish (Colombia)",
|
377 |
+
"es_CR"=> "Spanish (Costa Rica)",
|
378 |
+
"es_DO"=> "Spanish (Dominican Republic)",
|
379 |
+
"es_EC"=> "Spanish (Ecuador)",
|
380 |
+
"es_SV"=> "Spanish (El Salvador)",
|
381 |
+
"es_GQ"=> "Spanish (Equatorial Guinea)",
|
382 |
+
"es_GT"=> "Spanish (Guatemala)",
|
383 |
+
"es_HN"=> "Spanish (Honduras)",
|
384 |
+
"es_419"=> "Spanish (Latin America)",
|
385 |
+
"es_MX"=> "Spanish (Mexico)",
|
386 |
+
"es_NI"=> "Spanish (Nicaragua)",
|
387 |
+
"es_PA"=> "Spanish (Panama)",
|
388 |
+
"es_PY"=> "Spanish (Paraguay)",
|
389 |
+
"es_PE"=> "Spanish (Peru)",
|
390 |
+
"es_PR"=> "Spanish (Puerto Rico)",
|
391 |
+
"es_ES"=> "Spanish (Spain)",
|
392 |
+
"es_US"=> "Spanish (United States)",
|
393 |
+
"es_UY"=> "Spanish (Uruguay)",
|
394 |
+
"es_VE"=> "Spanish (Venezuela)",
|
395 |
+
"es"=> "Spanish",
|
396 |
+
"sw_KE"=> "Swahili (Kenya)",
|
397 |
+
"sw_TZ"=> "Swahili (Tanzania)",
|
398 |
+
"sw"=> "Swahili",
|
399 |
+
"sv_FI"=> "Swedish (Finland)",
|
400 |
+
"sv_SE"=> "Swedish (Sweden)",
|
401 |
+
"sv"=> "Swedish",
|
402 |
+
"gsw_CH"=> "Swiss German (Switzerland)",
|
403 |
+
"gsw"=> "Swiss German",
|
404 |
+
"shi_Latn"=> "Tachelhit (Latin)",
|
405 |
+
"shi_Latn_MA"=> "Tachelhit (Latin, Morocco)",
|
406 |
+
"shi_Tfng"=> "Tachelhit (Tifinagh)",
|
407 |
+
"shi_Tfng_MA"=> "Tachelhit (Tifinagh, Morocco)",
|
408 |
+
"shi"=> "Tachelhit",
|
409 |
+
"dav_KE"=> "Taita (Kenya)",
|
410 |
+
"dav"=> "Taita",
|
411 |
+
"ta_IN"=> "Tamil (India)",
|
412 |
+
"ta_LK"=> "Tamil (Sri Lanka)",
|
413 |
+
"ta"=> "Tamil",
|
414 |
+
"te_IN"=> "Telugu (India)",
|
415 |
+
"te"=> "Telugu",
|
416 |
+
"teo_KE"=> "Teso (Kenya)",
|
417 |
+
"teo_UG"=> "Teso (Uganda)",
|
418 |
+
"teo"=> "Teso",
|
419 |
+
"th_TH"=> "Thai (Thailand)",
|
420 |
+
"th"=> "Thai",
|
421 |
+
"bo_CN"=> "Tibetan (China)",
|
422 |
+
"bo_IN"=> "Tibetan (India)",
|
423 |
+
"bo"=> "Tibetan",
|
424 |
+
"ti_ER"=> "Tigrinya (Eritrea)",
|
425 |
+
"ti_ET"=> "Tigrinya (Ethiopia)",
|
426 |
+
"ti"=> "Tigrinya",
|
427 |
+
"to_TO"=> "Tonga (Tonga)",
|
428 |
+
"to"=> "Tonga",
|
429 |
+
"tr_TR"=> "Turkish (Turkey)",
|
430 |
+
"tr"=> "Turkish",
|
431 |
+
"uk_UA"=> "Ukrainian (Ukraine)",
|
432 |
+
"uk"=> "Ukrainian",
|
433 |
+
"ur_IN"=> "Urdu (India)",
|
434 |
+
"ur_PK"=> "Urdu (Pakistan)",
|
435 |
+
"ur"=> "Urdu",
|
436 |
+
"uz_Arab"=> "Uzbek (Arabic)",
|
437 |
+
"uz_Arab_AF"=> "Uzbek (Arabic, Afghanistan)",
|
438 |
+
"uz_Cyrl"=> "Uzbek (Cyrillic)",
|
439 |
+
"uz_Cyrl_UZ"=> "Uzbek (Cyrillic, Uzbekistan)",
|
440 |
+
"uz_Latn"=> "Uzbek (Latin)",
|
441 |
+
"uz_Latn_UZ"=> "Uzbek (Latin, Uzbekistan)",
|
442 |
+
"uz"=> "Uzbek",
|
443 |
+
"vi_VN"=> "Vietnamese (Vietnam)",
|
444 |
+
"vi"=> "Vietnamese",
|
445 |
+
"vun_TZ"=> "Vunjo (Tanzania)",
|
446 |
+
"vun"=> "Vunjo",
|
447 |
+
"cy_GB"=> "Welsh (United Kingdom)",
|
448 |
+
"cy"=> "Welsh",
|
449 |
+
"yo_NG"=> "Yoruba (Nigeria)",
|
450 |
+
"yo"=> "Yoruba",
|
451 |
+
"zu_ZA"=> "Zulu (South Africa)",
|
452 |
+
"zu"=> "Zulu"
|
453 |
+
];
|
454 |
+
}
|
455 |
+
|
456 |
+
/**
|
457 |
+
* @return array
|
458 |
+
*/
|
459 |
+
public static function simple()
|
460 |
+
{
|
461 |
+
$response = array();
|
462 |
+
foreach (static::all() as $key => $value) {
|
463 |
+
if (!strpos($key, '_') > 0) {
|
464 |
+
$response[$key] = $value;
|
465 |
+
}
|
466 |
+
}
|
467 |
+
return $response;
|
468 |
+
}
|
469 |
+
}
|
includes/class-mailchimp-woocommerce-activator.php
ADDED
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Fired during plugin activation.
|
4 |
+
*
|
5 |
+
* This class defines all code necessary to run during the plugin's activation.
|
6 |
+
*
|
7 |
+
* @since 1.0.1
|
8 |
+
* @package MailChimp_Woocommerce
|
9 |
+
* @subpackage MailChimp_Woocommerce/includes
|
10 |
+
* @author Ryan Hungate <ryan@mailchimp.com>
|
11 |
+
*/
|
12 |
+
class MailChimp_Woocommerce_Activator {
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Short Description. (use period)
|
16 |
+
*
|
17 |
+
* Long Description.
|
18 |
+
*
|
19 |
+
* @since 1.0.0
|
20 |
+
*/
|
21 |
+
public static function activate() {
|
22 |
+
|
23 |
+
add_option('mailchimp_woocommerce_plugin_do_activation_redirect', true);
|
24 |
+
|
25 |
+
// create the queue tables because we need them for the sync jobs.
|
26 |
+
static::create_queue_tables();
|
27 |
+
|
28 |
+
// create the api keys for MC just in case.
|
29 |
+
$data = static::create_keys('MailChimp', 'USER_ID', 'read_write');
|
30 |
+
|
31 |
+
// update the settings so we have them for use.
|
32 |
+
update_option('mailchimp-woocommerce', array(
|
33 |
+
'woo_consumer_key' => $data['consumer_key'],
|
34 |
+
'woo_consumer_secret' => $data['consumer_secret']
|
35 |
+
));
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Create keys.
|
40 |
+
*
|
41 |
+
* @since 2.4.0
|
42 |
+
*
|
43 |
+
* @param string $app_name
|
44 |
+
* @param string $app_user_id
|
45 |
+
* @param string $scope
|
46 |
+
*
|
47 |
+
* @return array
|
48 |
+
*/
|
49 |
+
public static function create_keys( $app_name, $app_user_id, $scope ) {
|
50 |
+
global $wpdb;
|
51 |
+
|
52 |
+
$description = sprintf( __( '%s - API %s (created on %s at %s).', 'woocommerce' ), wc_clean( $app_name ), __( 'Read/Write', 'woocommerce' ), date_i18n( wc_date_format() ), date_i18n( wc_time_format() ) );
|
53 |
+
$user = wp_get_current_user();
|
54 |
+
|
55 |
+
// Created API keys.
|
56 |
+
$permissions = 'read_write';
|
57 |
+
$consumer_key = 'ck_' . wc_rand_hash();
|
58 |
+
$consumer_secret = 'cs_' . wc_rand_hash();
|
59 |
+
|
60 |
+
$wpdb->insert(
|
61 |
+
$wpdb->prefix . 'woocommerce_api_keys',
|
62 |
+
array(
|
63 |
+
'user_id' => $user->ID,
|
64 |
+
'description' => $description,
|
65 |
+
'permissions' => $permissions,
|
66 |
+
'consumer_key' => wc_api_hash( $consumer_key ),
|
67 |
+
'consumer_secret' => $consumer_secret,
|
68 |
+
'truncated_key' => substr( $consumer_key, -7 )
|
69 |
+
),
|
70 |
+
array('%d', '%s', '%s', '%s', '%s', '%s')
|
71 |
+
);
|
72 |
+
|
73 |
+
return array(
|
74 |
+
'key_id' => $wpdb->insert_id,
|
75 |
+
'user_id' => $app_user_id,
|
76 |
+
'consumer_key' => $consumer_key,
|
77 |
+
'consumer_secret' => $consumer_secret,
|
78 |
+
'key_permissions' => $permissions
|
79 |
+
);
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Create the queue tables in the DB so we can use it for syncing.
|
84 |
+
*/
|
85 |
+
public static function create_queue_tables()
|
86 |
+
{
|
87 |
+
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
88 |
+
|
89 |
+
global $wpdb;
|
90 |
+
|
91 |
+
$wpdb->hide_errors();
|
92 |
+
|
93 |
+
$charset_collate = $wpdb->get_charset_collate();
|
94 |
+
|
95 |
+
$sql = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}queue (
|
96 |
+
id bigint(20) NOT NULL AUTO_INCREMENT,
|
97 |
+
job text NOT NULL,
|
98 |
+
attempts tinyint(1) NOT NULL DEFAULT 0,
|
99 |
+
locked tinyint(1) NOT NULL DEFAULT 0,
|
100 |
+
locked_at datetime DEFAULT NULL,
|
101 |
+
available_at datetime NOT NULL,
|
102 |
+
created_at datetime NOT NULL,
|
103 |
+
PRIMARY KEY (id)
|
104 |
+
) $charset_collate;";
|
105 |
+
|
106 |
+
dbDelta( $sql );
|
107 |
+
|
108 |
+
$sql = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}failed_jobs (
|
109 |
+
id bigint(20) NOT NULL AUTO_INCREMENT,
|
110 |
+
job text NOT NULL,
|
111 |
+
failed_at datetime NOT NULL,
|
112 |
+
PRIMARY KEY (id)
|
113 |
+
) $charset_collate;";
|
114 |
+
|
115 |
+
dbDelta( $sql );
|
116 |
+
}
|
117 |
+
}
|
includes/class-mailchimp-woocommerce-deactivator.php
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Fired during plugin deactivation
|
5 |
+
*
|
6 |
+
* @link https://mailchimp.com
|
7 |
+
* @since 1.0.1
|
8 |
+
*
|
9 |
+
* @package MailChimp_Woocommerce
|
10 |
+
* @subpackage MailChimp_Woocommerce/includes
|
11 |
+
*/
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Fired during plugin deactivation.
|
15 |
+
*
|
16 |
+
* This class defines all code necessary to run during the plugin's deactivation.
|
17 |
+
*
|
18 |
+
* @since 1.0.1
|
19 |
+
* @package MailChimp_Woocommerce
|
20 |
+
* @subpackage MailChimp_Woocommerce/includes
|
21 |
+
* @author Ryan Hungate <ryan@mailchimp.com>
|
22 |
+
*/
|
23 |
+
class MailChimp_Woocommerce_Deactivator {
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Short Description. (use period)
|
27 |
+
*
|
28 |
+
* Long Description.
|
29 |
+
*
|
30 |
+
* @since 1.0.0
|
31 |
+
*/
|
32 |
+
public static function deactivate() {
|
33 |
+
// if the api is valid, we need to try to delete the store
|
34 |
+
if (($api = mailchimp_get_api())) {
|
35 |
+
$api->deleteStore(mailchimp_get_store_id());
|
36 |
+
}
|
37 |
+
}
|
38 |
+
|
39 |
+
}
|
includes/class-mailchimp-woocommerce-i18n.php
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Define the internationalization functionality
|
5 |
+
*
|
6 |
+
* Loads and defines the internationalization files for this plugin
|
7 |
+
* so that it is ready for translation.
|
8 |
+
*
|
9 |
+
* @link https://mailchimp.com
|
10 |
+
* @since 1.0.1
|
11 |
+
*
|
12 |
+
* @package MailChimp_Woocommerce
|
13 |
+
* @subpackage MailChimp_Woocommerce/includes
|
14 |
+
*/
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Define the internationalization functionality.
|
18 |
+
*
|
19 |
+
* Loads and defines the internationalization files for this plugin
|
20 |
+
* so that it is ready for translation.
|
21 |
+
*
|
22 |
+
* @since 1.0.1
|
23 |
+
* @package MailChimp_Woocommerce
|
24 |
+
* @subpackage MailChimp_Woocommerce/includes
|
25 |
+
* @author Ryan Hungate <ryan@mailchimp.com>
|
26 |
+
*/
|
27 |
+
class MailChimp_Woocommerce_i18n {
|
28 |
+
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Load the plugin text domain for translation.
|
32 |
+
*
|
33 |
+
* @since 1.0.0
|
34 |
+
*/
|
35 |
+
public function load_plugin_textdomain() {
|
36 |
+
|
37 |
+
load_plugin_textdomain(
|
38 |
+
'mailchimp-woocommerce',
|
39 |
+
false,
|
40 |
+
dirname( dirname( plugin_basename( __FILE__ ) ) ) . '/languages/'
|
41 |
+
);
|
42 |
+
|
43 |
+
}
|
44 |
+
|
45 |
+
|
46 |
+
|
47 |
+
}
|
includes/class-mailchimp-woocommerce-loader.php
ADDED
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Register all actions and filters for the plugin
|
5 |
+
*
|
6 |
+
* @link https://mailchimp.com
|
7 |
+
* @since 1.0.1
|
8 |
+
*
|
9 |
+
* @package MailChimp_Woocommerce
|
10 |
+
* @subpackage MailChimp_Woocommerce/includes
|
11 |
+
*/
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Register all actions and filters for the plugin.
|
15 |
+
*
|
16 |
+
* Maintain a list of all hooks that are registered throughout
|
17 |
+
* the plugin, and register them with the WordPress API. Call the
|
18 |
+
* run function to execute the list of actions and filters.
|
19 |
+
*
|
20 |
+
* @package MailChimp_Woocommerce
|
21 |
+
* @subpackage MailChimp_Woocommerce/includes
|
22 |
+
* @author Ryan Hungate <ryan@mailchimp.com>
|
23 |
+
*/
|
24 |
+
class MailChimp_Woocommerce_Loader {
|
25 |
+
|
26 |
+
/**
|
27 |
+
* The array of actions registered with WordPress.
|
28 |
+
*
|
29 |
+
* @since 1.0.0
|
30 |
+
* @access protected
|
31 |
+
* @var array $actions The actions registered with WordPress to fire when the plugin loads.
|
32 |
+
*/
|
33 |
+
protected $actions;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* The array of filters registered with WordPress.
|
37 |
+
*
|
38 |
+
* @since 1.0.0
|
39 |
+
* @access protected
|
40 |
+
* @var array $filters The filters registered with WordPress to fire when the plugin loads.
|
41 |
+
*/
|
42 |
+
protected $filters;
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Initialize the collections used to maintain the actions and filters.
|
46 |
+
*
|
47 |
+
* @since 1.0.0
|
48 |
+
*/
|
49 |
+
public function __construct() {
|
50 |
+
|
51 |
+
$this->actions = array();
|
52 |
+
$this->filters = array();
|
53 |
+
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Add a new action to the collection to be registered with WordPress.
|
58 |
+
*
|
59 |
+
* @since 1.0.0
|
60 |
+
* @param string $hook The name of the WordPress action that is being registered.
|
61 |
+
* @param object $component A reference to the instance of the object on which the action is defined.
|
62 |
+
* @param string $callback The name of the function definition on the $component.
|
63 |
+
* @param int $priority Optional. he priority at which the function should be fired. Default is 10.
|
64 |
+
* @param int $accepted_args Optional. The number of arguments that should be passed to the $callback. Default is 1.
|
65 |
+
*/
|
66 |
+
public function add_action( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) {
|
67 |
+
$this->actions = $this->add( $this->actions, $hook, $component, $callback, $priority, $accepted_args );
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Add a new filter to the collection to be registered with WordPress.
|
72 |
+
*
|
73 |
+
* @since 1.0.0
|
74 |
+
* @param string $hook The name of the WordPress filter that is being registered.
|
75 |
+
* @param object $component A reference to the instance of the object on which the filter is defined.
|
76 |
+
* @param string $callback The name of the function definition on the $component.
|
77 |
+
* @param int $priority Optional. he priority at which the function should be fired. Default is 10.
|
78 |
+
* @param int $accepted_args Optional. The number of arguments that should be passed to the $callback. Default is 1
|
79 |
+
*/
|
80 |
+
public function add_filter( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) {
|
81 |
+
$this->filters = $this->add( $this->filters, $hook, $component, $callback, $priority, $accepted_args );
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* A utility function that is used to register the actions and hooks into a single
|
86 |
+
* collection.
|
87 |
+
*
|
88 |
+
* @since 1.0.0
|
89 |
+
* @access private
|
90 |
+
* @param array $hooks The collection of hooks that is being registered (that is, actions or filters).
|
91 |
+
* @param string $hook The name of the WordPress filter that is being registered.
|
92 |
+
* @param object $component A reference to the instance of the object on which the filter is defined.
|
93 |
+
* @param string $callback The name of the function definition on the $component.
|
94 |
+
* @param int $priority The priority at which the function should be fired.
|
95 |
+
* @param int $accepted_args The number of arguments that should be passed to the $callback.
|
96 |
+
* @return array The collection of actions and filters registered with WordPress.
|
97 |
+
*/
|
98 |
+
private function add( $hooks, $hook, $component, $callback, $priority, $accepted_args ) {
|
99 |
+
|
100 |
+
$hooks[] = array(
|
101 |
+
'hook' => $hook,
|
102 |
+
'component' => $component,
|
103 |
+
'callback' => $callback,
|
104 |
+
'priority' => $priority,
|
105 |
+
'accepted_args' => $accepted_args
|
106 |
+
);
|
107 |
+
|
108 |
+
return $hooks;
|
109 |
+
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Register the filters and actions with WordPress.
|
114 |
+
*
|
115 |
+
* @since 1.0.0
|
116 |
+
*/
|
117 |
+
public function run() {
|
118 |
+
|
119 |
+
foreach ( $this->filters as $hook ) {
|
120 |
+
add_filter( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] );
|
121 |
+
}
|
122 |
+
|
123 |
+
foreach ( $this->actions as $hook ) {
|
124 |
+
add_action( $hook['hook'], array( $hook['component'], $hook['callback'] ), $hook['priority'], $hook['accepted_args'] );
|
125 |
+
}
|
126 |
+
|
127 |
+
}
|
128 |
+
|
129 |
+
}
|
includes/class-mailchimp-woocommerce-newsletter.php
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by MailChimp.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 2/22/16
|
9 |
+
* Time: 9:09 AM
|
10 |
+
*/
|
11 |
+
class MailChimp_Newsletter extends MailChimp_Woocommerce_Options
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* @param WC_Checkout $checkout
|
15 |
+
*/
|
16 |
+
public function applyNewsletterField($checkout)
|
17 |
+
{
|
18 |
+
if (!is_admin()) {
|
19 |
+
$status = is_user_logged_in() ? get_user_meta(get_current_user_id(), 'mailchimp_woocommerce_is_subscribed',
|
20 |
+
true) : true;
|
21 |
+
|
22 |
+
$checkbox = '<p class="form-row form-row-wide create-account">';
|
23 |
+
$checkbox .= '<input class="input-checkbox" id="mailchimp_woocommerce_newsletter" type="checkbox" name="mailchimp_woocommerce_newsletter" value="1" checked="' . ($status ? 'checked' : '') . '"> ';
|
24 |
+
$checkbox .= '<label for="mailchimp_woocommerce_newsletter" class="checkbox">' . $this->getOption('newsletter_label',
|
25 |
+
'Subscribe to our newsletter') . '</label></p>';
|
26 |
+
$checkbox .= '<div class="clear"></div>';
|
27 |
+
|
28 |
+
echo $checkbox;
|
29 |
+
}
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* @param $order_id
|
34 |
+
* @param $posted
|
35 |
+
*/
|
36 |
+
public function processNewsletterField($order_id, $posted)
|
37 |
+
{
|
38 |
+
$this->handleStatus($order_id);
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* @param WC_Order $order
|
43 |
+
*/
|
44 |
+
public function processPayPalNewsletterField($order)
|
45 |
+
{
|
46 |
+
$this->handleStatus($order->id);
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* @param $sanitized_user_login
|
51 |
+
* @param $user_email
|
52 |
+
* @param $reg_errors
|
53 |
+
*/
|
54 |
+
public function processRegistrationForm($sanitized_user_login, $user_email, $reg_errors)
|
55 |
+
{
|
56 |
+
|
57 |
+
if (defined('WOOCOMMERCE_CHECKOUT')) {
|
58 |
+
return; // Ship checkout
|
59 |
+
}
|
60 |
+
$this->handleStatus();
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* @param null $order_id
|
65 |
+
* @return bool|int
|
66 |
+
*/
|
67 |
+
protected function handleStatus($order_id = null)
|
68 |
+
{
|
69 |
+
$status = isset($_POST['mailchimp_woocommerce_newsletter']) ? (int)$_POST['mailchimp_woocommerce_newsletter'] : 0;
|
70 |
+
|
71 |
+
if ($order_id) {
|
72 |
+
update_post_meta($order_id, 'mailchimp_woocommerce_is_subscribed', $status);
|
73 |
+
}
|
74 |
+
|
75 |
+
if (is_user_logged_in()) {
|
76 |
+
update_user_meta(get_current_user_id(), 'mailchimp_woocommerce_is_subscribed', $status);
|
77 |
+
|
78 |
+
return $status;
|
79 |
+
}
|
80 |
+
|
81 |
+
return false;
|
82 |
+
}
|
83 |
+
}
|
includes/class-mailchimp-woocommerce-options.php
ADDED
@@ -0,0 +1,238 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by MailChimp.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 2/22/16
|
9 |
+
* Time: 3:45 PM
|
10 |
+
*/
|
11 |
+
abstract class MailChimp_Woocommerce_Options
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* @var MailChimpApi
|
15 |
+
*/
|
16 |
+
protected $api;
|
17 |
+
protected $plugin_name = 'mailchimp-woocommerce';
|
18 |
+
protected $environment = 'production';
|
19 |
+
protected $version = '1.0.0';
|
20 |
+
protected $plugin_options = null;
|
21 |
+
protected $is_admin = false;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* hook calls this so that we know the admin is here.
|
25 |
+
*/
|
26 |
+
public function adminReady()
|
27 |
+
{
|
28 |
+
$this->is_admin = current_user_can('administrator');
|
29 |
+
if (get_option('mailchimp_woocommerce_plugin_do_activation_redirect', false)) {
|
30 |
+
delete_option('mailchimp_woocommerce_plugin_do_activation_redirect');
|
31 |
+
if (!isset($_GET['activate-multi'])) {
|
32 |
+
wp_redirect("options-general.php?page=mailchimp-woocommerce");
|
33 |
+
}
|
34 |
+
}
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* @return bool
|
39 |
+
*/
|
40 |
+
public function isAdmin()
|
41 |
+
{
|
42 |
+
return $this->is_admin;
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* @param $version
|
47 |
+
*/
|
48 |
+
public function setVersion($version)
|
49 |
+
{
|
50 |
+
$this->version = $version;
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* @return string
|
55 |
+
*/
|
56 |
+
public function getVersion()
|
57 |
+
{
|
58 |
+
return $this->version;
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* @return string
|
63 |
+
*/
|
64 |
+
public function getUniqueStoreID()
|
65 |
+
{
|
66 |
+
return md5(get_option('siteurl'));
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* @param $env
|
71 |
+
* @return $this
|
72 |
+
*/
|
73 |
+
public function setEnvironment($env)
|
74 |
+
{
|
75 |
+
$this->environment = $env;
|
76 |
+
return $this;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* @return string
|
81 |
+
*/
|
82 |
+
public function getEnvironment()
|
83 |
+
{
|
84 |
+
return $this->environment;
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* @param $key
|
89 |
+
* @param null $default
|
90 |
+
* @return null
|
91 |
+
*/
|
92 |
+
public function getOption($key, $default = null)
|
93 |
+
{
|
94 |
+
$options = $this->getOptions();
|
95 |
+
if (isset($options[$key])) {
|
96 |
+
return $options[$key];
|
97 |
+
}
|
98 |
+
return $default;
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* @param $key
|
103 |
+
* @param bool $default
|
104 |
+
* @return bool
|
105 |
+
*/
|
106 |
+
public function hasOption($key, $default = false)
|
107 |
+
{
|
108 |
+
return (bool) $this->getOption($key, $default);
|
109 |
+
}
|
110 |
+
|
111 |
+
/**
|
112 |
+
* @return array
|
113 |
+
*/
|
114 |
+
public function getOptions()
|
115 |
+
{
|
116 |
+
if (empty($this->plugin_options)) {
|
117 |
+
$this->plugin_options = get_option($this->plugin_name);
|
118 |
+
}
|
119 |
+
return is_array($this->plugin_options) ? $this->plugin_options : array();
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* @param $key
|
124 |
+
* @param $value
|
125 |
+
* @return $this
|
126 |
+
*/
|
127 |
+
public function setData($key, $value)
|
128 |
+
{
|
129 |
+
update_option($this->plugin_name.'-'.$key, $value);
|
130 |
+
return $this;
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* @param $key
|
135 |
+
* @param null $default
|
136 |
+
* @return mixed|void
|
137 |
+
*/
|
138 |
+
public function getData($key, $default = null)
|
139 |
+
{
|
140 |
+
return get_option($this->plugin_name.'-'.$key, $default);
|
141 |
+
}
|
142 |
+
|
143 |
+
|
144 |
+
/**
|
145 |
+
* @param $key
|
146 |
+
* @return bool
|
147 |
+
*/
|
148 |
+
public function removeData($key)
|
149 |
+
{
|
150 |
+
return delete_option($this->plugin_name.'-'.$key);
|
151 |
+
}
|
152 |
+
|
153 |
+
/**
|
154 |
+
* @param $key
|
155 |
+
* @param null $default
|
156 |
+
* @return null|mixed
|
157 |
+
*/
|
158 |
+
public function getCached($key, $default = null)
|
159 |
+
{
|
160 |
+
$cached = $this->getData("cached-$key", false);
|
161 |
+
if (empty($cached) || !($cached = unserialize($cached))) {
|
162 |
+
return $default;
|
163 |
+
}
|
164 |
+
|
165 |
+
if (empty($cached['till']) || (time() > $cached['till'])) {
|
166 |
+
$this->removeData("cached-$key");
|
167 |
+
return $default;
|
168 |
+
}
|
169 |
+
|
170 |
+
return $cached['value'];
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* @param $key
|
175 |
+
* @param $value
|
176 |
+
* @param $seconds
|
177 |
+
* @return $this
|
178 |
+
*/
|
179 |
+
public function setCached($key, $value, $seconds = 60)
|
180 |
+
{
|
181 |
+
$time = time();
|
182 |
+
$data = array('at' => $time, 'till' => $time + $seconds, 'value' => $value);
|
183 |
+
$this->setData("cached-$key", serialize($data));
|
184 |
+
|
185 |
+
return $this;
|
186 |
+
}
|
187 |
+
|
188 |
+
/**
|
189 |
+
* @param $key
|
190 |
+
* @param $callable
|
191 |
+
* @param int $seconds
|
192 |
+
* @return mixed|null
|
193 |
+
*/
|
194 |
+
public function getCachedWithSetDefault($key, $callable, $seconds = 60)
|
195 |
+
{
|
196 |
+
if (!($value = $this->getCached($key, false))) {
|
197 |
+
$value = call_user_func($callable);
|
198 |
+
$this->setCached($key, $value, $seconds);
|
199 |
+
}
|
200 |
+
return $value;
|
201 |
+
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* @return bool
|
205 |
+
*/
|
206 |
+
public function isConfigured()
|
207 |
+
{
|
208 |
+
return true;
|
209 |
+
//return $this->getOption('public_key', false) && $this->getOption('secret_key', false);
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* @return MailChimpApi
|
214 |
+
*/
|
215 |
+
public function api()
|
216 |
+
{
|
217 |
+
if (empty($this->api)) {
|
218 |
+
$this->api = new MailChimpApi($this->getOption('mailchimp_api_key', false));
|
219 |
+
}
|
220 |
+
|
221 |
+
return $this->api;
|
222 |
+
}
|
223 |
+
|
224 |
+
/**
|
225 |
+
* @param array $data
|
226 |
+
* @param $key
|
227 |
+
* @param null $default
|
228 |
+
* @return null|mixed
|
229 |
+
*/
|
230 |
+
public function array_get(array $data, $key, $default = null)
|
231 |
+
{
|
232 |
+
if (isset($data[$key])) {
|
233 |
+
return $data[$key];
|
234 |
+
}
|
235 |
+
|
236 |
+
return $default;
|
237 |
+
}
|
238 |
+
}
|
includes/class-mailchimp-woocommerce-service.php
ADDED
@@ -0,0 +1,492 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by MailChimp.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 2/17/16
|
9 |
+
* Time: 12:03 PM
|
10 |
+
*/
|
11 |
+
class MailChimp_Service extends MailChimp_Woocommerce_Options
|
12 |
+
{
|
13 |
+
protected $user_email = null;
|
14 |
+
protected $previous_email = null;
|
15 |
+
protected $force_cart_post = false;
|
16 |
+
protected $pushed_orders = array();
|
17 |
+
protected $cart_was_submitted = false;
|
18 |
+
protected $cart = array();
|
19 |
+
|
20 |
+
/**
|
21 |
+
* hook fired when we know everything is booted
|
22 |
+
*/
|
23 |
+
public function wooIsRunning()
|
24 |
+
{
|
25 |
+
$this->handleAdminFunctions();
|
26 |
+
$this->is_admin = current_user_can('administrator');
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @param $r
|
31 |
+
* @param $url
|
32 |
+
* @return mixed
|
33 |
+
*/
|
34 |
+
public function addHttpRequestArgs( $r, $url ) {
|
35 |
+
// not sure whether or not we need to implement something like this yet.
|
36 |
+
//$r['headers']['Authorization'] = 'Basic ' . base64_encode('username:password');
|
37 |
+
return $r;
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* @param $key
|
42 |
+
* @param $default
|
43 |
+
* @return mixed
|
44 |
+
*/
|
45 |
+
protected function cookie($key, $default = null)
|
46 |
+
{
|
47 |
+
if ($this->is_admin) {
|
48 |
+
return $default;
|
49 |
+
}
|
50 |
+
|
51 |
+
return isset($_COOKIE[$key]) ? $_COOKIE[$key] : $default;
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* @param $order_id
|
56 |
+
*/
|
57 |
+
public function handleOrderStatusChanged($order_id)
|
58 |
+
{
|
59 |
+
if ($this->hasOption('mailchimp_api_key') && !array_key_exists($order_id, $this->pushed_orders)) {
|
60 |
+
|
61 |
+
// register this order is already in process..
|
62 |
+
$this->pushed_orders[$order_id] = true;
|
63 |
+
|
64 |
+
// see if we have a session id and a campaign id, also only do this when this user is not the admin.
|
65 |
+
$campaign_id = $this->getCampaignTrackingID();
|
66 |
+
|
67 |
+
// queue up the single order to be processed.
|
68 |
+
$handler = new MailChimp_WooCommerce_Single_Order($order_id, null, $campaign_id);
|
69 |
+
wp_queue($handler);
|
70 |
+
}
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* @return bool|void
|
75 |
+
*/
|
76 |
+
public function handleCartUpdated()
|
77 |
+
{
|
78 |
+
if ($this->is_admin || $this->cart_was_submitted || !$this->hasOption('mailchimp_api_key')) {
|
79 |
+
return false;
|
80 |
+
}
|
81 |
+
|
82 |
+
if (empty($this->cart)) {
|
83 |
+
$this->cart = $this->getCartItems();
|
84 |
+
}
|
85 |
+
|
86 |
+
if (($user_email = $this->getCurrentUserEmail())) {
|
87 |
+
|
88 |
+
$previous = $this->getPreviousEmailFromSession();
|
89 |
+
|
90 |
+
$uid = md5(trim(strtolower($user_email)));
|
91 |
+
|
92 |
+
// delete the previous records.
|
93 |
+
if (!empty($previous) && $previous !== $user_email) {
|
94 |
+
if ($this->api()->deleteCartByID($this->getUniqueStoreID(), $previous_email = md5(trim(strtolower($previous))))) {
|
95 |
+
mailchimp_log('ac.cart_swap', "Deleted cart [$previous] :: ID [$previous_email]");
|
96 |
+
}
|
97 |
+
}
|
98 |
+
|
99 |
+
if ($this->cart && !empty($this->cart)) {
|
100 |
+
|
101 |
+
$this->cart_was_submitted = true;
|
102 |
+
|
103 |
+
// grab the cookie data that could play important roles in the submission
|
104 |
+
$campaign = $this->getCampaignTrackingID();
|
105 |
+
|
106 |
+
// fire up the job handler
|
107 |
+
$handler = new MailChimp_WooCommerce_Cart_Update($uid, $user_email, $campaign, $this->cart);
|
108 |
+
wp_queue($handler);
|
109 |
+
}
|
110 |
+
|
111 |
+
return true;
|
112 |
+
}
|
113 |
+
|
114 |
+
return false;
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Save post metadata when a post is saved.
|
119 |
+
*
|
120 |
+
* @param int $post_id The post ID.
|
121 |
+
* @param WP_Post $post The post object.
|
122 |
+
* @param bool $update Whether this is an existing post being updated or not.
|
123 |
+
*/
|
124 |
+
public function handlePostSaved($post_id, $post, $update) {
|
125 |
+
if ('product' == $post->post_type) {
|
126 |
+
wp_queue(new MailChimp_WooCommerce_Single_Product($post_id), 5);
|
127 |
+
} elseif ('shop_order' == $post->post_type) {
|
128 |
+
wp_queue(new MailChimp_WooCommerce_Single_Order($post_id, null, null));
|
129 |
+
}
|
130 |
+
}
|
131 |
+
|
132 |
+
/**
|
133 |
+
* @return bool|string
|
134 |
+
*/
|
135 |
+
public function getCurrentUserEmail()
|
136 |
+
{
|
137 |
+
if (isset($this->user_email) && !empty($this->user_email)) {
|
138 |
+
return $this->user_email = strtolower($this->user_email);
|
139 |
+
}
|
140 |
+
|
141 |
+
$user = wp_get_current_user();
|
142 |
+
$email = ($user->ID > 0 && isset($user->user_email)) ? $user->user_email : $this->getEmailFromSession();
|
143 |
+
|
144 |
+
return $this->user_email = strtolower($email);
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* @return bool|array
|
149 |
+
*/
|
150 |
+
public function getCartItems() {
|
151 |
+
if (!($this->cart = $this->getWooSession('cart', false))) {
|
152 |
+
$this->cart = WC()->cart->get_cart();
|
153 |
+
} else {
|
154 |
+
$cart_session = array();
|
155 |
+
foreach ( $this->cart as $key => $values ) {
|
156 |
+
$cart_session[$key] = $values;
|
157 |
+
unset($cart_session[$key]['data']); // Unset product object
|
158 |
+
}
|
159 |
+
return $this->cart = $cart_session;
|
160 |
+
}
|
161 |
+
|
162 |
+
return is_array($this->cart) ? $this->cart : false;
|
163 |
+
}
|
164 |
+
|
165 |
+
/**
|
166 |
+
* Set the cookie of the mailchimp campaigns if we have one.
|
167 |
+
*/
|
168 |
+
public function handleCampaignTracking()
|
169 |
+
{
|
170 |
+
$cookie_duration = $this->getCookieDuration();
|
171 |
+
|
172 |
+
if (isset($_REQUEST['mc_cid'])) {
|
173 |
+
$this->setCampaignTrackingID($_REQUEST['mc_cid'], $cookie_duration);
|
174 |
+
}
|
175 |
+
|
176 |
+
if (isset($_REQUEST['mc_eid'])) {
|
177 |
+
@setcookie('mailchimp_email_id', trim($_REQUEST['mc_eid']), $cookie_duration, '/' );
|
178 |
+
}
|
179 |
+
}
|
180 |
+
|
181 |
+
/**
|
182 |
+
* @return mixed|null
|
183 |
+
*/
|
184 |
+
public function getCampaignTrackingID()
|
185 |
+
{
|
186 |
+
$cookie = $this->cookie('mailchimp_campaign_id', false);
|
187 |
+
if (empty($cookie)) {
|
188 |
+
$cookie = $this->getWooSession('mailchimp_tracking_id', false);
|
189 |
+
}
|
190 |
+
return $cookie;
|
191 |
+
}
|
192 |
+
|
193 |
+
/**
|
194 |
+
* @param $id
|
195 |
+
* @param $cookie_duration
|
196 |
+
* @return $this
|
197 |
+
*/
|
198 |
+
public function setCampaignTrackingID($id, $cookie_duration)
|
199 |
+
{
|
200 |
+
$cid = trim($id);
|
201 |
+
|
202 |
+
@setcookie('mailchimp_campaign_id', $cid, $cookie_duration, '/' );
|
203 |
+
$this->setWooSession('mailchimp_campaign_id', $cid);
|
204 |
+
|
205 |
+
return $this;
|
206 |
+
}
|
207 |
+
|
208 |
+
/**
|
209 |
+
* @return bool
|
210 |
+
*/
|
211 |
+
protected function getEmailFromSession()
|
212 |
+
{
|
213 |
+
return $this->cookie('mailchimp_user_email', false);
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
* @return bool
|
218 |
+
*/
|
219 |
+
protected function getPreviousEmailFromSession()
|
220 |
+
{
|
221 |
+
if ($this->previous_email) {
|
222 |
+
return $this->previous_email = strtolower($this->previous_email);
|
223 |
+
}
|
224 |
+
$email = $this->cookie('mailchimp_user_previous_email', false);
|
225 |
+
return $email ? strtolower($email) : false;
|
226 |
+
}
|
227 |
+
|
228 |
+
/**
|
229 |
+
* @param $key
|
230 |
+
* @param null $default
|
231 |
+
* @return mixed|null
|
232 |
+
*/
|
233 |
+
public function getWooSession($key, $default = null)
|
234 |
+
{
|
235 |
+
if (!($woo = WC()) || empty($woo->session)) {
|
236 |
+
return $default;
|
237 |
+
}
|
238 |
+
return $woo->session->get($key, $default);
|
239 |
+
}
|
240 |
+
|
241 |
+
/**
|
242 |
+
* @param $key
|
243 |
+
* @param $value
|
244 |
+
* @return $this
|
245 |
+
*/
|
246 |
+
public function setWooSession($key, $value)
|
247 |
+
{
|
248 |
+
if (!($woo = WC()) || empty($woo->session)) {
|
249 |
+
return $this;
|
250 |
+
}
|
251 |
+
|
252 |
+
$woo->session->set($key, $value);
|
253 |
+
|
254 |
+
return $this;
|
255 |
+
}
|
256 |
+
|
257 |
+
/**
|
258 |
+
* @param $key
|
259 |
+
* @return $this
|
260 |
+
*/
|
261 |
+
public function removeWooSession($key)
|
262 |
+
{
|
263 |
+
if (!($woo = WC()) || empty($woo->session)) {
|
264 |
+
return $this;
|
265 |
+
}
|
266 |
+
|
267 |
+
$woo->session->__unset($key);
|
268 |
+
return $this;
|
269 |
+
}
|
270 |
+
|
271 |
+
/**
|
272 |
+
* @param string $time
|
273 |
+
* @return int
|
274 |
+
*/
|
275 |
+
protected function getCookieDuration($time = 'thirty_days')
|
276 |
+
{
|
277 |
+
$durations = array(
|
278 |
+
'one_day' => 86400, 'seven_days' => 604800, 'fourteen_days' => 1209600, 'thirty_days' => 2419200,
|
279 |
+
);
|
280 |
+
|
281 |
+
if (!array_key_exists($time, $durations)) {
|
282 |
+
$time = 'thirty_days';
|
283 |
+
}
|
284 |
+
|
285 |
+
return time() + $durations[$time];
|
286 |
+
}
|
287 |
+
|
288 |
+
/**
|
289 |
+
* Just a wrapper to call various methods from MailChimp to the store.
|
290 |
+
* Authentication is based on the secret keys being correct or it will fail.
|
291 |
+
*
|
292 |
+
* The get requests need:
|
293 |
+
* 1. mailchimp-woocommerce[action]
|
294 |
+
* 2. mailchimp-woocommerce[submission]
|
295 |
+
* 3. various other parts based on the api call.
|
296 |
+
*/
|
297 |
+
protected function handleAdminFunctions()
|
298 |
+
{
|
299 |
+
if (isset($_GET['reset_cookies'])) {
|
300 |
+
$buster = time()-300;
|
301 |
+
|
302 |
+
setcookie('mailchimp_user_previous_email', '', $buster);
|
303 |
+
setcookie('mailchimp_user_email', '', $buster);
|
304 |
+
setcookie('mailchimp_campaign_id', '', $buster);
|
305 |
+
setcookie('mailchimp_email_id', '', $buster);
|
306 |
+
|
307 |
+
$this->previous_email = null;
|
308 |
+
$this->user_email = null;
|
309 |
+
}
|
310 |
+
|
311 |
+
$methods = array(
|
312 |
+
'plugin-version' => 'respondAdminGetPluginVersion',
|
313 |
+
'submit-email' => 'respondAdminSubmitEmail',
|
314 |
+
'track-campaign' => 'respondAdminTrackCampaign',
|
315 |
+
'get-tracking-data' => 'respondAdminGetTrackingData',
|
316 |
+
'verify' => 'respondAdminVerify',
|
317 |
+
);
|
318 |
+
|
319 |
+
if (($action = $this->get('action'))) {
|
320 |
+
|
321 |
+
if ($action === 'sync') {
|
322 |
+
return $this->sync();
|
323 |
+
}
|
324 |
+
|
325 |
+
if (array_key_exists($action, $methods)) {
|
326 |
+
if (!in_array($action, array('submit-email', 'track-campaign', 'get-tracking-data'))) {
|
327 |
+
$this->authenticate();
|
328 |
+
}
|
329 |
+
$this->respondJSON($this->{$methods[$action]}());
|
330 |
+
}
|
331 |
+
}
|
332 |
+
}
|
333 |
+
|
334 |
+
/**
|
335 |
+
* Delete all the options pointing to the pages, and re-start the sync process.
|
336 |
+
* @return void
|
337 |
+
*/
|
338 |
+
protected function sync()
|
339 |
+
{
|
340 |
+
// only do this if we're an admin user.
|
341 |
+
if ($this->isAdmin()) {
|
342 |
+
|
343 |
+
delete_option('mailchimp-woocommerce-errors.store_info');
|
344 |
+
delete_option('mailchimp-woocommerce-sync.orders.completed_at');
|
345 |
+
delete_option('mailchimp-woocommerce-sync.orders.current_page');
|
346 |
+
delete_option('mailchimp-woocommerce-sync.products.completed_at');
|
347 |
+
delete_option('mailchimp-woocommerce-sync.products.current_page');
|
348 |
+
delete_option('mailchimp-woocommerce-sync.syncing');
|
349 |
+
delete_option('mailchimp-woocommerce-sync.started_at');
|
350 |
+
delete_option('mailchimp-woocommerce-sync.completed_at');
|
351 |
+
delete_option('mailchimp-woocommerce-validation.api.ping');
|
352 |
+
delete_option('mailchimp-woocommerce-cached-api-lists');
|
353 |
+
delete_option('mailchimp-woocommerce-cached-api-ping-check');
|
354 |
+
|
355 |
+
$job = new MailChimp_WooCommerce_Process_Products();
|
356 |
+
$job->flagStartSync();
|
357 |
+
wp_queue($job);
|
358 |
+
|
359 |
+
wp_redirect('/options-general.php?page=mailchimp-woocommerce&tab=api_key&success_notice=re-sync-started');
|
360 |
+
}
|
361 |
+
|
362 |
+
return;
|
363 |
+
}
|
364 |
+
|
365 |
+
/**
|
366 |
+
* @return array
|
367 |
+
*/
|
368 |
+
protected function respondAdminGetPluginVersion()
|
369 |
+
{
|
370 |
+
return array('success' => true, 'version' => $this->getVersion());
|
371 |
+
}
|
372 |
+
|
373 |
+
/**
|
374 |
+
* @return array
|
375 |
+
*/
|
376 |
+
protected function respondAdminVerify()
|
377 |
+
{
|
378 |
+
return array('success' => true);
|
379 |
+
}
|
380 |
+
|
381 |
+
/**
|
382 |
+
* @return array
|
383 |
+
*/
|
384 |
+
protected function respondAdminSubmitEmail()
|
385 |
+
{
|
386 |
+
if ($this->is_admin) {
|
387 |
+
return array('success' => false);
|
388 |
+
}
|
389 |
+
|
390 |
+
$submission = $this->get('submission');
|
391 |
+
|
392 |
+
if (is_array($submission) && isset($submission['email'])) {
|
393 |
+
|
394 |
+
$cookie_duration = $this->getCookieDuration();
|
395 |
+
|
396 |
+
$this->user_email = trim(str_replace(' ','+', $submission['email']));
|
397 |
+
|
398 |
+
if (($current_email = $this->getEmailFromSession()) && $current_email !== $this->user_email) {
|
399 |
+
$this->previous_email = $current_email;
|
400 |
+
$this->force_cart_post = true;
|
401 |
+
@setcookie('mailchimp_user_previous_email',$this->user_email, $cookie_duration, '/' );
|
402 |
+
}
|
403 |
+
|
404 |
+
@setcookie('mailchimp_user_email', $this->user_email, $cookie_duration, '/' );
|
405 |
+
|
406 |
+
$this->getCartItems();
|
407 |
+
|
408 |
+
$this->handleCartUpdated();
|
409 |
+
|
410 |
+
return array(
|
411 |
+
'success' => true,
|
412 |
+
'email' => $this->user_email,
|
413 |
+
'previous' => $this->previous_email,
|
414 |
+
'cart' => $this->cart,
|
415 |
+
);
|
416 |
+
}
|
417 |
+
return array('success' => false);
|
418 |
+
}
|
419 |
+
|
420 |
+
/**
|
421 |
+
* @return array
|
422 |
+
*/
|
423 |
+
protected function respondAdminTrackCampaign()
|
424 |
+
{
|
425 |
+
if ($this->is_admin) {
|
426 |
+
return array('success' => false);
|
427 |
+
}
|
428 |
+
|
429 |
+
$submission = $this->get('submission');
|
430 |
+
|
431 |
+
if (is_array($submission) && isset($submission['campaign_id'])) {
|
432 |
+
|
433 |
+
$duration = $this->getCookieDuration();
|
434 |
+
|
435 |
+
$campaign_id = trim($submission['campaign_id']);
|
436 |
+
$email_id = trim($submission['email_id']);
|
437 |
+
|
438 |
+
@setcookie('mailchimp_campaign_id', $campaign_id, $duration, '/');
|
439 |
+
@setcookie('mailchimp_email_id', $email_id, $duration, '/');
|
440 |
+
|
441 |
+
return $this->respondAdminGetTrackingData();
|
442 |
+
}
|
443 |
+
return array('success' => false);
|
444 |
+
}
|
445 |
+
|
446 |
+
/**
|
447 |
+
* @return array
|
448 |
+
*/
|
449 |
+
protected function respondAdminGetTrackingData()
|
450 |
+
{
|
451 |
+
return array(
|
452 |
+
'success' => true,
|
453 |
+
'campaign_id' => $this->cookie('mailchimp_campaign_id', 'n/a'),
|
454 |
+
'email_id' => $this->cookie('mailchimp_email_id', 'n/a')
|
455 |
+
);
|
456 |
+
}
|
457 |
+
|
458 |
+
/**
|
459 |
+
* @param $key
|
460 |
+
* @param bool $default
|
461 |
+
* @return bool
|
462 |
+
*/
|
463 |
+
protected function get($key, $default = false)
|
464 |
+
{
|
465 |
+
if (!isset($_REQUEST['mailchimp-woocommerce']) || !isset($_REQUEST['mailchimp-woocommerce'][$key])) {
|
466 |
+
return $default;
|
467 |
+
}
|
468 |
+
return $_REQUEST['mailchimp-woocommerce'][$key];
|
469 |
+
}
|
470 |
+
|
471 |
+
/**
|
472 |
+
* @return bool
|
473 |
+
*/
|
474 |
+
protected function authenticate()
|
475 |
+
{
|
476 |
+
if (trim((string) $this->getUniqueStoreID()) !== trim((string) $this->get('store_id'))) {
|
477 |
+
$this->respondJSON(array('success' => false, 'message' => 'Not Authorized'));
|
478 |
+
}
|
479 |
+
|
480 |
+
return true;
|
481 |
+
}
|
482 |
+
|
483 |
+
/**
|
484 |
+
* @param $data
|
485 |
+
*/
|
486 |
+
protected function respondJSON($data)
|
487 |
+
{
|
488 |
+
header('Content-Type: application/json');
|
489 |
+
echo json_encode($data);
|
490 |
+
exit;
|
491 |
+
}
|
492 |
+
}
|
includes/class-mailchimp-woocommerce.php
ADDED
@@ -0,0 +1,398 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* The file that defines the core plugin class
|
5 |
+
*
|
6 |
+
* A class definition that includes attributes and functions used across both the
|
7 |
+
* public-facing side of the site and the admin area.
|
8 |
+
*
|
9 |
+
* @link https://mailchimp.com
|
10 |
+
* @since 1.0.1
|
11 |
+
*
|
12 |
+
* @package MailChimp_Woocommerce
|
13 |
+
* @subpackage MailChimp_Woocommerce/includes
|
14 |
+
*/
|
15 |
+
|
16 |
+
/**
|
17 |
+
* The core plugin class.
|
18 |
+
*
|
19 |
+
* This is used to define internationalization, admin-specific hooks, and
|
20 |
+
* public-facing site hooks.
|
21 |
+
*
|
22 |
+
* Also maintains the unique identifier of this plugin as well as the current
|
23 |
+
* version of the plugin.
|
24 |
+
*
|
25 |
+
* @since 1.0.0
|
26 |
+
* @package MailChimp_Woocommerce
|
27 |
+
* @subpackage MailChimp_Woocommerce/includes
|
28 |
+
* @author Ryan Hungate <ryan@mailchimp.com>
|
29 |
+
*/
|
30 |
+
class MailChimp_Woocommerce {
|
31 |
+
|
32 |
+
/**
|
33 |
+
* The loader that's responsible for maintaining and registering all hooks that power
|
34 |
+
* the plugin.
|
35 |
+
*
|
36 |
+
* @since 1.0.0
|
37 |
+
* @access protected
|
38 |
+
* @var MailChimp_Woocommerce_Loader $loader Maintains and registers all hooks for the plugin.
|
39 |
+
*/
|
40 |
+
protected $loader;
|
41 |
+
|
42 |
+
/**
|
43 |
+
* The unique identifier of this plugin.
|
44 |
+
*
|
45 |
+
* @since 1.0.0
|
46 |
+
* @access protected
|
47 |
+
* @var string $plugin_name The string used to uniquely identify this plugin.
|
48 |
+
*/
|
49 |
+
protected $plugin_name;
|
50 |
+
|
51 |
+
/**
|
52 |
+
* The current version of the plugin.
|
53 |
+
*
|
54 |
+
* @since 1.0.0
|
55 |
+
* @access protected
|
56 |
+
* @var string $version The current version of the plugin.
|
57 |
+
*/
|
58 |
+
protected $version;
|
59 |
+
|
60 |
+
/**
|
61 |
+
* @var string
|
62 |
+
*/
|
63 |
+
protected $environment = 'production';
|
64 |
+
|
65 |
+
protected static $logging_config = null;
|
66 |
+
|
67 |
+
/**
|
68 |
+
* @return object
|
69 |
+
*/
|
70 |
+
public static function getLoggingConfig()
|
71 |
+
{
|
72 |
+
if (is_object(static::$logging_config)) {
|
73 |
+
return static::$logging_config;
|
74 |
+
}
|
75 |
+
|
76 |
+
$plugin_options = get_option('mailchimp-woocommerce');
|
77 |
+
$is_options = is_array($plugin_options);
|
78 |
+
|
79 |
+
$api_key = $is_options && array_key_exists('mailchimp_api_key', $plugin_options) ?
|
80 |
+
$plugin_options['mailchimp_api_key'] : false;
|
81 |
+
|
82 |
+
$enable_logging = $is_options &&
|
83 |
+
array_key_exists('mailchimp_debugging', $plugin_options) &&
|
84 |
+
$plugin_options['mailchimp_debugging'];
|
85 |
+
|
86 |
+
$account_id = $is_options && array_key_exists('mailchimp_account_info_id', $plugin_options) ?
|
87 |
+
$plugin_options['mailchimp_account_info_id'] : false;
|
88 |
+
|
89 |
+
$username = $is_options && array_key_exists('mailchimp_account_info_username', $plugin_options) ?
|
90 |
+
$plugin_options['mailchimp_account_info_username'] : false;
|
91 |
+
|
92 |
+
$api_key_parts = str_getcsv($api_key, '-');
|
93 |
+
$data_center = isset($api_key_parts[1]) ? $api_key_parts[1] : 'us1';
|
94 |
+
|
95 |
+
return static::$logging_config = (object) array(
|
96 |
+
'enable_logging' => (bool) $enable_logging,
|
97 |
+
'account_id' => $account_id,
|
98 |
+
'username' => $username,
|
99 |
+
'endpoint' => 'https://ecommerce.'.$data_center.'.list-manage.com/ecommerce/log',
|
100 |
+
);
|
101 |
+
}
|
102 |
+
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Define the core functionality of the plugin.
|
106 |
+
*
|
107 |
+
* Set the plugin name and the plugin version that can be used throughout the plugin.
|
108 |
+
* Load the dependencies, define the locale, and set the hooks for the admin area and
|
109 |
+
* the public-facing side of the site.
|
110 |
+
*
|
111 |
+
* @param string $environment
|
112 |
+
* @param string $version
|
113 |
+
*
|
114 |
+
* @since 1.0.0
|
115 |
+
*/
|
116 |
+
public function __construct($environment = 'production', $version = '1.0.0') {
|
117 |
+
|
118 |
+
$this->plugin_name = 'mailchimp-woocommerce';
|
119 |
+
$this->version = $version;
|
120 |
+
$this->environment = $environment;
|
121 |
+
|
122 |
+
$this->load_dependencies();
|
123 |
+
$this->set_locale();
|
124 |
+
$this->define_admin_hooks();
|
125 |
+
$this->define_public_hooks();
|
126 |
+
|
127 |
+
$this->activateMailChimpNewsletter();
|
128 |
+
$this->activateMailChimpService();
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Load the required dependencies for this plugin.
|
133 |
+
*
|
134 |
+
* Include the following files that make up the plugin:
|
135 |
+
*
|
136 |
+
* - MailChimp_Woocommerce_Loader. Orchestrates the hooks of the plugin.
|
137 |
+
* - MailChimp_Woocommerce_i18n. Defines internationalization functionality.
|
138 |
+
* - MailChimp_Woocommerce_Admin. Defines all hooks for the admin area.
|
139 |
+
* - MailChimp_Woocommerce_Public. Defines all hooks for the public side of the site.
|
140 |
+
*
|
141 |
+
* Create an instance of the loader which will be used to register the hooks
|
142 |
+
* with WordPress.
|
143 |
+
*
|
144 |
+
* @since 1.0.0
|
145 |
+
* @access private
|
146 |
+
*/
|
147 |
+
private function load_dependencies() {
|
148 |
+
|
149 |
+
$path = plugin_dir_path( dirname( __FILE__ ) );
|
150 |
+
|
151 |
+
$this->slack();
|
152 |
+
|
153 |
+
/** The abstract options class.*/
|
154 |
+
require_once $path . 'includes/class-mailchimp-woocommerce-options.php';
|
155 |
+
|
156 |
+
/** The class responsible for orchestrating the actions and filters of the core plugin.*/
|
157 |
+
require_once $path . 'includes/class-mailchimp-woocommerce-loader.php';
|
158 |
+
|
159 |
+
/** The class responsible for defining internationalization functionality of the plugin. */
|
160 |
+
require_once $path . 'includes/class-mailchimp-woocommerce-i18n.php';
|
161 |
+
|
162 |
+
/** The service class.*/
|
163 |
+
require_once $path . 'includes/class-mailchimp-woocommerce-service.php';
|
164 |
+
|
165 |
+
/** The newsletter class. */
|
166 |
+
require_once $path . 'includes/class-mailchimp-woocommerce-newsletter.php';
|
167 |
+
|
168 |
+
/** The class responsible for defining all actions that occur in the admin area.*/
|
169 |
+
require_once $path . 'admin/class-mailchimp-woocommerce-admin.php';
|
170 |
+
|
171 |
+
/** The class responsible for defining all actions that occur in the public-facing side of the site. */
|
172 |
+
require_once $path . 'public/class-mailchimp-woocommerce-public.php';
|
173 |
+
|
174 |
+
/** Require all the MailChimp Assets for the API */
|
175 |
+
require_once $path . 'includes/api/class-mailchimp-api.php';
|
176 |
+
require_once $path . 'includes/api/class-mailchimp-woocommerce-api.php';
|
177 |
+
require_once $path . 'includes/api/class-mailchimp-woocommerce-create-list-submission.php';
|
178 |
+
require_once $path . 'includes/api/class-mailchimp-woocommerce-transform-products.php';
|
179 |
+
require_once $path . 'includes/api/class-mailchimp-woocommerce-transform-orders.php';
|
180 |
+
|
181 |
+
/** Require all the mailchimp api asset classes */
|
182 |
+
require_once $path . 'includes/api/assets/class-mailchimp-address.php';
|
183 |
+
require_once $path . 'includes/api/assets/class-mailchimp-cart.php';
|
184 |
+
require_once $path . 'includes/api/assets/class-mailchimp-customer.php';
|
185 |
+
require_once $path . 'includes/api/assets/class-mailchimp-line-item.php';
|
186 |
+
require_once $path . 'includes/api/assets/class-mailchimp-order.php';
|
187 |
+
require_once $path . 'includes/api/assets/class-mailchimp-product.php';
|
188 |
+
require_once $path . 'includes/api/assets/class-mailchimp-product-variation.php';
|
189 |
+
require_once $path . 'includes/api/assets/class-mailchimp-store.php';
|
190 |
+
|
191 |
+
/** Require all the api error helpers */
|
192 |
+
require_once $path . 'includes/api/errors/class-mailchimp-error.php';
|
193 |
+
require_once $path . 'includes/api/errors/class-mailchimp-server-error.php';
|
194 |
+
|
195 |
+
/** Require the various helper scripts */
|
196 |
+
require_once $path . 'includes/api/helpers/class-mailchimp-woocommerce-api-currency-codes.php';
|
197 |
+
require_once $path . 'includes/api/helpers/class-mailchimp-woocommerce-api-locales.php';
|
198 |
+
|
199 |
+
/** Background job sync tools */
|
200 |
+
|
201 |
+
// make sure the queue exists first since the other files depend on it.
|
202 |
+
require_once $path . 'includes/vendor/queue.php';
|
203 |
+
|
204 |
+
// the abstract bulk sync class
|
205 |
+
require_once $path.'includes/processes/class-mailchimp-woocommerce-abstract-sync.php';
|
206 |
+
|
207 |
+
// bulk data sync
|
208 |
+
require_once $path.'includes/processes/class-mailchimp-woocommerce-process-orders.php';
|
209 |
+
require_once $path.'includes/processes/class-mailchimp-woocommerce-process-products.php';
|
210 |
+
|
211 |
+
// individual item sync
|
212 |
+
require_once $path.'includes/processes/class-mailchimp-woocommerce-cart-update.php';
|
213 |
+
require_once $path.'includes/processes/class-mailchimp-woocommerce-single-order.php';
|
214 |
+
require_once $path.'includes/processes/class-mailchimp-woocommerce-single-product.php';
|
215 |
+
|
216 |
+
// fire up the loader
|
217 |
+
$this->loader = new MailChimp_Woocommerce_Loader();
|
218 |
+
}
|
219 |
+
|
220 |
+
/**
|
221 |
+
*
|
222 |
+
*/
|
223 |
+
private function slack()
|
224 |
+
{
|
225 |
+
$path = plugin_dir_path( dirname( __FILE__ ) );
|
226 |
+
|
227 |
+
require_once $path.'includes/slack/Contracts/Http/Interactor.php';
|
228 |
+
require_once $path.'includes/slack/Contracts/Http/Response.php';
|
229 |
+
require_once $path.'includes/slack/Contracts/Http/ResponseFactory.php';
|
230 |
+
|
231 |
+
require_once $path.'includes/slack/Core/Commander.php';
|
232 |
+
|
233 |
+
require_once $path.'includes/slack/Http/CurlInteractor.php';
|
234 |
+
require_once $path.'includes/slack/Http/SlackResponse.php';
|
235 |
+
require_once $path.'includes/slack/Http/SlackResponseFactory.php';
|
236 |
+
|
237 |
+
require_once $path.'includes/slack/Logger.php';
|
238 |
+
}
|
239 |
+
|
240 |
+
/**
|
241 |
+
* Define the locale for this plugin for internationalization.
|
242 |
+
*
|
243 |
+
* Uses the MailChimp_Woocommerce_i18n class in order to set the domain and to register the hook
|
244 |
+
* with WordPress.
|
245 |
+
*
|
246 |
+
* @since 1.0.0
|
247 |
+
* @access private
|
248 |
+
*/
|
249 |
+
private function set_locale() {
|
250 |
+
|
251 |
+
$plugin_i18n = new MailChimp_Woocommerce_i18n();
|
252 |
+
|
253 |
+
$this->loader->add_action( 'plugins_loaded', $plugin_i18n, 'load_plugin_textdomain' );
|
254 |
+
}
|
255 |
+
|
256 |
+
/**
|
257 |
+
* Register all of the hooks related to the admin area functionality
|
258 |
+
* of the plugin.
|
259 |
+
*
|
260 |
+
* @since 1.0.0
|
261 |
+
* @access private
|
262 |
+
*/
|
263 |
+
private function define_admin_hooks() {
|
264 |
+
|
265 |
+
$plugin_admin = new MailChimp_Woocommerce_Admin( $this->get_plugin_name(), $this->get_version() );
|
266 |
+
|
267 |
+
$this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' );
|
268 |
+
$this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' );
|
269 |
+
|
270 |
+
// Add menu item
|
271 |
+
$this->loader->add_action( 'admin_menu', $plugin_admin, 'add_plugin_admin_menu' );
|
272 |
+
|
273 |
+
// Add Settings link to the plugin
|
274 |
+
$plugin_basename = plugin_basename( plugin_dir_path( __DIR__ ) . $this->plugin_name . '.php' );
|
275 |
+
$this->loader->add_filter( 'plugin_action_links_' . $plugin_basename, $plugin_admin, 'add_action_links' );
|
276 |
+
|
277 |
+
// make sure we're listening for the admin init
|
278 |
+
$this->loader->add_action('admin_init', $plugin_admin, 'options_update');
|
279 |
+
|
280 |
+
// put the menu on the admin top bar.
|
281 |
+
//$this->loader->add_action('admin_bar_menu', $plugin_admin, 'admin_bar', 100);
|
282 |
+
}
|
283 |
+
|
284 |
+
/**
|
285 |
+
* Register all of the hooks related to the public-facing functionality
|
286 |
+
* of the plugin.
|
287 |
+
*
|
288 |
+
* @since 1.0.0
|
289 |
+
* @access private
|
290 |
+
*/
|
291 |
+
private function define_public_hooks() {
|
292 |
+
|
293 |
+
$plugin_public = new MailChimp_Woocommerce_Public( $this->get_plugin_name(), $this->get_version() );
|
294 |
+
|
295 |
+
$this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_styles' );
|
296 |
+
$this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_scripts' );
|
297 |
+
}
|
298 |
+
|
299 |
+
/**
|
300 |
+
* Handle the newsletter actions here.
|
301 |
+
*/
|
302 |
+
private function activateMailChimpNewsletter()
|
303 |
+
{
|
304 |
+
$service = new MailChimp_Newsletter();
|
305 |
+
|
306 |
+
if ($service->isConfigured()) {
|
307 |
+
|
308 |
+
$service->setEnvironment($this->environment);
|
309 |
+
$service->setVersion($this->version);
|
310 |
+
|
311 |
+
$this->loader->add_action('woocommerce_after_checkout_billing_form', $service, 'applyNewsletterField', 5);
|
312 |
+
$this->loader->add_action('woocommerce_ppe_checkout_order_review', $service, 'applyNewsletterField', 5);
|
313 |
+
$this->loader->add_action('woocommerce_register_form', $service, 'applyNewsletterField', 5);
|
314 |
+
|
315 |
+
$this->loader->add_action('woocommerce_checkout_order_processed', $service, 'processNewsletterField', 5, 2);
|
316 |
+
$this->loader->add_action('woocommerce_ppe_do_payaction', $service, 'processPayPalNewsletterField', 5, 1);
|
317 |
+
$this->loader->add_action('woocommerce_register_post', $service, 'processRegistrationForm', 5, 3);
|
318 |
+
}
|
319 |
+
}
|
320 |
+
|
321 |
+
/**
|
322 |
+
* Handle all the service hooks here.
|
323 |
+
*/
|
324 |
+
private function activateMailChimpService()
|
325 |
+
{
|
326 |
+
$service = new MailChimp_Service();
|
327 |
+
|
328 |
+
if ($service->isConfigured()) {
|
329 |
+
|
330 |
+
$service->setEnvironment($this->environment);
|
331 |
+
$service->setVersion($this->version);
|
332 |
+
|
333 |
+
// core hook setup
|
334 |
+
$this->loader->add_action('admin_init', $service, 'adminReady');
|
335 |
+
$this->loader->add_action('woocommerce_init', $service, 'wooIsRunning');
|
336 |
+
|
337 |
+
// for the data sync we need to configure basic auth.
|
338 |
+
$this->loader->add_filter('http_request_args', $service, 'addHttpRequestArgs', 10, 2);
|
339 |
+
|
340 |
+
// campaign tracking
|
341 |
+
$this->loader->add_action( 'init', $service, 'handleCampaignTracking' );
|
342 |
+
|
343 |
+
// order hooks
|
344 |
+
$this->loader->add_action('woocommerce_api_create_order', $service, 'handleOrderStatusChanged');
|
345 |
+
$this->loader->add_action('woocommerce_thankyou', $service, 'handleOrderStatusChanged');
|
346 |
+
$this->loader->add_action('woocommerce_order_status_changed', $service, 'handleOrderStatusChanged');
|
347 |
+
|
348 |
+
// cart hooks
|
349 |
+
$this->loader->add_action('woocommerce_cart_updated', $service, 'handleCartUpdated');
|
350 |
+
$this->loader->add_action('woocommerce_add_to_cart', $service, 'handleCartUpdated');
|
351 |
+
$this->loader->add_action('woocommerce_cart_item_removed', $service, 'handleCartUpdated');
|
352 |
+
|
353 |
+
// save post hook for products
|
354 |
+
$this->loader->add_action('save_post', $service, 'handlePostSaved', 10, 3);
|
355 |
+
}
|
356 |
+
}
|
357 |
+
|
358 |
+
/**
|
359 |
+
* Run the loader to execute all of the hooks with WordPress.
|
360 |
+
*
|
361 |
+
* @since 1.0.0
|
362 |
+
*/
|
363 |
+
public function run() {
|
364 |
+
$this->loader->run();
|
365 |
+
}
|
366 |
+
|
367 |
+
/**
|
368 |
+
* The name of the plugin used to uniquely identify it within the context of
|
369 |
+
* WordPress and to define internationalization functionality.
|
370 |
+
*
|
371 |
+
* @since 1.0.1
|
372 |
+
* @return string The name of the plugin.
|
373 |
+
*/
|
374 |
+
public function get_plugin_name() {
|
375 |
+
return $this->plugin_name;
|
376 |
+
}
|
377 |
+
|
378 |
+
/**
|
379 |
+
* The reference to the class that orchestrates the hooks with the plugin.
|
380 |
+
*
|
381 |
+
* @since 1.0.1
|
382 |
+
* @return MailChimp_Woocommerce_Loader Orchestrates the hooks of the plugin.
|
383 |
+
*/
|
384 |
+
public function get_loader() {
|
385 |
+
return $this->loader;
|
386 |
+
}
|
387 |
+
|
388 |
+
/**
|
389 |
+
* Retrieve the version number of the plugin.
|
390 |
+
*
|
391 |
+
* @since 1.0.1
|
392 |
+
* @return string The version number of the plugin.
|
393 |
+
*/
|
394 |
+
public function get_version() {
|
395 |
+
return $this->version;
|
396 |
+
}
|
397 |
+
|
398 |
+
}
|
includes/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden
|
includes/plugin-update-checker/README.md
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Plugin Update Checker
|
2 |
+
=====================
|
3 |
+
|
4 |
+
This is a custom update checker library for WordPress plugins. It lets you add automatic update notifications and one-click upgrades to your commercial and private plugins. All you need to do is put your plugin details in a JSON file, place the file on your server, and pass the URL to the library. The library periodically checks the URL to see if there's a new version available and displays an update notification to the user if necessary.
|
5 |
+
|
6 |
+
From the users' perspective, it works just like with plugins hosted on WordPress.org. The update checker uses the default plugin upgrade UI that will already be familiar to most WordPress users.
|
7 |
+
|
8 |
+
[See this blog post](http://w-shadow.com/blog/2010/09/02/automatic-updates-for-any-plugin/) for more information and usage instructions.
|
9 |
+
|
10 |
+
Getting Started
|
11 |
+
---------------
|
12 |
+
|
13 |
+
### Self-hosted Plugins
|
14 |
+
|
15 |
+
1. Make a JSON file that describes your plugin. Here's a minimal example:
|
16 |
+
|
17 |
+
```json
|
18 |
+
{
|
19 |
+
"name" : "My Cool Plugin",
|
20 |
+
"version" : "2.0",
|
21 |
+
"author" : "John Smith",
|
22 |
+
"download_url" : "http://example.com/plugins/my-cool-plugin.zip",
|
23 |
+
"sections" : {
|
24 |
+
"description" : "Plugin description here. You can use HTML."
|
25 |
+
}
|
26 |
+
}
|
27 |
+
```
|
28 |
+
See [this table](https://spreadsheets.google.com/pub?key=0AqP80E74YcUWdEdETXZLcXhjd2w0cHMwX2U1eDlWTHc&authkey=CK7h9toK&hl=en&single=true&gid=0&output=html) for a full list of supported fields.
|
29 |
+
2. Upload this file to a publicly accessible location.
|
30 |
+
3. Download [the update checker](https://github.com/YahnisElsts/plugin-update-checker/releases/latest), unzip the archive and copy the `plugin-update-checker` directory to your plugin.
|
31 |
+
4. Add the following code to the main plugin file:
|
32 |
+
|
33 |
+
```php
|
34 |
+
require 'plugin-update-checker/plugin-update-checker.php';
|
35 |
+
$myUpdateChecker = PucFactory::buildUpdateChecker(
|
36 |
+
'http://example.com/path/to/metadata.json',
|
37 |
+
__FILE__
|
38 |
+
);
|
39 |
+
```
|
40 |
+
|
41 |
+
#### Notes
|
42 |
+
- You could use [wp-update-server](https://github.com/YahnisElsts/wp-update-server) to automatically generate JSON metadata from ZIP packages.
|
43 |
+
- The second argument passed to `buildUpdateChecker` should be the full path to the main plugin file.
|
44 |
+
- There are more options available - see the [blog](http://w-shadow.com/blog/2010/09/02/automatic-updates-for-any-plugin/) for details.
|
45 |
+
|
46 |
+
### Plugins Hosted on GitHub
|
47 |
+
|
48 |
+
*(GitHub support is experimental.)*
|
49 |
+
|
50 |
+
1. Download [the latest release](https://github.com/YahnisElsts/plugin-update-checker/releases/latest), unzip it and copy the `plugin-update-checker` directory to your plugin.
|
51 |
+
2. Add the following code to the main file of your plugin:
|
52 |
+
|
53 |
+
```php
|
54 |
+
require 'plugin-update-checker/plugin-update-checker.php';
|
55 |
+
$className = PucFactory::getLatestClassVersion('PucGitHubChecker');
|
56 |
+
$myUpdateChecker = new $className(
|
57 |
+
'https://github.com/user-name/plugin-repo-name/',
|
58 |
+
__FILE__,
|
59 |
+
'master'
|
60 |
+
);
|
61 |
+
```
|
62 |
+
The third argument specifies the branch to use for updating your plugin. The default is `master`. If the branch name is omitted or set to `master`, the update checker will use the latest release or tag (if available). Otherwise it will use the specified branch.
|
63 |
+
3. Optional: Add a `readme.txt` file formatted according to the [WordPress.org plugin readme standard](https://wordpress.org/plugins/about/readme.txt). The contents of this file will be shown when the user clicks the "View version 1.2.3 details" link.
|
64 |
+
|
65 |
+
#### Notes
|
66 |
+
|
67 |
+
If your GitHub repository requires an access token, you can specify it like this:
|
68 |
+
```php
|
69 |
+
$myUpdateChecker->setAccessToken('your-token-here');
|
70 |
+
```
|
71 |
+
|
72 |
+
The GitHub version of the library will pull update details from the following parts of a release/tag/branch:
|
73 |
+
|
74 |
+
- Changelog
|
75 |
+
- The "Changelog" section of `readme.txt`.
|
76 |
+
- One of the following files:
|
77 |
+
CHANGES.md, CHANGELOG.md, changes.md, changelog.md
|
78 |
+
- Release notes.
|
79 |
+
- Version number
|
80 |
+
- The "Version" plugin header.
|
81 |
+
- The latest release or tag name.
|
82 |
+
- Required and tested WordPress versions
|
83 |
+
- The "Requires at least" and "Tested up to" fields in `readme.txt`.
|
84 |
+
- The following plugin headers:
|
85 |
+
`Required WP`, `Tested WP`, `Requires at least`, `Tested up to`
|
86 |
+
- "Last updated" timestamp
|
87 |
+
- The creation timestamp of the latest release.
|
88 |
+
- The latest commit of the selected tag or branch that changed the main plugin file.
|
89 |
+
- Number of downloads
|
90 |
+
- The `download_count` statistic of the latest release.
|
91 |
+
- If you're not using GitHub releases, there will be no download stats.
|
92 |
+
- Other plugin details - author, homepage URL, description
|
93 |
+
- The "Description" section of `readme.txt`.
|
94 |
+
- Remote plugin headers (i.e. the latest version on GitHub).
|
95 |
+
- Local plugin headers (i.e. the currently installed version).
|
96 |
+
- Ratings, banners, screenshots
|
97 |
+
- Not supported.
|
98 |
+
|
99 |
+
Resources
|
100 |
+
---------
|
101 |
+
|
102 |
+
- [Theme Update Checker](http://w-shadow.com/blog/2011/06/02/automatic-updates-for-commercial-themes/)
|
103 |
+
- [Debug Bar](https://wordpress.org/plugins/debug-bar/) - useful for testing and debugging the update checker.
|
104 |
+
- [Securing download links](http://w-shadow.com/blog/2013/03/19/plugin-updates-securing-download-links/) - a general overview.
|
105 |
+
- [A GUI for entering download credentials](http://open-tools.net/documentation/tutorial-automatic-updates.html#wordpress)
|
includes/plugin-update-checker/composer.json
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "yahnis-elsts/plugin-update-checker",
|
3 |
+
"type": "library",
|
4 |
+
"description": "A custom update checker for WordPress plugins. Useful if you can't host your plugin in the official WP plugin repository but still want it to support automatic plugin updates.",
|
5 |
+
"keywords": ["wordpress", "plugin updates", "automatic updates"],
|
6 |
+
"homepage": "https://github.com/YahnisElsts/plugin-update-checker/",
|
7 |
+
"license": "MIT",
|
8 |
+
"authors": [
|
9 |
+
{
|
10 |
+
"name": "Yahnis Elsts",
|
11 |
+
"email": "whiteshadow@w-shadow.com",
|
12 |
+
"homepage": "http://w-shadow.com/",
|
13 |
+
"role": "Developer"
|
14 |
+
}
|
15 |
+
],
|
16 |
+
"require": {
|
17 |
+
"php": ">=5.2.0"
|
18 |
+
}
|
19 |
+
}
|
includes/plugin-update-checker/css/puc-debug-bar.css
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.puc-debug-bar-panel pre {
|
2 |
+
margin-top: 0;
|
3 |
+
}
|
4 |
+
|
5 |
+
/* Style the debug data table to match "widefat" table style used by WordPress. */
|
6 |
+
table.puc-debug-data {
|
7 |
+
width: 100%;
|
8 |
+
clear: both;
|
9 |
+
margin: 0;
|
10 |
+
|
11 |
+
border-spacing: 0;
|
12 |
+
background-color: #f9f9f9;
|
13 |
+
|
14 |
+
border-radius: 3px;
|
15 |
+
border: 1px solid #dfdfdf;
|
16 |
+
border-collapse: separate;
|
17 |
+
}
|
18 |
+
|
19 |
+
table.puc-debug-data * {
|
20 |
+
word-wrap: break-word;
|
21 |
+
}
|
22 |
+
|
23 |
+
table.puc-debug-data th {
|
24 |
+
width: 11em;
|
25 |
+
padding: 7px 7px 8px;
|
26 |
+
text-align: left;
|
27 |
+
|
28 |
+
font-family: "Georgia", "Times New Roman", "Bitstream Charter", "Times", serif;
|
29 |
+
font-weight: 400;
|
30 |
+
font-size: 14px;
|
31 |
+
line-height: 1.3em;
|
32 |
+
text-shadow: rgba(255, 255, 255, 0.804) 0 1px 0;
|
33 |
+
}
|
34 |
+
|
35 |
+
table.puc-debug-data td, table.puc-debug-data th {
|
36 |
+
border-width: 1px 0;
|
37 |
+
border-style: solid;
|
38 |
+
|
39 |
+
border-top-color: #fff;
|
40 |
+
border-bottom-color: #dfdfdf;
|
41 |
+
|
42 |
+
text-transform: none;
|
43 |
+
}
|
44 |
+
|
45 |
+
table.puc-debug-data td {
|
46 |
+
color: #555;
|
47 |
+
font-size: 12px;
|
48 |
+
padding: 4px 7px 2px;
|
49 |
+
vertical-align: top;
|
50 |
+
}
|
51 |
+
|
52 |
+
.puc-ajax-response {
|
53 |
+
border: 1px solid #dfdfdf;
|
54 |
+
border-radius: 3px;
|
55 |
+
padding: 0.5em;
|
56 |
+
margin: 5px 0;
|
57 |
+
background-color: white;
|
58 |
+
}
|
59 |
+
|
60 |
+
.puc-ajax-nonce {
|
61 |
+
display: none;
|
62 |
+
}
|
includes/plugin-update-checker/debug-bar-panel.php
ADDED
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if ( !class_exists('PluginUpdateCheckerPanel_3_1', false) && class_exists('Debug_Bar_Panel', false) ) {
|
4 |
+
|
5 |
+
/**
|
6 |
+
* A Debug Bar panel for the plugin update checker.
|
7 |
+
*/
|
8 |
+
class PluginUpdateCheckerPanel_3_1 extends Debug_Bar_Panel {
|
9 |
+
/** @var PluginUpdateChecker_3_1 */
|
10 |
+
private $updateChecker;
|
11 |
+
|
12 |
+
private $responseBox = '<div class="puc-ajax-response" style="display: none;"></div>';
|
13 |
+
|
14 |
+
public function __construct($updateChecker) {
|
15 |
+
$this->updateChecker = $updateChecker;
|
16 |
+
$title = sprintf(
|
17 |
+
'<span id="puc-debug-menu-link-%s">PUC (%s)</span>',
|
18 |
+
esc_attr($this->updateChecker->slug),
|
19 |
+
$this->updateChecker->slug
|
20 |
+
);
|
21 |
+
parent::Debug_Bar_Panel($title);
|
22 |
+
}
|
23 |
+
|
24 |
+
public function render() {
|
25 |
+
printf(
|
26 |
+
'<div class="puc-debug-bar-panel" id="puc-debug-bar-panel_%1$s" data-slug="%1$s" data-nonce="%2$s">',
|
27 |
+
esc_attr($this->updateChecker->slug),
|
28 |
+
esc_attr(wp_create_nonce('puc-ajax'))
|
29 |
+
);
|
30 |
+
|
31 |
+
$this->displayConfiguration();
|
32 |
+
$this->displayStatus();
|
33 |
+
$this->displayCurrentUpdate();
|
34 |
+
|
35 |
+
echo '</div>';
|
36 |
+
}
|
37 |
+
|
38 |
+
private function displayConfiguration() {
|
39 |
+
echo '<h3>Configuration</h3>';
|
40 |
+
echo '<table class="puc-debug-data">';
|
41 |
+
$this->row('Plugin file', htmlentities($this->updateChecker->pluginFile));
|
42 |
+
$this->row('Slug', htmlentities($this->updateChecker->slug));
|
43 |
+
$this->row('DB option', htmlentities($this->updateChecker->optionName));
|
44 |
+
|
45 |
+
$requestInfoButton = '';
|
46 |
+
if ( function_exists('get_submit_button') ) {
|
47 |
+
$requestInfoButton = get_submit_button('Request Info', 'secondary', 'puc-request-info-button', false, array('id' => 'puc-request-info-button-' . $this->updateChecker->slug));
|
48 |
+
}
|
49 |
+
$this->row('Metadata URL', htmlentities($this->updateChecker->metadataUrl) . ' ' . $requestInfoButton . $this->responseBox);
|
50 |
+
|
51 |
+
$scheduler = $this->updateChecker->scheduler;
|
52 |
+
if ( $scheduler->checkPeriod > 0 ) {
|
53 |
+
$this->row('Automatic checks', 'Every ' . $scheduler->checkPeriod . ' hours');
|
54 |
+
} else {
|
55 |
+
$this->row('Automatic checks', 'Disabled');
|
56 |
+
}
|
57 |
+
|
58 |
+
if ( isset($scheduler->throttleRedundantChecks) ) {
|
59 |
+
if ( $scheduler->throttleRedundantChecks && ($scheduler->checkPeriod > 0) ) {
|
60 |
+
$this->row(
|
61 |
+
'Throttling',
|
62 |
+
sprintf(
|
63 |
+
'Enabled. If an update is already available, check for updates every %1$d hours instead of every %2$d hours.',
|
64 |
+
$scheduler->throttledCheckPeriod,
|
65 |
+
$scheduler->checkPeriod
|
66 |
+
)
|
67 |
+
);
|
68 |
+
} else {
|
69 |
+
$this->row('Throttling', 'Disabled');
|
70 |
+
}
|
71 |
+
}
|
72 |
+
echo '</table>';
|
73 |
+
}
|
74 |
+
|
75 |
+
private function displayStatus() {
|
76 |
+
echo '<h3>Status</h3>';
|
77 |
+
echo '<table class="puc-debug-data">';
|
78 |
+
$state = $this->updateChecker->getUpdateState();
|
79 |
+
$checkNowButton = '';
|
80 |
+
if ( function_exists('get_submit_button') ) {
|
81 |
+
$checkNowButton = get_submit_button('Check Now', 'secondary', 'puc-check-now-button', false, array('id' => 'puc-check-now-button-' . $this->updateChecker->slug));
|
82 |
+
}
|
83 |
+
|
84 |
+
if ( isset($state, $state->lastCheck) ) {
|
85 |
+
$this->row('Last check', $this->formatTimeWithDelta($state->lastCheck) . ' ' . $checkNowButton . $this->responseBox);
|
86 |
+
} else {
|
87 |
+
$this->row('Last check', 'Never');
|
88 |
+
}
|
89 |
+
|
90 |
+
$nextCheck = wp_next_scheduled($this->updateChecker->scheduler->getCronHookName());
|
91 |
+
$this->row('Next automatic check', $this->formatTimeWithDelta($nextCheck));
|
92 |
+
|
93 |
+
if ( isset($state, $state->checkedVersion) ) {
|
94 |
+
$this->row('Checked version', htmlentities($state->checkedVersion));
|
95 |
+
$this->row('Cached update', $state->update);
|
96 |
+
}
|
97 |
+
$this->row('Update checker class', htmlentities(get_class($this->updateChecker)));
|
98 |
+
echo '</table>';
|
99 |
+
}
|
100 |
+
|
101 |
+
private function displayCurrentUpdate() {
|
102 |
+
$update = $this->updateChecker->getUpdate();
|
103 |
+
if ( $update !== null ) {
|
104 |
+
echo '<h3>An Update Is Available</h3>';
|
105 |
+
echo '<table class="puc-debug-data">';
|
106 |
+
$fields = array('version', 'download_url', 'slug', 'homepage', 'upgrade_notice');
|
107 |
+
foreach($fields as $field) {
|
108 |
+
$this->row(ucwords(str_replace('_', ' ', $field)), htmlentities($update->$field));
|
109 |
+
}
|
110 |
+
echo '</table>';
|
111 |
+
} else {
|
112 |
+
echo '<h3>No updates currently available</h3>';
|
113 |
+
}
|
114 |
+
}
|
115 |
+
|
116 |
+
private function formatTimeWithDelta($unixTime) {
|
117 |
+
if ( empty($unixTime) ) {
|
118 |
+
return 'Never';
|
119 |
+
}
|
120 |
+
|
121 |
+
$delta = time() - $unixTime;
|
122 |
+
$result = human_time_diff(time(), $unixTime);
|
123 |
+
if ( $delta < 0 ) {
|
124 |
+
$result = 'after ' . $result;
|
125 |
+
} else {
|
126 |
+
$result = $result . ' ago';
|
127 |
+
}
|
128 |
+
$result .= ' (' . $this->formatTimestamp($unixTime) . ')';
|
129 |
+
return $result;
|
130 |
+
}
|
131 |
+
|
132 |
+
private function formatTimestamp($unixTime) {
|
133 |
+
return gmdate('Y-m-d H:i:s', $unixTime + (get_option('gmt_offset') * 3600));
|
134 |
+
}
|
135 |
+
|
136 |
+
private function row($name, $value) {
|
137 |
+
if ( is_object($value) || is_array($value) ) {
|
138 |
+
$value = '<pre>' . htmlentities(print_r($value, true)) . '</pre>';
|
139 |
+
} else if ($value === null) {
|
140 |
+
$value = '<code>null</code>';
|
141 |
+
}
|
142 |
+
printf('<tr><th scope="row">%1$s</th> <td>%2$s</td></tr>', $name, $value);
|
143 |
+
}
|
144 |
+
}
|
145 |
+
|
146 |
+
}
|
includes/plugin-update-checker/debug-bar-plugin.php
ADDED
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
if ( !class_exists('PucDebugBarPlugin_3_1', false) ) {
|
3 |
+
|
4 |
+
class PucDebugBarPlugin_3_1 {
|
5 |
+
/** @var PluginUpdateChecker_3_1 */
|
6 |
+
private $updateChecker;
|
7 |
+
|
8 |
+
public function __construct($updateChecker) {
|
9 |
+
$this->updateChecker = $updateChecker;
|
10 |
+
|
11 |
+
add_filter('debug_bar_panels', array($this, 'addDebugBarPanel'));
|
12 |
+
add_action('debug_bar_enqueue_scripts', array($this, 'enqueuePanelDependencies'));
|
13 |
+
|
14 |
+
add_action('wp_ajax_puc_debug_check_now', array($this, 'ajaxCheckNow'));
|
15 |
+
add_action('wp_ajax_puc_debug_request_info', array($this, 'ajaxRequestInfo'));
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Register the PUC Debug Bar panel.
|
20 |
+
*
|
21 |
+
* @param array $panels
|
22 |
+
* @return array
|
23 |
+
*/
|
24 |
+
public function addDebugBarPanel($panels) {
|
25 |
+
require_once dirname(__FILE__) . '/debug-bar-panel.php';
|
26 |
+
if ( current_user_can('update_plugins') && class_exists('PluginUpdateCheckerPanel_3_1', false) ) {
|
27 |
+
$panels[] = new PluginUpdateCheckerPanel_3_1($this->updateChecker);
|
28 |
+
}
|
29 |
+
return $panels;
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Enqueue our Debug Bar scripts and styles.
|
34 |
+
*/
|
35 |
+
public function enqueuePanelDependencies() {
|
36 |
+
wp_enqueue_style(
|
37 |
+
'puc-debug-bar-style',
|
38 |
+
plugins_url( "/css/puc-debug-bar.css", __FILE__ ),
|
39 |
+
array('debug-bar'),
|
40 |
+
'20130927'
|
41 |
+
);
|
42 |
+
|
43 |
+
wp_enqueue_script(
|
44 |
+
'puc-debug-bar-js',
|
45 |
+
plugins_url( "/js/debug-bar.js", __FILE__ ),
|
46 |
+
array('jquery'),
|
47 |
+
'20121026'
|
48 |
+
);
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Run an update check and output the result. Useful for making sure that
|
53 |
+
* the update checking process works as expected.
|
54 |
+
*/
|
55 |
+
public function ajaxCheckNow() {
|
56 |
+
if ( $_POST['slug'] !== $this->updateChecker->slug ) {
|
57 |
+
return;
|
58 |
+
}
|
59 |
+
$this->preAjaxReqest();
|
60 |
+
$update = $this->updateChecker->checkForUpdates();
|
61 |
+
if ( $update !== null ) {
|
62 |
+
echo "An update is available:";
|
63 |
+
echo '<pre>', htmlentities(print_r($update, true)), '</pre>';
|
64 |
+
} else {
|
65 |
+
echo 'No updates found.';
|
66 |
+
}
|
67 |
+
exit;
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Request plugin info and output it.
|
72 |
+
*/
|
73 |
+
public function ajaxRequestInfo() {
|
74 |
+
if ( $_POST['slug'] !== $this->updateChecker->slug ) {
|
75 |
+
return;
|
76 |
+
}
|
77 |
+
$this->preAjaxReqest();
|
78 |
+
$info = $this->updateChecker->requestInfo();
|
79 |
+
if ( $info !== null ) {
|
80 |
+
echo 'Successfully retrieved plugin info from the metadata URL:';
|
81 |
+
echo '<pre>', htmlentities(print_r($info, true)), '</pre>';
|
82 |
+
} else {
|
83 |
+
echo 'Failed to retrieve plugin info from the metadata URL.';
|
84 |
+
}
|
85 |
+
exit;
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Check access permissions and enable error display (for debugging).
|
90 |
+
*/
|
91 |
+
private function preAjaxReqest() {
|
92 |
+
if ( !current_user_can('update_plugins') ) {
|
93 |
+
die('Access denied');
|
94 |
+
}
|
95 |
+
check_ajax_referer('puc-ajax');
|
96 |
+
|
97 |
+
error_reporting(E_ALL);
|
98 |
+
@ini_set('display_errors','On');
|
99 |
+
}
|
100 |
+
}
|
101 |
+
|
102 |
+
}
|
includes/plugin-update-checker/github-checker.php
ADDED
@@ -0,0 +1,458 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if ( !class_exists('PucGitHubChecker_3_1', false) ):
|
4 |
+
|
5 |
+
class PucGitHubChecker_3_1 extends PluginUpdateChecker_3_1 {
|
6 |
+
/**
|
7 |
+
* @var string GitHub username.
|
8 |
+
*/
|
9 |
+
protected $userName;
|
10 |
+
/**
|
11 |
+
* @var string GitHub repository name.
|
12 |
+
*/
|
13 |
+
protected $repositoryName;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* @var string Either a fully qualified repository URL, or just "user/repo-name".
|
17 |
+
*/
|
18 |
+
protected $repositoryUrl;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* @var string The branch to use as the latest version. Defaults to "master".
|
22 |
+
*/
|
23 |
+
protected $branch;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @var string GitHub authentication token. Optional.
|
27 |
+
*/
|
28 |
+
protected $accessToken;
|
29 |
+
|
30 |
+
public function __construct(
|
31 |
+
$repositoryUrl,
|
32 |
+
$pluginFile,
|
33 |
+
$branch = 'master',
|
34 |
+
$checkPeriod = 1,
|
35 |
+
$optionName = '',
|
36 |
+
$muPluginFile = ''
|
37 |
+
) {
|
38 |
+
|
39 |
+
$this->repositoryUrl = $repositoryUrl;
|
40 |
+
$this->branch = empty($branch) ? 'master' : $branch;
|
41 |
+
|
42 |
+
$path = @parse_url($repositoryUrl, PHP_URL_PATH);
|
43 |
+
if ( preg_match('@^/?(?P<username>[^/]+?)/(?P<repository>[^/#?&]+?)/?$@', $path, $matches) ) {
|
44 |
+
$this->userName = $matches['username'];
|
45 |
+
$this->repositoryName = $matches['repository'];
|
46 |
+
} else {
|
47 |
+
throw new InvalidArgumentException('Invalid GitHub repository URL: "' . $repositoryUrl . '"');
|
48 |
+
}
|
49 |
+
|
50 |
+
parent::__construct($repositoryUrl, $pluginFile, '', $checkPeriod, $optionName, $muPluginFile);
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Retrieve details about the latest plugin version from GitHub.
|
55 |
+
*
|
56 |
+
* @param array $unusedQueryArgs Unused.
|
57 |
+
* @return PluginInfo_3_1
|
58 |
+
*/
|
59 |
+
public function requestInfo($unusedQueryArgs = array()) {
|
60 |
+
$info = new PluginInfo_3_1();
|
61 |
+
$info->filename = $this->pluginFile;
|
62 |
+
$info->slug = $this->slug;
|
63 |
+
|
64 |
+
$this->setInfoFromHeader($this->getPluginHeader(), $info);
|
65 |
+
|
66 |
+
//Figure out which reference (tag or branch) we'll use to get the latest version of the plugin.
|
67 |
+
$ref = $this->branch;
|
68 |
+
if ( $this->branch === 'master' ) {
|
69 |
+
//Use the latest release.
|
70 |
+
$release = $this->getLatestRelease();
|
71 |
+
if ( $release !== null ) {
|
72 |
+
$ref = $release->tag_name;
|
73 |
+
$info->version = ltrim($release->tag_name, 'v'); //Remove the "v" prefix from "v1.2.3".
|
74 |
+
$info->last_updated = $release->created_at;
|
75 |
+
$info->download_url = $release->zipball_url;
|
76 |
+
|
77 |
+
if ( !empty($release->body) ) {
|
78 |
+
$info->sections['changelog'] = $this->parseMarkdown($release->body);
|
79 |
+
}
|
80 |
+
if ( isset($release->assets[0]) ) {
|
81 |
+
$info->downloaded = $release->assets[0]->download_count;
|
82 |
+
}
|
83 |
+
} else {
|
84 |
+
//Failing that, use the tag with the highest version number.
|
85 |
+
$tag = $this->getLatestTag();
|
86 |
+
if ( $tag !== null ) {
|
87 |
+
$ref = $tag->name;
|
88 |
+
$info->version = $tag->name;
|
89 |
+
$info->download_url = $tag->zipball_url;
|
90 |
+
}
|
91 |
+
}
|
92 |
+
}
|
93 |
+
|
94 |
+
if ( empty($info->download_url) ) {
|
95 |
+
$info->download_url = $this->buildArchiveDownloadUrl($ref);
|
96 |
+
} else if ( !empty($this->accessToken) ) {
|
97 |
+
$info->download_url = add_query_arg('access_token', $this->accessToken, $info->download_url);
|
98 |
+
}
|
99 |
+
|
100 |
+
//Get headers from the main plugin file in this branch/tag. Its "Version" header and other metadata
|
101 |
+
//are what the WordPress install will actually see after upgrading, so they take precedence over releases/tags.
|
102 |
+
$mainPluginFile = basename($this->pluginFile);
|
103 |
+
$remotePlugin = $this->getRemoteFile($mainPluginFile, $ref);
|
104 |
+
if ( !empty($remotePlugin) ) {
|
105 |
+
$remoteHeader = $this->getFileHeader($remotePlugin);
|
106 |
+
$this->setInfoFromHeader($remoteHeader, $info);
|
107 |
+
}
|
108 |
+
|
109 |
+
//Try parsing readme.txt. If it's formatted according to WordPress.org standards, it will contain
|
110 |
+
//a lot of useful information like the required/tested WP version, changelog, and so on.
|
111 |
+
if ( $this->readmeTxtExistsLocally() ) {
|
112 |
+
$this->setInfoFromRemoteReadme($ref, $info);
|
113 |
+
}
|
114 |
+
|
115 |
+
//The changelog might be in a separate file.
|
116 |
+
if ( empty($info->sections['changelog']) ) {
|
117 |
+
$info->sections['changelog'] = $this->getRemoteChangelog($ref);
|
118 |
+
if ( empty($info->sections['changelog']) ) {
|
119 |
+
$info->sections['changelog'] = __('There is no changelog available.', 'plugin-update-checker');
|
120 |
+
}
|
121 |
+
}
|
122 |
+
|
123 |
+
if ( empty($info->last_updated) ) {
|
124 |
+
//Fetch the latest commit that changed the main plugin file and use it as the "last_updated" date.
|
125 |
+
//It's reasonable to assume that every update will change the version number in that file.
|
126 |
+
$latestCommit = $this->getLatestCommit($mainPluginFile, $ref);
|
127 |
+
if ( $latestCommit !== null ) {
|
128 |
+
$info->last_updated = $latestCommit->commit->author->date;
|
129 |
+
}
|
130 |
+
}
|
131 |
+
$info = apply_filters('puc_request_info_result-' . $this->slug, $info, null);
|
132 |
+
return $info;
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Get the latest release from GitHub.
|
137 |
+
*
|
138 |
+
* @return StdClass|null
|
139 |
+
*/
|
140 |
+
protected function getLatestRelease() {
|
141 |
+
$releases = $this->api('/repos/:user/:repo/releases');
|
142 |
+
if ( is_wp_error($releases) || !is_array($releases) || !isset($releases[0]) ) {
|
143 |
+
return null;
|
144 |
+
}
|
145 |
+
|
146 |
+
$latestRelease = $releases[0];
|
147 |
+
return $latestRelease;
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* Get the tag that looks like the highest version number.
|
152 |
+
*
|
153 |
+
* @return StdClass|null
|
154 |
+
*/
|
155 |
+
protected function getLatestTag() {
|
156 |
+
$tags = $this->api('/repos/:user/:repo/tags');
|
157 |
+
|
158 |
+
if ( is_wp_error($tags) || empty($tags) || !is_array($tags) ) {
|
159 |
+
return null;
|
160 |
+
}
|
161 |
+
|
162 |
+
usort($tags, array($this, 'compareTagNames')); //Sort from highest to lowest.
|
163 |
+
return $tags[0];
|
164 |
+
}
|
165 |
+
|
166 |
+
/**
|
167 |
+
* Compare two GitHub tags as if they were version number.
|
168 |
+
*
|
169 |
+
* @param string $tag1
|
170 |
+
* @param string $tag2
|
171 |
+
* @return int
|
172 |
+
*/
|
173 |
+
protected function compareTagNames($tag1, $tag2) {
|
174 |
+
if ( !isset($tag1->name) ) {
|
175 |
+
return 1;
|
176 |
+
}
|
177 |
+
if ( !isset($tag2->name) ) {
|
178 |
+
return -1;
|
179 |
+
}
|
180 |
+
return -version_compare($tag1->name, $tag2->name);
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
* Get the latest commit that changed the specified file.
|
185 |
+
*
|
186 |
+
* @param string $filename
|
187 |
+
* @param string $ref Reference name (e.g. branch or tag).
|
188 |
+
* @return StdClass|null
|
189 |
+
*/
|
190 |
+
protected function getLatestCommit($filename, $ref = 'master') {
|
191 |
+
$commits = $this->api(
|
192 |
+
'/repos/:user/:repo/commits',
|
193 |
+
array(
|
194 |
+
'path' => $filename,
|
195 |
+
'sha' => $ref,
|
196 |
+
)
|
197 |
+
);
|
198 |
+
if ( !is_wp_error($commits) && is_array($commits) && isset($commits[0]) ) {
|
199 |
+
return $commits[0];
|
200 |
+
}
|
201 |
+
return null;
|
202 |
+
}
|
203 |
+
|
204 |
+
protected function getRemoteChangelog($ref = '') {
|
205 |
+
$filename = $this->getChangelogFilename();
|
206 |
+
if ( empty($filename) ) {
|
207 |
+
return null;
|
208 |
+
}
|
209 |
+
|
210 |
+
$changelog = $this->getRemoteFile($filename, $ref);
|
211 |
+
if ( $changelog === null ) {
|
212 |
+
return null;
|
213 |
+
}
|
214 |
+
return $this->parseMarkdown($changelog);
|
215 |
+
}
|
216 |
+
|
217 |
+
protected function getChangelogFilename() {
|
218 |
+
$pluginDirectory = dirname($this->pluginAbsolutePath);
|
219 |
+
if ( empty($this->pluginAbsolutePath) || !is_dir($pluginDirectory) || ($pluginDirectory === '.') ) {
|
220 |
+
return null;
|
221 |
+
}
|
222 |
+
|
223 |
+
$possibleNames = array('CHANGES.md', 'CHANGELOG.md', 'changes.md', 'changelog.md');
|
224 |
+
$files = scandir($pluginDirectory);
|
225 |
+
$foundNames = array_intersect($possibleNames, $files);
|
226 |
+
|
227 |
+
if ( !empty($foundNames) ) {
|
228 |
+
return reset($foundNames);
|
229 |
+
}
|
230 |
+
return null;
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* Convert Markdown to HTML.
|
235 |
+
*
|
236 |
+
* @param string $markdown
|
237 |
+
* @return string
|
238 |
+
*/
|
239 |
+
protected function parseMarkdown($markdown) {
|
240 |
+
if ( !class_exists('Parsedown', false) ) {
|
241 |
+
require_once(dirname(__FILE__) . '/vendor/Parsedown' . (version_compare(PHP_VERSION, '5.3.0', '>=') ? '' : 'Legacy') . '.php');
|
242 |
+
}
|
243 |
+
|
244 |
+
$instance = Parsedown::instance();
|
245 |
+
return $instance->text($markdown);
|
246 |
+
}
|
247 |
+
|
248 |
+
/**
|
249 |
+
* Perform a GitHub API request.
|
250 |
+
*
|
251 |
+
* @param string $url
|
252 |
+
* @param array $queryParams
|
253 |
+
* @return mixed|WP_Error
|
254 |
+
*/
|
255 |
+
protected function api($url, $queryParams = array()) {
|
256 |
+
$variables = array(
|
257 |
+
'user' => $this->userName,
|
258 |
+
'repo' => $this->repositoryName,
|
259 |
+
);
|
260 |
+
foreach ($variables as $name => $value) {
|
261 |
+
$url = str_replace('/:' . $name, '/' . urlencode($value), $url);
|
262 |
+
}
|
263 |
+
$url = 'https://api.github.com' . $url;
|
264 |
+
|
265 |
+
if ( !empty($this->accessToken) ) {
|
266 |
+
$queryParams['access_token'] = $this->accessToken;
|
267 |
+
}
|
268 |
+
if ( !empty($queryParams) ) {
|
269 |
+
$url = add_query_arg($queryParams, $url);
|
270 |
+
}
|
271 |
+
|
272 |
+
$response = wp_remote_get($url, array('timeout' => 10));
|
273 |
+
if ( is_wp_error($response) ) {
|
274 |
+
return $response;
|
275 |
+
}
|
276 |
+
|
277 |
+
$code = wp_remote_retrieve_response_code($response);
|
278 |
+
$body = wp_remote_retrieve_body($response);
|
279 |
+
if ( $code === 200 ) {
|
280 |
+
$document = json_decode($body);
|
281 |
+
return $document;
|
282 |
+
}
|
283 |
+
|
284 |
+
return new WP_Error(
|
285 |
+
'puc-github-http-error',
|
286 |
+
'GitHub API error. HTTP status: ' . $code
|
287 |
+
);
|
288 |
+
}
|
289 |
+
|
290 |
+
/**
|
291 |
+
* Set the access token that will be used to make authenticated GitHub API requests.
|
292 |
+
*
|
293 |
+
* @param string $accessToken
|
294 |
+
*/
|
295 |
+
public function setAccessToken($accessToken) {
|
296 |
+
$this->accessToken = $accessToken;
|
297 |
+
}
|
298 |
+
|
299 |
+
/**
|
300 |
+
* Get the contents of a file from a specific branch or tag.
|
301 |
+
*
|
302 |
+
* @param string $path File name.
|
303 |
+
* @param string $ref
|
304 |
+
* @return null|string Either the contents of the file, or null if the file doesn't exist or there's an error.
|
305 |
+
*/
|
306 |
+
protected function getRemoteFile($path, $ref = 'master') {
|
307 |
+
$apiUrl = '/repos/:user/:repo/contents/' . $path;
|
308 |
+
$response = $this->api($apiUrl, array('ref' => $ref));
|
309 |
+
|
310 |
+
if ( is_wp_error($response) || !isset($response->content) || ($response->encoding !== 'base64') ) {
|
311 |
+
return null;
|
312 |
+
}
|
313 |
+
return base64_decode($response->content);
|
314 |
+
}
|
315 |
+
|
316 |
+
/**
|
317 |
+
* Parse plugin metadata from the header comment.
|
318 |
+
* This is basically a simplified version of the get_file_data() function from /wp-includes/functions.php.
|
319 |
+
*
|
320 |
+
* @param $content
|
321 |
+
* @return array
|
322 |
+
*/
|
323 |
+
protected function getFileHeader($content) {
|
324 |
+
$headers = array(
|
325 |
+
'Name' => 'Plugin Name',
|
326 |
+
'PluginURI' => 'Plugin URI',
|
327 |
+
'Version' => 'Version',
|
328 |
+
'Description' => 'Description',
|
329 |
+
'Author' => 'Author',
|
330 |
+
'AuthorURI' => 'Author URI',
|
331 |
+
'TextDomain' => 'Text Domain',
|
332 |
+
'DomainPath' => 'Domain Path',
|
333 |
+
'Network' => 'Network',
|
334 |
+
|
335 |
+
//The newest WordPress version that this plugin requires or has been tested with.
|
336 |
+
//We support several different formats for compatibility with other libraries.
|
337 |
+
'Tested WP' => 'Tested WP',
|
338 |
+
'Requires WP' => 'Requires WP',
|
339 |
+
'Tested up to' => 'Tested up to',
|
340 |
+
'Requires at least' => 'Requires at least',
|
341 |
+
);
|
342 |
+
|
343 |
+
$content = str_replace("\r", "\n", $content); //Normalize line endings.
|
344 |
+
$results = array();
|
345 |
+
foreach ($headers as $field => $name) {
|
346 |
+
$success = preg_match('/^[ \t\/*#@]*' . preg_quote($name, '/') . ':(.*)$/mi', $content, $matches);
|
347 |
+
if ( ($success === 1) && $matches[1] ) {
|
348 |
+
$results[$field] = _cleanup_header_comment($matches[1]);
|
349 |
+
} else {
|
350 |
+
$results[$field] = '';
|
351 |
+
}
|
352 |
+
}
|
353 |
+
|
354 |
+
return $results;
|
355 |
+
}
|
356 |
+
|
357 |
+
/**
|
358 |
+
* Copy plugin metadata from a file header to a PluginInfo object.
|
359 |
+
*
|
360 |
+
* @param array $fileHeader
|
361 |
+
* @param PluginInfo_3_1 $pluginInfo
|
362 |
+
*/
|
363 |
+
protected function setInfoFromHeader($fileHeader, $pluginInfo) {
|
364 |
+
$headerToPropertyMap = array(
|
365 |
+
'Version' => 'version',
|
366 |
+
'Name' => 'name',
|
367 |
+
'PluginURI' => 'homepage',
|
368 |
+
'Author' => 'author',
|
369 |
+
'AuthorName' => 'author',
|
370 |
+
'AuthorURI' => 'author_homepage',
|
371 |
+
|
372 |
+
'Requires WP' => 'requires',
|
373 |
+
'Tested WP' => 'tested',
|
374 |
+
'Requires at least' => 'requires',
|
375 |
+
'Tested up to' => 'tested',
|
376 |
+
);
|
377 |
+
foreach ($headerToPropertyMap as $headerName => $property) {
|
378 |
+
if ( isset($fileHeader[$headerName]) && !empty($fileHeader[$headerName]) ) {
|
379 |
+
$pluginInfo->$property = $fileHeader[$headerName];
|
380 |
+
}
|
381 |
+
}
|
382 |
+
|
383 |
+
if ( !empty($fileHeader['Description']) ) {
|
384 |
+
$pluginInfo->sections['description'] = $fileHeader['Description'];
|
385 |
+
}
|
386 |
+
}
|
387 |
+
|
388 |
+
/**
|
389 |
+
* Copy plugin metadata from the remote readme.txt file.
|
390 |
+
*
|
391 |
+
* @param string $ref GitHub tag or branch where to look for the readme.
|
392 |
+
* @param PluginInfo_3_1 $pluginInfo
|
393 |
+
*/
|
394 |
+
protected function setInfoFromRemoteReadme($ref, $pluginInfo) {
|
395 |
+
$readmeTxt = $this->getRemoteFile('readme.txt', $ref);
|
396 |
+
if ( empty($readmeTxt) ) {
|
397 |
+
return;
|
398 |
+
}
|
399 |
+
|
400 |
+
$readme = $this->parseReadme($readmeTxt);
|
401 |
+
|
402 |
+
if ( isset($readme['sections']) ) {
|
403 |
+
$pluginInfo->sections = array_merge($pluginInfo->sections, $readme['sections']);
|
404 |
+
}
|
405 |
+
if ( !empty($readme['tested_up_to']) ) {
|
406 |
+
$pluginInfo->tested = $readme['tested_up_to'];
|
407 |
+
}
|
408 |
+
if ( !empty($readme['requires_at_least']) ) {
|
409 |
+
$pluginInfo->requires = $readme['requires_at_least'];
|
410 |
+
}
|
411 |
+
|
412 |
+
if ( isset($readme['upgrade_notice'], $readme['upgrade_notice'][$pluginInfo->version]) ) {
|
413 |
+
$pluginInfo->upgrade_notice = $readme['upgrade_notice'][$pluginInfo->version];
|
414 |
+
}
|
415 |
+
}
|
416 |
+
|
417 |
+
protected function parseReadme($content) {
|
418 |
+
if ( !class_exists('PucReadmeParser', false) ) {
|
419 |
+
require_once(dirname(__FILE__) . '/vendor/readme-parser.php');
|
420 |
+
}
|
421 |
+
$parser = new PucReadmeParser();
|
422 |
+
return $parser->parse_readme_contents($content);
|
423 |
+
}
|
424 |
+
|
425 |
+
/**
|
426 |
+
* Check if the currently installed version has a readme.txt file.
|
427 |
+
*
|
428 |
+
* @return bool
|
429 |
+
*/
|
430 |
+
protected function readmeTxtExistsLocally() {
|
431 |
+
$pluginDirectory = dirname($this->pluginAbsolutePath);
|
432 |
+
if ( empty($this->pluginAbsolutePath) || !is_dir($pluginDirectory) || ($pluginDirectory === '.') ) {
|
433 |
+
return false;
|
434 |
+
}
|
435 |
+
return is_file($pluginDirectory . '/readme.txt');
|
436 |
+
}
|
437 |
+
|
438 |
+
/**
|
439 |
+
* Generate a URL to download a ZIP archive of the specified branch/tag/etc.
|
440 |
+
*
|
441 |
+
* @param string $ref
|
442 |
+
* @return string
|
443 |
+
*/
|
444 |
+
protected function buildArchiveDownloadUrl($ref = 'master') {
|
445 |
+
$url = sprintf(
|
446 |
+
'https://api.github.com/repos/%1$s/%2$s/zipball/%3$s',
|
447 |
+
urlencode($this->userName),
|
448 |
+
urlencode($this->repositoryName),
|
449 |
+
urlencode($ref)
|
450 |
+
);
|
451 |
+
if ( !empty($this->accessToken) ) {
|
452 |
+
$url = add_query_arg('access_token', $this->accessToken, $url);
|
453 |
+
}
|
454 |
+
return $url;
|
455 |
+
}
|
456 |
+
}
|
457 |
+
|
458 |
+
endif;
|
includes/plugin-update-checker/js/debug-bar.js
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
jQuery(function($) {
|
2 |
+
|
3 |
+
function runAjaxAction(button, action) {
|
4 |
+
button = $(button);
|
5 |
+
var panel = button.closest('.puc-debug-bar-panel');
|
6 |
+
var responseBox = button.closest('td').find('.puc-ajax-response');
|
7 |
+
|
8 |
+
responseBox.text('Processing...').show();
|
9 |
+
$.post(
|
10 |
+
ajaxurl,
|
11 |
+
{
|
12 |
+
action : action,
|
13 |
+
slug : panel.data('slug'),
|
14 |
+
_wpnonce: panel.data('nonce')
|
15 |
+
},
|
16 |
+
function(data) {
|
17 |
+
responseBox.html(data);
|
18 |
+
},
|
19 |
+
'html'
|
20 |
+
);
|
21 |
+
}
|
22 |
+
|
23 |
+
$('.puc-debug-bar-panel input[name="puc-check-now-button"]').click(function() {
|
24 |
+
runAjaxAction(this, 'puc_debug_check_now');
|
25 |
+
return false;
|
26 |
+
});
|
27 |
+
|
28 |
+
$('.puc-debug-bar-panel input[name="puc-request-info-button"]').click(function() {
|
29 |
+
runAjaxAction(this, 'puc_debug_request_info');
|
30 |
+
return false;
|
31 |
+
});
|
32 |
+
|
33 |
+
|
34 |
+
// Debug Bar uses the panel class name as part of its link and container IDs. This means we can
|
35 |
+
// end up with multiple identical IDs if more than one plugin uses the update checker library.
|
36 |
+
// Fix it by replacing the class name with the plugin slug.
|
37 |
+
var panels = $('#debug-menu-targets').find('.puc-debug-bar-panel');
|
38 |
+
panels.each(function(index) {
|
39 |
+
var panel = $(this);
|
40 |
+
var slug = panel.data('slug');
|
41 |
+
var target = panel.closest('.debug-menu-target');
|
42 |
+
|
43 |
+
//Change the panel wrapper ID.
|
44 |
+
target.attr('id', 'debug-menu-target-puc-' + slug);
|
45 |
+
|
46 |
+
//Change the menu link ID as well and point it at the new target ID.
|
47 |
+
$('#puc-debug-menu-link-' + panel.data('slug'))
|
48 |
+
.closest('.debug-menu-link')
|
49 |
+
.attr('id', 'debug-menu-link-puc-' + slug)
|
50 |
+
.attr('href', '#' + target.attr('id'));
|
51 |
+
});
|
52 |
+
});
|
includes/plugin-update-checker/languages/plugin-update-checker-fr_FR.mo
ADDED
Binary file
|
includes/plugin-update-checker/languages/plugin-update-checker-fr_FR.po
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
msgid ""
|
2 |
+
msgstr ""
|
3 |
+
"Project-Id-Version: plugin-update-checker\n"
|
4 |
+
"POT-Creation-Date: 2016-02-17 14:21+0100\n"
|
5 |
+
"PO-Revision-Date: 2016-02-17 14:22+0100\n"
|
6 |
+
"Last-Translator: studio RVOLA <hello@rvola.com>\n"
|
7 |
+
"Language-Team: studio RVOLA <hello@rvola.com>\n"
|
8 |
+
"Language: fr_FR\n"
|
9 |
+
"MIME-Version: 1.0\n"
|
10 |
+
"Content-Type: text/plain; charset=UTF-8\n"
|
11 |
+
"Content-Transfer-Encoding: 8bit\n"
|
12 |
+
"X-Generator: Poedit 1.8.7\n"
|
13 |
+
"X-Poedit-Basepath: ..\n"
|
14 |
+
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
15 |
+
"X-Poedit-SourceCharset: UTF-8\n"
|
16 |
+
"X-Poedit-KeywordsList: __;_e\n"
|
17 |
+
"X-Poedit-SearchPath-0: .\n"
|
18 |
+
|
19 |
+
#: github-checker.php:120
|
20 |
+
msgid "There is no changelog available."
|
21 |
+
msgstr "Il n’y a aucun changelog disponible."
|
22 |
+
|
23 |
+
#: plugin-update-checker.php:637
|
24 |
+
msgid "Check for updates"
|
25 |
+
msgstr "Vérifier les mises à jour"
|
26 |
+
|
27 |
+
#: plugin-update-checker.php:681
|
28 |
+
msgid "This plugin is up to date."
|
29 |
+
msgstr "Ce plugin est à jour."
|
30 |
+
|
31 |
+
#: plugin-update-checker.php:683
|
32 |
+
msgid "A new version of this plugin is available."
|
33 |
+
msgstr "Une nouvelle version de ce plugin est disponible."
|
34 |
+
|
35 |
+
#: plugin-update-checker.php:685
|
36 |
+
#, php-format
|
37 |
+
msgid "Unknown update checker status \"%s\""
|
38 |
+
msgstr "Un problème inconnu est survenu \"%s\""
|
includes/plugin-update-checker/languages/plugin-update-checker-hu_HU.mo
ADDED
Binary file
|
includes/plugin-update-checker/languages/plugin-update-checker-hu_HU.po
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
msgid ""
|
2 |
+
msgstr ""
|
3 |
+
"Project-Id-Version: plugin-update-checker\n"
|
4 |
+
"POT-Creation-Date: 2016-01-11 21:23+0100\n"
|
5 |
+
"PO-Revision-Date: 2016-01-11 21:25+0100\n"
|
6 |
+
"Last-Translator: Tamás András Horváth <htomy92@gmail.com>\n"
|
7 |
+
"Language-Team: \n"
|
8 |
+
"Language: hu_HU\n"
|
9 |
+
"MIME-Version: 1.0\n"
|
10 |
+
"Content-Type: text/plain; charset=UTF-8\n"
|
11 |
+
"Content-Transfer-Encoding: 8bit\n"
|
12 |
+
"X-Generator: Poedit 1.8.6\n"
|
13 |
+
"X-Poedit-Basepath: ..\n"
|
14 |
+
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
15 |
+
"X-Poedit-SourceCharset: UTF-8\n"
|
16 |
+
"X-Poedit-KeywordsList: __;_e\n"
|
17 |
+
"X-Poedit-SearchPath-0: .\n"
|
18 |
+
|
19 |
+
#: github-checker.php:137
|
20 |
+
msgid "There is no changelog available."
|
21 |
+
msgstr "Nem érhető el a changelog."
|
22 |
+
|
23 |
+
#: plugin-update-checker.php:852
|
24 |
+
msgid "Check for updates"
|
25 |
+
msgstr "Frissítés ellenőrzése"
|
26 |
+
|
27 |
+
#: plugin-update-checker.php:896
|
28 |
+
msgid "This plugin is up to date."
|
29 |
+
msgstr "Ez a plugin naprakész."
|
30 |
+
|
31 |
+
#: plugin-update-checker.php:898
|
32 |
+
msgid "A new version of this plugin is available."
|
33 |
+
msgstr "Új verzió érhető el a kiegészítőhöz"
|
34 |
+
|
35 |
+
#: plugin-update-checker.php:900
|
36 |
+
#, php-format
|
37 |
+
msgid "Unknown update checker status \"%s\""
|
38 |
+
msgstr "Ismeretlen a frissítés ellenőrző státusza \"%s\""
|
39 |
+
|
40 |
+
#~ msgid "Every %d hours"
|
41 |
+
#~ msgstr "Minden %d órában"
|
includes/plugin-update-checker/languages/plugin-update-checker.pot
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#, fuzzy
|
2 |
+
msgid ""
|
3 |
+
msgstr ""
|
4 |
+
"Project-Id-Version: plugin-update-checker\n"
|
5 |
+
"POT-Creation-Date: 2016-01-11 21:22+0100\n"
|
6 |
+
"PO-Revision-Date: 2016-01-10 20:59+0100\n"
|
7 |
+
"Last-Translator: Tamás András Horváth <htomy92@gmail.com>\n"
|
8 |
+
"Language-Team: \n"
|
9 |
+
"Language: en_US\n"
|
10 |
+
"MIME-Version: 1.0\n"
|
11 |
+
"Content-Type: text/plain; charset=UTF-8\n"
|
12 |
+
"Content-Transfer-Encoding: 8bit\n"
|
13 |
+
"X-Generator: Poedit 1.8.6\n"
|
14 |
+
"X-Poedit-Basepath: ..\n"
|
15 |
+
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
16 |
+
"X-Poedit-SourceCharset: UTF-8\n"
|
17 |
+
"X-Poedit-KeywordsList: __;_e\n"
|
18 |
+
"X-Poedit-SearchPath-0: .\n"
|
19 |
+
|
20 |
+
#: github-checker.php:137
|
21 |
+
msgid "There is no changelog available."
|
22 |
+
msgstr ""
|
23 |
+
|
24 |
+
#: plugin-update-checker.php:852
|
25 |
+
msgid "Check for updates"
|
26 |
+
msgstr ""
|
27 |
+
|
28 |
+
#: plugin-update-checker.php:896
|
29 |
+
msgid "This plugin is up to date."
|
30 |
+
msgstr ""
|
31 |
+
|
32 |
+
#: plugin-update-checker.php:898
|
33 |
+
msgid "A new version of this plugin is available."
|
34 |
+
msgstr ""
|
35 |
+
|
36 |
+
#: plugin-update-checker.php:900
|
37 |
+
#, php-format
|
38 |
+
msgid "Unknown update checker status \"%s\""
|
39 |
+
msgstr ""
|
includes/plugin-update-checker/license.txt
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Copyright (c) 2014 Jānis Elsts
|
2 |
+
|
3 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4 |
+
|
5 |
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6 |
+
|
7 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
includes/plugin-update-checker/plugin-update-checker.php
ADDED
@@ -0,0 +1,1641 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Plugin Update Checker Library 3.1
|
4 |
+
* http://w-shadow.com/
|
5 |
+
*
|
6 |
+
* Copyright 2016 Janis Elsts
|
7 |
+
* Released under the MIT license. See license.txt for details.
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( !class_exists('PluginUpdateChecker_3_1', false) ):
|
11 |
+
|
12 |
+
/**
|
13 |
+
* A custom plugin update checker.
|
14 |
+
*
|
15 |
+
* @author Janis Elsts
|
16 |
+
* @copyright 2016
|
17 |
+
* @version 3.0
|
18 |
+
* @access public
|
19 |
+
*/
|
20 |
+
class PluginUpdateChecker_3_1 {
|
21 |
+
public $metadataUrl = ''; //The URL of the plugin's metadata file.
|
22 |
+
public $pluginAbsolutePath = ''; //Full path of the main plugin file.
|
23 |
+
public $pluginFile = ''; //Plugin filename relative to the plugins directory. Many WP APIs use this to identify plugins.
|
24 |
+
public $slug = ''; //Plugin slug.
|
25 |
+
public $optionName = ''; //Where to store the update info.
|
26 |
+
public $muPluginFile = ''; //For MU plugins, the plugin filename relative to the mu-plugins directory.
|
27 |
+
|
28 |
+
public $debugMode = false; //Set to TRUE to enable error reporting. Errors are raised using trigger_error()
|
29 |
+
//and should be logged to the standard PHP error log.
|
30 |
+
public $scheduler;
|
31 |
+
|
32 |
+
protected $upgraderStatus;
|
33 |
+
|
34 |
+
private $debugBarPlugin = null;
|
35 |
+
private $cachedInstalledVersion = null;
|
36 |
+
|
37 |
+
private $metadataHost = ''; //The host component of $metadataUrl.
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Class constructor.
|
41 |
+
*
|
42 |
+
* @param string $metadataUrl The URL of the plugin's metadata file.
|
43 |
+
* @param string $pluginFile Fully qualified path to the main plugin file.
|
44 |
+
* @param string $slug The plugin's 'slug'. If not specified, the filename part of $pluginFile sans '.php' will be used as the slug.
|
45 |
+
* @param integer $checkPeriod How often to check for updates (in hours). Defaults to checking every 12 hours. Set to 0 to disable automatic update checks.
|
46 |
+
* @param string $optionName Where to store book-keeping info about update checks. Defaults to 'external_updates-$slug'.
|
47 |
+
* @param string $muPluginFile Optional. The plugin filename relative to the mu-plugins directory.
|
48 |
+
*/
|
49 |
+
public function __construct($metadataUrl, $pluginFile, $slug = '', $checkPeriod = 12, $optionName = '', $muPluginFile = ''){
|
50 |
+
$this->metadataUrl = $metadataUrl;
|
51 |
+
$this->pluginAbsolutePath = $pluginFile;
|
52 |
+
$this->pluginFile = plugin_basename($this->pluginAbsolutePath);
|
53 |
+
$this->muPluginFile = $muPluginFile;
|
54 |
+
$this->slug = $slug;
|
55 |
+
$this->optionName = $optionName;
|
56 |
+
$this->debugMode = (bool)(constant('WP_DEBUG'));
|
57 |
+
|
58 |
+
//If no slug is specified, use the name of the main plugin file as the slug.
|
59 |
+
//For example, 'my-cool-plugin/cool-plugin.php' becomes 'cool-plugin'.
|
60 |
+
if ( empty($this->slug) ){
|
61 |
+
$this->slug = basename($this->pluginFile, '.php');
|
62 |
+
}
|
63 |
+
|
64 |
+
if ( empty($this->optionName) ){
|
65 |
+
$this->optionName = 'external_updates-' . $this->slug;
|
66 |
+
}
|
67 |
+
|
68 |
+
//Backwards compatibility: If the plugin is a mu-plugin but no $muPluginFile is specified, assume
|
69 |
+
//it's the same as $pluginFile given that it's not in a subdirectory (WP only looks in the base dir).
|
70 |
+
if ( (strpbrk($this->pluginFile, '/\\') === false) && $this->isUnknownMuPlugin() ) {
|
71 |
+
$this->muPluginFile = $this->pluginFile;
|
72 |
+
}
|
73 |
+
|
74 |
+
$this->scheduler = $this->createScheduler($checkPeriod);
|
75 |
+
$this->upgraderStatus = new PucUpgraderStatus_3_1();
|
76 |
+
|
77 |
+
$this->installHooks();
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Create an instance of the scheduler.
|
82 |
+
*
|
83 |
+
* This is implemented as a method to make it possible for plugins to subclass the update checker
|
84 |
+
* and substitute their own scheduler.
|
85 |
+
*
|
86 |
+
* @param int $checkPeriod
|
87 |
+
* @return PucScheduler_3_1
|
88 |
+
*/
|
89 |
+
protected function createScheduler($checkPeriod) {
|
90 |
+
return new PucScheduler_3_1($this, $checkPeriod);
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Install the hooks required to run periodic update checks and inject update info
|
95 |
+
* into WP data structures.
|
96 |
+
*
|
97 |
+
* @return void
|
98 |
+
*/
|
99 |
+
protected function installHooks(){
|
100 |
+
//Override requests for plugin information
|
101 |
+
add_filter('plugins_api', array($this, 'injectInfo'), 20, 3);
|
102 |
+
|
103 |
+
//Insert our update info into the update array maintained by WP.
|
104 |
+
add_filter('site_transient_update_plugins', array($this,'injectUpdate')); //WP 3.0+
|
105 |
+
add_filter('transient_update_plugins', array($this,'injectUpdate')); //WP 2.8+
|
106 |
+
add_filter('site_transient_update_plugins', array($this, 'injectTranslationUpdates'));
|
107 |
+
|
108 |
+
add_filter('plugin_row_meta', array($this, 'addCheckForUpdatesLink'), 10, 2);
|
109 |
+
add_action('admin_init', array($this, 'handleManualCheck'));
|
110 |
+
add_action('all_admin_notices', array($this, 'displayManualCheckResult'));
|
111 |
+
|
112 |
+
//Clear the version number cache when something - anything - is upgraded or WP clears the update cache.
|
113 |
+
add_filter('upgrader_post_install', array($this, 'clearCachedVersion'));
|
114 |
+
add_action('delete_site_transient_update_plugins', array($this, 'clearCachedVersion'));
|
115 |
+
//Clear translation updates when WP clears the update cache.
|
116 |
+
//This needs to be done directly because the library doesn't actually remove obsolete plugin updates,
|
117 |
+
//it just hides them (see getUpdate()). We can't do that with translations - too much disk I/O.
|
118 |
+
add_action('delete_site_transient_update_plugins', array($this, 'clearCachedTranslationUpdates'));
|
119 |
+
|
120 |
+
if ( did_action('plugins_loaded') ) {
|
121 |
+
$this->initDebugBarPanel();
|
122 |
+
} else {
|
123 |
+
add_action('plugins_loaded', array($this, 'initDebugBarPanel'));
|
124 |
+
}
|
125 |
+
|
126 |
+
//Rename the update directory to be the same as the existing directory.
|
127 |
+
add_filter('upgrader_source_selection', array($this, 'fixDirectoryName'), 10, 3);
|
128 |
+
|
129 |
+
//Enable language support (i18n).
|
130 |
+
load_plugin_textdomain('plugin-update-checker', false, plugin_basename(dirname(__FILE__)) . '/languages');
|
131 |
+
|
132 |
+
//Allow HTTP requests to the metadata URL even if it's on a local host.
|
133 |
+
$this->metadataHost = @parse_url($this->metadataUrl, PHP_URL_HOST);
|
134 |
+
add_filter('http_request_host_is_external', array($this, 'allowMetadataHost'), 10, 2);
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* Explicitly allow HTTP requests to the metadata URL.
|
139 |
+
*
|
140 |
+
* WordPress has a security feature where the HTTP API will reject all requests that are sent to
|
141 |
+
* another site hosted on the same server as the current site (IP match), a local host, or a local
|
142 |
+
* IP, unless the host exactly matches the current site.
|
143 |
+
*
|
144 |
+
* This feature is opt-in (at least in WP 4.4). Apparently some people enable it.
|
145 |
+
*
|
146 |
+
* That can be a problem when you're developing your plugin and you decide to host the update information
|
147 |
+
* on the same server as your test site. Update requests will mysteriously fail.
|
148 |
+
*
|
149 |
+
* We fix that by adding an exception for the metadata host.
|
150 |
+
*
|
151 |
+
* @param bool $allow
|
152 |
+
* @param string $host
|
153 |
+
* @return bool
|
154 |
+
*/
|
155 |
+
public function allowMetadataHost($allow, $host) {
|
156 |
+
if ( strtolower($host) === strtolower($this->metadataHost) ) {
|
157 |
+
return true;
|
158 |
+
}
|
159 |
+
return $allow;
|
160 |
+
}
|
161 |
+
|
162 |
+
/**
|
163 |
+
* Retrieve plugin info from the configured API endpoint.
|
164 |
+
*
|
165 |
+
* @uses wp_remote_get()
|
166 |
+
*
|
167 |
+
* @param array $queryArgs Additional query arguments to append to the request. Optional.
|
168 |
+
* @return PluginInfo_3_1
|
169 |
+
*/
|
170 |
+
public function requestInfo($queryArgs = array()){
|
171 |
+
//Query args to append to the URL. Plugins can add their own by using a filter callback (see addQueryArgFilter()).
|
172 |
+
$installedVersion = $this->getInstalledVersion();
|
173 |
+
$queryArgs['installed_version'] = ($installedVersion !== null) ? $installedVersion : '';
|
174 |
+
$queryArgs = apply_filters('puc_request_info_query_args-'.$this->slug, $queryArgs);
|
175 |
+
|
176 |
+
//Various options for the wp_remote_get() call. Plugins can filter these, too.
|
177 |
+
$options = array(
|
178 |
+
'timeout' => 10, //seconds
|
179 |
+
'headers' => array(
|
180 |
+
'Accept' => 'application/json'
|
181 |
+
),
|
182 |
+
);
|
183 |
+
$options = apply_filters('puc_request_info_options-'.$this->slug, $options);
|
184 |
+
|
185 |
+
//The plugin info should be at 'http://your-api.com/url/here/$slug/info.json'
|
186 |
+
$url = $this->metadataUrl;
|
187 |
+
if ( !empty($queryArgs) ){
|
188 |
+
$url = add_query_arg($queryArgs, $url);
|
189 |
+
}
|
190 |
+
|
191 |
+
$result = wp_remote_get(
|
192 |
+
$url,
|
193 |
+
$options
|
194 |
+
);
|
195 |
+
|
196 |
+
//Try to parse the response
|
197 |
+
$status = $this->validateApiResponse($result);
|
198 |
+
$pluginInfo = null;
|
199 |
+
if ( !is_wp_error($status) ){
|
200 |
+
$pluginInfo = PluginInfo_3_1::fromJson($result['body']);
|
201 |
+
if ( $pluginInfo !== null ) {
|
202 |
+
$pluginInfo->filename = $this->pluginFile;
|
203 |
+
$pluginInfo->slug = $this->slug;
|
204 |
+
}
|
205 |
+
} else {
|
206 |
+
$this->triggerError(
|
207 |
+
sprintf('The URL %s does not point to a valid plugin metadata file. ', $url)
|
208 |
+
. $status->get_error_message(),
|
209 |
+
E_USER_WARNING
|
210 |
+
);
|
211 |
+
}
|
212 |
+
|
213 |
+
$pluginInfo = apply_filters('puc_request_info_result-'.$this->slug, $pluginInfo, $result);
|
214 |
+
return $pluginInfo;
|
215 |
+
}
|
216 |
+
|
217 |
+
/**
|
218 |
+
* Check if $result is a successful update API response.
|
219 |
+
*
|
220 |
+
* @param array|WP_Error $result
|
221 |
+
* @return true|WP_Error
|
222 |
+
*/
|
223 |
+
private function validateApiResponse($result) {
|
224 |
+
if ( is_wp_error($result) ) { /** @var WP_Error $result */
|
225 |
+
return new WP_Error($result->get_error_code(), 'WP HTTP Error: ' . $result->get_error_message());
|
226 |
+
}
|
227 |
+
|
228 |
+
if ( !isset($result['response']['code']) ) {
|
229 |
+
return new WP_Error('puc_no_response_code', 'wp_remote_get() returned an unexpected result.');
|
230 |
+
}
|
231 |
+
|
232 |
+
if ( $result['response']['code'] !== 200 ) {
|
233 |
+
return new WP_Error(
|
234 |
+
'puc_unexpected_response_code',
|
235 |
+
'HTTP response code is ' . $result['response']['code'] . ' (expected: 200)'
|
236 |
+
);
|
237 |
+
}
|
238 |
+
|
239 |
+
if ( empty($result['body']) ) {
|
240 |
+
return new WP_Error('puc_empty_response', 'The metadata file appears to be empty.');
|
241 |
+
}
|
242 |
+
|
243 |
+
return true;
|
244 |
+
}
|
245 |
+
|
246 |
+
/**
|
247 |
+
* Retrieve the latest update (if any) from the configured API endpoint.
|
248 |
+
*
|
249 |
+
* @uses PluginUpdateChecker::requestInfo()
|
250 |
+
*
|
251 |
+
* @return PluginUpdate_3_1 An instance of PluginUpdate, or NULL when no updates are available.
|
252 |
+
*/
|
253 |
+
public function requestUpdate(){
|
254 |
+
//For the sake of simplicity, this function just calls requestInfo()
|
255 |
+
//and transforms the result accordingly.
|
256 |
+
$pluginInfo = $this->requestInfo(array('checking_for_updates' => '1'));
|
257 |
+
if ( $pluginInfo == null ){
|
258 |
+
return null;
|
259 |
+
}
|
260 |
+
$update = PluginUpdate_3_1::fromPluginInfo($pluginInfo);
|
261 |
+
|
262 |
+
//Keep only those translation updates that apply to this site.
|
263 |
+
$update->translations = $this->filterApplicableTranslations($update->translations);
|
264 |
+
|
265 |
+
return $update;
|
266 |
+
}
|
267 |
+
|
268 |
+
/**
|
269 |
+
* Filter a list of translation updates and return a new list that contains only updates
|
270 |
+
* that apply to the current site.
|
271 |
+
*
|
272 |
+
* @param array $translations
|
273 |
+
* @return array
|
274 |
+
*/
|
275 |
+
private function filterApplicableTranslations($translations) {
|
276 |
+
$languages = array_flip(array_values(get_available_languages()));
|
277 |
+
$installedTranslations = wp_get_installed_translations('plugins');
|
278 |
+
if ( isset($installedTranslations[$this->slug]) ) {
|
279 |
+
$installedTranslations = $installedTranslations[$this->slug];
|
280 |
+
} else {
|
281 |
+
$installedTranslations = array();
|
282 |
+
}
|
283 |
+
|
284 |
+
$applicableTranslations = array();
|
285 |
+
foreach($translations as $translation) {
|
286 |
+
//Does it match one of the available core languages?
|
287 |
+
$isApplicable = array_key_exists($translation->language, $languages);
|
288 |
+
//Is it more recent than an already-installed translation?
|
289 |
+
if ( isset($installedTranslations[$translation->language]) ) {
|
290 |
+
$updateTimestamp = strtotime($translation->updated);
|
291 |
+
$installedTimestamp = strtotime($installedTranslations[$translation->language]['PO-Revision-Date']);
|
292 |
+
$isApplicable = $updateTimestamp > $installedTimestamp;
|
293 |
+
}
|
294 |
+
|
295 |
+
if ( $isApplicable ) {
|
296 |
+
$applicableTranslations[] = $translation;
|
297 |
+
}
|
298 |
+
}
|
299 |
+
|
300 |
+
return $applicableTranslations;
|
301 |
+
}
|
302 |
+
|
303 |
+
/**
|
304 |
+
* Get the currently installed version of the plugin.
|
305 |
+
*
|
306 |
+
* @return string Version number.
|
307 |
+
*/
|
308 |
+
public function getInstalledVersion(){
|
309 |
+
if ( isset($this->cachedInstalledVersion) ) {
|
310 |
+
return $this->cachedInstalledVersion;
|
311 |
+
}
|
312 |
+
|
313 |
+
$pluginHeader = $this->getPluginHeader();
|
314 |
+
if ( isset($pluginHeader['Version']) ) {
|
315 |
+
$this->cachedInstalledVersion = $pluginHeader['Version'];
|
316 |
+
return $pluginHeader['Version'];
|
317 |
+
} else {
|
318 |
+
//This can happen if the filename points to something that is not a plugin.
|
319 |
+
$this->triggerError(
|
320 |
+
sprintf(
|
321 |
+
"Can't to read the Version header for '%s'. The filename is incorrect or is not a plugin.",
|
322 |
+
$this->pluginFile
|
323 |
+
),
|
324 |
+
E_USER_WARNING
|
325 |
+
);
|
326 |
+
return null;
|
327 |
+
}
|
328 |
+
}
|
329 |
+
|
330 |
+
/**
|
331 |
+
* Get plugin's metadata from its file header.
|
332 |
+
*
|
333 |
+
* @return array
|
334 |
+
*/
|
335 |
+
protected function getPluginHeader() {
|
336 |
+
if ( !is_file($this->pluginAbsolutePath) ) {
|
337 |
+
//This can happen if the plugin filename is wrong.
|
338 |
+
$this->triggerError(
|
339 |
+
sprintf(
|
340 |
+
"Can't to read the plugin header for '%s'. The file does not exist.",
|
341 |
+
$this->pluginFile
|
342 |
+
),
|
343 |
+
E_USER_WARNING
|
344 |
+
);
|
345 |
+
return array();
|
346 |
+
}
|
347 |
+
|
348 |
+
if ( !function_exists('get_plugin_data') ){
|
349 |
+
/** @noinspection PhpIncludeInspection */
|
350 |
+
require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
|
351 |
+
}
|
352 |
+
return get_plugin_data($this->pluginAbsolutePath, false, false);
|
353 |
+
}
|
354 |
+
|
355 |
+
/**
|
356 |
+
* Check for plugin updates.
|
357 |
+
* The results are stored in the DB option specified in $optionName.
|
358 |
+
*
|
359 |
+
* @return PluginUpdate_3_1|null
|
360 |
+
*/
|
361 |
+
public function checkForUpdates(){
|
362 |
+
$installedVersion = $this->getInstalledVersion();
|
363 |
+
//Fail silently if we can't find the plugin or read its header.
|
364 |
+
if ( $installedVersion === null ) {
|
365 |
+
$this->triggerError(
|
366 |
+
sprintf('Skipping update check for %s - installed version unknown.', $this->pluginFile),
|
367 |
+
E_USER_WARNING
|
368 |
+
);
|
369 |
+
return null;
|
370 |
+
}
|
371 |
+
|
372 |
+
$state = $this->getUpdateState();
|
373 |
+
if ( empty($state) ){
|
374 |
+
$state = new stdClass;
|
375 |
+
$state->lastCheck = 0;
|
376 |
+
$state->checkedVersion = '';
|
377 |
+
$state->update = null;
|
378 |
+
}
|
379 |
+
|
380 |
+
$state->lastCheck = time();
|
381 |
+
$state->checkedVersion = $installedVersion;
|
382 |
+
$this->setUpdateState($state); //Save before checking in case something goes wrong
|
383 |
+
|
384 |
+
$state->update = $this->requestUpdate();
|
385 |
+
$this->setUpdateState($state);
|
386 |
+
|
387 |
+
return $this->getUpdate();
|
388 |
+
}
|
389 |
+
|
390 |
+
/**
|
391 |
+
* Load the update checker state from the DB.
|
392 |
+
*
|
393 |
+
* @return stdClass|null
|
394 |
+
*/
|
395 |
+
public function getUpdateState() {
|
396 |
+
$state = get_site_option($this->optionName, null);
|
397 |
+
if ( empty($state) || !is_object($state)) {
|
398 |
+
$state = null;
|
399 |
+
}
|
400 |
+
|
401 |
+
if ( isset($state, $state->update) && is_object($state->update) ) {
|
402 |
+
$state->update = PluginUpdate_3_1::fromObject($state->update);
|
403 |
+
}
|
404 |
+
return $state;
|
405 |
+
}
|
406 |
+
|
407 |
+
|
408 |
+
/**
|
409 |
+
* Persist the update checker state to the DB.
|
410 |
+
*
|
411 |
+
* @param StdClass $state
|
412 |
+
* @return void
|
413 |
+
*/
|
414 |
+
private function setUpdateState($state) {
|
415 |
+
if ( isset($state->update) && is_object($state->update) && method_exists($state->update, 'toStdClass') ) {
|
416 |
+
$update = $state->update; /** @var PluginUpdate_3_1 $update */
|
417 |
+
$state->update = $update->toStdClass();
|
418 |
+
}
|
419 |
+
update_site_option($this->optionName, $state);
|
420 |
+
}
|
421 |
+
|
422 |
+
/**
|
423 |
+
* Reset update checker state - i.e. last check time, cached update data and so on.
|
424 |
+
*
|
425 |
+
* Call this when your plugin is being uninstalled, or if you want to
|
426 |
+
* clear the update cache.
|
427 |
+
*/
|
428 |
+
public function resetUpdateState() {
|
429 |
+
delete_site_option($this->optionName);
|
430 |
+
}
|
431 |
+
|
432 |
+
/**
|
433 |
+
* Intercept plugins_api() calls that request information about our plugin and
|
434 |
+
* use the configured API endpoint to satisfy them.
|
435 |
+
*
|
436 |
+
* @see plugins_api()
|
437 |
+
*
|
438 |
+
* @param mixed $result
|
439 |
+
* @param string $action
|
440 |
+
* @param array|object $args
|
441 |
+
* @return mixed
|
442 |
+
*/
|
443 |
+
public function injectInfo($result, $action = null, $args = null){
|
444 |
+
$relevant = ($action == 'plugin_information') && isset($args->slug) && (
|
445 |
+
($args->slug == $this->slug) || ($args->slug == dirname($this->pluginFile))
|
446 |
+
);
|
447 |
+
if ( !$relevant ) {
|
448 |
+
return $result;
|
449 |
+
}
|
450 |
+
|
451 |
+
$pluginInfo = $this->requestInfo();
|
452 |
+
$pluginInfo = apply_filters('puc_pre_inject_info-' . $this->slug, $pluginInfo);
|
453 |
+
if ( $pluginInfo ) {
|
454 |
+
return $pluginInfo->toWpFormat();
|
455 |
+
}
|
456 |
+
|
457 |
+
return $result;
|
458 |
+
}
|
459 |
+
|
460 |
+
/**
|
461 |
+
* Insert the latest update (if any) into the update list maintained by WP.
|
462 |
+
*
|
463 |
+
* @param StdClass $updates Update list.
|
464 |
+
* @return StdClass Modified update list.
|
465 |
+
*/
|
466 |
+
public function injectUpdate($updates){
|
467 |
+
//Is there an update to insert?
|
468 |
+
$update = $this->getUpdate();
|
469 |
+
|
470 |
+
//No update notifications for mu-plugins unless explicitly enabled. The MU plugin file
|
471 |
+
//is usually different from the main plugin file so the update wouldn't show up properly anyway.
|
472 |
+
if ( $this->isUnknownMuPlugin() ) {
|
473 |
+
$update = null;
|
474 |
+
}
|
475 |
+
|
476 |
+
if ( !empty($update) ) {
|
477 |
+
//Let plugins filter the update info before it's passed on to WordPress.
|
478 |
+
$update = apply_filters('puc_pre_inject_update-' . $this->slug, $update);
|
479 |
+
$updates = $this->addUpdateToList($updates, $update);
|
480 |
+
} else {
|
481 |
+
//Clean up any stale update info.
|
482 |
+
$updates = $this->removeUpdateFromList($updates);
|
483 |
+
}
|
484 |
+
|
485 |
+
return $updates;
|
486 |
+
}
|
487 |
+
|
488 |
+
/**
|
489 |
+
* @param StdClass|null $updates
|
490 |
+
* @param PluginUpdate_3_1 $updateToAdd
|
491 |
+
* @return StdClass
|
492 |
+
*/
|
493 |
+
private function addUpdateToList($updates, $updateToAdd) {
|
494 |
+
if ( !is_object($updates) ) {
|
495 |
+
$updates = new stdClass();
|
496 |
+
$updates->response = array();
|
497 |
+
}
|
498 |
+
|
499 |
+
$wpUpdate = $updateToAdd->toWpFormat();
|
500 |
+
$pluginFile = $this->pluginFile;
|
501 |
+
|
502 |
+
if ( $this->isMuPlugin() ) {
|
503 |
+
//WP does not support automatic update installation for mu-plugins, but we can still display a notice.
|
504 |
+
$wpUpdate->package = null;
|
505 |
+
$pluginFile = $this->muPluginFile;
|
506 |
+
}
|
507 |
+
$updates->response[$pluginFile] = $wpUpdate;
|
508 |
+
return $updates;
|
509 |
+
}
|
510 |
+
|
511 |
+
/**
|
512 |
+
* @param stdClass|null $updates
|
513 |
+
* @return stdClass|null
|
514 |
+
*/
|
515 |
+
private function removeUpdateFromList($updates) {
|
516 |
+
if ( isset($updates, $updates->response) ) {
|
517 |
+
unset($updates->response[$this->pluginFile]);
|
518 |
+
if ( !empty($this->muPluginFile) ) {
|
519 |
+
unset($updates->response[$this->muPluginFile]);
|
520 |
+
}
|
521 |
+
}
|
522 |
+
return $updates;
|
523 |
+
}
|
524 |
+
|
525 |
+
/**
|
526 |
+
* Insert translation updates into the list maintained by WordPress.
|
527 |
+
*
|
528 |
+
* @param stdClass $updates
|
529 |
+
* @return stdClass
|
530 |
+
*/
|
531 |
+
public function injectTranslationUpdates($updates) {
|
532 |
+
$translationUpdates = $this->getTranslationUpdates();
|
533 |
+
if ( empty($translationUpdates) ) {
|
534 |
+
return $updates;
|
535 |
+
}
|
536 |
+
|
537 |
+
//Being defensive.
|
538 |
+
if ( !is_object($updates) ) {
|
539 |
+
$updates = new stdClass();
|
540 |
+
}
|
541 |
+
if ( !isset($updates->translations) ) {
|
542 |
+
$updates->translations = array();
|
543 |
+
}
|
544 |
+
|
545 |
+
//In case there's a name collision with a plugin hosted on wordpress.org,
|
546 |
+
//remove any preexisting updates that match our plugin.
|
547 |
+
$translationType = 'plugin';
|
548 |
+
$filteredTranslations = array();
|
549 |
+
foreach($updates->translations as $translation) {
|
550 |
+
if ( ($translation['type'] === $translationType) && ($translation['slug'] === $this->slug) ) {
|
551 |
+
continue;
|
552 |
+
}
|
553 |
+
$filteredTranslations[] = $translation;
|
554 |
+
}
|
555 |
+
$updates->translations = $filteredTranslations;
|
556 |
+
|
557 |
+
//Add our updates to the list.
|
558 |
+
foreach($translationUpdates as $update) {
|
559 |
+
$convertedUpdate = array_merge(
|
560 |
+
array(
|
561 |
+
'type' => $translationType,
|
562 |
+
'slug' => $this->slug,
|
563 |
+
'autoupdate' => 0,
|
564 |
+
//AFAICT, WordPress doesn't actually use the "version" field for anything.
|
565 |
+
//But lets make sure it's there, just in case.
|
566 |
+
'version' => isset($update->version) ? $update->version : ('1.' . strtotime($update->updated)),
|
567 |
+
),
|
568 |
+
(array)$update
|
569 |
+
);
|
570 |
+
|
571 |
+
$updates->translations[] = $convertedUpdate;
|
572 |
+
}
|
573 |
+
|
574 |
+
return $updates;
|
575 |
+
}
|
576 |
+
|
577 |
+
/**
|
578 |
+
* Rename the update directory to match the existing plugin directory.
|
579 |
+
*
|
580 |
+
* When WordPress installs a plugin or theme update, it assumes that the ZIP file will contain
|
581 |
+
* exactly one directory, and that the directory name will be the same as the directory where
|
582 |
+
* the plugin/theme is currently installed.
|
583 |
+
*
|
584 |
+
* GitHub and other repositories provide ZIP downloads, but they often use directory names like
|
585 |
+
* "project-branch" or "project-tag-hash". We need to change the name to the actual plugin folder.
|
586 |
+
*
|
587 |
+
* This is a hook callback. Don't call it from a plugin.
|
588 |
+
*
|
589 |
+
* @param string $source The directory to copy to /wp-content/plugins. Usually a subdirectory of $remoteSource.
|
590 |
+
* @param string $remoteSource WordPress has extracted the update to this directory.
|
591 |
+
* @param WP_Upgrader $upgrader
|
592 |
+
* @return string|WP_Error
|
593 |
+
*/
|
594 |
+
public function fixDirectoryName($source, $remoteSource, $upgrader) {
|
595 |
+
global $wp_filesystem; /** @var WP_Filesystem_Base $wp_filesystem */
|
596 |
+
|
597 |
+
//Basic sanity checks.
|
598 |
+
if ( !isset($source, $remoteSource, $upgrader, $upgrader->skin, $wp_filesystem) ) {
|
599 |
+
return $source;
|
600 |
+
}
|
601 |
+
|
602 |
+
//If WordPress is upgrading anything other than our plugin, leave the directory name unchanged.
|
603 |
+
if ( !$this->isPluginBeingUpgraded($upgrader) ) {
|
604 |
+
return $source;
|
605 |
+
}
|
606 |
+
|
607 |
+
//Rename the source to match the existing plugin directory.
|
608 |
+
$pluginDirectoryName = dirname($this->pluginFile);
|
609 |
+
if ( $pluginDirectoryName === '.' ) {
|
610 |
+
return $source;
|
611 |
+
}
|
612 |
+
$correctedSource = trailingslashit($remoteSource) . $pluginDirectoryName . '/';
|
613 |
+
if ( $source !== $correctedSource ) {
|
614 |
+
//The update archive should contain a single directory that contains the rest of plugin files. Otherwise,
|
615 |
+
//WordPress will try to copy the entire working directory ($source == $remoteSource). We can't rename
|
616 |
+
//$remoteSource because that would break WordPress code that cleans up temporary files after update.
|
617 |
+
if ( $this->isBadDirectoryStructure($remoteSource) ) {
|
618 |
+
return new WP_Error(
|
619 |
+
'puc-incorrect-directory-structure',
|
620 |
+
sprintf(
|
621 |
+
'The directory structure of the update is incorrect. All plugin files should be inside ' .
|
622 |
+
'a directory named <span class="code">%s</span>, not at the root of the ZIP file.',
|
623 |
+
htmlentities($this->slug)
|
624 |
+
)
|
625 |
+
);
|
626 |
+
}
|
627 |
+
|
628 |
+
/** @var WP_Upgrader_Skin $upgrader->skin */
|
629 |
+
$upgrader->skin->feedback(sprintf(
|
630 |
+
'Renaming %s to %s…',
|
631 |
+
'<span class="code">' . basename($source) . '</span>',
|
632 |
+
'<span class="code">' . $pluginDirectoryName . '</span>'
|
633 |
+
));
|
634 |
+
|
635 |
+
if ( $wp_filesystem->move($source, $correctedSource, true) ) {
|
636 |
+
$upgrader->skin->feedback('Plugin directory successfully renamed.');
|
637 |
+
return $correctedSource;
|
638 |
+
} else {
|
639 |
+
return new WP_Error(
|
640 |
+
'puc-rename-failed',
|
641 |
+
'Unable to rename the update to match the existing plugin directory.'
|
642 |
+
);
|
643 |
+
}
|
644 |
+
}
|
645 |
+
|
646 |
+
return $source;
|
647 |
+
}
|
648 |
+
|
649 |
+
/**
|
650 |
+
* Check for incorrect update directory structure. An update must contain a single directory,
|
651 |
+
* all other files should be inside that directory.
|
652 |
+
*
|
653 |
+
* @param string $remoteSource Directory path.
|
654 |
+
* @return bool
|
655 |
+
*/
|
656 |
+
private function isBadDirectoryStructure($remoteSource) {
|
657 |
+
global $wp_filesystem; /** @var WP_Filesystem_Base $wp_filesystem */
|
658 |
+
|
659 |
+
$sourceFiles = $wp_filesystem->dirlist($remoteSource);
|
660 |
+
if ( is_array($sourceFiles) ) {
|
661 |
+
$sourceFiles = array_keys($sourceFiles);
|
662 |
+
$firstFilePath = trailingslashit($remoteSource) . $sourceFiles[0];
|
663 |
+
return (count($sourceFiles) > 1) || (!$wp_filesystem->is_dir($firstFilePath));
|
664 |
+
}
|
665 |
+
|
666 |
+
//Assume it's fine.
|
667 |
+
return false;
|
668 |
+
}
|
669 |
+
|
670 |
+
/**
|
671 |
+
* Is there and update being installed RIGHT NOW, for this specific plugin?
|
672 |
+
*
|
673 |
+
* @param WP_Upgrader|null $upgrader The upgrader that's performing the current update.
|
674 |
+
* @return bool
|
675 |
+
*/
|
676 |
+
public function isPluginBeingUpgraded($upgrader = null) {
|
677 |
+
return $this->upgraderStatus->isPluginBeingUpgraded($this->pluginFile, $upgrader);
|
678 |
+
}
|
679 |
+
|
680 |
+
/**
|
681 |
+
* Get the details of the currently available update, if any.
|
682 |
+
*
|
683 |
+
* If no updates are available, or if the last known update version is below or equal
|
684 |
+
* to the currently installed version, this method will return NULL.
|
685 |
+
*
|
686 |
+
* Uses cached update data. To retrieve update information straight from
|
687 |
+
* the metadata URL, call requestUpdate() instead.
|
688 |
+
*
|
689 |
+
* @return PluginUpdate_3_1|null
|
690 |
+
*/
|
691 |
+
public function getUpdate() {
|
692 |
+
$state = $this->getUpdateState(); /** @var StdClass $state */
|
693 |
+
|
694 |
+
//Is there an update available?
|
695 |
+
if ( isset($state, $state->update) ) {
|
696 |
+
$update = $state->update;
|
697 |
+
//Check if the update is actually newer than the currently installed version.
|
698 |
+
$installedVersion = $this->getInstalledVersion();
|
699 |
+
if ( ($installedVersion !== null) && version_compare($update->version, $installedVersion, '>') ){
|
700 |
+
$update->filename = $this->pluginFile;
|
701 |
+
return $update;
|
702 |
+
}
|
703 |
+
}
|
704 |
+
return null;
|
705 |
+
}
|
706 |
+
|
707 |
+
/**
|
708 |
+
* Get a list of available translation updates.
|
709 |
+
*
|
710 |
+
* This method will return an empty array if there are no updates.
|
711 |
+
* Uses cached update data.
|
712 |
+
*
|
713 |
+
* @return array
|
714 |
+
*/
|
715 |
+
public function getTranslationUpdates() {
|
716 |
+
$state = $this->getUpdateState();
|
717 |
+
if ( isset($state, $state->update, $state->update->translations) ) {
|
718 |
+
return $state->update->translations;
|
719 |
+
}
|
720 |
+
return array();
|
721 |
+
}
|
722 |
+
|
723 |
+
/**
|
724 |
+
* Remove all cached translation updates.
|
725 |
+
*
|
726 |
+
* @see wp_clean_update_cache
|
727 |
+
*/
|
728 |
+
public function clearCachedTranslationUpdates() {
|
729 |
+
$state = $this->getUpdateState();
|
730 |
+
if ( isset($state, $state->update, $state->update->translations) ) {
|
731 |
+
$state->update->translations = array();
|
732 |
+
$this->setUpdateState($state);
|
733 |
+
}
|
734 |
+
}
|
735 |
+
|
736 |
+
/**
|
737 |
+
* Add a "Check for updates" link to the plugin row in the "Plugins" page. By default,
|
738 |
+
* the new link will appear after the "Visit plugin site" link.
|
739 |
+
*
|
740 |
+
* You can change the link text by using the "puc_manual_check_link-$slug" filter.
|
741 |
+
* Returning an empty string from the filter will disable the link.
|
742 |
+
*
|
743 |
+
* @param array $pluginMeta Array of meta links.
|
744 |
+
* @param string $pluginFile
|
745 |
+
* @return array
|
746 |
+
*/
|
747 |
+
public function addCheckForUpdatesLink($pluginMeta, $pluginFile) {
|
748 |
+
$isRelevant = ($pluginFile == $this->pluginFile)
|
749 |
+
|| (!empty($this->muPluginFile) && $pluginFile == $this->muPluginFile);
|
750 |
+
|
751 |
+
if ( $isRelevant && current_user_can('update_plugins') ) {
|
752 |
+
$linkUrl = wp_nonce_url(
|
753 |
+
add_query_arg(
|
754 |
+
array(
|
755 |
+
'puc_check_for_updates' => 1,
|
756 |
+
'puc_slug' => $this->slug,
|
757 |
+
),
|
758 |
+
self_admin_url('plugins.php')
|
759 |
+
),
|
760 |
+
'puc_check_for_updates'
|
761 |
+
);
|
762 |
+
|
763 |
+
$linkText = apply_filters('puc_manual_check_link-' . $this->slug, __('Check for updates', 'plugin-update-checker'));
|
764 |
+
if ( !empty($linkText) ) {
|
765 |
+
$pluginMeta[] = sprintf('<a href="%s">%s</a>', esc_attr($linkUrl), $linkText);
|
766 |
+
}
|
767 |
+
}
|
768 |
+
return $pluginMeta;
|
769 |
+
}
|
770 |
+
|
771 |
+
/**
|
772 |
+
* Check for updates when the user clicks the "Check for updates" link.
|
773 |
+
* @see self::addCheckForUpdatesLink()
|
774 |
+
*
|
775 |
+
* @return void
|
776 |
+
*/
|
777 |
+
public function handleManualCheck() {
|
778 |
+
$shouldCheck =
|
779 |
+
isset($_GET['puc_check_for_updates'], $_GET['puc_slug'])
|
780 |
+
&& $_GET['puc_slug'] == $this->slug
|
781 |
+
&& current_user_can('update_plugins')
|
782 |
+
&& check_admin_referer('puc_check_for_updates');
|
783 |
+
|
784 |
+
if ( $shouldCheck ) {
|
785 |
+
$update = $this->checkForUpdates();
|
786 |
+
$status = ($update === null) ? 'no_update' : 'update_available';
|
787 |
+
wp_redirect(add_query_arg(
|
788 |
+
array(
|
789 |
+
'puc_update_check_result' => $status,
|
790 |
+
'puc_slug' => $this->slug,
|
791 |
+
),
|
792 |
+
self_admin_url('plugins.php')
|
793 |
+
));
|
794 |
+
}
|
795 |
+
}
|
796 |
+
|
797 |
+
/**
|
798 |
+
* Display the results of a manual update check.
|
799 |
+
* @see self::handleManualCheck()
|
800 |
+
*
|
801 |
+
* You can change the result message by using the "puc_manual_check_message-$slug" filter.
|
802 |
+
*/
|
803 |
+
public function displayManualCheckResult() {
|
804 |
+
if ( isset($_GET['puc_update_check_result'], $_GET['puc_slug']) && ($_GET['puc_slug'] == $this->slug) ) {
|
805 |
+
$status = strval($_GET['puc_update_check_result']);
|
806 |
+
if ( $status == 'no_update' ) {
|
807 |
+
$message = __('This plugin is up to date.', 'plugin-update-checker');
|
808 |
+
} else if ( $status == 'update_available' ) {
|
809 |
+
$message = __('A new version of this plugin is available.', 'plugin-update-checker');
|
810 |
+
} else {
|
811 |
+
$message = sprintf(__('Unknown update checker status "%s"', 'plugin-update-checker'), htmlentities($status));
|
812 |
+
}
|
813 |
+
printf(
|
814 |
+
'<div class="updated notice is-dismissible"><p>%s</p></div>',
|
815 |
+
apply_filters('puc_manual_check_message-' . $this->slug, $message, $status)
|
816 |
+
);
|
817 |
+
}
|
818 |
+
}
|
819 |
+
|
820 |
+
/**
|
821 |
+
* Check if the plugin file is inside the mu-plugins directory.
|
822 |
+
*
|
823 |
+
* @return bool
|
824 |
+
*/
|
825 |
+
protected function isMuPlugin() {
|
826 |
+
static $cachedResult = null;
|
827 |
+
|
828 |
+
if ( $cachedResult === null ) {
|
829 |
+
//Convert both paths to the canonical form before comparison.
|
830 |
+
$muPluginDir = realpath(WPMU_PLUGIN_DIR);
|
831 |
+
$pluginPath = realpath($this->pluginAbsolutePath);
|
832 |
+
|
833 |
+
$cachedResult = (strpos($pluginPath, $muPluginDir) === 0);
|
834 |
+
}
|
835 |
+
|
836 |
+
return $cachedResult;
|
837 |
+
}
|
838 |
+
|
839 |
+
/**
|
840 |
+
* MU plugins are partially supported, but only when we know which file in mu-plugins
|
841 |
+
* corresponds to this plugin.
|
842 |
+
*
|
843 |
+
* @return bool
|
844 |
+
*/
|
845 |
+
protected function isUnknownMuPlugin() {
|
846 |
+
return empty($this->muPluginFile) && $this->isMuPlugin();
|
847 |
+
}
|
848 |
+
|
849 |
+
/**
|
850 |
+
* Clear the cached plugin version. This method can be set up as a filter (hook) and will
|
851 |
+
* return the filter argument unmodified.
|
852 |
+
*
|
853 |
+
* @param mixed $filterArgument
|
854 |
+
* @return mixed
|
855 |
+
*/
|
856 |
+
public function clearCachedVersion($filterArgument = null) {
|
857 |
+
$this->cachedInstalledVersion = null;
|
858 |
+
return $filterArgument;
|
859 |
+
}
|
860 |
+
|
861 |
+
/**
|
862 |
+
* Register a callback for filtering query arguments.
|
863 |
+
*
|
864 |
+
* The callback function should take one argument - an associative array of query arguments.
|
865 |
+
* It should return a modified array of query arguments.
|
866 |
+
*
|
867 |
+
* @uses add_filter() This method is a convenience wrapper for add_filter().
|
868 |
+
*
|
869 |
+
* @param callable $callback
|
870 |
+
* @return void
|
871 |
+
*/
|
872 |
+
public function addQueryArgFilter($callback){
|
873 |
+
add_filter('puc_request_info_query_args-'.$this->slug, $callback);
|
874 |
+
}
|
875 |
+
|
876 |
+
/**
|
877 |
+
* Register a callback for filtering arguments passed to wp_remote_get().
|
878 |
+
*
|
879 |
+
* The callback function should take one argument - an associative array of arguments -
|
880 |
+
* and return a modified array or arguments. See the WP documentation on wp_remote_get()
|
881 |
+
* for details on what arguments are available and how they work.
|
882 |
+
*
|
883 |
+
* @uses add_filter() This method is a convenience wrapper for add_filter().
|
884 |
+
*
|
885 |
+
* @param callable $callback
|
886 |
+
* @return void
|
887 |
+
*/
|
888 |
+
public function addHttpRequestArgFilter($callback){
|
889 |
+
add_filter('puc_request_info_options-'.$this->slug, $callback);
|
890 |
+
}
|
891 |
+
|
892 |
+
/**
|
893 |
+
* Register a callback for filtering the plugin info retrieved from the external API.
|
894 |
+
*
|
895 |
+
* The callback function should take two arguments. If the plugin info was retrieved
|
896 |
+
* successfully, the first argument passed will be an instance of PluginInfo. Otherwise,
|
897 |
+
* it will be NULL. The second argument will be the corresponding return value of
|
898 |
+
* wp_remote_get (see WP docs for details).
|
899 |
+
*
|
900 |
+
* The callback function should return a new or modified instance of PluginInfo or NULL.
|
901 |
+
*
|
902 |
+
* @uses add_filter() This method is a convenience wrapper for add_filter().
|
903 |
+
*
|
904 |
+
* @param callable $callback
|
905 |
+
* @return void
|
906 |
+
*/
|
907 |
+
public function addResultFilter($callback){
|
908 |
+
add_filter('puc_request_info_result-'.$this->slug, $callback, 10, 2);
|
909 |
+
}
|
910 |
+
|
911 |
+
/**
|
912 |
+
* Register a callback for one of the update checker filters.
|
913 |
+
*
|
914 |
+
* Identical to add_filter(), except it automatically adds the "puc_" prefix
|
915 |
+
* and the "-$plugin_slug" suffix to the filter name. For example, "request_info_result"
|
916 |
+
* becomes "puc_request_info_result-your_plugin_slug".
|
917 |
+
*
|
918 |
+
* @param string $tag
|
919 |
+
* @param callable $callback
|
920 |
+
* @param int $priority
|
921 |
+
* @param int $acceptedArgs
|
922 |
+
*/
|
923 |
+
public function addFilter($tag, $callback, $priority = 10, $acceptedArgs = 1) {
|
924 |
+
add_filter('puc_' . $tag . '-' . $this->slug, $callback, $priority, $acceptedArgs);
|
925 |
+
}
|
926 |
+
|
927 |
+
/**
|
928 |
+
* Initialize the update checker Debug Bar plugin/add-on thingy.
|
929 |
+
*/
|
930 |
+
public function initDebugBarPanel() {
|
931 |
+
$debugBarPlugin = dirname(__FILE__) . '/debug-bar-plugin.php';
|
932 |
+
if ( class_exists('Debug_Bar', false) && file_exists($debugBarPlugin) ) {
|
933 |
+
/** @noinspection PhpIncludeInspection */
|
934 |
+
require_once $debugBarPlugin;
|
935 |
+
$this->debugBarPlugin = new PucDebugBarPlugin_3_1($this);
|
936 |
+
}
|
937 |
+
}
|
938 |
+
|
939 |
+
/**
|
940 |
+
* Trigger a PHP error, but only when $debugMode is enabled.
|
941 |
+
*
|
942 |
+
* @param string $message
|
943 |
+
* @param int $errorType
|
944 |
+
*/
|
945 |
+
protected function triggerError($message, $errorType) {
|
946 |
+
if ( $this->debugMode ) {
|
947 |
+
trigger_error($message, $errorType);
|
948 |
+
}
|
949 |
+
}
|
950 |
+
}
|
951 |
+
|
952 |
+
endif;
|
953 |
+
|
954 |
+
if ( !class_exists('PluginInfo_3_1', false) ):
|
955 |
+
|
956 |
+
/**
|
957 |
+
* A container class for holding and transforming various plugin metadata.
|
958 |
+
*
|
959 |
+
* @author Janis Elsts
|
960 |
+
* @copyright 2016
|
961 |
+
* @version 3.0
|
962 |
+
* @access public
|
963 |
+
*/
|
964 |
+
class PluginInfo_3_1 {
|
965 |
+
//Most fields map directly to the contents of the plugin's info.json file.
|
966 |
+
//See the relevant docs for a description of their meaning.
|
967 |
+
public $name;
|
968 |
+
public $slug;
|
969 |
+
public $version;
|
970 |
+
public $homepage;
|
971 |
+
public $sections = array();
|
972 |
+
public $banners;
|
973 |
+
public $translations = array();
|
974 |
+
public $download_url;
|
975 |
+
|
976 |
+
public $author;
|
977 |
+
public $author_homepage;
|
978 |
+
|
979 |
+
public $requires;
|
980 |
+
public $tested;
|
981 |
+
public $upgrade_notice;
|
982 |
+
|
983 |
+
public $rating;
|
984 |
+
public $num_ratings;
|
985 |
+
public $downloaded;
|
986 |
+
public $active_installs;
|
987 |
+
public $last_updated;
|
988 |
+
|
989 |
+
public $id = 0; //The native WP.org API returns numeric plugin IDs, but they're not used for anything.
|
990 |
+
|
991 |
+
public $filename; //Plugin filename relative to the plugins directory.
|
992 |
+
|
993 |
+
/**
|
994 |
+
* Create a new instance of PluginInfo from JSON-encoded plugin info
|
995 |
+
* returned by an external update API.
|
996 |
+
*
|
997 |
+
* @param string $json Valid JSON string representing plugin info.
|
998 |
+
* @return PluginInfo_3_1|null New instance of PluginInfo, or NULL on error.
|
999 |
+
*/
|
1000 |
+
public static function fromJson($json){
|
1001 |
+
/** @var StdClass $apiResponse */
|
1002 |
+
$apiResponse = json_decode($json);
|
1003 |
+
if ( empty($apiResponse) || !is_object($apiResponse) ){
|
1004 |
+
trigger_error(
|
1005 |
+
"Failed to parse plugin metadata. Try validating your .json file with http://jsonlint.com/",
|
1006 |
+
E_USER_NOTICE
|
1007 |
+
);
|
1008 |
+
return null;
|
1009 |
+
}
|
1010 |
+
|
1011 |
+
$valid = self::validateMetadata($apiResponse);
|
1012 |
+
if ( is_wp_error($valid) ){
|
1013 |
+
trigger_error($valid->get_error_message(), E_USER_NOTICE);
|
1014 |
+
return null;
|
1015 |
+
}
|
1016 |
+
|
1017 |
+
$info = new self();
|
1018 |
+
foreach(get_object_vars($apiResponse) as $key => $value){
|
1019 |
+
$info->$key = $value;
|
1020 |
+
}
|
1021 |
+
|
1022 |
+
//json_decode decodes assoc. arrays as objects. We want it as an array.
|
1023 |
+
$info->sections = (array)$info->sections;
|
1024 |
+
|
1025 |
+
return $info;
|
1026 |
+
}
|
1027 |
+
|
1028 |
+
/**
|
1029 |
+
* Very, very basic validation.
|
1030 |
+
*
|
1031 |
+
* @param StdClass $apiResponse
|
1032 |
+
* @return bool|WP_Error
|
1033 |
+
*/
|
1034 |
+
protected static function validateMetadata($apiResponse) {
|
1035 |
+
if (
|
1036 |
+
!isset($apiResponse->name, $apiResponse->version)
|
1037 |
+
|| empty($apiResponse->name)
|
1038 |
+
|| empty($apiResponse->version)
|
1039 |
+
) {
|
1040 |
+
return new WP_Error(
|
1041 |
+
'puc-invalid-metadata',
|
1042 |
+
"The plugin metadata file does not contain the required 'name' and/or 'version' keys."
|
1043 |
+
);
|
1044 |
+
}
|
1045 |
+
return true;
|
1046 |
+
}
|
1047 |
+
|
1048 |
+
|
1049 |
+
/**
|
1050 |
+
* Transform plugin info into the format used by the native WordPress.org API
|
1051 |
+
*
|
1052 |
+
* @return object
|
1053 |
+
*/
|
1054 |
+
public function toWpFormat(){
|
1055 |
+
$info = new stdClass;
|
1056 |
+
|
1057 |
+
//The custom update API is built so that many fields have the same name and format
|
1058 |
+
//as those returned by the native WordPress.org API. These can be assigned directly.
|
1059 |
+
$sameFormat = array(
|
1060 |
+
'name', 'slug', 'version', 'requires', 'tested', 'rating', 'upgrade_notice',
|
1061 |
+
'num_ratings', 'downloaded', 'active_installs', 'homepage', 'last_updated',
|
1062 |
+
);
|
1063 |
+
foreach($sameFormat as $field){
|
1064 |
+
if ( isset($this->$field) ) {
|
1065 |
+
$info->$field = $this->$field;
|
1066 |
+
} else {
|
1067 |
+
$info->$field = null;
|
1068 |
+
}
|
1069 |
+
}
|
1070 |
+
|
1071 |
+
//Other fields need to be renamed and/or transformed.
|
1072 |
+
$info->download_link = $this->download_url;
|
1073 |
+
$info->author = $this->getFormattedAuthor();
|
1074 |
+
$info->sections = array_merge(array('description' => ''), $this->sections);
|
1075 |
+
|
1076 |
+
if ( !empty($this->banners) ) {
|
1077 |
+
//WP expects an array with two keys: "high" and "low". Both are optional.
|
1078 |
+
//Docs: https://wordpress.org/plugins/about/faq/#banners
|
1079 |
+
$info->banners = is_object($this->banners) ? get_object_vars($this->banners) : $this->banners;
|
1080 |
+
$info->banners = array_intersect_key($info->banners, array('high' => true, 'low' => true));
|
1081 |
+
}
|
1082 |
+
|
1083 |
+
return $info;
|
1084 |
+
}
|
1085 |
+
|
1086 |
+
protected function getFormattedAuthor() {
|
1087 |
+
if ( !empty($this->author_homepage) ){
|
1088 |
+
return sprintf('<a href="%s">%s</a>', $this->author_homepage, $this->author);
|
1089 |
+
}
|
1090 |
+
return $this->author;
|
1091 |
+
}
|
1092 |
+
}
|
1093 |
+
|
1094 |
+
endif;
|
1095 |
+
|
1096 |
+
if ( !class_exists('PluginUpdate_3_1', false) ):
|
1097 |
+
|
1098 |
+
/**
|
1099 |
+
* A simple container class for holding information about an available update.
|
1100 |
+
*
|
1101 |
+
* @author Janis Elsts
|
1102 |
+
* @copyright 2016
|
1103 |
+
* @version 3.0
|
1104 |
+
* @access public
|
1105 |
+
*/
|
1106 |
+
class PluginUpdate_3_1 {
|
1107 |
+
public $id = 0;
|
1108 |
+
public $slug;
|
1109 |
+
public $version;
|
1110 |
+
public $homepage;
|
1111 |
+
public $download_url;
|
1112 |
+
public $upgrade_notice;
|
1113 |
+
public $tested;
|
1114 |
+
public $translations = array();
|
1115 |
+
public $filename; //Plugin filename relative to the plugins directory.
|
1116 |
+
|
1117 |
+
private static $fields = array(
|
1118 |
+
'id', 'slug', 'version', 'homepage', 'tested',
|
1119 |
+
'download_url', 'upgrade_notice', 'filename',
|
1120 |
+
'translations'
|
1121 |
+
);
|
1122 |
+
|
1123 |
+
/**
|
1124 |
+
* Create a new instance of PluginUpdate from its JSON-encoded representation.
|
1125 |
+
*
|
1126 |
+
* @param string $json
|
1127 |
+
* @return PluginUpdate_3_1|null
|
1128 |
+
*/
|
1129 |
+
public static function fromJson($json){
|
1130 |
+
//Since update-related information is simply a subset of the full plugin info,
|
1131 |
+
//we can parse the update JSON as if it was a plugin info string, then copy over
|
1132 |
+
//the parts that we care about.
|
1133 |
+
$pluginInfo = PluginInfo_3_1::fromJson($json);
|
1134 |
+
if ( $pluginInfo != null ) {
|
1135 |
+
return self::fromPluginInfo($pluginInfo);
|
1136 |
+
} else {
|
1137 |
+
return null;
|
1138 |
+
}
|
1139 |
+
}
|
1140 |
+
|
1141 |
+
/**
|
1142 |
+
* Create a new instance of PluginUpdate based on an instance of PluginInfo.
|
1143 |
+
* Basically, this just copies a subset of fields from one object to another.
|
1144 |
+
*
|
1145 |
+
* @param PluginInfo_3_1 $info
|
1146 |
+
* @return PluginUpdate_3_1
|
1147 |
+
*/
|
1148 |
+
public static function fromPluginInfo($info){
|
1149 |
+
return self::fromObject($info);
|
1150 |
+
}
|
1151 |
+
|
1152 |
+
/**
|
1153 |
+
* Create a new instance of PluginUpdate by copying the necessary fields from
|
1154 |
+
* another object.
|
1155 |
+
*
|
1156 |
+
* @param StdClass|PluginInfo_3_1|PluginUpdate_3_1 $object The source object.
|
1157 |
+
* @return PluginUpdate_3_1 The new copy.
|
1158 |
+
*/
|
1159 |
+
public static function fromObject($object) {
|
1160 |
+
$update = new self();
|
1161 |
+
$fields = self::$fields;
|
1162 |
+
if ( !empty($object->slug) ) {
|
1163 |
+
$fields = apply_filters('puc_retain_fields-' . $object->slug, $fields);
|
1164 |
+
}
|
1165 |
+
foreach($fields as $field){
|
1166 |
+
if (property_exists($object, $field)) {
|
1167 |
+
$update->$field = $object->$field;
|
1168 |
+
}
|
1169 |
+
}
|
1170 |
+
return $update;
|
1171 |
+
}
|
1172 |
+
|
1173 |
+
/**
|
1174 |
+
* Create an instance of StdClass that can later be converted back to
|
1175 |
+
* a PluginUpdate. Useful for serialization and caching, as it avoids
|
1176 |
+
* the "incomplete object" problem if the cached value is loaded before
|
1177 |
+
* this class.
|
1178 |
+
*
|
1179 |
+
* @return StdClass
|
1180 |
+
*/
|
1181 |
+
public function toStdClass() {
|
1182 |
+
$object = new stdClass();
|
1183 |
+
$fields = self::$fields;
|
1184 |
+
if ( !empty($this->slug) ) {
|
1185 |
+
$fields = apply_filters('puc_retain_fields-' . $this->slug, $fields);
|
1186 |
+
}
|
1187 |
+
foreach($fields as $field){
|
1188 |
+
if (property_exists($this, $field)) {
|
1189 |
+
$object->$field = $this->$field;
|
1190 |
+
}
|
1191 |
+
}
|
1192 |
+
return $object;
|
1193 |
+
}
|
1194 |
+
|
1195 |
+
|
1196 |
+
/**
|
1197 |
+
* Transform the update into the format used by WordPress native plugin API.
|
1198 |
+
*
|
1199 |
+
* @return object
|
1200 |
+
*/
|
1201 |
+
public function toWpFormat(){
|
1202 |
+
$update = new stdClass;
|
1203 |
+
|
1204 |
+
$update->id = $this->id;
|
1205 |
+
$update->slug = $this->slug;
|
1206 |
+
$update->new_version = $this->version;
|
1207 |
+
$update->url = $this->homepage;
|
1208 |
+
$update->package = $this->download_url;
|
1209 |
+
$update->tested = $this->tested;
|
1210 |
+
$update->plugin = $this->filename;
|
1211 |
+
|
1212 |
+
if ( !empty($this->upgrade_notice) ){
|
1213 |
+
$update->upgrade_notice = $this->upgrade_notice;
|
1214 |
+
}
|
1215 |
+
|
1216 |
+
return $update;
|
1217 |
+
}
|
1218 |
+
}
|
1219 |
+
|
1220 |
+
endif;
|
1221 |
+
|
1222 |
+
if ( !class_exists('PucScheduler_3_1', false) ):
|
1223 |
+
|
1224 |
+
/**
|
1225 |
+
* The scheduler decides when and how often to check for updates.
|
1226 |
+
* It calls @see PluginUpdateChecker::checkForUpdates() to perform the actual checks.
|
1227 |
+
*
|
1228 |
+
* @version 3.0
|
1229 |
+
*/
|
1230 |
+
class PucScheduler_3_1 {
|
1231 |
+
public $checkPeriod = 12; //How often to check for updates (in hours).
|
1232 |
+
public $throttleRedundantChecks = false; //Check less often if we already know that an update is available.
|
1233 |
+
public $throttledCheckPeriod = 72;
|
1234 |
+
|
1235 |
+
/**
|
1236 |
+
* @var PluginUpdateChecker_3_1
|
1237 |
+
*/
|
1238 |
+
protected $updateChecker;
|
1239 |
+
|
1240 |
+
private $cronHook = null;
|
1241 |
+
|
1242 |
+
/**
|
1243 |
+
* Scheduler constructor.
|
1244 |
+
*
|
1245 |
+
* @param PluginUpdateChecker_3_1 $updateChecker
|
1246 |
+
* @param int $checkPeriod How often to check for updates (in hours).
|
1247 |
+
*/
|
1248 |
+
public function __construct($updateChecker, $checkPeriod) {
|
1249 |
+
$this->updateChecker = $updateChecker;
|
1250 |
+
$this->checkPeriod = $checkPeriod;
|
1251 |
+
|
1252 |
+
//Set up the periodic update checks
|
1253 |
+
$this->cronHook = 'check_plugin_updates-' . $this->updateChecker->slug;
|
1254 |
+
if ( $this->checkPeriod > 0 ){
|
1255 |
+
|
1256 |
+
//Trigger the check via Cron.
|
1257 |
+
//Try to use one of the default schedules if possible as it's less likely to conflict
|
1258 |
+
//with other plugins and their custom schedules.
|
1259 |
+
$defaultSchedules = array(
|
1260 |
+
1 => 'hourly',
|
1261 |
+
12 => 'twicedaily',
|
1262 |
+
24 => 'daily',
|
1263 |
+
);
|
1264 |
+
if ( array_key_exists($this->checkPeriod, $defaultSchedules) ) {
|
1265 |
+
$scheduleName = $defaultSchedules[$this->checkPeriod];
|
1266 |
+
} else {
|
1267 |
+
//Use a custom cron schedule.
|
1268 |
+
$scheduleName = 'every' . $this->checkPeriod . 'hours';
|
1269 |
+
add_filter('cron_schedules', array($this, '_addCustomSchedule'));
|
1270 |
+
}
|
1271 |
+
|
1272 |
+
if ( !wp_next_scheduled($this->cronHook) && !defined('WP_INSTALLING') ) {
|
1273 |
+
wp_schedule_event(time(), $scheduleName, $this->cronHook);
|
1274 |
+
}
|
1275 |
+
add_action($this->cronHook, array($this, 'maybeCheckForUpdates'));
|
1276 |
+
|
1277 |
+
register_deactivation_hook($this->updateChecker->pluginFile, array($this, '_removeUpdaterCron'));
|
1278 |
+
|
1279 |
+
//In case Cron is disabled or unreliable, we also manually trigger
|
1280 |
+
//the periodic checks while the user is browsing the Dashboard.
|
1281 |
+
add_action( 'admin_init', array($this, 'maybeCheckForUpdates') );
|
1282 |
+
|
1283 |
+
//Like WordPress itself, we check more often on certain pages.
|
1284 |
+
/** @see wp_update_plugins */
|
1285 |
+
add_action('load-update-core.php', array($this, 'maybeCheckForUpdates'));
|
1286 |
+
add_action('load-plugins.php', array($this, 'maybeCheckForUpdates'));
|
1287 |
+
add_action('load-update.php', array($this, 'maybeCheckForUpdates'));
|
1288 |
+
//This hook fires after a bulk update is complete.
|
1289 |
+
add_action('upgrader_process_complete', array($this, 'maybeCheckForUpdates'), 11, 0);
|
1290 |
+
|
1291 |
+
} else {
|
1292 |
+
//Periodic checks are disabled.
|
1293 |
+
wp_clear_scheduled_hook($this->cronHook);
|
1294 |
+
}
|
1295 |
+
}
|
1296 |
+
|
1297 |
+
/**
|
1298 |
+
* Check for updates if the configured check interval has already elapsed.
|
1299 |
+
* Will use a shorter check interval on certain admin pages like "Dashboard -> Updates" or when doing cron.
|
1300 |
+
*
|
1301 |
+
* You can override the default behaviour by using the "puc_check_now-$slug" filter.
|
1302 |
+
* The filter callback will be passed three parameters:
|
1303 |
+
* - Current decision. TRUE = check updates now, FALSE = don't check now.
|
1304 |
+
* - Last check time as a Unix timestamp.
|
1305 |
+
* - Configured check period in hours.
|
1306 |
+
* Return TRUE to check for updates immediately, or FALSE to cancel.
|
1307 |
+
*
|
1308 |
+
* This method is declared public because it's a hook callback. Calling it directly is not recommended.
|
1309 |
+
*/
|
1310 |
+
public function maybeCheckForUpdates(){
|
1311 |
+
if ( empty($this->checkPeriod) ){
|
1312 |
+
return;
|
1313 |
+
}
|
1314 |
+
|
1315 |
+
$state = $this->updateChecker->getUpdateState();
|
1316 |
+
$shouldCheck =
|
1317 |
+
empty($state) ||
|
1318 |
+
!isset($state->lastCheck) ||
|
1319 |
+
( (time() - $state->lastCheck) >= $this->getEffectiveCheckPeriod() );
|
1320 |
+
|
1321 |
+
//Let plugin authors substitute their own algorithm.
|
1322 |
+
$shouldCheck = apply_filters(
|
1323 |
+
'puc_check_now-' . $this->updateChecker->slug,
|
1324 |
+
$shouldCheck,
|
1325 |
+
(!empty($state) && isset($state->lastCheck)) ? $state->lastCheck : 0,
|
1326 |
+
$this->checkPeriod
|
1327 |
+
);
|
1328 |
+
|
1329 |
+
if ( $shouldCheck ) {
|
1330 |
+
$this->updateChecker->checkForUpdates();
|
1331 |
+
}
|
1332 |
+
}
|
1333 |
+
|
1334 |
+
/**
|
1335 |
+
* Calculate the actual check period based on the current status and environment.
|
1336 |
+
*
|
1337 |
+
* @return int Check period in seconds.
|
1338 |
+
*/
|
1339 |
+
protected function getEffectiveCheckPeriod() {
|
1340 |
+
$currentFilter = current_filter();
|
1341 |
+
if ( in_array($currentFilter, array('load-update-core.php', 'upgrader_process_complete')) ) {
|
1342 |
+
//Check more often when the user visits "Dashboard -> Updates" or does a bulk update.
|
1343 |
+
$period = 60;
|
1344 |
+
} else if ( in_array($currentFilter, array('load-plugins.php', 'load-update.php')) ) {
|
1345 |
+
//Also check more often on the "Plugins" page and /wp-admin/update.php.
|
1346 |
+
$period = 3600;
|
1347 |
+
} else if ( $this->throttleRedundantChecks && ($this->updateChecker->getUpdate() !== null) ) {
|
1348 |
+
//Check less frequently if it's already known that an update is available.
|
1349 |
+
$period = $this->throttledCheckPeriod * 3600;
|
1350 |
+
} else if ( defined('DOING_CRON') && constant('DOING_CRON') ) {
|
1351 |
+
//WordPress cron schedules are not exact, so lets do an update check even
|
1352 |
+
//if slightly less than $checkPeriod hours have elapsed since the last check.
|
1353 |
+
$cronFuzziness = 20 * 60;
|
1354 |
+
$period = $this->checkPeriod * 3600 - $cronFuzziness;
|
1355 |
+
} else {
|
1356 |
+
$period = $this->checkPeriod * 3600;
|
1357 |
+
}
|
1358 |
+
|
1359 |
+
return $period;
|
1360 |
+
}
|
1361 |
+
|
1362 |
+
/**
|
1363 |
+
* Add our custom schedule to the array of Cron schedules used by WP.
|
1364 |
+
*
|
1365 |
+
* @param array $schedules
|
1366 |
+
* @return array
|
1367 |
+
*/
|
1368 |
+
public function _addCustomSchedule($schedules){
|
1369 |
+
if ( $this->checkPeriod && ($this->checkPeriod > 0) ){
|
1370 |
+
$scheduleName = 'every' . $this->checkPeriod . 'hours';
|
1371 |
+
$schedules[$scheduleName] = array(
|
1372 |
+
'interval' => $this->checkPeriod * 3600,
|
1373 |
+
'display' => sprintf('Every %d hours', $this->checkPeriod),
|
1374 |
+
);
|
1375 |
+
}
|
1376 |
+
return $schedules;
|
1377 |
+
}
|
1378 |
+
|
1379 |
+
/**
|
1380 |
+
* Remove the scheduled cron event that the library uses to check for updates.
|
1381 |
+
*
|
1382 |
+
* @return void
|
1383 |
+
*/
|
1384 |
+
public function _removeUpdaterCron(){
|
1385 |
+
wp_clear_scheduled_hook($this->cronHook);
|
1386 |
+
}
|
1387 |
+
|
1388 |
+
/**
|
1389 |
+
* Get the name of the update checker's WP-cron hook. Mostly useful for debugging.
|
1390 |
+
*
|
1391 |
+
* @return string
|
1392 |
+
*/
|
1393 |
+
public function getCronHookName() {
|
1394 |
+
return $this->cronHook;
|
1395 |
+
}
|
1396 |
+
}
|
1397 |
+
|
1398 |
+
endif;
|
1399 |
+
|
1400 |
+
|
1401 |
+
if ( !class_exists('PucUpgraderStatus_3_1', false) ):
|
1402 |
+
|
1403 |
+
/**
|
1404 |
+
* A utility class that helps figure out which plugin WordPress is upgrading.
|
1405 |
+
*
|
1406 |
+
* It may seem strange to have an separate class just for that, but the task is surprisingly complicated.
|
1407 |
+
* Core classes like Plugin_Upgrader don't expose the plugin file name during an in-progress update (AFAICT).
|
1408 |
+
* This class uses a few workarounds and heuristics to get the file name.
|
1409 |
+
*/
|
1410 |
+
class PucUpgraderStatus_3_1 {
|
1411 |
+
private $upgradedPluginFile = null; //The plugin that is currently being upgraded by WordPress.
|
1412 |
+
|
1413 |
+
public function __construct() {
|
1414 |
+
//Keep track of which plugin WordPress is currently upgrading.
|
1415 |
+
add_filter('upgrader_pre_install', array($this, 'setUpgradedPlugin'), 10, 2);
|
1416 |
+
add_filter('upgrader_package_options', array($this, 'setUpgradedPluginFromOptions'), 10, 1);
|
1417 |
+
add_filter('upgrader_post_install', array($this, 'clearUpgradedPlugin'), 10, 1);
|
1418 |
+
add_action('upgrader_process_complete', array($this, 'clearUpgradedPlugin'), 10, 1);
|
1419 |
+
}
|
1420 |
+
|
1421 |
+
/**
|
1422 |
+
* Is there and update being installed RIGHT NOW, for a specific plugin?
|
1423 |
+
*
|
1424 |
+
* Caution: This method is unreliable. WordPress doesn't make it easy to figure out what it is upgrading,
|
1425 |
+
* and upgrader implementations are liable to change without notice.
|
1426 |
+
*
|
1427 |
+
* @param string $pluginFile The plugin to check.
|
1428 |
+
* @param WP_Upgrader|null $upgrader The upgrader that's performing the current update.
|
1429 |
+
* @return bool True if the plugin identified by $pluginFile is being upgraded.
|
1430 |
+
*/
|
1431 |
+
public function isPluginBeingUpgraded($pluginFile, $upgrader = null) {
|
1432 |
+
if ( isset($upgrader) ) {
|
1433 |
+
$upgradedPluginFile = $this->getPluginBeingUpgradedBy($upgrader);
|
1434 |
+
if ( !empty($upgradedPluginFile) ) {
|
1435 |
+
$this->upgradedPluginFile = $upgradedPluginFile;
|
1436 |
+
}
|
1437 |
+
}
|
1438 |
+
return ( !empty($this->upgradedPluginFile) && ($this->upgradedPluginFile === $pluginFile) );
|
1439 |
+
}
|
1440 |
+
|
1441 |
+
/**
|
1442 |
+
* Get the file name of the plugin that's currently being upgraded.
|
1443 |
+
*
|
1444 |
+
* @param Plugin_Upgrader|WP_Upgrader $upgrader
|
1445 |
+
* @return string|null
|
1446 |
+
*/
|
1447 |
+
private function getPluginBeingUpgradedBy($upgrader) {
|
1448 |
+
if ( !isset($upgrader, $upgrader->skin) ) {
|
1449 |
+
return null;
|
1450 |
+
}
|
1451 |
+
|
1452 |
+
//Figure out which plugin is being upgraded.
|
1453 |
+
$pluginFile = null;
|
1454 |
+
$skin = $upgrader->skin;
|
1455 |
+
if ( $skin instanceof Plugin_Upgrader_Skin ) {
|
1456 |
+
if ( isset($skin->plugin) && is_string($skin->plugin) && ($skin->plugin !== '') ) {
|
1457 |
+
$pluginFile = $skin->plugin;
|
1458 |
+
}
|
1459 |
+
} elseif ( isset($skin->plugin_info) && is_array($skin->plugin_info) ) {
|
1460 |
+
//This case is tricky because Bulk_Plugin_Upgrader_Skin (etc) doesn't actually store the plugin
|
1461 |
+
//filename anywhere. Instead, it has the plugin headers in $plugin_info. So the best we can
|
1462 |
+
//do is compare those headers to the headers of installed plugins.
|
1463 |
+
$pluginFile = $this->identifyPluginByHeaders($skin->plugin_info);
|
1464 |
+
}
|
1465 |
+
|
1466 |
+
return $pluginFile;
|
1467 |
+
}
|
1468 |
+
|
1469 |
+
/**
|
1470 |
+
* Identify an installed plugin based on its headers.
|
1471 |
+
*
|
1472 |
+
* @param array $searchHeaders The plugin file header to look for.
|
1473 |
+
* @return string|null Plugin basename ("foo/bar.php"), or NULL if we can't identify the plugin.
|
1474 |
+
*/
|
1475 |
+
private function identifyPluginByHeaders($searchHeaders) {
|
1476 |
+
if ( !function_exists('get_plugins') ){
|
1477 |
+
/** @noinspection PhpIncludeInspection */
|
1478 |
+
require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
|
1479 |
+
}
|
1480 |
+
|
1481 |
+
$installedPlugins = get_plugins();
|
1482 |
+
$matches = array();
|
1483 |
+
foreach($installedPlugins as $pluginBasename => $headers) {
|
1484 |
+
$diff1 = array_diff_assoc($headers, $searchHeaders);
|
1485 |
+
$diff2 = array_diff_assoc($searchHeaders, $headers);
|
1486 |
+
if ( empty($diff1) && empty($diff2) ) {
|
1487 |
+
$matches[] = $pluginBasename;
|
1488 |
+
}
|
1489 |
+
}
|
1490 |
+
|
1491 |
+
//It's possible (though very unlikely) that there could be two plugins with identical
|
1492 |
+
//headers. In that case, we can't unambiguously identify the plugin that's being upgraded.
|
1493 |
+
if ( count($matches) !== 1 ) {
|
1494 |
+
return null;
|
1495 |
+
}
|
1496 |
+
|
1497 |
+
return reset($matches);
|
1498 |
+
}
|
1499 |
+
|
1500 |
+
/**
|
1501 |
+
* @access private
|
1502 |
+
*
|
1503 |
+
* @param mixed $input
|
1504 |
+
* @param array $hookExtra
|
1505 |
+
* @return mixed Returns $input unaltered.
|
1506 |
+
*/
|
1507 |
+
public function setUpgradedPlugin($input, $hookExtra) {
|
1508 |
+
if (!empty($hookExtra['plugin']) && is_string($hookExtra['plugin'])) {
|
1509 |
+
$this->upgradedPluginFile = $hookExtra['plugin'];
|
1510 |
+
} else {
|
1511 |
+
$this->upgradedPluginFile = null;
|
1512 |
+
}
|
1513 |
+
return $input;
|
1514 |
+
}
|
1515 |
+
|
1516 |
+
/**
|
1517 |
+
* @access private
|
1518 |
+
*
|
1519 |
+
* @param array $options
|
1520 |
+
* @return array
|
1521 |
+
*/
|
1522 |
+
public function setUpgradedPluginFromOptions($options) {
|
1523 |
+
if (isset($options['hook_extra']['plugin']) && is_string($options['hook_extra']['plugin'])) {
|
1524 |
+
$this->upgradedPluginFile = $options['hook_extra']['plugin'];
|
1525 |
+
} else {
|
1526 |
+
$this->upgradedPluginFile = null;
|
1527 |
+
}
|
1528 |
+
return $options;
|
1529 |
+
}
|
1530 |
+
|
1531 |
+
/**
|
1532 |
+
* @access private
|
1533 |
+
*
|
1534 |
+
* @param mixed $input
|
1535 |
+
* @return mixed Returns $input unaltered.
|
1536 |
+
*/
|
1537 |
+
public function clearUpgradedPlugin($input = null) {
|
1538 |
+
$this->upgradedPluginFile = null;
|
1539 |
+
return $input;
|
1540 |
+
}
|
1541 |
+
}
|
1542 |
+
|
1543 |
+
endif;
|
1544 |
+
|
1545 |
+
|
1546 |
+
if ( !class_exists('PucFactory', false) ):
|
1547 |
+
|
1548 |
+
/**
|
1549 |
+
* A factory that builds instances of other classes from this library.
|
1550 |
+
*
|
1551 |
+
* When multiple versions of the same class have been loaded (e.g. PluginUpdateChecker 1.2
|
1552 |
+
* and 1.3), this factory will always use the latest available version. Register class
|
1553 |
+
* versions by calling {@link PucFactory::addVersion()}.
|
1554 |
+
*
|
1555 |
+
* At the moment it can only build instances of the PluginUpdateChecker class. Other classes
|
1556 |
+
* are intended mainly for internal use and refer directly to specific implementations. If you
|
1557 |
+
* want to instantiate one of them anyway, you can use {@link PucFactory::getLatestClassVersion()}
|
1558 |
+
* to get the class name and then create it with <code>new $class(...)</code>.
|
1559 |
+
*/
|
1560 |
+
class PucFactory {
|
1561 |
+
protected static $classVersions = array();
|
1562 |
+
protected static $sorted = false;
|
1563 |
+
|
1564 |
+
/**
|
1565 |
+
* Create a new instance of PluginUpdateChecker.
|
1566 |
+
*
|
1567 |
+
* @see PluginUpdateChecker::__construct()
|
1568 |
+
*
|
1569 |
+
* @param $metadataUrl
|
1570 |
+
* @param $pluginFile
|
1571 |
+
* @param string $slug
|
1572 |
+
* @param int $checkPeriod
|
1573 |
+
* @param string $optionName
|
1574 |
+
* @param string $muPluginFile
|
1575 |
+
* @return PluginUpdateChecker_3_1
|
1576 |
+
*/
|
1577 |
+
public static function buildUpdateChecker($metadataUrl, $pluginFile, $slug = '', $checkPeriod = 12, $optionName = '', $muPluginFile = '') {
|
1578 |
+
$class = self::getLatestClassVersion('PluginUpdateChecker');
|
1579 |
+
return new $class($metadataUrl, $pluginFile, $slug, $checkPeriod, $optionName, $muPluginFile);
|
1580 |
+
}
|
1581 |
+
|
1582 |
+
/**
|
1583 |
+
* Get the specific class name for the latest available version of a class.
|
1584 |
+
*
|
1585 |
+
* @param string $class
|
1586 |
+
* @return string|null
|
1587 |
+
*/
|
1588 |
+
public static function getLatestClassVersion($class) {
|
1589 |
+
if ( !self::$sorted ) {
|
1590 |
+
self::sortVersions();
|
1591 |
+
}
|
1592 |
+
|
1593 |
+
if ( isset(self::$classVersions[$class]) ) {
|
1594 |
+
return reset(self::$classVersions[$class]);
|
1595 |
+
} else {
|
1596 |
+
return null;
|
1597 |
+
}
|
1598 |
+
}
|
1599 |
+
|
1600 |
+
/**
|
1601 |
+
* Sort available class versions in descending order (i.e. newest first).
|
1602 |
+
*/
|
1603 |
+
protected static function sortVersions() {
|
1604 |
+
foreach ( self::$classVersions as $class => $versions ) {
|
1605 |
+
uksort($versions, array(__CLASS__, 'compareVersions'));
|
1606 |
+
self::$classVersions[$class] = $versions;
|
1607 |
+
}
|
1608 |
+
self::$sorted = true;
|
1609 |
+
}
|
1610 |
+
|
1611 |
+
protected static function compareVersions($a, $b) {
|
1612 |
+
return -version_compare($a, $b);
|
1613 |
+
}
|
1614 |
+
|
1615 |
+
/**
|
1616 |
+
* Register a version of a class.
|
1617 |
+
*
|
1618 |
+
* @access private This method is only for internal use by the library.
|
1619 |
+
*
|
1620 |
+
* @param string $generalClass Class name without version numbers, e.g. 'PluginUpdateChecker'.
|
1621 |
+
* @param string $versionedClass Actual class name, e.g. 'PluginUpdateChecker_1_2'.
|
1622 |
+
* @param string $version Version number, e.g. '1.2'.
|
1623 |
+
*/
|
1624 |
+
public static function addVersion($generalClass, $versionedClass, $version) {
|
1625 |
+
if ( !isset(self::$classVersions[$generalClass]) ) {
|
1626 |
+
self::$classVersions[$generalClass] = array();
|
1627 |
+
}
|
1628 |
+
self::$classVersions[$generalClass][$version] = $versionedClass;
|
1629 |
+
self::$sorted = false;
|
1630 |
+
}
|
1631 |
+
}
|
1632 |
+
|
1633 |
+
endif;
|
1634 |
+
|
1635 |
+
require_once(dirname(__FILE__) . '/github-checker.php');
|
1636 |
+
|
1637 |
+
//Register classes defined in this file with the factory.
|
1638 |
+
PucFactory::addVersion('PluginUpdateChecker', 'PluginUpdateChecker_3_1', '3.1');
|
1639 |
+
PucFactory::addVersion('PluginUpdate', 'PluginUpdate_3_1', '3.1');
|
1640 |
+
PucFactory::addVersion('PluginInfo', 'PluginInfo_3_1', '3.1');
|
1641 |
+
PucFactory::addVersion('PucGitHubChecker', 'PucGitHubChecker_3_1', '3.1');
|
includes/plugin-update-checker/vendor/Parsedown.php
ADDED
@@ -0,0 +1,1538 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
#
|
4 |
+
#
|
5 |
+
# Parsedown
|
6 |
+
# http://parsedown.org
|
7 |
+
#
|
8 |
+
# (c) Emanuil Rusev
|
9 |
+
# http://erusev.com
|
10 |
+
#
|
11 |
+
# For the full license information, view the LICENSE file that was distributed
|
12 |
+
# with this source code.
|
13 |
+
#
|
14 |
+
#
|
15 |
+
|
16 |
+
class Parsedown
|
17 |
+
{
|
18 |
+
# ~
|
19 |
+
|
20 |
+
const version = '1.6.0';
|
21 |
+
|
22 |
+
# ~
|
23 |
+
|
24 |
+
function text($text)
|
25 |
+
{
|
26 |
+
# make sure no definitions are set
|
27 |
+
$this->DefinitionData = array();
|
28 |
+
|
29 |
+
# standardize line breaks
|
30 |
+
$text = str_replace(array("\r\n", "\r"), "\n", $text);
|
31 |
+
|
32 |
+
# remove surrounding line breaks
|
33 |
+
$text = trim($text, "\n");
|
34 |
+
|
35 |
+
# split text into lines
|
36 |
+
$lines = explode("\n", $text);
|
37 |
+
|
38 |
+
# iterate through lines to identify blocks
|
39 |
+
$markup = $this->lines($lines);
|
40 |
+
|
41 |
+
# trim line breaks
|
42 |
+
$markup = trim($markup, "\n");
|
43 |
+
|
44 |
+
return $markup;
|
45 |
+
}
|
46 |
+
|
47 |
+
#
|
48 |
+
# Setters
|
49 |
+
#
|
50 |
+
|
51 |
+
function setBreaksEnabled($breaksEnabled)
|
52 |
+
{
|
53 |
+
$this->breaksEnabled = $breaksEnabled;
|
54 |
+
|
55 |
+
return $this;
|
56 |
+
}
|
57 |
+
|
58 |
+
protected $breaksEnabled;
|
59 |
+
|
60 |
+
function setMarkupEscaped($markupEscaped)
|
61 |
+
{
|
62 |
+
$this->markupEscaped = $markupEscaped;
|
63 |
+
|
64 |
+
return $this;
|
65 |
+
}
|
66 |
+
|
67 |
+
protected $markupEscaped;
|
68 |
+
|
69 |
+
function setUrlsLinked($urlsLinked)
|
70 |
+
{
|
71 |
+
$this->urlsLinked = $urlsLinked;
|
72 |
+
|
73 |
+
return $this;
|
74 |
+
}
|
75 |
+
|
76 |
+
protected $urlsLinked = true;
|
77 |
+
|
78 |
+
#
|
79 |
+
# Lines
|
80 |
+
#
|
81 |
+
|
82 |
+
protected $BlockTypes = array(
|
83 |
+
'#' => array('Header'),
|
84 |
+
'*' => array('Rule', 'List'),
|
85 |
+
'+' => array('List'),
|
86 |
+
'-' => array('SetextHeader', 'Table', 'Rule', 'List'),
|
87 |
+
'0' => array('List'),
|
88 |
+
'1' => array('List'),
|
89 |
+
'2' => array('List'),
|
90 |
+
'3' => array('List'),
|
91 |
+
'4' => array('List'),
|
92 |
+
'5' => array('List'),
|
93 |
+
'6' => array('List'),
|
94 |
+
'7' => array('List'),
|
95 |
+
'8' => array('List'),
|
96 |
+
'9' => array('List'),
|
97 |
+
':' => array('Table'),
|
98 |
+
'<' => array('Comment', 'Markup'),
|
99 |
+
'=' => array('SetextHeader'),
|
100 |
+
'>' => array('Quote'),
|
101 |
+
'[' => array('Reference'),
|
102 |
+
'_' => array('Rule'),
|
103 |
+
'`' => array('FencedCode'),
|
104 |
+
'|' => array('Table'),
|
105 |
+
'~' => array('FencedCode'),
|
106 |
+
);
|
107 |
+
|
108 |
+
# ~
|
109 |
+
|
110 |
+
protected $unmarkedBlockTypes = array(
|
111 |
+
'Code',
|
112 |
+
);
|
113 |
+
|
114 |
+
#
|
115 |
+
# Blocks
|
116 |
+
#
|
117 |
+
|
118 |
+
protected function lines(array $lines)
|
119 |
+
{
|
120 |
+
$CurrentBlock = null;
|
121 |
+
|
122 |
+
foreach ($lines as $line)
|
123 |
+
{
|
124 |
+
if (chop($line) === '')
|
125 |
+
{
|
126 |
+
if (isset($CurrentBlock))
|
127 |
+
{
|
128 |
+
$CurrentBlock['interrupted'] = true;
|
129 |
+
}
|
130 |
+
|
131 |
+
continue;
|
132 |
+
}
|
133 |
+
|
134 |
+
if (strpos($line, "\t") !== false)
|
135 |
+
{
|
136 |
+
$parts = explode("\t", $line);
|
137 |
+
|
138 |
+
$line = $parts[0];
|
139 |
+
|
140 |
+
unset($parts[0]);
|
141 |
+
|
142 |
+
foreach ($parts as $part)
|
143 |
+
{
|
144 |
+
$shortage = 4 - mb_strlen($line, 'utf-8') % 4;
|
145 |
+
|
146 |
+
$line .= str_repeat(' ', $shortage);
|
147 |
+
$line .= $part;
|
148 |
+
}
|
149 |
+
}
|
150 |
+
|
151 |
+
$indent = 0;
|
152 |
+
|
153 |
+
while (isset($line[$indent]) and $line[$indent] === ' ')
|
154 |
+
{
|
155 |
+
$indent ++;
|
156 |
+
}
|
157 |
+
|
158 |
+
$text = $indent > 0 ? substr($line, $indent) : $line;
|
159 |
+
|
160 |
+
# ~
|
161 |
+
|
162 |
+
$Line = array('body' => $line, 'indent' => $indent, 'text' => $text);
|
163 |
+
|
164 |
+
# ~
|
165 |
+
|
166 |
+
if (isset($CurrentBlock['continuable']))
|
167 |
+
{
|
168 |
+
$Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock);
|
169 |
+
|
170 |
+
if (isset($Block))
|
171 |
+
{
|
172 |
+
$CurrentBlock = $Block;
|
173 |
+
|
174 |
+
continue;
|
175 |
+
}
|
176 |
+
else
|
177 |
+
{
|
178 |
+
if ($this->isBlockCompletable($CurrentBlock['type']))
|
179 |
+
{
|
180 |
+
$CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
|
181 |
+
}
|
182 |
+
}
|
183 |
+
}
|
184 |
+
|
185 |
+
# ~
|
186 |
+
|
187 |
+
$marker = $text[0];
|
188 |
+
|
189 |
+
# ~
|
190 |
+
|
191 |
+
$blockTypes = $this->unmarkedBlockTypes;
|
192 |
+
|
193 |
+
if (isset($this->BlockTypes[$marker]))
|
194 |
+
{
|
195 |
+
foreach ($this->BlockTypes[$marker] as $blockType)
|
196 |
+
{
|
197 |
+
$blockTypes []= $blockType;
|
198 |
+
}
|
199 |
+
}
|
200 |
+
|
201 |
+
#
|
202 |
+
# ~
|
203 |
+
|
204 |
+
foreach ($blockTypes as $blockType)
|
205 |
+
{
|
206 |
+
$Block = $this->{'block'.$blockType}($Line, $CurrentBlock);
|
207 |
+
|
208 |
+
if (isset($Block))
|
209 |
+
{
|
210 |
+
$Block['type'] = $blockType;
|
211 |
+
|
212 |
+
if ( ! isset($Block['identified']))
|
213 |
+
{
|
214 |
+
$Blocks []= $CurrentBlock;
|
215 |
+
|
216 |
+
$Block['identified'] = true;
|
217 |
+
}
|
218 |
+
|
219 |
+
if ($this->isBlockContinuable($blockType))
|
220 |
+
{
|
221 |
+
$Block['continuable'] = true;
|
222 |
+
}
|
223 |
+
|
224 |
+
$CurrentBlock = $Block;
|
225 |
+
|
226 |
+
continue 2;
|
227 |
+
}
|
228 |
+
}
|
229 |
+
|
230 |
+
# ~
|
231 |
+
|
232 |
+
if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted']))
|
233 |
+
{
|
234 |
+
$CurrentBlock['element']['text'] .= "\n".$text;
|
235 |
+
}
|
236 |
+
else
|
237 |
+
{
|
238 |
+
$Blocks []= $CurrentBlock;
|
239 |
+
|
240 |
+
$CurrentBlock = $this->paragraph($Line);
|
241 |
+
|
242 |
+
$CurrentBlock['identified'] = true;
|
243 |
+
}
|
244 |
+
}
|
245 |
+
|
246 |
+
# ~
|
247 |
+
|
248 |
+
if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type']))
|
249 |
+
{
|
250 |
+
$CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
|
251 |
+
}
|
252 |
+
|
253 |
+
# ~
|
254 |
+
|
255 |
+
$Blocks []= $CurrentBlock;
|
256 |
+
|
257 |
+
unset($Blocks[0]);
|
258 |
+
|
259 |
+
# ~
|
260 |
+
|
261 |
+
$markup = '';
|
262 |
+
|
263 |
+
foreach ($Blocks as $Block)
|
264 |
+
{
|
265 |
+
if (isset($Block['hidden']))
|
266 |
+
{
|
267 |
+
continue;
|
268 |
+
}
|
269 |
+
|
270 |
+
$markup .= "\n";
|
271 |
+
$markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']);
|
272 |
+
}
|
273 |
+
|
274 |
+
$markup .= "\n";
|
275 |
+
|
276 |
+
# ~
|
277 |
+
|
278 |
+
return $markup;
|
279 |
+
}
|
280 |
+
|
281 |
+
protected function isBlockContinuable($Type)
|
282 |
+
{
|
283 |
+
return method_exists($this, 'block'.$Type.'Continue');
|
284 |
+
}
|
285 |
+
|
286 |
+
protected function isBlockCompletable($Type)
|
287 |
+
{
|
288 |
+
return method_exists($this, 'block'.$Type.'Complete');
|
289 |
+
}
|
290 |
+
|
291 |
+
#
|
292 |
+
# Code
|
293 |
+
|
294 |
+
protected function blockCode($Line, $Block = null)
|
295 |
+
{
|
296 |
+
if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted']))
|
297 |
+
{
|
298 |
+
return;
|
299 |
+
}
|
300 |
+
|
301 |
+
if ($Line['indent'] >= 4)
|
302 |
+
{
|
303 |
+
$text = substr($Line['body'], 4);
|
304 |
+
|
305 |
+
$Block = array(
|
306 |
+
'element' => array(
|
307 |
+
'name' => 'pre',
|
308 |
+
'handler' => 'element',
|
309 |
+
'text' => array(
|
310 |
+
'name' => 'code',
|
311 |
+
'text' => $text,
|
312 |
+
),
|
313 |
+
),
|
314 |
+
);
|
315 |
+
|
316 |
+
return $Block;
|
317 |
+
}
|
318 |
+
}
|
319 |
+
|
320 |
+
protected function blockCodeContinue($Line, $Block)
|
321 |
+
{
|
322 |
+
if ($Line['indent'] >= 4)
|
323 |
+
{
|
324 |
+
if (isset($Block['interrupted']))
|
325 |
+
{
|
326 |
+
$Block['element']['text']['text'] .= "\n";
|
327 |
+
|
328 |
+
unset($Block['interrupted']);
|
329 |
+
}
|
330 |
+
|
331 |
+
$Block['element']['text']['text'] .= "\n";
|
332 |
+
|
333 |
+
$text = substr($Line['body'], 4);
|
334 |
+
|
335 |
+
$Block['element']['text']['text'] .= $text;
|
336 |
+
|
337 |
+
return $Block;
|
338 |
+
}
|
339 |
+
}
|
340 |
+
|
341 |
+
protected function blockCodeComplete($Block)
|
342 |
+
{
|
343 |
+
$text = $Block['element']['text']['text'];
|
344 |
+
|
345 |
+
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
346 |
+
|
347 |
+
$Block['element']['text']['text'] = $text;
|
348 |
+
|
349 |
+
return $Block;
|
350 |
+
}
|
351 |
+
|
352 |
+
#
|
353 |
+
# Comment
|
354 |
+
|
355 |
+
protected function blockComment($Line)
|
356 |
+
{
|
357 |
+
if ($this->markupEscaped)
|
358 |
+
{
|
359 |
+
return;
|
360 |
+
}
|
361 |
+
|
362 |
+
if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!')
|
363 |
+
{
|
364 |
+
$Block = array(
|
365 |
+
'markup' => $Line['body'],
|
366 |
+
);
|
367 |
+
|
368 |
+
if (preg_match('/-->$/', $Line['text']))
|
369 |
+
{
|
370 |
+
$Block['closed'] = true;
|
371 |
+
}
|
372 |
+
|
373 |
+
return $Block;
|
374 |
+
}
|
375 |
+
}
|
376 |
+
|
377 |
+
protected function blockCommentContinue($Line, array $Block)
|
378 |
+
{
|
379 |
+
if (isset($Block['closed']))
|
380 |
+
{
|
381 |
+
return;
|
382 |
+
}
|
383 |
+
|
384 |
+
$Block['markup'] .= "\n" . $Line['body'];
|
385 |
+
|
386 |
+
if (preg_match('/-->$/', $Line['text']))
|
387 |
+
{
|
388 |
+
$Block['closed'] = true;
|
389 |
+
}
|
390 |
+
|
391 |
+
return $Block;
|
392 |
+
}
|
393 |
+
|
394 |
+
#
|
395 |
+
# Fenced Code
|
396 |
+
|
397 |
+
protected function blockFencedCode($Line)
|
398 |
+
{
|
399 |
+
if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches))
|
400 |
+
{
|
401 |
+
$Element = array(
|
402 |
+
'name' => 'code',
|
403 |
+
'text' => '',
|
404 |
+
);
|
405 |
+
|
406 |
+
if (isset($matches[1]))
|
407 |
+
{
|
408 |
+
$class = 'language-'.$matches[1];
|
409 |
+
|
410 |
+
$Element['attributes'] = array(
|
411 |
+
'class' => $class,
|
412 |
+
);
|
413 |
+
}
|
414 |
+
|
415 |
+
$Block = array(
|
416 |
+
'char' => $Line['text'][0],
|
417 |
+
'element' => array(
|
418 |
+
'name' => 'pre',
|
419 |
+
'handler' => 'element',
|
420 |
+
'text' => $Element,
|
421 |
+
),
|
422 |
+
);
|
423 |
+
|
424 |
+
return $Block;
|
425 |
+
}
|
426 |
+
}
|
427 |
+
|
428 |
+
protected function blockFencedCodeContinue($Line, $Block)
|
429 |
+
{
|
430 |
+
if (isset($Block['complete']))
|
431 |
+
{
|
432 |
+
return;
|
433 |
+
}
|
434 |
+
|
435 |
+
if (isset($Block['interrupted']))
|
436 |
+
{
|
437 |
+
$Block['element']['text']['text'] .= "\n";
|
438 |
+
|
439 |
+
unset($Block['interrupted']);
|
440 |
+
}
|
441 |
+
|
442 |
+
if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text']))
|
443 |
+
{
|
444 |
+
$Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1);
|
445 |
+
|
446 |
+
$Block['complete'] = true;
|
447 |
+
|
448 |
+
return $Block;
|
449 |
+
}
|
450 |
+
|
451 |
+
$Block['element']['text']['text'] .= "\n".$Line['body'];;
|
452 |
+
|
453 |
+
return $Block;
|
454 |
+
}
|
455 |
+
|
456 |
+
protected function blockFencedCodeComplete($Block)
|
457 |
+
{
|
458 |
+
$text = $Block['element']['text']['text'];
|
459 |
+
|
460 |
+
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
461 |
+
|
462 |
+
$Block['element']['text']['text'] = $text;
|
463 |
+
|
464 |
+
return $Block;
|
465 |
+
}
|
466 |
+
|
467 |
+
#
|
468 |
+
# Header
|
469 |
+
|
470 |
+
protected function blockHeader($Line)
|
471 |
+
{
|
472 |
+
if (isset($Line['text'][1]))
|
473 |
+
{
|
474 |
+
$level = 1;
|
475 |
+
|
476 |
+
while (isset($Line['text'][$level]) and $Line['text'][$level] === '#')
|
477 |
+
{
|
478 |
+
$level ++;
|
479 |
+
}
|
480 |
+
|
481 |
+
if ($level > 6)
|
482 |
+
{
|
483 |
+
return;
|
484 |
+
}
|
485 |
+
|
486 |
+
$text = trim($Line['text'], '# ');
|
487 |
+
|
488 |
+
$Block = array(
|
489 |
+
'element' => array(
|
490 |
+
'name' => 'h' . min(6, $level),
|
491 |
+
'text' => $text,
|
492 |
+
'handler' => 'line',
|
493 |
+
),
|
494 |
+
);
|
495 |
+
|
496 |
+
return $Block;
|
497 |
+
}
|
498 |
+
}
|
499 |
+
|
500 |
+
#
|
501 |
+
# List
|
502 |
+
|
503 |
+
protected function blockList($Line)
|
504 |
+
{
|
505 |
+
list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]');
|
506 |
+
|
507 |
+
if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches))
|
508 |
+
{
|
509 |
+
$Block = array(
|
510 |
+
'indent' => $Line['indent'],
|
511 |
+
'pattern' => $pattern,
|
512 |
+
'element' => array(
|
513 |
+
'name' => $name,
|
514 |
+
'handler' => 'elements',
|
515 |
+
),
|
516 |
+
);
|
517 |
+
|
518 |
+
$Block['li'] = array(
|
519 |
+
'name' => 'li',
|
520 |
+
'handler' => 'li',
|
521 |
+
'text' => array(
|
522 |
+
$matches[2],
|
523 |
+
),
|
524 |
+
);
|
525 |
+
|
526 |
+
$Block['element']['text'] []= & $Block['li'];
|
527 |
+
|
528 |
+
return $Block;
|
529 |
+
}
|
530 |
+
}
|
531 |
+
|
532 |
+
protected function blockListContinue($Line, array $Block)
|
533 |
+
{
|
534 |
+
if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches))
|
535 |
+
{
|
536 |
+
if (isset($Block['interrupted']))
|
537 |
+
{
|
538 |
+
$Block['li']['text'] []= '';
|
539 |
+
|
540 |
+
unset($Block['interrupted']);
|
541 |
+
}
|
542 |
+
|
543 |
+
unset($Block['li']);
|
544 |
+
|
545 |
+
$text = isset($matches[1]) ? $matches[1] : '';
|
546 |
+
|
547 |
+
$Block['li'] = array(
|
548 |
+
'name' => 'li',
|
549 |
+
'handler' => 'li',
|
550 |
+
'text' => array(
|
551 |
+
$text,
|
552 |
+
),
|
553 |
+
);
|
554 |
+
|
555 |
+
$Block['element']['text'] []= & $Block['li'];
|
556 |
+
|
557 |
+
return $Block;
|
558 |
+
}
|
559 |
+
|
560 |
+
if ($Line['text'][0] === '[' and $this->blockReference($Line))
|
561 |
+
{
|
562 |
+
return $Block;
|
563 |
+
}
|
564 |
+
|
565 |
+
if ( ! isset($Block['interrupted']))
|
566 |
+
{
|
567 |
+
$text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
|
568 |
+
|
569 |
+
$Block['li']['text'] []= $text;
|
570 |
+
|
571 |
+
return $Block;
|
572 |
+
}
|
573 |
+
|
574 |
+
if ($Line['indent'] > 0)
|
575 |
+
{
|
576 |
+
$Block['li']['text'] []= '';
|
577 |
+
|
578 |
+
$text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
|
579 |
+
|
580 |
+
$Block['li']['text'] []= $text;
|
581 |
+
|
582 |
+
unset($Block['interrupted']);
|
583 |
+
|
584 |
+
return $Block;
|
585 |
+
}
|
586 |
+
}
|
587 |
+
|
588 |
+
#
|
589 |
+
# Quote
|
590 |
+
|
591 |
+
protected function blockQuote($Line)
|
592 |
+
{
|
593 |
+
if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
|
594 |
+
{
|
595 |
+
$Block = array(
|
596 |
+
'element' => array(
|
597 |
+
'name' => 'blockquote',
|
598 |
+
'handler' => 'lines',
|
599 |
+
'text' => (array) $matches[1],
|
600 |
+
),
|
601 |
+
);
|
602 |
+
|
603 |
+
return $Block;
|
604 |
+
}
|
605 |
+
}
|
606 |
+
|
607 |
+
protected function blockQuoteContinue($Line, array $Block)
|
608 |
+
{
|
609 |
+
if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
|
610 |
+
{
|
611 |
+
if (isset($Block['interrupted']))
|
612 |
+
{
|
613 |
+
$Block['element']['text'] []= '';
|
614 |
+
|
615 |
+
unset($Block['interrupted']);
|
616 |
+
}
|
617 |
+
|
618 |
+
$Block['element']['text'] []= $matches[1];
|
619 |
+
|
620 |
+
return $Block;
|
621 |
+
}
|
622 |
+
|
623 |
+
if ( ! isset($Block['interrupted']))
|
624 |
+
{
|
625 |
+
$Block['element']['text'] []= $Line['text'];
|
626 |
+
|
627 |
+
return $Block;
|
628 |
+
}
|
629 |
+
}
|
630 |
+
|
631 |
+
#
|
632 |
+
# Rule
|
633 |
+
|
634 |
+
protected function blockRule($Line)
|
635 |
+
{
|
636 |
+
if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text']))
|
637 |
+
{
|
638 |
+
$Block = array(
|
639 |
+
'element' => array(
|
640 |
+
'name' => 'hr'
|
641 |
+
),
|
642 |
+
);
|
643 |
+
|
644 |
+
return $Block;
|
645 |
+
}
|
646 |
+
}
|
647 |
+
|
648 |
+
#
|
649 |
+
# Setext
|
650 |
+
|
651 |
+
protected function blockSetextHeader($Line, array $Block = null)
|
652 |
+
{
|
653 |
+
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
|
654 |
+
{
|
655 |
+
return;
|
656 |
+
}
|
657 |
+
|
658 |
+
if (chop($Line['text'], $Line['text'][0]) === '')
|
659 |
+
{
|
660 |
+
$Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2';
|
661 |
+
|
662 |
+
return $Block;
|
663 |
+
}
|
664 |
+
}
|
665 |
+
|
666 |
+
#
|
667 |
+
# Markup
|
668 |
+
|
669 |
+
protected function blockMarkup($Line)
|
670 |
+
{
|
671 |
+
if ($this->markupEscaped)
|
672 |
+
{
|
673 |
+
return;
|
674 |
+
}
|
675 |
+
|
676 |
+
if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
|
677 |
+
{
|
678 |
+
$element = strtolower($matches[1]);
|
679 |
+
|
680 |
+
if (in_array($element, $this->textLevelElements))
|
681 |
+
{
|
682 |
+
return;
|
683 |
+
}
|
684 |
+
|
685 |
+
$Block = array(
|
686 |
+
'name' => $matches[1],
|
687 |
+
'depth' => 0,
|
688 |
+
'markup' => $Line['text'],
|
689 |
+
);
|
690 |
+
|
691 |
+
$length = strlen($matches[0]);
|
692 |
+
|
693 |
+
$remainder = substr($Line['text'], $length);
|
694 |
+
|
695 |
+
if (trim($remainder) === '')
|
696 |
+
{
|
697 |
+
if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
|
698 |
+
{
|
699 |
+
$Block['closed'] = true;
|
700 |
+
|
701 |
+
$Block['void'] = true;
|
702 |
+
}
|
703 |
+
}
|
704 |
+
else
|
705 |
+
{
|
706 |
+
if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
|
707 |
+
{
|
708 |
+
return;
|
709 |
+
}
|
710 |
+
|
711 |
+
if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder))
|
712 |
+
{
|
713 |
+
$Block['closed'] = true;
|
714 |
+
}
|
715 |
+
}
|
716 |
+
|
717 |
+
return $Block;
|
718 |
+
}
|
719 |
+
}
|
720 |
+
|
721 |
+
protected function blockMarkupContinue($Line, array $Block)
|
722 |
+
{
|
723 |
+
if (isset($Block['closed']))
|
724 |
+
{
|
725 |
+
return;
|
726 |
+
}
|
727 |
+
|
728 |
+
if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open
|
729 |
+
{
|
730 |
+
$Block['depth'] ++;
|
731 |
+
}
|
732 |
+
|
733 |
+
if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close
|
734 |
+
{
|
735 |
+
if ($Block['depth'] > 0)
|
736 |
+
{
|
737 |
+
$Block['depth'] --;
|
738 |
+
}
|
739 |
+
else
|
740 |
+
{
|
741 |
+
$Block['closed'] = true;
|
742 |
+
}
|
743 |
+
}
|
744 |
+
|
745 |
+
if (isset($Block['interrupted']))
|
746 |
+
{
|
747 |
+
$Block['markup'] .= "\n";
|
748 |
+
|
749 |
+
unset($Block['interrupted']);
|
750 |
+
}
|
751 |
+
|
752 |
+
$Block['markup'] .= "\n".$Line['body'];
|
753 |
+
|
754 |
+
return $Block;
|
755 |
+
}
|
756 |
+
|
757 |
+
#
|
758 |
+
# Reference
|
759 |
+
|
760 |
+
protected function blockReference($Line)
|
761 |
+
{
|
762 |
+
if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches))
|
763 |
+
{
|
764 |
+
$id = strtolower($matches[1]);
|
765 |
+
|
766 |
+
$Data = array(
|
767 |
+
'url' => $matches[2],
|
768 |
+
'title' => null,
|
769 |
+
);
|
770 |
+
|
771 |
+
if (isset($matches[3]))
|
772 |
+
{
|
773 |
+
$Data['title'] = $matches[3];
|
774 |
+
}
|
775 |
+
|
776 |
+
$this->DefinitionData['Reference'][$id] = $Data;
|
777 |
+
|
778 |
+
$Block = array(
|
779 |
+
'hidden' => true,
|
780 |
+
);
|
781 |
+
|
782 |
+
return $Block;
|
783 |
+
}
|
784 |
+
}
|
785 |
+
|
786 |
+
#
|
787 |
+
# Table
|
788 |
+
|
789 |
+
protected function blockTable($Line, array $Block = null)
|
790 |
+
{
|
791 |
+
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
|
792 |
+
{
|
793 |
+
return;
|
794 |
+
}
|
795 |
+
|
796 |
+
if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '')
|
797 |
+
{
|
798 |
+
$alignments = array();
|
799 |
+
|
800 |
+
$divider = $Line['text'];
|
801 |
+
|
802 |
+
$divider = trim($divider);
|
803 |
+
$divider = trim($divider, '|');
|
804 |
+
|
805 |
+
$dividerCells = explode('|', $divider);
|
806 |
+
|
807 |
+
foreach ($dividerCells as $dividerCell)
|
808 |
+
{
|
809 |
+
$dividerCell = trim($dividerCell);
|
810 |
+
|
811 |
+
if ($dividerCell === '')
|
812 |
+
{
|
813 |
+
continue;
|
814 |
+
}
|
815 |
+
|
816 |
+
$alignment = null;
|
817 |
+
|
818 |
+
if ($dividerCell[0] === ':')
|
819 |
+
{
|
820 |
+
$alignment = 'left';
|
821 |
+
}
|
822 |
+
|
823 |
+
if (substr($dividerCell, - 1) === ':')
|
824 |
+
{
|
825 |
+
$alignment = $alignment === 'left' ? 'center' : 'right';
|
826 |
+
}
|
827 |
+
|
828 |
+
$alignments []= $alignment;
|
829 |
+
}
|
830 |
+
|
831 |
+
# ~
|
832 |
+
|
833 |
+
$HeaderElements = array();
|
834 |
+
|
835 |
+
$header = $Block['element']['text'];
|
836 |
+
|
837 |
+
$header = trim($header);
|
838 |
+
$header = trim($header, '|');
|
839 |
+
|
840 |
+
$headerCells = explode('|', $header);
|
841 |
+
|
842 |
+
foreach ($headerCells as $index => $headerCell)
|
843 |
+
{
|
844 |
+
$headerCell = trim($headerCell);
|
845 |
+
|
846 |
+
$HeaderElement = array(
|
847 |
+
'name' => 'th',
|
848 |
+
'text' => $headerCell,
|
849 |
+
'handler' => 'line',
|
850 |
+
);
|
851 |
+
|
852 |
+
if (isset($alignments[$index]))
|
853 |
+
{
|
854 |
+
$alignment = $alignments[$index];
|
855 |
+
|
856 |
+
$HeaderElement['attributes'] = array(
|
857 |
+
'style' => 'text-align: '.$alignment.';',
|
858 |
+
);
|
859 |
+
}
|
860 |
+
|
861 |
+
$HeaderElements []= $HeaderElement;
|
862 |
+
}
|
863 |
+
|
864 |
+
# ~
|
865 |
+
|
866 |
+
$Block = array(
|
867 |
+
'alignments' => $alignments,
|
868 |
+
'identified' => true,
|
869 |
+
'element' => array(
|
870 |
+
'name' => 'table',
|
871 |
+
'handler' => 'elements',
|
872 |
+
),
|
873 |
+
);
|
874 |
+
|
875 |
+
$Block['element']['text'] []= array(
|
876 |
+
'name' => 'thead',
|
877 |
+
'handler' => 'elements',
|
878 |
+
);
|
879 |
+
|
880 |
+
$Block['element']['text'] []= array(
|
881 |
+
'name' => 'tbody',
|
882 |
+
'handler' => 'elements',
|
883 |
+
'text' => array(),
|
884 |
+
);
|
885 |
+
|
886 |
+
$Block['element']['text'][0]['text'] []= array(
|
887 |
+
'name' => 'tr',
|
888 |
+
'handler' => 'elements',
|
889 |
+
'text' => $HeaderElements,
|
890 |
+
);
|
891 |
+
|
892 |
+
return $Block;
|
893 |
+
}
|
894 |
+
}
|
895 |
+
|
896 |
+
protected function blockTableContinue($Line, array $Block)
|
897 |
+
{
|
898 |
+
if (isset($Block['interrupted']))
|
899 |
+
{
|
900 |
+
return;
|
901 |
+
}
|
902 |
+
|
903 |
+
if ($Line['text'][0] === '|' or strpos($Line['text'], '|'))
|
904 |
+
{
|
905 |
+
$Elements = array();
|
906 |
+
|
907 |
+
$row = $Line['text'];
|
908 |
+
|
909 |
+
$row = trim($row);
|
910 |
+
$row = trim($row, '|');
|
911 |
+
|
912 |
+
preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches);
|
913 |
+
|
914 |
+
foreach ($matches[0] as $index => $cell)
|
915 |
+
{
|
916 |
+
$cell = trim($cell);
|
917 |
+
|
918 |
+
$Element = array(
|
919 |
+
'name' => 'td',
|
920 |
+
'handler' => 'line',
|
921 |
+
'text' => $cell,
|
922 |
+
);
|
923 |
+
|
924 |
+
if (isset($Block['alignments'][$index]))
|
925 |
+
{
|
926 |
+
$Element['attributes'] = array(
|
927 |
+
'style' => 'text-align: '.$Block['alignments'][$index].';',
|
928 |
+
);
|
929 |
+
}
|
930 |
+
|
931 |
+
$Elements []= $Element;
|
932 |
+
}
|
933 |
+
|
934 |
+
$Element = array(
|
935 |
+
'name' => 'tr',
|
936 |
+
'handler' => 'elements',
|
937 |
+
'text' => $Elements,
|
938 |
+
);
|
939 |
+
|
940 |
+
$Block['element']['text'][1]['text'] []= $Element;
|
941 |
+
|
942 |
+
return $Block;
|
943 |
+
}
|
944 |
+
}
|
945 |
+
|
946 |
+
#
|
947 |
+
# ~
|
948 |
+
#
|
949 |
+
|
950 |
+
protected function paragraph($Line)
|
951 |
+
{
|
952 |
+
$Block = array(
|
953 |
+
'element' => array(
|
954 |
+
'name' => 'p',
|
955 |
+
'text' => $Line['text'],
|
956 |
+
'handler' => 'line',
|
957 |
+
),
|
958 |
+
);
|
959 |
+
|
960 |
+
return $Block;
|
961 |
+
}
|
962 |
+
|
963 |
+
#
|
964 |
+
# Inline Elements
|
965 |
+
#
|
966 |
+
|
967 |
+
protected $InlineTypes = array(
|
968 |
+
'"' => array('SpecialCharacter'),
|
969 |
+
'!' => array('Image'),
|
970 |
+
'&' => array('SpecialCharacter'),
|
971 |
+
'*' => array('Emphasis'),
|
972 |
+
':' => array('Url'),
|
973 |
+
'<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'),
|
974 |
+
'>' => array('SpecialCharacter'),
|
975 |
+
'[' => array('Link'),
|
976 |
+
'_' => array('Emphasis'),
|
977 |
+
'`' => array('Code'),
|
978 |
+
'~' => array('Strikethrough'),
|
979 |
+
'\\' => array('EscapeSequence'),
|
980 |
+
);
|
981 |
+
|
982 |
+
# ~
|
983 |
+
|
984 |
+
protected $inlineMarkerList = '!"*_&[:<>`~\\';
|
985 |
+
|
986 |
+
#
|
987 |
+
# ~
|
988 |
+
#
|
989 |
+
|
990 |
+
public function line($text)
|
991 |
+
{
|
992 |
+
$markup = '';
|
993 |
+
|
994 |
+
# $excerpt is based on the first occurrence of a marker
|
995 |
+
|
996 |
+
while ($excerpt = strpbrk($text, $this->inlineMarkerList))
|
997 |
+
{
|
998 |
+
$marker = $excerpt[0];
|
999 |
+
|
1000 |
+
$markerPosition = strpos($text, $marker);
|
1001 |
+
|
1002 |
+
$Excerpt = array('text' => $excerpt, 'context' => $text);
|
1003 |
+
|
1004 |
+
foreach ($this->InlineTypes[$marker] as $inlineType)
|
1005 |
+
{
|
1006 |
+
$Inline = $this->{'inline'.$inlineType}($Excerpt);
|
1007 |
+
|
1008 |
+
if ( ! isset($Inline))
|
1009 |
+
{
|
1010 |
+
continue;
|
1011 |
+
}
|
1012 |
+
|
1013 |
+
# makes sure that the inline belongs to "our" marker
|
1014 |
+
|
1015 |
+
if (isset($Inline['position']) and $Inline['position'] > $markerPosition)
|
1016 |
+
{
|
1017 |
+
continue;
|
1018 |
+
}
|
1019 |
+
|
1020 |
+
# sets a default inline position
|
1021 |
+
|
1022 |
+
if ( ! isset($Inline['position']))
|
1023 |
+
{
|
1024 |
+
$Inline['position'] = $markerPosition;
|
1025 |
+
}
|
1026 |
+
|
1027 |
+
# the text that comes before the inline
|
1028 |
+
$unmarkedText = substr($text, 0, $Inline['position']);
|
1029 |
+
|
1030 |
+
# compile the unmarked text
|
1031 |
+
$markup .= $this->unmarkedText($unmarkedText);
|
1032 |
+
|
1033 |
+
# compile the inline
|
1034 |
+
$markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']);
|
1035 |
+
|
1036 |
+
# remove the examined text
|
1037 |
+
$text = substr($text, $Inline['position'] + $Inline['extent']);
|
1038 |
+
|
1039 |
+
continue 2;
|
1040 |
+
}
|
1041 |
+
|
1042 |
+
# the marker does not belong to an inline
|
1043 |
+
|
1044 |
+
$unmarkedText = substr($text, 0, $markerPosition + 1);
|
1045 |
+
|
1046 |
+
$markup .= $this->unmarkedText($unmarkedText);
|
1047 |
+
|
1048 |
+
$text = substr($text, $markerPosition + 1);
|
1049 |
+
}
|
1050 |
+
|
1051 |
+
$markup .= $this->unmarkedText($text);
|
1052 |
+
|
1053 |
+
return $markup;
|
1054 |
+
}
|
1055 |
+
|
1056 |
+
#
|
1057 |
+
# ~
|
1058 |
+
#
|
1059 |
+
|
1060 |
+
protected function inlineCode($Excerpt)
|
1061 |
+
{
|
1062 |
+
$marker = $Excerpt['text'][0];
|
1063 |
+
|
1064 |
+
if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
|
1065 |
+
{
|
1066 |
+
$text = $matches[2];
|
1067 |
+
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
1068 |
+
$text = preg_replace("/[ ]*\n/", ' ', $text);
|
1069 |
+
|
1070 |
+
return array(
|
1071 |
+
'extent' => strlen($matches[0]),
|
1072 |
+
'element' => array(
|
1073 |
+
'name' => 'code',
|
1074 |
+
'text' => $text,
|
1075 |
+
),
|
1076 |
+
);
|
1077 |
+
}
|
1078 |
+
}
|
1079 |
+
|
1080 |
+
protected function inlineEmailTag($Excerpt)
|
1081 |
+
{
|
1082 |
+
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches))
|
1083 |
+
{
|
1084 |
+
$url = $matches[1];
|
1085 |
+
|
1086 |
+
if ( ! isset($matches[2]))
|
1087 |
+
{
|
1088 |
+
$url = 'mailto:' . $url;
|
1089 |
+
}
|
1090 |
+
|
1091 |
+
return array(
|
1092 |
+
'extent' => strlen($matches[0]),
|
1093 |
+
'element' => array(
|
1094 |
+
'name' => 'a',
|
1095 |
+
'text' => $matches[1],
|
1096 |
+
'attributes' => array(
|
1097 |
+
'href' => $url,
|
1098 |
+
),
|
1099 |
+
),
|
1100 |
+
);
|
1101 |
+
}
|
1102 |
+
}
|
1103 |
+
|
1104 |
+
protected function inlineEmphasis($Excerpt)
|
1105 |
+
{
|
1106 |
+
if ( ! isset($Excerpt['text'][1]))
|
1107 |
+
{
|
1108 |
+
return;
|
1109 |
+
}
|
1110 |
+
|
1111 |
+
$marker = $Excerpt['text'][0];
|
1112 |
+
|
1113 |
+
if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
|
1114 |
+
{
|
1115 |
+
$emphasis = 'strong';
|
1116 |
+
}
|
1117 |
+
elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
|
1118 |
+
{
|
1119 |
+
$emphasis = 'em';
|
1120 |
+
}
|
1121 |
+
else
|
1122 |
+
{
|
1123 |
+
return;
|
1124 |
+
}
|
1125 |
+
|
1126 |
+
return array(
|
1127 |
+
'extent' => strlen($matches[0]),
|
1128 |
+
'element' => array(
|
1129 |
+
'name' => $emphasis,
|
1130 |
+
'handler' => 'line',
|
1131 |
+
'text' => $matches[1],
|
1132 |
+
),
|
1133 |
+
);
|
1134 |
+
}
|
1135 |
+
|
1136 |
+
protected function inlineEscapeSequence($Excerpt)
|
1137 |
+
{
|
1138 |
+
if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters))
|
1139 |
+
{
|
1140 |
+
return array(
|
1141 |
+
'markup' => $Excerpt['text'][1],
|
1142 |
+
'extent' => 2,
|
1143 |
+
);
|
1144 |
+
}
|
1145 |
+
}
|
1146 |
+
|
1147 |
+
protected function inlineImage($Excerpt)
|
1148 |
+
{
|
1149 |
+
if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[')
|
1150 |
+
{
|
1151 |
+
return;
|
1152 |
+
}
|
1153 |
+
|
1154 |
+
$Excerpt['text']= substr($Excerpt['text'], 1);
|
1155 |
+
|
1156 |
+
$Link = $this->inlineLink($Excerpt);
|
1157 |
+
|
1158 |
+
if ($Link === null)
|
1159 |
+
{
|
1160 |
+
return;
|
1161 |
+
}
|
1162 |
+
|
1163 |
+
$Inline = array(
|
1164 |
+
'extent' => $Link['extent'] + 1,
|
1165 |
+
'element' => array(
|
1166 |
+
'name' => 'img',
|
1167 |
+
'attributes' => array(
|
1168 |
+
'src' => $Link['element']['attributes']['href'],
|
1169 |
+
'alt' => $Link['element']['text'],
|
1170 |
+
),
|
1171 |
+
),
|
1172 |
+
);
|
1173 |
+
|
1174 |
+
$Inline['element']['attributes'] += $Link['element']['attributes'];
|
1175 |
+
|
1176 |
+
unset($Inline['element']['attributes']['href']);
|
1177 |
+
|
1178 |
+
return $Inline;
|
1179 |
+
}
|
1180 |
+
|
1181 |
+
protected function inlineLink($Excerpt)
|
1182 |
+
{
|
1183 |
+
$Element = array(
|
1184 |
+
'name' => 'a',
|
1185 |
+
'handler' => 'line',
|
1186 |
+
'text' => null,
|
1187 |
+
'attributes' => array(
|
1188 |
+
'href' => null,
|
1189 |
+
'title' => null,
|
1190 |
+
),
|
1191 |
+
);
|
1192 |
+
|
1193 |
+
$extent = 0;
|
1194 |
+
|
1195 |
+
$remainder = $Excerpt['text'];
|
1196 |
+
|
1197 |
+
if (preg_match('/\[((?:[^][]|(?R))*)\]/', $remainder, $matches))
|
1198 |
+
{
|
1199 |
+
$Element['text'] = $matches[1];
|
1200 |
+
|
1201 |
+
$extent += strlen($matches[0]);
|
1202 |
+
|
1203 |
+
$remainder = substr($remainder, $extent);
|
1204 |
+
}
|
1205 |
+
else
|
1206 |
+
{
|
1207 |
+
return;
|
1208 |
+
}
|
1209 |
+
|
1210 |
+
if (preg_match('/^[(]((?:[^ ()]|[(][^ )]+[)])+)(?:[ ]+("[^"]*"|\'[^\']*\'))?[)]/', $remainder, $matches))
|
1211 |
+
{
|
1212 |
+
$Element['attributes']['href'] = $matches[1];
|
1213 |
+
|
1214 |
+
if (isset($matches[2]))
|
1215 |
+
{
|
1216 |
+
$Element['attributes']['title'] = substr($matches[2], 1, - 1);
|
1217 |
+
}
|
1218 |
+
|
1219 |
+
$extent += strlen($matches[0]);
|
1220 |
+
}
|
1221 |
+
else
|
1222 |
+
{
|
1223 |
+
if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
|
1224 |
+
{
|
1225 |
+
$definition = strlen($matches[1]) ? $matches[1] : $Element['text'];
|
1226 |
+
$definition = strtolower($definition);
|
1227 |
+
|
1228 |
+
$extent += strlen($matches[0]);
|
1229 |
+
}
|
1230 |
+
else
|
1231 |
+
{
|
1232 |
+
$definition = strtolower($Element['text']);
|
1233 |
+
}
|
1234 |
+
|
1235 |
+
if ( ! isset($this->DefinitionData['Reference'][$definition]))
|
1236 |
+
{
|
1237 |
+
return;
|
1238 |
+
}
|
1239 |
+
|
1240 |
+
$Definition = $this->DefinitionData['Reference'][$definition];
|
1241 |
+
|
1242 |
+
$Element['attributes']['href'] = $Definition['url'];
|
1243 |
+
$Element['attributes']['title'] = $Definition['title'];
|
1244 |
+
}
|
1245 |
+
|
1246 |
+
$Element['attributes']['href'] = str_replace(array('&', '<'), array('&', '<'), $Element['attributes']['href']);
|
1247 |
+
|
1248 |
+
return array(
|
1249 |
+
'extent' => $extent,
|
1250 |
+
'element' => $Element,
|
1251 |
+
);
|
1252 |
+
}
|
1253 |
+
|
1254 |
+
protected function inlineMarkup($Excerpt)
|
1255 |
+
{
|
1256 |
+
if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false)
|
1257 |
+
{
|
1258 |
+
return;
|
1259 |
+
}
|
1260 |
+
|
1261 |
+
if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w*[ ]*>/s', $Excerpt['text'], $matches))
|
1262 |
+
{
|
1263 |
+
return array(
|
1264 |
+
'markup' => $matches[0],
|
1265 |
+
'extent' => strlen($matches[0]),
|
1266 |
+
);
|
1267 |
+
}
|
1268 |
+
|
1269 |
+
if ($Excerpt['text'][1] === '!' and preg_match('/^<!---?[^>-](?:-?[^-])*-->/s', $Excerpt['text'], $matches))
|
1270 |
+
{
|
1271 |
+
return array(
|
1272 |
+
'markup' => $matches[0],
|
1273 |
+
'extent' => strlen($matches[0]),
|
1274 |
+
);
|
1275 |
+
}
|
1276 |
+
|
1277 |
+
if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches))
|
1278 |
+
{
|
1279 |
+
return array(
|
1280 |
+
'markup' => $matches[0],
|
1281 |
+
'extent' => strlen($matches[0]),
|
1282 |
+
);
|
1283 |
+
}
|
1284 |
+
}
|
1285 |
+
|
1286 |
+
protected function inlineSpecialCharacter($Excerpt)
|
1287 |
+
{
|
1288 |
+
if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text']))
|
1289 |
+
{
|
1290 |
+
return array(
|
1291 |
+
'markup' => '&',
|
1292 |
+
'extent' => 1,
|
1293 |
+
);
|
1294 |
+
}
|
1295 |
+
|
1296 |
+
$SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot');
|
1297 |
+
|
1298 |
+
if (isset($SpecialCharacter[$Excerpt['text'][0]]))
|
1299 |
+
{
|
1300 |
+
return array(
|
1301 |
+
'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';',
|
1302 |
+
'extent' => 1,
|
1303 |
+
);
|
1304 |
+
}
|
1305 |
+
}
|
1306 |
+
|
1307 |
+
protected function inlineStrikethrough($Excerpt)
|
1308 |
+
{
|
1309 |
+
if ( ! isset($Excerpt['text'][1]))
|
1310 |
+
{
|
1311 |
+
return;
|
1312 |
+
}
|
1313 |
+
|
1314 |
+
if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches))
|
1315 |
+
{
|
1316 |
+
return array(
|
1317 |
+
'extent' => strlen($matches[0]),
|
1318 |
+
'element' => array(
|
1319 |
+
'name' => 'del',
|
1320 |
+
'text' => $matches[1],
|
1321 |
+
'handler' => 'line',
|
1322 |
+
),
|
1323 |
+
);
|
1324 |
+
}
|
1325 |
+
}
|
1326 |
+
|
1327 |
+
protected function inlineUrl($Excerpt)
|
1328 |
+
{
|
1329 |
+
if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/')
|
1330 |
+
{
|
1331 |
+
return;
|
1332 |
+
}
|
1333 |
+
|
1334 |
+
if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
|
1335 |
+
{
|
1336 |
+
$Inline = array(
|
1337 |
+
'extent' => strlen($matches[0][0]),
|
1338 |
+
'position' => $matches[0][1],
|
1339 |
+
'element' => array(
|
1340 |
+
'name' => 'a',
|
1341 |
+
'text' => $matches[0][0],
|
1342 |
+
'attributes' => array(
|
1343 |
+
'href' => $matches[0][0],
|
1344 |
+
),
|
1345 |
+
),
|
1346 |
+
);
|
1347 |
+
|
1348 |
+
return $Inline;
|
1349 |
+
}
|
1350 |
+
}
|
1351 |
+
|
1352 |
+
protected function inlineUrlTag($Excerpt)
|
1353 |
+
{
|
1354 |
+
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches))
|
1355 |
+
{
|
1356 |
+
$url = str_replace(array('&', '<'), array('&', '<'), $matches[1]);
|
1357 |
+
|
1358 |
+
return array(
|
1359 |
+
'extent' => strlen($matches[0]),
|
1360 |
+
'element' => array(
|
1361 |
+
'name' => 'a',
|
1362 |
+
'text' => $url,
|
1363 |
+
'attributes' => array(
|
1364 |
+
'href' => $url,
|
1365 |
+
),
|
1366 |
+
),
|
1367 |
+
);
|
1368 |
+
}
|
1369 |
+
}
|
1370 |
+
|
1371 |
+
# ~
|
1372 |
+
|
1373 |
+
protected function unmarkedText($text)
|
1374 |
+
{
|
1375 |
+
if ($this->breaksEnabled)
|
1376 |
+
{
|
1377 |
+
$text = preg_replace('/[ ]*\n/', "<br />\n", $text);
|
1378 |
+
}
|
1379 |
+
else
|
1380 |
+
{
|
1381 |
+
$text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "<br />\n", $text);
|
1382 |
+
$text = str_replace(" \n", "\n", $text);
|
1383 |
+
}
|
1384 |
+
|
1385 |
+
return $text;
|
1386 |
+
}
|
1387 |
+
|
1388 |
+
#
|
1389 |
+
# Handlers
|
1390 |
+
#
|
1391 |
+
|
1392 |
+
protected function element(array $Element)
|
1393 |
+
{
|
1394 |
+
$markup = '<'.$Element['name'];
|
1395 |
+
|
1396 |
+
if (isset($Element['attributes']))
|
1397 |
+
{
|
1398 |
+
foreach ($Element['attributes'] as $name => $value)
|
1399 |
+
{
|
1400 |
+
if ($value === null)
|
1401 |
+
{
|
1402 |
+
continue;
|
1403 |
+
}
|
1404 |
+
|
1405 |
+
$markup .= ' '.$name.'="'.$value.'"';
|
1406 |
+
}
|
1407 |
+
}
|
1408 |
+
|
1409 |
+
if (isset($Element['text']))
|
1410 |
+
{
|
1411 |
+
$markup .= '>';
|
1412 |
+
|
1413 |
+
if (isset($Element['handler']))
|
1414 |
+
{
|
1415 |
+
$markup .= $this->{$Element['handler']}($Element['text']);
|
1416 |
+
}
|
1417 |
+
else
|
1418 |
+
{
|
1419 |
+
$markup .= $Element['text'];
|
1420 |
+
}
|
1421 |
+
|
1422 |
+
$markup .= '</'.$Element['name'].'>';
|
1423 |
+
}
|
1424 |
+
else
|
1425 |
+
{
|
1426 |
+
$markup .= ' />';
|
1427 |
+
}
|
1428 |
+
|
1429 |
+
return $markup;
|
1430 |
+
}
|
1431 |
+
|
1432 |
+
protected function elements(array $Elements)
|
1433 |
+
{
|
1434 |
+
$markup = '';
|
1435 |
+
|
1436 |
+
foreach ($Elements as $Element)
|
1437 |
+
{
|
1438 |
+
$markup .= "\n" . $this->element($Element);
|
1439 |
+
}
|
1440 |
+
|
1441 |
+
$markup .= "\n";
|
1442 |
+
|
1443 |
+
return $markup;
|
1444 |
+
}
|
1445 |
+
|
1446 |
+
# ~
|
1447 |
+
|
1448 |
+
protected function li($lines)
|
1449 |
+
{
|
1450 |
+
$markup = $this->lines($lines);
|
1451 |
+
|
1452 |
+
$trimmedMarkup = trim($markup);
|
1453 |
+
|
1454 |
+
if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '<p>')
|
1455 |
+
{
|
1456 |
+
$markup = $trimmedMarkup;
|
1457 |
+
$markup = substr($markup, 3);
|
1458 |
+
|
1459 |
+
$position = strpos($markup, "</p>");
|
1460 |
+
|
1461 |
+
$markup = substr_replace($markup, '', $position, 4);
|
1462 |
+
}
|
1463 |
+
|
1464 |
+
return $markup;
|
1465 |
+
}
|
1466 |
+
|
1467 |
+
#
|
1468 |
+
# Deprecated Methods
|
1469 |
+
#
|
1470 |
+
|
1471 |
+
function parse($text)
|
1472 |
+
{
|
1473 |
+
$markup = $this->text($text);
|
1474 |
+
|
1475 |
+
return $markup;
|
1476 |
+
}
|
1477 |
+
|
1478 |
+
#
|
1479 |
+
# Static Methods
|
1480 |
+
#
|
1481 |
+
|
1482 |
+
static function instance($name = 'default')
|
1483 |
+
{
|
1484 |
+
if (isset(self::$instances[$name]))
|
1485 |
+
{
|
1486 |
+
return self::$instances[$name];
|
1487 |
+
}
|
1488 |
+
|
1489 |
+
$instance = new static();
|
1490 |
+
|
1491 |
+
self::$instances[$name] = $instance;
|
1492 |
+
|
1493 |
+
return $instance;
|
1494 |
+
}
|
1495 |
+
|
1496 |
+
private static $instances = array();
|
1497 |
+
|
1498 |
+
#
|
1499 |
+
# Fields
|
1500 |
+
#
|
1501 |
+
|
1502 |
+
protected $DefinitionData;
|
1503 |
+
|
1504 |
+
#
|
1505 |
+
# Read-Only
|
1506 |
+
|
1507 |
+
protected $specialCharacters = array(
|
1508 |
+
'\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|',
|
1509 |
+
);
|
1510 |
+
|
1511 |
+
protected $StrongRegex = array(
|
1512 |
+
'*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s',
|
1513 |
+
'_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us',
|
1514 |
+
);
|
1515 |
+
|
1516 |
+
protected $EmRegex = array(
|
1517 |
+
'*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
|
1518 |
+
'_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us',
|
1519 |
+
);
|
1520 |
+
|
1521 |
+
protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?';
|
1522 |
+
|
1523 |
+
protected $voidElements = array(
|
1524 |
+
'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source',
|
1525 |
+
);
|
1526 |
+
|
1527 |
+
protected $textLevelElements = array(
|
1528 |
+
'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont',
|
1529 |
+
'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing',
|
1530 |
+
'i', 'rp', 'del', 'code', 'strike', 'marquee',
|
1531 |
+
'q', 'rt', 'ins', 'font', 'strong',
|
1532 |
+
's', 'tt', 'sub', 'mark',
|
1533 |
+
'u', 'xm', 'sup', 'nobr',
|
1534 |
+
'var', 'ruby',
|
1535 |
+
'wbr', 'span',
|
1536 |
+
'time',
|
1537 |
+
);
|
1538 |
+
}
|
includes/plugin-update-checker/vendor/ParsedownLegacy.php
ADDED
@@ -0,0 +1,1535 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
#
|
4 |
+
#
|
5 |
+
# Parsedown
|
6 |
+
# http://parsedown.org
|
7 |
+
#
|
8 |
+
# (c) Emanuil Rusev
|
9 |
+
# http://erusev.com
|
10 |
+
#
|
11 |
+
# For the full license information, view the LICENSE file that was distributed
|
12 |
+
# with this source code.
|
13 |
+
#
|
14 |
+
#
|
15 |
+
|
16 |
+
class Parsedown
|
17 |
+
{
|
18 |
+
# ~
|
19 |
+
|
20 |
+
const version = '1.5.0';
|
21 |
+
|
22 |
+
# ~
|
23 |
+
|
24 |
+
function text($text)
|
25 |
+
{
|
26 |
+
# make sure no definitions are set
|
27 |
+
$this->DefinitionData = array();
|
28 |
+
|
29 |
+
# standardize line breaks
|
30 |
+
$text = str_replace(array("\r\n", "\r"), "\n", $text);
|
31 |
+
|
32 |
+
# remove surrounding line breaks
|
33 |
+
$text = trim($text, "\n");
|
34 |
+
|
35 |
+
# split text into lines
|
36 |
+
$lines = explode("\n", $text);
|
37 |
+
|
38 |
+
# iterate through lines to identify blocks
|
39 |
+
$markup = $this->lines($lines);
|
40 |
+
|
41 |
+
# trim line breaks
|
42 |
+
$markup = trim($markup, "\n");
|
43 |
+
|
44 |
+
return $markup;
|
45 |
+
}
|
46 |
+
|
47 |
+
#
|
48 |
+
# Setters
|
49 |
+
#
|
50 |
+
|
51 |
+
function setBreaksEnabled($breaksEnabled)
|
52 |
+
{
|
53 |
+
$this->breaksEnabled = $breaksEnabled;
|
54 |
+
|
55 |
+
return $this;
|
56 |
+
}
|
57 |
+
|
58 |
+
protected $breaksEnabled;
|
59 |
+
|
60 |
+
function setMarkupEscaped($markupEscaped)
|
61 |
+
{
|
62 |
+
$this->markupEscaped = $markupEscaped;
|
63 |
+
|
64 |
+
return $this;
|
65 |
+
}
|
66 |
+
|
67 |
+
protected $markupEscaped;
|
68 |
+
|
69 |
+
function setUrlsLinked($urlsLinked)
|
70 |
+
{
|
71 |
+
$this->urlsLinked = $urlsLinked;
|
72 |
+
|
73 |
+
return $this;
|
74 |
+
}
|
75 |
+
|
76 |
+
protected $urlsLinked = true;
|
77 |
+
|
78 |
+
#
|
79 |
+
# Lines
|
80 |
+
#
|
81 |
+
|
82 |
+
protected $BlockTypes = array(
|
83 |
+
'#' => array('Header'),
|
84 |
+
'*' => array('Rule', 'List'),
|
85 |
+
'+' => array('List'),
|
86 |
+
'-' => array('SetextHeader', 'Table', 'Rule', 'List'),
|
87 |
+
'0' => array('List'),
|
88 |
+
'1' => array('List'),
|
89 |
+
'2' => array('List'),
|
90 |
+
'3' => array('List'),
|
91 |
+
'4' => array('List'),
|
92 |
+
'5' => array('List'),
|
93 |
+
'6' => array('List'),
|
94 |
+
'7' => array('List'),
|
95 |
+
'8' => array('List'),
|
96 |
+
'9' => array('List'),
|
97 |
+
':' => array('Table'),
|
98 |
+
'<' => array('Comment', 'Markup'),
|
99 |
+
'=' => array('SetextHeader'),
|
100 |
+
'>' => array('Quote'),
|
101 |
+
'[' => array('Reference'),
|
102 |
+
'_' => array('Rule'),
|
103 |
+
'`' => array('FencedCode'),
|
104 |
+
'|' => array('Table'),
|
105 |
+
'~' => array('FencedCode'),
|
106 |
+
);
|
107 |
+
|
108 |
+
# ~
|
109 |
+
|
110 |
+
protected $DefinitionTypes = array(
|
111 |
+
'[' => array('Reference'),
|
112 |
+
);
|
113 |
+
|
114 |
+
# ~
|
115 |
+
|
116 |
+
protected $unmarkedBlockTypes = array(
|
117 |
+
'Code',
|
118 |
+
);
|
119 |
+
|
120 |
+
#
|
121 |
+
# Blocks
|
122 |
+
#
|
123 |
+
|
124 |
+
private function lines(array $lines)
|
125 |
+
{
|
126 |
+
$CurrentBlock = null;
|
127 |
+
|
128 |
+
foreach ($lines as $line)
|
129 |
+
{
|
130 |
+
if (chop($line) === '')
|
131 |
+
{
|
132 |
+
if (isset($CurrentBlock))
|
133 |
+
{
|
134 |
+
$CurrentBlock['interrupted'] = true;
|
135 |
+
}
|
136 |
+
|
137 |
+
continue;
|
138 |
+
}
|
139 |
+
|
140 |
+
if (strpos($line, "\t") !== false)
|
141 |
+
{
|
142 |
+
$parts = explode("\t", $line);
|
143 |
+
|
144 |
+
$line = $parts[0];
|
145 |
+
|
146 |
+
unset($parts[0]);
|
147 |
+
|
148 |
+
foreach ($parts as $part)
|
149 |
+
{
|
150 |
+
$shortage = 4 - mb_strlen($line, 'utf-8') % 4;
|
151 |
+
|
152 |
+
$line .= str_repeat(' ', $shortage);
|
153 |
+
$line .= $part;
|
154 |
+
}
|
155 |
+
}
|
156 |
+
|
157 |
+
$indent = 0;
|
158 |
+
|
159 |
+
while (isset($line[$indent]) and $line[$indent] === ' ')
|
160 |
+
{
|
161 |
+
$indent ++;
|
162 |
+
}
|
163 |
+
|
164 |
+
$text = $indent > 0 ? substr($line, $indent) : $line;
|
165 |
+
|
166 |
+
# ~
|
167 |
+
|
168 |
+
$Line = array('body' => $line, 'indent' => $indent, 'text' => $text);
|
169 |
+
|
170 |
+
# ~
|
171 |
+
|
172 |
+
if (isset($CurrentBlock['incomplete']))
|
173 |
+
{
|
174 |
+
$Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock);
|
175 |
+
|
176 |
+
if (isset($Block))
|
177 |
+
{
|
178 |
+
$CurrentBlock = $Block;
|
179 |
+
|
180 |
+
continue;
|
181 |
+
}
|
182 |
+
else
|
183 |
+
{
|
184 |
+
if (method_exists($this, 'block'.$CurrentBlock['type'].'Complete'))
|
185 |
+
{
|
186 |
+
$CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
|
187 |
+
}
|
188 |
+
|
189 |
+
unset($CurrentBlock['incomplete']);
|
190 |
+
}
|
191 |
+
}
|
192 |
+
|
193 |
+
# ~
|
194 |
+
|
195 |
+
$marker = $text[0];
|
196 |
+
|
197 |
+
# ~
|
198 |
+
|
199 |
+
$blockTypes = $this->unmarkedBlockTypes;
|
200 |
+
|
201 |
+
if (isset($this->BlockTypes[$marker]))
|
202 |
+
{
|
203 |
+
foreach ($this->BlockTypes[$marker] as $blockType)
|
204 |
+
{
|
205 |
+
$blockTypes []= $blockType;
|
206 |
+
}
|
207 |
+
}
|
208 |
+
|
209 |
+
#
|
210 |
+
# ~
|
211 |
+
|
212 |
+
foreach ($blockTypes as $blockType)
|
213 |
+
{
|
214 |
+
$Block = $this->{'block'.$blockType}($Line, $CurrentBlock);
|
215 |
+
|
216 |
+
if (isset($Block))
|
217 |
+
{
|
218 |
+
$Block['type'] = $blockType;
|
219 |
+
|
220 |
+
if ( ! isset($Block['identified']))
|
221 |
+
{
|
222 |
+
$Blocks []= $CurrentBlock;
|
223 |
+
|
224 |
+
$Block['identified'] = true;
|
225 |
+
}
|
226 |
+
|
227 |
+
if (method_exists($this, 'block'.$blockType.'Continue'))
|
228 |
+
{
|
229 |
+
$Block['incomplete'] = true;
|
230 |
+
}
|
231 |
+
|
232 |
+
$CurrentBlock = $Block;
|
233 |
+
|
234 |
+
continue 2;
|
235 |
+
}
|
236 |
+
}
|
237 |
+
|
238 |
+
# ~
|
239 |
+
|
240 |
+
if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted']))
|
241 |
+
{
|
242 |
+
$CurrentBlock['element']['text'] .= "\n".$text;
|
243 |
+
}
|
244 |
+
else
|
245 |
+
{
|
246 |
+
$Blocks []= $CurrentBlock;
|
247 |
+
|
248 |
+
$CurrentBlock = $this->paragraph($Line);
|
249 |
+
|
250 |
+
$CurrentBlock['identified'] = true;
|
251 |
+
}
|
252 |
+
}
|
253 |
+
|
254 |
+
# ~
|
255 |
+
|
256 |
+
if (isset($CurrentBlock['incomplete']) and method_exists($this, 'block'.$CurrentBlock['type'].'Complete'))
|
257 |
+
{
|
258 |
+
$CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
|
259 |
+
}
|
260 |
+
|
261 |
+
# ~
|
262 |
+
|
263 |
+
$Blocks []= $CurrentBlock;
|
264 |
+
|
265 |
+
unset($Blocks[0]);
|
266 |
+
|
267 |
+
# ~
|
268 |
+
|
269 |
+
$markup = '';
|
270 |
+
|
271 |
+
foreach ($Blocks as $Block)
|
272 |
+
{
|
273 |
+
if (isset($Block['hidden']))
|
274 |
+
{
|
275 |
+
continue;
|
276 |
+
}
|
277 |
+
|
278 |
+
$markup .= "\n";
|
279 |
+
$markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']);
|
280 |
+
}
|
281 |
+
|
282 |
+
$markup .= "\n";
|
283 |
+
|
284 |
+
# ~
|
285 |
+
|
286 |
+
return $markup;
|
287 |
+
}
|
288 |
+
|
289 |
+
#
|
290 |
+
# Code
|
291 |
+
|
292 |
+
protected function blockCode($Line, $Block = null)
|
293 |
+
{
|
294 |
+
if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted']))
|
295 |
+
{
|
296 |
+
return;
|
297 |
+
}
|
298 |
+
|
299 |
+
if ($Line['indent'] >= 4)
|
300 |
+
{
|
301 |
+
$text = substr($Line['body'], 4);
|
302 |
+
|
303 |
+
$Block = array(
|
304 |
+
'element' => array(
|
305 |
+
'name' => 'pre',
|
306 |
+
'handler' => 'element',
|
307 |
+
'text' => array(
|
308 |
+
'name' => 'code',
|
309 |
+
'text' => $text,
|
310 |
+
),
|
311 |
+
),
|
312 |
+
);
|
313 |
+
|
314 |
+
return $Block;
|
315 |
+
}
|
316 |
+
}
|
317 |
+
|
318 |
+
protected function blockCodeContinue($Line, $Block)
|
319 |
+
{
|
320 |
+
if ($Line['indent'] >= 4)
|
321 |
+
{
|
322 |
+
if (isset($Block['interrupted']))
|
323 |
+
{
|
324 |
+
$Block['element']['text']['text'] .= "\n";
|
325 |
+
|
326 |
+
unset($Block['interrupted']);
|
327 |
+
}
|
328 |
+
|
329 |
+
$Block['element']['text']['text'] .= "\n";
|
330 |
+
|
331 |
+
$text = substr($Line['body'], 4);
|
332 |
+
|
333 |
+
$Block['element']['text']['text'] .= $text;
|
334 |
+
|
335 |
+
return $Block;
|
336 |
+
}
|
337 |
+
}
|
338 |
+
|
339 |
+
protected function blockCodeComplete($Block)
|
340 |
+
{
|
341 |
+
$text = $Block['element']['text']['text'];
|
342 |
+
|
343 |
+
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
344 |
+
|
345 |
+
$Block['element']['text']['text'] = $text;
|
346 |
+
|
347 |
+
return $Block;
|
348 |
+
}
|
349 |
+
|
350 |
+
#
|
351 |
+
# Comment
|
352 |
+
|
353 |
+
protected function blockComment($Line)
|
354 |
+
{
|
355 |
+
if ($this->markupEscaped)
|
356 |
+
{
|
357 |
+
return;
|
358 |
+
}
|
359 |
+
|
360 |
+
if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!')
|
361 |
+
{
|
362 |
+
$Block = array(
|
363 |
+
'markup' => $Line['body'],
|
364 |
+
);
|
365 |
+
|
366 |
+
if (preg_match('/-->$/', $Line['text']))
|
367 |
+
{
|
368 |
+
$Block['closed'] = true;
|
369 |
+
}
|
370 |
+
|
371 |
+
return $Block;
|
372 |
+
}
|
373 |
+
}
|
374 |
+
|
375 |
+
protected function blockCommentContinue($Line, array $Block)
|
376 |
+
{
|
377 |
+
if (isset($Block['closed']))
|
378 |
+
{
|
379 |
+
return;
|
380 |
+
}
|
381 |
+
|
382 |
+
$Block['markup'] .= "\n" . $Line['body'];
|
383 |
+
|
384 |
+
if (preg_match('/-->$/', $Line['text']))
|
385 |
+
{
|
386 |
+
$Block['closed'] = true;
|
387 |
+
}
|
388 |
+
|
389 |
+
return $Block;
|
390 |
+
}
|
391 |
+
|
392 |
+
#
|
393 |
+
# Fenced Code
|
394 |
+
|
395 |
+
protected function blockFencedCode($Line)
|
396 |
+
{
|
397 |
+
if (preg_match('/^(['.$Line['text'][0].']{3,})[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches))
|
398 |
+
{
|
399 |
+
$Element = array(
|
400 |
+
'name' => 'code',
|
401 |
+
'text' => '',
|
402 |
+
);
|
403 |
+
|
404 |
+
if (isset($matches[2]))
|
405 |
+
{
|
406 |
+
$class = 'language-'.$matches[2];
|
407 |
+
|
408 |
+
$Element['attributes'] = array(
|
409 |
+
'class' => $class,
|
410 |
+
);
|
411 |
+
}
|
412 |
+
|
413 |
+
$Block = array(
|
414 |
+
'char' => $Line['text'][0],
|
415 |
+
'element' => array(
|
416 |
+
'name' => 'pre',
|
417 |
+
'handler' => 'element',
|
418 |
+
'text' => $Element,
|
419 |
+
),
|
420 |
+
);
|
421 |
+
|
422 |
+
return $Block;
|
423 |
+
}
|
424 |
+
}
|
425 |
+
|
426 |
+
protected function blockFencedCodeContinue($Line, $Block)
|
427 |
+
{
|
428 |
+
if (isset($Block['complete']))
|
429 |
+
{
|
430 |
+
return;
|
431 |
+
}
|
432 |
+
|
433 |
+
if (isset($Block['interrupted']))
|
434 |
+
{
|
435 |
+
$Block['element']['text']['text'] .= "\n";
|
436 |
+
|
437 |
+
unset($Block['interrupted']);
|
438 |
+
}
|
439 |
+
|
440 |
+
if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text']))
|
441 |
+
{
|
442 |
+
$Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1);
|
443 |
+
|
444 |
+
$Block['complete'] = true;
|
445 |
+
|
446 |
+
return $Block;
|
447 |
+
}
|
448 |
+
|
449 |
+
$Block['element']['text']['text'] .= "\n".$Line['body'];;
|
450 |
+
|
451 |
+
return $Block;
|
452 |
+
}
|
453 |
+
|
454 |
+
protected function blockFencedCodeComplete($Block)
|
455 |
+
{
|
456 |
+
$text = $Block['element']['text']['text'];
|
457 |
+
|
458 |
+
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
459 |
+
|
460 |
+
$Block['element']['text']['text'] = $text;
|
461 |
+
|
462 |
+
return $Block;
|
463 |
+
}
|
464 |
+
|
465 |
+
#
|
466 |
+
# Header
|
467 |
+
|
468 |
+
protected function blockHeader($Line)
|
469 |
+
{
|
470 |
+
if (isset($Line['text'][1]))
|
471 |
+
{
|
472 |
+
$level = 1;
|
473 |
+
|
474 |
+
while (isset($Line['text'][$level]) and $Line['text'][$level] === '#')
|
475 |
+
{
|
476 |
+
$level ++;
|
477 |
+
}
|
478 |
+
|
479 |
+
if ($level > 6)
|
480 |
+
{
|
481 |
+
return;
|
482 |
+
}
|
483 |
+
|
484 |
+
$text = trim($Line['text'], '# ');
|
485 |
+
|
486 |
+
$Block = array(
|
487 |
+
'element' => array(
|
488 |
+
'name' => 'h' . min(6, $level),
|
489 |
+
'text' => $text,
|
490 |
+
'handler' => 'line',
|
491 |
+
),
|
492 |
+
);
|
493 |
+
|
494 |
+
return $Block;
|
495 |
+
}
|
496 |
+
}
|
497 |
+
|
498 |
+
#
|
499 |
+
# List
|
500 |
+
|
501 |
+
protected function blockList($Line)
|
502 |
+
{
|
503 |
+
list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]');
|
504 |
+
|
505 |
+
if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches))
|
506 |
+
{
|
507 |
+
$Block = array(
|
508 |
+
'indent' => $Line['indent'],
|
509 |
+
'pattern' => $pattern,
|
510 |
+
'element' => array(
|
511 |
+
'name' => $name,
|
512 |
+
'handler' => 'elements',
|
513 |
+
),
|
514 |
+
);
|
515 |
+
|
516 |
+
$Block['li'] = array(
|
517 |
+
'name' => 'li',
|
518 |
+
'handler' => 'li',
|
519 |
+
'text' => array(
|
520 |
+
$matches[2],
|
521 |
+
),
|
522 |
+
);
|
523 |
+
|
524 |
+
$Block['element']['text'] []= & $Block['li'];
|
525 |
+
|
526 |
+
return $Block;
|
527 |
+
}
|
528 |
+
}
|
529 |
+
|
530 |
+
protected function blockListContinue($Line, array $Block)
|
531 |
+
{
|
532 |
+
if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches))
|
533 |
+
{
|
534 |
+
if (isset($Block['interrupted']))
|
535 |
+
{
|
536 |
+
$Block['li']['text'] []= '';
|
537 |
+
|
538 |
+
unset($Block['interrupted']);
|
539 |
+
}
|
540 |
+
|
541 |
+
unset($Block['li']);
|
542 |
+
|
543 |
+
$text = isset($matches[1]) ? $matches[1] : '';
|
544 |
+
|
545 |
+
$Block['li'] = array(
|
546 |
+
'name' => 'li',
|
547 |
+
'handler' => 'li',
|
548 |
+
'text' => array(
|
549 |
+
$text,
|
550 |
+
),
|
551 |
+
);
|
552 |
+
|
553 |
+
$Block['element']['text'] []= & $Block['li'];
|
554 |
+
|
555 |
+
return $Block;
|
556 |
+
}
|
557 |
+
|
558 |
+
if ($Line['text'][0] === '[' and $this->blockReference($Line))
|
559 |
+
{
|
560 |
+
return $Block;
|
561 |
+
}
|
562 |
+
|
563 |
+
if ( ! isset($Block['interrupted']))
|
564 |
+
{
|
565 |
+
$text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
|
566 |
+
|
567 |
+
$Block['li']['text'] []= $text;
|
568 |
+
|
569 |
+
return $Block;
|
570 |
+
}
|
571 |
+
|
572 |
+
if ($Line['indent'] > 0)
|
573 |
+
{
|
574 |
+
$Block['li']['text'] []= '';
|
575 |
+
|
576 |
+
$text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
|
577 |
+
|
578 |
+
$Block['li']['text'] []= $text;
|
579 |
+
|
580 |
+
unset($Block['interrupted']);
|
581 |
+
|
582 |
+
return $Block;
|
583 |
+
}
|
584 |
+
}
|
585 |
+
|
586 |
+
#
|
587 |
+
# Quote
|
588 |
+
|
589 |
+
protected function blockQuote($Line)
|
590 |
+
{
|
591 |
+
if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
|
592 |
+
{
|
593 |
+
$Block = array(
|
594 |
+
'element' => array(
|
595 |
+
'name' => 'blockquote',
|
596 |
+
'handler' => 'lines',
|
597 |
+
'text' => (array) $matches[1],
|
598 |
+
),
|
599 |
+
);
|
600 |
+
|
601 |
+
return $Block;
|
602 |
+
}
|
603 |
+
}
|
604 |
+
|
605 |
+
protected function blockQuoteContinue($Line, array $Block)
|
606 |
+
{
|
607 |
+
if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
|
608 |
+
{
|
609 |
+
if (isset($Block['interrupted']))
|
610 |
+
{
|
611 |
+
$Block['element']['text'] []= '';
|
612 |
+
|
613 |
+
unset($Block['interrupted']);
|
614 |
+
}
|
615 |
+
|
616 |
+
$Block['element']['text'] []= $matches[1];
|
617 |
+
|
618 |
+
return $Block;
|
619 |
+
}
|
620 |
+
|
621 |
+
if ( ! isset($Block['interrupted']))
|
622 |
+
{
|
623 |
+
$Block['element']['text'] []= $Line['text'];
|
624 |
+
|
625 |
+
return $Block;
|
626 |
+
}
|
627 |
+
}
|
628 |
+
|
629 |
+
#
|
630 |
+
# Rule
|
631 |
+
|
632 |
+
protected function blockRule($Line)
|
633 |
+
{
|
634 |
+
if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text']))
|
635 |
+
{
|
636 |
+
$Block = array(
|
637 |
+
'element' => array(
|
638 |
+
'name' => 'hr'
|
639 |
+
),
|
640 |
+
);
|
641 |
+
|
642 |
+
return $Block;
|
643 |
+
}
|
644 |
+
}
|
645 |
+
|
646 |
+
#
|
647 |
+
# Setext
|
648 |
+
|
649 |
+
protected function blockSetextHeader($Line, array $Block = null)
|
650 |
+
{
|
651 |
+
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
|
652 |
+
{
|
653 |
+
return;
|
654 |
+
}
|
655 |
+
|
656 |
+
if (chop($Line['text'], $Line['text'][0]) === '')
|
657 |
+
{
|
658 |
+
$Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2';
|
659 |
+
|
660 |
+
return $Block;
|
661 |
+
}
|
662 |
+
}
|
663 |
+
|
664 |
+
#
|
665 |
+
# Markup
|
666 |
+
|
667 |
+
protected function blockMarkup($Line)
|
668 |
+
{
|
669 |
+
if ($this->markupEscaped)
|
670 |
+
{
|
671 |
+
return;
|
672 |
+
}
|
673 |
+
|
674 |
+
if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
|
675 |
+
{
|
676 |
+
if (in_array($matches[1], $this->textLevelElements))
|
677 |
+
{
|
678 |
+
return;
|
679 |
+
}
|
680 |
+
|
681 |
+
$Block = array(
|
682 |
+
'name' => $matches[1],
|
683 |
+
'depth' => 0,
|
684 |
+
'markup' => $Line['text'],
|
685 |
+
);
|
686 |
+
|
687 |
+
$length = strlen($matches[0]);
|
688 |
+
|
689 |
+
$remainder = substr($Line['text'], $length);
|
690 |
+
|
691 |
+
if (trim($remainder) === '')
|
692 |
+
{
|
693 |
+
if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
|
694 |
+
{
|
695 |
+
$Block['closed'] = true;
|
696 |
+
|
697 |
+
$Block['void'] = true;
|
698 |
+
}
|
699 |
+
}
|
700 |
+
else
|
701 |
+
{
|
702 |
+
if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
|
703 |
+
{
|
704 |
+
return;
|
705 |
+
}
|
706 |
+
|
707 |
+
if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder))
|
708 |
+
{
|
709 |
+
$Block['closed'] = true;
|
710 |
+
}
|
711 |
+
}
|
712 |
+
|
713 |
+
return $Block;
|
714 |
+
}
|
715 |
+
}
|
716 |
+
|
717 |
+
protected function blockMarkupContinue($Line, array $Block)
|
718 |
+
{
|
719 |
+
if (isset($Block['closed']))
|
720 |
+
{
|
721 |
+
return;
|
722 |
+
}
|
723 |
+
|
724 |
+
if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open
|
725 |
+
{
|
726 |
+
$Block['depth'] ++;
|
727 |
+
}
|
728 |
+
|
729 |
+
if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close
|
730 |
+
{
|
731 |
+
if ($Block['depth'] > 0)
|
732 |
+
{
|
733 |
+
$Block['depth'] --;
|
734 |
+
}
|
735 |
+
else
|
736 |
+
{
|
737 |
+
$Block['closed'] = true;
|
738 |
+
}
|
739 |
+
|
740 |
+
$Block['markup'] .= $matches[1];
|
741 |
+
}
|
742 |
+
|
743 |
+
if (isset($Block['interrupted']))
|
744 |
+
{
|
745 |
+
$Block['markup'] .= "\n";
|
746 |
+
|
747 |
+
unset($Block['interrupted']);
|
748 |
+
}
|
749 |
+
|
750 |
+
$Block['markup'] .= "\n".$Line['body'];
|
751 |
+
|
752 |
+
return $Block;
|
753 |
+
}
|
754 |
+
|
755 |
+
#
|
756 |
+
# Reference
|
757 |
+
|
758 |
+
protected function blockReference($Line)
|
759 |
+
{
|
760 |
+
if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches))
|
761 |
+
{
|
762 |
+
$id = strtolower($matches[1]);
|
763 |
+
|
764 |
+
$Data = array(
|
765 |
+
'url' => $matches[2],
|
766 |
+
'title' => null,
|
767 |
+
);
|
768 |
+
|
769 |
+
if (isset($matches[3]))
|
770 |
+
{
|
771 |
+
$Data['title'] = $matches[3];
|
772 |
+
}
|
773 |
+
|
774 |
+
$this->DefinitionData['Reference'][$id] = $Data;
|
775 |
+
|
776 |
+
$Block = array(
|
777 |
+
'hidden' => true,
|
778 |
+
);
|
779 |
+
|
780 |
+
return $Block;
|
781 |
+
}
|
782 |
+
}
|
783 |
+
|
784 |
+
#
|
785 |
+
# Table
|
786 |
+
|
787 |
+
protected function blockTable($Line, array $Block = null)
|
788 |
+
{
|
789 |
+
if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
|
790 |
+
{
|
791 |
+
return;
|
792 |
+
}
|
793 |
+
|
794 |
+
if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '')
|
795 |
+
{
|
796 |
+
$alignments = array();
|
797 |
+
|
798 |
+
$divider = $Line['text'];
|
799 |
+
|
800 |
+
$divider = trim($divider);
|
801 |
+
$divider = trim($divider, '|');
|
802 |
+
|
803 |
+
$dividerCells = explode('|', $divider);
|
804 |
+
|
805 |
+
foreach ($dividerCells as $dividerCell)
|
806 |
+
{
|
807 |
+
$dividerCell = trim($dividerCell);
|
808 |
+
|
809 |
+
if ($dividerCell === '')
|
810 |
+
{
|
811 |
+
continue;
|
812 |
+
}
|
813 |
+
|
814 |
+
$alignment = null;
|
815 |
+
|
816 |
+
if ($dividerCell[0] === ':')
|
817 |
+
{
|
818 |
+
$alignment = 'left';
|
819 |
+
}
|
820 |
+
|
821 |
+
if (substr($dividerCell, - 1) === ':')
|
822 |
+
{
|
823 |
+
$alignment = $alignment === 'left' ? 'center' : 'right';
|
824 |
+
}
|
825 |
+
|
826 |
+
$alignments []= $alignment;
|
827 |
+
}
|
828 |
+
|
829 |
+
# ~
|
830 |
+
|
831 |
+
$HeaderElements = array();
|
832 |
+
|
833 |
+
$header = $Block['element']['text'];
|
834 |
+
|
835 |
+
$header = trim($header);
|
836 |
+
$header = trim($header, '|');
|
837 |
+
|
838 |
+
$headerCells = explode('|', $header);
|
839 |
+
|
840 |
+
foreach ($headerCells as $index => $headerCell)
|
841 |
+
{
|
842 |
+
$headerCell = trim($headerCell);
|
843 |
+
|
844 |
+
$HeaderElement = array(
|
845 |
+
'name' => 'th',
|
846 |
+
'text' => $headerCell,
|
847 |
+
'handler' => 'line',
|
848 |
+
);
|
849 |
+
|
850 |
+
if (isset($alignments[$index]))
|
851 |
+
{
|
852 |
+
$alignment = $alignments[$index];
|
853 |
+
|
854 |
+
$HeaderElement['attributes'] = array(
|
855 |
+
'style' => 'text-align: '.$alignment.';',
|
856 |
+
);
|
857 |
+
}
|
858 |
+
|
859 |
+
$HeaderElements []= $HeaderElement;
|
860 |
+
}
|
861 |
+
|
862 |
+
# ~
|
863 |
+
|
864 |
+
$Block = array(
|
865 |
+
'alignments' => $alignments,
|
866 |
+
'identified' => true,
|
867 |
+
'element' => array(
|
868 |
+
'name' => 'table',
|
869 |
+
'handler' => 'elements',
|
870 |
+
),
|
871 |
+
);
|
872 |
+
|
873 |
+
$Block['element']['text'] []= array(
|
874 |
+
'name' => 'thead',
|
875 |
+
'handler' => 'elements',
|
876 |
+
);
|
877 |
+
|
878 |
+
$Block['element']['text'] []= array(
|
879 |
+
'name' => 'tbody',
|
880 |
+
'handler' => 'elements',
|
881 |
+
'text' => array(),
|
882 |
+
);
|
883 |
+
|
884 |
+
$Block['element']['text'][0]['text'] []= array(
|
885 |
+
'name' => 'tr',
|
886 |
+
'handler' => 'elements',
|
887 |
+
'text' => $HeaderElements,
|
888 |
+
);
|
889 |
+
|
890 |
+
return $Block;
|
891 |
+
}
|
892 |
+
}
|
893 |
+
|
894 |
+
protected function blockTableContinue($Line, array $Block)
|
895 |
+
{
|
896 |
+
if (isset($Block['interrupted']))
|
897 |
+
{
|
898 |
+
return;
|
899 |
+
}
|
900 |
+
|
901 |
+
if ($Line['text'][0] === '|' or strpos($Line['text'], '|'))
|
902 |
+
{
|
903 |
+
$Elements = array();
|
904 |
+
|
905 |
+
$row = $Line['text'];
|
906 |
+
|
907 |
+
$row = trim($row);
|
908 |
+
$row = trim($row, '|');
|
909 |
+
|
910 |
+
preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches);
|
911 |
+
|
912 |
+
foreach ($matches[0] as $index => $cell)
|
913 |
+
{
|
914 |
+
$cell = trim($cell);
|
915 |
+
|
916 |
+
$Element = array(
|
917 |
+
'name' => 'td',
|
918 |
+
'handler' => 'line',
|
919 |
+
'text' => $cell,
|
920 |
+
);
|
921 |
+
|
922 |
+
if (isset($Block['alignments'][$index]))
|
923 |
+
{
|
924 |
+
$Element['attributes'] = array(
|
925 |
+
'style' => 'text-align: '.$Block['alignments'][$index].';',
|
926 |
+
);
|
927 |
+
}
|
928 |
+
|
929 |
+
$Elements []= $Element;
|
930 |
+
}
|
931 |
+
|
932 |
+
$Element = array(
|
933 |
+
'name' => 'tr',
|
934 |
+
'handler' => 'elements',
|
935 |
+
'text' => $Elements,
|
936 |
+
);
|
937 |
+
|
938 |
+
$Block['element']['text'][1]['text'] []= $Element;
|
939 |
+
|
940 |
+
return $Block;
|
941 |
+
}
|
942 |
+
}
|
943 |
+
|
944 |
+
#
|
945 |
+
# ~
|
946 |
+
#
|
947 |
+
|
948 |
+
protected function paragraph($Line)
|
949 |
+
{
|
950 |
+
$Block = array(
|
951 |
+
'element' => array(
|
952 |
+
'name' => 'p',
|
953 |
+
'text' => $Line['text'],
|
954 |
+
'handler' => 'line',
|
955 |
+
),
|
956 |
+
);
|
957 |
+
|
958 |
+
return $Block;
|
959 |
+
}
|
960 |
+
|
961 |
+
#
|
962 |
+
# Inline Elements
|
963 |
+
#
|
964 |
+
|
965 |
+
protected $InlineTypes = array(
|
966 |
+
'"' => array('SpecialCharacter'),
|
967 |
+
'!' => array('Image'),
|
968 |
+
'&' => array('SpecialCharacter'),
|
969 |
+
'*' => array('Emphasis'),
|
970 |
+
':' => array('Url'),
|
971 |
+
'<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'),
|
972 |
+
'>' => array('SpecialCharacter'),
|
973 |
+
'[' => array('Link'),
|
974 |
+
'_' => array('Emphasis'),
|
975 |
+
'`' => array('Code'),
|
976 |
+
'~' => array('Strikethrough'),
|
977 |
+
'\\' => array('EscapeSequence'),
|
978 |
+
);
|
979 |
+
|
980 |
+
# ~
|
981 |
+
|
982 |
+
protected $inlineMarkerList = '!"*_&[:<>`~\\';
|
983 |
+
|
984 |
+
#
|
985 |
+
# ~
|
986 |
+
#
|
987 |
+
|
988 |
+
public function line($text)
|
989 |
+
{
|
990 |
+
$markup = '';
|
991 |
+
|
992 |
+
$unexaminedText = $text;
|
993 |
+
|
994 |
+
$markerPosition = 0;
|
995 |
+
|
996 |
+
while ($excerpt = strpbrk($unexaminedText, $this->inlineMarkerList))
|
997 |
+
{
|
998 |
+
$marker = $excerpt[0];
|
999 |
+
|
1000 |
+
$markerPosition += strpos($unexaminedText, $marker);
|
1001 |
+
|
1002 |
+
$Excerpt = array('text' => $excerpt, 'context' => $text);
|
1003 |
+
|
1004 |
+
foreach ($this->InlineTypes[$marker] as $inlineType)
|
1005 |
+
{
|
1006 |
+
$Inline = $this->{'inline'.$inlineType}($Excerpt);
|
1007 |
+
|
1008 |
+
if ( ! isset($Inline))
|
1009 |
+
{
|
1010 |
+
continue;
|
1011 |
+
}
|
1012 |
+
|
1013 |
+
if (isset($Inline['position']) and $Inline['position'] > $markerPosition) # position is ahead of marker
|
1014 |
+
{
|
1015 |
+
continue;
|
1016 |
+
}
|
1017 |
+
|
1018 |
+
if ( ! isset($Inline['position']))
|
1019 |
+
{
|
1020 |
+
$Inline['position'] = $markerPosition;
|
1021 |
+
}
|
1022 |
+
|
1023 |
+
$unmarkedText = substr($text, 0, $Inline['position']);
|
1024 |
+
|
1025 |
+
$markup .= $this->unmarkedText($unmarkedText);
|
1026 |
+
|
1027 |
+
$markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']);
|
1028 |
+
|
1029 |
+
$text = substr($text, $Inline['position'] + $Inline['extent']);
|
1030 |
+
|
1031 |
+
$unexaminedText = $text;
|
1032 |
+
|
1033 |
+
$markerPosition = 0;
|
1034 |
+
|
1035 |
+
continue 2;
|
1036 |
+
}
|
1037 |
+
|
1038 |
+
$unexaminedText = substr($excerpt, 1);
|
1039 |
+
|
1040 |
+
$markerPosition ++;
|
1041 |
+
}
|
1042 |
+
|
1043 |
+
$markup .= $this->unmarkedText($text);
|
1044 |
+
|
1045 |
+
return $markup;
|
1046 |
+
}
|
1047 |
+
|
1048 |
+
#
|
1049 |
+
# ~
|
1050 |
+
#
|
1051 |
+
|
1052 |
+
protected function inlineCode($Excerpt)
|
1053 |
+
{
|
1054 |
+
$marker = $Excerpt['text'][0];
|
1055 |
+
|
1056 |
+
if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
|
1057 |
+
{
|
1058 |
+
$text = $matches[2];
|
1059 |
+
$text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
|
1060 |
+
$text = preg_replace("/[ ]*\n/", ' ', $text);
|
1061 |
+
|
1062 |
+
return array(
|
1063 |
+
'extent' => strlen($matches[0]),
|
1064 |
+
'element' => array(
|
1065 |
+
'name' => 'code',
|
1066 |
+
'text' => $text,
|
1067 |
+
),
|
1068 |
+
);
|
1069 |
+
}
|
1070 |
+
}
|
1071 |
+
|
1072 |
+
protected function inlineEmailTag($Excerpt)
|
1073 |
+
{
|
1074 |
+
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches))
|
1075 |
+
{
|
1076 |
+
$url = $matches[1];
|
1077 |
+
|
1078 |
+
if ( ! isset($matches[2]))
|
1079 |
+
{
|
1080 |
+
$url = 'mailto:' . $url;
|
1081 |
+
}
|
1082 |
+
|
1083 |
+
return array(
|
1084 |
+
'extent' => strlen($matches[0]),
|
1085 |
+
'element' => array(
|
1086 |
+
'name' => 'a',
|
1087 |
+
'text' => $matches[1],
|
1088 |
+
'attributes' => array(
|
1089 |
+
'href' => $url,
|
1090 |
+
),
|
1091 |
+
),
|
1092 |
+
);
|
1093 |
+
}
|
1094 |
+
}
|
1095 |
+
|
1096 |
+
protected function inlineEmphasis($Excerpt)
|
1097 |
+
{
|
1098 |
+
if ( ! isset($Excerpt['text'][1]))
|
1099 |
+
{
|
1100 |
+
return;
|
1101 |
+
}
|
1102 |
+
|
1103 |
+
$marker = $Excerpt['text'][0];
|
1104 |
+
|
1105 |
+
if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
|
1106 |
+
{
|
1107 |
+
$emphasis = 'strong';
|
1108 |
+
}
|
1109 |
+
elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
|
1110 |
+
{
|
1111 |
+
$emphasis = 'em';
|
1112 |
+
}
|
1113 |
+
else
|
1114 |
+
{
|
1115 |
+
return;
|
1116 |
+
}
|
1117 |
+
|
1118 |
+
return array(
|
1119 |
+
'extent' => strlen($matches[0]),
|
1120 |
+
'element' => array(
|
1121 |
+
'name' => $emphasis,
|
1122 |
+
'handler' => 'line',
|
1123 |
+
'text' => $matches[1],
|
1124 |
+
),
|
1125 |
+
);
|
1126 |
+
}
|
1127 |
+
|
1128 |
+
protected function inlineEscapeSequence($Excerpt)
|
1129 |
+
{
|
1130 |
+
if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters))
|
1131 |
+
{
|
1132 |
+
return array(
|
1133 |
+
'markup' => $Excerpt['text'][1],
|
1134 |
+
'extent' => 2,
|
1135 |
+
);
|
1136 |
+
}
|
1137 |
+
}
|
1138 |
+
|
1139 |
+
protected function inlineImage($Excerpt)
|
1140 |
+
{
|
1141 |
+
if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[')
|
1142 |
+
{
|
1143 |
+
return;
|
1144 |
+
}
|
1145 |
+
|
1146 |
+
$Excerpt['text']= substr($Excerpt['text'], 1);
|
1147 |
+
|
1148 |
+
$Link = $this->inlineLink($Excerpt);
|
1149 |
+
|
1150 |
+
if ($Link === null)
|
1151 |
+
{
|
1152 |
+
return;
|
1153 |
+
}
|
1154 |
+
|
1155 |
+
$Inline = array(
|
1156 |
+
'extent' => $Link['extent'] + 1,
|
1157 |
+
'element' => array(
|
1158 |
+
'name' => 'img',
|
1159 |
+
'attributes' => array(
|
1160 |
+
'src' => $Link['element']['attributes']['href'],
|
1161 |
+
'alt' => $Link['element']['text'],
|
1162 |
+
),
|
1163 |
+
),
|
1164 |
+
);
|
1165 |
+
|
1166 |
+
$Inline['element']['attributes'] += $Link['element']['attributes'];
|
1167 |
+
|
1168 |
+
unset($Inline['element']['attributes']['href']);
|
1169 |
+
|
1170 |
+
return $Inline;
|
1171 |
+
}
|
1172 |
+
|
1173 |
+
protected function inlineLink($Excerpt)
|
1174 |
+
{
|
1175 |
+
$Element = array(
|
1176 |
+
'name' => 'a',
|
1177 |
+
'handler' => 'line',
|
1178 |
+
'text' => null,
|
1179 |
+
'attributes' => array(
|
1180 |
+
'href' => null,
|
1181 |
+
'title' => null,
|
1182 |
+
),
|
1183 |
+
);
|
1184 |
+
|
1185 |
+
$extent = 0;
|
1186 |
+
|
1187 |
+
$remainder = $Excerpt['text'];
|
1188 |
+
|
1189 |
+
if (preg_match('/\[((?:[^][]|(?R))*)\]/', $remainder, $matches))
|
1190 |
+
{
|
1191 |
+
$Element['text'] = $matches[1];
|
1192 |
+
|
1193 |
+
$extent += strlen($matches[0]);
|
1194 |
+
|
1195 |
+
$remainder = substr($remainder, $extent);
|
1196 |
+
}
|
1197 |
+
else
|
1198 |
+
{
|
1199 |
+
return;
|
1200 |
+
}
|
1201 |
+
|
1202 |
+
if (preg_match('/^[(]((?:[^ (]|[(][^ )]+[)])+)(?:[ ]+("[^"]+"|\'[^\']+\'))?[)]/', $remainder, $matches))
|
1203 |
+
{
|
1204 |
+
$Element['attributes']['href'] = $matches[1];
|
1205 |
+
|
1206 |
+
if (isset($matches[2]))
|
1207 |
+
{
|
1208 |
+
$Element['attributes']['title'] = substr($matches[2], 1, - 1);
|
1209 |
+
}
|
1210 |
+
|
1211 |
+
$extent += strlen($matches[0]);
|
1212 |
+
}
|
1213 |
+
else
|
1214 |
+
{
|
1215 |
+
if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
|
1216 |
+
{
|
1217 |
+
$definition = $matches[1] ? $matches[1] : $Element['text'];
|
1218 |
+
$definition = strtolower($definition);
|
1219 |
+
|
1220 |
+
$extent += strlen($matches[0]);
|
1221 |
+
}
|
1222 |
+
else
|
1223 |
+
{
|
1224 |
+
$definition = strtolower($Element['text']);
|
1225 |
+
}
|
1226 |
+
|
1227 |
+
if ( ! isset($this->DefinitionData['Reference'][$definition]))
|
1228 |
+
{
|
1229 |
+
return;
|
1230 |
+
}
|
1231 |
+
|
1232 |
+
$Definition = $this->DefinitionData['Reference'][$definition];
|
1233 |
+
|
1234 |
+
$Element['attributes']['href'] = $Definition['url'];
|
1235 |
+
$Element['attributes']['title'] = $Definition['title'];
|
1236 |
+
}
|
1237 |
+
|
1238 |
+
$Element['attributes']['href'] = str_replace(array('&', '<'), array('&', '<'), $Element['attributes']['href']);
|
1239 |
+
|
1240 |
+
return array(
|
1241 |
+
'extent' => $extent,
|
1242 |
+
'element' => $Element,
|
1243 |
+
);
|
1244 |
+
}
|
1245 |
+
|
1246 |
+
protected function inlineMarkup($Excerpt)
|
1247 |
+
{
|
1248 |
+
if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false)
|
1249 |
+
{
|
1250 |
+
return;
|
1251 |
+
}
|
1252 |
+
|
1253 |
+
if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w*[ ]*>/s', $Excerpt['text'], $matches))
|
1254 |
+
{
|
1255 |
+
return array(
|
1256 |
+
'markup' => $matches[0],
|
1257 |
+
'extent' => strlen($matches[0]),
|
1258 |
+
);
|
1259 |
+
}
|
1260 |
+
|
1261 |
+
if ($Excerpt['text'][1] === '!' and preg_match('/^<!---?[^>-](?:-?[^-])*-->/s', $Excerpt['text'], $matches))
|
1262 |
+
{
|
1263 |
+
return array(
|
1264 |
+
'markup' => $matches[0],
|
1265 |
+
'extent' => strlen($matches[0]),
|
1266 |
+
);
|
1267 |
+
}
|
1268 |
+
|
1269 |
+
if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches))
|
1270 |
+
{
|
1271 |
+
return array(
|
1272 |
+
'markup' => $matches[0],
|
1273 |
+
'extent' => strlen($matches[0]),
|
1274 |
+
);
|
1275 |
+
}
|
1276 |
+
}
|
1277 |
+
|
1278 |
+
protected function inlineSpecialCharacter($Excerpt)
|
1279 |
+
{
|
1280 |
+
if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text']))
|
1281 |
+
{
|
1282 |
+
return array(
|
1283 |
+
'markup' => '&',
|
1284 |
+
'extent' => 1,
|
1285 |
+
);
|
1286 |
+
}
|
1287 |
+
|
1288 |
+
$SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot');
|
1289 |
+
|
1290 |
+
if (isset($SpecialCharacter[$Excerpt['text'][0]]))
|
1291 |
+
{
|
1292 |
+
return array(
|
1293 |
+
'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';',
|
1294 |
+
'extent' => 1,
|
1295 |
+
);
|
1296 |
+
}
|
1297 |
+
}
|
1298 |
+
|
1299 |
+
protected function inlineStrikethrough($Excerpt)
|
1300 |
+
{
|
1301 |
+
if ( ! isset($Excerpt['text'][1]))
|
1302 |
+
{
|
1303 |
+
return;
|
1304 |
+
}
|
1305 |
+
|
1306 |
+
if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches))
|
1307 |
+
{
|
1308 |
+
return array(
|
1309 |
+
'extent' => strlen($matches[0]),
|
1310 |
+
'element' => array(
|
1311 |
+
'name' => 'del',
|
1312 |
+
'text' => $matches[1],
|
1313 |
+
'handler' => 'line',
|
1314 |
+
),
|
1315 |
+
);
|
1316 |
+
}
|
1317 |
+
}
|
1318 |
+
|
1319 |
+
protected function inlineUrl($Excerpt)
|
1320 |
+
{
|
1321 |
+
if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/')
|
1322 |
+
{
|
1323 |
+
return;
|
1324 |
+
}
|
1325 |
+
|
1326 |
+
if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
|
1327 |
+
{
|
1328 |
+
$Inline = array(
|
1329 |
+
'extent' => strlen($matches[0][0]),
|
1330 |
+
'position' => $matches[0][1],
|
1331 |
+
'element' => array(
|
1332 |
+
'name' => 'a',
|
1333 |
+
'text' => $matches[0][0],
|
1334 |
+
'attributes' => array(
|
1335 |
+
'href' => $matches[0][0],
|
1336 |
+
),
|
1337 |
+
),
|
1338 |
+
);
|
1339 |
+
|
1340 |
+
return $Inline;
|
1341 |
+
}
|
1342 |
+
}
|
1343 |
+
|
1344 |
+
protected function inlineUrlTag($Excerpt)
|
1345 |
+
{
|
1346 |
+
if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches))
|
1347 |
+
{
|
1348 |
+
$url = str_replace(array('&', '<'), array('&', '<'), $matches[1]);
|
1349 |
+
|
1350 |
+
return array(
|
1351 |
+
'extent' => strlen($matches[0]),
|
1352 |
+
'element' => array(
|
1353 |
+
'name' => 'a',
|
1354 |
+
'text' => $url,
|
1355 |
+
'attributes' => array(
|
1356 |
+
'href' => $url,
|
1357 |
+
),
|
1358 |
+
),
|
1359 |
+
);
|
1360 |
+
}
|
1361 |
+
}
|
1362 |
+
|
1363 |
+
#
|
1364 |
+
# ~
|
1365 |
+
|
1366 |
+
protected $unmarkedInlineTypes = array("\n" => 'Break', '://' => 'Url');
|
1367 |
+
|
1368 |
+
# ~
|
1369 |
+
|
1370 |
+
protected function unmarkedText($text)
|
1371 |
+
{
|
1372 |
+
if ($this->breaksEnabled)
|
1373 |
+
{
|
1374 |
+
$text = preg_replace('/[ ]*\n/', "<br />\n", $text);
|
1375 |
+
}
|
1376 |
+
else
|
1377 |
+
{
|
1378 |
+
$text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "<br />\n", $text);
|
1379 |
+
$text = str_replace(" \n", "\n", $text);
|
1380 |
+
}
|
1381 |
+
|
1382 |
+
return $text;
|
1383 |
+
}
|
1384 |
+
|
1385 |
+
#
|
1386 |
+
# Handlers
|
1387 |
+
#
|
1388 |
+
|
1389 |
+
protected function element(array $Element)
|
1390 |
+
{
|
1391 |
+
$markup = '<'.$Element['name'];
|
1392 |
+
|
1393 |
+
if (isset($Element['attributes']))
|
1394 |
+
{
|
1395 |
+
foreach ($Element['attributes'] as $name => $value)
|
1396 |
+
{
|
1397 |
+
if ($value === null)
|
1398 |
+
{
|
1399 |
+
continue;
|
1400 |
+
}
|
1401 |
+
|
1402 |
+
$markup .= ' '.$name.'="'.$value.'"';
|
1403 |
+
}
|
1404 |
+
}
|
1405 |
+
|
1406 |
+
if (isset($Element['text']))
|
1407 |
+
{
|
1408 |
+
$markup .= '>';
|
1409 |
+
|
1410 |
+
if (isset($Element['handler']))
|
1411 |
+
{
|
1412 |
+
$markup .= $this->$Element['handler']($Element['text']);
|
1413 |
+
}
|
1414 |
+
else
|
1415 |
+
{
|
1416 |
+
$markup .= $Element['text'];
|
1417 |
+
}
|
1418 |
+
|
1419 |
+
$markup .= '</'.$Element['name'].'>';
|
1420 |
+
}
|
1421 |
+
else
|
1422 |
+
{
|
1423 |
+
$markup .= ' />';
|
1424 |
+
}
|
1425 |
+
|
1426 |
+
return $markup;
|
1427 |
+
}
|
1428 |
+
|
1429 |
+
protected function elements(array $Elements)
|
1430 |
+
{
|
1431 |
+
$markup = '';
|
1432 |
+
|
1433 |
+
foreach ($Elements as $Element)
|
1434 |
+
{
|
1435 |
+
$markup .= "\n" . $this->element($Element);
|
1436 |
+
}
|
1437 |
+
|
1438 |
+
$markup .= "\n";
|
1439 |
+
|
1440 |
+
return $markup;
|
1441 |
+
}
|
1442 |
+
|
1443 |
+
# ~
|
1444 |
+
|
1445 |
+
protected function li($lines)
|
1446 |
+
{
|
1447 |
+
$markup = $this->lines($lines);
|
1448 |
+
|
1449 |
+
$trimmedMarkup = trim($markup);
|
1450 |
+
|
1451 |
+
if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '<p>')
|
1452 |
+
{
|
1453 |
+
$markup = $trimmedMarkup;
|
1454 |
+
$markup = substr($markup, 3);
|
1455 |
+
|
1456 |
+
$position = strpos($markup, "</p>");
|
1457 |
+
|
1458 |
+
$markup = substr_replace($markup, '', $position, 4);
|
1459 |
+
}
|
1460 |
+
|
1461 |
+
return $markup;
|
1462 |
+
}
|
1463 |
+
|
1464 |
+
#
|
1465 |
+
# Deprecated Methods
|
1466 |
+
#
|
1467 |
+
|
1468 |
+
function parse($text)
|
1469 |
+
{
|
1470 |
+
$markup = $this->text($text);
|
1471 |
+
|
1472 |
+
return $markup;
|
1473 |
+
}
|
1474 |
+
|
1475 |
+
#
|
1476 |
+
# Static Methods
|
1477 |
+
#
|
1478 |
+
|
1479 |
+
static function instance($name = 'default')
|
1480 |
+
{
|
1481 |
+
if (isset(self::$instances[$name]))
|
1482 |
+
{
|
1483 |
+
return self::$instances[$name];
|
1484 |
+
}
|
1485 |
+
|
1486 |
+
$instance = new self();
|
1487 |
+
|
1488 |
+
self::$instances[$name] = $instance;
|
1489 |
+
|
1490 |
+
return $instance;
|
1491 |
+
}
|
1492 |
+
|
1493 |
+
private static $instances = array();
|
1494 |
+
|
1495 |
+
#
|
1496 |
+
# Fields
|
1497 |
+
#
|
1498 |
+
|
1499 |
+
protected $DefinitionData;
|
1500 |
+
|
1501 |
+
#
|
1502 |
+
# Read-Only
|
1503 |
+
|
1504 |
+
protected $specialCharacters = array(
|
1505 |
+
'\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|',
|
1506 |
+
);
|
1507 |
+
|
1508 |
+
protected $StrongRegex = array(
|
1509 |
+
'*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s',
|
1510 |
+
'_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us',
|
1511 |
+
);
|
1512 |
+
|
1513 |
+
protected $EmRegex = array(
|
1514 |
+
'*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
|
1515 |
+
'_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us',
|
1516 |
+
);
|
1517 |
+
|
1518 |
+
protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?';
|
1519 |
+
|
1520 |
+
protected $voidElements = array(
|
1521 |
+
'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source',
|
1522 |
+
);
|
1523 |
+
|
1524 |
+
protected $textLevelElements = array(
|
1525 |
+
'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont',
|
1526 |
+
'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing',
|
1527 |
+
'i', 'rp', 'del', 'code', 'strike', 'marquee',
|
1528 |
+
'q', 'rt', 'ins', 'font', 'strong',
|
1529 |
+
's', 'tt', 'sub', 'mark',
|
1530 |
+
'u', 'xm', 'sup', 'nobr',
|
1531 |
+
'var', 'ruby',
|
1532 |
+
'wbr', 'span',
|
1533 |
+
'time',
|
1534 |
+
);
|
1535 |
+
}
|
includes/plugin-update-checker/vendor/readme-parser.php
ADDED
@@ -0,0 +1,331 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* This is a slightly modified version of github.com/markjaquith/WordPress-Plugin-Readme-Parser
|
5 |
+
* It uses Parsedown instead of the "Markdown Extra" parser.
|
6 |
+
*/
|
7 |
+
|
8 |
+
Class PucReadmeParser {
|
9 |
+
|
10 |
+
function __construct() {
|
11 |
+
// This space intentially blank
|
12 |
+
}
|
13 |
+
|
14 |
+
function parse_readme( $file ) {
|
15 |
+
$file_contents = @implode('', @file($file));
|
16 |
+
return $this->parse_readme_contents( $file_contents );
|
17 |
+
}
|
18 |
+
|
19 |
+
function parse_readme_contents( $file_contents ) {
|
20 |
+
$file_contents = str_replace(array("\r\n", "\r"), "\n", $file_contents);
|
21 |
+
$file_contents = trim($file_contents);
|
22 |
+
if ( 0 === strpos( $file_contents, "\xEF\xBB\xBF" ) )
|
23 |
+
$file_contents = substr( $file_contents, 3 );
|
24 |
+
|
25 |
+
// Markdown transformations
|
26 |
+
$file_contents = preg_replace( "|^###([^#]+)#*?\s*?\n|im", '=$1='."\n", $file_contents );
|
27 |
+
$file_contents = preg_replace( "|^##([^#]+)#*?\s*?\n|im", '==$1=='."\n", $file_contents );
|
28 |
+
$file_contents = preg_replace( "|^#([^#]+)#*?\s*?\n|im", '===$1==='."\n", $file_contents );
|
29 |
+
|
30 |
+
// === Plugin Name ===
|
31 |
+
// Must be the very first thing.
|
32 |
+
if ( !preg_match('|^===(.*)===|', $file_contents, $_name) )
|
33 |
+
return array(); // require a name
|
34 |
+
$name = trim($_name[1], '=');
|
35 |
+
$name = $this->sanitize_text( $name );
|
36 |
+
|
37 |
+
$file_contents = $this->chop_string( $file_contents, $_name[0] );
|
38 |
+
|
39 |
+
|
40 |
+
// Requires at least: 1.5
|
41 |
+
if ( preg_match('|Requires at least:(.*)|i', $file_contents, $_requires_at_least) )
|
42 |
+
$requires_at_least = $this->sanitize_text($_requires_at_least[1]);
|
43 |
+
else
|
44 |
+
$requires_at_least = NULL;
|
45 |
+
|
46 |
+
|
47 |
+
// Tested up to: 2.1
|
48 |
+
if ( preg_match('|Tested up to:(.*)|i', $file_contents, $_tested_up_to) )
|
49 |
+
$tested_up_to = $this->sanitize_text( $_tested_up_to[1] );
|
50 |
+
else
|
51 |
+
$tested_up_to = NULL;
|
52 |
+
|
53 |
+
|
54 |
+
// Stable tag: 10.4-ride-the-fire-eagle-danger-day
|
55 |
+
if ( preg_match('|Stable tag:(.*)|i', $file_contents, $_stable_tag) )
|
56 |
+
$stable_tag = $this->sanitize_text( $_stable_tag[1] );
|
57 |
+
else
|
58 |
+
$stable_tag = NULL; // we assume trunk, but don't set it here to tell the difference between specified trunk and default trunk
|
59 |
+
|
60 |
+
|
61 |
+
// Tags: some tag, another tag, we like tags
|
62 |
+
if ( preg_match('|Tags:(.*)|i', $file_contents, $_tags) ) {
|
63 |
+
$tags = preg_split('|,[\s]*?|', trim($_tags[1]));
|
64 |
+
foreach ( array_keys($tags) as $t )
|
65 |
+
$tags[$t] = $this->sanitize_text( $tags[$t] );
|
66 |
+
} else {
|
67 |
+
$tags = array();
|
68 |
+
}
|
69 |
+
|
70 |
+
|
71 |
+
// Contributors: markjaquith, mdawaffe, zefrank
|
72 |
+
$contributors = array();
|
73 |
+
if ( preg_match('|Contributors:(.*)|i', $file_contents, $_contributors) ) {
|
74 |
+
$temp_contributors = preg_split('|,[\s]*|', trim($_contributors[1]));
|
75 |
+
foreach ( array_keys($temp_contributors) as $c ) {
|
76 |
+
$tmp_sanitized = $this->user_sanitize( $temp_contributors[$c] );
|
77 |
+
if ( strlen(trim($tmp_sanitized)) > 0 )
|
78 |
+
$contributors[$c] = $tmp_sanitized;
|
79 |
+
unset($tmp_sanitized);
|
80 |
+
}
|
81 |
+
}
|
82 |
+
|
83 |
+
|
84 |
+
// Donate Link: URL
|
85 |
+
if ( preg_match('|Donate link:(.*)|i', $file_contents, $_donate_link) )
|
86 |
+
$donate_link = esc_url( $_donate_link[1] );
|
87 |
+
else
|
88 |
+
$donate_link = NULL;
|
89 |
+
|
90 |
+
|
91 |
+
// togs, conts, etc are optional and order shouldn't matter. So we chop them only after we've grabbed their values.
|
92 |
+
foreach ( array('tags', 'contributors', 'requires_at_least', 'tested_up_to', 'stable_tag', 'donate_link') as $chop ) {
|
93 |
+
if ( $$chop ) {
|
94 |
+
$_chop = '_' . $chop;
|
95 |
+
$file_contents = $this->chop_string( $file_contents, ${$_chop}[0] );
|
96 |
+
}
|
97 |
+
}
|
98 |
+
|
99 |
+
$file_contents = trim($file_contents);
|
100 |
+
|
101 |
+
|
102 |
+
// short-description fu
|
103 |
+
if ( !preg_match('/(^(.*?))^[\s]*=+?[\s]*.+?[\s]*=+?/ms', $file_contents, $_short_description) )
|
104 |
+
$_short_description = array( 1 => &$file_contents, 2 => &$file_contents );
|
105 |
+
$short_desc_filtered = $this->sanitize_text( $_short_description[2] );
|
106 |
+
$short_desc_length = strlen($short_desc_filtered);
|
107 |
+
$short_description = substr($short_desc_filtered, 0, 150);
|
108 |
+
if ( $short_desc_length > strlen($short_description) )
|
109 |
+
$truncated = true;
|
110 |
+
else
|
111 |
+
$truncated = false;
|
112 |
+
if ( $_short_description[1] )
|
113 |
+
$file_contents = $this->chop_string( $file_contents, $_short_description[1] ); // yes, the [1] is intentional
|
114 |
+
|
115 |
+
// == Section ==
|
116 |
+
// Break into sections
|
117 |
+
// $_sections[0] will be the title of the first section, $_sections[1] will be the content of the first section
|
118 |
+
// the array alternates from there: title2, content2, title3, content3... and so forth
|
119 |
+
$_sections = preg_split('/^[\s]*==[\s]*(.+?)[\s]*==/m', $file_contents, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
|
120 |
+
|
121 |
+
$sections = array();
|
122 |
+
for ( $i=1; $i <= count($_sections); $i +=2 ) {
|
123 |
+
$_sections[$i] = preg_replace('/^[\s]*=[\s]+(.+?)[\s]+=/m', '<h4>$1</h4>', $_sections[$i]);
|
124 |
+
$_sections[$i] = $this->filter_text( $_sections[$i], true );
|
125 |
+
$title = $this->sanitize_text( $_sections[$i-1] );
|
126 |
+
$sections[str_replace(' ', '_', strtolower($title))] = array('title' => $title, 'content' => $_sections[$i]);
|
127 |
+
}
|
128 |
+
|
129 |
+
|
130 |
+
// Special sections
|
131 |
+
// This is where we nab our special sections, so we can enforce their order and treat them differently, if needed
|
132 |
+
// upgrade_notice is not a section, but parse it like it is for now
|
133 |
+
$final_sections = array();
|
134 |
+
foreach ( array('description', 'installation', 'frequently_asked_questions', 'screenshots', 'changelog', 'change_log', 'upgrade_notice') as $special_section ) {
|
135 |
+
if ( isset($sections[$special_section]) ) {
|
136 |
+
$final_sections[$special_section] = $sections[$special_section]['content'];
|
137 |
+
unset($sections[$special_section]);
|
138 |
+
}
|
139 |
+
}
|
140 |
+
if ( isset($final_sections['change_log']) && empty($final_sections['changelog']) )
|
141 |
+
$final_sections['changelog'] = $final_sections['change_log'];
|
142 |
+
|
143 |
+
|
144 |
+
$final_screenshots = array();
|
145 |
+
if ( isset($final_sections['screenshots']) ) {
|
146 |
+
preg_match_all('|<li>(.*?)</li>|s', $final_sections['screenshots'], $screenshots, PREG_SET_ORDER);
|
147 |
+
if ( $screenshots ) {
|
148 |
+
foreach ( (array) $screenshots as $ss )
|
149 |
+
$final_screenshots[] = $ss[1];
|
150 |
+
}
|
151 |
+
}
|
152 |
+
|
153 |
+
// Parse the upgrade_notice section specially:
|
154 |
+
// 1.0 => blah, 1.1 => fnord
|
155 |
+
$upgrade_notice = array();
|
156 |
+
if ( isset($final_sections['upgrade_notice']) ) {
|
157 |
+
$split = preg_split( '#<h4>(.*?)</h4>#', $final_sections['upgrade_notice'], -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
|
158 |
+
for ( $i = 0; $i < count( $split ); $i += 2 )
|
159 |
+
$upgrade_notice[$this->sanitize_text( $split[$i] )] = substr( $this->sanitize_text( $split[$i + 1] ), 0, 300 );
|
160 |
+
unset( $final_sections['upgrade_notice'] );
|
161 |
+
}
|
162 |
+
|
163 |
+
// No description?
|
164 |
+
// No problem... we'll just fall back to the old style of description
|
165 |
+
// We'll even let you use markup this time!
|
166 |
+
$excerpt = false;
|
167 |
+
if ( !isset($final_sections['description']) ) {
|
168 |
+
$final_sections = array_merge(array('description' => $this->filter_text( $_short_description[2], true )), $final_sections);
|
169 |
+
$excerpt = true;
|
170 |
+
}
|
171 |
+
|
172 |
+
|
173 |
+
// dump the non-special sections into $remaining_content
|
174 |
+
// their order will be determined by their original order in the readme.txt
|
175 |
+
$remaining_content = '';
|
176 |
+
foreach ( $sections as $s_name => $s_data ) {
|
177 |
+
$remaining_content .= "\n<h3>{$s_data['title']}</h3>\n{$s_data['content']}";
|
178 |
+
}
|
179 |
+
$remaining_content = trim($remaining_content);
|
180 |
+
|
181 |
+
|
182 |
+
// All done!
|
183 |
+
// $r['tags'] and $r['contributors'] are simple arrays
|
184 |
+
// $r['sections'] is an array with named elements
|
185 |
+
$r = array(
|
186 |
+
'name' => $name,
|
187 |
+
'tags' => $tags,
|
188 |
+
'requires_at_least' => $requires_at_least,
|
189 |
+
'tested_up_to' => $tested_up_to,
|
190 |
+
'stable_tag' => $stable_tag,
|
191 |
+
'contributors' => $contributors,
|
192 |
+
'donate_link' => $donate_link,
|
193 |
+
'short_description' => $short_description,
|
194 |
+
'screenshots' => $final_screenshots,
|
195 |
+
'is_excerpt' => $excerpt,
|
196 |
+
'is_truncated' => $truncated,
|
197 |
+
'sections' => $final_sections,
|
198 |
+
'remaining_content' => $remaining_content,
|
199 |
+
'upgrade_notice' => $upgrade_notice
|
200 |
+
);
|
201 |
+
|
202 |
+
return $r;
|
203 |
+
}
|
204 |
+
|
205 |
+
function chop_string( $string, $chop ) { // chop a "prefix" from a string: Agressive! uses strstr not 0 === strpos
|
206 |
+
if ( $_string = strstr($string, $chop) ) {
|
207 |
+
$_string = substr($_string, strlen($chop));
|
208 |
+
return trim($_string);
|
209 |
+
} else {
|
210 |
+
return trim($string);
|
211 |
+
}
|
212 |
+
}
|
213 |
+
|
214 |
+
function user_sanitize( $text, $strict = false ) { // whitelisted chars
|
215 |
+
if ( function_exists('user_sanitize') ) // bbPress native
|
216 |
+
return user_sanitize( $text, $strict );
|
217 |
+
|
218 |
+
if ( $strict ) {
|
219 |
+
$text = preg_replace('/[^a-z0-9-]/i', '', $text);
|
220 |
+
$text = preg_replace('|-+|', '-', $text);
|
221 |
+
} else {
|
222 |
+
$text = preg_replace('/[^a-z0-9_-]/i', '', $text);
|
223 |
+
}
|
224 |
+
return $text;
|
225 |
+
}
|
226 |
+
|
227 |
+
function sanitize_text( $text ) { // not fancy
|
228 |
+
$text = strip_tags($text);
|
229 |
+
$text = esc_html($text);
|
230 |
+
$text = trim($text);
|
231 |
+
return $text;
|
232 |
+
}
|
233 |
+
|
234 |
+
function filter_text( $text, $markdown = false ) { // fancy, Markdown
|
235 |
+
$text = trim($text);
|
236 |
+
|
237 |
+
$text = call_user_func( array( __CLASS__, 'code_trick' ), $text, $markdown ); // A better parser than Markdown's for: backticks -> CODE
|
238 |
+
|
239 |
+
if ( $markdown ) { // Parse markdown.
|
240 |
+
if ( !class_exists('Parsedown', false) ) {
|
241 |
+
require_once(dirname(__FILE__) . '/Parsedown' . (version_compare(PHP_VERSION, '5.3.0', '>=') ? '' : 'Legacy') . '.php');
|
242 |
+
}
|
243 |
+
$instance = Parsedown::instance();
|
244 |
+
$text = $instance->text($text);
|
245 |
+
}
|
246 |
+
|
247 |
+
$allowed = array(
|
248 |
+
'a' => array(
|
249 |
+
'href' => array(),
|
250 |
+
'title' => array(),
|
251 |
+
'rel' => array()),
|
252 |
+
'blockquote' => array('cite' => array()),
|
253 |
+
'br' => array(),
|
254 |
+
'p' => array(),
|
255 |
+
'code' => array(),
|
256 |
+
'pre' => array(),
|
257 |
+
'em' => array(),
|
258 |
+
'strong' => array(),
|
259 |
+
'ul' => array(),
|
260 |
+
'ol' => array(),
|
261 |
+
'li' => array(),
|
262 |
+
'h3' => array(),
|
263 |
+
'h4' => array()
|
264 |
+
);
|
265 |
+
|
266 |
+
$text = balanceTags($text);
|
267 |
+
|
268 |
+
$text = wp_kses( $text, $allowed );
|
269 |
+
$text = trim($text);
|
270 |
+
return $text;
|
271 |
+
}
|
272 |
+
|
273 |
+
function code_trick( $text, $markdown ) { // Don't use bbPress native function - it's incompatible with Markdown
|
274 |
+
// If doing markdown, first take any user formatted code blocks and turn them into backticks so that
|
275 |
+
// markdown will preserve things like underscores in code blocks
|
276 |
+
if ( $markdown )
|
277 |
+
$text = preg_replace_callback("!(<pre><code>|<code>)(.*?)(</code></pre>|</code>)!s", array( __CLASS__,'decodeit'), $text);
|
278 |
+
|
279 |
+
$text = str_replace(array("\r\n", "\r"), "\n", $text);
|
280 |
+
if ( !$markdown ) {
|
281 |
+
// This gets the "inline" code blocks, but can't be used with Markdown.
|
282 |
+
$text = preg_replace_callback("|(`)(.*?)`|", array( __CLASS__, 'encodeit'), $text);
|
283 |
+
// This gets the "block level" code blocks and converts them to PRE CODE
|
284 |
+
$text = preg_replace_callback("!(^|\n)`(.*?)`!s", array( __CLASS__, 'encodeit'), $text);
|
285 |
+
} else {
|
286 |
+
// Markdown can do inline code, we convert bbPress style block level code to Markdown style
|
287 |
+
$text = preg_replace_callback("!(^|\n)([ \t]*?)`(.*?)`!s", array( __CLASS__, 'indent'), $text);
|
288 |
+
}
|
289 |
+
return $text;
|
290 |
+
}
|
291 |
+
|
292 |
+
function indent( $matches ) {
|
293 |
+
$text = $matches[3];
|
294 |
+
$text = preg_replace('|^|m', $matches[2] . ' ', $text);
|
295 |
+
return $matches[1] . $text;
|
296 |
+
}
|
297 |
+
|
298 |
+
function encodeit( $matches ) {
|
299 |
+
if ( function_exists('encodeit') ) // bbPress native
|
300 |
+
return encodeit( $matches );
|
301 |
+
|
302 |
+
$text = trim($matches[2]);
|
303 |
+
$text = htmlspecialchars($text, ENT_QUOTES);
|
304 |
+
$text = str_replace(array("\r\n", "\r"), "\n", $text);
|
305 |
+
$text = preg_replace("|\n\n\n+|", "\n\n", $text);
|
306 |
+
$text = str_replace('&lt;', '<', $text);
|
307 |
+
$text = str_replace('&gt;', '>', $text);
|
308 |
+
$text = "<code>$text</code>";
|
309 |
+
if ( "`" != $matches[1] )
|
310 |
+
$text = "<pre>$text</pre>";
|
311 |
+
return $text;
|
312 |
+
}
|
313 |
+
|
314 |
+
function decodeit( $matches ) {
|
315 |
+
if ( function_exists('decodeit') ) // bbPress native
|
316 |
+
return decodeit( $matches );
|
317 |
+
|
318 |
+
$text = $matches[2];
|
319 |
+
$trans_table = array_flip(get_html_translation_table(HTML_ENTITIES));
|
320 |
+
$text = strtr($text, $trans_table);
|
321 |
+
$text = str_replace('<br />', '', $text);
|
322 |
+
$text = str_replace('&', '&', $text);
|
323 |
+
$text = str_replace(''', "'", $text);
|
324 |
+
if ( '<pre><code>' == $matches[1] )
|
325 |
+
$text = "\n$text\n";
|
326 |
+
return "`$text`";
|
327 |
+
}
|
328 |
+
|
329 |
+
} // end class
|
330 |
+
|
331 |
+
Class Automattic_Readme extends PucReadmeParser {}
|
includes/processes/class-mailchimp-woocommerce-abstract-sync.php
ADDED
@@ -0,0 +1,323 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 7/14/16
|
9 |
+
* Time: 11:54 AM
|
10 |
+
*/
|
11 |
+
abstract class MailChimp_WooCommerce_Abtstract_Sync extends WP_Job
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* @var MailChimp_WooCommerce_Api
|
15 |
+
*/
|
16 |
+
private $api;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var MailChimpApi
|
20 |
+
*/
|
21 |
+
private $mc;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var string
|
25 |
+
*/
|
26 |
+
private $plugin_name = 'mailchimp-woocommerce';
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var string
|
30 |
+
*/
|
31 |
+
protected $store_id = '';
|
32 |
+
|
33 |
+
/**
|
34 |
+
* @return mixed
|
35 |
+
*/
|
36 |
+
abstract public function getResourceType();
|
37 |
+
|
38 |
+
/**
|
39 |
+
* @param $item
|
40 |
+
* @return mixed
|
41 |
+
*/
|
42 |
+
abstract protected function iterate($item);
|
43 |
+
|
44 |
+
/**
|
45 |
+
* @return mixed
|
46 |
+
*/
|
47 |
+
abstract protected function complete();
|
48 |
+
|
49 |
+
/**
|
50 |
+
* @return mixed
|
51 |
+
*/
|
52 |
+
public function go()
|
53 |
+
{
|
54 |
+
return $this->handle();
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* @return string
|
59 |
+
*/
|
60 |
+
public function getStoreID()
|
61 |
+
{
|
62 |
+
return md5(get_option('siteurl'));
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Task
|
67 |
+
*
|
68 |
+
* Override this method to perform any actions required on each
|
69 |
+
* queue item. Return the modified item for further processing
|
70 |
+
* in the next pass through. Or, return false to remove the
|
71 |
+
* item from the queue.
|
72 |
+
*
|
73 |
+
* @param mixed $item Queue item to iterate over
|
74 |
+
*
|
75 |
+
* @return mixed
|
76 |
+
*/
|
77 |
+
public function handle() {
|
78 |
+
|
79 |
+
if (!($page = $this->getResources()) || !($this->store_id = $this->getStoreID())) {
|
80 |
+
return false;
|
81 |
+
}
|
82 |
+
|
83 |
+
$this->setResourcePagePointer($page->page + 1, $this->getResourceType());
|
84 |
+
|
85 |
+
// if we've got a 0 count, that means we're done.
|
86 |
+
if ($page->count <= 0) {
|
87 |
+
|
88 |
+
// reset the resource page back to 1
|
89 |
+
$this->resourceComplete($this->getResourceType());
|
90 |
+
|
91 |
+
// call the completed event to process further
|
92 |
+
$this->complete();
|
93 |
+
|
94 |
+
return false;
|
95 |
+
}
|
96 |
+
|
97 |
+
// iterate through the items and send each one through the pipeline based on this class.
|
98 |
+
foreach ($page->items as $resource) {
|
99 |
+
$this->iterate($resource);
|
100 |
+
}
|
101 |
+
|
102 |
+
// this will paginate through all records for the resource type until they return no records.
|
103 |
+
wp_queue(new static());
|
104 |
+
|
105 |
+
return false;
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* @return $this
|
110 |
+
*/
|
111 |
+
public function flagStartSync()
|
112 |
+
{
|
113 |
+
mailchimp_log('sync.started', "Starting Sync :: ".date('D, M j, Y g:i A'));
|
114 |
+
|
115 |
+
// this is the last thing we're doing so it's complete as of now.
|
116 |
+
$this->setData('sync.syncing', true);
|
117 |
+
$this->setData('sync.started_at', time());
|
118 |
+
|
119 |
+
return $this;
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* @return $this
|
124 |
+
*/
|
125 |
+
public function flagStopSync()
|
126 |
+
{
|
127 |
+
// this is the last thing we're doing so it's complete as of now.
|
128 |
+
$this->setData('sync.syncing', false);
|
129 |
+
$this->setData('sync.completed_at', time());
|
130 |
+
|
131 |
+
mailchimp_log('sync.completed', "Finished Sync :: ".date('D, M j, Y g:i A'));
|
132 |
+
|
133 |
+
return $this;
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* @return bool|object|stdClass
|
138 |
+
*/
|
139 |
+
public function getResources()
|
140 |
+
{
|
141 |
+
$current_page = $this->getResourcePagePointer($this->getResourceType());
|
142 |
+
|
143 |
+
if ($current_page === 'complete') {
|
144 |
+
return false;
|
145 |
+
}
|
146 |
+
|
147 |
+
return $this->api()->paginate($this->getResourceType(), $current_page, 10);
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* @param null|string $resource
|
152 |
+
* @return $this
|
153 |
+
*/
|
154 |
+
protected function resetResourcePagePointer($resource = null)
|
155 |
+
{
|
156 |
+
if (empty($resource)) $resource = $this->getResourceType();
|
157 |
+
|
158 |
+
$this->setData('sync.'.$resource.'.current_page', 1);
|
159 |
+
|
160 |
+
return $this;
|
161 |
+
}
|
162 |
+
|
163 |
+
/**
|
164 |
+
* @param null|string $resource
|
165 |
+
* @return null
|
166 |
+
*/
|
167 |
+
protected function getResourcePagePointer($resource = null)
|
168 |
+
{
|
169 |
+
if (empty($resource)) $resource = $this->getResourceType();
|
170 |
+
|
171 |
+
return $this->getData('sync.'.$resource.'.current_page', 1);
|
172 |
+
}
|
173 |
+
|
174 |
+
/**
|
175 |
+
* @param $page
|
176 |
+
* @param null $resource
|
177 |
+
* @return MailChimp_WooCommerce_Abtstract_Sync
|
178 |
+
*/
|
179 |
+
protected function setResourcePagePointer($page, $resource = null)
|
180 |
+
{
|
181 |
+
if (empty($resource)) $resource = $this->getResourceType();
|
182 |
+
|
183 |
+
return $this->setData('sync.'.$resource.'.current_page', $page);
|
184 |
+
}
|
185 |
+
|
186 |
+
/**
|
187 |
+
* @param null|string $resource
|
188 |
+
* @return $this
|
189 |
+
*/
|
190 |
+
protected function resourceComplete($resource = null)
|
191 |
+
{
|
192 |
+
if (empty($resource)) $resource = $this->getResourceType();
|
193 |
+
|
194 |
+
$this->setData('sync.'.$resource.'.current_page', 'complete');
|
195 |
+
|
196 |
+
return $this;
|
197 |
+
}
|
198 |
+
|
199 |
+
/**
|
200 |
+
* @return null
|
201 |
+
*/
|
202 |
+
protected function setResourceCompleteTime($resource = null)
|
203 |
+
{
|
204 |
+
if (empty($resource)) $resource = $this->getResourceType();
|
205 |
+
|
206 |
+
return $this->setData('sync.'.$resource.'.completed_at', time());
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
* @param null $resource
|
211 |
+
* @return bool|DateTime
|
212 |
+
*/
|
213 |
+
protected function getResourceCompleteTime($resource = null)
|
214 |
+
{
|
215 |
+
if (empty($resource)) $resource = $this->getResourceType();
|
216 |
+
|
217 |
+
$time = $this->getData('sync.'.$resource.'.completed_at', false);
|
218 |
+
|
219 |
+
if ($time > 0) {
|
220 |
+
return new \DateTime($time);
|
221 |
+
}
|
222 |
+
|
223 |
+
return false;
|
224 |
+
}
|
225 |
+
|
226 |
+
/**
|
227 |
+
* @param $key
|
228 |
+
* @param null $default
|
229 |
+
* @return null
|
230 |
+
*/
|
231 |
+
public function getOption($key, $default = null)
|
232 |
+
{
|
233 |
+
$options = $this->getOptions();
|
234 |
+
if (isset($options[$key])) {
|
235 |
+
return $options[$key];
|
236 |
+
}
|
237 |
+
return $default;
|
238 |
+
}
|
239 |
+
|
240 |
+
/**
|
241 |
+
* @param $key
|
242 |
+
* @param $value
|
243 |
+
* @return $this
|
244 |
+
*/
|
245 |
+
public function setOption($key, $value)
|
246 |
+
{
|
247 |
+
$options = $this->getOptions();
|
248 |
+
$options[$key] = $value;
|
249 |
+
update_option($this->plugin_name, $options);
|
250 |
+
return $this;
|
251 |
+
}
|
252 |
+
|
253 |
+
/**
|
254 |
+
* @param $key
|
255 |
+
* @param bool $default
|
256 |
+
* @return bool
|
257 |
+
*/
|
258 |
+
public function hasOption($key, $default = false)
|
259 |
+
{
|
260 |
+
return (bool) $this->getOption($key, $default);
|
261 |
+
}
|
262 |
+
|
263 |
+
/**
|
264 |
+
* @return array
|
265 |
+
*/
|
266 |
+
public function getOptions()
|
267 |
+
{
|
268 |
+
$options = get_option($this->plugin_name);
|
269 |
+
return is_array($options) ? $options : array();
|
270 |
+
}
|
271 |
+
|
272 |
+
/**
|
273 |
+
* @param $key
|
274 |
+
* @param $value
|
275 |
+
* @return $this
|
276 |
+
*/
|
277 |
+
public function setData($key, $value)
|
278 |
+
{
|
279 |
+
update_option($this->plugin_name.'-'.$key, $value);
|
280 |
+
return $this;
|
281 |
+
}
|
282 |
+
|
283 |
+
/**
|
284 |
+
* @param $key
|
285 |
+
* @param null $default
|
286 |
+
* @return mixed|void
|
287 |
+
*/
|
288 |
+
public function getData($key, $default = null)
|
289 |
+
{
|
290 |
+
return get_option($this->plugin_name.'-'.$key, $default);
|
291 |
+
}
|
292 |
+
|
293 |
+
/**
|
294 |
+
* @param $key
|
295 |
+
* @return bool
|
296 |
+
*/
|
297 |
+
public function removeData($key)
|
298 |
+
{
|
299 |
+
return delete_option($this->plugin_name.'-'.$key);
|
300 |
+
}
|
301 |
+
|
302 |
+
/**
|
303 |
+
* @return MailChimp_WooCommerce_Api
|
304 |
+
*/
|
305 |
+
protected function api()
|
306 |
+
{
|
307 |
+
if (empty($this->api)) {
|
308 |
+
$this->api = new MailChimp_WooCommerce_Api();
|
309 |
+
}
|
310 |
+
return $this->api;
|
311 |
+
}
|
312 |
+
|
313 |
+
/**
|
314 |
+
* @return MailChimpApi
|
315 |
+
*/
|
316 |
+
protected function mailchimp()
|
317 |
+
{
|
318 |
+
if (empty($this->mc)) {
|
319 |
+
$this->mc = new MailChimpApi($this->getOption('mailchimp_api_key'));
|
320 |
+
}
|
321 |
+
return $this->mc;
|
322 |
+
}
|
323 |
+
}
|
includes/processes/class-mailchimp-woocommerce-cart-update.php
ADDED
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 7/15/16
|
9 |
+
* Time: 11:42 AM
|
10 |
+
*/
|
11 |
+
class MailChimp_WooCommerce_Cart_Update extends WP_Job
|
12 |
+
{
|
13 |
+
public $unique_id;
|
14 |
+
public $email;
|
15 |
+
public $previous_email;
|
16 |
+
public $campaign_id;
|
17 |
+
public $cart_data;
|
18 |
+
public $ip_address;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* MailChimp_WooCommerce_Cart_Update constructor.
|
22 |
+
* @param null $uid
|
23 |
+
* @param null $email
|
24 |
+
* @param null $campaign_id
|
25 |
+
* @param array $cart_data
|
26 |
+
*/
|
27 |
+
public function __construct($uid = null, $email = null, $campaign_id = null, array $cart_data = array())
|
28 |
+
{
|
29 |
+
if ($uid) {
|
30 |
+
$this->unique_id = $uid;
|
31 |
+
}
|
32 |
+
if ($email) {
|
33 |
+
$this->email = $email;
|
34 |
+
}
|
35 |
+
if (!empty($cart_data)) {
|
36 |
+
$this->cart_data = json_encode($cart_data);
|
37 |
+
}
|
38 |
+
|
39 |
+
if ($campaign_id) {
|
40 |
+
$this->campaign_id = $campaign_id;
|
41 |
+
}
|
42 |
+
|
43 |
+
$this->ip_address = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null;
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* @return bool
|
48 |
+
*/
|
49 |
+
public function handle()
|
50 |
+
{
|
51 |
+
if (($result = $this->process())) {
|
52 |
+
mailchimp_log('ac.success', 'Added', array('api_response' => $result->toArray()));
|
53 |
+
}
|
54 |
+
|
55 |
+
return false;
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* @return bool|MailChimp_Cart
|
60 |
+
*/
|
61 |
+
public function process()
|
62 |
+
{
|
63 |
+
try {
|
64 |
+
$options = get_option('mailchimp-woocommerce', array());
|
65 |
+
$store_id = mailchimp_get_store_id();
|
66 |
+
|
67 |
+
if (!empty($store_id) && is_array($options) && isset($options['mailchimp_api_key'])) {
|
68 |
+
|
69 |
+
$this->cart_data = json_decode($this->cart_data, true);
|
70 |
+
|
71 |
+
$api = new MailChimpApi($options['mailchimp_api_key']);
|
72 |
+
|
73 |
+
// delete it and the add it back.
|
74 |
+
$api->deleteCartByID($store_id, $this->unique_id);
|
75 |
+
|
76 |
+
// if they emptied the cart ignore it.
|
77 |
+
if (!is_array($this->cart_data) || empty($this->cart_data)) {
|
78 |
+
return false;
|
79 |
+
}
|
80 |
+
|
81 |
+
$customer = new MailChimp_Customer();
|
82 |
+
$customer->setId($this->unique_id);
|
83 |
+
$customer->setEmailAddress($this->email);
|
84 |
+
$customer->setOptInStatus(false);
|
85 |
+
|
86 |
+
$cart = new MailChimp_Cart();
|
87 |
+
$cart->setId($this->unique_id);
|
88 |
+
$cart->setCampaignID($this->campaign_id);
|
89 |
+
$cart->setCheckoutUrl(wc_get_checkout_url());
|
90 |
+
$cart->setCurrencyCode(isset($options['store_currency_code']) ? $options['store_currency_code'] : 'USD');
|
91 |
+
|
92 |
+
$cart->setCustomer($customer);
|
93 |
+
|
94 |
+
$order_total = 0;
|
95 |
+
$products = array();
|
96 |
+
|
97 |
+
foreach ($this->cart_data as $hash => $item) {
|
98 |
+
try {
|
99 |
+
$line = $this->transformLineItem($hash, $item);
|
100 |
+
$cart->addItem($line);
|
101 |
+
$order_total += ($item['quantity'] * $line->getPrice());
|
102 |
+
$products[] = $line;
|
103 |
+
} catch (\Exception $e) {}
|
104 |
+
}
|
105 |
+
|
106 |
+
if (empty($products)) {
|
107 |
+
return false;
|
108 |
+
}
|
109 |
+
|
110 |
+
$cart->setOrderTotal($order_total);
|
111 |
+
|
112 |
+
try {
|
113 |
+
mailchimp_log('abandoned_cart.submitting', "email: {$customer->getEmailAddress()}");
|
114 |
+
|
115 |
+
// if the post is successful we're all good.
|
116 |
+
$api->addCart($store_id, $cart, false);
|
117 |
+
|
118 |
+
mailchimp_log('abandoned_cart.success', "email: {$customer->getEmailAddress()}");
|
119 |
+
|
120 |
+
} catch (\Exception $e) {
|
121 |
+
|
122 |
+
mailchimp_log('abandoned_cart.error', "email: {$customer->getEmailAddress()}");
|
123 |
+
|
124 |
+
// if we have an error it's most likely due to a product not being found.
|
125 |
+
// let's loop through each item, verify that we have the product or not.
|
126 |
+
// if not, we will add it.
|
127 |
+
foreach ($products as $item) {
|
128 |
+
/** @var MailChimp_LineItem $item */
|
129 |
+
$transformer = new MailChimp_WooCommerce_Single_Product($item->getProductID());
|
130 |
+
if (!$transformer->api()->getStoreProduct($store_id, $item->getProductId())) {
|
131 |
+
$transformer->handle();
|
132 |
+
}
|
133 |
+
}
|
134 |
+
|
135 |
+
mailchimp_log('abandoned_cart.submitting', "email: {$customer->getEmailAddress()}");
|
136 |
+
|
137 |
+
// if the post is successful we're all good.
|
138 |
+
$api->addCart($store_id, $cart, false);
|
139 |
+
|
140 |
+
mailchimp_log('abandoned_cart.success', "email: {$customer->getEmailAddress()}");
|
141 |
+
}
|
142 |
+
}
|
143 |
+
|
144 |
+
} catch (\Exception $e) {
|
145 |
+
update_option('mailchimp-woocommerce-cart-error', $e->getMessage());
|
146 |
+
mailchimp_log('abandoned_cart.error', "{$e->getMessage()} on {$e->getLine()} in {$e->getFile()}");
|
147 |
+
}
|
148 |
+
|
149 |
+
return false;
|
150 |
+
}
|
151 |
+
|
152 |
+
/**
|
153 |
+
* @param string $hash
|
154 |
+
* @param $item
|
155 |
+
* @return MailChimp_LineItem
|
156 |
+
*/
|
157 |
+
protected function transformLineItem($hash, $item)
|
158 |
+
{
|
159 |
+
$product = new WC_Product($item['product_id']);
|
160 |
+
|
161 |
+
$line = new MailChimp_LineItem();
|
162 |
+
$line->setId($hash);
|
163 |
+
$line->setProductId($item['product_id']);
|
164 |
+
|
165 |
+
if (isset($item['variation_id']) && $item['variation_id'] > 0) {
|
166 |
+
$line->setProductVariantId($item['variation_id']);
|
167 |
+
} else {
|
168 |
+
$line->setProductVariantId($item['product_id']);
|
169 |
+
}
|
170 |
+
|
171 |
+
$line->setQuantity($item['quantity']);
|
172 |
+
$line->setPrice($product->get_price());
|
173 |
+
|
174 |
+
return $line;
|
175 |
+
}
|
176 |
+
}
|
includes/processes/class-mailchimp-woocommerce-process-orders.php
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 7/14/16
|
9 |
+
* Time: 10:57 AM
|
10 |
+
*/
|
11 |
+
class MailChimp_WooCommerce_Process_Orders extends MailChimp_WooCommerce_Abtstract_Sync
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* @var string
|
15 |
+
*/
|
16 |
+
protected $action = 'mailchimp_woocommerce_process_orders';
|
17 |
+
public $items = array();
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @return string
|
21 |
+
*/
|
22 |
+
public function getResourceType()
|
23 |
+
{
|
24 |
+
return 'orders';
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* @param MailChimp_Order $item
|
29 |
+
*
|
30 |
+
* @return mixed
|
31 |
+
*/
|
32 |
+
protected function iterate($item)
|
33 |
+
{
|
34 |
+
if ($item instanceof MailChimp_Order) {
|
35 |
+
|
36 |
+
// since we're syncing the customer for the first time, this is where we need to add the override
|
37 |
+
// for subscriber status. We don't get the checkbox until this plugin is actually installed and working!
|
38 |
+
|
39 |
+
if ((bool) $this->getOption('mailchimp_auto_subscribe', true)) {
|
40 |
+
$item->getCustomer()->setOptInStatus(true);
|
41 |
+
}
|
42 |
+
|
43 |
+
$type = $this->mailchimp()->getStoreOrder($this->store_id, $item->getId()) ? 'update' : 'create';
|
44 |
+
$call = $type === 'create' ? 'addStoreOrder' : 'updateStoreOrder';
|
45 |
+
|
46 |
+
try {
|
47 |
+
|
48 |
+
$log = "$call :: #{$item->getId()} :: email: {$item->getCustomer()->getEmailAddress()}";
|
49 |
+
|
50 |
+
mailchimp_log('sync.orders.submitting', $log);
|
51 |
+
|
52 |
+
// make the call
|
53 |
+
$response = $this->mailchimp()->$call($this->store_id, $item, false);
|
54 |
+
|
55 |
+
mailchimp_log('sync.orders.success', $log);
|
56 |
+
|
57 |
+
$this->items[] = array('response' => $response, 'item' => $item);
|
58 |
+
|
59 |
+
return $response;
|
60 |
+
|
61 |
+
} catch (MailChimp_Error $e) {
|
62 |
+
mailchimp_log('sync.orders.error', "$call :: MailChimp_Error :: {$e->getMessage()}");
|
63 |
+
} catch (MailChimp_ServerError $e) {
|
64 |
+
mailchimp_log('sync.orders.error', "$call :: MailChimp_ServerError :: {$e->getMessage()}");
|
65 |
+
} catch (Exception $e) {
|
66 |
+
mailchimp_log('sync.orders.error', "$call :: Uncaught Exception :: {$e->getMessage()}");
|
67 |
+
}
|
68 |
+
}
|
69 |
+
|
70 |
+
return false;
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* After the resources have been loaded and pushed
|
75 |
+
*/
|
76 |
+
protected function complete()
|
77 |
+
{
|
78 |
+
mailchimp_log('sync.orders.completed', 'Done with the order sync.');
|
79 |
+
|
80 |
+
// add a timestamp for the orders sync completion
|
81 |
+
$this->setResourceCompleteTime();
|
82 |
+
|
83 |
+
// this is the last thing we're doing so it's complete as of now.
|
84 |
+
$this->flagStopSync();
|
85 |
+
}
|
86 |
+
}
|
includes/processes/class-mailchimp-woocommerce-process-products.php
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 7/14/16
|
9 |
+
* Time: 10:57 AM
|
10 |
+
*/
|
11 |
+
class MailChimp_WooCommerce_Process_Products extends MailChimp_WooCommerce_Abtstract_Sync
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* @var string
|
15 |
+
*/
|
16 |
+
protected $action = 'mailchimp_woocommerce_process_products';
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @return string
|
20 |
+
*/
|
21 |
+
public function getResourceType()
|
22 |
+
{
|
23 |
+
return 'products';
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @param MailChimp_Product $item
|
28 |
+
*
|
29 |
+
* @return mixed
|
30 |
+
*/
|
31 |
+
protected function iterate($item) {
|
32 |
+
|
33 |
+
if ($item instanceof MailChimp_Product) {
|
34 |
+
|
35 |
+
// need to run the delete option on this before submitting because the API does not support PATCH yet.
|
36 |
+
$this->mailchimp()->deleteStoreProduct($this->store_id, $item->getId());
|
37 |
+
|
38 |
+
// add the product.
|
39 |
+
try {
|
40 |
+
mailchimp_log('sync.products.submitting', "addStoreProduct :: #{$item->getId()}");
|
41 |
+
|
42 |
+
// make the call
|
43 |
+
$response = $this->mailchimp()->addStoreProduct($this->store_id, $item);
|
44 |
+
|
45 |
+
mailchimp_log('sync.products.success', "addStoreProduct :: #{$item->getId()}");
|
46 |
+
|
47 |
+
return $response;
|
48 |
+
|
49 |
+
} catch (MailChimp_Error $e) {
|
50 |
+
mailchimp_log('sync.products.error', "addStoreProduct :: MailChimp_Error :: {$e->getMessage()}");
|
51 |
+
} catch (MailChimp_ServerError $e) {
|
52 |
+
mailchimp_log('sync.products.error', "addStoreProduct :: MailChimp_ServerError :: {$e->getMessage()}");
|
53 |
+
} catch (Exception $e) {
|
54 |
+
mailchimp_log('sync.products.error', "addStoreProduct :: Uncaught Exception :: {$e->getMessage()}");
|
55 |
+
}
|
56 |
+
}
|
57 |
+
|
58 |
+
return false;
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Called after all the products have been iterated and processed into MailChimp
|
63 |
+
*/
|
64 |
+
protected function complete()
|
65 |
+
{
|
66 |
+
mailchimp_log('sync.products.completed', 'Done with the product sync :: queuing up the orders next!');
|
67 |
+
|
68 |
+
// add a timestamp for the product sync completion
|
69 |
+
$this->setResourceCompleteTime();
|
70 |
+
|
71 |
+
// since the products are all good, let's sync up the orders now.
|
72 |
+
wp_queue(new MailChimp_WooCommerce_Process_Orders());
|
73 |
+
}
|
74 |
+
}
|
includes/processes/class-mailchimp-woocommerce-single-order.php
ADDED
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 7/15/16
|
9 |
+
* Time: 11:42 AM
|
10 |
+
*/
|
11 |
+
class MailChimp_WooCommerce_Single_Order extends WP_Job
|
12 |
+
{
|
13 |
+
public $order_id;
|
14 |
+
public $cart_session_id;
|
15 |
+
public $campaign_id;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* MailChimp_WooCommerce_Single_Order constructor.
|
19 |
+
* @param null $order_id
|
20 |
+
* @param null $cart_session_id
|
21 |
+
* @param null $campaign_id
|
22 |
+
*/
|
23 |
+
public function __construct($order_id = null, $cart_session_id = null, $campaign_id = null)
|
24 |
+
{
|
25 |
+
if (!empty($order_id)) {
|
26 |
+
$this->order_id = $order_id;
|
27 |
+
}
|
28 |
+
if (!empty($cart_session_id)) {
|
29 |
+
$this->cart_session_id = $cart_session_id;
|
30 |
+
}
|
31 |
+
if (!empty($campaign_id)) {
|
32 |
+
$this->campaign_id = $campaign_id;
|
33 |
+
}
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* @return bool
|
38 |
+
*/
|
39 |
+
public function handle()
|
40 |
+
{
|
41 |
+
$this->process();
|
42 |
+
|
43 |
+
return false;
|
44 |
+
}
|
45 |
+
|
46 |
+
public function process()
|
47 |
+
{
|
48 |
+
$options = get_option('mailchimp-woocommerce', array());
|
49 |
+
$store_id = mailchimp_get_store_id();
|
50 |
+
|
51 |
+
// only if we have the right parameters to do the work
|
52 |
+
if (!empty($store_id) && is_array($options) && isset($options['mailchimp_api_key'])) {
|
53 |
+
|
54 |
+
$job = new MailChimp_WooCommerce_Transform_Orders();
|
55 |
+
$api = new MailChimpApi($options['mailchimp_api_key']);
|
56 |
+
|
57 |
+
// set the campaign ID
|
58 |
+
$job->campaign_id = $this->campaign_id;
|
59 |
+
|
60 |
+
$call = $api->getStoreOrder($store_id, $this->order_id) ? 'updateStoreOrder' : 'addStoreOrder';
|
61 |
+
|
62 |
+
// if we already pushed this order into the system, we need to unset it now just in case there
|
63 |
+
// was another campaign that had been sent and this was only an order update.
|
64 |
+
if ($call === 'updateStoreOrder') {
|
65 |
+
$job->campaign_id = null;
|
66 |
+
}
|
67 |
+
|
68 |
+
// will either add or update the order
|
69 |
+
try {
|
70 |
+
|
71 |
+
// transform the order
|
72 |
+
$order = $job->transform(get_post($this->order_id));
|
73 |
+
|
74 |
+
// will be the same as the customer id. an md5'd hash of a lowercased email.
|
75 |
+
$this->cart_session_id = $order->getCustomer()->getId();
|
76 |
+
|
77 |
+
$log = "$call :: #{$order->getId()} :: email: {$order->getCustomer()->getEmailAddress()}";
|
78 |
+
|
79 |
+
mailchimp_log('order_submit.submitting', $log);
|
80 |
+
|
81 |
+
// update or create
|
82 |
+
$api_response = $api->$call($store_id, $order, false);
|
83 |
+
|
84 |
+
if (!empty($job->campaign_id)) {
|
85 |
+
$log .= ' :: campaign id '.$job->campaign_id;
|
86 |
+
}
|
87 |
+
|
88 |
+
mailchimp_log('order_submit.success', $log);
|
89 |
+
|
90 |
+
// if we're adding a new order and the session id is here, we need to delete the AC cart record.
|
91 |
+
if (!empty($this->cart_session_id)) {
|
92 |
+
$api->deleteCartByID($store_id, $this->cart_session_id);
|
93 |
+
}
|
94 |
+
|
95 |
+
return $api_response;
|
96 |
+
|
97 |
+
} catch (\Exception $e) {
|
98 |
+
|
99 |
+
mailchimp_log('order_submit.tracing_error', $message = strtolower($e->getMessage()));
|
100 |
+
|
101 |
+
if (!isset($order)) {
|
102 |
+
// transform the order
|
103 |
+
$order = $job->transform(get_post($this->order_id));
|
104 |
+
$this->cart_session_id = $order->getCustomer()->getId();
|
105 |
+
}
|
106 |
+
|
107 |
+
// this can happen when a customer changes their email.
|
108 |
+
if (isset($order) && strpos($message, 'not be changed')) {
|
109 |
+
|
110 |
+
try {
|
111 |
+
|
112 |
+
mailchimp_log('order_submit.deleting_customer', "#{$order->getId()} :: email: {$order->getCustomer()->getEmailAddress()}");
|
113 |
+
|
114 |
+
// delete the customer before adding it again.
|
115 |
+
$api->deleteCustomer($store_id, $order->getCustomer()->getId());
|
116 |
+
|
117 |
+
// update or create
|
118 |
+
$api_response = $api->$call($store_id, $order, false);
|
119 |
+
|
120 |
+
$log = "Deleted Customer :: $call :: #{$order->getId()} :: email: {$order->getCustomer()->getEmailAddress()}";
|
121 |
+
|
122 |
+
if (!empty($job->campaign_id)) {
|
123 |
+
$log .= ' :: campaign id '.$job->campaign_id;
|
124 |
+
}
|
125 |
+
|
126 |
+
mailchimp_log('order_submit.success', $log);
|
127 |
+
|
128 |
+
// if we're adding a new order and the session id is here, we need to delete the AC cart record.
|
129 |
+
if (!empty($this->cart_session_id)) {
|
130 |
+
$api->deleteCartByID($store_id, $this->cart_session_id);
|
131 |
+
}
|
132 |
+
|
133 |
+
return $api_response;
|
134 |
+
|
135 |
+
} catch (\Exception $e) {
|
136 |
+
mailchimp_log('order_submit.error', 'deleting-customer-re-add :: #'.$this->order_id.' :: '.$e->getMessage());
|
137 |
+
}
|
138 |
+
}
|
139 |
+
}
|
140 |
+
}
|
141 |
+
|
142 |
+
return false;
|
143 |
+
}
|
144 |
+
}
|
includes/processes/class-mailchimp-woocommerce-single-product.php
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Created by Vextras.
|
5 |
+
*
|
6 |
+
* Name: Ryan Hungate
|
7 |
+
* Email: ryan@mailchimp.com
|
8 |
+
* Date: 7/15/16
|
9 |
+
* Time: 11:42 AM
|
10 |
+
*/
|
11 |
+
class MailChimp_WooCommerce_Single_Product extends WP_Job
|
12 |
+
{
|
13 |
+
public $product_id;
|
14 |
+
protected $store_id;
|
15 |
+
protected $api;
|
16 |
+
protected $service;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* MailChimp_WooCommerce_Single_Order constructor.
|
20 |
+
* @param null|int $product_id
|
21 |
+
*/
|
22 |
+
public function __construct($product_id = null)
|
23 |
+
{
|
24 |
+
if (!empty($product_id)) {
|
25 |
+
$this->product_id = $product_id instanceof WP_Post ? $product_id->ID : $product_id;
|
26 |
+
}
|
27 |
+
}
|
28 |
+
|
29 |
+
/**
|
30 |
+
* @return bool
|
31 |
+
*/
|
32 |
+
public function handle()
|
33 |
+
{
|
34 |
+
$this->process();
|
35 |
+
|
36 |
+
return false;
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* @return MailChimp_Product
|
41 |
+
* @throws Exception
|
42 |
+
*/
|
43 |
+
public function process()
|
44 |
+
{
|
45 |
+
if ($this->api()->getStoreProduct($this->store_id, $this->product_id)) {
|
46 |
+
$this->api()->deleteStoreProduct($this->store_id, $this->product_id);
|
47 |
+
}
|
48 |
+
|
49 |
+
try {
|
50 |
+
|
51 |
+
$product = $this->transformer()->transform(get_post($this->product_id));
|
52 |
+
|
53 |
+
mailchimp_log('product_submit.submitting', "addStoreProduct :: #{$product->getId()}");
|
54 |
+
|
55 |
+
$this->api()->addStoreProduct($this->store_id, $product, false);
|
56 |
+
|
57 |
+
mailchimp_log('product_submit.success', "addStoreProduct :: #{$product->getId()}");
|
58 |
+
|
59 |
+
update_option('mailchimp-woocommerce-last_product_updated', $product->getId());
|
60 |
+
|
61 |
+
return $product;
|
62 |
+
|
63 |
+
} catch (MailChimp_Error $e) {
|
64 |
+
mailchimp_log('product_submit.error', "addStoreProduct :: MailChimp_Error :: {$e->getMessage()}");
|
65 |
+
} catch (MailChimp_ServerError $e) {
|
66 |
+
mailchimp_log('product_submit.error', "addStoreProduct :: MailChimp_ServerError :: {$e->getMessage()}");
|
67 |
+
} catch (Exception $e) {
|
68 |
+
mailchimp_log('product_submit.error', "addStoreProduct :: Uncaught Exception :: {$e->getMessage()}");
|
69 |
+
}
|
70 |
+
|
71 |
+
return false;
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* @return MailChimpApi
|
76 |
+
*/
|
77 |
+
public function api()
|
78 |
+
{
|
79 |
+
if (is_null($this->api)) {
|
80 |
+
|
81 |
+
$this->store_id = mailchimp_get_store_id();
|
82 |
+
$options = get_option('mailchimp-woocommerce', array());
|
83 |
+
|
84 |
+
if (!empty($this->store_id) && is_array($options) && isset($options['mailchimp_api_key'])) {
|
85 |
+
return $this->api = new MailChimpApi($options['mailchimp_api_key']);
|
86 |
+
}
|
87 |
+
|
88 |
+
throw new \RuntimeException('The MailChimp API is not currently configured!');
|
89 |
+
}
|
90 |
+
|
91 |
+
return $this->api;
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* @return MailChimp_WooCommerce_Transform_Products
|
96 |
+
*/
|
97 |
+
public function transformer()
|
98 |
+
{
|
99 |
+
if (is_null($this->service)) {
|
100 |
+
return $this->service = new MailChimp_WooCommerce_Transform_Products();
|
101 |
+
}
|
102 |
+
|
103 |
+
return $this->service;
|
104 |
+
}
|
105 |
+
}
|
includes/slack/Contracts/Http/Interactor.php
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php namespace Frlnc\Slack\Contracts\Http;
|
2 |
+
|
3 |
+
interface Interactor {
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Send a get request to a URL.
|
7 |
+
*
|
8 |
+
* @param string $url
|
9 |
+
* @param array $parameters
|
10 |
+
* @param array $headers
|
11 |
+
* @return \Frlnc\Slack\Contracts\Http\Response
|
12 |
+
*/
|
13 |
+
public function get($url, array $parameters = [], array $headers = []);
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Send a post request to a URL.
|
17 |
+
*
|
18 |
+
* @param string $url
|
19 |
+
* @param array $urlParameters
|
20 |
+
* @param array $postParameters
|
21 |
+
* @param array $headers
|
22 |
+
* @return \Frlnc\Slack\Contracts\Http\Response
|
23 |
+
*/
|
24 |
+
public function post($url, array $urlParameters = [], array $postParameters = [], array $headers = []);
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Sets the response factory to use.
|
28 |
+
*
|
29 |
+
* @param \Frlnc\Slack\Contracts\Http\ResponseFactory $factory
|
30 |
+
* @return void
|
31 |
+
*/
|
32 |
+
public function setResponseFactory(ResponseFactory $factory);
|
33 |
+
|
34 |
+
}
|
includes/slack/Contracts/Http/Response.php
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php namespace Frlnc\Slack\Contracts\Http;
|
2 |
+
|
3 |
+
interface Response {
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Gets the body of the response.
|
7 |
+
*
|
8 |
+
* @return string
|
9 |
+
*/
|
10 |
+
public function getBody();
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Gets the headers of the response.
|
14 |
+
*
|
15 |
+
* @return array
|
16 |
+
*/
|
17 |
+
public function getHeaders();
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Gets the status code of the response.
|
21 |
+
*
|
22 |
+
* @return integer
|
23 |
+
*/
|
24 |
+
public function getStatusCode();
|
25 |
+
|
26 |
+
}
|
includes/slack/Contracts/Http/ResponseFactory.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php namespace Frlnc\Slack\Contracts\Http;
|
2 |
+
|
3 |
+
interface ResponseFactory {
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Builds the response.
|
7 |
+
*
|
8 |
+
* @param string $body
|
9 |
+
* @param array $headers
|
10 |
+
* @param integer $statusCode
|
11 |
+
* @return \Frlnc\Slack\Contracts\Http\Response
|
12 |
+
*/
|
13 |
+
public function build($body, array $headers, $statusCode);
|
14 |
+
|
15 |
+
}
|
includes/slack/Core/Commander.php
ADDED
@@ -0,0 +1,508 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php namespace Frlnc\Slack\Core;
|
2 |
+
|
3 |
+
use InvalidArgumentException;
|
4 |
+
use Frlnc\Slack\Contracts\Http\Interactor;
|
5 |
+
|
6 |
+
class Commander {
|
7 |
+
|
8 |
+
/**
|
9 |
+
* The default command headers.
|
10 |
+
*
|
11 |
+
* @var array
|
12 |
+
*/
|
13 |
+
protected static $defaultHeaders = [];
|
14 |
+
|
15 |
+
/**
|
16 |
+
* The commands.
|
17 |
+
*
|
18 |
+
* @var array
|
19 |
+
*/
|
20 |
+
protected static $commands = [
|
21 |
+
'api.test' => [
|
22 |
+
'endpoint' => '/api.test',
|
23 |
+
'token' => false
|
24 |
+
],
|
25 |
+
'auth.test' => [
|
26 |
+
'endpoint' => '/auth.test',
|
27 |
+
'token' => true
|
28 |
+
],
|
29 |
+
'channels.archive' => [
|
30 |
+
'token' => true,
|
31 |
+
'endpoint' => '/channels.archive'
|
32 |
+
],
|
33 |
+
'channels.create' => [
|
34 |
+
'token' => true,
|
35 |
+
'endpoint' => '/channels.create'
|
36 |
+
],
|
37 |
+
'channels.history' => [
|
38 |
+
'token' => true,
|
39 |
+
'endpoint' => '/channels.history'
|
40 |
+
],
|
41 |
+
'channels.info' => [
|
42 |
+
'token' => true,
|
43 |
+
'endpoint' => '/channels.info'
|
44 |
+
],
|
45 |
+
'channels.invite' => [
|
46 |
+
'token' => true,
|
47 |
+
'endpoint' => '/channels.invite'
|
48 |
+
],
|
49 |
+
'channels.join' => [
|
50 |
+
'token' => true,
|
51 |
+
'endpoint' => '/channels.join'
|
52 |
+
],
|
53 |
+
'channels.kick' => [
|
54 |
+
'token' => true,
|
55 |
+
'endpoint' => '/channels.kick'
|
56 |
+
],
|
57 |
+
'channels.leave' => [
|
58 |
+
'token' => true,
|
59 |
+
'endpoint' => '/channels.leave'
|
60 |
+
],
|
61 |
+
'channels.list' => [
|
62 |
+
'token' => true,
|
63 |
+
'endpoint' => '/channels.list'
|
64 |
+
],
|
65 |
+
'channels.mark' => [
|
66 |
+
'token' => true,
|
67 |
+
'endpoint' => '/channels.mark'
|
68 |
+
],
|
69 |
+
'channels.rename' => [
|
70 |
+
'token' => true,
|
71 |
+
'endpoint' => '/channels.rename'
|
72 |
+
],
|
73 |
+
'channels.setPurpose' => [
|
74 |
+
'token' => true,
|
75 |
+
'endpoint' => '/channels.setPurpose',
|
76 |
+
'format' => [
|
77 |
+
'purpose'
|
78 |
+
]
|
79 |
+
],
|
80 |
+
'channels.setTopic' => [
|
81 |
+
'token' => true,
|
82 |
+
'endpoint' => '/channels.setTopic',
|
83 |
+
'format' => [
|
84 |
+
'topic'
|
85 |
+
]
|
86 |
+
],
|
87 |
+
'channels.unarchive' => [
|
88 |
+
'token' => true,
|
89 |
+
'endpoint' => '/channels.unarchive'
|
90 |
+
],
|
91 |
+
'chat.delete' => [
|
92 |
+
'token' => true,
|
93 |
+
'endpoint' => '/chat.delete'
|
94 |
+
],
|
95 |
+
'chat.postMessage' => [
|
96 |
+
'token' => true,
|
97 |
+
'endpoint' => '/chat.postMessage',
|
98 |
+
'format' => [
|
99 |
+
'text',
|
100 |
+
'username'
|
101 |
+
]
|
102 |
+
],
|
103 |
+
'chat.update' => [
|
104 |
+
'token' => true,
|
105 |
+
'endpoint' => '/chat.update',
|
106 |
+
'format' => [
|
107 |
+
'text'
|
108 |
+
]
|
109 |
+
],
|
110 |
+
'dnd.endDnd' => [
|
111 |
+
'token' => true,
|
112 |
+
'endpoint' => '/dnd.endDnd'
|
113 |
+
],
|
114 |
+
'dnd.endSnooze' => [
|
115 |
+
'token' => true,
|
116 |
+
'endpoint' => '/dnd.endSnooze'
|
117 |
+
],
|
118 |
+
'dnd.info' => [
|
119 |
+
'token' => true,
|
120 |
+
'endpoint' => '/dnd.info'
|
121 |
+
],
|
122 |
+
'dnd.setSnooze' => [
|
123 |
+
'token' => true,
|
124 |
+
'endpoint' => '/dnd.setSnooze'
|
125 |
+
],
|
126 |
+
'dnd.teamInfo' => [
|
127 |
+
'token' => true,
|
128 |
+
'endpoint' => '/dnd.teamInfo'
|
129 |
+
],
|
130 |
+
'emoji.list' => [
|
131 |
+
'token' => true,
|
132 |
+
'endpoint' => '/emoji.list'
|
133 |
+
],
|
134 |
+
'files.comments.add' => [
|
135 |
+
'token' => true,
|
136 |
+
'endpoint' => '/files.comments.add'
|
137 |
+
],
|
138 |
+
'files.comments.delete' => [
|
139 |
+
'token' => true,
|
140 |
+
'endpoint' => '/files.comments.delete'
|
141 |
+
],
|
142 |
+
'files.comments.edit' => [
|
143 |
+
'token' => true,
|
144 |
+
'endpoint' => '/files.comments.edit'
|
145 |
+
],
|
146 |
+
'files.delete' => [
|
147 |
+
'token' => true,
|
148 |
+
'endpoint' => '/files.delete'
|
149 |
+
],
|
150 |
+
'files.info' => [
|
151 |
+
'token' => true,
|
152 |
+
'endpoint' => '/files.info'
|
153 |
+
],
|
154 |
+
'files.list' => [
|
155 |
+
'token' => true,
|
156 |
+
'endpoint' => '/files.list'
|
157 |
+
],
|
158 |
+
'files.revokePublicURL' => [
|
159 |
+
'token' => true,
|
160 |
+
'endpoint' => '/files.revokePublicURL'
|
161 |
+
],
|
162 |
+
'files.sharedPublcURL' => [
|
163 |
+
'token' => true,
|
164 |
+
'endpoint' => '/files.sharedPublcURL'
|
165 |
+
],
|
166 |
+
'files.upload' => [
|
167 |
+
'token' => true,
|
168 |
+
'endpoint' => '/files.upload',
|
169 |
+
'post' => true,
|
170 |
+
'headers' => [
|
171 |
+
'Content-Type' => 'multipart/form-data'
|
172 |
+
],
|
173 |
+
'format' => [
|
174 |
+
'filename',
|
175 |
+
'title',
|
176 |
+
'initial_comment'
|
177 |
+
]
|
178 |
+
],
|
179 |
+
'groups.archive' => [
|
180 |
+
'token' => true,
|
181 |
+
'endpoint' => '/groups.archive'
|
182 |
+
],
|
183 |
+
'groups.close' => [
|
184 |
+
'token' => true,
|
185 |
+
'endpoint' => '/groups.close'
|
186 |
+
],
|
187 |
+
'groups.create' => [
|
188 |
+
'token' => true,
|
189 |
+
'endpoint' => '/groups.create',
|
190 |
+
'format' => [
|
191 |
+
'name'
|
192 |
+
]
|
193 |
+
],
|
194 |
+
'groups.createChild' => [
|
195 |
+
'token' => true,
|
196 |
+
'endpoint' => '/groups.createChild'
|
197 |
+
],
|
198 |
+
'groups.history' => [
|
199 |
+
'token' => true,
|
200 |
+
'endpoint' => '/groups.history'
|
201 |
+
],
|
202 |
+
'groups.info' => [
|
203 |
+
'token' => true,
|
204 |
+
'endpoint' => '/groups.info'
|
205 |
+
],
|
206 |
+
'groups.invite' => [
|
207 |
+
'token' => true,
|
208 |
+
'endpoint' => '/groups.invite'
|
209 |
+
],
|
210 |
+
'groups.kick' => [
|
211 |
+
'token' => true,
|
212 |
+
'endpoint' => '/groups.kick'
|
213 |
+
],
|
214 |
+
'groups.leave' => [
|
215 |
+
'token' => true,
|
216 |
+
'endpoint' => '/groups.leave'
|
217 |
+
],
|
218 |
+
'groups.list' => [
|
219 |
+
'token' => true,
|
220 |
+
'endpoint' => '/groups.list'
|
221 |
+
],
|
222 |
+
'groups.mark' => [
|
223 |
+
'token' => true,
|
224 |
+
'endpoint' => '/groups.mark'
|
225 |
+
],
|
226 |
+
'groups.open' => [
|
227 |
+
'token' => true,
|
228 |
+
'endpoint' => '/groups.open'
|
229 |
+
],
|
230 |
+
'groups.rename' => [
|
231 |
+
'token' => true,
|
232 |
+
'endpoint' => '/groups.rename'
|
233 |
+
],
|
234 |
+
'groups.setPurpose' => [
|
235 |
+
'token' => true,
|
236 |
+
'endpoint' => '/groups.setPurpose',
|
237 |
+
'format' => [
|
238 |
+
'purpose'
|
239 |
+
]
|
240 |
+
],
|
241 |
+
'groups.setTopic' => [
|
242 |
+
'token' => true,
|
243 |
+
'endpoint' => '/groups.setTopic',
|
244 |
+
'format' => [
|
245 |
+
'topic'
|
246 |
+
]
|
247 |
+
],
|
248 |
+
'groups.unarchive' => [
|
249 |
+
'token' => true,
|
250 |
+
'endpoint' => '/groups.unarchive'
|
251 |
+
],
|
252 |
+
'im.close' => [
|
253 |
+
'token' => true,
|
254 |
+
'endpoint' => '/im.close'
|
255 |
+
],
|
256 |
+
'im.history' => [
|
257 |
+
'token' => true,
|
258 |
+
'endpoint' => '/im.history'
|
259 |
+
],
|
260 |
+
'im.list' => [
|
261 |
+
'token' => true,
|
262 |
+
'endpoint' => '/im.list'
|
263 |
+
],
|
264 |
+
'im.mark' => [
|
265 |
+
'token' => true,
|
266 |
+
'endpoint' => '/im.mark'
|
267 |
+
],
|
268 |
+
'im.open' => [
|
269 |
+
'token' => true,
|
270 |
+
'endpoint' => '/im.open'
|
271 |
+
],
|
272 |
+
'mpim.close' => [
|
273 |
+
'token' => true,
|
274 |
+
'endpoint' => '/mpim.close'
|
275 |
+
],
|
276 |
+
'mpmim.history' => [
|
277 |
+
'token' => true,
|
278 |
+
'endpoint' => '/mpmim.history'
|
279 |
+
],
|
280 |
+
'mpim.list' => [
|
281 |
+
'token' => true,
|
282 |
+
'endpoint' => '/mpim.list'
|
283 |
+
],
|
284 |
+
'mpim.mark' => [
|
285 |
+
'token' => true,
|
286 |
+
'endpoint' => '/mpim.mark'
|
287 |
+
],
|
288 |
+
'mpim.open' => [
|
289 |
+
'token' => true,
|
290 |
+
'endpoint' => '/mpim.open'
|
291 |
+
],
|
292 |
+
'oauth.access' => [
|
293 |
+
'token' => false,
|
294 |
+
'endpoint' => '/oauth.access'
|
295 |
+
],
|
296 |
+
'pins.add' => [
|
297 |
+
'token' => true,
|
298 |
+
'endpoint' => '/pins.add'
|
299 |
+
],
|
300 |
+
'pins.list' => [
|
301 |
+
'token' => true,
|
302 |
+
'endpoint' => '/pins.list'
|
303 |
+
],
|
304 |
+
'pins.remove' => [
|
305 |
+
'token' => true,
|
306 |
+
'endpoint' => '/pins.remove'
|
307 |
+
],
|
308 |
+
'reactions.add' => [
|
309 |
+
'token' => true,
|
310 |
+
'endpoint' => '/reactions.add'
|
311 |
+
],
|
312 |
+
'reactions.get' => [
|
313 |
+
'token' => true,
|
314 |
+
'endpoint' => '/reactions.get'
|
315 |
+
],
|
316 |
+
'reactions.list' => [
|
317 |
+
'token' => true,
|
318 |
+
'endpoint' => '/reactions.list'
|
319 |
+
],
|
320 |
+
'reactions.remove' => [
|
321 |
+
'token' => true,
|
322 |
+
'endpoint' => '/reactions.remove'
|
323 |
+
],
|
324 |
+
'rtm.start' => [
|
325 |
+
'token' => true,
|
326 |
+
'endpoint' => '/rtm.start'
|
327 |
+
],
|
328 |
+
'search.all' => [
|
329 |
+
'token' => true,
|
330 |
+
'endpoint' => '/search.all'
|
331 |
+
],
|
332 |
+
'search.files' => [
|
333 |
+
'token' => true,
|
334 |
+
'endpoint' => '/search.files'
|
335 |
+
],
|
336 |
+
'search.messages' => [
|
337 |
+
'token' => true,
|
338 |
+
'endpoint' => '/search.messages'
|
339 |
+
],
|
340 |
+
'stars.add' => [
|
341 |
+
'token' => true,
|
342 |
+
'endpoint' => '/stars.add'
|
343 |
+
],
|
344 |
+
'stars.list' => [
|
345 |
+
'token' => true,
|
346 |
+
'endpoint' => '/stars.list'
|
347 |
+
],
|
348 |
+
'stars.remove' => [
|
349 |
+
'token' => true,
|
350 |
+
'endpoint' => '/stars.remove'
|
351 |
+
],
|
352 |
+
'team.accessLogs' => [
|
353 |
+
'token' => true,
|
354 |
+
'endpoint' => '/team.accessLogs'
|
355 |
+
],
|
356 |
+
'team.info' => [
|
357 |
+
'token' => true,
|
358 |
+
'endpoint' => '/team.info'
|
359 |
+
],
|
360 |
+
'team.integrationLogs' => [
|
361 |
+
'token' => true,
|
362 |
+
'endpoint' => '/team.integrationLogs'
|
363 |
+
],
|
364 |
+
'usergroups.create' => [
|
365 |
+
'token' => true,
|
366 |
+
'endpoint' => '/usergroups.create'
|
367 |
+
],
|
368 |
+
'usergroups.disable' => [
|
369 |
+
'token' => true,
|
370 |
+
'endpoint' => '/usergroups.disable'
|
371 |
+
],
|
372 |
+
'usergroups.enable' => [
|
373 |
+
'token' => true,
|
374 |
+
'endpoint' => '/usergroups.enable'
|
375 |
+
],
|
376 |
+
'usergroups.list' => [
|
377 |
+
'token' => true,
|
378 |
+
'endpoint' => '/usergroups.list'
|
379 |
+
],
|
380 |
+
'usergroups.update' => [
|
381 |
+
'token' => true,
|
382 |
+
'endpoint' => '/usergroups.update'
|
383 |
+
],
|
384 |
+
'usergroups.users.list' => [
|
385 |
+
'token' => true,
|
386 |
+
'endpoint' => '/usergroups.users.list'
|
387 |
+
],
|
388 |
+
'usergroups.users.update' => [
|
389 |
+
'token' => true,
|
390 |
+
'endpoint' => '/usergroups.users.update'
|
391 |
+
],
|
392 |
+
'users.getPresence' => [
|
393 |
+
'token' => true,
|
394 |
+
'endpoint' => '/users.getPresence'
|
395 |
+
],
|
396 |
+
'users.info' => [
|
397 |
+
'token' => true,
|
398 |
+
'endpoint' => '/users.info'
|
399 |
+
],
|
400 |
+
'users.list' => [
|
401 |
+
'token' => true,
|
402 |
+
'endpoint' => '/users.list'
|
403 |
+
],
|
404 |
+
'users.setActive' => [
|
405 |
+
'token' => true,
|
406 |
+
'endpoint' => '/users.setActive'
|
407 |
+
],
|
408 |
+
'users.setPresence' => [
|
409 |
+
'token' => true,
|
410 |
+
'endpoint' => '/users.setPresence'
|
411 |
+
],
|
412 |
+
'users.admin.invite' => [
|
413 |
+
'token' => true,
|
414 |
+
'endpoint' => '/users.admin.invite'
|
415 |
+
]
|
416 |
+
];
|
417 |
+
|
418 |
+
/**
|
419 |
+
* The base URL.
|
420 |
+
*
|
421 |
+
* @var string
|
422 |
+
*/
|
423 |
+
protected static $baseUrl = 'https://slack.com/api';
|
424 |
+
|
425 |
+
/**
|
426 |
+
* The API token.
|
427 |
+
*
|
428 |
+
* @var string
|
429 |
+
*/
|
430 |
+
protected $token;
|
431 |
+
|
432 |
+
/**
|
433 |
+
* The Http interactor.
|
434 |
+
*
|
435 |
+
* @var \Frlnc\Slack\Contracts\Http\Interactor
|
436 |
+
*/
|
437 |
+
protected $interactor;
|
438 |
+
|
439 |
+
/**
|
440 |
+
* @param string $token
|
441 |
+
* @param \Frlnc\Slack\Contracts\Http\Interactor $interactor
|
442 |
+
*/
|
443 |
+
public function __construct($token, Interactor $interactor)
|
444 |
+
{
|
445 |
+
$this->token = $token;
|
446 |
+
$this->interactor = $interactor;
|
447 |
+
}
|
448 |
+
|
449 |
+
/**
|
450 |
+
* Executes a command.
|
451 |
+
*
|
452 |
+
* @param string $command
|
453 |
+
* @param array $parameters
|
454 |
+
* @return \Frlnc\Slack\Contracts\Http\Response
|
455 |
+
*/
|
456 |
+
public function execute($command, array $parameters = [])
|
457 |
+
{
|
458 |
+
if (!isset(self::$commands[$command]))
|
459 |
+
throw new InvalidArgumentException("The command '{$command}' is not currently supported");
|
460 |
+
|
461 |
+
$command = self::$commands[$command];
|
462 |
+
|
463 |
+
if ($command['token'])
|
464 |
+
$parameters = array_merge($parameters, ['token' => $this->token]);
|
465 |
+
|
466 |
+
if (isset($command['format']))
|
467 |
+
foreach ($command['format'] as $format)
|
468 |
+
if (isset($parameters[$format]))
|
469 |
+
$parameters[$format] = self::format($parameters[$format]);
|
470 |
+
|
471 |
+
$headers = [];
|
472 |
+
if (isset($command['headers']))
|
473 |
+
$headers = $command['headers'];
|
474 |
+
|
475 |
+
$url = self::$baseUrl . $command['endpoint'];
|
476 |
+
|
477 |
+
if (isset($command['post']) && $command['post'])
|
478 |
+
return $this->interactor->post($url, [], $parameters, $headers);
|
479 |
+
|
480 |
+
return $this->interactor->get($url, $parameters, $headers);
|
481 |
+
}
|
482 |
+
|
483 |
+
/**
|
484 |
+
* Sets the token.
|
485 |
+
*
|
486 |
+
* @param string $token
|
487 |
+
*/
|
488 |
+
public function setToken($token)
|
489 |
+
{
|
490 |
+
$this->token = $token;
|
491 |
+
}
|
492 |
+
|
493 |
+
/**
|
494 |
+
* Formats a string for Slack.
|
495 |
+
*
|
496 |
+
* @param string $string
|
497 |
+
* @return string
|
498 |
+
*/
|
499 |
+
public static function format($string)
|
500 |
+
{
|
501 |
+
$string = str_replace('&', '&', $string);
|
502 |
+
$string = str_replace('<', '<', $string);
|
503 |
+
$string = str_replace('>', '>', $string);
|
504 |
+
|
505 |
+
return $string;
|
506 |
+
}
|
507 |
+
|
508 |
+
}
|
includes/slack/Http/CurlInteractor.php
ADDED
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php namespace Frlnc\Slack\Http;
|
2 |
+
|
3 |
+
use Frlnc\Slack\Contracts\Http\ResponseFactory;
|
4 |
+
|
5 |
+
class CurlInteractor implements \Frlnc\Slack\Contracts\Http\Interactor {
|
6 |
+
|
7 |
+
/**
|
8 |
+
* The response factory to use.
|
9 |
+
*
|
10 |
+
* @var \Frlnc\Slack\Contracts\Http\ResponseFactory
|
11 |
+
*/
|
12 |
+
protected $factory;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* {@inheritdoc}
|
16 |
+
*/
|
17 |
+
public function get($url, array $parameters = [], array $headers = [])
|
18 |
+
{
|
19 |
+
$request = $this->prepareRequest($url, $parameters, $headers);
|
20 |
+
|
21 |
+
return $this->executeRequest($request);
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* {@inheritdoc}
|
26 |
+
*/
|
27 |
+
public function post($url, array $urlParameters = [], array $postParameters = [], array $headers = [])
|
28 |
+
{
|
29 |
+
$request = $this->prepareRequest($url, $urlParameters, $headers);
|
30 |
+
|
31 |
+
curl_setopt($request, CURLOPT_POST, count($postParameters));
|
32 |
+
curl_setopt($request, CURLOPT_POSTFIELDS, http_build_query($postParameters));
|
33 |
+
|
34 |
+
return $this->executeRequest($request);
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Prepares a request using curl.
|
39 |
+
*
|
40 |
+
* @param string $url [description]
|
41 |
+
* @param array $parameters [description]
|
42 |
+
* @param array $headers [description]
|
43 |
+
* @return resource
|
44 |
+
*/
|
45 |
+
protected static function prepareRequest($url, $parameters = [], $headers = [])
|
46 |
+
{
|
47 |
+
$request = curl_init();
|
48 |
+
|
49 |
+
if ($query = http_build_query($parameters))
|
50 |
+
$url .= '?' . $query;
|
51 |
+
|
52 |
+
curl_setopt($request, CURLOPT_URL, $url);
|
53 |
+
curl_setopt($request, CURLOPT_RETURNTRANSFER, true);
|
54 |
+
curl_setopt($request, CURLOPT_HTTPHEADER, $headers);
|
55 |
+
curl_setopt($request, CURLINFO_HEADER_OUT, true);
|
56 |
+
curl_setopt($request, CURLOPT_SSL_VERIFYPEER, false);
|
57 |
+
|
58 |
+
return $request;
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Executes a curl request.
|
63 |
+
*
|
64 |
+
* @param resource $request
|
65 |
+
* @return \Frlnc\Slack\Contracts\Http\Response
|
66 |
+
*/
|
67 |
+
public function executeRequest($request)
|
68 |
+
{
|
69 |
+
$body = curl_exec($request);
|
70 |
+
$info = curl_getinfo($request);
|
71 |
+
|
72 |
+
curl_close($request);
|
73 |
+
|
74 |
+
$statusCode = $info['http_code'];
|
75 |
+
$headers = $info['request_header'];
|
76 |
+
|
77 |
+
if (function_exists('http_parse_headers'))
|
78 |
+
$headers = http_parse_headers($headers);
|
79 |
+
else
|
80 |
+
{
|
81 |
+
$header_text = substr($headers, 0, strpos($headers, "\r\n\r\n"));
|
82 |
+
$headers = [];
|
83 |
+
|
84 |
+
foreach (explode("\r\n", $header_text) as $i => $line)
|
85 |
+
if ($i === 0)
|
86 |
+
continue;
|
87 |
+
else
|
88 |
+
{
|
89 |
+
list ($key, $value) = explode(': ', $line);
|
90 |
+
|
91 |
+
$headers[$key] = $value;
|
92 |
+
}
|
93 |
+
}
|
94 |
+
|
95 |
+
return $this->factory->build($body, $headers, $statusCode);
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* {@inheritdoc}
|
100 |
+
*/
|
101 |
+
public function setResponseFactory(ResponseFactory $factory)
|
102 |
+
{
|
103 |
+
$this->factory = $factory;
|
104 |
+
}
|
105 |
+
|
106 |
+
}
|
includes/slack/Http/SlackResponse.php
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php namespace Frlnc\Slack\Http;
|
2 |
+
|
3 |
+
class SlackResponse implements \Frlnc\Slack\Contracts\Http\Response, \JsonSerializable {
|
4 |
+
|
5 |
+
/**
|
6 |
+
* The response body.
|
7 |
+
*
|
8 |
+
* @var string
|
9 |
+
*/
|
10 |
+
protected $body;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* The response headers.
|
14 |
+
*
|
15 |
+
* @var array
|
16 |
+
*/
|
17 |
+
protected $headers;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* The response status code.
|
21 |
+
*
|
22 |
+
* @var integer
|
23 |
+
*/
|
24 |
+
protected $statusCode;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @param string $body
|
28 |
+
* @param array $headers
|
29 |
+
* @param integer $statusCode
|
30 |
+
*/
|
31 |
+
public function __construct($body, array $headers = [], $statusCode = 404)
|
32 |
+
{
|
33 |
+
$this->body = json_decode($body, true);
|
34 |
+
$this->headers = $headers;
|
35 |
+
$this->statusCode = $statusCode;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* {@inheritdoc}
|
40 |
+
*/
|
41 |
+
public function getBody()
|
42 |
+
{
|
43 |
+
return $this->body;
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* {@inheritdoc}
|
48 |
+
*/
|
49 |
+
public function getHeaders()
|
50 |
+
{
|
51 |
+
return $this->headers;
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* {@inheritdoc}
|
56 |
+
*/
|
57 |
+
public function getStatusCode()
|
58 |
+
{
|
59 |
+
return $this->statusCode;
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* {@inheritdoc}
|
64 |
+
*/
|
65 |
+
public function jsonSerialize()
|
66 |
+
{
|
67 |
+
return $this->toArray();
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Converts the response to an array.
|
72 |
+
*
|
73 |
+
* @return array
|
74 |
+
*/
|
75 |
+
public function toArray()
|
76 |
+
{
|
77 |
+
return [
|
78 |
+
'status_code' => $this->getStatusCode(),
|
79 |
+
'headers' => $this->getHeaders(),
|
80 |
+
'body' => $this->getBody()
|
81 |
+
];
|
82 |
+
}
|
83 |
+
|
84 |
+
}
|
includes/slack/Http/SlackResponseFactory.php
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php namespace Frlnc\Slack\Http;
|
2 |
+
|
3 |
+
class SlackResponseFactory implements \Frlnc\Slack\Contracts\Http\ResponseFactory {
|
4 |
+
|
5 |
+
/**
|
6 |
+
* {@inheritdoc}
|
7 |
+
*/
|
8 |
+
public function build($body, array $headers, $statusCode)
|
9 |
+
{
|
10 |
+
return new SlackResponse($body, $headers, $statusCode);
|
11 |
+
}
|
12 |
+
|
13 |
+
}
|
includes/slack/Logger.php
ADDED
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php namespace Frlnc\Slack;
|
2 |
+
|
3 |
+
use Frlnc\Slack\Http\CurlInteractor;
|
4 |
+
use Frlnc\Slack\Http\SlackResponseFactory;
|
5 |
+
use Frlnc\Slack\Core\Commander;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Created by Vextras.
|
9 |
+
*
|
10 |
+
* Name: Ryan Hungate
|
11 |
+
* Email: ryan@vextras.com
|
12 |
+
* Date: 8/12/16
|
13 |
+
* Time: 9:36 AM
|
14 |
+
*/
|
15 |
+
class Logger
|
16 |
+
{
|
17 |
+
private static $instance = null;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* @var null|Commander
|
21 |
+
*/
|
22 |
+
private $commander = null;
|
23 |
+
public $api_token = null;
|
24 |
+
public $channel = null;
|
25 |
+
|
26 |
+
|
27 |
+
/**
|
28 |
+
* @return mixed
|
29 |
+
*/
|
30 |
+
public static function instance()
|
31 |
+
{
|
32 |
+
if (empty(static::$instance)) {
|
33 |
+
$vars = mailchimp_environment_variables();
|
34 |
+
static::$instance = new Logger(
|
35 |
+
(isset($vars->slack_token) ? $vars->slack_token : null),
|
36 |
+
(isset($vars->slack_channel) ? $vars->slack_channel : null)
|
37 |
+
);
|
38 |
+
}
|
39 |
+
return static::$instance;
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Logger constructor.
|
44 |
+
* @param string $api_token
|
45 |
+
* @param string $channel
|
46 |
+
*/
|
47 |
+
public function __construct($api_token = null, $channel = null)
|
48 |
+
{
|
49 |
+
if ($api_token && $channel) {
|
50 |
+
$this->setup($api_token, $channel);
|
51 |
+
}
|
52 |
+
}
|
53 |
+
|
54 |
+
/**
|
55 |
+
* @param $api_token
|
56 |
+
* @param $channel
|
57 |
+
* @return $this
|
58 |
+
*/
|
59 |
+
public function setup($api_token, $channel)
|
60 |
+
{
|
61 |
+
$this->channel = $channel;
|
62 |
+
$this->api_token = $api_token;
|
63 |
+
|
64 |
+
$curl = new CurlInteractor;
|
65 |
+
$curl->setResponseFactory(new SlackResponseFactory);
|
66 |
+
|
67 |
+
$this->commander = new Commander($this->api_token, $curl);
|
68 |
+
|
69 |
+
return $this;
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* @param $message
|
74 |
+
* @return Logger
|
75 |
+
*/
|
76 |
+
public function notice($message)
|
77 |
+
{
|
78 |
+
if (empty($this->commander) || empty($this->api_token) || empty($this->channel)) {
|
79 |
+
return $this;
|
80 |
+
}
|
81 |
+
|
82 |
+
try {
|
83 |
+
$this->commander->execute('chat.postMessage', [
|
84 |
+
'channel' => '#'.$this->channel,
|
85 |
+
'text' => $message
|
86 |
+
]);
|
87 |
+
} catch (\Exception $e) {
|
88 |
+
|
89 |
+
}
|
90 |
+
|
91 |
+
return $this;
|
92 |
+
}
|
93 |
+
}
|
includes/vendor/queue.php
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/*
|
3 |
+
Plugin Name: WP Background Processing
|
4 |
+
Plugin URI: https://github.com/A5hleyRich/wp-background-processing
|
5 |
+
Description: Asynchronous requests and background processing in WordPress.
|
6 |
+
Author: Delicious Brains Inc.
|
7 |
+
Version: 1.0
|
8 |
+
Author URI: https://deliciousbrains.com/
|
9 |
+
*/
|
10 |
+
|
11 |
+
$queue_folder_path = plugin_dir_path( __FILE__ );
|
12 |
+
|
13 |
+
require_once $queue_folder_path . 'queue/classes/wp-job.php';
|
14 |
+
require_once $queue_folder_path . 'queue/classes/wp-queue.php';
|
15 |
+
require_once $queue_folder_path . 'queue/classes/worker/wp-worker.php';
|
16 |
+
require_once $queue_folder_path . 'queue/classes/worker/wp-http-worker.php';
|
17 |
+
|
18 |
+
// Add WP CLI commands
|
19 |
+
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
20 |
+
require_once $queue_folder_path . 'queue/classes/cli/queue-command.php';
|
21 |
+
|
22 |
+
WP_CLI::add_command( 'queue', 'Queue_Command' );
|
23 |
+
}
|
24 |
+
|
25 |
+
global $wp_queue;
|
26 |
+
$wp_queue = new WP_Queue();
|
27 |
+
|
28 |
+
// Instantiate HTTP queue worker
|
29 |
+
new WP_Http_Worker($wp_queue);
|
30 |
+
|
31 |
+
if ( ! function_exists( 'wp_queue' ) ) {
|
32 |
+
/**
|
33 |
+
* WP queue.
|
34 |
+
*
|
35 |
+
* @param WP_Job $job
|
36 |
+
* @param int $delay
|
37 |
+
*/
|
38 |
+
function wp_queue( WP_Job $job, $delay = 0 ) {
|
39 |
+
global $wp_queue;
|
40 |
+
$wp_queue->push( $job, $delay );
|
41 |
+
do_action( 'wp_queue_job_pushed', $job );
|
42 |
+
}
|
43 |
+
}
|
includes/vendor/queue/classes/cli/queue-command.php
ADDED
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Manage queue and jobs.
|
5 |
+
*
|
6 |
+
* @package wp-cli
|
7 |
+
*/
|
8 |
+
class Queue_Command extends WP_CLI_Command {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Creates the queue tables.
|
12 |
+
*
|
13 |
+
* @subcommand create-tables
|
14 |
+
*/
|
15 |
+
public function create_tables( $args, $assoc_args = array() ) {
|
16 |
+
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
17 |
+
|
18 |
+
global $wpdb;
|
19 |
+
|
20 |
+
$wpdb->hide_errors();
|
21 |
+
|
22 |
+
$charset_collate = $wpdb->get_charset_collate();
|
23 |
+
|
24 |
+
$sql = "CREATE TABLE {$wpdb->prefix}queue (
|
25 |
+
id bigint(20) NOT NULL AUTO_INCREMENT,
|
26 |
+
job text NOT NULL,
|
27 |
+
attempts tinyint(1) NOT NULL DEFAULT 0,
|
28 |
+
locked tinyint(1) NOT NULL DEFAULT 0,
|
29 |
+
locked_at datetime DEFAULT NULL,
|
30 |
+
available_at datetime NOT NULL,
|
31 |
+
created_at datetime NOT NULL,
|
32 |
+
PRIMARY KEY (id)
|
33 |
+
) $charset_collate;";
|
34 |
+
|
35 |
+
dbDelta( $sql );
|
36 |
+
|
37 |
+
$sql = "CREATE TABLE {$wpdb->prefix}failed_jobs (
|
38 |
+
id bigint(20) NOT NULL AUTO_INCREMENT,
|
39 |
+
job text NOT NULL,
|
40 |
+
failed_at datetime NOT NULL,
|
41 |
+
PRIMARY KEY (id)
|
42 |
+
) $charset_collate;";
|
43 |
+
|
44 |
+
dbDelta( $sql );
|
45 |
+
|
46 |
+
WP_CLI::success( "Table {$wpdb->prefix}queue created." );
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Listen to the queue.
|
51 |
+
*/
|
52 |
+
public function listen( $args, $assoc_args = array() ) {
|
53 |
+
global $wp_queue;
|
54 |
+
|
55 |
+
$worker = new WP_Worker( $wp_queue );
|
56 |
+
|
57 |
+
WP_CLI::log( 'Listening for queue jobs...' );
|
58 |
+
|
59 |
+
while ( true ) {
|
60 |
+
if ( $worker->should_run() ) {
|
61 |
+
if ( $worker->process_next_job() ) {
|
62 |
+
WP_CLI::success( 'Processed: ' . $worker->get_job_name() );
|
63 |
+
} else {
|
64 |
+
WP_CLI::warning( 'Failed: ' . $worker->get_job_name() );
|
65 |
+
}
|
66 |
+
} else {
|
67 |
+
sleep( 5 );
|
68 |
+
}
|
69 |
+
}
|
70 |
+
}
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Process the next job in the queue.
|
74 |
+
*/
|
75 |
+
public function work( $args, $assoc_args = array() ) {
|
76 |
+
global $wp_queue;
|
77 |
+
|
78 |
+
$worker = new WP_Worker( $wp_queue );
|
79 |
+
|
80 |
+
if ( $worker->should_run() ) {
|
81 |
+
if ( $worker->process_next_job() ) {
|
82 |
+
WP_CLI::success( 'Processed: ' . $worker->get_job_name() );
|
83 |
+
} else {
|
84 |
+
WP_CLI::warning( 'Failed: ' . $worker->get_job_name() );
|
85 |
+
}
|
86 |
+
} else {
|
87 |
+
WP_CLI::log( 'No jobs to process...' );
|
88 |
+
}
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Show queue status.
|
93 |
+
*/
|
94 |
+
public function status( $args, $assoc_args = array() ) {
|
95 |
+
global $wp_queue;
|
96 |
+
|
97 |
+
WP_CLI::log( $wp_queue->available_jobs() . ' jobs in the queue' );
|
98 |
+
WP_CLI::log( $wp_queue->failed_jobs() . ' failed jobs' );
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Push failed jobs back onto the queue.
|
103 |
+
*
|
104 |
+
* @subcommand restart-failed
|
105 |
+
*/
|
106 |
+
public function restart_failed( $args, $assoc_args = array() ) {
|
107 |
+
global $wp_queue;
|
108 |
+
|
109 |
+
if ( ! $wp_queue->failed_jobs() ) {
|
110 |
+
WP_CLI::log( 'No failed jobs to restart...' );
|
111 |
+
|
112 |
+
return;
|
113 |
+
}
|
114 |
+
|
115 |
+
$count = $wp_queue->restart_failed_jobs();
|
116 |
+
|
117 |
+
WP_CLI::success( $count . ' failed jobs pushed to the queue' );
|
118 |
+
}
|
119 |
+
|
120 |
+
}
|
includes/vendor/queue/classes/worker/wp-http-worker.php
ADDED
@@ -0,0 +1,323 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if ( ! class_exists( 'WP_Http_Worker' ) ) {
|
4 |
+
class WP_Http_Worker extends WP_Worker {
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Has the worker been dispatched in this request?
|
8 |
+
*
|
9 |
+
* @var bool
|
10 |
+
*/
|
11 |
+
protected $dispatched = false;
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Timestamp of when this worker started processing the queue.
|
15 |
+
*
|
16 |
+
* @var int
|
17 |
+
*/
|
18 |
+
protected $start_time;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* WP_Http_Worker constructor
|
22 |
+
*
|
23 |
+
* @param WP_Queue $queue
|
24 |
+
*/
|
25 |
+
public function __construct( $queue ) {
|
26 |
+
parent::__construct( $queue );
|
27 |
+
|
28 |
+
// Cron health check
|
29 |
+
add_action( 'http_worker_cron', array( $this, 'handle_cron' ) );
|
30 |
+
add_filter( 'cron_schedules', array( $this, 'schedule_cron' ) );
|
31 |
+
$this->maybe_schedule_cron();
|
32 |
+
|
33 |
+
// Dispatch handlers
|
34 |
+
add_action( 'wp_ajax_http_worker', array( $this, 'maybe_handle' ) );
|
35 |
+
add_action( 'wp_ajax_nopriv_http_worker', array( $this, 'maybe_handle' ) );
|
36 |
+
|
37 |
+
// Dispatch listener
|
38 |
+
add_action( 'wp_queue_job_pushed', array( $this, 'maybe_dispatch_worker' ) );
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Maybe handle
|
43 |
+
*
|
44 |
+
* Process the queue if no other HTTP worker is running and
|
45 |
+
* the current worker is within server memory and time limit constraints.
|
46 |
+
* Automatically dispatch another worker and kill the current process if
|
47 |
+
* jobs remain in the queue and server limits reached.
|
48 |
+
*/
|
49 |
+
public function maybe_handle() {
|
50 |
+
check_ajax_referer( 'http_worker', 'nonce' );
|
51 |
+
|
52 |
+
if ( $this->is_worker_running() ) {
|
53 |
+
// Worker already running, die
|
54 |
+
wp_die();
|
55 |
+
}
|
56 |
+
|
57 |
+
// Lock worker to prevent multiple instances spawning
|
58 |
+
$this->lock_worker();
|
59 |
+
|
60 |
+
// Loop over jobs while within server limits
|
61 |
+
while ( ! $this->time_exceeded() && ! $this->memory_exceeded() ) {
|
62 |
+
if ( $this->should_run() ) {
|
63 |
+
$this->process_next_job();
|
64 |
+
} else {
|
65 |
+
break;
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
// Unlock worker to allow another instance to be spawned
|
70 |
+
$this->unlock_worker();
|
71 |
+
|
72 |
+
if ( $this->queue->available_jobs() ) {
|
73 |
+
// Job queue not empty, dispatch async worker request
|
74 |
+
$this->dispatch();
|
75 |
+
}
|
76 |
+
|
77 |
+
wp_die();
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Memory exceeded
|
82 |
+
*
|
83 |
+
* Ensures the worker process never exceeds 80%
|
84 |
+
* of the maximum allowed PHP memory.
|
85 |
+
*
|
86 |
+
* @return bool
|
87 |
+
*/
|
88 |
+
protected function memory_exceeded() {
|
89 |
+
$memory_limit = $this->get_memory_limit() * 0.8; // 80% of max memory
|
90 |
+
$current_memory = memory_get_usage( true );
|
91 |
+
$return = false;
|
92 |
+
|
93 |
+
if ( $current_memory >= $memory_limit ) {
|
94 |
+
$return = true;
|
95 |
+
}
|
96 |
+
|
97 |
+
return apply_filters( 'http_worker_memory_exceeded', $return );
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Get memory limit
|
102 |
+
*
|
103 |
+
* @return int
|
104 |
+
*/
|
105 |
+
protected function get_memory_limit() {
|
106 |
+
if ( function_exists( 'ini_get' ) ) {
|
107 |
+
$memory_limit = ini_get( 'memory_limit' );
|
108 |
+
} else {
|
109 |
+
// Sensible default
|
110 |
+
$memory_limit = '128M';
|
111 |
+
}
|
112 |
+
|
113 |
+
if ( ! $memory_limit || -1 == $memory_limit ) {
|
114 |
+
// Unlimited, set to 32GB
|
115 |
+
$memory_limit = '32000M';
|
116 |
+
}
|
117 |
+
|
118 |
+
return intval( $memory_limit ) * 1024 * 1024;
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
* Time exceeded
|
123 |
+
*
|
124 |
+
* Ensures the worker never exceeds a sensible time limit (20s by default).
|
125 |
+
* A timeout limit of 30s is common on shared hosting.
|
126 |
+
*
|
127 |
+
* @return bool
|
128 |
+
*/
|
129 |
+
protected function time_exceeded() {
|
130 |
+
$finish = $this->start_time + apply_filters( 'http_worker_default_time_limit', 20 ); // 20 seconds
|
131 |
+
$return = false;
|
132 |
+
|
133 |
+
if ( time() >= $finish ) {
|
134 |
+
$return = true;
|
135 |
+
}
|
136 |
+
|
137 |
+
return apply_filters( 'http_worker_time_exceeded', $return );
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Maybe dispatch worker
|
142 |
+
*
|
143 |
+
* Dispatch a worker process if we haven't already in this request
|
144 |
+
* and no other HTTP workers are running.
|
145 |
+
*
|
146 |
+
* @param WP_Job $job
|
147 |
+
*/
|
148 |
+
public function maybe_dispatch_worker( $job ) {
|
149 |
+
if ( $this->is_worker_running() ) {
|
150 |
+
// HTTP worker already running, return
|
151 |
+
return;
|
152 |
+
}
|
153 |
+
|
154 |
+
// Dispatch async worker request
|
155 |
+
$this->dispatch();
|
156 |
+
}
|
157 |
+
|
158 |
+
/**
|
159 |
+
* Is worker running
|
160 |
+
*
|
161 |
+
* Check if another instance of the HTTP worker is running.
|
162 |
+
*
|
163 |
+
* @return bool
|
164 |
+
*/
|
165 |
+
protected function is_worker_running() {
|
166 |
+
if ( get_site_transient( 'http_worker_lock' ) ) {
|
167 |
+
// Process already running
|
168 |
+
return true;
|
169 |
+
}
|
170 |
+
|
171 |
+
return false;
|
172 |
+
}
|
173 |
+
|
174 |
+
/**
|
175 |
+
* Lock worker
|
176 |
+
*
|
177 |
+
* Lock the HTTP worker to prevent multiple instances running.
|
178 |
+
*/
|
179 |
+
protected function lock_worker() {
|
180 |
+
$this->start_time = time(); // Set start time of current worker
|
181 |
+
|
182 |
+
$lock_duration = apply_filters( 'http_worker_lock_time', 60 ); // 60 seconds
|
183 |
+
|
184 |
+
set_site_transient( 'http_worker_lock', microtime(), $lock_duration );
|
185 |
+
}
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Unlock worker
|
189 |
+
*
|
190 |
+
* Unlock the HTTP worker to allow other instances to be spawned.
|
191 |
+
*/
|
192 |
+
protected function unlock_worker() {
|
193 |
+
delete_site_transient( 'http_worker_lock' );
|
194 |
+
}
|
195 |
+
|
196 |
+
/**
|
197 |
+
* Dispatch
|
198 |
+
*
|
199 |
+
* Fire off a non-blocking async request if we haven't already
|
200 |
+
* in this request.
|
201 |
+
*/
|
202 |
+
protected function dispatch() {
|
203 |
+
if ( $this->is_http_worker_disabled() ) {
|
204 |
+
return;
|
205 |
+
}
|
206 |
+
|
207 |
+
if ( ! $this->dispatched ) {
|
208 |
+
$this->async_request();
|
209 |
+
}
|
210 |
+
|
211 |
+
$this->dispatched = true;
|
212 |
+
}
|
213 |
+
|
214 |
+
/**
|
215 |
+
* Is HTTP worker disabled
|
216 |
+
*
|
217 |
+
* @return bool
|
218 |
+
*/
|
219 |
+
protected function is_http_worker_disabled() {
|
220 |
+
if ( ! defined( 'DISABLE_WP_HTTP_WORKER' ) || true !== DISABLE_WP_HTTP_WORKER ) {
|
221 |
+
return false;
|
222 |
+
}
|
223 |
+
|
224 |
+
return true;
|
225 |
+
}
|
226 |
+
|
227 |
+
/**
|
228 |
+
* Async request
|
229 |
+
*
|
230 |
+
* Fire off a non-blocking request to admin-ajax.php.
|
231 |
+
*
|
232 |
+
* @return array|WP_Error
|
233 |
+
*/
|
234 |
+
protected function async_request() {
|
235 |
+
$action = 'http_worker';
|
236 |
+
|
237 |
+
$query_args = apply_filters( 'http_worker_query_args', array(
|
238 |
+
'action' => $action,
|
239 |
+
'nonce' => wp_create_nonce( $action ),
|
240 |
+
) );
|
241 |
+
|
242 |
+
$query_url = apply_filters( 'http_worker_query_url', admin_url( 'admin-ajax.php' ) );
|
243 |
+
|
244 |
+
$post_args = apply_filters( 'http_worker_post_args', array(
|
245 |
+
'timeout' => 0.01,
|
246 |
+
'blocking' => false,
|
247 |
+
'cookies' => $_COOKIE,
|
248 |
+
'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
|
249 |
+
) );
|
250 |
+
|
251 |
+
$url = add_query_arg( $query_args, $query_url );
|
252 |
+
|
253 |
+
return wp_remote_post( esc_url_raw( $url ), $post_args );
|
254 |
+
}
|
255 |
+
|
256 |
+
/**
|
257 |
+
* Handle cron
|
258 |
+
*
|
259 |
+
* Restart the HTTP worker if not already running
|
260 |
+
* and data exists in the queue.
|
261 |
+
*/
|
262 |
+
public function handle_cron() {
|
263 |
+
if ( $this->is_worker_running() ) {
|
264 |
+
// Worker already running, die
|
265 |
+
wp_die();
|
266 |
+
}
|
267 |
+
|
268 |
+
if ( ! $this->queue->available_jobs() ) {
|
269 |
+
// No jobs on the queue to process, die
|
270 |
+
wp_die();
|
271 |
+
}
|
272 |
+
|
273 |
+
$this->dispatch();
|
274 |
+
|
275 |
+
exit;
|
276 |
+
}
|
277 |
+
|
278 |
+
/**
|
279 |
+
* Cron schedules
|
280 |
+
*
|
281 |
+
* @param $schedules
|
282 |
+
*
|
283 |
+
* @return mixed
|
284 |
+
*/
|
285 |
+
public function schedule_cron( $schedules ) {
|
286 |
+
$interval = apply_filters( 'http_worker_cron_interval', 3 );
|
287 |
+
|
288 |
+
// Adds every 3 minutes to the existing schedules.
|
289 |
+
$schedules[ 'http_worker_cron_interval' ] = array(
|
290 |
+
'interval' => MINUTE_IN_SECONDS * $interval,
|
291 |
+
'display' => sprintf( __( 'Every %d Minutes' ), $interval ),
|
292 |
+
);
|
293 |
+
|
294 |
+
return $schedules;
|
295 |
+
}
|
296 |
+
|
297 |
+
/**
|
298 |
+
* Maybe schedule cron
|
299 |
+
*
|
300 |
+
* Schedule health check cron if not disabled. Remove schedule if
|
301 |
+
* disabled and already scheduled.
|
302 |
+
*/
|
303 |
+
public function maybe_schedule_cron() {
|
304 |
+
if ( $this->is_http_worker_disabled() ) {
|
305 |
+
die('http worker is disabled');
|
306 |
+
// Remove health check cron event, if scheduled
|
307 |
+
$timestamp = wp_next_scheduled( 'http_worker_cron' );
|
308 |
+
|
309 |
+
if ( wp_next_scheduled( 'http_worker_cron' ) ) {
|
310 |
+
wp_unschedule_event( $timestamp, 'http_worker_cron' );
|
311 |
+
}
|
312 |
+
|
313 |
+
return;
|
314 |
+
}
|
315 |
+
|
316 |
+
if ( ! wp_next_scheduled( 'http_worker_cron' ) ) {
|
317 |
+
// Schedule health check
|
318 |
+
wp_schedule_event( time(), 'http_worker_cron_interval', 'http_worker_cron' );
|
319 |
+
}
|
320 |
+
}
|
321 |
+
|
322 |
+
}
|
323 |
+
}
|
includes/vendor/queue/classes/worker/wp-worker.php
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if ( ! class_exists( 'WP_Worker' ) ) {
|
4 |
+
class WP_Worker {
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var WP_Queue
|
8 |
+
*/
|
9 |
+
protected $queue;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var WP_Job
|
13 |
+
*/
|
14 |
+
protected $payload;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* WP_Worker constructor.
|
18 |
+
*
|
19 |
+
* @param WP_Queue $queue
|
20 |
+
*/
|
21 |
+
public function __construct( $queue ) {
|
22 |
+
$this->queue = $queue;
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Should run
|
27 |
+
*
|
28 |
+
* @return bool
|
29 |
+
*/
|
30 |
+
public function should_run() {
|
31 |
+
if ( $this->queue->available_jobs() ) {
|
32 |
+
return true;
|
33 |
+
}
|
34 |
+
|
35 |
+
return false;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Process next job.
|
40 |
+
*
|
41 |
+
* @return bool
|
42 |
+
*/
|
43 |
+
public function process_next_job() {
|
44 |
+
$job = $this->queue->get_next_job();
|
45 |
+
$this->payload = unserialize( $job->job );
|
46 |
+
|
47 |
+
$this->queue->lock_job( $job );
|
48 |
+
$this->payload->set_job( $job );
|
49 |
+
|
50 |
+
try {
|
51 |
+
$this->payload->handle();
|
52 |
+
|
53 |
+
if ( $this->payload->is_released() ) {
|
54 |
+
// Job manually released, release back onto queue
|
55 |
+
$this->queue->release( $job, $this->payload->get_delay() );
|
56 |
+
}
|
57 |
+
|
58 |
+
if ( $this->payload->is_deleted() ) {
|
59 |
+
// Job manually deleted, delete from queue
|
60 |
+
$this->queue->delete( $job );
|
61 |
+
}
|
62 |
+
|
63 |
+
if ( ! $this->payload->is_deleted_or_released() ) {
|
64 |
+
// Job completed, delete from queue
|
65 |
+
$this->queue->delete( $job );
|
66 |
+
}
|
67 |
+
} catch ( Exception $e ) {
|
68 |
+
$this->queue->release( $job );
|
69 |
+
|
70 |
+
return false;
|
71 |
+
}
|
72 |
+
|
73 |
+
return true;
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Get job name.
|
78 |
+
*
|
79 |
+
* @return object
|
80 |
+
*/
|
81 |
+
public function get_job_name() {
|
82 |
+
return get_class( $this->payload );
|
83 |
+
}
|
84 |
+
|
85 |
+
}
|
86 |
+
}
|
includes/vendor/queue/classes/wp-job.php
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if ( ! class_exists( 'WP_Job' ) ) {
|
4 |
+
abstract class WP_Job {
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var stdClass
|
8 |
+
*/
|
9 |
+
private $job;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var int
|
13 |
+
*/
|
14 |
+
private $delay = 0;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var bool
|
18 |
+
*/
|
19 |
+
private $deleted = false;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var bool
|
23 |
+
*/
|
24 |
+
private $released = false;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Set job
|
28 |
+
*
|
29 |
+
* @param $job
|
30 |
+
*/
|
31 |
+
public function set_job( $job ) {
|
32 |
+
$this->job = $job;
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Delete the job from the queue
|
37 |
+
*/
|
38 |
+
protected function delete() {
|
39 |
+
$this->deleted = true;
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Release a job back onto the queue
|
44 |
+
*
|
45 |
+
* @param int $delay
|
46 |
+
*/
|
47 |
+
protected function release( $delay = 0 ) {
|
48 |
+
$this->released = true;
|
49 |
+
$this->delay = $delay;
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Attempts
|
54 |
+
*
|
55 |
+
* @return int
|
56 |
+
*/
|
57 |
+
protected function attempts() {
|
58 |
+
return (int) $this->job->attempts;
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Is deleted.
|
63 |
+
*
|
64 |
+
* @return bool
|
65 |
+
*/
|
66 |
+
public function is_deleted() {
|
67 |
+
return $this->deleted;
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Is released.
|
72 |
+
*
|
73 |
+
* @return bool
|
74 |
+
*/
|
75 |
+
public function is_released() {
|
76 |
+
return $this->released;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Is deleted for released
|
81 |
+
*
|
82 |
+
* @return bool
|
83 |
+
*/
|
84 |
+
public function is_deleted_or_released() {
|
85 |
+
return $this->is_deleted() || $this->is_released();
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Get delay.
|
90 |
+
*
|
91 |
+
* @return int
|
92 |
+
*/
|
93 |
+
public function get_delay() {
|
94 |
+
return $this->delay;
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Handle the job.
|
99 |
+
*/
|
100 |
+
abstract public function handle();
|
101 |
+
|
102 |
+
}
|
103 |
+
}
|
includes/vendor/queue/classes/wp-queue.php
ADDED
@@ -0,0 +1,231 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if ( ! class_exists( 'WP_Queue' ) ) {
|
4 |
+
class WP_Queue {
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @var string
|
8 |
+
*/
|
9 |
+
public $table;
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var string
|
13 |
+
*/
|
14 |
+
public $failed_table;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var int
|
18 |
+
*/
|
19 |
+
public $release_time = 60;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* WP_Queue constructor
|
23 |
+
*/
|
24 |
+
public function __construct() {
|
25 |
+
global $wpdb;
|
26 |
+
|
27 |
+
$this->table = $wpdb->prefix . 'queue';
|
28 |
+
$this->failed_table = $wpdb->prefix . 'failed_jobs';
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Push a job onto the queue.
|
33 |
+
*
|
34 |
+
* @param WP_Job $job
|
35 |
+
* @param int $delay
|
36 |
+
*
|
37 |
+
* @return $this
|
38 |
+
*/
|
39 |
+
public function push( WP_Job $job, $delay = 0 ) {
|
40 |
+
global $wpdb;
|
41 |
+
|
42 |
+
$data = array(
|
43 |
+
'job' => maybe_serialize( $job ),
|
44 |
+
'available_at' => $this->datetime( $delay ),
|
45 |
+
'created_at' => $this->datetime(),
|
46 |
+
);
|
47 |
+
|
48 |
+
$id = $wpdb->insert( $this->table, $data );
|
49 |
+
|
50 |
+
return $this;
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Release.
|
55 |
+
*
|
56 |
+
* @param object $job
|
57 |
+
* @param int $delay
|
58 |
+
*/
|
59 |
+
public function release( $job, $delay = 0 ) {
|
60 |
+
if ( $job->attempts >= 3 ) {
|
61 |
+
$this->failed( $job );
|
62 |
+
|
63 |
+
return;
|
64 |
+
}
|
65 |
+
|
66 |
+
global $wpdb;
|
67 |
+
|
68 |
+
$data = array(
|
69 |
+
'attempts' => $job->attempts + 1,
|
70 |
+
'locked' => 0,
|
71 |
+
'locked_at' => null,
|
72 |
+
'available_at' => $this->datetime( $delay ),
|
73 |
+
);
|
74 |
+
$where = array(
|
75 |
+
'id' => $job->id,
|
76 |
+
);
|
77 |
+
|
78 |
+
$wpdb->update( $this->table, $data, $where );
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Failed
|
83 |
+
*
|
84 |
+
* @param stdClass $job
|
85 |
+
*/
|
86 |
+
protected function failed( $job ) {
|
87 |
+
global $wpdb;
|
88 |
+
|
89 |
+
$wpdb->insert( $this->failed_table, array(
|
90 |
+
'job' => $job->job,
|
91 |
+
'failed_at' => $this->datetime(),
|
92 |
+
) );
|
93 |
+
|
94 |
+
$payload = unserialize($job->job);
|
95 |
+
|
96 |
+
if (method_exists($payload, 'failed')) {
|
97 |
+
$payload->failed();
|
98 |
+
}
|
99 |
+
|
100 |
+
$this->delete( $job );
|
101 |
+
}
|
102 |
+
|
103 |
+
/**
|
104 |
+
* Delete.
|
105 |
+
*
|
106 |
+
* @param object $job
|
107 |
+
*/
|
108 |
+
public function delete( $job ) {
|
109 |
+
global $wpdb;
|
110 |
+
|
111 |
+
$where = array(
|
112 |
+
'id' => $job->id,
|
113 |
+
);
|
114 |
+
|
115 |
+
$wpdb->delete( $this->table, $where );
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Get MySQL datetime.
|
120 |
+
*
|
121 |
+
* @param int $offset Seconds, can pass negative int.
|
122 |
+
*
|
123 |
+
* @return string
|
124 |
+
*/
|
125 |
+
protected function datetime($offset = 0) {
|
126 |
+
$timestamp = time() + $offset;
|
127 |
+
|
128 |
+
return gmdate( 'Y-m-d H:i:s', $timestamp );
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Available jobs.
|
133 |
+
*/
|
134 |
+
public function available_jobs() {
|
135 |
+
global $wpdb;
|
136 |
+
|
137 |
+
$now = $this->datetime();
|
138 |
+
$sql = $wpdb->prepare( "
|
139 |
+
SELECT COUNT(*) FROM {$this->table}
|
140 |
+
WHERE available_at <= %s"
|
141 |
+
, $now );
|
142 |
+
|
143 |
+
return $wpdb->get_var( $sql );
|
144 |
+
}
|
145 |
+
|
146 |
+
/**
|
147 |
+
* Available jobs.
|
148 |
+
*/
|
149 |
+
public function failed_jobs() {
|
150 |
+
global $wpdb;
|
151 |
+
|
152 |
+
return $wpdb->get_var( "SELECT COUNT(*) FROM {$this->failed_table}" );
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* Restart failed jobs.
|
157 |
+
*/
|
158 |
+
public function restart_failed_jobs() {
|
159 |
+
global $wpdb;
|
160 |
+
|
161 |
+
$count = 0;
|
162 |
+
$jobs = $wpdb->get_results( "SELECT * FROM {$this->failed_table}" );
|
163 |
+
|
164 |
+
foreach ( $jobs as $job ) {
|
165 |
+
$this->push( maybe_unserialize( $job->job ) );
|
166 |
+
$wpdb->delete( $this->failed_table, array(
|
167 |
+
'id' => $job->id,
|
168 |
+
) );
|
169 |
+
|
170 |
+
$count++;
|
171 |
+
}
|
172 |
+
|
173 |
+
return $count;
|
174 |
+
}
|
175 |
+
|
176 |
+
/**
|
177 |
+
* Get next job.
|
178 |
+
*/
|
179 |
+
public function get_next_job() {
|
180 |
+
global $wpdb;
|
181 |
+
|
182 |
+
$this->maybe_release_locked_jobs();
|
183 |
+
|
184 |
+
$now = $this->datetime();
|
185 |
+
$sql = $wpdb->prepare( "
|
186 |
+
SELECT * FROM {$this->table}
|
187 |
+
WHERE locked = 0
|
188 |
+
AND available_at <= %s"
|
189 |
+
, $now );
|
190 |
+
|
191 |
+
return $wpdb->get_row( $sql );
|
192 |
+
}
|
193 |
+
|
194 |
+
/**
|
195 |
+
* Maybe release locked jobs.
|
196 |
+
*/
|
197 |
+
protected function maybe_release_locked_jobs() {
|
198 |
+
global $wpdb;
|
199 |
+
|
200 |
+
$expired = $this->datetime( - $this->release_time );
|
201 |
+
|
202 |
+
$sql = $wpdb->prepare( "
|
203 |
+
UPDATE {$this->table}
|
204 |
+
SET attempts = attempts + 1, locked = 0, locked_at = NULL
|
205 |
+
WHERE locked = 1
|
206 |
+
AND locked_at <= %s"
|
207 |
+
, $expired );
|
208 |
+
|
209 |
+
$wpdb->query( $sql );
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* Lock job.
|
214 |
+
*
|
215 |
+
* @param object $job
|
216 |
+
*/
|
217 |
+
public function lock_job( $job ) {
|
218 |
+
global $wpdb;
|
219 |
+
|
220 |
+
$data = array(
|
221 |
+
'locked' => 1,
|
222 |
+
'locked_at' => $this->datetime(),
|
223 |
+
);
|
224 |
+
$where = array(
|
225 |
+
'id' => $job->id,
|
226 |
+
);
|
227 |
+
|
228 |
+
$wpdb->update( $this->table, $data, $where );
|
229 |
+
}
|
230 |
+
}
|
231 |
+
}
|
index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden
|
languages/mailchimp-woocommerce.pot
ADDED
File without changes
|
mailchimp-woocommerce.php
ADDED
@@ -0,0 +1,274 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* The plugin bootstrap file
|
5 |
+
*
|
6 |
+
* This file is read by WordPress to generate the plugin information in the plugin
|
7 |
+
* admin area. This file also includes all of the dependencies used by the plugin,
|
8 |
+
* registers the activation and deactivation functions, and defines a function
|
9 |
+
* that starts the plugin.
|
10 |
+
*
|
11 |
+
* @link https://mailchimp.com
|
12 |
+
* @since 1.0.0
|
13 |
+
* @package MailChimp_Woocommerce
|
14 |
+
*
|
15 |
+
* @wordpress-plugin
|
16 |
+
* Plugin Name: MailChimp WooCommerce
|
17 |
+
* Plugin URI: https://mailchimp.com/connect-your-store/
|
18 |
+
* Description: MailChimp - WooCommerce plugin
|
19 |
+
* Version: 1.0.0
|
20 |
+
* Author: MailChimp
|
21 |
+
* Author URI: https://mailchimp.com
|
22 |
+
* License: GPL-2.0+
|
23 |
+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
|
24 |
+
* Text Domain: mailchimp-woocommerce
|
25 |
+
* Domain Path: /languages
|
26 |
+
*/
|
27 |
+
|
28 |
+
// If this file is called directly, abort.
|
29 |
+
if ( ! defined( 'WPINC' ) ) {
|
30 |
+
die;
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* @return object
|
35 |
+
*/
|
36 |
+
function mailchimp_environment_variables() {
|
37 |
+
return (object) array(
|
38 |
+
'repo' => 'master',
|
39 |
+
'environment' => 'production',
|
40 |
+
'version' => '0.1.11',
|
41 |
+
'slack_token' => false,
|
42 |
+
'slack_channel' => 'mc-woo',
|
43 |
+
);
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* @return string
|
48 |
+
*/
|
49 |
+
function mailchimp_get_store_id() {
|
50 |
+
return md5(get_option('siteurl'));
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
* @return bool|MailChimpApi
|
55 |
+
*/
|
56 |
+
function mailchimp_get_api() {
|
57 |
+
if (($options = get_option('mailchimp-woocommerce', false)) && is_array($options)) {
|
58 |
+
if (isset($options['mailchimp_api_key'])) {
|
59 |
+
return new MailChimpApi($options['mailchimp_api_key']);
|
60 |
+
}
|
61 |
+
}
|
62 |
+
return false;
|
63 |
+
}
|
64 |
+
|
65 |
+
/**
|
66 |
+
* @param $key
|
67 |
+
* @param null $default
|
68 |
+
* @return null
|
69 |
+
*/
|
70 |
+
function mailchimp_get_option($key, $default = null) {
|
71 |
+
$options = get_option('mailchimp-woocommerce');
|
72 |
+
if (!is_array($options)) {
|
73 |
+
return $default;
|
74 |
+
}
|
75 |
+
if (!array_key_exists($key, $options)) {
|
76 |
+
return $default;
|
77 |
+
}
|
78 |
+
return $options[$key];
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* @param $key
|
83 |
+
* @param $default
|
84 |
+
* @return mixed|void
|
85 |
+
*/
|
86 |
+
function mailchimp_get_data($key, $default) {
|
87 |
+
return get_option('mailchimp-woocommerce-'.$key, $default);
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* @param $date
|
92 |
+
* @return DateTime
|
93 |
+
*/
|
94 |
+
function mailchimp_date_utc($date) {
|
95 |
+
$timezone = wc_timezone_string();
|
96 |
+
//$timezone = mailchimp_get_option('store_timezone', 'America/New_York');
|
97 |
+
$date = new \DateTime($date, new DateTimeZone($timezone));
|
98 |
+
$date->setTimezone(new DateTimeZone('UTC'));
|
99 |
+
return $date;
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
* @param $date
|
104 |
+
* @return DateTime
|
105 |
+
*/
|
106 |
+
function mailchimp_date_local($date) {
|
107 |
+
$timezone = mailchimp_get_option('store_timezone', 'America/New_York');
|
108 |
+
$date = new \DateTime($date, new DateTimeZone('UTC'));
|
109 |
+
$date->setTimezone(new DateTimeZone($timezone));
|
110 |
+
return $date;
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* @param array $data
|
115 |
+
* @return mixed
|
116 |
+
*/
|
117 |
+
function mailchimp_array_remove_empty($data) {
|
118 |
+
if (empty($data) || !is_array($data)) {
|
119 |
+
return array();
|
120 |
+
}
|
121 |
+
foreach ($data as $key => $value) {
|
122 |
+
if ($value === null || $value === '') {
|
123 |
+
unset($data[$key]);
|
124 |
+
}
|
125 |
+
}
|
126 |
+
return $data;
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* @return array
|
131 |
+
*/
|
132 |
+
function mailchimp_get_timezone_list() {
|
133 |
+
$zones_array = array();
|
134 |
+
$timestamp = time();
|
135 |
+
$current = date_default_timezone_get();
|
136 |
+
|
137 |
+
foreach(timezone_identifiers_list() as $key => $zone) {
|
138 |
+
date_default_timezone_set($zone);
|
139 |
+
$zones_array[$key]['zone'] = $zone;
|
140 |
+
$zones_array[$key]['diff_from_GMT'] = 'UTC/GMT ' . date('P', $timestamp);
|
141 |
+
}
|
142 |
+
|
143 |
+
date_default_timezone_set($current);
|
144 |
+
|
145 |
+
return $zones_array;
|
146 |
+
}
|
147 |
+
|
148 |
+
/**
|
149 |
+
* The code that runs during plugin activation.
|
150 |
+
* This action is documented in includes/class-mailchimp-woocommerce-activator.php
|
151 |
+
*/
|
152 |
+
function activate_mailchimp_woocommerce() {
|
153 |
+
require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-woocommerce-activator.php';
|
154 |
+
|
155 |
+
MailChimp_Woocommerce_Activator::activate();
|
156 |
+
}
|
157 |
+
|
158 |
+
/**
|
159 |
+
* Create the queue tables
|
160 |
+
*/
|
161 |
+
function install_mailchimp_queue()
|
162 |
+
{
|
163 |
+
require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-woocommerce-activator.php';
|
164 |
+
MailChimp_Woocommerce_Activator::create_queue_tables();
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* The code that runs during plugin deactivation.
|
169 |
+
* This action is documented in includes/class-mailchimp-woocommerce-deactivator.php
|
170 |
+
*/
|
171 |
+
function deactivate_mailchimp_woocommerce() {
|
172 |
+
require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-woocommerce-deactivator.php';
|
173 |
+
MailChimp_Woocommerce_Deactivator::deactivate();
|
174 |
+
}
|
175 |
+
|
176 |
+
/**
|
177 |
+
* See if we need to run any updates.
|
178 |
+
*/
|
179 |
+
function run_mailchimp_plugin_updater() {
|
180 |
+
if (!class_exists('PucFactory')) {
|
181 |
+
require plugin_dir_path( __FILE__ ) . 'includes/plugin-update-checker/plugin-update-checker.php';
|
182 |
+
}
|
183 |
+
|
184 |
+
$env = mailchimp_environment_variables();
|
185 |
+
|
186 |
+
/** @var \PucGitHubChecker_3_1 $checker */
|
187 |
+
$updater = PucFactory::getLatestClassVersion('PucGitHubChecker');
|
188 |
+
$checker = new $updater('https://github.com/mailchimp/mc-woocommerce/', __FILE__, $env->repo, 1);
|
189 |
+
$checker->handleManualCheck();
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* @return \Frlnc\Slack\Logger
|
194 |
+
*/
|
195 |
+
function slack()
|
196 |
+
{
|
197 |
+
return Frlnc\Slack\Logger::instance();
|
198 |
+
}
|
199 |
+
|
200 |
+
/**
|
201 |
+
* @param $action
|
202 |
+
* @param $message
|
203 |
+
* @param array $data
|
204 |
+
* @return array|WP_Error
|
205 |
+
*/
|
206 |
+
function mailchimp_log($action, $message, $data = array())
|
207 |
+
{
|
208 |
+
$options = MailChimp_Woocommerce::getLoggingConfig();
|
209 |
+
|
210 |
+
if (!$options->enable_logging || !$options->account_id || !$options->username) {
|
211 |
+
return false;
|
212 |
+
}
|
213 |
+
|
214 |
+
$data = array(
|
215 |
+
'account_id' => $options->account_id,
|
216 |
+
'username' => $options->username,
|
217 |
+
'store_domain' => site_url(),
|
218 |
+
'platform' => 'woocommerce',
|
219 |
+
'action' => $action,
|
220 |
+
'message' => $message,
|
221 |
+
'data' => $data,
|
222 |
+
);
|
223 |
+
|
224 |
+
$slack_message = "$action :: $message";
|
225 |
+
|
226 |
+
if (!empty($data['data'])) {
|
227 |
+
$slack_message .= "\n\n".(print_r($data['data'], true));
|
228 |
+
}
|
229 |
+
|
230 |
+
slack()->notice($slack_message);
|
231 |
+
|
232 |
+
return wp_remote_post($options->endpoint, array(
|
233 |
+
'headers' => array(
|
234 |
+
'Accept: application/json',
|
235 |
+
'Content-Type: application/json'
|
236 |
+
),
|
237 |
+
'body' => json_encode($data),
|
238 |
+
));
|
239 |
+
}
|
240 |
+
|
241 |
+
register_activation_hook( __FILE__, 'activate_mailchimp_woocommerce' );
|
242 |
+
register_deactivation_hook( __FILE__, 'deactivate_mailchimp_woocommerce' );
|
243 |
+
|
244 |
+
/**
|
245 |
+
* The core plugin class that is used to define internationalization,
|
246 |
+
* admin-specific hooks, and public-facing site hooks.
|
247 |
+
*/
|
248 |
+
require plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-woocommerce.php';
|
249 |
+
|
250 |
+
/**
|
251 |
+
* Begins execution of the plugin.
|
252 |
+
*
|
253 |
+
* Since everything within the plugin is registered via hooks,
|
254 |
+
* then kicking off the plugin from this point in the file does
|
255 |
+
* not affect the page life cycle.
|
256 |
+
*
|
257 |
+
* @since 1.0.0
|
258 |
+
*/
|
259 |
+
function run_mailchimp_woocommerce() {
|
260 |
+
$env = mailchimp_environment_variables();
|
261 |
+
$plugin = new MailChimp_Woocommerce($env->environment, $env->version);
|
262 |
+
$plugin->run();
|
263 |
+
}
|
264 |
+
|
265 |
+
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
266 |
+
$forwarded_address = explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']);
|
267 |
+
$_SERVER['REMOTE_ADDR'] = $forwarded_address[0];
|
268 |
+
}
|
269 |
+
|
270 |
+
/** Add the plugin updater function ONLY when they are logged in as admin. */
|
271 |
+
add_action('admin_init', 'run_mailchimp_plugin_updater');
|
272 |
+
|
273 |
+
/** Add all the MailChimp hooks. */
|
274 |
+
run_mailchimp_woocommerce();
|
public/class-mailchimp-woocommerce-public.php
ADDED
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* The public-facing functionality of the plugin.
|
5 |
+
*
|
6 |
+
* @link https://mailchimp.com
|
7 |
+
* @since 1.0.1
|
8 |
+
*
|
9 |
+
* @package MailChimp_Woocommerce
|
10 |
+
* @subpackage MailChimp_Woocommerce/public
|
11 |
+
*/
|
12 |
+
|
13 |
+
/**
|
14 |
+
* The public-facing functionality of the plugin.
|
15 |
+
*
|
16 |
+
* Defines the plugin name, version, and two examples hooks for how to
|
17 |
+
* enqueue the admin-specific stylesheet and JavaScript.
|
18 |
+
*
|
19 |
+
* @package MailChimp_Woocommerce
|
20 |
+
* @subpackage MailChimp_Woocommerce/public
|
21 |
+
* @author Ryan Hungate <ryan@mailchimp.com>
|
22 |
+
*/
|
23 |
+
class MailChimp_Woocommerce_Public {
|
24 |
+
|
25 |
+
/**
|
26 |
+
* The ID of this plugin.
|
27 |
+
*
|
28 |
+
* @since 1.0.0
|
29 |
+
* @access private
|
30 |
+
* @var string $plugin_name The ID of this plugin.
|
31 |
+
*/
|
32 |
+
private $plugin_name;
|
33 |
+
|
34 |
+
/**
|
35 |
+
* The version of this plugin.
|
36 |
+
*
|
37 |
+
* @since 1.0.0
|
38 |
+
* @access private
|
39 |
+
* @var string $version The current version of this plugin.
|
40 |
+
*/
|
41 |
+
private $version;
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Initialize the class and set its properties.
|
45 |
+
*
|
46 |
+
* @since 1.0.0
|
47 |
+
* @param string $plugin_name The name of the plugin.
|
48 |
+
* @param string $version The version of this plugin.
|
49 |
+
*/
|
50 |
+
public function __construct( $plugin_name, $version ) {
|
51 |
+
|
52 |
+
$this->plugin_name = $plugin_name;
|
53 |
+
$this->version = $version;
|
54 |
+
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Register the stylesheets for the public-facing side of the site.
|
59 |
+
*
|
60 |
+
* @since 1.0.0
|
61 |
+
*/
|
62 |
+
public function enqueue_styles() {
|
63 |
+
|
64 |
+
/**
|
65 |
+
* This function is provided for demonstration purposes only.
|
66 |
+
*
|
67 |
+
* An instance of this class should be passed to the run() function
|
68 |
+
* defined in MailChimp_Woocommerce_Loader as all of the hooks are defined
|
69 |
+
* in that particular class.
|
70 |
+
*
|
71 |
+
* The MailChimp_Woocommerce_Loader will then create the relationship
|
72 |
+
* between the defined hooks and the functions defined in this
|
73 |
+
* class.
|
74 |
+
*/
|
75 |
+
|
76 |
+
wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/mailchimp-woocommerce-public.css', array(), $this->version, 'all' );
|
77 |
+
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Register the JavaScript for the public-facing side of the site.
|
82 |
+
*
|
83 |
+
* @since 1.0.0
|
84 |
+
*/
|
85 |
+
public function enqueue_scripts() {
|
86 |
+
|
87 |
+
/**
|
88 |
+
* This function is provided for demonstration purposes only.
|
89 |
+
*
|
90 |
+
* An instance of this class should be passed to the run() function
|
91 |
+
* defined in MailChimp_Woocommerce_Loader as all of the hooks are defined
|
92 |
+
* in that particular class.
|
93 |
+
*
|
94 |
+
* The MailChimp_Woocommerce_Loader will then create the relationship
|
95 |
+
* between the defined hooks and the functions defined in this
|
96 |
+
* class.
|
97 |
+
*/
|
98 |
+
|
99 |
+
wp_register_script($this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/mailchimp-woocommerce-public.js', array( 'jquery' ), $this->version, false);
|
100 |
+
|
101 |
+
wp_localize_script($this->plugin_name, 'public_data', array(
|
102 |
+
'site_url' => site_url(),
|
103 |
+
));
|
104 |
+
|
105 |
+
// Enqueued script with localized data.
|
106 |
+
wp_enqueue_script($this->plugin_name);
|
107 |
+
|
108 |
+
}
|
109 |
+
}
|
public/css/mailchimp-woocommerce-public.css
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* All of the CSS for your public-facing functionality should be
|
3 |
+
* included in this file.
|
4 |
+
*/
|
public/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden
|
public/js/mailchimp-woocommerce-public.js
ADDED
@@ -0,0 +1,312 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
var mailchimp;
|
2 |
+
var mailchimp_cart;
|
3 |
+
var mailchimp_public_data;
|
4 |
+
|
5 |
+
(function( $ ) {
|
6 |
+
'use strict';
|
7 |
+
|
8 |
+
/**
|
9 |
+
* All of the code for your public-facing JavaScript source
|
10 |
+
* should reside in this file.
|
11 |
+
*
|
12 |
+
* Note: It has been assumed you will write jQuery code here, so the
|
13 |
+
* $ function reference has been prepared for usage within the scope
|
14 |
+
* of this function.
|
15 |
+
*
|
16 |
+
* This enables you to define handlers, for when the DOM is ready:
|
17 |
+
*
|
18 |
+
* $(function() {
|
19 |
+
*
|
20 |
+
* });
|
21 |
+
*
|
22 |
+
* When the window is loaded:
|
23 |
+
*
|
24 |
+
* $( window ).load(function() {
|
25 |
+
*
|
26 |
+
* });
|
27 |
+
*
|
28 |
+
* ...and/or other possibilities.
|
29 |
+
*
|
30 |
+
* Ideally, it is not considered best practise to attach more than a
|
31 |
+
* single DOM-ready or window-load handler for a particular page.
|
32 |
+
* Although scripts in the WordPress core, Plugins and Themes may be
|
33 |
+
* practising this, we should strive to set a better example in our own work.
|
34 |
+
*/
|
35 |
+
|
36 |
+
var requestTransport = null;
|
37 |
+
var scriptTagCounter = 1, head;
|
38 |
+
var storageLife = "30";
|
39 |
+
var clientIP = null;
|
40 |
+
var saved_ip;
|
41 |
+
var script;
|
42 |
+
|
43 |
+
mailchimp_public_data = public_data || {site_url:document.location.origin};
|
44 |
+
|
45 |
+
function invokeJsonp(fullUrl, cacheOk)
|
46 |
+
{
|
47 |
+
var c = cacheOk || true;
|
48 |
+
script = buildScriptTag(fullUrl, c);
|
49 |
+
if (typeof head != 'object') {
|
50 |
+
head = document.getElementsByTagName("head").item(0);
|
51 |
+
}
|
52 |
+
head.appendChild(script);
|
53 |
+
return script;
|
54 |
+
}
|
55 |
+
|
56 |
+
function removeTag(tag)
|
57 |
+
{
|
58 |
+
if (typeof head != 'object') {
|
59 |
+
head = document.getElementsByTagName("head").item(0);
|
60 |
+
}
|
61 |
+
head.removeChild(script);
|
62 |
+
}
|
63 |
+
|
64 |
+
function buildScriptTag(url, cacheOk)
|
65 |
+
{
|
66 |
+
var element = document.createElement("script"),
|
67 |
+
additionalQueryParams, conjunction,
|
68 |
+
actualUrl = url,
|
69 |
+
elementId = 'jsonp-script-' + scriptTagCounter++;
|
70 |
+
if (!cacheOk) {
|
71 |
+
additionalQueryParams = '_=' + (new Date()).getTime();
|
72 |
+
conjunction = (url.indexOf('?') == -1) ? '?' : '&';
|
73 |
+
actualUrl = url + conjunction + additionalQueryParams;
|
74 |
+
}
|
75 |
+
element.setAttribute("type", "text/javascript");
|
76 |
+
element.setAttribute("src", actualUrl);
|
77 |
+
element.setAttribute("id", elementId);
|
78 |
+
return element;
|
79 |
+
}
|
80 |
+
|
81 |
+
var mailchimpUtils =
|
82 |
+
{
|
83 |
+
extend:function (e, t) {
|
84 |
+
for (var n in t || {}) {
|
85 |
+
if (t.hasOwnProperty(n)) {
|
86 |
+
e[n] = t[n]
|
87 |
+
}
|
88 |
+
}
|
89 |
+
return e
|
90 |
+
},
|
91 |
+
getQueryStringVars:function ()
|
92 |
+
{
|
93 |
+
var e = window.location.search || "";
|
94 |
+
var t = [];
|
95 |
+
var n = {};
|
96 |
+
e = e.substr(1);
|
97 |
+
if (e.length) {
|
98 |
+
t = e.split("&");
|
99 |
+
for (var r in t)
|
100 |
+
{
|
101 |
+
var i = t[r];
|
102 |
+
if(typeof i !== 'string'){continue;}
|
103 |
+
var s = i.split("=");
|
104 |
+
var o = s[0];
|
105 |
+
var u = s[1];
|
106 |
+
if (!o.length)continue;
|
107 |
+
if (typeof n[o] === "undefined") {
|
108 |
+
n[o] = []
|
109 |
+
}
|
110 |
+
n[o].push(u)
|
111 |
+
}
|
112 |
+
}
|
113 |
+
return n
|
114 |
+
},
|
115 |
+
unEscape:function (e) {
|
116 |
+
return decodeURIComponent(e)
|
117 |
+
},
|
118 |
+
escape:function (e) {
|
119 |
+
return encodeURIComponent(e)
|
120 |
+
},
|
121 |
+
createDate:function (e, t) {
|
122 |
+
if (!e) {
|
123 |
+
e = 0
|
124 |
+
}
|
125 |
+
var n = new Date;
|
126 |
+
var r = t ? n.getDate() - e : n.getDate() + e;
|
127 |
+
n.setDate(r);
|
128 |
+
return n
|
129 |
+
},
|
130 |
+
arrayUnique:function (e) {
|
131 |
+
var t = e.concat();
|
132 |
+
for (var n = 0; n < t.length; ++n) {
|
133 |
+
for (var r = n + 1; r < t.length; ++r) {
|
134 |
+
if (t[n] === t[r]) {
|
135 |
+
t.splice(r, 1)
|
136 |
+
}
|
137 |
+
}
|
138 |
+
}
|
139 |
+
return t
|
140 |
+
},
|
141 |
+
objectCombineUnique:function (e) {
|
142 |
+
var t = e[0];
|
143 |
+
for (var n = 1; n < e.length; n++) {
|
144 |
+
var r = e[n];
|
145 |
+
for (var i in r) {
|
146 |
+
t[i] = r[i]
|
147 |
+
}
|
148 |
+
}
|
149 |
+
return t
|
150 |
+
}
|
151 |
+
};
|
152 |
+
|
153 |
+
var mailchimpStorage = function(e, t)
|
154 |
+
{
|
155 |
+
var n = function (e, t, r) {
|
156 |
+
return 1 === arguments.length ? n.get(e) : n.set(e, t, r)
|
157 |
+
};
|
158 |
+
n.get = function (t, r) {
|
159 |
+
e.cookie !== n._cacheString && n._populateCache();
|
160 |
+
return n._cache[t] == undefined ? r : n._cache[t]
|
161 |
+
};
|
162 |
+
n.defaults = {path:"/"};
|
163 |
+
n.set = function (r, i, s) {
|
164 |
+
s = {path:s && s.path || n.defaults.path, domain:s && s.domain || n.defaults.domain, expires:s && s.expires || n.defaults.expires, secure:s && s.secure !== t ? s.secure : n.defaults.secure};
|
165 |
+
i === t && (s.expires = -1);
|
166 |
+
switch (typeof s.expires) {
|
167 |
+
case"number":
|
168 |
+
s.expires = new Date((new Date).getTime() + 1e3 * s.expires);
|
169 |
+
break;
|
170 |
+
case"string":
|
171 |
+
s.expires = new Date(s.expires)
|
172 |
+
}
|
173 |
+
r = encodeURIComponent(r) + "=" + (i + "").replace(/[^!#-+\--:<-\[\]-~]/g, encodeURIComponent);
|
174 |
+
r += s.path ? ";path=" + s.path : "";
|
175 |
+
r += s.domain ? ";domain=" + s.domain : "";
|
176 |
+
r += s.expires ? ";expires=" + s.expires.toGMTString() : "";
|
177 |
+
r += s.secure ? ";secure" : "";
|
178 |
+
e.cookie = r;
|
179 |
+
return n
|
180 |
+
};
|
181 |
+
n.expire = function (e, r) {
|
182 |
+
return n.set(e, t, r)
|
183 |
+
};
|
184 |
+
n._populateCache = function () {
|
185 |
+
n._cache = {};
|
186 |
+
try {
|
187 |
+
n._cacheString = e.cookie;
|
188 |
+
for (var r = n._cacheString.split("; "), i = 0; i < r.length; i++) {
|
189 |
+
var s = r[i].indexOf("="), o = decodeURIComponent(r[i].substr(0, s)), s = decodeURIComponent(r[i].substr(s + 1));
|
190 |
+
n._cache[o] === t && (n._cache[o] = s)
|
191 |
+
}
|
192 |
+
} catch (e) {
|
193 |
+
console.log(e);
|
194 |
+
}
|
195 |
+
};
|
196 |
+
n.enabled = function () {
|
197 |
+
var e = "1" === n.set("cookies.js", "1").get("cookies.js");
|
198 |
+
n.expire("cookies.js");
|
199 |
+
return e
|
200 |
+
}();
|
201 |
+
return n;
|
202 |
+
}(document);
|
203 |
+
|
204 |
+
var Jsonp = {invoke : invokeJsonp, removeTag: removeTag};
|
205 |
+
|
206 |
+
mailchimp =
|
207 |
+
{
|
208 |
+
storage : mailchimpStorage,
|
209 |
+
utils : mailchimpUtils
|
210 |
+
};
|
211 |
+
|
212 |
+
function MailChimpCart() {
|
213 |
+
|
214 |
+
this.email_types = "input[type=email]";
|
215 |
+
this.regex_email = /^([A-Za-z0-9_+\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
|
216 |
+
this.current_email = null;
|
217 |
+
this.previous_email = null;
|
218 |
+
|
219 |
+
this.expireUser = function () {
|
220 |
+
this.current_email = null;
|
221 |
+
mailchimp.storage.expire('mailchimp.cart.current_email');
|
222 |
+
};
|
223 |
+
|
224 |
+
this.expireSaved = function () {
|
225 |
+
mailchimp.storage.expire('mailchimp.cart.items');
|
226 |
+
};
|
227 |
+
|
228 |
+
this.setEmail = function (email) {
|
229 |
+
if (this.valueEmail(email)) {
|
230 |
+
this.setPreviousEmail(this.getEmail());
|
231 |
+
this.current_email = email;
|
232 |
+
mailchimp.storage.set('mailchimp.cart.current_email', email);
|
233 |
+
}
|
234 |
+
};
|
235 |
+
this.getEmail = function () {
|
236 |
+
if (this.current_email) {
|
237 |
+
return this.current_email;
|
238 |
+
}
|
239 |
+
var current_email = mailchimp.storage.get('mailchimp.cart.current_email', false);
|
240 |
+
if (!current_email || !this.valueEmail(current_email)) {
|
241 |
+
return false;
|
242 |
+
}
|
243 |
+
this.current_email = current_email;
|
244 |
+
return current_email;
|
245 |
+
};
|
246 |
+
this.setPreviousEmail = function (prev_email) {
|
247 |
+
if (this.valueEmail(prev_email)) {
|
248 |
+
mailchimp.storage.set('mailchimp.cart.previous_email', prev_email);
|
249 |
+
this.previous_email = prev_email;
|
250 |
+
}
|
251 |
+
};
|
252 |
+
this.valueEmail = function (email) {
|
253 |
+
return this.regex_email.test(email);
|
254 |
+
};
|
255 |
+
|
256 |
+
$(document).on("blur", "#billing_email",function() {
|
257 |
+
var user = $("#billing_email").val();
|
258 |
+
if (!mailchimp_cart.valueEmail(user)) {
|
259 |
+
return false;
|
260 |
+
}
|
261 |
+
mailchimp_cart.setEmail(user);
|
262 |
+
|
263 |
+
$.ajax({
|
264 |
+
beforeSend: function (xhrObj) {
|
265 |
+
xhrObj.setRequestHeader("Content-Type", "application/json");
|
266 |
+
xhrObj.setRequestHeader("Accept", "application/json");
|
267 |
+
},
|
268 |
+
crossDomain: true,
|
269 |
+
dataType: "json",
|
270 |
+
type: 'POST',
|
271 |
+
url: mailchimp_public_data.site_url+'?mailchimp-woocommerce[action]=submit-email&mailchimp-woocommerce[submission][email]='+user,
|
272 |
+
data: {},
|
273 |
+
success: function (responseData, textStatus, jqXHR) {
|
274 |
+
console.log('email saved', responseData);
|
275 |
+
},
|
276 |
+
error: function (responseData, textStatus, errorThrown) {
|
277 |
+
mailchimp_cart.post_error = errorThrown;
|
278 |
+
console.log('error while saving email', responseData);
|
279 |
+
}
|
280 |
+
});
|
281 |
+
});
|
282 |
+
|
283 |
+
return this;
|
284 |
+
}
|
285 |
+
|
286 |
+
mailchimp_cart = new MailChimpCart();
|
287 |
+
|
288 |
+
var qsc = mailchimpUtils.getQueryStringVars();
|
289 |
+
|
290 |
+
// MailChimp Data //
|
291 |
+
if (qsc.mc_cid !== undefined && qsc.mc_eid !== undefined) {
|
292 |
+
$.ajax({
|
293 |
+
beforeSend: function (xhrObj) {
|
294 |
+
xhrObj.setRequestHeader("Content-Type", "application/json");
|
295 |
+
xhrObj.setRequestHeader("Accept", "application/json");
|
296 |
+
},
|
297 |
+
crossDomain: true,
|
298 |
+
dataType: "json",
|
299 |
+
type: 'POST',
|
300 |
+
url: mailchimp_public_data.site_url+'?mailchimp-woocommerce[action]=track-campaign&mailchimp-woocommerce[submission][campaign_id]='+qsc.mc_cid[0]+'&mailchimp-woocommerce[submission][email_id]='+qsc.mc_eid[0],
|
301 |
+
data: {},
|
302 |
+
success: function (responseData, textStatus, jqXHR) {
|
303 |
+
console.log('campaign data saved', responseData);
|
304 |
+
},
|
305 |
+
error: function (responseData, textStatus, errorThrown) {
|
306 |
+
mailchimp_cart.post_error = errorThrown;
|
307 |
+
console.log('error while saving campaign data', responseData);
|
308 |
+
}
|
309 |
+
});
|
310 |
+
}
|
311 |
+
|
312 |
+
})( jQuery );
|
public/partials/mailchimp-woocommerce-public-display.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Provide a public-facing view for the plugin
|
5 |
+
*
|
6 |
+
* This file is used to markup the public-facing aspects of the plugin.
|
7 |
+
*
|
8 |
+
* @link https://mailchimp.com
|
9 |
+
* @since 1.0.1
|
10 |
+
*
|
11 |
+
* @package MailChimp_Woocommerce
|
12 |
+
* @subpackage MailChimp_Woocommerce/public/partials
|
13 |
+
*/
|
14 |
+
?>
|
15 |
+
|
16 |
+
<!-- This file should primarily consist of HTML with a little bit of PHP. -->
|
uninstall.php
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Fired when the plugin is uninstalled.
|
5 |
+
*
|
6 |
+
* When populating this file, consider the following flow
|
7 |
+
* of control:
|
8 |
+
*
|
9 |
+
* - This method should be static
|
10 |
+
* - Check if the $_REQUEST content actually is the plugin name
|
11 |
+
* - Run an admin referrer check to make sure it goes through authentication
|
12 |
+
* - Verify the output of $_GET makes sense
|
13 |
+
* - Repeat with other user roles. Best directly by using the links/query string parameters.
|
14 |
+
* - Repeat things for multisite. Once for a single site in the network, once sitewide.
|
15 |
+
*
|
16 |
+
* This file may be updated more in future version of the Boilerplate; however, this is the
|
17 |
+
* general skeleton and outline for how the file should work.
|
18 |
+
*
|
19 |
+
* For more information, see the following discussion:
|
20 |
+
* https://github.com/tommcfarlin/WordPress-Plugin-Boilerplate/pull/123#issuecomment-28541913
|
21 |
+
*
|
22 |
+
* @link https://mailchimp.com
|
23 |
+
* @since 1.0.1
|
24 |
+
*
|
25 |
+
* @package MailChimp_Woocommerce
|
26 |
+
*/
|
27 |
+
|
28 |
+
// If uninstall not called from WordPress, then exit.
|
29 |
+
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
|
30 |
+
exit;
|
31 |
+
}
|
32 |
+
|
33 |
+
delete_option('mailchimp-woocommerce');
|
34 |
+
delete_option('mailchimp-woocommerce-errors.store_info');
|
35 |
+
delete_option('mailchimp-woocommerce-sync.orders.completed_at');
|
36 |
+
delete_option('mailchimp-woocommerce-sync.orders.current_page');
|
37 |
+
delete_option('mailchimp-woocommerce-sync.products.completed_at');
|
38 |
+
delete_option('mailchimp-woocommerce-sync.products.current_page');
|
39 |
+
delete_option('mailchimp-woocommerce-sync.syncing');
|
40 |
+
delete_option('mailchimp-woocommerce-sync.started_at');
|
41 |
+
delete_option('mailchimp-woocommerce-sync.completed_at');
|
42 |
+
delete_option('mailchimp-woocommerce-validation.api.ping');
|
43 |
+
delete_option('mailchimp-woocommerce-cached-api-lists');
|
44 |
+
delete_option('mailchimp-woocommerce-cached-api-ping-check');
|