initial docker setup

This commit is contained in:
GotPPay
2018-06-14 16:49:28 +02:00
parent bc80b7342e
commit b5f87f27f8
3023 changed files with 985078 additions and 1 deletions

View File

@@ -0,0 +1,34 @@
# Only allow direct access to specific Web-available files.
# Apache 2.2
<IfModule !mod_authz_core.c>
Order Deny,Allow
Deny from all
</IfModule>
# Apache 2.4
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
# Akismet CSS and JS
<FilesMatch "^(form\.js|akismet\.js|akismet\.css)$">
<IfModule !mod_authz_core.c>
Allow from all
</IfModule>
<IfModule mod_authz_core.c>
Require all granted
</IfModule>
</FilesMatch>
# Akismet images
<FilesMatch "^logo-full-2x\.png$">
<IfModule !mod_authz_core.c>
Allow from all
</IfModule>
<IfModule mod_authz_core.c>
Require all granted
</IfModule>
</FilesMatch>

View File

@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@@ -0,0 +1,586 @@
.wp-admin.jetpack_page_akismet-key-config, .wp-admin.settings_page_akismet-key-config {
background-color:#f3f6f8;
}
#submitted-on {
position: relative;
}
#the-comment-list .author .akismet-user-comment-count {
display: inline;
}
#the-comment-list .author a span {
text-decoration: none;
color: #999;
}
#the-comment-list .author a span.akismet-span-link {
text-decoration: inherit;
color: inherit;
}
#the-comment-list .akismet_remove_url {
margin-left: 3px;
color: #999;
padding: 2px 3px 2px 0;
}
#the-comment-list .akismet_remove_url:hover {
color: #A7301F;
font-weight: bold;
padding: 2px 2px 2px 0;
}
#dashboard_recent_comments .akismet-status {
display: none;
}
.akismet-status {
float: right;
}
.akismet-status a {
color: #AAA;
font-style: italic;
}
table.comments td.comment p a {
text-decoration: underline;
}
table.comments td.comment p a:after {
content: attr(href);
color: #aaa;
display: inline-block; /* Show the URL without the link's underline extending under it. */
padding: 0 1ex; /* Because it's inline block, we can't just use spaces in the content: attribute to separate it from the link text. */
}
.mshot-arrow {
width: 0;
height: 0;
border-top: 10px solid transparent;
border-bottom: 10px solid transparent;
border-right: 10px solid #5C5C5C;
position: absolute;
left: -6px;
top: 91px;
}
.mshot-container {
background: #5C5C5C;
position: absolute;
top: -94px;
padding: 7px;
width: 450px;
height: 338px;
z-index: 20000;
-moz-border-radius: 6px;
border-radius: 6px;
-webkit-border-radius: 6px;
}
.akismet-mshot {
position: absolute;
z-index: 100;
}
.akismet-mshot .mshot-image {
margin: 0;
height: 338px;
width: 450px;
}
.checkforspam {
display: inline-block !important;
}
.checkforspam-progress {
padding-left: 1ex;
display: none;
}
.checkforspam.button-disabled .checkforspam-progress {
display: inline;
}
.checkforspam-spinner {
display: inline-block;
margin-top: 7px;
}
.akismet-right {
float: right;
}
.akismet-card .akismet-right {
margin: 1em 0;
}
.akismet-alert-text {
color: #dd3d36;
font-weight: bold;
font-size: 120%;
margin-top: .5rem;
}
.akismet-alert {
border: 1px solid #e5e5e5;
padding: 0.4em 1em 1.4em 1em;
border-radius: 3px;
-webkit-border-radius: 3px;
border-width: 1px;
border-style: solid;
}
.akismet-alert h3.akismet-key-status {
color: #fff;
margin: 1em 0 0.5em 0;
}
.akismet-alert.akismet-critical {
background-color: #993300;
}
.akismet-alert.akismet-active {
background-color: #649316;
}
.akismet-alert p.akismet-key-status {
font-size: 24px;
}
.akismet-alert p.akismet-description {
color:#fff;
font-size: 14px;
margin: 0 0;
font-style: normal;
}
.akismet-alert p.akismet-description a,
.akismet-alert p.akismet-description a,
.akismet-alert p.akismet-description a,
.akismet-alert p.akismet-description a {
color: #fff;
}
.akismet-new-snapshot {
margin-top: 1em;
padding: 1em;
text-align: center;
background: #fff;
}
.akismet-new-snapshot h3 {
background: #f5f5f5;
color: #888;
font-size: 11px;
margin: 0;
padding: 3px;
}
.new-snapspot ul {
font-size: 12px;
width: 100%;
}
.akismet-new-snapshot ul li {
color: #999;
float: left;
font-size: 11px;
padding: 0 20px;
text-transform: uppercase;
width: 33%;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
-ms-box-sizing: border-box;
}
.akismet-new-snapshot ul li:first-child,
.akismet-new-snapshot ul li:nth-child(2) {
border-right:1px dotted #ccc;
}
.akismet-new-snapshot ul li span {
color: #52accc;
display: block;
font-size: 32px;
font-weight: lighter;
line-height: 1.5em;
}
.akismet-settings th:first-child {
vertical-align: top;
padding-top: 15px;
}
.akismet-settings th.akismet-api-key {
vertical-align: middle;
padding-top: 0;
}
.akismet-settings input[type=text] {
width: 75%;
}
.akismet-settings span.akismet-note{
float: left;
padding-left: 23px;
font-size: 75%;
margin-top: -10px;
}
/**
* For the activation notice on the plugins page.
*/
.akismet_activate {
min-width: 825px;
border: 1px solid #4F800D;
padding: 5px;
margin: 15px 0;
background: #83AF24;
background-image: -webkit-gradient(linear, 0% 0, 80% 100%, from(#83AF24), to(#4F800D));
background-image: -moz-linear-gradient(80% 100% 120deg, #4F800D, #83AF24);
-moz-border-radius: 3px;
border-radius: 3px;
-webkit-border-radius: 3px;
position: relative;
overflow: hidden;
}
.akismet_activate .aa_a {
position: absolute;
top: -5px;
right: 10px;
font-size: 140px;
color: #769F33;
font-family: Georgia, "Times New Roman", Times, serif;
z-index: 1;
}
.akismet_activate .aa_button {
font-weight: bold;
border: 1px solid #029DD6;
border-top: 1px solid #06B9FD;
font-size: 15px;
text-align: center;
padding: 9px 0 8px 0;
color: #FFF;
background: #029DD6;
background-image: -webkit-gradient(linear, 0% 0, 0% 100%, from(#029DD6), to(#0079B1));
background-image: -moz-linear-gradient(0% 100% 90deg, #0079B1, #029DD6);
-moz-border-radius: 2px;
border-radius: 2px;
-webkit-border-radius: 2px;
width: 100%;
cursor: pointer;
margin: 0;
}
.akismet_activate .aa_button:hover {
text-decoration: none !important;
border: 1px solid #029DD6;
border-bottom: 1px solid #00A8EF;
font-size: 15px;
text-align: center;
padding: 9px 0 8px 0;
color: #F0F8FB;
background: #0079B1;
background-image: -webkit-gradient(linear, 0% 0, 0% 100%, from(#0079B1), to(#0092BF));
background-image: -moz-linear-gradient(0% 100% 90deg, #0092BF, #0079B1);
-moz-border-radius: 2px;
border-radius: 2px;
-webkit-border-radius: 2px;
}
.akismet_activate .aa_button_border {
border: 1px solid #006699;
-moz-border-radius: 2px;
border-radius: 2px;
-webkit-border-radius: 2px;
background: #029DD6;
background-image: -webkit-gradient(linear, 0% 0, 0% 100%, from(#029DD6), to(#0079B1));
background-image: -moz-linear-gradient(0% 100% 90deg, #0079B1, #029DD6);
}
.akismet_activate .aa_button_container {
display: inline-block;
background: #DEF1B8;
padding: 5px;
-moz-border-radius: 2px;
border-radius: 2px;
-webkit-border-radius: 2px;
width: 266px;
}
.akismet_activate .aa_description {
position: absolute;
top: 22px;
left: 285px;
margin-left: 25px;
color: #E5F2B1;
font-size: 15px;
z-index: 1000;
}
.akismet_activate .aa_description strong {
color: #FFF;
font-weight: normal;
}
.jetpack_page_akismet-key-config #wpcontent, .settings_page_akismet-key-config #wpcontent {
padding-left: 0;
}
.akismet-masthead {
background-color:#fff;
text-align:center;
box-shadow:0 1px 0 rgba(200,215,225,0.5),0 1px 2px #e9eff3
}
@media (max-width: 45rem) {
.akismet-masthead {
padding:0 1.25rem
}
}
.akismet-masthead__inside-container {
padding:.375rem 0;
margin:0 auto;
width:100%;
max-width:45rem;
text-align: left;
}
.akismet-masthead__logo-container {
padding:.3125rem 0 0
}
.akismet-masthead__logo {
width:10.375rem;
height:1.8125rem;
}
.akismet-masthead__logo-link {
display:inline-block;
outline:none;
vertical-align:middle
}
.akismet-masthead__logo-link:focus {
line-height:0;
box-shadow:0 0 0 2px #78dcfa
}
.akismet-masthead__logo-link+code {
margin:0 10px;
padding:5px 9px;
border-radius:2px;
background:#e6ecf1;
color:#647a88
}
.akismet-masthead__links {
display:-ms-flexbox;
display:flex;
-ms-flex-flow:row wrap;
flex-flow:row wrap;
-ms-flex:2 50%;
flex:2 50%;
-ms-flex-pack:end;
justify-content:flex-end;
margin:0
}
@media (max-width: 480px) {
.akismet-masthead__links {
padding-right:.625rem
}
}
.akismet-masthead__link-li {
margin:0;
padding:0
}
.akismet-masthead__link {
font-style:normal;
color:#0087be;
padding:.625rem;
display:inline-block
}
.akismet-masthead__link:visited {
color:#0087be
}
.akismet-masthead__link:active,.akismet-masthead__link:hover {
color:#00aadc
}
.akismet-masthead__link:hover {
text-decoration:underline
}
.akismet-masthead__link .dashicons {
display:none
}
@media (max-width: 480px) {
.akismet-masthead__link:hover,.akismet-masthead__link:active {
text-decoration:none
}
.akismet-masthead__link .dashicons {
display:block;
font-size:1.75rem
}
.akismet-masthead__link span+span {
display:none
}
}
.akismet-masthead__link-li:last-of-type .akismet-masthead__link {
padding-right:0
}
.akismet-lower {
margin: 0 auto;
text-align: left;
max-width: 45rem;
padding: 1.5rem;
}
.akismet-card {
margin-top: 1rem;
margin-bottom: 0;
position: relative;
margin: 0 auto 0.625rem auto;
box-sizing: border-box;
background: white;
box-shadow: 0 0 0 1px rgba(200, 215, 225, 0.5), 0 1px 2px #e9eff3;
}
.akismet-card:after, .akismet-card .inside:after, .akismet-masthead__logo-container:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.akismet-card .inside {
padding: 1.5rem;
padding-top: 1rem;
}
.akismet-card .akismet-card-actions {
margin-top: 1rem;
}
.jetpack_page_akismet-key-config .update-nag, .settings_page_akismet-key-config .update-nag {
display: none;
}
.akismet-masthead .akismet-right {
line-height: 2.125rem;
font-size: 0.9rem;
}
.akismet-box {
box-sizing: border-box;
background: white;
border: 1px solid rgba(200, 215, 225, 0.5);
}
.akismet-box h2, .akismet-box h3 {
padding: 1.5rem 1.5rem .5rem 1.5rem;
margin: 0;
}
.akismet-box p {
padding: 0 1.5rem 1.5rem 1.5rem;
margin: 0;
}
.akismet-jetpack-email {
font-style: oblique;
}
.akismet-jetpack-gravatar {
padding: 0 0 0 1.5rem;
float: left;
margin-right: 1rem;
width: 54px;
height: 54px;
}
.akismet-box p:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.akismet-box .akismet-right {
padding-right: 1.5rem;
}
.akismet-boxes .akismet-box {
margin-bottom: 0;
padding: 0;
margin-top: -1px;
}
.akismet-boxes .akismet-box:last-child {
margin-bottom: 1.5rem;
}
.akismet-boxes .akismet-box:first-child {
margin-top: 1.5rem;
}
.akismet-button, .akismet-button:hover, .akismet-button:visited {
background: white;
border-color: #c8d7e1;
border-style: solid;
border-width: 1px 1px 2px;
color: #2e4453;
cursor: pointer;
display: inline-block;
margin: 0;
outline: 0;
overflow: hidden;
font-size: 14px;
font-weight: 500;
text-overflow: ellipsis;
text-decoration: none;
vertical-align: top;
box-sizing: border-box;
font-size: 14px;
line-height: 21px;
border-radius: 4px;
padding: 7px 14px 9px;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
.akismet-button:hover {
border-color: #a8bece;
}
.akismet-button:active {
border-width: 2px 1px 1px;
}
.akismet-is-primary, .akismet-is-primary:hover, .akismet-is-primary:visited {
background: #00aadc;
border-color: #0087be;
color: white;
}
.akismet-is-primary:hover, .akismet-is-primary:focus {
border-color: #005082;
}
.akismet-is-primary:hover {
border-color: #005082;
}
.akismet-section-header {
position: relative;
margin: 0 auto 0.625rem auto;
padding: 1rem;
box-sizing: border-box;
box-shadow: 0 0 0 1px rgba(200, 215, 225, 0.5), 0 1px 2px #e9eff3;
background: #ffffff;
width: 100%;
padding-top: 0.6875rem;
padding-bottom: 0.6875rem;
display: flex;
}
.akismet-section-header__label {
display: -ms-flexbox;
display: flex;
-ms-flex-align: center;
align-items: center;
-ms-flex-positive: 1;
flex-grow: 1;
line-height: 1.75rem;
position: relative;
font-size: 0.875rem;
color: #4f748e;
}
.akismet-section-header__actions {
line-height: 1.75rem;
}

View File

@@ -0,0 +1,276 @@
jQuery( function ( $ ) {
var mshotRemovalTimer = null;
var mshotSecondTryTimer = null
var mshotThirdTryTimer = null
var mshotEnabledLinkSelector = 'a[id^="author_comment_url"], tr.pingback td.column-author a:first-of-type, td.comment p a';
$('.akismet-status').each(function () {
var thisId = $(this).attr('commentid');
$(this).prependTo('#comment-' + thisId + ' .column-comment');
});
$('.akismet-user-comment-count').each(function () {
var thisId = $(this).attr('commentid');
$(this).insertAfter('#comment-' + thisId + ' .author strong:first').show();
});
akismet_enable_comment_author_url_removal();
$( '#the-comment-list' ).on( 'click', '.akismet_remove_url', function () {
var thisId = $(this).attr('commentid');
var data = {
action: 'comment_author_deurl',
_wpnonce: WPAkismet.comment_author_url_nonce,
id: thisId
};
$.ajax({
url: ajaxurl,
type: 'POST',
data: data,
beforeSend: function () {
// Removes "x" link
$("a[commentid='"+ thisId +"']").hide();
// Show temp status
$("#author_comment_url_"+ thisId).html( $( '<span/>' ).text( WPAkismet.strings['Removing...'] ) );
},
success: function (response) {
if (response) {
// Show status/undo link
$("#author_comment_url_"+ thisId)
.attr('cid', thisId)
.addClass('akismet_undo_link_removal')
.html(
$( '<span/>' ).text( WPAkismet.strings['URL removed'] )
)
.append( ' ' )
.append(
$( '<span/>' )
.text( WPAkismet.strings['(undo)'] )
.addClass( 'akismet-span-link' )
);
}
}
});
return false;
}).on( 'click', '.akismet_undo_link_removal', function () {
var thisId = $(this).attr('cid');
var thisUrl = $(this).attr('href');
var data = {
action: 'comment_author_reurl',
_wpnonce: WPAkismet.comment_author_url_nonce,
id: thisId,
url: thisUrl
};
$.ajax({
url: ajaxurl,
type: 'POST',
data: data,
beforeSend: function () {
// Show temp status
$("#author_comment_url_"+ thisId).html( $( '<span/>' ).text( WPAkismet.strings['Re-adding...'] ) );
},
success: function (response) {
if (response) {
// Add "x" link
$("a[commentid='"+ thisId +"']").show();
// Show link. Core strips leading http://, so let's do that too.
$("#author_comment_url_"+ thisId).removeClass('akismet_undo_link_removal').text( thisUrl.replace( /^http:\/\/(www\.)?/ig, '' ) );
}
}
});
return false;
});
// Show a preview image of the hovered URL. Applies to author URLs and URLs inside the comments.
$( '#the-comment-list' ).on( 'mouseover', mshotEnabledLinkSelector, function () {
clearTimeout( mshotRemovalTimer );
if ( $( '.akismet-mshot' ).length > 0 ) {
if ( $( '.akismet-mshot:first' ).data( 'link' ) == this ) {
// The preview is already showing for this link.
return;
}
else {
// A new link is being hovered, so remove the old preview.
$( '.akismet-mshot' ).remove();
}
}
clearTimeout( mshotSecondTryTimer );
clearTimeout( mshotThirdTryTimer );
var thisHref = $( this ).attr( 'href' );
var mShot = $( '<div class="akismet-mshot mshot-container"><div class="mshot-arrow"></div><img src="' + akismet_mshot_url( thisHref ) + '" width="450" height="338" class="mshot-image" /></div>' );
mShot.data( 'link', this );
var offset = $( this ).offset();
mShot.offset( {
left : Math.min( $( window ).width() - 475, offset.left + $( this ).width() + 10 ), // Keep it on the screen if the link is near the edge of the window.
top: offset.top + ( $( this ).height() / 2 ) - 101 // 101 = top offset of the arrow plus the top border thickness
} );
// These retries appear to be superfluous if .mshot-image has already loaded, but it's because mShots
// can return a "Generating thumbnail..." image if it doesn't have a thumbnail ready, so we need
// to retry to see if we can get the newly generated thumbnail.
mshotSecondTryTimer = setTimeout( function () {
mShot.find( '.mshot-image' ).attr( 'src', akismet_mshot_url( thisHref, 2 ) );
}, 6000 );
mshotThirdTryTimer = setTimeout( function () {
mShot.find( '.mshot-image' ).attr( 'src', akismet_mshot_url( thisHref, 3 ) );
}, 12000 );
$( 'body' ).append( mShot );
} ).on( 'mouseout', 'a[id^="author_comment_url"], tr.pingback td.column-author a:first-of-type, td.comment p a', function () {
mshotRemovalTimer = setTimeout( function () {
clearTimeout( mshotSecondTryTimer );
clearTimeout( mshotThirdTryTimer );
$( '.akismet-mshot' ).remove();
}, 200 );
} ).on( 'mouseover', 'tr', function () {
// When the mouse hovers over a comment row, begin preloading mshots for any links in the comment or the comment author.
var linksToPreloadMshotsFor = $( this ).find( mshotEnabledLinkSelector );
linksToPreloadMshotsFor.each( function () {
// Don't attempt to preload an mshot for a single link twice. Browser caching should cover this, but in case of
// race conditions, save a flag locally when we've begun trying to preload one.
if ( ! $( this ).data( 'akismet-mshot-preloaded' ) ) {
akismet_preload_mshot( $( this ).attr( 'href' ) );
$( this ).data( 'akismet-mshot-preloaded', true );
}
} );
} );
$('.checkforspam:not(.button-disabled)').click( function(e) {
e.preventDefault();
$('.checkforspam:not(.button-disabled)').addClass('button-disabled');
$('.checkforspam-spinner').addClass( 'spinner' ).addClass( 'is-active' );
// Update the label on the "Check for Spam" button to use the active "Checking for Spam" language.
$( '.checkforspam .akismet-label' ).text( $( '.checkforspam' ).data( 'active-label' ) );
akismet_check_for_spam(0, 100);
});
var spam_count = 0;
var recheck_count = 0;
function akismet_check_for_spam(offset, limit) {
var check_for_spam_buttons = $( '.checkforspam' );
// We show the percentage complete down to one decimal point so even queues with 100k
// pending comments will show some progress pretty quickly.
var percentage_complete = Math.round( ( recheck_count / check_for_spam_buttons.data( 'pending-comment-count' ) ) * 1000 ) / 10;
// Update the progress counter on the "Check for Spam" button.
$( '.checkforspam-progress' ).text( check_for_spam_buttons.data( 'progress-label-format' ).replace( '%1$s', percentage_complete ) );
$.post(
ajaxurl,
{
'action': 'akismet_recheck_queue',
'offset': offset,
'limit': limit
},
function(result) {
recheck_count += result.counts.processed;
spam_count += result.counts.spam;
if (result.counts.processed < limit) {
window.location.href = check_for_spam_buttons.data( 'success-url' ).replace( '__recheck_count__', recheck_count ).replace( '__spam_count__', spam_count );
}
else {
// Account for comments that were caught as spam and moved out of the queue.
akismet_check_for_spam(offset + limit - result.counts.spam, limit);
}
}
);
}
if ( "start_recheck" in WPAkismet && WPAkismet.start_recheck ) {
$( '.checkforspam' ).click();
}
if ( typeof MutationObserver !== 'undefined' ) {
// Dynamically add the "X" next the the author URL links when a comment is quick-edited.
var comment_list_container = document.getElementById( 'the-comment-list' );
if ( comment_list_container ) {
var observer = new MutationObserver( function ( mutations ) {
for ( var i = 0, _len = mutations.length; i < _len; i++ ) {
if ( mutations[i].addedNodes.length > 0 ) {
akismet_enable_comment_author_url_removal();
// Once we know that we'll have to check for new author links, skip the rest of the mutations.
break;
}
}
} );
observer.observe( comment_list_container, { attributes: true, childList: true, characterData: true } );
}
}
function akismet_enable_comment_author_url_removal() {
$( '#the-comment-list' )
.find( 'tr.comment, tr[id ^= "comment-"]' )
.find( '.column-author a[href^="http"]:first' ) // Ignore mailto: links, which would be the comment author's email.
.each(function () {
if ( $( this ).parent().find( '.akismet_remove_url' ).length > 0 ) {
return;
}
var linkHref = $(this).attr( 'href' );
// Ignore any links to the current domain, which are diagnostic tools, like the IP address link
// or any other links another plugin might add.
var currentHostParts = document.location.href.split( '/' );
var currentHost = currentHostParts[0] + '//' + currentHostParts[2] + '/';
if ( linkHref.indexOf( currentHost ) != 0 ) {
var thisCommentId = $(this).parents('tr:first').attr('id').split("-");
$(this)
.attr("id", "author_comment_url_"+ thisCommentId[1])
.after(
$( '<a href="#" class="akismet_remove_url">x</a>' )
.attr( 'commentid', thisCommentId[1] )
.attr( 'title', WPAkismet.strings['Remove this URL'] )
);
}
});
}
/**
* Generate an mShot URL if given a link URL.
*
* @param string linkUrl
* @param int retry If retrying a request, the number of the retry.
* @return string The mShot URL;
*/
function akismet_mshot_url( linkUrl, retry ) {
var mshotUrl = '//s0.wordpress.com/mshots/v1/' + encodeURIComponent( linkUrl ) + '?w=900';
if ( retry ) {
mshotUrl += '&r=' + encodeURIComponent( retry );
}
return mshotUrl;
}
/**
* Begin loading an mShot preview of a link.
*
* @param string linkUrl
*/
function akismet_preload_mshot( linkUrl ) {
var img = new Image();
img.src = akismet_mshot_url( linkUrl );
}
});

View File

@@ -0,0 +1,30 @@
var ak_js = document.getElementById( "ak_js" );
if ( ! ak_js ) {
ak_js = document.createElement( 'input' );
ak_js.setAttribute( 'id', 'ak_js' );
ak_js.setAttribute( 'name', 'ak_js' );
ak_js.setAttribute( 'type', 'hidden' );
}
else {
ak_js.parentNode.removeChild( ak_js );
}
ak_js.setAttribute( 'value', ( new Date() ).getTime() );
var commentForm = document.getElementById( 'commentform' );
if ( commentForm ) {
commentForm.appendChild( ak_js );
}
else {
var replyRowContainer = document.getElementById( 'replyrow' );
if ( replyRowContainer ) {
var children = replyRowContainer.getElementsByTagName( 'td' );
if ( children.length > 0 ) {
children[0].appendChild( ak_js );
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -0,0 +1,66 @@
<?php
/**
* @package Akismet
*/
/*
Plugin Name: Akismet Anti-Spam
Plugin URI: https://akismet.com/
Description: Used by millions, Akismet is quite possibly the best way in the world to <strong>protect your blog from spam</strong>. It keeps your site protected even while you sleep. To get started: activate the Akismet plugin and then go to your Akismet Settings page to set up your API key.
Version: 4.0.3
Author: Automattic
Author URI: https://automattic.com/wordpress-plugins/
License: GPLv2 or later
Text Domain: akismet
*/
/*
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Copyright 2005-2015 Automattic, Inc.
*/
// Make sure we don't expose any info if called directly
if ( !function_exists( 'add_action' ) ) {
echo 'Hi there! I\'m just a plugin, not much I can do when called directly.';
exit;
}
define( 'AKISMET_VERSION', '4.0.3' );
define( 'AKISMET__MINIMUM_WP_VERSION', '4.0' );
define( 'AKISMET__PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
define( 'AKISMET_DELETE_LIMIT', 100000 );
register_activation_hook( __FILE__, array( 'Akismet', 'plugin_activation' ) );
register_deactivation_hook( __FILE__, array( 'Akismet', 'plugin_deactivation' ) );
require_once( AKISMET__PLUGIN_DIR . 'class.akismet.php' );
require_once( AKISMET__PLUGIN_DIR . 'class.akismet-widget.php' );
require_once( AKISMET__PLUGIN_DIR . 'class.akismet-rest-api.php' );
add_action( 'init', array( 'Akismet', 'init' ) );
add_action( 'rest_api_init', array( 'Akismet_REST_API', 'init' ) );
if ( is_admin() || ( defined( 'WP_CLI' ) && WP_CLI ) ) {
require_once( AKISMET__PLUGIN_DIR . 'class.akismet-admin.php' );
add_action( 'init', array( 'Akismet_Admin', 'init' ) );
}
//add wrapper class around deprecated akismet functions that are referenced elsewhere
require_once( AKISMET__PLUGIN_DIR . 'wrapper.php' );
if ( defined( 'WP_CLI' ) && WP_CLI ) {
require_once( AKISMET__PLUGIN_DIR . 'class.akismet-cli.php' );
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,91 @@
<?php
WP_CLI::add_command( 'akismet', 'Akismet_CLI' );
/**
* Filter spam comments.
*/
class Akismet_CLI extends WP_CLI_Command {
/**
* Checks one or more comments against the Akismet API.
*
* ## OPTIONS
* <comment_id>...
* : The ID(s) of the comment(s) to check.
*
* [--noaction]
* : Don't change the status of the comment. Just report what Akismet thinks it is.
*
* ## EXAMPLES
*
* wp akismet check 12345
*
* @alias comment-check
*/
public function check( $args, $assoc_args ) {
foreach ( $args as $comment_id ) {
if ( isset( $assoc_args['noaction'] ) ) {
// Check the comment, but don't reclassify it.
$api_response = Akismet::check_db_comment( $comment_id, 'wp-cli' );
}
else {
$api_response = Akismet::recheck_comment( $comment_id, 'wp-cli' );
}
if ( 'true' === $api_response ) {
WP_CLI::line( sprintf( __( "Comment #%d is spam.", 'akismet' ), $comment_id ) );
}
else if ( 'false' === $api_response ) {
WP_CLI::line( sprintf( __( "Comment #%d is not spam.", 'akismet' ), $comment_id ) );
}
else {
if ( false === $api_response ) {
WP_CLI::error( __( "Failed to connect to Akismet.", 'akismet' ) );
}
else if ( is_wp_error( $api_response ) ) {
WP_CLI::warning( sprintf( __( "Comment #%d could not be checked.", 'akismet' ), $comment_id ) );
}
}
}
}
/**
* Recheck all comments in the Pending queue.
*
* ## EXAMPLES
*
* wp akismet recheck_queue
*
* @alias recheck-queue
*/
public function recheck_queue() {
$batch_size = 100;
$start = 0;
$total_counts = array();
do {
$result_counts = Akismet_Admin::recheck_queue_portion( $start, $batch_size );
if ( $result_counts['processed'] > 0 ) {
foreach ( $result_counts as $key => $count ) {
if ( ! isset( $total_counts[ $key ] ) ) {
$total_counts[ $key ] = $count;
}
else {
$total_counts[ $key ] += $count;
}
}
$start += $batch_size;
$start -= $result_counts['spam']; // These comments will have been removed from the queue.
}
} while ( $result_counts['processed'] > 0 );
WP_CLI::line( sprintf( _n( "Processed %d comment.", "Processed %d comments.", $total_counts['processed'], 'akismet' ), number_format( $total_counts['processed'] ) ) );
WP_CLI::line( sprintf( _n( "%d comment moved to Spam.", "%d comments moved to Spam.", $total_counts['spam'], 'akismet' ), number_format( $total_counts['spam'] ) ) );
if ( $total_counts['error'] ) {
WP_CLI::line( sprintf( _n( "%d comment could not be checked.", "%d comments could not be checked.", $total_counts['error'], 'akismet' ), number_format( $total_counts['error'] ) ) );
}
}
}

View File

@@ -0,0 +1,271 @@
<?php
class Akismet_REST_API {
/**
* Register the REST API routes.
*/
public static function init() {
if ( ! function_exists( 'register_rest_route' ) ) {
// The REST API wasn't integrated into core until 4.4, and we support 4.0+ (for now).
return false;
}
register_rest_route( 'akismet/v1', '/key', array(
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ),
'callback' => array( 'Akismet_REST_API', 'get_key' ),
), array(
'methods' => WP_REST_Server::EDITABLE,
'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ),
'callback' => array( 'Akismet_REST_API', 'set_key' ),
'args' => array(
'key' => array(
'required' => true,
'type' => 'string',
'sanitize_callback' => array( 'Akismet_REST_API', 'sanitize_key' ),
'description' => __( 'A 12-character Akismet API key. Available at akismet.com/get/', 'akismet' ),
),
),
), array(
'methods' => WP_REST_Server::DELETABLE,
'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ),
'callback' => array( 'Akismet_REST_API', 'delete_key' ),
)
) );
register_rest_route( 'akismet/v1', '/settings/', array(
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ),
'callback' => array( 'Akismet_REST_API', 'get_settings' ),
),
array(
'methods' => WP_REST_Server::EDITABLE,
'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ),
'callback' => array( 'Akismet_REST_API', 'set_boolean_settings' ),
'args' => array(
'akismet_strictness' => array(
'required' => false,
'type' => 'boolean',
'description' => __( 'If true, Akismet will automatically discard the worst spam automatically rather than putting it in the spam folder.', 'akismet' ),
),
'akismet_show_user_comments_approved' => array(
'required' => false,
'type' => 'boolean',
'description' => __( 'If true, show the number of approved comments beside each comment author in the comments list page.', 'akismet' ),
),
),
)
) );
register_rest_route( 'akismet/v1', '/stats', array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ),
'callback' => array( 'Akismet_REST_API', 'get_stats' ),
'args' => array(
'interval' => array(
'required' => false,
'type' => 'string',
'sanitize_callback' => array( 'Akismet_REST_API', 'sanitize_interval' ),
'description' => __( 'The time period for which to retrieve stats. Options: 60-days, 6-months, all', 'akismet' ),
'default' => 'all',
),
),
) );
register_rest_route( 'akismet/v1', '/stats/(?P<interval>[\w+])', array(
'args' => array(
'interval' => array(
'description' => __( 'The time period for which to retrieve stats. Options: 60-days, 6-months, all', 'akismet' ),
'type' => 'string',
),
),
array(
'methods' => WP_REST_Server::READABLE,
'permission_callback' => array( 'Akismet_REST_API', 'privileged_permission_callback' ),
'callback' => array( 'Akismet_REST_API', 'get_stats' ),
)
) );
}
/**
* Get the current Akismet API key.
*
* @param WP_REST_Request $request
* @return WP_Error|WP_REST_Response
*/
public static function get_key( $request = null ) {
return rest_ensure_response( Akismet::get_api_key() );
}
/**
* Set the API key, if possible.
*
* @param WP_REST_Request $request
* @return WP_Error|WP_REST_Response
*/
public static function set_key( $request ) {
if ( defined( 'WPCOM_API_KEY' ) ) {
return rest_ensure_response( new WP_Error( 'hardcoded_key', __( 'This site\'s API key is hardcoded and cannot be changed via the API.', 'akismet' ), array( 'status'=> 409 ) ) );
}
$new_api_key = $request->get_param( 'key' );
if ( ! self::key_is_valid( $new_api_key ) ) {
return rest_ensure_response( new WP_Error( 'invalid_key', __( 'The value provided is not a valid and registered API key.', 'akismet' ), array( 'status' => 400 ) ) );
}
update_option( 'wordpress_api_key', $new_api_key );
return self::get_key();
}
/**
* Unset the API key, if possible.
*
* @param WP_REST_Request $request
* @return WP_Error|WP_REST_Response
*/
public static function delete_key( $request ) {
if ( defined( 'WPCOM_API_KEY' ) ) {
return rest_ensure_response( new WP_Error( 'hardcoded_key', __( 'This site\'s API key is hardcoded and cannot be deleted.', 'akismet' ), array( 'status'=> 409 ) ) );
}
delete_option( 'wordpress_api_key' );
return rest_ensure_response( true );
}
/**
* Get the Akismet settings.
*
* @param WP_REST_Request $request
* @return WP_Error|WP_REST_Response
*/
public static function get_settings( $request = null ) {
return rest_ensure_response( array(
'akismet_strictness' => ( get_option( 'akismet_strictness', '1' ) === '1' ),
'akismet_show_user_comments_approved' => ( get_option( 'akismet_show_user_comments_approved', '1' ) === '1' ),
) );
}
/**
* Update the Akismet settings.
*
* @param WP_REST_Request $request
* @return WP_Error|WP_REST_Response
*/
public static function set_boolean_settings( $request ) {
foreach ( array(
'akismet_strictness',
'akismet_show_user_comments_approved',
) as $setting_key ) {
$setting_value = $request->get_param( $setting_key );
if ( is_null( $setting_value ) ) {
// This setting was not specified.
continue;
}
// From 4.7+, WP core will ensure that these are always boolean
// values because they are registered with 'type' => 'boolean',
// but we need to do this ourselves for prior versions.
$setting_value = Akismet_REST_API::parse_boolean( $setting_value );
update_option( $setting_key, $setting_value ? '1' : '0' );
}
return self::get_settings();
}
/**
* Parse a numeric or string boolean value into a boolean.
*
* @param mixed $value The value to convert into a boolean.
* @return bool The converted value.
*/
public static function parse_boolean( $value ) {
switch ( $value ) {
case true:
case 'true':
case '1':
case 1:
return true;
case false:
case 'false':
case '0':
case 0:
return false;
default:
return (bool) $value;
}
}
/**
* Get the Akismet stats for a given time period.
*
* Possible `interval` values:
* - all
* - 60-days
* - 6-months
*
* @param WP_REST_Request $request
* @return WP_Error|WP_REST_Response
*/
public static function get_stats( $request ) {
$api_key = Akismet::get_api_key();
$interval = $request->get_param( 'interval' );
$stat_totals = array();
$response = Akismet::http_post( Akismet::build_query( array( 'blog' => get_option( 'home' ), 'key' => $api_key, 'from' => $interval ) ), 'get-stats' );
if ( ! empty( $response[1] ) ) {
$stat_totals[$interval] = json_decode( $response[1] );
}
return rest_ensure_response( $stat_totals );
}
private static function key_is_valid( $key ) {
$response = Akismet::http_post(
Akismet::build_query(
array(
'key' => $key,
'blog' => get_option( 'home' )
)
),
'verify-key'
);
if ( $response[1] == 'valid' ) {
return true;
}
return false;
}
public static function privileged_permission_callback() {
return current_user_can( 'manage_options' );
}
public static function sanitize_interval( $interval, $request, $param ) {
$interval = trim( $interval );
$valid_intervals = array( '60-days', '6-months', 'all', );
if ( ! in_array( $interval, $valid_intervals ) ) {
$interval = 'all';
}
return $interval;
}
public static function sanitize_key( $key, $request, $param ) {
return trim( $key );
}
}

View File

@@ -0,0 +1,114 @@
<?php
/**
* @package Akismet
*/
class Akismet_Widget extends WP_Widget {
function __construct() {
load_plugin_textdomain( 'akismet' );
parent::__construct(
'akismet_widget',
__( 'Akismet Widget' , 'akismet'),
array( 'description' => __( 'Display the number of spam comments Akismet has caught' , 'akismet') )
);
if ( is_active_widget( false, false, $this->id_base ) ) {
add_action( 'wp_head', array( $this, 'css' ) );
}
}
function css() {
?>
<style type="text/css">
.a-stats {
width: auto;
}
.a-stats a {
background: #7CA821;
background-image:-moz-linear-gradient(0% 100% 90deg,#5F8E14,#7CA821);
background-image:-webkit-gradient(linear,0% 0,0% 100%,from(#7CA821),to(#5F8E14));
border: 1px solid #5F8E14;
border-radius:3px;
color: #CFEA93;
cursor: pointer;
display: block;
font-weight: normal;
height: 100%;
-moz-border-radius:3px;
padding: 7px 0 8px;
text-align: center;
text-decoration: none;
-webkit-border-radius:3px;
width: 100%;
}
.a-stats a:hover {
text-decoration: none;
background-image:-moz-linear-gradient(0% 100% 90deg,#6F9C1B,#659417);
background-image:-webkit-gradient(linear,0% 0,0% 100%,from(#659417),to(#6F9C1B));
}
.a-stats .count {
color: #FFF;
display: block;
font-size: 15px;
line-height: 16px;
padding: 0 13px;
white-space: nowrap;
}
</style>
<?php
}
function form( $instance ) {
if ( $instance && isset( $instance['title'] ) ) {
$title = $instance['title'];
}
else {
$title = __( 'Spam Blocked' , 'akismet' );
}
?>
<p>
<label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php esc_html_e( 'Title:' , 'akismet'); ?></label>
<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
</p>
<?php
}
function update( $new_instance, $old_instance ) {
$instance['title'] = strip_tags( $new_instance['title'] );
return $instance;
}
function widget( $args, $instance ) {
$count = get_option( 'akismet_spam_count' );
if ( ! isset( $instance['title'] ) ) {
$instance['title'] = __( 'Spam Blocked' , 'akismet' );
}
echo $args['before_widget'];
if ( ! empty( $instance['title'] ) ) {
echo $args['before_title'];
echo esc_html( $instance['title'] );
echo $args['after_title'];
}
?>
<div class="a-stats">
<a href="https://akismet.com" target="_blank" title=""><?php printf( _n( '<strong class="count">%1$s spam</strong> blocked by <strong>Akismet</strong>', '<strong class="count">%1$s spam</strong> blocked by <strong>Akismet</strong>', $count , 'akismet'), number_format_i18n( $count ) ); ?></a>
</div>
<?php
echo $args['after_widget'];
}
}
function akismet_register_widgets() {
register_widget( 'Akismet_Widget' );
}
add_action( 'widgets_init', 'akismet_register_widgets' );

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
<?php
# Silence is golden.

View File

@@ -0,0 +1,411 @@
=== Akismet Anti-Spam ===
Contributors: matt, ryan, andy, mdawaffe, tellyworth, josephscott, lessbloat, eoigal, cfinke, automattic, jgs, procifer, stephdau
Tags: akismet, comments, spam, antispam, anti-spam, anti spam, comment moderation, comment spam, contact form spam, spam comments
Requires at least: 4.0
Tested up to: 4.9.1
Stable tag: 4.0.3
License: GPLv2 or later
Akismet checks your comments and contact form submissions against our global database of spam to protect you and your site from malicious content.
== Description ==
Akismet checks your comments and contact form submissions against our global database of spam to prevent your site from publishing malicious content. You can review the comment spam it catches on your blog's "Comments" admin screen.
Major features in Akismet include:
* Automatically checks all comments and filters out the ones that look like spam.
* Each comment has a status history, so you can easily see which comments were caught or cleared by Akismet and which were spammed or unspammed by a moderator.
* URLs are shown in the comment body to reveal hidden or misleading links.
* Moderators can see the number of approved comments for each user.
* A discard feature that outright blocks the worst spam, saving you disk space and speeding up your site.
PS: You'll need an [Akismet.com API key](https://akismet.com/get/) to use it. Keys are free for personal blogs; paid subscriptions are available for businesses and commercial sites.
== Installation ==
Upload the Akismet plugin to your blog, Activate it, then enter your [Akismet.com API key](https://akismet.com/get/).
1, 2, 3: You're done!
== Changelog ==
= 4.0.3 =
*Release Date - 19 February 2018*
* Added a scheduled task to remove entries in wp_commentmeta that no longer have corresponding comments in wp_comments.
* Added a new `akismet_batch_delete_count` action to the batch delete methods for people who'd like to keep track of the numbers of records being processed by those methods.
= 4.0.2 =
*Release Date - 18 December 2017*
* Fixed a bug that could cause Akismet to recheck a comment that has already been manually approved or marked as spam.
* Fixed a bug that could cause Akismet to claim that some comments are still waiting to be checked when no comments are waiting to be checked.
= 4.0.1 =
*Release Date - 6 November 2017*
* Fixed a bug that could prevent some users from connecting Akismet via their Jetpack connection.
* Ensured that any pending Akismet-related events are unscheduled if the plugin is deactivated.
* Allow some JavaScript to be run asynchronously to avoid affecting page render speeds.
= 4.0 =
*Release Date - 19 September 2017*
* Added REST API endpoints for configuring Akismet and retrieving stats.
* Increased the minimum supported WordPress version to 4.0.
* Added compatibility with comments submitted via the REST API.
* Improved the progress indicator on the "Check for Spam" button.
= 3.3.4 =
*Release Date - 3 August 2017*
* Disabled Akismet's debug log output by default unless AKISMET_DEBUG is defined.
* URL previews now begin preloading when the mouse moves near them in the comments section of wp-admin.
* When a comment is caught by the Comment Blacklist, Akismet will always allow it to stay in the trash even if it is spam as well.
* Fixed a bug that was preventing an error from being shown when a site can't reach Akismet's servers.
= 3.3.3 =
*Release Date - 13 July 2017*
* Reduced amount of bandwidth used by the URL Preview feature.
* Improved the admin UI when the API key is manually pre-defined for the site.
* Removed a workaround for WordPress installations older than 3.3 that will improve Akismet's compatibility with other plugins.
* The number of spam blocked that is displayed on the WordPress dashboard will now be more accurate and updated more frequently.
* Fixed a bug in the Akismet widget that could cause PHP warnings.
= 3.3.2 =
*Release Date - 10 May 2017*
* Fixed a bug causing JavaScript errors in some browsers.
= 3.3.1 =
*Release Date - 2 May 2017*
* Improve performance by only requesting the akismet_comment_nonce option when absolutely necessary.
* Fixed two bugs that could cause PHP warnings.
* Fixed a bug that was preventing the "Remove author URL" feature from working after a comment was edited using "Quick Edit."
* Fixed a bug that was preventing the URL preview feature from working after a comment was edited using "Quick Edit."
= 3.3 =
*Release Date - 23 February 2017*
* Updated the Akismet admin pages with a new clean design.
* Fixed bugs preventing the `akismet_add_comment_nonce` and `akismet_update_alert` wrapper functions from working properly.
* Fixed bug preventing the loading indicator from appearing when re-checking all comments for spam.
* Added a progress indicator to the "Check for Spam" button.
* Added a success message after manually rechecking the Pending queue for spam.
= 3.2 =
*Release Date - 6 September 2016*
* Added a WP-CLI module. You can now check comments and recheck the moderation queue from the command line.
* Stopped using the deprecated jQuery function `.live()`.
* Fixed a bug in `remove_comment_author_url()` and `add_comment_author_url()` that could generate PHP notices.
* Fixed a bug that could cause an infinite loop for sites with very very very large comment IDs.
* Fixed a bug that could cause the Akismet widget title to be blank.
= 3.1.11 =
*Release Date - 12 May 2016*
* Fixed a bug that could cause the "Check for Spam" button to skip some comments.
* Fixed a bug that could prevent some spam submissions from being sent to Akismet.
* Updated all links to use https:// when possible.
* Disabled Akismet debug logging unless WP_DEBUG and WP_DEBUG_LOG are both enabled.
= 3.1.10 =
*Release Date - 1 April 2016*
* Fixed a bug that could cause comments caught as spam to be placed in the Pending queue.
* Fixed a bug that could have resulted in comments that were caught by the core WordPress comment blacklist not to have a corresponding History entry.
* Fixed a bug that could have caused avoidable PHP warnings in the error log.
= 3.1.9 =
*Release Date - 28 March 2016*
* Add compatibility with Jetpack so that Jetpack can automatically configure Akismet settings when appropriate.
* Fixed a bug preventing some comment data from being sent to Akismet.
= 3.1.8 =
*Release Date - 4 March 2016*
* Fixed a bug preventing Akismet from being used with some plugins that rewrite admin URLs.
* Reduced the amount of bandwidth used on Akismet API calls
* Reduced the amount of space Akismet uses in the database
* Fixed a bug that could cause comments caught as spam to be placed in the Pending queue.
= 3.1.7 =
*Release Date - 4 January 2016*
* Added documentation for the 'akismet_comment_nonce' filter.
* The post-install activation button is now accessible to screen readers and keyboard-only users.
* Fixed a bug that was preventing the "Remove author URL" feature from working in WordPress 4.4
= 3.1.6 =
*Release Date - 14 December 2015*
* Improve the notices shown after activating Akismet.
* Update some strings to allow for the proper plural forms in all languages.
= 3.1.5 =
*Release Date - 13 October 2015*
* Closes a potential XSS vulnerability.
= 3.1.4 =
*Release Date - 24 September 2015*
* Fixed a bug that was preventing some users from automatically connecting using Jetpack if they didn't have a current Akismet subscription.
* Fixed a bug that could cause comments caught as spam to be placed in the Pending queue.
* Error messages and instructions have been simplified to be more understandable.
* Link previews are enabled for all links inside comments, not just the author's website link.
= 3.1.3 =
*Release Date - 6 July 2015*
* Notify users when their account status changes after previously being successfully set up. This should help any users who are seeing blank Akismet settings screens.
= 3.1.2 =
*Release Date - 7 June 2015*
* Reduced the amount of space Akismet uses in the commentmeta table.
* Fixed a bug where some comments with quotes in the author name weren't getting history entries
* Pre-emptive security improvements to ensure that the Akismet plugin can't be used by attackers to compromise a WordPress installation.
* Better UI for the key entry field: allow whitespace to be included at the beginning or end of the key and strip it out automatically when the form is submitted.
* When deactivating the plugin, notify the Akismet API so the site can be marked as inactive.
* Clearer error messages.
= 3.1.1 =
*Release Date - 17th March, 2015*
* Improvements to the "Remove comment author URL" JavaScript
* Include the pingback pre-check from the 2.6 branch.
= 3.1 =
*Release Date - 11th March, 2015*
* Use HTTPS by default for all requests to Akismet.
* Fix for a situation where Akismet might strip HTML from a comment.
= 3.0.4 =
*Release Date - 11th December, 2014*
* Fix to make .htaccess compatible with Apache 2.4.
* Fix to allow removal of https author URLs.
* Fix to avoid stripping part of the author URL when removing and re-adding.
* Removed the "Check for Spam" button from the "Trash" and "Approved" queues, where it would have no effect.
* Allow automatic API key configuration when Jetpack is installed and connected to a WordPress.com account
= 3.0.3 =
*Release Date - 3rd November, 2014*
* Fix for sending the wrong data to delete_comment action that could have prevented old spam comments from being deleted.
* Added a filter to disable logging of Akismet debugging information.
* Added a filter for the maximum comment age when deleting old spam comments.
* Added a filter for the number per batch when deleting old spam comments.
* Removed the "Check for Spam" button from the Spam folder.
= 3.0.2 =
*Release Date - 18th August, 2014*
* Performance improvements.
* Fixed a bug that could truncate the comment data being sent to Akismet for checking.
= 3.0.1 =
*Release Date - 9th July, 2014*
* Removed dependency on PHP's fsockopen function
* Fix spam/ham reports to work when reported outside of the WP dashboard, e.g., from Notifications or the WP app
* Remove jQuery dependency for comment form JavaScript
* Remove unnecessary data from some Akismet comment meta
* Suspended keys will now result in all comments being put in moderation, not spam.
= 3.0.0 =
*Release Date - 15th April, 2014*
* Move Akismet to Settings menu
* Drop Akismet Stats menu
* Add stats snapshot to Akismet settings
* Add Akismet subscription details and status to Akismet settings
* Add contextual help for each page
* Improve Akismet setup to use Jetpack to automate plugin setup
* Fix "Check for Spam" to use AJAX to avoid page timing out
* Fix Akismet settings page to be responsive
* Drop legacy code
* Tidy up CSS and Javascript
* Replace the old discard setting with a new "discard pervasive spam" feature.
= 2.6.0 =
*Release Date - 18th March, 2014*
* Add ajax paging to the check for spam button to handle large volumes of comments
* Optimize javascript and add localization support
* Fix bug in link to spam comments from right now dashboard widget
* Fix bug with deleting old comments to avoid timeouts dealing with large volumes of comments
* Include X-Pingback-Forwarded-For header in outbound WordPress pingback verifications
* Add pre-check for pingbacks, to stop spam before an outbound verification request is made
= 2.5.9 =
*Release Date - 1st August, 2013*
* Update 'Already have a key' link to redirect page rather than depend on javascript
* Fix some non-translatable strings to be translatable
* Update Activation banner in plugins page to redirect user to Akismet config page
= 2.5.8 =
*Release Date - 20th January, 2013*
* Simplify the activation process for new users
* Remove the reporter_ip parameter
* Minor preventative security improvements
= 2.5.7 =
*Release Date - 13th December, 2012*
* FireFox Stats iframe preview bug
* Fix mshots preview when using https
* Add .htaccess to block direct access to files
* Prevent some PHP notices
* Fix Check For Spam return location when referrer is empty
* Fix Settings links for network admins
* Fix prepare() warnings in WP 3.5
= 2.5.6 =
*Release Date - 26th April, 2012*
* Prevent retry scheduling problems on sites where wp_cron is misbehaving
* Preload mshot previews
* Modernize the widget code
* Fix a bug where comments were not held for moderation during an error condition
* Improve the UX and display when comments are temporarily held due to an error
* Make the Check For Spam button force a retry when comments are held due to an error
* Handle errors caused by an invalid key
* Don't retry comments that are too old
* Improve error messages when verifying an API key
= 2.5.5 =
*Release Date - 11th January, 2012*
* Add nonce check for comment author URL remove action
* Fix the settings link
= 2.5.4 =
*Release Date - 5th January, 2012*
* Limit Akismet CSS and Javascript loading in wp-admin to just the pages that need it
* Added author URL quick removal functionality
* Added mShot preview on Author URL hover
* Added empty index.php to prevent directory listing
* Move wp-admin menu items under Jetpack, if it is installed
* Purge old Akismet comment meta data, default of 15 days
= 2.5.3 =
*Release Date - 8th Febuary, 2011*
* Specify the license is GPL v2 or later
* Fix a bug that could result in orphaned commentmeta entries
* Include hotfix for WordPress 3.0.5 filter issue
= 2.5.2 =
*Release Date - 14th January, 2011*
* Properly format the comment count for author counts
* Look for super admins on multisite installs when looking up user roles
* Increase the HTTP request timeout
* Removed padding for author approved count
* Fix typo in function name
* Set Akismet stats iframe height to fixed 2500px. Better to have one tall scroll bar than two side by side.
= 2.5.1 =
*Release Date - 17th December, 2010*
* Fix a bug that caused the "Auto delete" option to fail to discard comments correctly
* Remove the comment nonce form field from the 'Akismet Configuration' page in favor of using a filter, akismet_comment_nonce
* Fixed padding bug in "author" column of posts screen
* Added margin-top to "cleared by ..." badges on dashboard
* Fix possible error when calling akismet_cron_recheck()
* Fix more PHP warnings
* Clean up XHTML warnings for comment nonce
* Fix for possible condition where scheduled comment re-checks could get stuck
* Clean up the comment meta details after deleting a comment
* Only show the status badge if the comment status has been changed by someone/something other than Akismet
* Show a 'History' link in the row-actions
* Translation fixes
* Reduced font-size on author name
* Moved "flagged by..." notification to top right corner of comment container and removed heavy styling
* Hid "flagged by..." notification while on dashboard
= 2.5.0 =
*Release Date - 7th December, 2010*
* Track comment actions under 'Akismet Status' on the edit comment screen
* Fix a few remaining deprecated function calls ( props Mike Glendinning )
* Use HTTPS for the stats IFRAME when wp-admin is using HTTPS
* Use the WordPress HTTP class if available
* Move the admin UI code to a separate file, only loaded when needed
* Add cron retry feature, to replace the old connectivity check
* Display Akismet status badge beside each comment
* Record history for each comment, and display it on the edit page
* Record the complete comment as originally submitted in comment_meta, to use when reporting spam and ham
* Highlight links in comment content
* New option, "Show the number of comments you've approved beside each comment author."
* New option, "Use a nonce on the comment form."
= 2.4.0 =
*Release Date - 23rd August, 2010*
* Spell out that the license is GPLv2
* Fix PHP warnings
* Fix WordPress deprecated function calls
* Fire the delete_comment action when deleting comments
* Move code specific for older WP versions to legacy.php
* General code clean up
= 2.3.0 =
*Release Date - 5th June, 2010*
* Fix "Are you sure" nonce message on config screen in WPMU
* Fix XHTML compliance issue in sidebar widget
* Change author link; remove some old references to WordPress.com accounts
* Localize the widget title (core ticket #13879)
= 2.2.9 =
*Release Date - 2nd June, 2010*
* Eliminate a potential conflict with some plugins that may cause spurious reports
= 2.2.8 =
*Release Date - 27th May, 2010*
* Fix bug in initial comment check for ipv6 addresses
* Report comments as ham when they are moved from spam to moderation
* Report comments as ham when clicking undo after spam
* Use transition_comment_status action when available instead of older actions for spam/ham submissions
* Better diagnostic messages when PHP network functions are unavailable
* Better handling of comments by logged-in users
= 2.2.7 =
*Release Date - 17th December, 2009*
* Add a new AKISMET_VERSION constant
* Reduce the possibility of over-counting spam when another spam filter plugin is in use
* Disable the connectivity check when the API key is hard-coded for WPMU
= 2.2.6 =
*Release Date - 20th July, 2009*
* Fix a global warning introduced in 2.2.5
* Add changelog and additional readme.txt tags
* Fix an array conversion warning in some versions of PHP
* Support a new WPCOM_API_KEY constant for easier use with WordPress MU
= 2.2.5 =
*Release Date - 13th July, 2009*
* Include a new Server Connectivity diagnostic check, to detect problems caused by firewalls
= 2.2.4 =
*Release Date - 3rd June, 2009*
* Fixed a key problem affecting the stats feature in WordPress MU
* Provide additional blog information in Akismet API calls

View File

@@ -0,0 +1,231 @@
<div id="akismet-plugin-container">
<div class="akismet-masthead">
<div class="akismet-masthead__inside-container">
<div class="akismet-masthead__logo-container">
<img class="akismet-masthead__logo" src="<?php echo esc_url( plugins_url( '../_inc/img/logo-full-2x.png', __FILE__ ) ); ?>" alt="Akismet" />
</div>
</div>
</div>
<div class="akismet-lower">
<?php if ( Akismet::get_api_key() ) { ?>
<?php Akismet_Admin::display_status(); ?>
<?php } ?>
<?php if ( ! empty( $notices ) ) { ?>
<?php foreach ( $notices as $notice ) { ?>
<?php Akismet::view( 'notice', $notice ); ?>
<?php } ?>
<?php } ?>
<?php if ( $stat_totals && isset( $stat_totals['all'] ) && (int) $stat_totals['all']->spam > 0 ) : ?>
<div class="akismet-card">
<div class="akismet-section-header">
<div class="akismet-section-header__label">
<span><?php esc_html_e( 'Statistics' , 'akismet'); ?></span>
</div>
<div class="akismet-section-header__actions">
<a href="<?php echo esc_url( Akismet_Admin::get_page_url( 'stats' ) ); ?>">
<?php esc_html_e( 'Detailed Stats' , 'akismet');?>
</a>
</div>
</div>
<div class="akismet-new-snapshot">
<iframe allowtransparency="true" scrolling="no" frameborder="0" style="width: 100%; height: 220px; overflow: hidden;" src="<?php printf( '//akismet.com/web/1.0/snapshot.php?blog=%s&api_key=%s&height=200&locale=%s', urlencode( get_option( 'home' ) ), Akismet::get_api_key(), get_locale() );?>"></iframe>
<ul>
<li>
<h3><?php esc_html_e( 'Past six months' , 'akismet');?></h3>
<span><?php echo number_format( $stat_totals['6-months']->spam );?></span>
<?php echo esc_html( _n( 'Spam blocked', 'Spam blocked', $stat_totals['6-months']->spam, 'akismet' ) ); ?>
</li>
<li>
<h3><?php esc_html_e( 'All time' , 'akismet');?></h3>
<span><?php echo number_format( $stat_totals['all']->spam );?></span>
<?php echo esc_html( _n( 'Spam blocked', 'Spam blocked', $stat_totals['all']->spam, 'akismet' ) ); ?>
</li>
<li>
<h3><?php esc_html_e( 'Accuracy' , 'akismet');?></h3>
<span><?php echo floatval( $stat_totals['all']->accuracy ); ?>%</span>
<?php printf( _n( '%s missed spam', '%s missed spam', $stat_totals['all']->missed_spam, 'akismet' ), number_format( $stat_totals['all']->missed_spam ) ); ?>
|
<?php printf( _n( '%s false positive', '%s false positives', $stat_totals['all']->false_positives, 'akismet' ), number_format( $stat_totals['all']->false_positives ) ); ?>
</li>
</ul>
</div>
</div>
<?php endif;?>
<?php if ( $akismet_user ):?>
<div class="akismet-card">
<div class="akismet-section-header">
<div class="akismet-section-header__label">
<span><?php esc_html_e( 'Settings' , 'akismet'); ?></span>
</div>
</div>
<div class="inside">
<form action="<?php echo esc_url( Akismet_Admin::get_page_url() ); ?>" method="POST">
<table cellspacing="0" class="akismet-settings">
<tbody>
<?php if ( ! Akismet::predefined_api_key() ) { ?>
<tr>
<th class="akismet-api-key" width="10%" align="left" scope="row"><?php esc_html_e('API Key', 'akismet');?></th>
<td width="5%"/>
<td align="left">
<span class="api-key"><input id="key" name="key" type="text" size="15" value="<?php echo esc_attr( get_option('wordpress_api_key') ); ?>" class="<?php echo esc_attr( 'regular-text code ' . $akismet_user->status ); ?>"></span>
</td>
</tr>
<?php } ?>
<?php if ( isset( $_GET['ssl_status'] ) ) { ?>
<tr>
<th align="left" scope="row"><?php esc_html_e( 'SSL Status', 'akismet' ); ?></th>
<td></td>
<td align="left">
<p>
<?php
if ( ! wp_http_supports( array( 'ssl' ) ) ) {
?><b><?php esc_html_e( 'Disabled.', 'akismet' ); ?></b> <?php esc_html_e( 'Your Web server cannot make SSL requests; contact your Web host and ask them to add support for SSL requests.', 'akismet' ); ?><?php
}
else {
$ssl_disabled = get_option( 'akismet_ssl_disabled' );
if ( $ssl_disabled ) {
?><b><?php esc_html_e( 'Temporarily disabled.', 'akismet' ); ?></b> <?php esc_html_e( 'Akismet encountered a problem with a previous SSL request and disabled it temporarily. It will begin using SSL for requests again shortly.', 'akismet' ); ?><?php
}
else {
?><b><?php esc_html_e( 'Enabled.', 'akismet' ); ?></b> <?php esc_html_e( 'All systems functional.', 'akismet' ); ?><?php
}
}
?>
</p>
</td>
</tr>
<?php } ?>
<tr>
<th align="left" scope="row"><?php esc_html_e('Comments', 'akismet');?></th>
<td></td>
<td align="left">
<p>
<label for="akismet_show_user_comments_approved" title="<?php esc_attr_e( 'Show approved comments' , 'akismet'); ?>">
<input
name="akismet_show_user_comments_approved"
id="akismet_show_user_comments_approved"
value="1"
type="checkbox"
<?php
// If the option isn't set, or if it's enabled ('1'), or if it was enabled a long time ago ('true'), check the checkbox.
checked( true, ( in_array( get_option( 'akismet_show_user_comments_approved' ), array( false, '1', 'true' ), true ) ) );
?>
/>
<?php esc_html_e( 'Show the number of approved comments beside each comment author', 'akismet' ); ?>
</label>
</p>
</td>
</tr>
<tr>
<th class="strictness" align="left" scope="row"><?php esc_html_e('Strictness', 'akismet'); ?></th>
<td></td>
<td align="left">
<fieldset><legend class="screen-reader-text"><span><?php esc_html_e('Akismet anti-spam strictness', 'akismet'); ?></span></legend>
<p><label for="akismet_strictness_1"><input type="radio" name="akismet_strictness" id="akismet_strictness_1" value="1" <?php checked('1', get_option('akismet_strictness')); ?> /> <?php esc_html_e('Silently discard the worst and most pervasive spam so I never see it.', 'akismet'); ?></label></p>
<p><label for="akismet_strictness_0"><input type="radio" name="akismet_strictness" id="akismet_strictness_0" value="0" <?php checked('0', get_option('akismet_strictness')); ?> /> <?php esc_html_e('Always put spam in the Spam folder for review.', 'akismet'); ?></label></p>
</fieldset>
<span class="akismet-note"><strong><?php esc_html_e('Note:', 'akismet');?></strong>
<?php
$delete_interval = max( 1, intval( apply_filters( 'akismet_delete_comment_interval', 15 ) ) );
printf(
_n(
'Spam in the <a href="%1$s">spam folder</a> older than 1 day is deleted automatically.',
'Spam in the <a href="%1$s">spam folder</a> older than %2$d days is deleted automatically.',
$delete_interval,
'akismet'
),
admin_url( 'edit-comments.php?comment_status=spam' ),
$delete_interval
);
?>
</td>
</tr>
</tbody>
</table>
<div class="akismet-card-actions">
<?php if ( ! Akismet::predefined_api_key() ) { ?>
<div id="delete-action">
<a class="submitdelete deletion" href="<?php echo esc_url( Akismet_Admin::get_page_url( 'delete_key' ) ); ?>"><?php esc_html_e('Disconnect this account', 'akismet'); ?></a>
</div>
<?php } ?>
<?php wp_nonce_field(Akismet_Admin::NONCE) ?>
<div id="publishing-action">
<input type="hidden" name="action" value="enter-key">
<input type="submit" name="submit" id="submit" class="akismet-button akismet-is-primary" value="<?php esc_attr_e('Save Changes', 'akismet');?>">
</div>
<div class="clear"></div>
</div>
</form>
</div>
</div>
<?php if ( ! Akismet::predefined_api_key() ) { ?>
<div class="akismet-card">
<div class="akismet-section-header">
<div class="akismet-section-header__label">
<span><?php esc_html_e( 'Account' , 'akismet'); ?></span>
</div>
</div>
<div class="inside">
<table cellspacing="0" border="0" class="akismet-settings">
<tbody>
<tr>
<th scope="row" align="left"><?php esc_html_e( 'Subscription Type' , 'akismet');?></th>
<td width="5%"/>
<td align="left">
<p><?php echo esc_html( $akismet_user->account_name ); ?></p>
</td>
</tr>
<tr>
<th scope="row" align="left"><?php esc_html_e( 'Status' , 'akismet');?></th>
<td width="5%"/>
<td align="left">
<p><?php
if ( 'cancelled' == $akismet_user->status ) :
esc_html_e( 'Cancelled', 'akismet' );
elseif ( 'suspended' == $akismet_user->status ) :
esc_html_e( 'Suspended', 'akismet' );
elseif ( 'missing' == $akismet_user->status ) :
esc_html_e( 'Missing', 'akismet' );
elseif ( 'no-sub' == $akismet_user->status ) :
esc_html_e( 'No Subscription Found', 'akismet' );
else :
esc_html_e( 'Active', 'akismet' );
endif; ?></p>
</td>
</tr>
<?php if ( $akismet_user->next_billing_date ) : ?>
<tr>
<th scope="row" align="left"><?php esc_html_e( 'Next Billing Date' , 'akismet');?></th>
<td width="5%"/>
<td align="left">
<p><?php echo date( 'F j, Y', $akismet_user->next_billing_date ); ?></p>
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
<div class="akismet-card-actions">
<div id="publishing-action">
<?php Akismet::view( 'get', array( 'text' => ( $akismet_user->account_type == 'free-api-key' && $akismet_user->status == 'active' ? __( 'Upgrade' , 'akismet') : __( 'Change' , 'akismet') ), 'redirect' => 'upgrade' ) ); ?>
</div>
<div class="clear"></div>
</div>
</div>
</div>
<?php } ?>
<?php endif;?>
</div>
</div>

View File

@@ -0,0 +1,6 @@
<form name="akismet_activate" action="https://akismet.com/get/" method="POST" target="_blank">
<input type="hidden" name="passback_url" value="<?php echo esc_url( Akismet_Admin::get_page_url() ); ?>"/>
<input type="hidden" name="blog" value="<?php echo esc_url( get_option( 'home' ) ); ?>"/>
<input type="hidden" name="redirect" value="<?php echo isset( $redirect ) ? $redirect : 'plugin-signup'; ?>"/>
<input type="submit" class="<?php echo isset( $classes ) && count( $classes ) > 0 ? implode( ' ', $classes ) : 'akismet-button akismet-is-primary';?>" value="<?php echo esc_attr( $text ); ?>"/>
</form>

View File

@@ -0,0 +1,135 @@
<?php if ( $type == 'plugin' ) :?>
<div class="updated" style="padding: 0; margin: 0; border: none; background: none;">
<form name="akismet_activate" action="<?php echo esc_url( Akismet_Admin::get_page_url() ); ?>" method="POST">
<div class="akismet_activate">
<div class="aa_a">A</div>
<div class="aa_button_container">
<div class="aa_button_border">
<input type="submit" class="aa_button" value="<?php esc_attr_e( 'Set up your Akismet account', 'akismet' ); ?>" />
</div>
</div>
<div class="aa_description"><?php _e('<strong>Almost done</strong> - configure Akismet and say goodbye to spam', 'akismet');?></div>
</div>
</form>
</div>
<?php elseif ( $type == 'spam-check' ) :?>
<div class="notice notice-warning">
<p><strong><?php esc_html_e( 'Akismet has detected a problem.', 'akismet' );?></strong></p>
<p><?php printf( __( 'Some comments have not yet been checked for spam by Akismet. They have been temporarily held for moderation and will automatically be rechecked later.', 'akismet' ) ); ?></p>
<?php if ( $link_text ) { ?>
<p><?php echo $link_text; ?></p>
<?php } ?>
</div>
<?php elseif ( $type == 'alert' ) :?>
<div class='error'>
<p><strong><?php printf( esc_html__( 'Akismet Error Code: %s', 'akismet' ), $code ); ?></strong></p>
<p><?php echo esc_html( $msg ); ?></p>
<p><?php
/* translators: the placeholder is a clickable URL that leads to more information regarding an error code. */
printf( esc_html__( 'For more information: %s' , 'akismet'), '<a href="https://akismet.com/errors/' . $code . '">https://akismet.com/errors/' . $code . '</a>' );
?>
</p>
</div>
<?php elseif ( $type == 'notice' ) :?>
<div class="akismet-alert akismet-critical">
<h3 class="akismet-key-status failed"><?php echo $notice_header; ?></h3>
<p class="akismet-description">
<?php echo $notice_text; ?>
</p>
</div>
<?php elseif ( $type == 'missing-functions' ) :?>
<div class="akismet-alert akismet-critical">
<h3 class="akismet-key-status failed"><?php esc_html_e('Network functions are disabled.', 'akismet'); ?></h3>
<p class="akismet-description"><?php printf( __('Your web host or server administrator has disabled PHP&#8217;s <code>gethostbynamel</code> function. <strong>Akismet cannot work correctly until this is fixed.</strong> Please contact your web host or firewall administrator and give them <a href="%s" target="_blank">this information about Akismet&#8217;s system requirements</a>.', 'akismet'), 'https://blog.akismet.com/akismet-hosting-faq/'); ?></p>
</div>
<?php elseif ( $type == 'servers-be-down' ) :?>
<div class="akismet-alert akismet-critical">
<h3 class="akismet-key-status failed"><?php esc_html_e("Your site can&#8217;t connect to the Akismet servers.", 'akismet'); ?></h3>
<p class="akismet-description"><?php printf( __('Your firewall may be blocking Akismet from connecting to its API. Please contact your host and refer to <a href="%s" target="_blank">our guide about firewalls</a>.', 'akismet'), 'https://blog.akismet.com/akismet-hosting-faq/'); ?></p>
</div>
<?php elseif ( $type == 'active-dunning' ) :?>
<div class="akismet-alert akismet-critical">
<h3 class="akismet-key-status"><?php esc_html_e("Please update your payment information.", 'akismet'); ?></h3>
<p class="akismet-description"><?php printf( __('We cannot process your payment. Please <a href="%s" target="_blank">update your payment details</a>.', 'akismet'), 'https://akismet.com/account/'); ?></p>
</div>
<?php elseif ( $type == 'cancelled' ) :?>
<div class="akismet-alert akismet-critical">
<h3 class="akismet-key-status"><?php esc_html_e("Your Akismet plan has been cancelled.", 'akismet'); ?></h3>
<p class="akismet-description"><?php printf( __('Please visit your <a href="%s" target="_blank">Akismet account page</a> to reactivate your subscription.', 'akismet'), 'https://akismet.com/account/'); ?></p>
</div>
<?php elseif ( $type == 'suspended' ) :?>
<div class="akismet-alert akismet-critical">
<h3 class="akismet-key-status failed"><?php esc_html_e("Your Akismet subscription is suspended.", 'akismet'); ?></h3>
<p class="akismet-description"><?php printf( __('Please contact <a href="%s" target="_blank">Akismet support</a> for assistance.', 'akismet'), 'https://akismet.com/contact/'); ?></p>
</div>
<?php elseif ( $type == 'active-notice' && $time_saved ) :?>
<div class="akismet-alert akismet-active">
<h3 class="akismet-key-status"><?php echo esc_html( $time_saved ); ?></h3>
<p class="akismet-description"><?php printf( __('You can help us fight spam and upgrade your account by <a href="%s" target="_blank">contributing a token amount</a>.', 'akismet'), 'https://akismet.com/account/upgrade/'); ?></p>
</div>
<?php elseif ( $type == 'missing' ) :?>
<div class="akismet-alert akismet-critical">
<h3 class="akismet-key-status failed"><?php esc_html_e( 'There is a problem with your API key.', 'akismet'); ?></h3>
<p class="akismet-description"><?php printf( __('Please contact <a href="%s" target="_blank">Akismet support</a> for assistance.', 'akismet'), 'https://akismet.com/contact/'); ?></p>
</div>
<?php elseif ( $type == 'no-sub' ) :?>
<div class="akismet-alert akismet-critical">
<h3 class="akismet-key-status failed"><?php esc_html_e( 'You don&#8217;t have an Akismet plan.', 'akismet'); ?></h3>
<p class="akismet-description">
<?php printf( __( 'In 2012, Akismet began using subscription plans for all accounts (even free ones). A plan has not been assigned to your account, and we&#8217;d appreciate it if you&#8217;d <a href="%s" target="_blank">sign into your account</a> and choose one.', 'akismet'), 'https://akismet.com/account/upgrade/' ); ?>
<br /><br />
<?php printf( __( 'Please <a href="%s" target="_blank">contact our support team</a> with any questions.', 'akismet' ), 'https://akismet.com/contact/' ); ?>
</p>
</div>
<?php elseif ( $type == 'new-key-valid' ) :
global $wpdb;
$check_pending_link = false;
$at_least_one_comment_in_moderation = !! $wpdb->get_var( "SELECT comment_ID FROM {$wpdb->comments} WHERE comment_approved = '0' LIMIT 1" );
if ( $at_least_one_comment_in_moderation) {
$check_pending_link = 'edit-comments.php?akismet_recheck=' . wp_create_nonce( 'akismet_recheck' );
}
?>
<div class="akismet-alert akismet-active">
<h3 class="akismet-key-status"><?php esc_html_e( 'Akismet is now protecting your site from spam. Happy blogging!', 'akismet' ); ?></h3>
<?php if ( $check_pending_link ) { ?>
<p class="akismet-description"><?php printf( __( 'Would you like to <a href="%s">check pending comments</a>?', 'akismet' ), esc_url( $check_pending_link ) ); ?></p>
<?php } ?>
</div>
<?php elseif ( $type == 'new-key-invalid' ) :?>
<div class="akismet-alert akismet-critical">
<h3 class="akismet-key-status"><?php esc_html_e( 'The key you entered is invalid. Please double-check it.' , 'akismet'); ?></h3>
</div>
<?php elseif ( $type == 'existing-key-invalid' ) :?>
<div class="akismet-alert akismet-critical">
<h3 class="akismet-key-status"><?php esc_html_e( 'Your API key is no longer valid. Please enter a new key or contact support@akismet.com.' , 'akismet'); ?></h3>
</div>
<?php elseif ( $type == 'new-key-failed' ) :?>
<div class="akismet-alert akismet-critical">
<h3 class="akismet-key-status"><?php esc_html_e( 'The API key you entered could not be verified.' , 'akismet'); ?></h3>
<p class="akismet-description"><?php printf( __('The connection to akismet.com could not be established. Please refer to <a href="%s" target="_blank">our guide about firewalls</a> and check your server configuration.', 'akismet'), 'https://blog.akismet.com/akismet-hosting-faq/'); ?></p>
</div>
<?php elseif ( $type == 'limit-reached' && in_array( $level, array( 'yellow', 'red' ) ) ) :?>
<div class="akismet-alert akismet-critical">
<?php if ( $level == 'yellow' ): ?>
<h3 class="akismet-key-status failed"><?php esc_html_e( 'You&#8217;re using your Akismet key on more sites than your Pro subscription allows.', 'akismet' ); ?></h3>
<p class="akismet-description">
<?php printf( __( 'Your Pro subscription allows the use of Akismet on only one site. Please <a href="%s" target="_blank">purchase additional Pro subscriptions</a> or upgrade to an Enterprise subscription that allows the use of Akismet on unlimited sites.', 'akismet' ), 'https://docs.akismet.com/billing/add-more-sites/' ); ?>
<br /><br />
<?php printf( __( 'Please <a href="%s" target="_blank">contact our support team</a> with any questions.', 'akismet' ), 'https://akismet.com/contact/'); ?>
</p>
<?php elseif ( $level == 'red' ): ?>
<h3 class="akismet-key-status failed"><?php esc_html_e( 'You&#8217;re using Akismet on far too many sites for your Pro subscription.', 'akismet' ); ?></h3>
<p class="akismet-description">
<?php printf( __( 'To continue your service, <a href="%s" target="_blank">upgrade to an Enterprise subscription</a>, which covers an unlimited number of sites.', 'akismet'), 'https://akismet.com/account/upgrade/' ); ?>
<br /><br />
<?php printf( __( 'Please <a href="%s" target="_blank">contact our support team</a> with any questions.', 'akismet' ), 'https://akismet.com/contact/'); ?>
</p>
<?php endif; ?>
</div>
<?php endif;?>

View File

@@ -0,0 +1,102 @@
<div id="akismet-plugin-container">
<div class="akismet-masthead">
<div class="akismet-masthead__inside-container">
<div class="akismet-masthead__logo-container">
<img class="akismet-masthead__logo" src="<?php echo esc_url( plugins_url( '../_inc/img/logo-full-2x.png', __FILE__ ) ); ?>" alt="Akismet" />
</div>
</div>
</div>
<div class="akismet-lower">
<?php Akismet_Admin::display_status(); ?>
<div class="akismet-box">
<h2><?php esc_html_e( 'Eliminate spam from your site', 'akismet' ); ?></h2>
<p><?php esc_html_e( 'Select one of the options below to get started.', 'akismet' ); ?></p>
</div>
<div class="akismet-boxes">
<?php if ( ! Akismet::predefined_api_key() ) { ?>
<?php if ( $akismet_user && in_array( $akismet_user->status, array( 'active', 'active-dunning', 'no-sub', 'missing', 'cancelled', 'suspended' ) ) ) { ?>
<?php if ( in_array( $akismet_user->status, array( 'no-sub', 'missing' ) ) ) { ?>
<div class="akismet-box">
<h3><?php esc_html_e( 'Connect via Jetpack', 'akismet' ); ?></h3>
<p><?php esc_html_e( 'Use your Jetpack connection to activate Akismet.', 'akismet' ); ?></p>
<form name="akismet_activate" id="akismet_activate" action="https://akismet.com/get/" method="post" class="akismet-right" target="_blank">
<input type="hidden" name="passback_url" value="<?php echo esc_url( Akismet_Admin::get_page_url() ); ?>"/>
<input type="hidden" name="blog" value="<?php echo esc_url( get_option( 'home' ) ); ?>"/>
<input type="hidden" name="auto-connect" value="<?php echo esc_attr( $akismet_user->ID ); ?>"/>
<input type="hidden" name="redirect" value="plugin-signup"/>
<input type="submit" class="akismet-button akismet-is-primary" value="<?php esc_attr_e( 'Connect with Jetpack' , 'akismet' ); ?>"/>
</form>
<?php echo get_avatar( $akismet_user->user_email, null, null, null, array( 'class' => 'akismet-jetpack-gravatar' ) ); ?>
<p><?php echo sprintf( esc_html( __( 'You are connected as %s.', 'akismet' ) ), '<b>' . esc_html( $akismet_user->user_login ) . '</b>' ); ?><br /><span class="akismet-jetpack-email"><?php echo esc_html( $akismet_user->user_email ); ?></span></p>
</div>
<?php } elseif ( $akismet_user->status == 'cancelled' ) { ?>
<div class="akismet-box">
<h3><?php esc_html_e( 'Connect via Jetpack', 'akismet' ); ?></h3>
<form name="akismet_activate" id="akismet_activate" action="https://akismet.com/get/" method="post" class="akismet-right" target="_blank">
<input type="hidden" name="passback_url" value="<?php echo esc_url( Akismet_Admin::get_page_url() ); ?>"/>
<input type="hidden" name="blog" value="<?php echo esc_url( get_option( 'home' ) ); ?>"/>
<input type="hidden" name="user_id" value="<?php echo esc_attr( $akismet_user->ID ); ?>"/>
<input type="hidden" name="redirect" value="upgrade"/>
<input type="submit" class="akismet-button akismet-is-primary" value="<?php esc_attr_e( 'Reactivate Akismet' , 'akismet' ); ?>"/>
</form>
<p><?php echo esc_html( sprintf( __( 'Your subscription for %s is cancelled.' , 'akismet' ), $akismet_user->user_email ) ); ?></p>
</div>
<?php } elseif ( $akismet_user->status == 'suspended' ) { ?>
<div class="centered akismet-box">
<h3><?php esc_html_e( 'Connected via Jetpack' , 'akismet' ); ?></h3>
<p class="akismet-alert-text"><?php echo esc_html( sprintf( __( 'Your subscription for %s is suspended.' , 'akismet' ), $akismet_user->user_email ) ); ?></p>
<p><?php esc_html_e( 'No worries! Get in touch and we&#8217;ll sort this out.', 'akismet' ); ?></p>
<p><a href="https://akismet.com/contact" class="akismet-button akismet-is-primary"><?php esc_html_e( 'Contact Akismet support' , 'akismet' ); ?></a></p>
</div>
<?php } else { // ask do they want to use akismet account found using jetpack wpcom connection ?>
<div class="akismet-box">
<h3><?php esc_html_e( 'Connect via Jetpack', 'akismet' ); ?></h3>
<p><?php esc_html_e( 'Use your Jetpack connection to activate Akismet.', 'akismet' ); ?></p>
<form name="akismet_use_wpcom_key" action="<?php echo esc_url( Akismet_Admin::get_page_url() ); ?>" method="post" id="akismet-activate" class="akismet-right">
<input type="hidden" name="key" value="<?php echo esc_attr( $akismet_user->api_key );?>"/>
<input type="hidden" name="action" value="enter-key">
<?php wp_nonce_field( Akismet_Admin::NONCE ) ?>
<input type="submit" class="akismet-button akismet-is-primary" value="<?php esc_attr_e( 'Connect with Jetpack' , 'akismet' ); ?>"/>
</form>
<?php echo get_avatar( $akismet_user->user_email, null, null, null, array( 'class' => 'akismet-jetpack-gravatar' ) ); ?>
<p><?php echo sprintf( esc_html( __( 'You are connected as %s.', 'akismet' ) ), '<b>' . esc_html( $akismet_user->user_login ) . '</b>' ); ?><br /><span class="akismet-jetpack-email"><?php echo esc_html( $akismet_user->user_email ); ?></span></p>
</div>
<?php } ?>
<div class="akismet-box">
<h3><?php esc_html_e( 'Or sign up with a different email address', 'akismet' ); ?></h3>
<div class="akismet-right">
<?php Akismet::view( 'get', array( 'text' => __( 'Sign up with a different email address' , 'akismet' ), 'classes' => array( 'akismet-button' ) ) ); ?>
</div>
<p><?php esc_html_e( 'Choose this option to use Akismet independently of your Jetpack connection.', 'akismet' ); ?></p>
</div>
<?php } else { ?>
<div class="akismet-box">
<h3><?php esc_html_e( 'Activate Akismet' , 'akismet' );?></h3>
<div class="akismet-right">
<?php Akismet::view( 'get', array( 'text' => __( 'Get your API key' , 'akismet' ), 'classes' => array( 'akismet-button', 'akismet-is-primary' ) ) ); ?>
</div>
<p><?php esc_html_e( 'Log in or sign up now.', 'akismet' ); ?></p>
</div>
<?php } ?>
<div class="akismet-box">
<h3><?php esc_html_e( 'Or enter an API key', 'akismet' ); ?></h3>
<p><?php esc_html_e( 'Already have your key? Enter it here.', 'akismet' ); ?> <a href="https://docs.akismet.com/getting-started/api-key/" target="_blank"><?php esc_html_e( '(What is an API key?)', 'akismet' ); ?></a></p>
<form action="<?php echo esc_url( Akismet_Admin::get_page_url() ); ?>" method="post">
<?php wp_nonce_field( Akismet_Admin::NONCE ) ?>
<input type="hidden" name="action" value="enter-key">
<p style="width: 100%; display: flex; flex-wrap: nowrap; box-sizing: border-box;">
<input id="key" name="key" type="text" size="15" value="" class="regular-text code" style="flex-grow: 1; margin-right: 1rem;">
<input type="submit" name="submit" id="submit" class="akismet-button" value="<?php esc_attr_e( 'Connect with API key', 'akismet' );?>">
</p>
</form>
</div>
<?php } else { ?>
<div class="akismet-box">
<h2><?php esc_html_e( 'Manual Configuration', 'akismet' ); ?></h2>
<p><?php echo sprintf( esc_html__( 'An Akismet API key has been defined in the %s file for this site.', 'akismet' ), '<code>wp-config.php</code>' ); ?></p>
</div>
<?php } ?>
</div>
</div>
</div>

View File

@@ -0,0 +1,11 @@
<div id="akismet-plugin-container">
<div class="akismet-masthead">
<div class="akismet-masthead__inside-container">
<a href="<?php echo esc_url( Akismet_Admin::get_page_url() );?>" class="akismet-right"><?php esc_html_e( 'Akismet Settings' , 'akismet' ); ?></a>
<div class="akismet-masthead__logo-container">
<img class="akismet-masthead__logo" src="<?php echo esc_url( plugins_url( '../_inc/img/logo-full-2x.png', __FILE__ ) ); ?>" alt="Akismet" />
</div>
</div>
</div>
<iframe src="<?php echo esc_url( sprintf( '//akismet.com/web/1.0/user-stats.php?blog=%s&api_key=%s&locale=%s', urlencode( get_option( 'home' ) ), Akismet::get_api_key(), get_locale() ) ); ?>" width="100%" height="2500px" frameborder="0"></iframe>
</div>

View File

@@ -0,0 +1,213 @@
<?php
global $wpcom_api_key, $akismet_api_host, $akismet_api_port;
$wpcom_api_key = defined( 'WPCOM_API_KEY' ) ? constant( 'WPCOM_API_KEY' ) : '';
$akismet_api_host = Akismet::get_api_key() . '.rest.akismet.com';
$akismet_api_port = 80;
function akismet_test_mode() {
return Akismet::is_test_mode();
}
function akismet_http_post( $request, $host, $path, $port = 80, $ip = null ) {
$path = str_replace( '/1.1/', '', $path );
return Akismet::http_post( $request, $path, $ip );
}
function akismet_microtime() {
return Akismet::_get_microtime();
}
function akismet_delete_old() {
return Akismet::delete_old_comments();
}
function akismet_delete_old_metadata() {
return Akismet::delete_old_comments_meta();
}
function akismet_check_db_comment( $id, $recheck_reason = 'recheck_queue' ) {
return Akismet::check_db_comment( $id, $recheck_reason );
}
function akismet_rightnow() {
if ( !class_exists( 'Akismet_Admin' ) )
return false;
return Akismet_Admin::rightnow_stats();
}
function akismet_admin_init() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_version_warning() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_load_js_and_css() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_nonce_field( $action = -1 ) {
return wp_nonce_field( $action );
}
function akismet_plugin_action_links( $links, $file ) {
return Akismet_Admin::plugin_action_links( $links, $file );
}
function akismet_conf() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_stats_display() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_stats() {
return Akismet_Admin::dashboard_stats();
}
function akismet_admin_warnings() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_comment_row_action( $a, $comment ) {
return Akismet_Admin::comment_row_actions( $a, $comment );
}
function akismet_comment_status_meta_box( $comment ) {
return Akismet_Admin::comment_status_meta_box( $comment );
}
function akismet_comments_columns( $columns ) {
_deprecated_function( __FUNCTION__, '3.0' );
return $columns;
}
function akismet_comment_column_row( $column, $comment_id ) {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_text_add_link_callback( $m ) {
return Akismet_Admin::text_add_link_callback( $m );
}
function akismet_text_add_link_class( $comment_text ) {
return Akismet_Admin::text_add_link_class( $comment_text );
}
function akismet_check_for_spam_button( $comment_status ) {
return Akismet_Admin::check_for_spam_button( $comment_status );
}
function akismet_submit_nonspam_comment( $comment_id ) {
return Akismet::submit_nonspam_comment( $comment_id );
}
function akismet_submit_spam_comment( $comment_id ) {
return Akismet::submit_spam_comment( $comment_id );
}
function akismet_transition_comment_status( $new_status, $old_status, $comment ) {
return Akismet::transition_comment_status( $new_status, $old_status, $comment );
}
function akismet_spam_count( $type = false ) {
return Akismet_Admin::get_spam_count( $type );
}
function akismet_recheck_queue() {
return Akismet_Admin::recheck_queue();
}
function akismet_remove_comment_author_url() {
return Akismet_Admin::remove_comment_author_url();
}
function akismet_add_comment_author_url() {
return Akismet_Admin::add_comment_author_url();
}
function akismet_check_server_connectivity() {
return Akismet_Admin::check_server_connectivity();
}
function akismet_get_server_connectivity( $cache_timeout = 86400 ) {
return Akismet_Admin::get_server_connectivity( $cache_timeout );
}
function akismet_server_connectivity_ok() {
_deprecated_function( __FUNCTION__, '3.0' );
return true;
}
function akismet_admin_menu() {
return Akismet_Admin::admin_menu();
}
function akismet_load_menu() {
return Akismet_Admin::load_menu();
}
function akismet_init() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_get_key() {
return Akismet::get_api_key();
}
function akismet_check_key_status( $key, $ip = null ) {
return Akismet::check_key_status( $key, $ip );
}
function akismet_update_alert( $response ) {
return Akismet::update_alert( $response );
}
function akismet_verify_key( $key, $ip = null ) {
return Akismet::verify_key( $key, $ip );
}
function akismet_get_user_roles( $user_id ) {
return Akismet::get_user_roles( $user_id );
}
function akismet_result_spam( $approved ) {
return Akismet::comment_is_spam( $approved );
}
function akismet_result_hold( $approved ) {
return Akismet::comment_needs_moderation( $approved );
}
function akismet_get_user_comments_approved( $user_id, $comment_author_email, $comment_author, $comment_author_url ) {
return Akismet::get_user_comments_approved( $user_id, $comment_author_email, $comment_author, $comment_author_url );
}
function akismet_update_comment_history( $comment_id, $message, $event = null ) {
return Akismet::update_comment_history( $comment_id, $message, $event );
}
function akismet_get_comment_history( $comment_id ) {
return Akismet::get_comment_history( $comment_id );
}
function akismet_cmp_time( $a, $b ) {
return Akismet::_cmp_time( $a, $b );
}
function akismet_auto_check_update_meta( $id, $comment ) {
return Akismet::auto_check_update_meta( $id, $comment );
}
function akismet_auto_check_comment( $commentdata ) {
return Akismet::auto_check_comment( $commentdata );
}
function akismet_get_ip_address() {
return Akismet::get_ip_address();
}
function akismet_cron_recheck() {
return Akismet::cron_recheck();
}
function akismet_add_comment_nonce( $post_id ) {
return Akismet::add_comment_nonce( $post_id );
}
function akismet_fix_scheduled_recheck() {
return Akismet::fix_scheduled_recheck();
}
function akismet_spam_comments() {
_deprecated_function( __FUNCTION__, '3.0' );
return array();
}
function akismet_spam_totals() {
_deprecated_function( __FUNCTION__, '3.0' );
return array();
}
function akismet_manage_page() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_caught() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function redirect_old_akismet_urls() {
_deprecated_function( __FUNCTION__, '3.0' );
}
function akismet_kill_proxy_check( $option ) {
_deprecated_function( __FUNCTION__, '3.0' );
return 0;
}
function akismet_pingback_forwarded_for( $r, $url ) {
return Akismet::pingback_forwarded_for( $r, $url );
}
function akismet_pre_check_pingback( $method ) {
return Akismet::pre_check_pingback( $method );
}

View File

@@ -0,0 +1,81 @@
<?php
/**
* @package Hello_Dolly
* @version 1.7
*/
/*
Plugin Name: Hello Dolly
Plugin URI: http://wordpress.org/plugins/hello-dolly/
Description: This is not just a plugin, it symbolizes the hope and enthusiasm of an entire generation summed up in two words sung most famously by Louis Armstrong: Hello, Dolly. When activated you will randomly see a lyric from <cite>Hello, Dolly</cite> in the upper right of your admin screen on every page.
Author: Matt Mullenweg
Version: 1.7
Author URI: http://ma.tt/
*/
function hello_dolly_get_lyric() {
/** These are the lyrics to Hello Dolly */
$lyrics = "Hello, Dolly
Well, hello, Dolly
It's so nice to have you back where you belong
You're lookin' swell, Dolly
I can tell, Dolly
You're still glowin', you're still crowin'
You're still goin' strong
I feel the room swayin'
While the band's playin'
One of our old favorite songs from way back when
So, take her wrap, fellas
Dolly, never go away again
Hello, Dolly
Well, hello, Dolly
It's so nice to have you back where you belong
You're lookin' swell, Dolly
I can tell, Dolly
You're still glowin', you're still crowin'
You're still goin' strong
I feel the room swayin'
While the band's playin'
One of our old favorite songs from way back when
So, golly, gee, fellas
Have a little faith in me, fellas
Dolly, never go away
Promise, you'll never go away
Dolly'll never go away again";
// Here we split it into lines
$lyrics = explode( "\n", $lyrics );
// And then randomly choose a line
return wptexturize( $lyrics[ mt_rand( 0, count( $lyrics ) - 1 ) ] );
}
// This just echoes the chosen line, we'll position it later
function hello_dolly() {
$chosen = hello_dolly_get_lyric();
echo "<p id='dolly'>$chosen</p>";
}
// Now we set that function up to execute when the admin_notices action is called
add_action( 'admin_notices', 'hello_dolly' );
// We need some CSS to position the paragraph
function dolly_css() {
// This makes sure that the positioning is also good for right-to-left languages
$x = is_rtl() ? 'left' : 'right';
echo "
<style type='text/css'>
#dolly {
float: $x;
padding-$x: 15px;
padding-top: 5px;
margin: 0;
font-size: 11px;
}
</style>
";
}
add_action( 'admin_head', 'dolly_css' );
?>

View File

@@ -0,0 +1,2 @@
<?php
// Silence is golden.

View File

@@ -0,0 +1 @@
# Klarna Checkout for WooCommerce plugin

View File

@@ -0,0 +1,162 @@
@font-face {
font-family: Klarna;
src: url("../fonts/KlarnaSans-Regular.otf") format("opentype");
}
@font-face {
font-family: KlarnaHeadline;
font-weight: bold;
src: url("../fonts/KlarnaHeadline-Bold.otf") format("opentype");
}
/* Banner */
#klarna-banner {
background-image: url("../img/admin-banner.jpg");
background-position: center center;
background-size: 100% auto;
padding: 30px;
margin: 0 20px 20px 0;
color: #fff;
position: relative;
}
#kb-spacer {
height: 20px;
}
#klarna-banner > div {
text-align: center;
padding: 0 60px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
font-family: Klarna, sans-serif;
}
#klarna-banner h1 {
font-size: 32px;
line-height: 1.2;
font-weight: bold;
color: #fff;
font-family: KlarnaHeadline, sans-serif;
margin-top: 0;
}
#klarna-banner p {
margin-bottom: 1em;
font-size: 14px;
letter-spacing: -0.2px;
}
#kb-image {
position: absolute;
right: 20px;
bottom: 20px;
z-index: 1;
}
a.kb-button {
display: inline-block;
background: #eb6f93;
color: #fff;
text-decoration: none;
padding: 0.75em 2em;
-webkit-border-radius: 0.5em;
-moz-border-radius: 0.5em;
border-radius: 0.5em;
-webkit-box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
-moz-box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.kb-dismiss {
position: absolute;
top: 5px;
right: 5px;
}
.kb-dismiss:hover {
color: #eb6f93;
cursor: pointer;
}
@media (min-width: 800px) {
#klarna-banner {
overflow: hidden;
}
#klarna-banner > div {
float: left;
width: 50%;
}
#klarna-banner > div > * {
margin-left: auto;
margin-right: auto;
max-width: 500px;
}
}
@media (max-width: 799px) {
#klarna-banner > div {
margin-bottom: 40px;
}
}
/* Sidebar */
@media (min-width: 880px) {
#klarna-wrapper:after {
content: '';
display: block;
clear: both;
}
#klarna-main {
width: 70%;
float: left;
padding: 0 2% 0 0;
border-right: 1px solid #ccc;
box-sizing: border-box;
}
#klarna-sidebar {
width: 28%;
max-width: 350px;
margin-left: 2%;
float: left;
box-sizing: border-box;
text-align: center;
font-family: Klarna, sans-serif;
}
}
#klarna-settings-logo {
display: block;
margin: 20px 0;
}
#klarna-sidebar h3 {
font-family: KlarnaHeadline, sans-serif;
font-size: 24px;
line-height: 1.2;
}
#klarna-sidebar p {
margin-bottom: 1.5em;
}
#klarna-sidebar img {
display: inline-block;
margin: 0 5px;
}
.kb-sidebar-section {
margin-bottom: 40px;
}
.wc-settings-sub-title a, .wc-settings-sub-title a:hover, .wc-settings-sub-title a:click, .wc-settings-sub-title a:visited, .wc-settings-sub-title a:focus {
border:none;
outline:none;
text-decoration:none;
color:black;
}

View File

@@ -0,0 +1,33 @@
#kco-wrapper {
overflow: hidden;
padding: 20px 0;
}
#kco-order-review {
margin-bottom: 50px;
}
.product-name .quantity {
display: inline-block;
}
#kco-order-review table.woocommerce-checkout-review-order-table .product-name {
width: auto;
}
@media only screen and (min-width : 769px) {
#kco-order-review {
float: left;
width: 40%;
padding-right: 20px;
font-size: 0.9em;
box-sizing: border-box;
}
#kco-iframe {
float: right;
width: 60%;
padding-left: 20px;
box-sizing: border-box;
}
}

View File

@@ -0,0 +1 @@
#kco-wrapper{overflow:hidden;padding:20px 0}#kco-order-review{margin-bottom:50px}.product-name .quantity{display:inline-block}#kco-order-review table.woocommerce-checkout-review-order-table .product-name{width:auto}@media only screen and (min-width :769px){#kco-order-review{float:left;width:40%;padding-right:20px;font-size:.9em;box-sizing:border-box}#kco-iframe{float:right;width:60%;padding-left:20px;box-sizing:border-box}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 KiB

View File

@@ -0,0 +1,28 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="480" width="640" viewBox="0 0 640 480">
<defs>
<g id="d">
<g id="b">
<path d="M0-1l-.31.95.477.156z" id="a"/>
<use transform="scale(-1 1)" xlink:href="#a"/>
</g>
<g id="c">
<use transform="rotate(72)" xlink:href="#b"/>
<use transform="rotate(144)" xlink:href="#b"/>
</g>
<use transform="scale(-1 1)" xlink:href="#c"/>
</g>
</defs>
<path fill="#039" d="M0 0h640v480H0z"/>
<g transform="translate(320 242.263) scale(23.7037)" fill="#fc0">
<use height="100%" width="100%" xlink:href="#d" y="-6"/>
<use height="100%" width="100%" xlink:href="#d" y="6"/>
<g id="e">
<use height="100%" width="100%" xlink:href="#d" x="-6"/>
<use height="100%" width="100%" xlink:href="#d" transform="rotate(-144 -2.344 -2.11)"/>
<use height="100%" width="100%" xlink:href="#d" transform="rotate(144 -2.11 -2.344)"/>
<use height="100%" width="100%" xlink:href="#d" transform="rotate(72 -4.663 -2.076)"/>
<use height="100%" width="100%" xlink:href="#d" transform="rotate(72 -5.076 .534)"/>
</g>
<use height="100%" width="100%" xlink:href="#e" transform="scale(-1 1)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg" height="480" width="640" viewBox="0 0 640 480">
<g fill-rule="evenodd" transform="scale(.9375)">
<g stroke-width="1pt">
<path d="M0 0h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0z" fill="#bd3d44"/>
<path d="M0 39.385h972.81V78.77H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0zm0 78.77h972.81v39.385H0z" fill="#fff"/>
</g>
<path fill="#192f5d" d="M0 0h389.12v275.69H0z"/>
<g fill="#fff">
<path d="M32.427 11.8l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.853 0l3.541 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735H93.74zm64.856 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.269 6.734 3.54-10.896-9.269-6.735h11.458zm64.852 0l3.54 10.896h11.457l-9.269 6.735 3.54 10.896-9.268-6.734-9.27 6.734 3.541-10.896-9.27-6.735h11.458zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.27 6.734 3.542-10.896-9.27-6.735h11.458zM64.855 39.37l3.54 10.896h11.458L70.583 57l3.542 10.897-9.27-6.734-9.269 6.734L59.126 57l-9.269-6.734h11.458zm64.852 0l3.54 10.896h11.457L135.435 57l3.54 10.897-9.268-6.734-9.27 6.734L123.978 57l-9.27-6.734h11.458zm64.855 0l3.54 10.896h11.458L200.29 57l3.541 10.897-9.27-6.734-9.268 6.734L188.833 57l-9.269-6.734h11.457zm64.855 0l3.54 10.896h11.458L265.145 57l3.541 10.897-9.269-6.734-9.27 6.734L253.69 57l-9.27-6.734h11.458zm64.852 0l3.54 10.896h11.457L329.997 57l3.54 10.897-9.268-6.734-9.27 6.734L318.54 57l-9.27-6.734h11.458zM32.427 66.939l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.853 0l3.541 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735H93.74zm64.856 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.269 6.734 3.54-10.896-9.269-6.735h11.458zm64.852 0l3.54 10.896h11.457l-9.269 6.735 3.54 10.896-9.268-6.734-9.27 6.734 3.541-10.896-9.27-6.735h11.458zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.27 6.734 3.542-10.896-9.27-6.735h11.458zM64.855 94.508l3.54 10.897h11.458l-9.27 6.734 3.542 10.897-9.27-6.734-9.269 6.734 3.54-10.897-9.269-6.734h11.458zm64.852 0l3.54 10.897h11.457l-9.269 6.734 3.54 10.897-9.268-6.734-9.27 6.734 3.541-10.897-9.27-6.734h11.458zm64.855 0l3.54 10.897h11.458l-9.27 6.734 3.541 10.897-9.27-6.734-9.268 6.734 3.54-10.897-9.269-6.734h11.457zm64.855 0l3.54 10.897h11.458l-9.27 6.734 3.541 10.897-9.269-6.734-9.27 6.734 3.542-10.897-9.27-6.734h11.458zm64.852 0l3.54 10.897h11.457l-9.269 6.734 3.54 10.897-9.268-6.734-9.27 6.734 3.541-10.897-9.27-6.734h11.458zM32.427 122.078l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.853 0l3.541 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735H93.74zm64.856 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.269 6.734 3.54-10.896-9.269-6.735h11.458zm64.852 0l3.54 10.896h11.457l-9.269 6.735 3.54 10.896-9.268-6.734-9.27 6.734 3.541-10.896-9.27-6.735h11.458zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.27 6.734 3.542-10.896-9.27-6.735h11.458zM64.855 149.647l3.54 10.897h11.458l-9.27 6.734 3.542 10.897-9.27-6.734-9.269 6.734 3.54-10.897-9.269-6.734h11.458zm64.852 0l3.54 10.897h11.457l-9.269 6.734 3.54 10.897-9.268-6.734-9.27 6.734 3.541-10.897-9.27-6.734h11.458zm64.855 0l3.54 10.897h11.458l-9.27 6.734 3.541 10.897-9.27-6.734-9.268 6.734 3.54-10.897-9.269-6.734h11.457zm64.855 0l3.54 10.897h11.458l-9.27 6.734 3.541 10.897-9.269-6.734-9.27 6.734 3.542-10.897-9.27-6.734h11.458zm64.852 0l3.54 10.897h11.457l-9.269 6.734 3.54 10.897-9.268-6.734-9.27 6.734 3.541-10.897-9.27-6.734h11.458z"/>
<g>
<path d="M32.427 177.217l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.853 0l3.541 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735H93.74zm64.856 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.269 6.734 3.54-10.896-9.269-6.735h11.458zm64.852 0l3.54 10.896h11.457l-9.269 6.735 3.54 10.896-9.268-6.734-9.27 6.734 3.541-10.896-9.27-6.735h11.458zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.27 6.734 3.542-10.896-9.27-6.735h11.458zM64.855 204.786l3.54 10.897h11.458l-9.27 6.734 3.542 10.897-9.27-6.734-9.269 6.734 3.54-10.897-9.269-6.734h11.458zm64.852 0l3.54 10.897h11.457l-9.269 6.734 3.54 10.897-9.268-6.734-9.27 6.734 3.541-10.897-9.27-6.734h11.458zm64.855 0l3.54 10.897h11.458l-9.27 6.734 3.541 10.897-9.27-6.734-9.268 6.734 3.54-10.897-9.269-6.734h11.457zm64.855 0l3.54 10.897h11.458l-9.27 6.734 3.541 10.897-9.269-6.734-9.27 6.734 3.542-10.897-9.27-6.734h11.458zm64.852 0l3.54 10.897h11.457l-9.269 6.734 3.54 10.897-9.268-6.734-9.27 6.734 3.541-10.897-9.27-6.734h11.458z"/>
</g>
<g>
<path d="M32.427 232.356l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.853 0l3.541 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735H93.74zm64.856 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.269 6.734 3.54-10.896-9.269-6.735h11.458zm64.852 0l3.54 10.896h11.457l-9.269 6.735 3.54 10.896-9.268-6.734-9.27 6.734 3.541-10.896-9.27-6.735h11.458zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.27-6.734-9.268 6.734 3.54-10.896-9.269-6.735h11.457zm64.855 0l3.54 10.896h11.458l-9.27 6.735 3.541 10.896-9.269-6.734-9.27 6.734 3.542-10.896-9.27-6.735h11.458z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

View File

@@ -0,0 +1,42 @@
jQuery( function($) {
'use strict';
var location = kco_admin_params.location;
var titles = $('h3.wc-settings-sub-title');
var tables = $('h3.wc-settings-sub-title + table.form-table');
var submit = $('.wrap.woocommerce p.submit');
titles.append(' <a href="#" style="font-size:12px; font-weight: normal; text-decoration: none"><span class="dashicons dashicons-arrow-down-alt2"></span></a>');
tables.css('marginLeft', '20px').hide();
if(location === 'EU') {
var title = $('#woocommerce_kco_credentials_eu');
$('#woocommerce_kco_credentials_us').find('a').addClass('collapsed');
title.find('a').html('<span class="dashicons dashicons-arrow-up-alt2">');
title.next().show();
} else if( location === 'US') {
var title = $('#woocommerce_kco_credentials_us');
$('#woocommerce_kco_credentials_eu').find('a').addClass('collapsed');
title.find('a').html('<span class="dashicons dashicons-arrow-up-alt2">');
title.next().show();
} else {
var title = titles;
}
$('#woocommerce_kco_color_settings_title').find('a').addClass('collapsed');
titles.find('a').click(function(e) {
e.preventDefault();
if ($(this).hasClass('collapsed')) {
$(this).parent().next().show();
$(this).removeClass('collapsed');
$(this).html('<span class="dashicons dashicons-arrow-up-alt2"></span>');
} else {
$(this).parent().next().hide();
$(this).addClass('collapsed');
$(this).html('<span class="dashicons dashicons-arrow-down-alt2"></span>');
}
});
titles.before('<hr style="margin-top:2em;margin-bottom:2em" />');
submit.before('<hr style="margin-top:2em;margin-bottom:2em" />');
});

View File

@@ -0,0 +1 @@
jQuery(function(e){"use strict";var t=e("h3.wc-settings-sub-title"),s=e("h3.wc-settings-sub-title + table.form-table"),a=e(".wrap.woocommerce p.submit");t.append(' <a href="#" style="font-size:12px; font-weight: normal; text-decoration: none">[expand]</a>'),s.css("marginLeft","20px").hide(),t.find("a").addClass("collapsed"),t.find("a").click(function(t){t.preventDefault(),e(this).hasClass("collapsed")?(e(this).parent().next().show(),e(this).removeClass("collapsed"),e(this).text("[collapse]")):(e(this).parent().next().hide(),e(this).addClass("collapsed"),e(this).text("[expand]"))}),t.before('<hr style="margin-top:2em;margin-bottom:2em" />'),a.before('<hr style="margin-top:2em;margin-bottom:2em" />')});

View File

@@ -0,0 +1,395 @@
/* global kco_params */
jQuery(function($) {
// Check if we have params.
if ( typeof kco_params === 'undefined' ) {
return false;
}
var kco_wc = {
bodyEl: $('body'),
checkoutFormSelector: 'form.checkout',
// Order notes
orderNotesValue: '',
orderNotesSelector: 'textarea#order_comments',
orderNotesEl: $('textarea#order_comments'),
// Order notes
extraFieldsValues: {},
extraFieldsSelectorText: 'div#kco-extra-fields input[type="text"], div#kco-extra-fields input[type="password"], div#kco-extra-fields textarea',
extraFieldsSelectorNonText: 'div#kco-extra-fields select, div#kco-extra-fields input[type="radio"], div#kco-extra-fields input[type="checkbox"], div#kco-extra-fields input.checkout-date-picker, input#terms input[type="checkbox"]',
// Payment method
paymentMethodEl: $('input[name="payment_method"]'),
paymentMethod: '',
selectAnotherSelector: '#klarna-checkout-select-other',
// Form fields
formFields: [],
documentReady: function() {
kco_wc.log(kco_params);
kco_wc.setFormData();
if (kco_wc.paymentMethodEl.length > 0) {
kco_wc.paymentMethod = kco_wc.paymentMethodEl.filter(':checked').val();
} else {
kco_wc.paymentMethod = 'kco';
}
kco_wc.confirmLoading();
},
kcoSuspend: function () {
if (window._klarnaCheckout) {
window._klarnaCheckout(function (api) {
api.suspend();
});
}
},
kcoResume: function () {
if (window._klarnaCheckout) {
window._klarnaCheckout(function (api) {
api.resume();
});
}
},
confirmLoading: function () {
$('#kco-confirm-loading')
.css('minHeight', '300px')
.block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
},
updateCart: function () {
kco_wc.kcoSuspend();
$.ajax({
type: 'POST',
url: kco_params.update_cart_url,
data: {
checkout: $('form.checkout').serialize(),
nonce: kco_params.update_cart_nonce
},
dataType: 'json',
success: function(data) {
},
error: function(data) {
},
complete: function(data) {
$('body').trigger('update_checkout');
kco_wc.kcoResume();
}
});
},
updateExtraFields: function() {
var elementName = $(this).attr('name');
if( elementName === 'terms' ) {
var updatedValue = ( $("input#terms:checked").length === 1 ) ? 1 : '';
} else {
var updatedValue = $(this).val();
}
kco_wc.log('value');
kco_wc.log(updatedValue);
kco_wc.log('name');
kco_wc.log(elementName);
kco_wc.log(typeof kco_wc.extraFieldsValues);
kco_wc.log(kco_wc.extraFieldsValues);
if (null === kco_wc.extraFieldsValues && '' === updatedValue) {
return;
}
if (null !== kco_wc.extraFieldsValues && elementName in kco_wc.extraFieldsValues && updatedValue === kco_wc.extraFieldsValues) {
return;
}
if (null === kco_wc.extraFieldsValues) {
kco_wc.extraFieldsValues = {};
}
kco_wc.log('update');
kco_wc.extraFieldsValues[elementName] = updatedValue;
$.ajax({
type: 'POST',
url: kco_params.update_extra_fields_url,
data: {
extra_fields_values: kco_wc.extraFieldsValues,
nonce: kco_params.update_extra_fields_nonce
},
success: function (data) {},
error: function (data) {},
complete: function (data) {
kco_wc.log('complete', data);
}
});
},
updateOrderNotes: function() {
if (kco_wc.orderNotesEl.val() !== kco_wc.orderNotesValue) {
kco_wc.orderNotesValue = kco_wc.orderNotesEl.val();
$.ajax({
type: 'POST',
url: kco_params.update_order_notes_url,
data: {
order_notes: kco_wc.orderNotesValue,
nonce: kco_params.update_order_notes_nonce
},
success: function (data) {},
error: function (data) {},
complete: function (data) {
kco_wc.log('complete', data);
}
});
}
},
updateKlarnaOrder: function() {
if ('kco' === kco_wc.paymentMethod) {
$.ajax({
type: 'POST',
url: kco_params.update_klarna_order_url,
data: {
nonce: kco_params.update_klarna_order_nonce
},
dataType: 'json',
success: function(data) {
},
error: function(data) {
},
complete: function(data) {
kco_wc.kcoResume();
}
});
}
},
// When "Change to another payment method" is clicked.
changeFromKco: function(e) {
e.preventDefault();
$(kco_wc.checkoutFormSelector).block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
$.ajax({
type: 'POST',
dataType: 'json',
data: {
kco: false,
nonce: kco_params.change_payment_method_nonce
},
url: kco_params.change_payment_method_url,
success: function (data) {},
error: function (data) {},
complete: function (data) {
kco_wc.log(data.responseJSON);
window.location.href = data.responseJSON.data.redirect;
}
});
},
// When payment method is changed to KCO in regular WC Checkout page.
maybeChangeToKco: function() {
kco_wc.log($(this).val());
if ( 'kco' === $(this).val() ) {
$('.woocommerce-info').remove();
$(kco_wc.checkoutFormSelector).block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
$.ajax({
type: 'POST',
data: {
kco: true,
nonce: kco_params.change_payment_method_nonce
},
dataType: 'json',
url: kco_params.change_payment_method_url,
success: function (data) {},
error: function (data) {},
complete: function (data) {
kco_wc.log(data.responseJSON);
window.location.href = data.responseJSON.data.redirect;
}
});
}
},
checkoutError: function() {
if ('kco' === kco_wc.paymentMethod) {
var error_message = $( ".woocommerce-NoticeGroup-checkout" ).text();
$.ajax({
type: 'POST',
dataType: 'json',
data: {
kco: false,
error_message: error_message,
nonce: kco_params.checkout_error_nonce
},
url: kco_params.checkout_error_url,
success: function (data) {
},
error: function (data) {
},
complete: function (data) {
kco_wc.log(data.responseJSON);
window.location.href = data.responseJSON.data.redirect;
}
});
}
},
log: function(message) {
if (kco_params.logging) {
console.log(message);
}
},
setFormData: function() {
var form = $('form[name="checkout"] input');
var i;
var newForm = [];
for ( i = 0; i < form.length; i++ ) {
if ( form[i]['name'] !== '' ) {
var name = form[i]['name'];
var field = $('*[name="' + name + '"]');
var id = field.attr('id');
var label = $('label[for="' + id + '"]');
var check = ( label.has( "abbr" ).length ? true : ( id === 'terms' ) ? true : false );
if ( check === true ) {
var value = ( ! field.is(':checkbox') ) ? form[i].value : ( field.is(":checked") ) ? form[i].value : '';
if( form[i].name === 'terms' ) {
value = ( $("input#terms:checked").length === 1 ) ? 1 : '';
}
newForm.push({
name: form[i].name,
value: value,
required: true
});
}
}
}
kco_wc.formFields = newForm;
kco_wc.saveFormData();
},
saveFormData: function() {
$.ajax({
type: 'POST',
url: kco_params.save_form_data,
data: {
form: kco_wc.formFields,
nonce: kco_params.save_form_data_nonce
},
dataType: 'json',
success: function(data) {
},
error: function(data) {
},
complete: function(data) {
}
});
},
init: function () {
$(document).ready(kco_wc.documentReady);
kco_wc.bodyEl.on('update_checkout', kco_wc.kcoSuspend);
kco_wc.bodyEl.on('updated_checkout', kco_wc.updateKlarnaOrder);
kco_wc.bodyEl.on('checkout_error', kco_wc.checkoutError);
kco_wc.bodyEl.on('change', 'input.qty', kco_wc.updateCart);
kco_wc.bodyEl.on('blur', kco_wc.extraFieldsSelectorText, kco_wc.setFormData);
kco_wc.bodyEl.on('change', kco_wc.extraFieldsSelectorNonText, kco_wc.setFormData);
kco_wc.bodyEl.on('blur', kco_wc.extraFieldsSelectorText, kco_wc.updateExtraFields);
kco_wc.bodyEl.on('change', kco_wc.extraFieldsSelectorNonText, kco_wc.updateExtraFields);
kco_wc.bodyEl.on('change', 'input[name="payment_method"]', kco_wc.maybeChangeToKco);
kco_wc.bodyEl.on('click', kco_wc.selectAnotherSelector, kco_wc.changeFromKco);
kco_wc.bodyEl.on('click', 'input#terms', kco_wc.setFormData)
kco_wc.bodyEl.on('click', 'input#terms', kco_wc.updateExtraFields)
if (typeof window._klarnaCheckout === 'function') {
window._klarnaCheckout(function (api) {
api.on({
'shipping_address_change': function(data) {
kco_wc.log('shipping_address_change');
kco_wc.log(data);
$('.woocommerce-checkout-review-order-table').block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
kco_wc.kcoSuspend();
$.ajax(
{
url: kco_params.iframe_shipping_address_change_url,
type: 'POST',
dataType: 'json',
data: {
data: data,
nonce: kco_params.iframe_shipping_address_change_nonce
},
success: function (response) {
kco_wc.log(response);
$('.woocommerce-checkout-review-order-table').replaceWith(response.data.html);
},
error: function (response) {
kco_wc.log(response);
},
complete: function() {
$('.woocommerce-checkout-review-order-table').unblock();
kco_wc.kcoResume();
}
}
);
},
'change': function(data) {
kco_wc.log('change', data);
},
'order_total_change': function(data) {
kco_wc.log('order_total_change', data);
},
'shipping_option_change': function(data) {
kco_wc.log('shipping_option_change', data);
},
'can_not_complete_order': function(data) {
kco_wc.log('can_not_complete_order', data);
}
});
});
}
}
};
kco_wc.init();
$('body').on('blur', kco_wc.setFormData );
$(document).on("keypress", "#kco-order-review", function(event) {
if (event.keyCode == 13) {
event.preventDefault();
}
});
});

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,98 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* Returns error messages depending on
*
* @class Collector_Checkout_Admin_Notices
* @version 1.0
* @package Collector_Checkout/Classes
* @category Class
* @author Krokedil
*/
class Klarna_Checkout_For_WooCommerce_Admin_Notices {
/**
* The reference the *Singleton* instance of this class.
*
* @var $instance
*/
protected static $instance;
/**
* Checks if KCO gateway is enabled.
*
* @var $enabled
*/
protected $enabled;
/**
* Returns the *Singleton* instance of this class.
*
* @return self::$instance The *Singleton* instance.
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Collector_Checkout_Admin_Notices constructor.
*/
public function __construct() {
$settings = get_option( 'woocommerce_kco_settings' );
$this->enabled = $settings['enabled'];
add_action( 'admin_init', array( $this, 'check_settings' ) );
}
/**
* Checks the settings.
*/
public function check_settings() {
if ( ! empty( $_POST ) ) {
add_action( 'woocommerce_settings_saved', array( $this, 'check_terms' ) );
} else {
add_action( 'admin_notices', array( $this, 'check_terms' ) );
add_action( 'admin_notices', array( $this, 'check_https' ) );
}
}
/**
* Check if terms page is set.
*/
public function check_terms() {
if ( 'yes' !== $this->enabled ) {
return;
}
// Terms page.
if ( ! wc_get_page_id( 'terms' ) || wc_get_page_id( 'terms' ) < 0 ) {
echo '<div class="notice notice-error">';
echo '<p>' . esc_html( __( 'You need to specify a terms page in WooCommerce Settings to be able to use Klarna Checkout.', 'klarna-checkout-for-woocommerce' ) ) . '</p>';
echo '</div>';
}
}
/**
* Check if https is configured.
*/
public function check_https() {
if ( 'yes' !== $this->enabled ) {
return;
}
if ( ! is_ssl() ) {
echo '<div class="notice notice-error">';
echo '<p>' . esc_html( __( 'You need to enable and configure https to be able to use Klarna Checkout.', 'klarna-checkout-for-woocommerce' ) ) . '</p>';
echo '</div>';
}
}
}
Klarna_Checkout_For_WooCommerce_Admin_Notices::get_instance();

View File

@@ -0,0 +1,308 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_AJAX class.
*
* Registers AJAX actions for Klarna Checkout for WooCommerce.
*
* @extends WC_AJAX
*/
class Klarna_Checkout_For_WooCommerce_AJAX extends WC_AJAX {
/**
* Hook in ajax handlers.
*/
public static function init() {
self::add_ajax_events();
}
/**
* Hook in methods - uses WordPress ajax handlers (admin-ajax).
*/
public static function add_ajax_events() {
$ajax_events = array(
'kco_wc_update_cart' => true,
'kco_wc_update_shipping' => true,
'kco_wc_update_extra_fields' => true,
'kco_wc_change_payment_method' => true,
'kco_wc_update_klarna_order' => true,
'kco_wc_iframe_shipping_address_change' => true,
'kco_wc_checkout_error' => true,
'kco_wc_save_form_data' => true,
);
foreach ( $ajax_events as $ajax_event => $nopriv ) {
add_action( 'wp_ajax_woocommerce_' . $ajax_event, array( __CLASS__, $ajax_event ) );
if ( $nopriv ) {
add_action( 'wp_ajax_nopriv_woocommerce_' . $ajax_event, array( __CLASS__, $ajax_event ) );
// WC AJAX can be used for frontend ajax requests.
add_action( 'wc_ajax_' . $ajax_event, array( __CLASS__, $ajax_event ) );
}
}
}
/**
* Cart quantity update function.
*/
public static function kco_wc_update_cart() {
if ( ! wp_verify_nonce( $_POST['nonce'], 'kco_wc_update_cart' ) ) {
wp_send_json_error( 'bad_nonce' );
exit;
}
$values = array();
parse_str( $_POST['checkout'], $values );
$cart = $values['cart'];
foreach ( $cart as $cart_key => $cart_value ) {
$new_quantity = (int) $cart_value['qty'];
WC()->cart->set_quantity( $cart_key, $new_quantity, false );
}
WC()->cart->calculate_shipping();
WC()->cart->calculate_fees();
WC()->cart->calculate_totals();
KCO_WC()->api->request_pre_update_order();
wp_die();
}
/**
* Update shipping method function.
*/
public static function kco_wc_update_shipping() {
if ( ! wp_verify_nonce( $_POST['nonce'], 'kco_wc_update_shipping' ) ) {
wp_send_json_error( 'bad_nonce' );
exit;
}
wc_maybe_define_constant( 'WOOCOMMERCE_CART', true );
$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
if ( isset( $_POST['shipping'] ) && is_array( $_POST['shipping'] ) ) {
foreach ( $_POST['shipping'] as $i => $value ) {
$chosen_shipping_methods[ $i ] = wc_clean( $value );
}
}
WC()->session->set( 'chosen_shipping_methods', $chosen_shipping_methods );
WC()->cart->calculate_shipping();
WC()->cart->calculate_fees();
WC()->cart->calculate_totals();
KCO_WC()->api->request_pre_update_order();
wp_die();
}
/**
* Save order notes value to session and use it when creating the order.
*/
public static function kco_wc_update_extra_fields() {
if ( ! wp_verify_nonce( $_POST['nonce'], 'kco_wc_update_extra_fields' ) ) {
wp_send_json_error( 'bad_nonce' );
exit;
}
if ( is_array( $_POST['extra_fields_values'] ) ) {
$update_values = array_map( 'sanitize_textarea_field', $_POST['extra_fields_values'] );
$session_values = WC()->session->get( 'kco_wc_extra_fields_values', array() );
// Update session array, instead of overwriting it.
foreach ( $update_values as $update_key => $update_value ) {
$session_values[ $update_key ] = $update_value;
}
WC()->session->set( 'kco_wc_extra_fields_values', $session_values );
}
wp_die();
}
/**
* Refresh checkout fragment.
*/
public static function kco_wc_change_payment_method() {
if ( ! wp_verify_nonce( $_POST['nonce'], 'kco_wc_change_payment_method' ) ) {
wp_send_json_error( 'bad_nonce' );
exit;
}
$available_gateways = WC()->payment_gateways()->get_available_payment_gateways();
if ( 'false' === $_POST['kco'] ) {
// Set chosen payment method to first gateway that is not Klarna Checkout for WooCommerce.
$first_gateway = reset( $available_gateways );
if ( 'kco' !== $first_gateway->id ) {
WC()->session->set( 'chosen_payment_method', $first_gateway->id );
} else {
$second_gateway = next( $available_gateways );
WC()->session->set( 'chosen_payment_method', $second_gateway->id );
}
} else {
WC()->session->set( 'chosen_payment_method', 'kco' );
}
WC()->payment_gateways()->set_current_gateway( $available_gateways );
$redirect = wc_get_checkout_url();
$data = array(
'redirect' => $redirect,
);
wp_send_json_success( $data );
wp_die();
}
/**
* Updates Klarna order.
*/
public static function kco_wc_update_klarna_order() {
if ( 'kco' === WC()->session->get( 'chosen_payment_method' ) ) {
$klarna_order_id = KCO_WC()->api->get_order_id_from_session();
$klarna_order = KCO_WC()->api->request_pre_retrieve_order( $klarna_order_id );
if ( 'checkout_incomplete' === $klarna_order->status ) {
WC()->cart->calculate_shipping();
WC()->cart->calculate_fees();
WC()->cart->calculate_totals();
KCO_WC()->api->request_pre_update_order();
}
}
wp_send_json_success();
wp_die();
}
/**
* Iframe change callback function.
*/
public static function kco_wc_iframe_shipping_address_change() {
if ( ! wp_verify_nonce( $_POST['nonce'], 'kco_wc_iframe_shipping_address_change' ) ) {
wp_send_json_error( 'bad_nonce' );
exit;
}
if ( isset( $_REQUEST['data'] ) && is_array( $_REQUEST['data'] ) ) {
$address = array_map( 'sanitize_text_field', $_REQUEST['data'] );
}
$customer_data = array();
if ( isset( $address['email'] ) ) {
$customer_data['email'] = $address['email'];
$customer_data['billing_email'] = $address['email'];
}
if ( isset( $address['postal_code'] ) ) {
$customer_data['postcode'] = $address['postal_code'];
$customer_data['billing_postcode'] = $address['postal_code'];
$customer_data['shipping_postcode'] = $address['postal_code'];
}
if ( isset( $address['given_name'] ) ) {
$customer_data['first_name'] = $address['given_name'];
$customer_data['billing_first_name'] = $address['given_name'];
$customer_data['shipping_first_name'] = $address['given_name'];
}
if ( isset( $address['family_name'] ) ) {
$customer_data['last_name'] = $address['family_name'];
$customer_data['billing_last_name'] = $address['family_name'];
$customer_data['shipping_last_name'] = $address['family_name'];
}
if ( isset( $address['region'] ) ) {
$customer_data['state'] = $address['region'];
$customer_data['billing_state'] = $address['region'];
$customer_data['shipping_state'] = $address['region'];
}
if ( isset( $address['country'] ) && kco_wc_country_code_converter( $address['country'] ) ) {
$country = kco_wc_country_code_converter( $address['country'] );
$customer_data['country'] = $country;
$customer_data['billing_country'] = $country;
$customer_data['shipping_country'] = $country;
}
WC()->customer->set_props( $customer_data );
WC()->customer->save();
WC()->cart->calculate_shipping();
WC()->cart->calculate_totals();
KCO_WC()->api->request_pre_update_order();
ob_start();
woocommerce_order_review();
$html = ob_get_clean();
wp_send_json_success( array( 'html' => $html ) );
wp_die();
}
/**
* Handles WooCommerce checkout error, after Klarna order has already been created.
*/
public static function kco_wc_checkout_error() {
if ( ! wp_verify_nonce( $_POST['nonce'], 'kco_wc_checkout_error' ) ) { // Input var okay.
wp_send_json_error( 'bad_nonce' );
exit;
}
if ( ! empty( $_POST['error_message'] ) ) { // Input var okay.
$error_message = 'Error message: ' . sanitize_text_field( trim( $_POST['error_message'] ) );
} else {
$error_message = 'Error message could not be retreived';
}
KCO_WC()->logger->log( 'Checkout form submission failed. Starting fallback order creation.... ' . $error_message );
krokedil_log_events( null, 'Checkout form submission failed', $error_message );
if ( ! empty( $_GET['kco_wc_order_id'] ) ) { // Input var okay.
$klarna_order_id = $_GET['kco_wc_order_id'];
} else {
$klarna_order_id = KCO_WC()->api->get_order_id_from_session();
}
// Create order via fallback sequence
$order = Klarna_Checkout_For_WooCommerce_Create_Local_Order_Fallback::create( $klarna_order_id, $error_message );
if( is_object( $order ) ) {
KCO_WC()->logger->log( 'Fallback order creation done. Redirecting customer to thank you page.' );
krokedil_log_events( null, 'Fallback order creation done. Redirecting customer to thank you page.', '' );
$note = sprintf( __( 'This order was made as a fallback due to an error in the checkout (%s). Please verify the order with Klarna.', 'klarna-checkout-for-woocommerce' ), $error_message );
$order->add_order_note( $note );
$redirect_url = $order->get_checkout_order_received_url();
} else {
KCO_WC()->logger->log( 'Fallback order creation ERROR. Redirecting customer to simplified thank you page.' . json_decode( $order ) );
krokedil_log_events( null, 'Fallback order creation ERROR. Redirecting customer to simplified thank you page.', $order );
$redirect_url = wc_get_endpoint_url( 'order-received', '', wc_get_page_permalink( 'checkout' ) );
$redirect_url = add_query_arg( 'kco_wc', 'true', $redirect_url );
}
wp_send_json_success( array( 'redirect' => $redirect_url ) );
wp_die();
}
/**
* Handles the saving of form data to transient.
*/
public static function kco_wc_save_form_data() {
if ( ! wp_verify_nonce( $_POST['nonce'], 'kco_wc_save_form_data' ) ) { // Input var okay.
wp_send_json_error( 'bad_nonce' );
exit;
}
$form = $_POST['form'];
set_transient( WC()->session->get( 'kco_wc_order_id' ), $form, 60 * 60 * 24 );
wp_send_json_success();
wp_die();
}
}
Klarna_Checkout_For_WooCommerce_AJAX::init();

View File

@@ -0,0 +1,462 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_API_Callbacks class.
*
* Class that handles KCO API callbacks.
*/
class Klarna_Checkout_For_WooCommerce_API_Callbacks {
/**
* The reference the *Singleton* instance of this class.
*
* @var $instance
*/
protected static $instance;
/**
* Returns the *Singleton* instance of this class.
*
* @return self::$instance The *Singleton* instance.
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Klarna_Checkout_For_WooCommerce_API_Callbacks constructor.
*/
public function __construct() {
add_action( 'woocommerce_api_kco_wc_push', array( $this, 'push_cb' ) );
add_action( 'woocommerce_api_kco_wc_notification', array( $this, 'notification_cb' ) );
// add_action( 'woocommerce_api_kco_wc_country_change', array( $this, 'country_change_cb' ) );
add_action( 'woocommerce_api_kco_wc_validation', array( $this, 'validation_cb' ) );
add_action( 'woocommerce_api_kco_wc_shipping_option_update', array( $this, 'shipping_option_update_cb' ) );
add_action( 'woocommerce_api_kco_wc_address_update', array( $this, 'address_update_cb' ) );
add_action( 'kco_wc_punted_notification', array( $this, 'kco_wc_punted_notification_cb' ), 10, 2 );
}
/**
* Push callback function.
*/
public function push_cb() {
/**
* 1. Handle POST request
* 2. Request the order from Klarna
* 3. Backup order creation
* 4. Acknowledge the order
* 5. Send merchant_reference1
*/
// Do nothing if there's no Klarna Checkout order ID.
if ( ! $_GET['kco_wc_order_id'] ) {
return;
}
$klarna_order_id = sanitize_key( $_GET['kco_wc_order_id'] );
$query_args = array(
'fields' => 'ids',
'post_type' => wc_get_order_types(),
'post_status' => array_keys( wc_get_order_statuses() ),
'meta_key' => '_wc_klarna_order_id',
'meta_value' => $klarna_order_id,
);
$orders = get_posts( $query_args );
// If zero matching orders were found, create backup order.
if ( empty( $orders ) ) {
// Backup order creation.
$this->backup_order_creation( $klarna_order_id );
return;
}
$order_id = $orders[0];
$order = wc_get_order( $order_id );
if ( $order ) {
// The order was already created. Check if order status was set (in thankyou page).
if ( ! $order->has_status( array( 'on-hold', 'processing', 'completed' ) ) ) {
$response = KCO_WC()->api->request_post_get_order( $klarna_order_id );
$klarna_order = json_decode( $response['body'] );
krokedil_log_events( $order_id, 'Klarna push callback. Updating order status.', $klarna_order );
if ( 'ACCEPTED' === $klarna_order->fraud_status ) {
$order->payment_complete( $klarna_order_id );
// translators: Klarna order ID.
$note = sprintf( __( 'Payment via Klarna Checkout, order ID: %s', 'klarna-checkout-for-woocommerce' ), sanitize_key( $klarna_order->order_id ) );
$order->add_order_note( $note );
} elseif ( 'REJECTED' === $klarna_order->fraud_status ) {
$order->update_status( 'on-hold', __( 'Klarna Checkout order was rejected.', 'klarna-checkout-for-woocommerce' ) );
} elseif ( 'PENDING' === $klarna_order->fraud_status ) {
// translators: Klarna order ID.
$note = sprintf( __( 'Klarna order is under review, order ID: %s.', 'klarna-checkout-for-woocommerce' ), sanitize_key( $klarna_order->order_id ) );
$order->update_status( 'on-hold', $note );
}
KCO_WC()->api->request_post_acknowledge_order( $klarna_order_id );
KCO_WC()->api->request_post_set_merchant_reference(
$klarna_order_id,
array(
'merchant_reference1' => $order->get_order_number(),
'merchant_reference2' => $order->get_id(),
)
);
} else {
krokedil_log_events( $order_id, 'Klarna push callback. Order status already set to On hold/Processing/Completed.', $klarna_order );
}
} else {
// Backup order creation.
$this->backup_order_creation( $klarna_order_id );
}
}
/**
* Notification callback function, used for pending orders.
*/
public function notification_cb() {
/**
* Notification callback URL has Klarna Order ID (kco_wc_order_id) in it.
*
* 1. Get Klarna Order ID
* 2. Try to find matching WooCommerce order, to see if it was created
* 3. If WooCommerce order does not exist, that means regular creation failed AND confirmation callback
* either hasn't happened yet or failed. In this case, schedule a single event, 5 minutes from now
* and try to get WooCommerce order then.
* 4. If WooCommerce order does exist, fire the hook.
*/
$order_id = '';
if ( $_GET['kco_wc_order_id'] ) { // KCO.
$klarna_order_id = sanitize_key( $_GET['kco_wc_order_id'] );
$query_args = array(
'fields' => 'ids',
'post_type' => wc_get_order_types(),
'post_status' => array_keys( wc_get_order_statuses() ),
'meta_key' => '_wc_klarna_order_id',
'meta_value' => $klarna_order_id,
);
$orders = get_posts( $query_args );
// If zero matching orders were found, return.
if ( ! empty( $orders ) ) {
$order_id = $orders[0];
}
}
if ( '' !== $order_id ) {
do_action( 'wc_klarna_notification_listener' );
} else {
$post_body = file_get_contents( 'php://input' );
$data = json_decode( $post_body, true );
krokedil_log_events( $order_id, 'Klarna notification callback data', $data );
wp_schedule_single_event( time() + 300, 'kco_wc_punted_notification', array( $klarna_order_id, $data ) );
}
}
/**
* Punted notification callback.
*
* @param string $klarna_order_id Klarna order ID.
*/
public function kco_wc_punted_notification_cb( $klarna_order_id, $data ) {
do_action( 'wc_klarna_notification_listener', $klarna_order_id, $data );
}
/**
* Order validation callback function.
* Response must be sent to Klarna API.
*
* @link https://developers.klarna.com/api/#checkout-api-callbacks-order-validation
*/
public function validation_cb() {
$post_body = file_get_contents( 'php://input' );
$data = json_decode( $post_body, true );
krokedil_log_events( null, 'Klarna validation callback data', $data );
$all_in_stock = true;
$shipping_chosen = false;
$form_data = get_transient( $data['order_id'] );
$has_required_data = true;
$failed_required_check = array();
foreach ( $form_data as $form_row ) {
if ( isset( $form_row['required'] ) && '' === $form_row['value'] ) {
$has_required_data = false;
wc_add_notice( 'test', 'error' );
$failed_required_check[] = $form_row['name'];
}
}
// Check stock for each item and shipping method.
$cart_items = $data['order_lines'];
foreach ( $cart_items as $cart_item ) {
if ( 'physical' === $cart_item['type'] ) {
// Get product by SKU or ID.
if ( wc_get_product_id_by_sku( $cart_item['reference'] ) ) {
$cart_item_product = wc_get_product( wc_get_product_id_by_sku( $cart_item['reference'] ) );
} else {
$cart_item_product = wc_get_product( $cart_item['reference'] );
}
if ( $cart_item_product ) {
if ( ! $cart_item_product->has_enough_stock( $cart_item['quantity'] ) ) {
$all_in_stock = false;
}
if( ! $cart_item_product->is_virtual() ) {
$needs_shipping = true;
}
}
} elseif ( 'shipping_fee' === $cart_item['type'] ) {
$shipping_chosen = true;
}
}
do_action( 'kco_validate_checkout', $data, $all_in_stock, $shipping_chosen );
if ( $all_in_stock && $shipping_chosen && $has_required_data ) {
header( 'HTTP/1.0 200 OK' );
} else {
header( 'HTTP/1.0 303 See Other' );
if ( ! $all_in_stock ) {
$logger = new WC_Logger();
$logger->add( 'klarna-checkout-for-woocommerce', 'Stock validation failed for SKU ' . $cart_item['reference'] );
header( 'Location: ' . wc_get_cart_url() . '?stock_validate_failed' );
} elseif ( ! $shipping_chosen && $needs_shipping ) {
header( 'Location: ' . wc_get_checkout_url() . '?no_shipping' );
} elseif ( ! $has_required_data ) {
$validation_hash = base64_encode( json_encode( $failed_required_check ) );
header( 'Location: ' . wc_get_checkout_url() . '?required_fields=' . $validation_hash );
}
}
}
/**
* Shipping option update callback function.
* Response must be sent to Klarna API.
*
* @link https://developers.klarna.com/api/#checkout-api-callbacks-shipping-option-update
*/
public function shipping_option_update_cb() {
// Send back order amount, order tax amount, order lines, purchase currency and status 200
}
/**
* Address update callback function.
* Response must be sent to Klarna API.
*
* @link https://developers.klarna.com/api/#checkout-api-callbacks-address-update
* @ref https://github.com/mmartche/coach/blob/30022c266089fc7499c54e149883e951c288dc9f/catalog/controller/extension/payment/klarna_checkout.php#L509
*/
public function address_update_cb() {
// Currently disabled, because of how response body needs to be calculated in WooCommerce.
}
/**
* Backup order creation, in case checkout process failed.
*
* @param string $klarna_order_id Klarna order ID.
*
* @throws Exception WC_Data_Exception.
*/
public function backup_order_creation( $klarna_order_id ) {
$response = KCO_WC()->api->request_post_get_order( $klarna_order_id );
$klarna_order = json_decode( $response['body'] );
// Process customer data.
$this->process_customer_data( $klarna_order );
// Process customer data.
$this->process_cart( $klarna_order );
// Process order.
$this->process_order( $klarna_order );
}
/**
* Processes customer data on backup order creation.
*
* @param Klarna_Checkout_Order $klarna_order Klarna order.
*
* @throws Exception WC_Data_Exception.
*/
private function process_customer_data( $klarna_order ) {
// First name.
WC()->customer->set_billing_first_name( sanitize_text_field( $klarna_order->billing_address->given_name ) );
WC()->customer->set_shipping_first_name( sanitize_text_field( $klarna_order->shipping_address->given_name ) );
// Last name.
WC()->customer->set_billing_last_name( sanitize_text_field( $klarna_order->billing_address->family_name ) );
WC()->customer->set_shipping_last_name( sanitize_text_field( $klarna_order->shipping_address->family_name ) );
// Country.
WC()->customer->set_billing_country( sanitize_text_field( $klarna_order->billing_address->country ) );
WC()->customer->set_shipping_country( sanitize_text_field( $klarna_order->shipping_address->country ) );
// Street address 1.
WC()->customer->set_billing_address_1( sanitize_text_field( $klarna_order->billing_address->street_address ) );
WC()->customer->set_shipping_address_1( sanitize_text_field( $klarna_order->shipping_address->street_address ) );
// Street address 2.
WC()->customer->set_billing_address_2( sanitize_text_field( $klarna_order->billing_address->street_address2 ) );
WC()->customer->set_shipping_address_2( sanitize_text_field( $klarna_order->shipping_address->street_address2 ) );
// City.
WC()->customer->set_billing_city( sanitize_text_field( $klarna_order->billing_address->city ) );
WC()->customer->set_shipping_city( sanitize_text_field( $klarna_order->shipping_address->city ) );
// County/State.
WC()->customer->set_billing_state( sanitize_text_field( $klarna_order->billing_address->region ) );
WC()->customer->set_shipping_state( sanitize_text_field( $klarna_order->shipping_address->region ) );
// Postcode.
WC()->customer->set_billing_postcode( sanitize_text_field( $klarna_order->billing_address->postal_code ) );
WC()->customer->set_shipping_postcode( sanitize_text_field( $klarna_order->shipping_address->postal_code ) );
// Phone.
WC()->customer->set_billing_phone( sanitize_text_field( $klarna_order->billing_address->phone ) );
// Email.
WC()->customer->set_billing_email( sanitize_text_field( $klarna_order->billing_address->email ) );
WC()->customer->save();
}
/**
* Processes cart contents on backup order creation.
*
* @param Klarna_Checkout_Order $klarna_order Klarna order.
*
* @throws Exception WC_Data_Exception.
*/
private function process_cart( $klarna_order ) {
WC()->cart->empty_cart();
foreach ( $klarna_order->order_lines as $cart_item ) {
if ( 'physical' === $cart_item->type ) {
if ( wc_get_product_id_by_sku( $cart_item->reference ) ) {
$id = wc_get_product_id_by_sku( $cart_item->reference );
} else {
$id = $cart_item->reference;
}
try {
WC()->cart->add_to_cart( $id, $cart_item->quantity );
} catch ( Exception $e ) {
$logger = new WC_Logger();
$logger->add( 'klarna-checkout-for-woocommerce', 'Backup order creation error add to cart error: ' . $e->getCode() . ' - ' . $e->getMessage() );
}
}
}
WC()->cart->calculate_shipping();
WC()->cart->calculate_fees();
WC()->cart->calculate_totals();
// Check cart items (quantity, coupon validity etc).
if ( ! WC()->cart->check_cart_items() ) {
return;
}
WC()->cart->check_cart_coupons();
}
/**
* Processes WooCommerce order on backup order creation.
*
* @param Klarna_Checkout_Order $klarna_order Klarna order.
*
* @throws Exception WC_Data_Exception.
*/
private function process_order( $klarna_order ) {
try {
$order = new WC_Order();
$order->set_billing_first_name( sanitize_text_field( $klarna_order->billing_address->given_name ) );
$order->set_billing_last_name( sanitize_text_field( $klarna_order->billing_address->family_name ) );
$order->set_billing_country( sanitize_text_field( $klarna_order->billing_address->country ) );
$order->set_billing_address_1( sanitize_text_field( $klarna_order->billing_address->street_address ) );
$order->set_billing_address_2( sanitize_text_field( $klarna_order->billing_address->street_address2 ) );
$order->set_billing_city( sanitize_text_field( $klarna_order->billing_address->city ) );
$order->set_billing_state( sanitize_text_field( $klarna_order->billing_address->region ) );
$order->set_billing_postcode( sanitize_text_field( $klarna_order->billing_address->postal_code ) );
$order->set_billing_phone( sanitize_text_field( $klarna_order->billing_address->phone ) );
$order->set_billing_email( sanitize_text_field( $klarna_order->billing_address->email ) );
$order->set_shipping_first_name( sanitize_text_field( $klarna_order->shipping_address->given_name ) );
$order->set_shipping_last_name( sanitize_text_field( $klarna_order->shipping_address->family_name ) );
$order->set_shipping_country( sanitize_text_field( $klarna_order->shipping_address->country ) );
$order->set_shipping_address_1( sanitize_text_field( $klarna_order->shipping_address->street_address ) );
$order->set_shipping_address_2( sanitize_text_field( $klarna_order->shipping_address->street_address2 ) );
$order->set_shipping_city( sanitize_text_field( $klarna_order->shipping_address->city ) );
$order->set_shipping_state( sanitize_text_field( $klarna_order->shipping_address->region ) );
$order->set_shipping_postcode( sanitize_text_field( $klarna_order->shipping_address->postal_code ) );
$order->set_created_via( 'klarna_checkout_backup_order_creation' );
$order->set_currency( sanitize_text_field( $klarna_order->purchase_currency ) );
$order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
$order->set_payment_method( 'kco' );
$order->set_shipping_total( WC()->cart->get_shipping_total() );
$order->set_discount_total( WC()->cart->get_discount_total() );
$order->set_discount_tax( WC()->cart->get_discount_tax() );
$order->set_cart_tax( WC()->cart->get_cart_contents_tax() + WC()->cart->get_fee_tax() );
$order->set_shipping_tax( WC()->cart->get_shipping_tax() );
$order->set_total( WC()->cart->get_total( 'edit' ) );
WC()->checkout()->create_order_line_items( $order, WC()->cart );
WC()->checkout()->create_order_fee_lines( $order, WC()->cart );
WC()->checkout()->create_order_shipping_lines( $order, WC()->session->get( 'chosen_shipping_methods' ), WC()->shipping->get_packages() );
WC()->checkout()->create_order_tax_lines( $order, WC()->cart );
WC()->checkout()->create_order_coupon_lines( $order, WC()->cart );
$order->save();
if ( 'ACCEPTED' === $klarna_order->fraud_status ) {
$order->payment_complete( $klarna_order->order_id );
// translators: Klarna order ID.
$note = sprintf( __( 'Payment via Klarna Checkout, order ID: %s', 'klarna-checkout-for-woocommerce' ), sanitize_key( $klarna_order->order_id ) );
$order->add_order_note( $note );
} elseif ( 'REJECTED' === $klarna_order->fraud_status ) {
$order->update_status( 'on-hold', __( 'Klarna Checkout order was rejected.', 'klarna-checkout-for-woocommerce' ) );
} elseif ( 'PENDING' === $klarna_order->fraud_status ) {
// translators: Klarna order ID.
$note = sprintf( __( 'Klarna order is under review, order ID: %s.', 'klarna-checkout-for-woocommerce' ), sanitize_key( $klarna_order->order_id ) );
$order->update_status( 'on-hold', $note );
}
KCO_WC()->api->request_post_acknowledge_order( $klarna_order->order_id );
KCO_WC()->api->request_post_set_merchant_reference(
$klarna_order->order_id,
array(
'merchant_reference1' => $order->get_order_number(),
'merchant_reference2' => $order->get_id(),
)
);
if ( (int) round( $order->get_total() * 100 ) !== (int) $klarna_order->order_amount ) {
$order->update_status( 'on-hold', sprintf(__( 'Order needs manual review, WooCommerce total and Klarna total do not match. Klarna order total: %s.', 'klarna-checkout-for-woocommerce' ), $klarna_order->order_amount ) );
}
} catch ( Exception $e ) {
$logger = new WC_Logger();
$logger->add( 'klarna-checkout-for-woocommerce', 'Backup order creation error: ' . $e->getCode() . ' - ' . $e->getMessage() );
}
}
}
Klarna_Checkout_For_WooCommerce_API_Callbacks::get_instance();

View File

@@ -0,0 +1,703 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_API class.
*
* Class that talks to KCO API, wrapper for V2 and V3.
*/
class Klarna_Checkout_For_WooCommerce_API {
/**
* Klarna Checkout for WooCommerce settings.
*
* @var array
*/
private $settings = array();
/**
* Klarna_Checkout_For_WooCommerce_API constructor.
*/
public function __construct() {
$this->settings = get_option( 'woocommerce_kco_settings' );
}
/**
* Creates Klarna Checkout order.
*
* @return array|mixed|object|WP_Error
*/
public function request_pre_create_order() {
$request_url = $this->get_api_url_base() . 'checkout/v3/orders';
$request_args = array(
'headers' => $this->get_request_headers(),
'user-agent' => $this->get_user_agent(),
'body' => $this->get_request_body( 'create' ),
);
$log_array = array(
'headers' => $request_args['headers'],
'user-agent' => $request_args['user-agent'],
'body' => json_decode( $request_args['body'] ),
);
KCO_WC()->logger->log( 'Create Klarna order (' . $request_url . ') ' . json_encode( $request_args ) );
krokedil_log_events( null, 'Pre Create Order request args', $log_array );
$response = wp_safe_remote_post( $request_url, $request_args );
if ( is_wp_error( $response ) ) {
return $this->extract_error_messages( $response );
}
if ( $response['response']['code'] >= 200 && $response['response']['code'] <= 299 ) {
$klarna_order = json_decode( $response['body'] );
$this->save_order_id_to_session( sanitize_key( $klarna_order->order_id ) );
$this->save_order_api_to_session( $klarna_order );
$log_order = clone $klarna_order;
$log_order->html_snippet = '';
krokedil_log_events( null, 'Pre Create Order response', $log_order );
return $klarna_order;
} else if( $response['response']['code'] === 405 ) {
$error = $response['response']['message'];
return $error;
} else {
$error = $this->extract_error_messages( $response );
krokedil_log_events( null, 'Pre Create Order response', $error );
return $error;
}
}
/**
* Retrieve ongoing Klarna order.
*
* @param string $klarna_order_id Klarna order ID.
*
* @return object $klarna_order Klarna order.
*/
public function request_pre_retrieve_order( $klarna_order_id ) {
$request_url = $this->get_api_url_base() . 'checkout/v3/orders/' . $klarna_order_id;
$request_args = array(
'headers' => $this->get_request_headers(),
'user-agent' => $this->get_user_agent(),
);
krokedil_log_events( null, 'Pre Retrieve Order request args', $request_args );
KCO_WC()->logger->log( 'Retrieve ongoing Klarna order (' . $request_url . ') ' . json_encode( $request_args ) );
$response = wp_safe_remote_get( $request_url, $request_args );
if ( $response['response']['code'] >= 200 && $response['response']['code'] <= 299 ) {
$klarna_order = json_decode( $response['body'] );
$log_order = clone $klarna_order;
$log_order->html_snippet = '';
krokedil_log_events( null, 'Pre Retrieve Order response', $log_order );
return $klarna_order;
} else {
$error = $this->extract_error_messages( $response );
krokedil_log_events( null, 'Pre Retrieve Order response', $error );
return $error;
}
}
/**
* Update ongoing Klarna order.
*
* @return object $klarna_order Klarna order.
*/
public function request_pre_update_order() {
$klarna_order_id = $this->get_order_id_from_session();
$request_url = $this->get_api_url_base() . 'checkout/v3/orders/' . $klarna_order_id;
$request_args = array(
'headers' => $this->get_request_headers(),
'user-agent' => $this->get_user_agent(),
'body' => $this->get_request_body(),
);
$log_array = array(
'headers' => $request_args['headers'],
'user-agent' => $request_args['user-agent'],
'body' => json_decode( $request_args['body'] ),
);
// No update if nothing changed in data being sent to Klarna.
if ( WC()->session->get( 'kco_wc_update_md5' ) && WC()->session->get( 'kco_wc_update_md5' ) === md5( serialize( $request_args ) ) ) {
return;
}
krokedil_log_events( null, 'Pre Update Order request args', $log_array );
KCO_WC()->logger->log( 'Update ongoing Klarna order (' . $request_url . ') ' . json_encode( $request_args ) );
$response = wp_safe_remote_post( $request_url, $request_args );
if ( $response['response']['code'] >= 200 && $response['response']['code'] <= 299 ) {
WC()->session->set( 'kco_wc_update_md5', md5( serialize( $request_args ) ) );
$klarna_order = json_decode( $response['body'] );
$log_order = clone $klarna_order;
$log_order->html_snippet = '';
krokedil_log_events( null, 'Pre Update Order response', $log_order );
return $klarna_order;
} else {
WC()->session->__unset( 'kco_wc_update_md5' );
WC()->session->__unset( 'kco_wc_order_id' );
$error = $this->extract_error_messages( $response );
krokedil_log_events( null, 'Pre Update Order response', $error );
return $error;
}
}
/**
* Acknowledges Klarna Checkout order.
*
* @param string $klarna_order_id Klarna order ID.
*
* @return WP_Error|array $response
*/
public function request_post_get_order( $klarna_order_id ) {
$request_url = $this->get_api_url_base() . 'ordermanagement/v1/orders/' . $klarna_order_id;
$request_args = array(
'headers' => $this->get_request_headers(),
'user-agent' => $this->get_user_agent(),
);
$response = wp_safe_remote_get( $request_url, $request_args );
krokedil_log_events( null, 'Post Get Order response', stripslashes_deep( $response ) );
KCO_WC()->logger->log( 'Post Get Order response (' . $request_url . ') ' . stripslashes_deep( json_encode( $response ) ) );
return $response;
}
/**
* Acknowledges Klarna Checkout order.
*
* @param string $klarna_order_id Klarna order ID.
*
* @return WP_Error|array $response
*/
public function request_post_acknowledge_order( $klarna_order_id ) {
$request_url = $this->get_api_url_base() . 'ordermanagement/v1/orders/' . $klarna_order_id . '/acknowledge';
$request_args = array(
'headers' => $this->get_request_headers(),
'user-agent' => $this->get_user_agent(),
);
$response = wp_safe_remote_post( $request_url, $request_args );
return $response;
}
/**
* Adds WooCommerce order ID to Klarna order as merchant_reference. And clear Klarna order ID value from WC session.
*
* @param string $klarna_order_id Klarna order ID.
* @param array $merchant_references Array of merchant references.
*
* @return WP_Error|array $response
*/
public function request_post_set_merchant_reference( $klarna_order_id, $merchant_references ) {
$request_url = $this->get_api_url_base() . 'ordermanagement/v1/orders/' . $klarna_order_id . '/merchant-references';
$request_args = array(
'headers' => $this->get_request_headers(),
'user-agent' => $this->get_user_agent(),
'method' => 'PATCH',
'body' => wp_json_encode( array(
'merchant_reference1' => $merchant_references['merchant_reference1'],
'merchant_reference2' => $merchant_references['merchant_reference2'],
) ),
);
$response = wp_safe_remote_request( $request_url, $request_args );
return $response;
}
/**
* Gets Klarna API URL base.
*/
public function get_api_url_base() {
$base_location = wc_get_base_location();
$country_string = 'US' === $base_location['country'] ? '-na' : '';
$test_string = 'yes' === $this->settings['testmode'] ? '.playground' : '';
return 'https://api' . $country_string . $test_string . '.klarna.com/';
}
/**
* Gets Klarna order from WC_Session
*
* @return array|string
*/
public function get_order_id_from_session() {
return WC()->session->get( 'kco_wc_order_id' );
}
/**
* Saves Klarna order ID to WooCommerce session.
*
* @param string $order_id Klarna order ID.
*/
public function save_order_id_to_session( $order_id ) {
WC()->session->set( 'kco_wc_order_id', $order_id );
}
/**
* Saves Klarna order API to WooCommerce session.
*
* 'na' or 'eu', used for order updates, to avoid trying to switch API when updating KCO Global orders.
*
* @param string $klarna_order Klarna order.
*/
public function save_order_api_to_session() {
if ( 'US' === $this->get_purchase_country() ) {
$api = 'US';
} else {
$api = 'EU';
}
WC()->session->set( 'kco_wc_order_api', $api );
}
/**
* Gets Klarna Checkout order.
*
* If WC_Session value for Klarna order ID exists, attempt to retrieve that order.
* If this fails, create a new one and retrieve it.
* If WC_Session value for Klarna order ID does not exist, create a new order and retrieve it.
*
* @return Klarna_Checkout_Order $order Klarna order.
*/
public function get_order() {
$order_id = $this->get_order_id_from_session();
if ( $order_id ) {
$order = $this->request_pre_retrieve_order( $order_id );
if ( ! $order || is_wp_error( $order ) ) {
$order = $this->request_pre_create_order();
} elseif ( 'checkout_incomplete' === $order->status ) {
// Only update order if its status is incomplete.
$this->request_pre_update_order();
}
} else {
if ( is_order_received_page() ) {
return;
}
$order = $this->request_pre_create_order();
}
return $order;
}
/**
* Gets KCO iframe snippet from KCO order.
*
* @param Klarna_Order $order Klarna Checkout order.
*
* @return mixed
*/
public function get_snippet( $order ) {
if ( ! is_wp_error( $order ) ) {
$this->maybe_clear_session_values( $order );
return $order->html_snippet;
}
return $order->get_error_message();
}
/**
* Clear WooCommerce session values if Klarna Checkout order is completed.
*
* @param Klarna_Order $order Klarna Checkout order.
*/
public function maybe_clear_session_values( $order ) {
if ( 'checkout_complete' === $order->status ) {
WC()->session->__unset( 'kco_wc_update_md5' );
WC()->session->__unset( 'kco_wc_order_id' );
WC()->session->__unset( 'kco_wc_order_notes' );
WC()->session->__unset( 'kco_wc_order_api' );
WC()->session->__unset( 'kco_wc_extra_fields_values' );
WC()->session->__unset( 'kco_wc_prefill_consent' );
}
}
/**
* Gets Klarna merchant ID.
*
* @return string
*/
public function get_merchant_id() {
$credentials = KCO_WC()->credentials->get_credentials_from_session();
return $credentials['merchant_id'];
}
/**
* Gets Klarna shared secret.
*
* @return string
*/
public function get_shared_secret() {
$credentials = KCO_WC()->credentials->get_credentials_from_session();
return $credentials['shared_secret'];
}
/**
* Gets country for Klarna purchase.
*
* @return string
*/
public function get_purchase_country() {
$base_location = wc_get_base_location();
$country = $base_location['country'];
return $country;
}
/**
* Gets currency for Klarna purchase.
*
* @return string
*/
public function get_purchase_currency() {
return get_woocommerce_currency();
}
/**
* Gets locale for Klarna purchase.
*
* @return string
*/
public function get_purchase_locale() {
if ( defined( 'ICL_LANGUAGE_CODE' ) ) {
$locale = ICL_LANGUAGE_CODE;
} else {
$locale = explode('_', get_locale());
$locale = $locale[0];
}
switch ( WC()->checkout()->get_value( 'billing_country' ) ) {
case 'AT':
if ( 'de' == $locale ) {
$klarna_locale = 'de-at';
} else {
$klarna_locale = 'en-at';
}
break;
case 'DE':
if ( 'de' == $locale ) {
$klarna_locale = 'de-de';
} else {
$klarna_locale = 'en-de';
}
break;
case 'DK':
if ( 'da' == $locale ) {
$klarna_locale = 'da-dk';
} else {
$klarna_locale = 'en-dk';
}
break;
case 'FI':
if ( 'fi' == $locale ) {
$klarna_locale = 'fi-fi';
} elseif( 'sv' == $locale ) {
$klarna_locale = 'sv-fi';
} else {
$klarna_locale = 'en-fi';
}
break;
case 'NL':
if ( 'nl' == $locale ) {
$klarna_locale = 'nl-nl';
} else {
$klarna_locale = 'en-nl';
}
break;
case 'NO':
if ( 'nb' == $locale || 'nn' == $locale ) {
$klarna_locale = 'nb-no';
} else {
$klarna_locale = 'en-no';
}
break;
case 'SE':
if ( 'sv' == $locale ) {
$klarna_locale = 'sv-se';
} else {
$klarna_locale = 'en-se';
}
break;
case 'GB':
$klarna_locale = 'en-gb';
break;
case 'US':
$klarna_locale = 'en-us';
break;
default:
$klarna_locale = 'en-us';
} // End switch().
return $klarna_locale;
}
/**
* Gets merchant URLs for Klarna purchase.
*
* @return array
*/
public function get_merchant_urls() {
return KCO_WC()->merchant_urls->get_urls();
}
/**
* Gets Klarna API request headers.
*
* @return array
*/
public function get_request_headers() {
$request_headers = array(
'Authorization' => 'Basic ' . base64_encode( $this->get_merchant_id() . ':' . $this->get_shared_secret() ),
'Content-Type' => 'application/json',
);
return $request_headers;
}
/**
* Gets Klarna API request headers.
*
* @return string
*/
public function get_user_agent() {
$user_agent = apply_filters( 'http_headers_useragent', 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ) ) . ' - KCO:' . KCO_WC_VERSION . ' - PHP Version: ' . phpversion() . ' - Krokedil';
return $user_agent;
}
/**
* Gets Klarna API request body.
*
* @TODO: Clean this up by using maybe_add functions.
*
* @param string $request_type Type of request.
*
* @return false|string
*/
public function get_request_body( $request_type = null ) {
KCO_WC()->order_lines->process_data();
$request_args = array(
'purchase_country' => $this->get_purchase_country(),
'purchase_currency' => $this->get_purchase_currency(),
'locale' => $this->get_purchase_locale(),
'merchant_urls' => $this->get_merchant_urls(),
'order_amount' => KCO_WC()->order_lines->get_order_amount(),
'order_tax_amount' => KCO_WC()->order_lines->get_order_tax_amount(),
'order_lines' => KCO_WC()->order_lines->get_order_lines(),
'shipping_countries' => $this->get_shipping_countries(),
);
if ( kco_wc_prefill_allowed() ) {
$request_args['billing_address'] = array(
'email' => WC()->checkout()->get_value( 'billing_email' ),
'postal_code' => WC()->checkout()->get_value( 'billing_postcode' ),
'country' => WC()->checkout()->get_value( 'billing_country' ),
'phone' => WC()->checkout()->get_value( 'billing_phone' ),
'given_name' => WC()->checkout()->get_value( 'billing_first_name' ),
'family_name' => WC()->checkout()->get_value( 'billing_last_name' ),
'street_address' => WC()->checkout()->get_value( 'billing_address_1' ),
'street_address2' => WC()->checkout()->get_value( 'billing_address_2' ),
'city' => WC()->checkout()->get_value( 'billing_city' ),
'region' => WC()->checkout()->get_value( 'billing_state' ),
);
}
$request_args['options']['title_mandatory'] = $this->get_title_mandatory();
$request_args['options'] = array();
$request_args['options']['allow_separate_shipping_address'] = $this->get_allow_separate_shipping_address();
$request_args['options']['date_of_birth_mandatory'] = $this->get_dob_mandatory();
$request_args['options']['national_identification_number_mandatory'] = $this->get_dob_mandatory();
$request_args['options']['title_mandatory'] = $this->get_title_mandatory();
if ( $this->get_iframe_colors() ) {
$request_args['options'] = array_merge( $request_args['options'], $this->get_iframe_colors() );
}
if ( $this->get_shipping_details() ) {
$request_args['options']['shipping_details'] = $this->get_shipping_details();
}
// Allow external payment method plugin to do its thing.
// @TODO: Extract this into a hooked function.
if ( 'create' === $request_type ) {
if ( in_array( $this->get_purchase_country(), array( 'SE', 'NO', 'FI' ), true ) ) {
if ( isset( $this->settings['allowed_customer_types'] ) ) {
$customer_types_setting = $this->settings['allowed_customer_types'];
switch ( $customer_types_setting ) {
case 'B2B':
$allowed_customer_types = array( 'organization' );
$customer_type = 'organization';
break;
case 'B2BC':
$allowed_customer_types = array( 'person', 'organization' );
$customer_type = 'organization';
break;
case 'B2CB':
$allowed_customer_types = array( 'person', 'organization' );
$customer_type = 'person';
break;
default:
$allowed_customer_types = array( 'person' );
$customer_type = 'person';
}
$request_args['options']['allowed_customer_types'] = $allowed_customer_types;
$request_args['customer']['type'] = $customer_type;
}
}
$request_args = apply_filters( 'kco_wc_create_order', $request_args );
}
$request_body = wp_json_encode( apply_filters( 'kco_wc_api_request_args', $request_args ) );
return $request_body;
}
/**
* Gets shipping countries formatted for Klarna.
*
* @return array
*/
public function get_shipping_countries() {
$wc_countries = new WC_Countries();
return array_keys( $wc_countries->get_shipping_countries() );
}
/**
* Gets allowed separate shipping details option.
*
* @return bool
*/
public function get_allow_separate_shipping_address() {
$allow_separate_shipping = array_key_exists( 'allow_separate_shipping', $this->settings ) && 'yes' === $this->settings['allow_separate_shipping'];
return $allow_separate_shipping;
}
/**
* Gets date of birth mandatory option.
*
* @return bool
*/
public function get_dob_mandatory() {
$dob_mandatory = array_key_exists( 'dob_mandatory', $this->settings ) && 'yes' === $this->settings['dob_mandatory'];
return $dob_mandatory;
}
/**
* Gets date of birth mandatory option.
*
* @return bool
*/
public function get_title_mandatory() {
$title_mandatory = array_key_exists( 'title_mandatory', $this->settings ) && 'yes' === $this->settings['title_mandatory'];
return $title_mandatory;
}
/**
* Gets shipping details note.
*
* @return bool
*/
public function get_shipping_details() {
if ( array_key_exists( 'shipping_details', $this->settings ) ) {
return $this->settings['shipping_details'];
}
return false;
}
/**
* Gets iframe color settings.
*
* @return array|bool
*/
private function get_iframe_colors() {
$color_settings = array();
if ( $this->check_option_field( 'color_button' ) ) {
$color_settings['color_button'] = $this->check_option_field( 'color_button' );
}
if ( $this->check_option_field( 'color_button_text' ) ) {
$color_settings['color_button_text'] = $this->check_option_field( 'color_button_text' );
}
if ( $this->check_option_field( 'color_checkbox' ) ) {
$color_settings['color_checkbox'] = $this->check_option_field( 'color_checkbox' );
}
if ( $this->check_option_field( 'color_checkbox_checkmark' ) ) {
$color_settings['color_checkbox_checkmark'] = $this->check_option_field( 'color_checkbox_checkmark' );
}
if ( $this->check_option_field( 'color_header' ) ) {
$color_settings['color_header'] = $this->check_option_field( 'color_header' );
}
if ( $this->check_option_field( 'color_link' ) ) {
$color_settings['color_link'] = $this->check_option_field( 'color_link' );
}
if ( $this->check_option_field( 'radius_border' ) ) {
$color_settings['radius_border'] = $this->check_option_field( 'radius_border' );
}
if ( count( $color_settings ) > 0 ) {
return $color_settings;
}
return false;
}
private function check_option_field( $field ) {
if ( array_key_exists( $field, $this->settings ) && '' !== $this->settings[ $field ] ) {
return $this->settings[ $field ];
}
return false;
}
/**
* Extracts error messages from Klarna's response.
*
* @param mixed $response Klarna API response.
*
* @return mixed
*/
private function extract_error_messages( $response ) {
if ( is_wp_error( $response ) ) {
return $response;
}
$response_body = json_decode( $response['body'] );
$error = new WP_Error();
if ( ! empty( $response_body->error_messages ) && is_array( $response_body->error_messages ) ) {
KCO_WC()->logger->log( var_export( $response_body, true ) );
foreach ( $response_body->error_messages as $error_message ) {
$error->add( 'kco', $error_message );
}
}
return $error;
}
}

View File

@@ -0,0 +1,285 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_Confirmation class.
*
* Handles Klarna Checkout confirmation page.
*/
class Klarna_Checkout_For_WooCommerce_Confirmation {
/**
* The reference the *Singleton* instance of this class.
*
* @var $instance
*/
protected static $instance;
/**
* Returns the *Singleton* instance of this class.
*
* @return self::$instance The *Singleton* instance.
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Klarna_Checkout_For_WooCommerce_Confirmation constructor.
*/
public function __construct() {
add_action( 'wp_head', array( $this, 'maybe_hide_checkout_form' ) );
add_action( 'woocommerce_before_checkout_form', array( $this, 'maybe_populate_wc_checkout' ) );
add_action( 'wp_footer', array( $this, 'maybe_submit_wc_checkout' ), 999 );
add_filter( 'the_title', array( $this, 'confirm_page_title' ) );
add_filter( 'woocommerce_checkout_fields', array( $this, 'unrequire_fields' ), 99 );
add_filter( 'woocommerce_checkout_posted_data', array( $this, 'unrequire_posted_data' ), 99 );
add_action( 'woocommerce_checkout_after_order_review', array( $this, 'add_kco_order_id_field' ) );
add_action( 'woocommerce_checkout_create_order', array( $this, 'save_kco_order_id_field' ), 10, 2 );
}
/**
* Filter Checkout page title in confirmation page.
*
* @param $title
*
* @return string
*/
public function confirm_page_title( $title ) {
if ( ! is_admin() && is_main_query() && in_the_loop() && is_page() && is_checkout() && isset( $_GET['confirm'] ) && 'yes' === $_GET['confirm'] ) {
$title = __( 'Please wait while we process your order.', 'klarna-checkout-for-woocommerce' );
remove_filter( 'the_title', array( $this, 'confirm_page_title' ) );
}
return $title;
}
/**
* Hides WooCommerce checkout form in KCO confirmation page.
*/
public function maybe_hide_checkout_form() {
if ( ! $this->is_kco_confirmation() ) {
return;
}
echo '<style>form.woocommerce-checkout,div.woocommerce-info{display:none!important}</style>';
}
/**
* Populates WooCommerce checkout form in KCO confirmation page.
*/
public function maybe_populate_wc_checkout( $checkout ) {
if ( ! $this->is_kco_confirmation() ) {
return;
}
echo '<div id="kco-confirm-loading"></div>';
$klarna_order_id = WC()->session->get( 'kco_wc_order_id' );
$response = KCO_WC()->api->request_post_get_order( $klarna_order_id );
$klarna_order = apply_filters( 'kco_wc_klarna_order_pre_submit', json_decode( $response['body'] ) );
$this->save_customer_data( $klarna_order );
}
/**
* Submits WooCommerce checkout form in KCO confirmation page.
*/
public function maybe_submit_wc_checkout() {
if ( ! $this->is_kco_confirmation() ) {
return;
}
?>
<script>
jQuery(function ($) {
$('input#terms').prop('checked', true);
// If order value = 0, payment method fields will not be in the page, so we need to
if (!$('input#payment_method_kco').length) {
$('#order_review').append('<input id="payment_method_kco" type="radio" class="input-radio" name="payment_method" value="kco" checked="checked" />');
}
$('input#payment_method_kco').prop('checked', true);
<?php
$extra_field_values = WC()->session->get( 'kco_wc_extra_fields_values', array() );
foreach ( $extra_field_values as $field_name => $field_value ) { ?>
var elementName = "<?php echo $field_name; ?>";
var elementValue = <?php echo wp_json_encode( $field_value ); ?>;
var element = $('*[name="' + elementName + '"]');
console.log(elementName);
console.log(elementValue);
console.log(element);
console.log(element.type);
if (element.length) {
if (element.is('select')) { // Select.
var selectedOption = element.find('option[value="' + elementValue + '"]');
selectedOption.prop('selected', true);
} else if ('radio' === element.get(0).type) { // Radio.
var checkedRadio = $('*[name="' + elementName + '"][value="' + elementValue + '"]');
checkedRadio.prop('checked', true);
} else if ('checkbox' === element.get(0).type) { // Checkbox.
if (elementValue) {
element.prop('checked', true);
}
} else { // Text and textarea.
element.val(elementValue);
}
}
<?php
}
do_action( 'kco_wc_before_submit' );
?>
$('.validate-required').removeClass('validate-required');
$('form.woocommerce-checkout').submit();
});
</script>
<?php
}
/**
* Checks if in KCO confirmation page.
*
* @return bool
*/
private function is_kco_confirmation() {
if ( isset( $_GET['confirm'] ) && 'yes' === $_GET['confirm'] && isset( $_GET['kco_wc_order_id'] ) ) {
return true;
}
return false;
}
/**
* Saves customer data from Klarna order into WC()->customer.
*
* @param $klarna_order
*/
private function save_customer_data( $klarna_order ) {
// First name.
WC()->customer->set_billing_first_name( sanitize_text_field( $klarna_order->billing_address->given_name ) );
WC()->customer->set_shipping_first_name( sanitize_text_field( $klarna_order->shipping_address->given_name ) );
// Last name.
WC()->customer->set_billing_last_name( sanitize_text_field( $klarna_order->billing_address->family_name ) );
WC()->customer->set_shipping_last_name( sanitize_text_field( $klarna_order->shipping_address->family_name ) );
// Country.
WC()->customer->set_billing_country( strtoupper( sanitize_text_field( $klarna_order->billing_address->country ) ) );
WC()->customer->set_shipping_country( strtoupper( sanitize_text_field( $klarna_order->shipping_address->country ) ) );
// Street address 1.
WC()->customer->set_billing_address_1( sanitize_text_field( $klarna_order->billing_address->street_address ) );
WC()->customer->set_shipping_address_1( sanitize_text_field( $klarna_order->shipping_address->street_address ) );
// Street address 2.
if ( isset( $klarna_order->billing_address->street_address2 ) ) {
WC()->customer->set_billing_address_2( sanitize_text_field( $klarna_order->billing_address->street_address2 ) );
WC()->customer->set_shipping_address_2( sanitize_text_field( $klarna_order->shipping_address->street_address2 ) );
}
// City.
WC()->customer->set_billing_city( sanitize_text_field( $klarna_order->billing_address->city ) );
WC()->customer->set_shipping_city( sanitize_text_field( $klarna_order->shipping_address->city ) );
// County/State.
WC()->customer->set_billing_state( sanitize_text_field( $klarna_order->billing_address->region ) );
WC()->customer->set_shipping_state( sanitize_text_field( $klarna_order->shipping_address->region ) );
// Postcode.
WC()->customer->set_billing_postcode( sanitize_text_field( $klarna_order->billing_address->postal_code ) );
WC()->customer->set_shipping_postcode( sanitize_text_field( $klarna_order->shipping_address->postal_code ) );
// Phone.
WC()->customer->set_billing_phone( sanitize_text_field( $klarna_order->billing_address->phone ) );
// Email.
WC()->customer->set_billing_email( sanitize_text_field( $klarna_order->billing_address->email ) );
WC()->customer->save();
}
/**
* When checking out using KCO, we need to make sure none of the WooCommerce are required, in case Klarna
* does not return info for some of them.
*
* @param array $fields WooCommerce checkout fields.
*
* @return mixed
*/
public function unrequire_fields( $fields ) {
if ( 'kco' === WC()->session->get( 'chosen_payment_method' ) ) {
foreach ( $fields as $fieldset_key => $fieldset ) {
foreach ( $fieldset as $key => $field ) {
$fields[ $fieldset_key ][ $key ]['required'] = '';
$fields[ $fieldset_key ][ $key ]['wooccm_required'] = '';
}
}
}
return $fields;
}
/**
* Makes sure there's no empty data sent for validation.
*
* @param array $data Posted data.
*
* @return mixed
*/
public function unrequire_posted_data( $data ) {
if ( 'kco' === WC()->session->get( 'chosen_payment_method' ) ) {
foreach ( $data as $key => $value ) {
if ( '' === $value ) {
unset( $data[ $key ] );
}
}
}
return $data;
}
/**
* Adds hidden field to WooCommerce checkout form, holding Klarna Checkout order ID.
*/
public function add_kco_order_id_field() {
if ( 'kco' === WC()->session->get( 'chosen_payment_method' ) && isset( $_GET['confirm'] ) && 'yes' === $_GET['confirm'] ) {
if ( isset( $_GET['kco_wc_order_id'] ) ) { // Input var okay.
$klarna_order_id = esc_attr( sanitize_text_field( $_GET['kco_wc_order_id'] ) );
echo '<input type="hidden" id="kco_order_id" name="kco_order_id" value="' . $klarna_order_id . '" />';
}
}
}
/**
* Saves KCO order ID to WooCommerce order as meta field.
*
* @param WC_Order $order WooCommerce order.
* @param array $data Posted data.
*/
public function save_kco_order_id_field( $order, $data ) {
if ( isset( $_POST['kco_order_id'] ) ) {
$kco_order_id = sanitize_text_field( $_POST['kco_order_id'] );
update_post_meta( $order->get_id(), '_wc_klarna_order_id', sanitize_key( $kco_order_id ) );
update_post_meta( $order->get_id(), '_transaction_id', sanitize_key( $kco_order_id ) );
}
}
}
Klarna_Checkout_For_WooCommerce_Confirmation::get_instance();

View File

@@ -0,0 +1,327 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_Create_Local_Order_Fallback class.
*
* Class that handles fallback order creation in Woocommerce if checkout form submission fails.
*/
class Klarna_Checkout_For_WooCommerce_Create_Local_Order_Fallback {
/**
* The reference the *Singleton* instance of this class.
*
* @var $instance
*/
protected static $instance;
/**
* Returns the *Singleton* instance of this class.
*
* @return self::$instance The *Singleton* instance.
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Klarna_Checkout_For_WooCommerce_Create_Local_Order_Fallback constructor.
*/
public function __construct() {
}
/**
* Fallbck order creation, in case checkout process failed.
*
* @param string $klarna_order_id Klarna order ID.
*
* @throws Exception WC_Data_Exception.
*/
public static function create( $klarna_order_id ) {
$response = KCO_WC()->api->request_post_get_order( $klarna_order_id );
$klarna_order = json_decode( $response['body'] );
// Create order
$order = wc_create_order();
try {
$order_id = $order->get_id();
$cart_hash = md5( json_encode( wc_clean( WC()->cart->get_cart_for_session() ) ) . WC()->cart->total );
$order->set_created_via( 'checkout' );
$order->set_cart_hash( $cart_hash );
$order->set_customer_id( apply_filters( 'woocommerce_checkout_customer_id', get_current_user_id() ) );
$order->set_currency( get_woocommerce_currency() );
$order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
$order->set_customer_ip_address( WC_Geolocation::get_ip_address() );
$order->set_customer_user_agent( wc_get_user_agent() );
$order->set_shipping_total( WC()->cart->shipping_total );
$order->set_discount_total( WC()->cart->get_cart_discount_total() );
$order->set_discount_tax( WC()->cart->get_cart_discount_tax_total() );
$order->set_cart_tax( WC()->cart->tax_total );
$order->set_shipping_tax( WC()->cart->shipping_tax_total );
$order->set_total( WC()->cart->total );
update_post_meta( $order_id, '_created_via_klarna_fallback', 'yes' );
//Add payment method
self::add_order_payment_method( $order );
// Process customer data.
self::process_customer_data( $order, $klarna_order );
// Process customer data.
self::create_order_line_items( $order, WC()->cart );
//Add fees to order.
self::create_order_fee_lines( $order, WC()->cart );
// Add shipping
self::create_order_shipping_lines( $order );
// Tax
self::create_order_tax_lines( $order, WC()->cart );
// Coupons
self::create_order_coupon_lines( $order, WC()->cart );
// Save the order.
$order->save();
return $order;
} catch ( Exception $e ) {
return new WP_Error( 'checkout-error', $e->getMessage() );
}
}
/**
* Set payment method.
*
* @param WC_Order $order WooCommerce order.
*
*/
public static function add_order_payment_method( $order ) {
$available_gateways = WC()->payment_gateways->payment_gateways();
$payment_method = $available_gateways['kco'];
$order->set_payment_method( $payment_method );
}
/**
* Processes customer data on fallback order creation.
*
* @param WC_Order $order WooCommerce order.
* @param Klarna_Checkout_Order $klarna_order Klarna order.
*
* @throws Exception WC_Data_Exception.
*/
private static function process_customer_data( $order, $klarna_order ) {
// First name.
$order->set_billing_first_name( sanitize_text_field( $klarna_order->billing_address->given_name ) );
$order->set_shipping_first_name( sanitize_text_field( $klarna_order->shipping_address->given_name ) );
// Last name.
$order->set_billing_last_name( sanitize_text_field( $klarna_order->billing_address->family_name ) );
$order->set_shipping_last_name( sanitize_text_field( $klarna_order->shipping_address->family_name ) );
// Country.
$order->set_billing_country( sanitize_text_field( $klarna_order->billing_address->country ) );
$order->set_shipping_country( sanitize_text_field( $klarna_order->shipping_address->country ) );
// Street address 1.
$order->set_billing_address_1( sanitize_text_field( $klarna_order->billing_address->street_address ) );
$order->set_shipping_address_1( sanitize_text_field( $klarna_order->shipping_address->street_address ) );
// Street address 2.
$order->set_billing_address_2( sanitize_text_field( $klarna_order->billing_address->street_address2 ) );
$order->set_shipping_address_2( sanitize_text_field( $klarna_order->shipping_address->street_address2 ) );
// City.
$order->set_billing_city( sanitize_text_field( $klarna_order->billing_address->city ) );
$order->set_shipping_city( sanitize_text_field( $klarna_order->shipping_address->city ) );
// County/State.
$order->set_billing_state( sanitize_text_field( $klarna_order->billing_address->region ) );
$order->set_shipping_state( sanitize_text_field( $klarna_order->shipping_address->region ) );
// Postcode.
$order->set_billing_postcode( sanitize_text_field( $klarna_order->billing_address->postal_code ) );
$order->set_shipping_postcode( sanitize_text_field( $klarna_order->shipping_address->postal_code ) );
// Phone.
$order->set_billing_phone( sanitize_text_field( $klarna_order->billing_address->phone ) );
// Email.
$order->set_billing_email( sanitize_text_field( $klarna_order->billing_address->email ) );
}
/**
* Processes cart contents on fallback order creation.
*
* @paramWC_Order $order WooCommerce order.
*
* @throws Exception WC_Data_Exception.
*/
private static function create_order_line_items( &$order, $cart ) {
// Remove items as to stop the item lines from being duplicated.
$order->remove_order_items();
foreach ( $cart->get_cart() as $cart_item_key => $values ) {
$product = $values['data'];
$item = new WC_Order_Item_Product();
$item->legacy_values = $values; // @deprecated For legacy actions.
$item->legacy_cart_item_key = $cart_item_key; // @deprecated For legacy actions.
$item->set_props( array(
'quantity' => $values['quantity'],
'variation' => $values['variation'],
'subtotal' => $values['line_subtotal'],
'total' => $values['line_total'],
'subtotal_tax' => $values['line_subtotal_tax'],
'total_tax' => $values['line_tax'],
'taxes' => $values['line_tax_data'],
) );
if ( $product ) {
$item->set_props( array(
'name' => $product->get_name(),
'tax_class' => $product->get_tax_class(),
'product_id' => $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id(),
'variation_id' => $product->is_type( 'variation' ) ? $product->get_id() : 0,
) );
}
$item->set_backorder_meta();
/**
* Action hook to adjust item before save.
* @since 3.0.0
*/
do_action( 'woocommerce_checkout_create_order_line_item', $item, $cart_item_key, $values, $order );
// Add item to order and save.
$order->add_item( $item );
}
}
/**
* Add fees to the order.
*
* @param WC_Order $order
*/
public static function create_order_fee_lines( &$order, $cart ) {
foreach ( $cart->get_fees() as $fee_key => $fee ) {
$item = new WC_Order_Item_Fee();
$item->legacy_fee = $fee; // @deprecated For legacy actions.
$item->legacy_fee_key = $fee_key; // @deprecated For legacy actions.
$item->set_props( array(
'name' => $fee->name,
'tax_class' => $fee->taxable ? $fee->tax_class : 0,
'total' => $fee->amount,
'total_tax' => $fee->tax,
'taxes' => array(
'total' => $fee->tax_data,
),
) );
/**
* Action hook to adjust item before save.
* @since 3.0.0
*/
do_action( 'woocommerce_checkout_create_order_fee_item', $item, $fee_key, $fee, $order );
// Add item to order and save.
$order->add_item( $item );
}
}
/**
* Add shipping lines to the order.
*
* @param WC_Order $order
*/
public static function create_order_shipping_lines( &$order ) {
if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
define( 'WOOCOMMERCE_CART', true );
}
$order_id = $order->get_id() ;
$this_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
WC()->cart->calculate_shipping();
// Store shipping for all packages.
foreach ( WC()->shipping->get_packages() as $package_key => $package ) {
if ( isset( $package['rates'][ $this_shipping_methods[ $package_key ] ] ) ) {
$item_id = $order->add_shipping( $package['rates'][ $this_shipping_methods[ $package_key ] ] );
if ( ! $item_id ) {
KCO_WC()->logger->log( 'Error: Unable to add shipping item in Create Local Order Fallback.' );
krokedil_log_events( null, 'Error: Unable to add shipping item in Create Local Order Fallback.', '' );
}
// Allows plugins to add order item meta to shipping.
do_action( 'woocommerce_add_shipping_order_item', $order_id, $item_id, $package_key );
}
}
}
/**
* Add tax lines to the order.
*
* @param WC_Order $order
*/
public static function create_order_tax_lines( &$order, $cart ) {
foreach ( array_keys( $cart->taxes + $cart->shipping_taxes ) as $tax_rate_id ) {
if ( $tax_rate_id && apply_filters( 'woocommerce_cart_remove_taxes_zero_rate_id', 'zero-rated' ) !== $tax_rate_id ) {
$item = new WC_Order_Item_Tax();
$item->set_props( array(
'rate_id' => $tax_rate_id,
'tax_total' => $cart->get_tax_amount( $tax_rate_id ),
'shipping_tax_total' => $cart->get_shipping_tax_amount( $tax_rate_id ),
'rate_code' => WC_Tax::get_rate_code( $tax_rate_id ),
'label' => WC_Tax::get_rate_label( $tax_rate_id ),
'compound' => WC_Tax::is_compound( $tax_rate_id ),
) );
/**
* Action hook to adjust item before save.
* @since 3.0.0
*/
do_action( 'woocommerce_checkout_create_order_tax_item', $item, $tax_rate_id, $order );
// Add item to order and save.
$order->add_item( $item );
}
}
}
/**
* Add coupon lines to the order.
*
* @param WC_Order $order
*/
public static function create_order_coupon_lines( &$order, $cart ) {
foreach ( $cart->get_coupons() as $code => $coupon ) {
$item = new WC_Order_Item_Coupon();
$item->set_props( array(
'code' => $code,
'discount' => $cart->get_coupon_discount_amount( $code ),
'discount_tax' => $cart->get_coupon_discount_tax_amount( $code ),
) );
/**
* Action hook to adjust item before save.
* @since 3.0.0
*/
do_action( 'woocommerce_checkout_create_order_coupon_item', $item, $code, $coupon, $order );
// Add item to order and save.
$order->add_item( $item );
}
}
}
Klarna_Checkout_For_WooCommerce_Create_Local_Order_Fallback::get_instance();

View File

@@ -0,0 +1,65 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_Credentials class.
*
* Gets correct credentials based on customer country, store country and test/live mode.
*/
class Klarna_Checkout_For_WooCommerce_Credentials {
/**
* Klarna Checkout for WooCommerce settings.
*
* @var $settings
*/
public $settings = array();
/**
* Klarna_Checkout_For_WooCommerce_Credentials constructor.
*/
public function __construct() {
$this->settings = get_option( 'woocommerce_kco_settings' );
}
/**
* Gets Klarna API credentials (merchant ID and shared secret) from user session.
*
* @return bool|array $credentials
*/
public function get_credentials_from_session() {
$base_location = wc_get_base_location();
if ( 'US' === $base_location['country'] ) {
$country_string = 'us';
} else {
$country_string = 'eu';
}
$test_string = 'yes' === $this->settings['testmode'] ? 'test_' : '';
$merchant_id = $this->settings[ $test_string . 'merchant_id_' . $country_string ];
$shared_secret = $this->settings[ $test_string . 'shared_secret_' . $country_string ];
// Merchant id and/or shared secret not found for matching country.
if ( '' === $merchant_id || '' === $shared_secret ) {
return false;
}
$credentials = array(
'merchant_id' => $this->settings[ $test_string . 'merchant_id_' . $country_string ],
'shared_secret' => htmlspecialchars_decode( $this->settings[ $test_string . 'shared_secret_' . $country_string ] ),
);
return $credentials;
}
/**
* Gets Klarna API credentials (merchant ID and shared secret) from a completed WC order.
*/
public function get_credentials_from_order() {
}
}

View File

@@ -0,0 +1,88 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_Extra_Checkout_Fields class.
*
* Class that handles extra checkout fields displayed when Klarna Checkout is the selected payment option.
*/
class Klarna_Checkout_For_WooCommerce_Extra_Checkout_Fields {
/**
* Returns array of all WooCommerce Checkout fields.
*/
public function get_all_checkout_fields() {
$default_billing_fields = WC()->checkout()->get_checkout_fields( 'billing' );
$default_shipping_fields = WC()->checkout()->get_checkout_fields( 'shipping' );
$default_account_fields = WC()->checkout()->get_checkout_fields( 'account' );
$default_order_fields = WC()->checkout()->get_checkout_fields( 'order' );
return array(
'billing' => $default_billing_fields,
'shipping' => $default_shipping_fields,
'account' => $default_account_fields,
'order' => $default_order_fields,
);
}
/**
* Returns list of WooCommerce checkout fields that can be populated using information from Klarna.
*/
public function get_klarna_checkout_fields() {
$klarna_fields = array(
'billing' => array(
'billing_first_name',
'billing_last_name',
'billing_country',
'billing_address_1',
'billing_address_2',
'billing_city',
'billing_state',
'billing_postcode',
'billing_phone',
'billing_email',
),
'shipping' => array(
'shipping_first_name',
'shipping_last_name',
'shipping_country',
'shipping_address_1',
'shipping_address_2',
'shipping_city',
'shipping_state',
'shipping_postcode',
),
);
return apply_filters( 'kco_wc_klarna_checkout_fields', $klarna_fields );
}
/**
* Returns list of WooCommerce checkout fields that can NOT be populated using information from Klarna.
*/
public function get_remaining_checkout_fields() {
$all_fields = $this->get_all_checkout_fields();
$klarna_fields = $this->get_klarna_checkout_fields();
foreach ( $klarna_fields['billing'] as $field ) {
if ( array_key_exists( $field, $all_fields['billing'] ) ) {
unset( $all_fields['billing'][ $field ] );
}
unset( $all_fields['billing']['billing_company'] ); // B2C only for now.
}
foreach ( $klarna_fields['shipping'] as $field ) {
if ( array_key_exists( $field, $all_fields['shipping'] ) ) {
unset( $all_fields['shipping'][ $field ] );
}
unset( $all_fields['shipping']['shipping_company'] ); // B2C only for now.
}
return $all_fields;
}
}

View File

@@ -0,0 +1,273 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_Fields class.
*
* Klarna Checkout for WooCommerce settings fields.
*/
class Klarna_Checkout_For_WooCommerce_Fields {
/**
* Returns the fields.
*/
public static function fields() {
$settings = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Enable Klarna Checkout', 'klarna-checkout-for-woocommerce' ),
'type' => 'checkbox',
'description' => '',
'default' => 'no',
),
'title' => array(
'title' => __( 'Title', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Payment method title.', 'klarna-checkout-for-woocommerce' ),
'default' => 'Klarna',
'desc_tip' => true,
),
'description' => array(
'title' => __( 'Description', 'klarna-checkout-for-woocommerce' ),
'type' => 'textarea',
'description' => __( 'Payment method description.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'allow_separate_shipping' => array(
'title' => __( 'Separate shipping address', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Allow separate shipping address', 'klarna-checkout-for-woocommerce' ),
'type' => 'checkbox',
'description' => __( 'If this option is checked, customers will be able to enter shipping address different than their billing address in checkout.', 'klarna-checkout-for-woocommerce' ),
'default' => 'no',
'desc_tip' => true,
),
'select_another_method_text' => array(
'title' => __( 'Other payment method button text', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Customize the <em>Select another payment method</em> button text that is displayed in checkout if using other payment methods than Klarna Checkout. Leave blank to use the default (and translatable) text.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'shipping_details' => array(
'title' => __( 'Shipping details', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Shipping details note shown to customer', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Will be shown to customer in thank you page.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => false,
),
'allowed_customer_types' => array(
'title' => __( 'Allowed Customer Types', 'klarna-checkout-for-woocommerce' ),
'type' => 'select',
'options' => array(
'B2C' => __( 'B2C only', 'klarna-checkout-for-woocommerce' ),
'B2B' => __( 'B2B only', 'klarna-checkout-for-woocommerce' ),
'B2CB' => __( 'B2C & B2B (defaults to B2C)', 'klarna-checkout-for-woocommerce' ),
'B2BC' => __( 'B2B & B2C (defaults to B2B)', 'klarna-checkout-for-woocommerce' ),
),
'description' => sprintf( __( 'Select if you want to sell both to consumers and companies or only to one of them (available for SE, NO and FI). Learn more and <a href="%s" target="_blank">sign up for Klarna Checkout B2B here</a>.', 'klarna-checkout-for-woocommerce' ), 'https://www.klarna.com/se/foretag/klarna-checkout/klarna-checkout-foretag-form' ),
'default' => 'B2C',
'desc_tip' => false
),
'send_product_urls' => array(
'title' => __( 'Product URLs', 'klarna-checkout-for-woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Send product and product image URLs to Klarna', 'klarna-checkout-for-woocommerce' ),
'default' => 'yes',
'desc_tip' => true,
),
'logging' => array(
'title' => __( 'Logging', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Log debug messages', 'klarna-checkout-for-woocommerce' ),
'type' => 'checkbox',
'description' => __( 'Save debug messages to the WooCommerce System Status log.', 'klarna-checkout-for-woocommerce' ),
'default' => 'no',
'desc_tip' => true,
),
'testmode' => array(
'title' => __( 'Test mode', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Enable Test Mode', 'klarna-checkout-for-woocommerce' ),
'type' => 'checkbox',
'description' => __( 'Place the payment gateway in test mode using test API keys.', 'klarna-checkout-for-woocommerce' ),
'default' => 'yes',
'desc_tip' => true,
),
'dob_mandatory' => array(
'title' => __( 'Date of birth mandatory', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Make customer date of birth mandatory', 'klarna-checkout-for-woocommerce' ),
'type' => 'checkbox',
'description' => __( 'If checked, the customer cannot skip date of birth.', 'klarna-checkout-for-woocommerce' ),
'default' => 'no',
'desc_tip' => true,
),
// EU.
'credentials_eu' => array(
'title' => '<img src="' . plugins_url( 'assets/img/flags/eu.svg', KCO_WC_MAIN_FILE ) . '" height="12" /> API Credentials Europe',
'type' => 'title',
),
'merchant_id_eu' => array(
'title' => __( 'Production Username (UID)', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Get your API keys from your Klarna Checkout merchant account for Europe.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'shared_secret_eu' => array(
'title' => __( 'Production Password', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Get your API keys from your Klarna Checkout merchant account for Europe.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'test_merchant_id_eu' => array(
'title' => __( 'Test Username (UID)', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Get your API keys from your Klarna Checkout merchant account for Europe.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'test_shared_secret_eu' => array(
'title' => __( 'Test Password', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Get your API keys from your Klarna Checkout merchant account for Europe.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'title_mandatory' => array(
'title' => __( 'Title mandatory (GB)', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Make customer title mandatory', 'klarna-checkout-for-woocommerce' ),
'type' => 'checkbox',
'description' => __( 'If unchecked, title becomes optional. Only available for orders for country GB.', 'klarna-checkout-for-woocommerce' ),
'default' => 'yes',
'desc_tip' => true,
),
'prefill_consent' => array(
'title' => __( 'Show prefill consent notice', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Only applicable for stores based in Germany and Austria', 'klarna-checkout-for-woocommerce' ),
'type' => 'checkbox',
'default' => 'yes',
),
// US.
'credentials_us' => array(
'title' => '<img src="' . plugins_url( 'assets/img/flags/us.svg', KCO_WC_MAIN_FILE ) . '" height="12" /> API Credentials United States',
'type' => 'title',
),
'merchant_id_us' => array(
'title' => __( 'Production Username (UID)', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Get your API keys from your Klarna Checkout merchant account for US.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'shared_secret_us' => array(
'title' => __( 'Production Password', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Get your API keys from your Klarna Checkout merchant account for US.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'test_merchant_id_us' => array(
'title' => __( 'Test Username (UID)', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Get your API keys from your Klarna Checkout merchant account for US.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'test_shared_secret_us' => array(
'title' => __( 'Test Password', 'klarna-checkout-for-woocommerce' ),
'type' => 'text',
'description' => __( 'Get your API keys from your Klarna Checkout merchant account for US.', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
// Checkout iframe settings.
'color_settings_title' => array(
'title' => __( 'Color Settings', 'klarna-checkout-for-woocommerce' ),
'type' => 'title',
),
'color_button' => array(
'title' => __( 'Checkout button color', 'klarna-checkout-for-woocommerce' ),
'type' => 'color',
'description' => __( 'Checkout page button color', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'color_button_text' => array(
'title' => __( 'Checkout button text color', 'klarna-checkout-for-woocommerce' ),
'type' => 'color',
'description' => __( 'Checkout page button text color', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'color_checkbox' => array(
'title' => __( 'Checkout checkbox color', 'klarna-checkout-for-woocommerce' ),
'type' => 'color',
'description' => __( 'Checkout page checkbox color', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'color_checkbox_checkmark' => array(
'title' => __( 'Checkout checkbox checkmark color', 'klarna-checkout-for-woocommerce' ),
'type' => 'color',
'description' => __( 'Checkout page checkbox checkmark color', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'color_header' => array(
'title' => __( 'Checkout header color', 'klarna-checkout-for-woocommerce' ),
'type' => 'color',
'description' => __( 'Checkout page header color', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'color_link' => array(
'title' => __( 'Checkout link color', 'klarna-checkout-for-woocommerce' ),
'type' => 'color',
'description' => __( 'Checkout page link color', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
'radius_border' => array(
'title' => __( 'Checkout radius border (px)', 'klarna-checkout-for-woocommerce' ),
'type' => 'number',
'description' => __( 'Checkout page radius border in pixels', 'klarna-checkout-for-woocommerce' ),
'default' => '',
'desc_tip' => true,
),
);
$wc_version = defined( 'WC_VERSION' ) && WC_VERSION ? WC_VERSION : null;
if ( version_compare( $wc_version, '3.4', '>=' ) ) {
$new_settings = array();
foreach ( $settings as $key => $value ) {
$new_settings[ $key ] = $value;
if ( 'dob_mandatory' === $key ) {
$new_settings['display_privacy_policy_text'] = array(
'title' => __( 'Checkout privacy policy text', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Select if you want to show the <em>Checkout privacy policy</em> text on the checkout page, and where you want to display it.', 'klarna-checkout-for-woocommerce' ),
'type' => 'select',
'default' => 'no',
'options' => array(
'no' => __( 'Do not display', 'klarna-checkout-for-woocommerce' ),
'above' => __( 'Display above checkout', 'klarna-checkout-for-woocommerce' ),
'below' => __( 'Display below checkout', 'klarna-checkout-for-woocommerce' ),
),
);
$new_settings['add_terms_and_conditions_checkbox'] = array(
'title' => __( 'Terms and conditions checkbox', 'klarna-checkout-for-woocommerce' ),
'label' => __( 'Add a terms and conditions checkbox inside Klarna checkout iframe', 'klarna-checkout-for-woocommerce' ),
'type' => 'checkbox',
'description' => __( 'To change the text navigate to → Appearance → Customize → WooCommerce → Checkout.', 'klarna-checkout-for-woocommerce' ),
'default' => 'no',
'desc_tip' => false,
);
}
}
$settings = $new_settings;
}
return apply_filters( 'kco_wc_gateway_settings', $settings );
}
}

View File

@@ -0,0 +1,366 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_Gateway class.
*
* @extends WC_Payment_Gateway
*/
class Klarna_Checkout_For_WooCommerce_Gateway extends WC_Payment_Gateway {
/**
* Klarna_Checkout_For_WooCommerce_Gateway constructor.
*/
public function __construct() {
$this->id = 'kco';
$this->method_title = __( 'Klarna Checkout', 'klarna-checkout-for-woocommerce' );
$this->method_description = __( 'Klarna Checkout replaces standard WooCommerce checkout page.', 'klarna-checkout-for-woocommerce' );
$this->has_fields = false;
$this->supports = apply_filters( 'kco_wc_supports', array( 'products' ) );
// Load the form fields.
$this->init_form_fields();
// Load the settings.
$this->init_settings();
$this->title = $this->get_option( 'title' );
$this->description = $this->get_option( 'description' );
$this->enabled = $this->get_option( 'enabled' );
$this->testmode = 'yes' === $this->get_option( 'testmode' );
$this->logging = 'yes' === $this->get_option( 'logging' );
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array(
$this,
'process_admin_options',
) );
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
add_action( 'woocommerce_thankyou_' . $this->id, array( $this, 'show_thank_you_snippet' ) );
add_action( 'woocommerce_admin_order_data_after_billing_address', array( $this, 'address_notice' ) );
add_action( 'woocommerce_checkout_init', array( $this, 'prefill_consent' ) );
add_action( 'woocommerce_checkout_init', array( $this, 'show_log_in_notice' ) );
// Remove WooCommerce footer text from our settings page.
add_filter( 'admin_footer_text', array( $this, 'admin_footer_text' ), 999 );
}
/**
* Get gateway icon.
*
* @return string
*/
public function get_icon() {
$icon_src = 'https://cdn.klarna.com/1.0/shared/image/generic/logo/en_us/basic/logo_black.png?width=100';
$icon_html = '<img src="' . $icon_src . '" alt="Klarna Checkout" style="border-radius:0px"/>';
return apply_filters( 'wc_klarna_checkout_icon_html', $icon_html );
}
/**
* Process the payment and return the result.
*
* @param int $order_id WooCommerce order ID.
*
* @return array
*/
public function process_payment( $order_id ) {
$order = wc_get_order( $order_id );
krokedil_set_order_gateway_version( $order_id, KCO_WC_VERSION );
return array(
'result' => 'success',
'redirect' => $this->get_return_url( $order ),
);
}
/**
* This plugin doesn't handle order management, but it allows Klarna Order Management plugin to process refunds
* and then return true or false.
*
* @param int $order_id WooCommerce order ID.
* @param null|int $amount Refund amount.
* @param string $reason Reason for refund.
*
* @return bool
*/
public function process_refund( $order_id, $amount = null, $reason = '' ) {
return apply_filters( 'wc_klarna_checkout_process_refund', false, $order_id, $amount, $reason );
}
/**
* Initialise settings fields.
*/
public function init_form_fields() {
$this->form_fields = Klarna_Checkout_For_WooCommerce_Fields::fields();
}
/**
* Checks if method should be available.
*
* @return bool
*/
public function is_available() {
if ( 'yes' !== $this->enabled ) {
return false;
}
// If we can't retrieve a set of credentials, disable KCO.
if ( is_checkout() && ! KCO_WC()->credentials->get_credentials_from_session() ) {
return false;
}
return true;
}
/**
* Add sidebar to the settings page.
*/
public function admin_options() {
ob_start();
parent::admin_options();
$parent_options = ob_get_contents();
ob_end_clean();
WC_Klarna_Banners::settings_sidebar( $parent_options );
}
/**
* Enqueue payment scripts.
*
* @hook wp_enqueue_scripts
*/
public function enqueue_scripts() {
if ( ! is_checkout() ) {
return;
}
if ( is_order_received_page() ) {
return;
}
if ( ! kco_wc_prefill_allowed() ) {
add_thickbox();
}
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
wp_register_script(
'kco',
plugins_url( 'assets/js/klarna-checkout-for-woocommerce' . $suffix . '.js', KCO_WC_MAIN_FILE ),
array( 'jquery', 'wc-cart' ),
KCO_WC_VERSION,
true
);
wp_register_style(
'kco',
plugins_url( 'assets/css/klarna-checkout-for-woocommerce' . $suffix . '.css', KCO_WC_MAIN_FILE ),
array(),
KCO_WC_VERSION
);
$form = array();
if ( false !== get_transient( WC()->session->get( 'kco_wc_order_id' ) ) ) {
$form = get_transient( WC()->session->get( 'kco_wc_order_id' ) );
}
$checkout_localize_params = array(
'update_cart_url' => WC_AJAX::get_endpoint( 'kco_wc_update_cart' ),
'update_cart_nonce' => wp_create_nonce( 'kco_wc_update_cart' ),
'update_shipping_url' => WC_AJAX::get_endpoint( 'kco_wc_update_shipping' ),
'update_shipping_nonce' => wp_create_nonce( 'kco_wc_update_shipping' ),
'update_extra_fields_url' => WC_AJAX::get_endpoint( 'kco_wc_update_extra_fields' ),
'update_extra_fields_nonce' => wp_create_nonce( 'kco_wc_update_extra_fields' ),
'change_payment_method_url' => WC_AJAX::get_endpoint( 'kco_wc_change_payment_method' ),
'change_payment_method_nonce' => wp_create_nonce( 'kco_wc_change_payment_method' ),
'update_klarna_order_url' => WC_AJAX::get_endpoint( 'kco_wc_update_klarna_order' ),
'update_klarna_order_nonce' => wp_create_nonce( 'kco_wc_update_klarna_order' ),
'iframe_shipping_address_change_url' => WC_AJAX::get_endpoint( 'kco_wc_iframe_shipping_address_change' ),
'iframe_shipping_address_change_nonce' => wp_create_nonce( 'kco_wc_iframe_shipping_address_change' ),
'checkout_error_url' => WC_AJAX::get_endpoint( 'kco_wc_checkout_error' ),
'checkout_error_nonce' => wp_create_nonce( 'kco_wc_checkout_error' ),
'logging' => $this->logging,
'save_form_data' => WC_AJAX::get_endpoint( 'kco_wc_save_form_data' ),
'save_form_data_nonce' => wp_create_nonce( 'kco_wc_save_form_data' ),
'form' => $form,
);
wp_localize_script( 'kco', 'kco_params', $checkout_localize_params );
wp_enqueue_script( 'kco' );
wp_enqueue_style( 'kco' );
}
/**
* Enqueue admin scripts.
*
* @param string $hook Admin page hook.
*
* @hook admin_enqueue_scripts
*/
public function admin_enqueue_scripts( $hook ) {
if ( 'woocommerce_page_wc-settings' !== $hook ) {
return;
}
if ( ! isset( $_GET['section'] ) || 'kco' !== $_GET['section'] ) {
return;
}
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
$store_base_location = wc_get_base_location();
if ( 'US' === $store_base_location['country'] ) {
$location = 'US';
} else {
$location = $this->check_if_eu( $store_base_location['country'] );
}
wp_register_script(
'kco_admin',
plugins_url( 'assets/js/klarna-checkout-for-woocommerce-admin.js', KCO_WC_MAIN_FILE ),
array(),
KCO_WC_VERSION
);
$admin_localize_params = array(
'location' => $location,
);
wp_localize_script( 'kco_admin', 'kco_admin_params', $admin_localize_params );
wp_enqueue_script( 'kco_admin' );
}
/**
* Detect if EU country.
*
* @param string $store_base_location The WooCommerce stores base country.
*/
private function check_if_eu( $store_base_location ) {
$eu_countries = array(
'AL', 'AD', 'AM', 'AT', 'BY', 'BE', 'BA', 'BG', 'CH', 'CY', 'CZ', 'DE',
'DK', 'EE', 'ES', 'FO', 'FI', 'FR', 'GB', 'GE', 'GI', 'GR', 'HU', 'HR',
'IE', 'IS', 'IT', 'LT', 'LU', 'LV', 'MC', 'MK', 'MT', 'NO', 'NL', 'PL',
'PT', 'RO', 'RU', 'SE', 'SI', 'SK', 'SM', 'TR', 'UA', 'VA',
);
if( in_array( $store_base_location, $eu_countries ) ) {
return 'EU';
} else {
return '';
}
}
/**
* Displays Klarna Checkout thank you iframe and clears Klarna order ID value from WC session.
*
* @param int $order_id WooCommerce order ID.
*/
public function show_thank_you_snippet( $order_id = null ) {
if ( ! WC()->session->get( 'kco_wc_order_id' ) ) {
return;
}
$klarna_order = KCO_WC()->api->get_order();
echo KCO_WC()->api->get_snippet( $klarna_order );
if ( $order_id ) {
// Set WC order transaction ID.
$order = wc_get_order( $order_id );
update_post_meta( $order_id, '_wc_klarna_order_id', sanitize_key( $klarna_order->order_id ) );
update_post_meta( $order_id, '_transaction_id', sanitize_key( $klarna_order->order_id ) );
$environment = $this->testmode ? 'test' : 'live';
update_post_meta( $order_id, '_wc_klarna_environment', $environment );
$klarna_country = WC()->checkout()->get_value( 'billing_country' );
update_post_meta( $order_id, '_wc_klarna_country', $klarna_country );
krokedil_log_events( $order_id, 'Klarna order in show_thank_you_snippet', $klarna_order );
$response = KCO_WC()->api->request_post_get_order( $klarna_order->order_id );
$klarna_post_order = json_decode( $response['body'] );
krokedil_log_events( $order_id, 'Klarna post_order in show_thank_you_snippet', $klarna_post_order );
if ( 'ACCEPTED' === $klarna_post_order->fraud_status ) {
$order->payment_complete();
// translators: Klarna order ID.
$note = sprintf( __( 'Payment via Klarna Checkout, order ID: %s', 'klarna-checkout-for-woocommerce' ), sanitize_key( $klarna_order->order_id ) );
$order->add_order_note( $note );
} elseif ( 'REJECTED' === $klarna_post_order->fraud_status ) {
$order->update_status( 'on-hold', __( 'Klarna Checkout order was rejected.', 'klarna-checkout-for-woocommerce' ) );
} elseif ( 'PENDING' === $klarna_post_order->fraud_status ) {
// translators: Klarna order ID.
$note = sprintf( __( 'Klarna order is under review, order ID: %s.', 'klarna-checkout-for-woocommerce' ), sanitize_key( $klarna_order->order_id ) );
$order->update_status( 'on-hold', $note );
}
KCO_WC()->api->request_post_acknowledge_order( $klarna_order->order_id );
KCO_WC()->api->request_post_set_merchant_reference(
$klarna_order->order_id,
array(
'merchant_reference1' => $order->get_order_number(),
'merchant_reference2' => $order->get_id(),
)
);
}
}
/**
* Changes footer text in KCO settings page.
*
* @TODO: Get a URL to link to.
*
* @param string $text Footer text.
*
* @return string
*/
public function admin_footer_text( $text ) {
if ( isset( $_GET['section'] ) && 'kco' === $_GET['section'] ) {
$text = 'If you like Klarna Checkout for WooCommerce, please consider <strong>assigning Krokedil as your integration partner.</strong>.';
}
return $text;
}
/**
* Adds can't edit address notice to KP EU orders.
*
* @param WC_Order $order WooCommerce order object.
*/
public function address_notice( $order ) {
if ( $this->id === $order->get_payment_method() ) {
echo '<div style="margin: 10px 0; padding: 10px; border: 1px solid #B33A3A; font-size: 12px">';
esc_html_e( 'Order address should not be changed and any changes you make will not be reflected in Klarna system.', 'klarna-checkout-for-woocommerce' );
echo '</div>';
}
}
/**
* Show notice that tells customers they need to log in.
*/
public function show_log_in_notice() {
if ( isset( $_GET['login_required'] ) && 'yes' === $_GET['login_required'] ) {
wc_add_notice(
sanitize_textarea_field( __(
'An account is already registered with your email address. Please log in.',
'klarna-checkout-for-woocommerce'
) ),
'error'
);
}
}
/**
* Adds prefill consent to WC session.
*/
public function prefill_consent() {
if ( isset( $_GET['prefill_consent'] ) ) { // Input var okay.
if ( 'yes' === sanitize_text_field( $_GET['prefill_consent'] ) ) {
WC()->session->set( 'kco_wc_prefill_consent', true );
}
}
}
}

View File

@@ -0,0 +1,91 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Compliance with European Union's General Data Protection Regulation.
*
* @class Klarna_Checkout_For_Woocommerce_GDPR
* @version 1.0.0
* @package Klarna_Checkout/Classes
* @category Class
* @author Krokedil
*/
class Klarna_Checkout_For_Woocommerce_GDPR {
/**
* Class constructor.
*/
public function __construct() {
add_action( 'admin_init', array( $this, 'privacy_declarations' ) );
add_action( 'init', array( $this, 'maybe_add_privacy_policy_text' ) );
add_filter( 'kco_wc_api_request_args', array( $this, 'maybe_add_checkbox' ) );
}
/**
* Privacy declarations.
*
* @return void
*/
public function privacy_declarations() {
if ( function_exists( 'wp_add_privacy_policy_content' ) ) {
$content =
__(
'When you place an order in the webstore with Klarna Checkout as the choosen payment method, ' .
'information about the products in the order (name, price, quantity, SKU) is sent to Klarna. ' .
'When the purchase is finalized Klarna sends your billing and shipping address back to the webstore. ' .
'This data plus an unique identifier for the purchase is then stored as billing and shipping data in the order in WooCommerce.',
'klarna-checkout-for-woocommerce'
);
wp_add_privacy_policy_content(
'Klarna Checkout for WooCommerce',
wp_kses_post( wpautop( $content ) )
);
}
}
/**
* Maybe adds the terms checkbox to the checkout.
*
* @return void
*/
public function maybe_add_privacy_policy_text() {
$settings = get_option( 'woocommerce_kco_settings' );
$display_privacy_policy_text = $settings['display_privacy_policy_text'];
if ( 'above' == $display_privacy_policy_text ) {
add_action( 'kco_wc_before_snippet', array( $this, 'kco_wc_display_privacy_policy_text' ) );
} elseif ( 'below' == $display_privacy_policy_text ) {
add_action( 'kco_wc_after_snippet', array( $this, 'kco_wc_display_privacy_policy_text' ) );
}
}
/**
* Gets the terms template.
*
* @return void
*/
public function kco_wc_display_privacy_policy_text() {
if ( function_exists( 'wc_checkout_privacy_policy_text' ) ) {
echo wc_checkout_privacy_policy_text();
}
}
/**
* Maybe adds a checkbox to the Klarna iFrame.
*
* @param array $args The arguments array for the Klarna request.
* @return array $args The arguments array for the Klarna request.
*/
public function maybe_add_checkbox( $args ) {
if ( function_exists( 'wc_terms_and_conditions_checkbox_enabled' ) ) {
$settings = get_option( 'woocommerce_kco_settings' );
$add_checkbox = $settings['add_terms_and_conditions_checkbox'];
if ( 'yes' === $add_checkbox && wc_terms_and_conditions_checkbox_enabled() ) {
$args['options']['additional_checkbox']['text'] = wc_replace_policy_page_link_placeholders( wc_get_terms_and_conditions_checkbox_text() );
$args['options']['additional_checkbox']['checked'] = false;
$args['options']['additional_checkbox']['required'] = true;
}
}
return $args;
}
}
new Klarna_Checkout_For_Woocommerce_GDPR();

View File

@@ -0,0 +1,45 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_AJAX class.
*
* Registers AJAX actions for Klarna Checkout for WooCommerce.
*
* @extends WC_AJAX
*/
class Klarna_Checkout_For_WooCommerce_Logging {
/** @var WC_Logger Logger instance */
private $logger = false;
/**
* Logging function.
*
* @param string $message Error message.
* @param string $level Error level.
*/
public function log( $message, $level = 'info' ) {
if ( $this->log_enabled() ) {
if ( empty( $this->logger ) ) {
$this->logger = wc_get_logger();
}
$this->logger->log( $level, $message, array( 'source' => 'klarna-checkout-for-woocommerce' ) );
}
}
/**
* Checks if logging is enabled in plugin settings.
*
* @return bool
*/
private function log_enabled() {
$settings = get_option( 'woocommerce_kco_settings' );
return ( null !== $settings['logging'] && 'yes' === $settings['logging'] );
}
}

View File

@@ -0,0 +1,141 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_Merchant_URLs class.
*
* Class that formats gets merchant URLs Klarna API.
*/
class Klarna_Checkout_For_WooCommerce_Merchant_URLs {
/**
* Gets formatted merchant URLs array.
*
* @return array
*/
public function get_urls() {
$merchant_urls = array(
'terms' => $this->get_terms_url(), // Required.
'checkout' => $this->get_checkout_url(), // Required.
'confirmation' => $this->get_confirmation_url(), // Required.
'push' => $this->get_push_url(), // Required.
'validation' => $this->get_validation_url(), // HTTPS.
'shipping_option_update' => $this->get_shipping_option_update_url(), // HTTPS.
// 'address_update' => $this->get_address_update_url(), // HTTPS.
'notification' => $this->get_notification_url(),
// 'country_change' => $this->get_country_change_url(), // HTTPS.
);
return $merchant_urls;
}
/**
* Terms URL.
*
* Required. URL of merchant terms and conditions. Should be different than checkout, confirmation and push URLs.
*
* @return string
*/
private function get_terms_url() {
$terms_url = get_permalink( wc_get_page_id( 'terms' ) );
return $terms_url;
}
/**
* Checkout URL.
*
* Required. URL of merchant checkout page. Should be different than terms, confirmation and push URLs.
*
* @return string
*/
private function get_checkout_url() {
$checkout_url = wc_get_checkout_url();
return $checkout_url;
}
/**
* Confirmation URL.
*
* Required. URL of merchant confirmation page. Should be different than checkout and confirmation URLs.
*
* @return string
*/
private function get_confirmation_url() {
$confirmation_url = wc_get_checkout_url() . '?confirm=yes&kco_wc_order_id={checkout.order.id}';
return $confirmation_url;
}
/**
* Push URL.
*
* Required. URL of merchant confirmation page. Should be different than checkout and confirmation URLs.
*
* @return string
*/
private function get_push_url() {
$push_url = get_home_url() . '/wc-api/KCO_WC_Push/?kco_wc_order_id={checkout.order.id}';
return $push_url;
}
/**
* Validation URL.
*
* URL that will be requested for final merchant validation, must be https.
*
* @return string
*/
private function get_validation_url() {
$validation_url = get_home_url() . '/wc-api/KCO_WC_Validation/?kco_wc_order_id={checkout.order.id}';
return str_replace( 'http:', 'https:', $validation_url );
}
/**
* Shipping option update URL.
*
* URL for shipping option update, must be https.
*
* @return string
*/
private function get_shipping_option_update_url() {
$shipping_option_update_url = get_home_url() . '/wc-api/KCO_WC_Shipping_Option_Update/';
return str_replace( 'http:', 'https:', $shipping_option_update_url );
}
/**
* Address update URL.
*
* URL for shipping, tax and purchase currency updates. Will be called on address changes, must be https.
*
* @return string
*/
private function get_address_update_url() {
$address_update_url = get_home_url() . '/wc-api/KCO_WC_Address_Update/?kco_wc_order_id={checkout.order.id}';
return str_replace( 'http:', 'https:', $address_update_url );
}
/**
* Notification URL.
*
* URL for notifications on pending orders.
*
* @return string
*/
private function get_notification_url() {
$notification_url = get_home_url() . '/wc-api/KCO_WC_Notification/?kco_wc_order_id={checkout.order.id}';
return $notification_url;
}
/**
* Country change URL.
*
* URL for shipping, tax and purchase currency updates. Will be called on purchase country changes, must be https.
*
* @return string
*/
private function get_country_change_url() {
$country_change_url = get_home_url() . '/wp-json/kcowc/v1/address/{checkout.order.id}';
return str_replace( 'http:', 'https:', $country_change_url );
}
}

View File

@@ -0,0 +1,592 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_Order_Lines class.
*
* Class that formats WooCommerce cart contents for Klarna API.
*/
class Klarna_Checkout_For_WooCommerce_Order_Lines {
/**
* Formatted order lines.
*
* @var $order_lines
*/
public $order_lines = array();
/**
* Shop country.
*
* @var string
*/
public $shop_country;
/**
* Send sales tax as separate item (US merchants).
*
* @var bool
*/
public $separate_sales_tax = false;
/**
* WC_Klarna_Payments_Order_Lines constructor.
*
* @param bool|string $shop_country Shop country.
*/
public function __construct( $shop_country = null ) {
if ( ! $shop_country ) {
$base_location = wc_get_base_location();
$shop_country = $base_location['country'];
}
$this->shop_country = $shop_country;
if ( 'US' === $this->shop_country ) {
$this->separate_sales_tax = true;
}
}
/**
* Processes cart data
*/
public function process_data() {
// @TODO: Process fees
$this->process_cart();
$this->process_shipping();
$this->process_sales_tax();
$this->process_coupons();
$this->process_fees();
}
/**
* Gets formatted order lines from WooCommerce cart.
*
* @return array
*/
public function get_order_lines() {
return $this->order_lines;
}
/**
* Gets order amount for Klarna API.
*
* @return int
*/
public function get_order_amount() {
return round( WC()->cart->total * 100 );
}
/**
* Gets order tax amount for Klarna API.
*
* @return int
*/
public function get_order_tax_amount() {
return round( ( WC()->cart->tax_total + WC()->cart->shipping_tax_total ) * 100 );
}
/**
* Process WooCommerce cart to Klarna Payments order lines.
*/
public function process_cart() {
foreach ( WC()->cart->get_cart() as $cart_item ) {
if ( $cart_item['quantity'] ) {
if ( $cart_item['variation_id'] ) {
$product = wc_get_product( $cart_item['variation_id'] );
} else {
$product = wc_get_product( $cart_item['product_id'] );
}
$klarna_item = array(
'reference' => $this->get_item_reference( $product ),
'name' => $this->get_item_name( $cart_item ),
'quantity' => $this->get_item_quantity( $cart_item ),
'unit_price' => $this->get_item_price( $cart_item ),
'tax_rate' => $this->get_item_tax_rate( $cart_item, $product ),
'total_amount' => $this->get_item_total_amount( $cart_item ),
'total_tax_amount' => $this->get_item_tax_amount( $cart_item ),
'total_discount_amount' => $this->get_item_discount_amount( $cart_item ),
);
// Add images.
$klarna_checkout_settings = get_option( 'woocommerce_kco_settings' );
if ( array_key_exists( 'send_product_urls', $klarna_checkout_settings ) && 'yes' === $klarna_checkout_settings['send_product_urls'] ) {
$klarna_item['product_url'] = $this->get_item_product_url( $product );
if ( $this->get_item_image_url( $product ) ) {
$klarna_item['image_url'] = $this->get_item_image_url( $product );
}
}
$this->order_lines[] = $klarna_item;
}
}
}
/**
* Process WooCommerce shipping to Klarna Payments order lines.
*/
public function process_shipping() {
if ( WC()->shipping->get_packages() && WC()->session->get( 'chosen_shipping_methods' )[0] ) {
$shipping = array(
'type' => 'shipping_fee',
'reference' => $this->get_shipping_reference(),
'name' => $this->get_shipping_name(),
'quantity' => 1,
'unit_price' => $this->get_shipping_amount(),
'tax_rate' => $this->get_shipping_tax_rate(),
'total_amount' => $this->get_shipping_amount(),
'total_tax_amount' => $this->get_shipping_tax_amount(),
);
$this->order_lines[] = $shipping;
}
}
/**
* Process sales tax for US.
*/
public function process_sales_tax() {
if ( $this->separate_sales_tax ) {
$sales_tax_amount = round( ( WC()->cart->tax_total + WC()->cart->shipping_tax_total ) * 100 );
// Add sales tax line item.
$sales_tax = array(
'type' => 'sales_tax',
'reference' => __( 'Sales Tax', 'klarna-checkout-for-woocommerce' ),
'name' => __( 'Sales Tax', 'klarna-checkout-for-woocommerce' ),
'quantity' => 1,
'unit_price' => $sales_tax_amount,
'tax_rate' => 0,
'total_amount' => $sales_tax_amount,
'total_discount_amount' => 0,
'total_tax_amount' => 0,
);
$this->order_lines[] = $sales_tax;
}
}
/**
* Process smart coupons.
*/
public function process_coupons() {
if ( ! empty( WC()->cart->get_coupons() ) ) {
foreach ( WC()->cart->get_coupons() as $coupon_key => $coupon ) {
$coupon_reference = '';
$coupon_amount = 0;
$coupon_tax_amount = '';
// Smart coupons are processed as real line items, cart and product discounts sent for reference only.
if ( 'smart_coupon' === $coupon->get_discount_type() ) {
$coupon_amount = - WC()->cart->get_coupon_discount_amount( $coupon_key ) * 100;
$coupon_tax_amount = - WC()->cart->get_coupon_discount_tax_amount( $coupon_key ) * 100;
$coupon_reference = 'Discount';
} else {
if ( 'US' === $this->shop_country ) {
$coupon_amount = 0;
$coupon_tax_amount = 0;
if ( $coupon->is_type( 'fixed_cart' ) || $coupon->is_type( 'percent' ) ) {
$coupon_type = 'Cart discount';
} elseif ( $coupon->is_type( 'fixed_product' ) || $coupon->is_type( 'percent_product' ) ) {
$coupon_type = 'Product discount';
} else {
$coupon_type = 'Discount';
}
$coupon_reference = $coupon_type . ' (amount: ' . WC()->cart->get_coupon_discount_amount( $coupon_key ) . ', tax amount: ' . WC()->cart->get_coupon_discount_tax_amount( $coupon_key ) . ')';
}
}
// Add separate discount line item, but only if it's a smart coupon or country is US.
if ( 'US' === $this->shop_country || 'smart_coupon' === $coupon->get_discount_type() ) {
$discount = array(
'type' => 'discount',
'reference' => $coupon_reference,
'name' => $coupon_key,
'quantity' => 1,
'unit_price' => $coupon_amount,
'tax_rate' => 0,
'total_amount' => $coupon_amount,
'total_discount_amount' => 0,
'total_tax_amount' => $coupon_tax_amount,
);
$this->order_lines[] = $discount;
}
} // End foreach().
} // End if().
}
/**
* Process cart fees.
*/
public function process_fees() {
if ( ! empty( WC()->cart->get_fees() ) ) {
foreach ( WC()->cart->get_fees() as $fee_key => $fee ) {
if ( $this->separate_sales_tax ) {
$fee_tax_rate = 0;
$fee_tax_amount = 0;
$fee_amount = round( $fee->amount * 100 );
} else {
$fee_tax_amount = round( $fee->tax * 100 );
$fee_amount = round( ( $fee->amount + $fee->tax ) * 100 );
$_tax = new WC_Tax();
$tmp_rates = $_tax->get_rates( $fee->tax_class );
$vat = array_shift( $tmp_rates );
if ( isset( $vat['rate'] ) ) {
$fee_tax_rate = round( $vat['rate'] * 100 );
} else {
$fee_tax_rate = 0;
}
}
// Add separate discount line item, but only if it's a smart coupon or country is US.
$fee_item = array(
'type' => 'shipping_fee',
'reference' => $fee->id,
'name' => $fee->name,
'quantity' => 1,
'unit_price' => $fee_amount,
'tax_rate' => $fee_tax_rate,
'total_amount' => $fee_amount,
'total_discount_amount' => 0,
'total_tax_amount' => $fee_tax_amount,
);
$this->order_lines[] = $fee_item;
} // End foreach().
} // End if().
}
// Helpers.
/**
* Get cart item name.
*
* @since 1.0
* @access public
*
* @param array $cart_item Cart item.
*
* @return string $item_name Cart item name.
*/
public function get_item_name( $cart_item ) {
$cart_item_data = $cart_item['data'];
$item_name = $cart_item_data->get_name();
return strip_tags( $item_name );
}
/**
* Calculate item tax percentage.
*
* @since 1.0
* @access public
*
* @param array $cart_item Cart item.
*
* @return integer $item_tax_amount Item tax amount.
*/
public function get_item_tax_amount( $cart_item ) {
if ( $this->separate_sales_tax ) {
$item_tax_amount = 0;
} else {
$item_tax_amount = $cart_item['line_tax'] * 100;
}
return round( $item_tax_amount );
}
/**
* Calculate item tax percentage.
*
* @since 1.0
* @access public
*
* @param array $cart_item Cart item.
* @param object $product Product object.
*
* @return integer $item_tax_rate Item tax percentage formatted for Klarna.
*/
public function get_item_tax_rate( $cart_item, $product ) {
if ( $product->is_taxable() && $cart_item['line_subtotal_tax'] > 0 ) {
// Calculate tax rate.
if ( $this->separate_sales_tax ) {
$item_tax_rate = 0;
} else {
$_tax = new WC_Tax();
$tmp_rates = $_tax->get_rates( $product->get_tax_class() );
$vat = array_shift( $tmp_rates );
if ( isset( $vat['rate'] ) ) {
$item_tax_rate = round( $vat['rate'] * 100 );
} else {
$item_tax_rate = 0;
}
}
} else {
$item_tax_rate = 0;
}
return round( $item_tax_rate );
}
/**
* Get cart item price.
*
* @since 1.0
* @access public
*
* @param array $cart_item Cart item.
*
* @return integer $item_price Cart item price.
*/
public function get_item_price( $cart_item ) {
if ( $this->separate_sales_tax ) {
$item_subtotal = $cart_item['line_subtotal'];
} else {
$item_subtotal = $cart_item['line_subtotal'] + $cart_item['line_subtotal_tax'];
}
$item_price = $item_subtotal * 100 / $cart_item['quantity'];
return round( $item_price );
}
/**
* Get cart item quantity.
*
* @since 1.0
* @access public
*
* @param array $cart_item Cart item.
*
* @return integer $item_quantity Cart item quantity.
*/
public function get_item_quantity( $cart_item ) {
return round( $cart_item['quantity'] );
}
/**
* Get cart item reference.
*
* Returns SKU or product ID.
*
* @since 1.0
* @access public
*
* @param object $product Product object.
*
* @return string $item_reference Cart item reference.
*/
public function get_item_reference( $product ) {
if ( $product->get_sku() ) {
$item_reference = $product->get_sku();
} else {
$item_reference = $product->get_id();
}
return substr( (string) $item_reference, 0, 64 );
}
/**
* Get cart item discount.
*
* @since 1.0
* @access public
*
* @param array $cart_item Cart item.
*
* @return integer $item_discount_amount Cart item discount.
*/
public function get_item_discount_amount( $cart_item ) {
if ( $cart_item['line_subtotal'] > $cart_item['line_total'] ) {
if ( $this->separate_sales_tax ) {
$item_discount_amount = $cart_item['line_subtotal'] - $cart_item['line_total'];
} else {
$item_discount_amount = $cart_item['line_subtotal'] + $cart_item['line_subtotal_tax'] - $cart_item['line_total'] - $cart_item['line_tax'];
}
$item_discount_amount = $item_discount_amount * 100;
} else {
$item_discount_amount = 0;
}
return round( $item_discount_amount );
}
/**
* Get cart item product URL.
*
* @since 1.1
* @access public
*
* @param WC_Product $product Product.
*
* @return string $item_product_url Cart item product URL.
*/
public function get_item_product_url( $product ) {
return $product->get_permalink();
}
/**
* Get cart item product image URL.
*
* @since 1.1
* @access public
*
* @param WC_Product $product Product.
*
* @return string $item_product_image_url Cart item product image URL.
*/
public function get_item_image_url( $product ) {
$image_url = false;
if ( $product->get_image_id() > 0 ) {
$image_id = $product->get_image_id();
$image_url = wp_get_attachment_image_url( $image_id, 'shop_thumbnail', false );
}
return $image_url;
}
/**
* Get cart item discount rate.
*
* @since 1.0
* @access public
*
* @param array $cart_item Cart item.
*
* @return integer $item_discount_rate Cart item discount rate.
*/
public function get_item_discount_rate( $cart_item ) {
$item_discount_rate = ( 1 - ( $cart_item['line_total'] / $cart_item['line_subtotal'] ) ) * 100 * 100;
return round( $item_discount_rate );
}
/**
* Get cart item total amount.
*
* @since 1.0
* @access public
*
* @param array $cart_item Cart item.
*
* @return integer $item_total_amount Cart item total amount.
*/
public function get_item_total_amount( $cart_item ) {
if ( $this->separate_sales_tax ) {
$item_total_amount = ( $cart_item['line_total'] * 100 );
} else {
$item_total_amount = ( ( $cart_item['line_total'] + $cart_item['line_tax'] ) * 100 );
}
return round( $item_total_amount );
}
/**
* Get shipping method name.
*
* @since 1.0
* @access public
*
* @return string $shipping_name Name for selected shipping method.
*/
public function get_shipping_name() {
$shipping_packages = WC()->shipping->get_packages();
foreach ( $shipping_packages as $i => $package ) {
$chosen_method = isset( WC()->session->chosen_shipping_methods[ $i ] ) ? WC()->session->chosen_shipping_methods[ $i ] : '';
if ( '' !== $chosen_method ) {
$package_rates = $package['rates'];
foreach ( $package_rates as $rate_key => $rate_value ) {
if ( $rate_key === $chosen_method ) {
$shipping_name = $rate_value->label;
}
}
}
}
if ( ! isset( $shipping_name ) ) {
$shipping_name = __( 'Shipping', 'klarna-checkout-for-woocommerce' );
}
return (string) $shipping_name;
}
/**
* Get shipping reference.
*
* @since 1.0
* @access public
*
* @return string $shipping_reference Reference for selected shipping method.
*/
public function get_shipping_reference() {
$shipping_packages = WC()->shipping->get_packages();
foreach ( $shipping_packages as $i => $package ) {
$chosen_method = isset( WC()->session->chosen_shipping_methods[ $i ] ) ? WC()->session->chosen_shipping_methods[ $i ] : '';
if ( '' !== $chosen_method ) {
$package_rates = $package['rates'];
foreach ( $package_rates as $rate_key => $rate_value ) {
if ( $rate_key === $chosen_method ) {
$shipping_reference = $rate_value->id;
}
}
}
}
if ( ! isset( $shipping_reference ) ) {
$shipping_reference = __( 'Shipping', 'klarna-checkout-for-woocommerce' );
}
return (string) $shipping_reference;
}
/**
* Get shipping method amount.
*
* @since 1.0
* @access public
*
* @return integer $shipping_amount Amount for selected shipping method.
*/
public function get_shipping_amount() {
if ( $this->separate_sales_tax ) {
$shipping_amount = (int) number_format( WC()->cart->shipping_total * 100, 0, '', '' );
} else {
$shipping_amount = (int) number_format( ( WC()->cart->shipping_total + WC()->cart->shipping_tax_total ) * 100, 0, '', '' );
}
return $shipping_amount;
}
/**
* Get shipping method tax rate.
*
* @since 1.0
* @access public
*
* @return integer $shipping_tax_rate Tax rate for selected shipping method.
*/
public function get_shipping_tax_rate() {
if ( WC()->cart->shipping_tax_total > 0 && ! $this->separate_sales_tax ) {
$shipping_tax_rate = round( WC()->cart->shipping_tax_total / WC()->cart->shipping_total, 2 ) * 100 * 100;
} else {
$shipping_tax_rate = 0;
}
return round( $shipping_tax_rate );
}
/**
* Get shipping method tax amount.
*
* @since 1.0
* @access public
*
* @return integer $shipping_tax_amount Tax amount for selected shipping method.
*/
public function get_shipping_tax_amount() {
if ( $this->separate_sales_tax ) {
$shipping_tax_amount = 0;
} else {
$shipping_tax_amount = WC()->cart->shipping_tax_total * 100;
}
return round( $shipping_tax_amount );
}
}

View File

@@ -0,0 +1,22 @@
<?php
/**
* WooCommerce status page extension
*
* @class Klarna_Checkout_For_WooCommerce_Status
* @version 0.8.0
* @package Klarna_Checkout_For_WooCommerce/Classes
* @category Class
* @author Krokedil
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class Klarna_Checkout_For_WooCommerce_Status {
public function __construct() {
add_action( 'woocommerce_system_status_report', array( $this, 'add_status_page_box' ) );
}
public function add_status_page_box() {
include_once( KCO_WC_PLUGIN_PATH . '/includes/klarna-checkout-for-woocommerce-status-report.php' );
}
}
$wc_collector_checkout_status = new Klarna_Checkout_For_WooCommerce_Status();

View File

@@ -0,0 +1,116 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Klarna_Checkout_For_WooCommerce_Templates class.
*/
class Klarna_Checkout_For_WooCommerce_Templates {
/**
* The reference the *Singleton* instance of this class.
*
* @var $instance
*/
protected static $instance;
/**
* Returns the *Singleton* instance of this class.
*
* @return self::$instance The *Singleton* instance.
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Plugin actions.
*/
public function __construct() {
// Override template if Klarna Checkout page.
add_filter( 'woocommerce_locate_template', array( $this, 'override_template' ), 10, 3 );
// Template hooks.
add_action( 'kco_wc_before_checkout_form', 'kco_wc_print_notices' );
add_action( 'kco_wc_before_checkout_form', 'kco_wc_calculate_totals', 1 );
add_action( 'kco_wc_before_checkout_form', 'woocommerce_checkout_login_form', 10 );
add_action( 'kco_wc_before_checkout_form', 'woocommerce_checkout_coupon_form', 20 );
add_action( 'kco_wc_after_order_review', 'kco_wc_show_extra_fields', 10 );
add_action( 'kco_wc_after_order_review', 'kco_wc_show_another_gateway_button', 20 );
add_action( 'kco_wc_before_snippet', 'kco_wc_prefill_consent', 10 );
add_action( 'kco_wc_after_snippet', 'kco_wc_show_payment_method_field', 10 );
}
/**
* Override checkout form template if Klarna Checkout is the selected payment method.
*
* @param string $template Template.
* @param string $template_name Template name.
* @param string $template_path Template path.
*
* @return string
*/
public function override_template( $template, $template_name, $template_path ) {
if ( is_checkout() ) {
// Klarna Checkout.
if ( 'checkout/form-checkout.php' === $template_name ) {
$available_gateways = WC()->payment_gateways()->get_available_payment_gateways();
if ( locate_template( 'woocommerce/klarna-checkout.php' ) ) {
$klarna_checkout_template = locate_template( 'woocommerce/klarna-checkout.php' );
} else {
$klarna_checkout_template = KCO_WC_PLUGIN_PATH . '/templates/klarna-checkout.php';
}
// Klarna checkout page.
if ( array_key_exists( 'kco', $available_gateways ) ) {
// If chosen payment method exists.
if ( 'kco' === WC()->session->get( 'chosen_payment_method' ) ) {
if ( ! isset( $_GET['confirm'] ) ) {
$template = $klarna_checkout_template;
}
}
// If chosen payment method does not exist and KCO is the first gateway.
if ( null === WC()->session->get( 'chosen_payment_method' ) || '' === WC()->session->get( 'chosen_payment_method' ) ) {
reset( $available_gateways );
if ( 'kco' === key( $available_gateways ) ) {
if ( ! isset( $_GET['confirm'] ) ) {
$template = $klarna_checkout_template;
}
}
}
// If another gateway is saved in session, but has since become unavailable.
if ( WC()->session->get( 'chosen_payment_method' ) ) {
if ( ! array_key_exists( WC()->session->get( 'chosen_payment_method' ), $available_gateways ) ) {
reset( $available_gateways );
if ( 'kco' === key( $available_gateways ) ) {
if ( ! isset( $_GET['confirm'] ) ) {
$template = $klarna_checkout_template;
}
}
}
}
}
}
}
// Fallback Klarna Order Received, used when WooCommerce checkout form submission fails.
if ( 'checkout/thankyou.php' === $template_name ) {
if ( isset( $_GET['kco_wc'] ) && 'true' === $_GET['kco_wc'] ) {
$template = KCO_WC_PLUGIN_PATH . '/templates/klarna-checkout-order-received.php';
}
}
return $template;
}
}
Klarna_Checkout_For_WooCommerce_Templates::get_instance();

View File

@@ -0,0 +1,157 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'WC_Klarna_Banners' ) ) {
/**
* Displays merchant information in the backend.
*/
class WC_Klarna_Banners {
/**
* WC_Klarna_Banners constructor.
*/
public function __construct() {
add_action( 'in_admin_header', array( $this, 'klarna_banner' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'load_admin_css' ) );
add_action( 'wp_ajax_hide_klarna_banner', array( $this, 'hide_klarna_banner' ) );
add_action( 'wp_ajax_nopriv_hide_klarna_banner', array( $this, 'hide_klarna_banner' ) );
}
/**
* Loads admin CSS file, has to be done here instead of gateway class, because
* it is required in all admin pages.
*/
public function load_admin_css() {
wp_enqueue_style(
'klarna_payments_admin',
plugins_url( 'assets/css/klarna-checkout-admin.css?v=120320182113', KCO_WC_MAIN_FILE )
);
}
/**
* Loads Klarna banner in admin pages.
*/
public function klarna_banner() {
$kco_settings = get_option( 'woocommerce_kco_settings' );
$show_banner = false;
// Always show banner in testmode.
if ( isset( $kco_settings['testmode'] ) && 'yes' === $kco_settings['testmode'] ) {
$show_banner = true;
}
// Go through countries and check if at least one has credentials configured.
$countries = array( 'eu', 'us' );
$country_set = false;
foreach ( $countries as $country ) {
if ( '' !== $kco_settings[ 'merchant_id_' . $country ] && '' !== $kco_settings[ 'shared_secret_' . $country ] ) {
$country_set = true;
}
}
if ( ! $country_set ) {
$show_banner = true;
}
if ( $show_banner && false === get_transient( 'klarna_hide_banner' ) ) {
?>
<div id="kb-spacer"></div>
<div id="klarna-banner">
<div id="kb-left">
<h1>Go live</h1>
<p>Before you can start to sell with Klarna you need your store to be approved by Klarna. When the installation is done and you are ready to go live, Klarna will need to verify the integration. Then you can go live with your store! If you wish to switch Klarna products then youll need the Klarna team to approve your store again.</p>
<a class="kb-button"
href="https://www.klarna.com/international/business/woocommerce/?utm_source=woo-backend&utm_medium=referral&utm_campaign=woo&utm_content=banner"
target="_blank">Go live with Klarna</a>
</div>
<div id="kb-right">
<h1>Currently using Klarna?</h1>
<p>Pay now, Pay later and Slice it. Klarna is entering a new world of smoooth. We would love for you to join us on the ride and to do so, you will need to upgrade your Klarna products to a new integration. You will then always get the latest features that Klarna develops and youll keep your current agreement along with your price settings.</p>
<a class="kb-button"
href="https://hello.klarna.com/product-upgrade?utm_source=woo-backend&utm_medium=referral&utm_campaign=woo&utm_content=banner"
target="_blank">Upgrade your contract with Klarna</a>
</div>
<img id="kb-image"
src="<?php echo esc_url( KCO_WC_PLUGIN_URL ); ?>/assets/img/klarna_logo_white.png"
alt="Klarna logo" width="110"/>
<span class="kb-dismiss dashicons dashicons-dismiss"></span>
</div>
<script type="text/javascript">
jQuery(document).ready(function($){
jQuery('.kb-dismiss').click(function(){
jQuery('#klarna-banner').slideUp();
jQuery.post(
ajaxurl,
{
action : 'hide_klarna_banner',
_wpnonce : '<?php echo wp_create_nonce('hide-klarna-banner'); ?>',
},
function(response){
console.log('Success hide kco banner');
}
);
});
});
</script>
<?php
}
}
/**
* @param $parent_options
*/
public static function settings_sidebar( $parent_options ) {
?>
<img id="klarna-settings-logo"
src="<?php echo esc_url( KCO_WC_PLUGIN_URL ); ?>/assets/img/klarna_logo_black.png" width="200"/>
<div id="klarna-wrapper">
<div id="klarna-main">
<?php echo $parent_options; ?>
</div>
<div id="klarna-sidebar">
<div class="kb-sidebar-section">
<img src="<?php echo esc_url( KCO_WC_PLUGIN_URL ); ?>/assets/img/icon_reminder.png" width="64"/>
<h3>Go live</h3>
<p>Before you can start to sell with Klarna you need your store to be approved by Klarna. When the installation is done and you are ready to go live, Klarna will need to verify the integration. Then you can go live with your store! If you wish to switch Klarna products then youll need the Klarna team to approve your store again.</p>
<a class="kb-button"
href="https://www.klarna.com/international/business/woocommerce/?utm_source=woo-backend&utm_medium=referral&utm_campaign=woo&utm_content=kco"
target="_blank">Go live with Klarna</a>
</div>
<div class="kb-sidebar-section">
<div>
<img src="<?php echo esc_url( KCO_WC_PLUGIN_URL ); ?>/assets/img/klarna_icons.png"
width="192"/>
</div>
<h3>Currently using Klarna?</h3>
<p>Pay now, Pay later and Slice it. Klarna is entering a new world of smoooth. We would love for you to join us on the ride and to do so, you will need to upgrade your Klarna products to a new integration. You will then always get the latest features that Klarna develops and youll keep your current agreement along with your price settings.</p>
<a class="kb-button"
href="https://hello.klarna.com/product-upgrade?utm_source=woo-backend&utm_medium=referral&utm_campaign=woo&utm_content=kco"
target="_blank">Upgrade your contract with Klarna</a>
</div>
</div>
</div>
<?php
}
/**
* Hide Klarna banner in admin pages for.
*/
public function hide_klarna_banner() {
set_transient( 'klarna_hide_banner', '1', 5 * DAY_IN_SECONDS );
wp_send_json_success( 'Hejä' );
wp_die();
}
}
new WC_Klarna_Banners();
}

View File

@@ -0,0 +1,476 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Echoes Klarna Checkout iframe snippet.
*/
function kco_wc_show_snippet() {
$klarna_order = KCO_WC()->api->get_order();
echo KCO_WC()->api->get_snippet( $klarna_order );
}
/**
* Shows order notes field in Klarna Checkout page.
*/
function kco_wc_show_order_notes() {
$order_fields = WC()->checkout()->get_checkout_fields( 'order' );
$key = 'order_comments';
if ( array_key_exists( $key, $order_fields ) ) {
$order_notes_field = $order_fields[ $key ];
woocommerce_form_field( $key, $order_notes_field, WC()->checkout()->get_value( $key ) );
}
}
/**
* Shows extra fields in Klarna Checkout page.
*/
function kco_wc_show_extra_fields() {
// Clear extra fields session values on reload.
// WC()->session->__unset( 'kco_wc_extra_fields_values' );
echo '<div id="kco-extra-fields">';
$extra_fields_values = WC()->session->get( 'kco_wc_extra_fields_values', array() );
$kco_wc_extra_checkout_fields = new Klarna_Checkout_For_WooCommerce_Extra_Checkout_Fields;
$extra_fields = $kco_wc_extra_checkout_fields->get_remaining_checkout_fields();
// Billing.
do_action( 'woocommerce_before_checkout_billing_form', WC()->checkout() );
foreach ( $extra_fields['billing'] as $key => $field ) {
if ( isset( $field['country_field'], $default_billing_fields[ $field['country_field'] ] ) ) {
$field['country'] = WC()->checkout()->get_value( $field['country_field'] );
}
$key_value = array_key_exists( $key, $extra_fields_values ) ? $extra_fields_values[ $key ] : '';
woocommerce_form_field( $key, $field, $key_value );
}
do_action( 'woocommerce_after_checkout_billing_form', WC()->checkout() );
if ( ! is_user_logged_in() && WC()->checkout()->is_registration_enabled() ) { ?>
<div class="woocommerce-account-fields">
<?php if ( ! WC()->checkout()->is_registration_required() ) { ?>
<p class="form-row form-row-wide create-account">
<label class="woocommerce-form__label woocommerce-form__label-for-checkbox checkbox">
<input class="woocommerce-form__input woocommerce-form__input-checkbox input-checkbox"
id="createaccount" <?php checked( ( true === WC()->checkout()->get_value( 'createaccount' ) || ( true === apply_filters( 'woocommerce_create_account_default_checked', false ) ) ), true ) ?>
type="checkbox" name="createaccount" value="1"/>
<span><?php _e( 'Create an account?', 'klarna-checkout-for-woocommerce' ); ?></span>
</label>
</p>
<?php } ?>
<?php do_action( 'woocommerce_before_checkout_registration_form', WC()->checkout() ); ?>
<?php if ( WC()->checkout()->get_checkout_fields( 'account' ) ) { ?>
<div class="create-account">
<?php foreach ( WC()->checkout()->get_checkout_fields( 'account' ) as $key => $field ) { ?>
<?php woocommerce_form_field( $key, $field, WC()->checkout()->get_value( $key ) ); ?>
<?php } ?>
<div class="clear"></div>
</div>
<?php } ?>
<?php do_action( 'woocommerce_after_checkout_registration_form', WC()->checkout() ); ?>
</div>
<?php
}
// Shipping.
do_action( 'woocommerce_before_checkout_shipping_form', WC()->checkout() );
foreach ( $extra_fields['shipping'] as $key => $field ) {
if ( isset( $field['country_field'], $default_shipping_fields[ $field['country_field'] ] ) ) {
$field['country'] = WC()->checkout()->get_value( $field['country_field'] );
}
$key_value = array_key_exists( $key, $extra_fields_values ) ? $extra_fields_values[ $key ] : '';
woocommerce_form_field( $key, $field, $key_value );
}
do_action( 'woocommerce_after_checkout_shipping_form', WC()->checkout() );
// Order.
do_action( 'woocommerce_before_order_notes', WC()->checkout() );
if ( apply_filters( 'woocommerce_enable_order_notes_field', true ) ) {
foreach ( $extra_fields['order'] as $key => $field ) {
$key_value = array_key_exists( $key, $extra_fields_values ) ? $extra_fields_values[ $key ] : '';
woocommerce_form_field( $key, $field, $key_value );
}
}
do_action( 'woocommerce_after_order_notes', WC()->checkout() );
echo '</div>';
}
/**
* Shows select another payment method button in Klarna Checkout page.
*/
function kco_wc_show_another_gateway_button() {
$available_gateways = WC()->payment_gateways()->get_available_payment_gateways();
if ( count( $available_gateways ) > 1 ) {
$settings = get_option( 'woocommerce_kco_settings' );
$select_another_method_text = isset( $settings['select_another_method_text'] ) && '' !== $settings['select_another_method_text'] ? $settings['select_another_method_text'] : __( 'Select another payment method', 'klarna-checkout-for-woocommerce' );
?>
<p style="margin-top:30px">
<a class="checkout-button button" href="#" id="klarna-checkout-select-other">
<?php echo $select_another_method_text; ?>
</a>
</p>
<?php
}
}
/**
* Is it OK to prefill customer data?
*/
function kco_wc_prefill_allowed() {
$base_location = wc_get_base_location();
if ( 'DE' === $base_location['country'] || 'AT' === $base_location['country'] ) {
$settings = get_option( 'woocommerce_kco_settings' );
$consent_setting_checked = ( isset( $settings['prefill_consent'] ) && 'yes' === $settings['prefill_consent'] );
if ( $consent_setting_checked && is_user_logged_in() && WC()->session->get( 'kco_wc_prefill_consent', false ) ) {
return true;
}
return false;
}
return true;
}
/**
* Calculates cart totals.
*/
function kco_wc_calculate_totals() {
WC()->cart->calculate_fees();
WC()->cart->calculate_shipping();
WC()->cart->calculate_totals();
}
function kco_wc_show_payment_method_field() {
?>
<input style="display:none" type="radio" name="payment_method" value="kco"/>
<?php
}
/**
* Shows prefill consent text.
*/
function kco_wc_prefill_consent() {
if ( ! kco_wc_prefill_allowed() && is_user_logged_in() ) {
$consent_url = add_query_arg(
[ 'prefill_consent' => 'yes' ],
wc_get_checkout_url()
);
$credentials = KCO_WC()->credentials->get_credentials_from_session();
$merchant_id = $credentials['merchant_id'];
if ( 'de_DE' === get_locale() ) {
$button_text = 'Meine Adressdaten vorausfüllen';
$link_text = 'Es gelten die Nutzungsbedingungen zur Datenübertragung';
$popup_text = 'In unserem Kassenbereich nutzen wir Klarna Checkout. Dazu werden Ihre Daten, wie E-Mail-Adresse, Vor- und
Nachname, Geburtsdatum, Adresse und Telefonnummer, soweit erforderlich, automatisch an Klarna AB übertragen,
sobald Sie in den Kassenbereich gelangen. Die Nutzungsbedingungen für Klarna Checkout finden Sie hier:
<a href="https://cdn.klarna.com/1.0/shared/content/legal/terms/' . $merchant_id . '/de_de/checkout" target="_blank">https://cdn.klarna.com/1.0/shared/content/legal/terms/' . $merchant_id . '/de_de/checkout</a>';
} else {
$button_text = 'Meine Adressdaten vorausfüllen';
$link_text = 'Es gelten die Nutzungsbedingungen zur Datenübertragung';
$popup_text = 'We use Klarna Checkout as our checkout, which offers a simplified purchase experience. When you choose to go to the checkout, your email address, first name, last name, date of birth, address and phone number may be automatically transferred to Klarna AB, enabling the provision of Klarna Checkout. These User Terms apply for the use of Klarna Checkout is available here:
<a target="_blank" href="https://cdn.klarna.com/1.0/shared/content/legal/terms/' . $merchant_id . '/en_us/checkout">https://cdn.klarna.com/1.0/shared/content/legal/terms/' . $merchant_id . '/en_us/checkout</a>';
}
?>
<p><a class="button" href="<?php echo $consent_url; ?>"><?php echo $button_text; ?></a></p>
<p><a href="#TB_inline?width=600&height=550&inlineId=consent-text"
class="thickbox"><?php echo $link_text; ?></a>
</p>
<div id="consent-text" style="display:none;">
<p><?php echo $popup_text; ?></p>
</div>
<?php
}
}
/**
* Converts 3-letter ISO returned from Klarna to 2-letter code used in WooCommerce.
*
* @param $country
*/
function kco_wc_country_code_converter( $country ) {
$countries = array(
'AF' => 'AFG', // Afghanistan.
'AX' => 'ALA', // Aland Islands.
'AL' => 'ALB', // Albania.
'DZ' => 'DZA', // Algeria.
'AS' => 'ASM', // American Samoa.
'AD' => 'AND', // Andorra.
'AO' => 'AGO', // Angola.
'AI' => 'AIA', // Anguilla.
'AQ' => 'ATA', // Antarctica.
'AG' => 'ATG', // Antigua and Barbuda.
'AR' => 'ARG', // Argentina.
'AM' => 'ARM', // Armenia.
'AW' => 'ABW', // Aruba.
'AU' => 'AUS', // Australia.
'AT' => 'AUT', // Austria.
'AZ' => 'AZE', // Azerbaijan.
'BS' => 'BHS', // Bahamas.
'BH' => 'BHR', // Bahrain.
'BD' => 'BGD', // Bangladesh.
'BB' => 'BRB', // Barbados.
'BY' => 'BLR', // Belarus.
'BE' => 'BEL', // Belgium.
'BZ' => 'BLZ', // Belize.
'BJ' => 'BEN', // Benin.
'BM' => 'BMU', // Bermuda.
'BT' => 'BTN', // Bhutan.
'BO' => 'BOL', // Bolivia.
'BQ' => 'BES', // Bonaire, Saint Estatius and Saba.
'BA' => 'BIH', // Bosnia and Herzegovina.
'BW' => 'BWA', // Botswana.
'BV' => 'BVT', // Bouvet Islands.
'BR' => 'BRA', // Brazil.
'IO' => 'IOT', // British Indian Ocean Territory.
'BN' => 'BRN', // Brunei.
'BG' => 'BGR', // Bulgaria.
'BF' => 'BFA', // Burkina Faso.
'BI' => 'BDI', // Burundi.
'KH' => 'KHM', // Cambodia.
'CM' => 'CMR', // Cameroon.
'CA' => 'CAN', // Canada.
'CV' => 'CPV', // Cape Verde.
'KY' => 'CYM', // Cayman Islands.
'CF' => 'CAF', // Central African Republic.
'TD' => 'TCD', // Chad.
'CL' => 'CHL', // Chile.
'CN' => 'CHN', // China.
'CX' => 'CXR', // Christmas Island.
'CC' => 'CCK', // Cocos (Keeling) Islands.
'CO' => 'COL', // Colombia.
'KM' => 'COM', // Comoros.
'CG' => 'COG', // Congo.
'CD' => 'COD', // Congo, Democratic Republic of the.
'CK' => 'COK', // Cook Islands.
'CR' => 'CRI', // Costa Rica.
'CI' => 'CIV', // Côte d\'Ivoire.
'HR' => 'HRV', // Croatia.
'CU' => 'CUB', // Cuba.
'CW' => 'CUW', // Curaçao.
'CY' => 'CYP', // Cyprus.
'CZ' => 'CZE', // Czech Republic.
'DK' => 'DNK', // Denmark.
'DJ' => 'DJI', // Djibouti.
'DM' => 'DMA', // Dominica.
'DO' => 'DOM', // Dominican Republic.
'EC' => 'ECU', // Ecuador.
'EG' => 'EGY', // Egypt.
'SV' => 'SLV', // El Salvador.
'GQ' => 'GNQ', // Equatorial Guinea.
'ER' => 'ERI', // Eritrea.
'EE' => 'EST', // Estonia.
'ET' => 'ETH', // Ethiopia.
'FK' => 'FLK', // Falkland Islands.
'FO' => 'FRO', // Faroe Islands.
'FJ' => 'FIJ', // Fiji.
'FI' => 'FIN', // Finland.
'FR' => 'FRA', // France.
'GF' => 'GUF', // French Guiana.
'PF' => 'PYF', // French Polynesia.
'TF' => 'ATF', // French Southern Territories.
'GA' => 'GAB', // Gabon.
'GM' => 'GMB', // Gambia.
'GE' => 'GEO', // Georgia.
'DE' => 'DEU', // Germany.
'GH' => 'GHA', // Ghana.
'GI' => 'GIB', // Gibraltar.
'GR' => 'GRC', // Greece.
'GL' => 'GRL', // Greenland.
'GD' => 'GRD', // Grenada.
'GP' => 'GLP', // Guadeloupe.
'GU' => 'GUM', // Guam.
'GT' => 'GTM', // Guatemala.
'GG' => 'GGY', // Guernsey.
'GN' => 'GIN', // Guinea.
'GW' => 'GNB', // Guinea-Bissau.
'GY' => 'GUY', // Guyana.
'HT' => 'HTI', // Haiti.
'HM' => 'HMD', // Heard Island and McDonald Islands.
'VA' => 'VAT', // Holy See (Vatican City State).
'HN' => 'HND', // Honduras.
'HK' => 'HKG', // Hong Kong.
'HU' => 'HUN', // Hungary.
'IS' => 'ISL', // Iceland.
'IN' => 'IND', // India.
'ID' => 'IDN', // Indonesia.
'IR' => 'IRN', // Iran.
'IQ' => 'IRQ', // Iraq.
'IE' => 'IRL', // Republic of Ireland.
'IM' => 'IMN', // Isle of Man.
'IL' => 'ISR', // Israel.
'IT' => 'ITA', // Italy.
'JM' => 'JAM', // Jamaica.
'JP' => 'JPN', // Japan.
'JE' => 'JEY', // Jersey.
'JO' => 'JOR', // Jordan.
'KZ' => 'KAZ', // Kazakhstan.
'KE' => 'KEN', // Kenya.
'KI' => 'KIR', // Kiribati.
'KP' => 'PRK', // Korea, Democratic People's Republic of.
'KR' => 'KOR', // Korea, Republic of (South).
'KW' => 'KWT', // Kuwait.
'KG' => 'KGZ', // Kyrgyzstan.
'LA' => 'LAO', // Laos.
'LV' => 'LVA', // Latvia.
'LB' => 'LBN', // Lebanon.
'LS' => 'LSO', // Lesotho.
'LR' => 'LBR', // Liberia.
'LY' => 'LBY', // Libya.
'LI' => 'LIE', // Liechtenstein.
'LT' => 'LTU', // Lithuania.
'LU' => 'LUX', // Luxembourg.
'MO' => 'MAC', // Macao S.A.R., China.
'MK' => 'MKD', // Macedonia.
'MG' => 'MDG', // Madagascar.
'MW' => 'MWI', // Malawi.
'MY' => 'MYS', // Malaysia.
'MV' => 'MDV', // Maldives.
'ML' => 'MLI', // Mali.
'MT' => 'MLT', // Malta.
'MH' => 'MHL', // Marshall Islands.
'MQ' => 'MTQ', // Martinique.
'MR' => 'MRT', // Mauritania.
'MU' => 'MUS', // Mauritius.
'YT' => 'MYT', // Mayotte.
'MX' => 'MEX', // Mexico.
'FM' => 'FSM', // Micronesia.
'MD' => 'MDA', // Moldova.
'MC' => 'MCO', // Monaco.
'MN' => 'MNG', // Mongolia.
'ME' => 'MNE', // Montenegro.
'MS' => 'MSR', // Montserrat.
'MA' => 'MAR', // Morocco.
'MZ' => 'MOZ', // Mozambique.
'MM' => 'MMR', // Myanmar.
'NA' => 'NAM', // Namibia.
'NR' => 'NRU', // Nauru.
'NP' => 'NPL', // Nepal.
'NL' => 'NLD', // Netherlands.
'AN' => 'ANT', // Netherlands Antilles.
'NC' => 'NCL', // New Caledonia.
'NZ' => 'NZL', // New Zealand.
'NI' => 'NIC', // Nicaragua.
'NE' => 'NER', // Niger.
'NG' => 'NGA', // Nigeria.
'NU' => 'NIU', // Niue.
'NF' => 'NFK', // Norfolk Island.
'MP' => 'MNP', // Northern Mariana Islands.
'NO' => 'NOR', // Norway.
'OM' => 'OMN', // Oman.
'PK' => 'PAK', // Pakistan.
'PW' => 'PLW', // Palau.
'PS' => 'PSE', // Palestinian Territory.
'PA' => 'PAN', // Panama.
'PG' => 'PNG', // Papua New Guinea.
'PY' => 'PRY', // Paraguay.
'PE' => 'PER', // Peru.
'PH' => 'PHL', // Philippines.
'PN' => 'PCN', // Pitcairn.
'PL' => 'POL', // Poland.
'PT' => 'PRT', // Portugal.
'PR' => 'PRI', // Puerto Rico.
'QA' => 'QAT', // Qatar.
'RE' => 'REU', // Reunion.
'RO' => 'ROU', // Romania.
'RU' => 'RUS', // Russia.
'RW' => 'RWA', // Rwanda.
'BL' => 'BLM', // Saint Bartholemy.
'SH' => 'SHN', // Saint Helena.
'KN' => 'KNA', // Saint Kitts and Nevis.
'LC' => 'LCA', // Saint Lucia.
'MF' => 'MAF', // Saint Martin (French part).
'SX' => 'SXM', // Sint Maarten / Saint Martin (Dutch part).
'PM' => 'SPM', // Saint Pierre and Miquelon.
'VC' => 'VCT', // Saint Vincent and the Grenadines.
'WS' => 'WSM', // Samoa.
'SM' => 'SMR', // San Marino.
'ST' => 'STP', // Sso Tome and Principe.
'SA' => 'SAU', // Saudi Arabia.
'SN' => 'SEN', // Senegal.
'RS' => 'SRB', // Serbia.
'SC' => 'SYC', // Seychelles.
'SL' => 'SLE', // Sierra Leone.
'SG' => 'SGP', // Singapore.
'SK' => 'SVK', // Slovakia.
'SI' => 'SVN', // Slovenia.
'SB' => 'SLB', // Solomon Islands.
'SO' => 'SOM', // Somalia.
'ZA' => 'ZAF', // South Africa.
'GS' => 'SGS', // South Georgia/Sandwich Islands.
'SS' => 'SSD', // South Sudan.
'ES' => 'ESP', // Spain.
'LK' => 'LKA', // Sri Lanka.
'SD' => 'SDN', // Sudan.
'SR' => 'SUR', // Suriname.
'SJ' => 'SJM', // Svalbard and Jan Mayen.
'SZ' => 'SWZ', // Swaziland.
'SE' => 'SWE', // Sweden.
'CH' => 'CHE', // Switzerland.
'SY' => 'SYR', // Syria.
'TW' => 'TWN', // Taiwan.
'TJ' => 'TJK', // Tajikistan.
'TZ' => 'TZA', // Tanzania.
'TH' => 'THA', // Thailand.
'TL' => 'TLS', // Timor-Leste.
'TG' => 'TGO', // Togo.
'TK' => 'TKL', // Tokelau.
'TO' => 'TON', // Tonga.
'TT' => 'TTO', // Trinidad and Tobago.
'TN' => 'TUN', // Tunisia.
'TR' => 'TUR', // Turkey.
'TM' => 'TKM', // Turkmenistan.
'TC' => 'TCA', // Turks and Caicos Islands.
'TV' => 'TUV', // Tuvalu.
'UG' => 'UGA', // Uganda.
'UA' => 'UKR', // Ukraine.
'AE' => 'ARE', // United Arab Emirates.
'GB' => 'GBR', // United Kingdom.
'US' => 'USA', // United States.
'UM' => 'UMI', // United States Minor Outlying Islands.
'UY' => 'URY', // Uruguay.
'UZ' => 'UZB', // Uzbekistan.
'VU' => 'VUT', // Vanuatu.
'VE' => 'VEN', // Venezuela.
'VN' => 'VNM', // Vietnam.
'VG' => 'VGB', // Virgin Islands, British.
'VI' => 'VIR', // Virgin Island, U.S..
'WF' => 'WLF', // Wallis and Futuna.
'EH' => 'ESH', // Western Sahara.
'YE' => 'YEM', // Yemen.
'ZM' => 'ZMB', // Zambia.
'ZW' => 'ZWE', // Zimbabwe.
);
return array_search( strtoupper( $country ), $countries, true );
}
/**
* Prints error notices if needed.
*/
function kco_wc_print_notices() {
if ( isset( $_GET['stock_validate_failed'] ) ) {
wc_add_notice( __( 'Not all products are in stock.', 'klarna-checkout-for-woocommerce' ), 'error' );
} elseif ( isset( $_GET['no_shipping'] ) ) {
wc_add_notice( __( 'No shipping was selected.', 'klarna-checkout-for-woocommerce' ), 'error' );
} elseif ( isset( $_GET['required_fields'] ) ) {
$failed_fields = json_decode( base64_decode( $_GET['required_fields'] ) );
$fields_string = '';
foreach ( $failed_fields as $field ) {
$fields_string = $fields_string . ' ' . $field;
}
wc_add_notice( __( sprintf( 'The following fields are required:%s.', $fields_string ), 'klarna-checkout-for-woocommerce' ), 'error' );
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* Admin View: Page - Status Report.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?>
<table class="wc_status_table widefat" cellspacing="0">
<thead>
<tr>
<th colspan="3" data-export-label="Klarna Checkout">
<h2><?php _e( 'Klarna Checkout', 'klarna-checkout-for-woocommerce' ); ?><?php echo wc_help_tip( __( 'Klarna Checkout System Status.', 'klarna-checkout-for-woocommerce' ) ); ?></h2>
</th>
</tr>
</thead>
<tbody>
<tr>
<td data-export-label="Orders created via API callback"><?php _e( 'Orders created via API callback', 'klarna-checkout-for-woocommerce' ); ?>:</td>
<td class="help"><?php echo wc_help_tip( __( 'Displays the number of orders created via the API callback feature during the last month.', 'klarna-checkout-for-woocommerce' ) ); ?></td>
<td>
<?php
$query = new WC_Order_Query( array(
'limit' => -1,
'orderby' => 'date',
'order' => 'DESC',
'return' => 'ids',
'payment_method' => 'kco',
'date_created' => '>' . ( time() - MONTH_IN_SECONDS )
) );
$orders = $query->get_orders();
$amont_of_klarna_orders = count( $orders );
$amont_of_api_callback_orders = 0;
foreach( $orders as $order_id ) {
if( 'klarna_checkout_backup_order_creation' == get_post_meta( $order_id, '_created_via', true ) ) {
$amont_of_api_callback_orders++;
}
}
if( $amont_of_api_callback_orders > 0 ) {
$percent_of_orders = round( ($amont_of_api_callback_orders/$amont_of_klarna_orders) * 100 );
} else {
$percent_of_orders = 0;
}
if( $percent_of_orders >= 10 ) {
$status = 'error';
} else {
$status = 'yes';
}
echo '<strong><mark class="' . $status . '">' . $percent_of_orders . '% (' . $amont_of_api_callback_orders . ' of ' . $amont_of_klarna_orders . ')</mark></strong> of all orders payed via Klarna Checkout was created via API callback during the last month. This is a fallback order creation feature. You should aim for 0%.';
?>
</td>
</tr>
</tbody>
</table>

View File

@@ -0,0 +1,377 @@
<?php
/*
* Plugin Name: Klarna Checkout for WooCommerce
* Plugin URI: https://krokedil.com/klarna/
* Description: Klarna Checkout payment gateway for WooCommerce.
* Author: Krokedil
* Author URI: https://krokedil.com/
* Version: 1.5.2
* Text Domain: klarna-checkout-for-woocommerce
* Domain Path: /languages
*
* WC requires at least: 3.0
* WC tested up to: 3.4.0
*
* Copyright (c) 2017-2018 Krokedil
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Required minimums and constants
*/
define( 'KCO_WC_VERSION', '1.5.2' );
define( 'KCO_WC_MIN_PHP_VER', '5.3.0' );
define( 'KCO_WC_MIN_WC_VER', '2.5.0' );
define( 'KCO_WC_MAIN_FILE', __FILE__ );
define( 'KCO_WC_PLUGIN_PATH', untrailingslashit( plugin_dir_path( __FILE__ ) ) );
define( 'KCO_WC_PLUGIN_URL', untrailingslashit( plugin_dir_url( __FILE__ ) ) );
define( 'KROKEDIL_LOGGER_GATEWAY', 'kco' );
if ( ! class_exists( 'Klarna_Checkout_For_WooCommerce' ) ) {
/**
* Class Klarna_Checkout_For_WooCommerce
*/
class Klarna_Checkout_For_WooCommerce {
/**
* The reference the *Singleton* instance of this class.
*
* @var $instance
*/
protected static $instance;
/**
* Reference to API class.
*
* @var $api
*/
public $api;
/**
* Reference to merchant URLs class.
*
* @var $merchant_urls
*/
public $merchant_urls;
/**
* Reference to order lines class.
*
* @var $order_lines
*/
public $order_lines;
/**
* Reference to credentials class.
*
* @var $credentials
*/
public $credentials;
/**
* Reference to logging class.
*
* @var $log
*/
public $logger;
/**
* Returns the *Singleton* instance of this class.
*
* @return self::$instance The *Singleton* instance.
*/
public static function get_instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Private clone method to prevent cloning of the instance of the
* *Singleton* instance.
*
* @return void
*/
private function __clone() {
wc_doing_it_wrong( __FUNCTION__, __( 'Nope' ), '1.0' );
}
/**
* Private unserialize method to prevent unserializing of the *Singleton*
* instance.
*
* @return void
*/
private function __wakeup() {
wc_doing_it_wrong( __FUNCTION__, __( 'Nope' ), '1.0' );
}
/**
* Notices (array)
*
* @var array
*/
public $notices = array();
/**
* Protected constructor to prevent creating a new instance of the
* *Singleton* via the `new` operator from outside of this class.
*/
protected function __construct() {
add_action( 'admin_notices', array( $this, 'admin_notices' ), 15 );
add_action( 'plugins_loaded', array( $this, 'init' ) );
add_action( 'admin_notices', array( $this, 'order_management_check' ) );
// Add quantity button in woocommerce_order_review() function.
add_filter( 'woocommerce_checkout_cart_item_quantity', array( $this, 'add_quantity_field' ), 10, 3 );
$KCO_options = get_option( 'woocommerce_kco_settings' );
if ( 'yes' === $KCO_options['logging'] ) {
define( 'KROKEDIL_LOGGER_ON', true );
}
}
/**
* Init the plugin after plugins_loaded so environment variables are set.
*/
public function init() {
// Init the gateway itself.
$this->init_gateways();
add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), array( $this, 'plugin_action_links' ) );
}
/**
* Adds plugin action links
*
* @param array $links Plugin action link before filtering.
*
* @return array Filtered links.
*/
public function plugin_action_links( $links ) {
$setting_link = $this->get_setting_link();
$plugin_links = array(
'<a href="' . $setting_link . '">' . __( 'Settings', 'klarna-checkout-for-woocommerce' ) . '</a>',
'<a href="http://krokedil.se/">' . __( 'Support', 'klarna-checkout-for-woocommerce' ) . '</a>',
);
return array_merge( $plugin_links, $links );
}
/**
* Get setting link.
*
* @since 1.0.0
*
* @return string Setting link
*/
public function get_setting_link() {
$section_slug = 'kco';
return admin_url( 'admin.php?page=wc-settings&tab=checkout&section=' . $section_slug );
}
/**
* Show admin notice if Order Management plugin is not active.
*/
public function order_management_check() {
/**
* Check if file exists
* - yes: check if activated
* - yes: all good
* - no: show activate button
* - no: show install button
*/
$plugin_slug = 'klarna-order-management-for-woocommerce';
// If plugin file exists.
if ( file_exists( WP_PLUGIN_DIR . '/' . $plugin_slug . '/' . $plugin_slug . '.php' ) ) {
// If plugin is not active show Activate button.
if ( ! is_plugin_active( $plugin_slug . '/' . $plugin_slug . '.php' ) && current_user_can( 'activate_plugins' ) ) {
include_once ABSPATH . 'wp-admin/includes/plugin-install.php';
$plugin = plugins_api( 'plugin_information', array(
'slug' => $plugin_slug,
) );
$plugin = (array) $plugin;
$status = install_plugin_install_status( $plugin );
$name = wp_kses( $plugin['name'], array() );
$url = add_query_arg( array(
'_wpnonce' => wp_create_nonce( 'activate-plugin_' . $status['file'] ),
'action' => 'activate',
'plugin' => $status['file'],
), network_admin_url( 'plugins.php' ) );
$description = $name . ' is not active. Please activate it so you can capture, cancel, update and refund Klarna orders.';
?>
<div class="notice notice-warning">
<p>
<?php echo esc_html( $description ); ?>
<a class="install-now button" data-slug="<?php esc_attr_e( $plugin_slug ); ?>"
href="<?php echo esc_url( $url ); ?>"
aria-label="Activate <?php esc_attr_e( $name ); ?> now"
data-name="<?php esc_attr_e( $name ); ?>"><?php _e( 'Activate Now', 'klarna-checkout-for-woocommerce' ); ?></a>
</p>
</div>
<?php
}
} else { // If plugin file does not exist, show Install button.
if ( current_user_can( 'install_plugins' ) ) {
include_once ABSPATH . 'wp-admin/includes/plugin-install.php';
$plugin = plugins_api( 'plugin_information', array(
'slug' => $plugin_slug,
) );
$plugin = (array) $plugin;
$status = install_plugin_install_status( $plugin );
if ( 'install' === $status['status'] && $status['url'] ) {
$name = wp_kses( $plugin['name'], array() );
$url = $status['url'];
$description = $name . ' is not installed. Please install and activate it so you can capture, cancel, update and refund Klarna orders.';
?>
<div class="notice notice-warning">
<p>
<?php echo esc_html( $description ); ?>
<a class="install-now button" data-slug="<?php esc_attr_e( $plugin_slug ); ?>"
href="<?php echo esc_url( $url ); ?>"
aria-label="Install <?php esc_attr_e( $name ); ?> now"
data-name="<?php esc_attr_e( $name ); ?>"><?php _e( 'Install Now', 'klarna-checkout-for-woocommerce' ); ?></a>
</p>
</div>
<?php
}
}
} // End if().
}
/**
* Display any notices we've collected thus far (e.g. for connection, disconnection)
*/
public function admin_notices() {
foreach ( (array) $this->notices as $notice_key => $notice ) {
echo "<div class='" . esc_attr( $notice['class'] ) . "'><p>";
echo wp_kses( $notice['message'], array( 'a' => array( 'href' => array() ) ) );
echo '</p></div>';
}
}
/**
* Initialize the gateway. Called very early - in the context of the plugins_loaded action
*
* @since 1.0.0
*/
public function init_gateways() {
if ( ! class_exists( 'WC_Payment_Gateway' ) ) {
return;
}
include_once KCO_WC_PLUGIN_PATH . '/includes/class-klarna-checkout-for-woocommerce-gateway.php';
include_once KCO_WC_PLUGIN_PATH . '/includes/class-klarna-checkout-for-woocommerce-api.php';
include_once KCO_WC_PLUGIN_PATH . '/includes/class-klarna-checkout-for-woocommerce-api-callbacks.php';
include_once KCO_WC_PLUGIN_PATH . '/includes/class-klarna-checkout-for-woocommerce-templates.php';
include_once KCO_WC_PLUGIN_PATH . '/includes/class-klarna-checkout-for-woocommerce-ajax.php';
include_once KCO_WC_PLUGIN_PATH . '/includes/class-klarna-checkout-for-woocommerce-order-lines.php';
include_once KCO_WC_PLUGIN_PATH . '/includes/class-klarna-checkout-for-woocommerce-merchant-urls.php';
include_once KCO_WC_PLUGIN_PATH . '/includes/class-klarna-checkout-for-woocommerce-credentials.php';
include_once KCO_WC_PLUGIN_PATH . '/includes/class-klarna-checkout-for-woocommerce-logging.php';
include_once KCO_WC_PLUGIN_PATH . '/includes/class-klarna-checkout-for-woocommerce-fields.php';
include_once KCO_WC_PLUGIN_PATH . '/includes/class-klarna-checkout-for-woocommerce-confirmation.php';
include_once KCO_WC_PLUGIN_PATH . '/includes/class-klarna-checkout-for-woocommerce-extra-checkout-fields.php';
include_once KCO_WC_PLUGIN_PATH . '/includes/class-klarna-checkout-for-woocommerce-status.php';
include_once KCO_WC_PLUGIN_PATH . '/includes/class-klarna-checkout-for-woocommerce-create-local-order-fallback.php';
include_once KCO_WC_PLUGIN_PATH . '/includes/class-klarna-checkout-for-woocommerce-gdpr.php';
include_once KCO_WC_PLUGIN_PATH . '/includes/klarna-checkout-for-woocommerce-functions.php';
include_once KCO_WC_PLUGIN_PATH . '/vendor/autoload.php';
if ( is_admin() ) {
include_once KCO_WC_PLUGIN_PATH . '/includes/class-klarna-checkout-for-woocommerce-admin-notices.php';
include_once KCO_WC_PLUGIN_PATH . '/includes/class-wc-klarna-banners.php';
}
$this->api = new Klarna_Checkout_For_WooCommerce_API();
$this->merchant_urls = new Klarna_Checkout_For_WooCommerce_Merchant_URLs();
$this->order_lines = new Klarna_Checkout_For_WooCommerce_Order_Lines();
$this->credentials = new Klarna_Checkout_For_WooCommerce_Credentials();
$this->logger = new Klarna_Checkout_For_WooCommerce_Logging();
load_plugin_textdomain( 'klarna-checkout-for-woocommerce', false, plugin_basename( __DIR__ ) . '/languages' );
add_filter( 'woocommerce_payment_gateways', array( $this, 'add_gateways' ) );
}
/**
* Add the gateways to WooCommerce
*
* @param array $methods Payment methods.
*
* @return array $methods Payment methods.
* @since 1.0.0
*/
public function add_gateways( $methods ) {
$methods[] = 'Klarna_Checkout_For_WooCommerce_Gateway';
return $methods;
}
/**
* Filters cart item quantity output.
*
* @param string $output HTML output.
* @param array $cart_item Cart item.
* @param string $cart_item_key Cart item key.
*
* @return string $output
*/
public function add_quantity_field( $output, $cart_item, $cart_item_key ) {
if ( 'kco' === WC()->session->get( 'chosen_payment_method' ) ) {
foreach ( WC()->cart->get_cart() as $cart_key => $cart_value ) {
if ( $cart_key === $cart_item_key ) {
$_product = $cart_item['data'];
if ( $_product->is_sold_individually() ) {
$return_value = sprintf( '1 <input type="hidden" name="cart[%s][qty]" value="1" />', $cart_key );
} else {
$return_value = woocommerce_quantity_input( array(
'input_name' => 'cart[' . $cart_key . '][qty]',
'input_value' => $cart_item['quantity'],
'max_value' => $_product->backorders_allowed() ? '' : $_product->get_stock_quantity(),
'min_value' => '1',
), $_product, false );
}
$output = $return_value;
}
}
}
return $output;
}
}
Klarna_Checkout_For_WooCommerce::get_instance();
}
/**
* Main instance Klarna_Checkout_For_WooCommerce WooCommerce.
*
* Returns the main instance of Klarna_Checkout_For_WooCommerce.
*
* @return Klarna_Checkout_For_WooCommerce
*/
function KCO_WC() {
return Klarna_Checkout_For_WooCommerce::get_instance();
}

View File

@@ -0,0 +1,374 @@
# Copyright (C) 2018 klarna-checkout-for-woocommerce
# This file is distributed under the same license as the klarna-checkout-for-woocommerce package.
msgid ""
msgstr ""
"Project-Id-Version: klarna-checkout-for-woocommerce\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language-Team: Krokedil <info@krokedil.se>\n"
"Last-Translator: Krokedil <info@krokedil.se>\n"
"Report-Msgid-Bugs-To: http://krokedil.se\n"
"X-Poedit-Basepath: ..\n"
"X-Poedit-KeywordsList: __;_e;_ex:1,2c;_n:1,2;_n_noop:1,2;_nx:1,2,4c;_nx_noop:1,2,3c;_x:1,2c;esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c\n"
"X-Poedit-SearchPath-0: .\n"
"X-Poedit-SearchPathExcluded-0: *.js\n"
"X-Poedit-SourceCharset: UTF-8\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../includes/class-klarna-checkout-for-woocommerce-admin-notices.php:77
msgid "You need to specify a terms page in WooCommerce Settings to be able to use Klarna Checkout."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-admin-notices.php:92
msgid "You need to enable and configure https to be able to use Klarna Checkout."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-ajax.php:278
msgid "This order was made as a fallback due to an error in the checkout (%s). Please verify the order with Klarna."
msgstr ""
#. translators: Klarna order ID.
#. translators: Klarna order ID.
#. translators: Klarna order ID.
#: ../includes/class-klarna-checkout-for-woocommerce-api-callbacks.php:97, ../includes/class-klarna-checkout-for-woocommerce-api-callbacks.php:432, ../includes/class-klarna-checkout-for-woocommerce-gateway.php:279
msgid "Payment via Klarna Checkout, order ID: %s"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-api-callbacks.php:100, ../includes/class-klarna-checkout-for-woocommerce-api-callbacks.php:435, ../includes/class-klarna-checkout-for-woocommerce-gateway.php:282
msgid "Klarna Checkout order was rejected."
msgstr ""
#. translators: Klarna order ID.
#. translators: Klarna order ID.
#. translators: Klarna order ID.
#: ../includes/class-klarna-checkout-for-woocommerce-api-callbacks.php:103, ../includes/class-klarna-checkout-for-woocommerce-api-callbacks.php:438, ../includes/class-klarna-checkout-for-woocommerce-gateway.php:285
msgid "Klarna order is under review, order ID: %s."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-api-callbacks.php:451
msgid "Order needs manual review, WooCommerce total and Klarna total do not match. Klarna order total: %s."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-confirmation.php:56
msgid "Please wait while we process your order."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:19
msgid "Enable/Disable"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:20
msgid "Enable Klarna Checkout"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:26
msgid "Title"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:28
msgid "Payment method title."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:33
msgid "Description"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:35
msgid "Payment method description."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:40
msgid "Separate shipping address"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:41
msgid "Allow separate shipping address"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:43
msgid "If this option is checked, customers will be able to enter shipping address different than their billing address in checkout."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:48
msgid "Other payment method button text"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:50
msgid "Customize the <em>Select another payment method</em> button text that is displayed in checkout if using other payment methods than Klarna Checkout. Leave blank to use the default (and translatable) text."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:55
msgid "Shipping details"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:56
msgid "Shipping details note shown to customer"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:58
msgid "Will be shown to customer in thank you page."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:63
msgid "Allowed Customer Types"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:66
msgid "B2C only"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:67
msgid "B2B only"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:68
msgid "B2C & B2B (defaults to B2C)"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:69
msgid "B2B & B2C (defaults to B2B)"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:71
msgid "Select if you want to sell both to consumers and companies or only to one of them (available for SE, NO and FI). Learn more and <a href=\"%s\" target=\"_blank\">sign up for Klarna Checkout B2B here</a>."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:76
msgid "Product URLs"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:78
msgid "Send product and product image URLs to Klarna"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:83
msgid "Logging"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:84
msgid "Log debug messages"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:86
msgid "Save debug messages to the WooCommerce System Status log."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:91
msgid "Test mode"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:92
msgid "Enable Test Mode"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:94
msgid "Place the payment gateway in test mode using test API keys."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:99
msgid "Date of birth mandatory"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:100
msgid "Make customer date of birth mandatory"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:102
msgid "If checked, the customer cannot skip date of birth. "
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:107
msgid "Show terms and conditions"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:108
msgid "Select if you want to show terms and conditions on checkout page, and where you want to display them."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:112
msgid "Do not display"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:113
msgid "Display above checkout"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:114
msgid "Display below checkout"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:123, ../includes/class-klarna-checkout-for-woocommerce-fields.php:170
msgid "Production Username (UID)"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:125, ../includes/class-klarna-checkout-for-woocommerce-fields.php:132, ../includes/class-klarna-checkout-for-woocommerce-fields.php:139, ../includes/class-klarna-checkout-for-woocommerce-fields.php:146
msgid "Get your API keys from your Klarna Checkout merchant account for Europe."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:130, ../includes/class-klarna-checkout-for-woocommerce-fields.php:177
msgid "Production Password"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:137, ../includes/class-klarna-checkout-for-woocommerce-fields.php:184
msgid "Test Username (UID)"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:144, ../includes/class-klarna-checkout-for-woocommerce-fields.php:191
msgid "Test Password"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:151
msgid "Title mandatory (GB)"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:152
msgid "Make customer title mandatory"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:154
msgid "If unchecked, title becomes optional. Only available for orders for country GB."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:159
msgid "Show prefill consent notice"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:160
msgid "Only applicable for stores based in Germany and Austria"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:172, ../includes/class-klarna-checkout-for-woocommerce-fields.php:179, ../includes/class-klarna-checkout-for-woocommerce-fields.php:186, ../includes/class-klarna-checkout-for-woocommerce-fields.php:193
msgid "Get your API keys from your Klarna Checkout merchant account for US."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:199
msgid "Color Settings"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:203
msgid "Checkout button color"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:205
msgid "Checkout page button color"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:210
msgid "Checkout button text color"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:212
msgid "Checkout page button text color"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:217
msgid "Checkout checkbox color"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:219
msgid "Checkout page checkbox color"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:224
msgid "Checkout checkbox checkmark color"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:226
msgid "Checkout page checkbox checkmark color"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:231
msgid "Checkout header color"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:233
msgid "Checkout page header color"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:238
msgid "Checkout link color"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:240
msgid "Checkout page link color"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:245
msgid "Checkout radius border (px)"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-fields.php:247
msgid "Checkout page radius border in pixels"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-gateway.php:19, ../includes/klarna-checkout-for-woocommerce-status-report.php:15
msgid "Klarna Checkout"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-gateway.php:20
msgid "Klarna Checkout replaces standard WooCommerce checkout page."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-gateway.php:324
msgid "Order address should not be changed and any changes you make will not be reflected in Klarna system."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-gateway.php:335
msgid "An account is already registered with your email address. Please log in."
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-gdpr.php:30, ../includes/klarna-checkout-for-woocommerce-functions.php:474
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-order-lines.php:155, ../includes/class-klarna-checkout-for-woocommerce-order-lines.php:156
msgid "Sales Tax"
msgstr ""
#: ../includes/class-klarna-checkout-for-woocommerce-order-lines.php:504, ../includes/class-klarna-checkout-for-woocommerce-order-lines.php:532
msgid "Shipping"
msgstr ""
#: ../includes/klarna-checkout-for-woocommerce-functions.php:58
msgid "Create an account?"
msgstr ""
#: ../includes/klarna-checkout-for-woocommerce-functions.php:113
msgid "Select another payment method"
msgstr ""
#: ../includes/klarna-checkout-for-woocommerce-functions.php:465
msgid "Not all products are in stock."
msgstr ""
#: ../includes/klarna-checkout-for-woocommerce-functions.php:467
msgid "No shipping was selected."
msgstr ""
#: ../includes/klarna-checkout-for-woocommerce-status-report.php:15
msgid "Klarna Checkout System Status."
msgstr ""
#: ../includes/klarna-checkout-for-woocommerce-status-report.php:21
msgid "Orders created via API callback"
msgstr ""
#: ../includes/klarna-checkout-for-woocommerce-status-report.php:22
msgid "Displays the number of orders created via the API callback feature during the last month."
msgstr ""
#: ../klarna-checkout-for-woocommerce.php:170
msgid "Settings"
msgstr ""
#: ../klarna-checkout-for-woocommerce.php:171
msgid "Support"
msgstr ""
#: ../klarna-checkout-for-woocommerce.php:228
msgid "Activate Now"
msgstr ""
#: ../klarna-checkout-for-woocommerce.php:252
msgid "Install Now"
msgstr ""

View File

@@ -0,0 +1,152 @@
=== Klarna Checkout for WooCommerce ===
Contributors: klarna, krokedil, automattic
Tags: woocommerce, klarna, ecommerce, e-commerce, checkout
Donate link: https://klarna.com
Requires at least: 4.0
Tested up to: 4.9.6
Requires PHP: 5.6
WC requires at least: 3.0.0
WC tested up to: 3.4.0
Stable tag: trunk
License: GPLv3 or later
License URI: http://www.gnu.org/licenses/gpl-3.0.html
== DESCRIPTION ==
*A full checkout experience embedded on your site with Pay Now, Pay Later and Slice It. No credit card numbers, no passwords, no worries.*
https://www.youtube.com/watch?v=XayUzOUkyDQ
Our complete checkout is a seamless and mobile optimized solution that delivers a best-in-class user experience that comes with all our payment methods. It also identifies the customer and enables one-click repeat purchases across Klarnas merchant network, resulting in increased average order value, conversions, and loyalty.
This official Klarna extension also makes it easy for you to handle orders in WooCommerce after a purchase is complete. With a single click of a button, you can activate, update, refund and cancel orders directly from WooCommerce without logging into the Klarna administration.
=== Pay Now (direct payments) ===
Customers who want to pay in full at checkout can do it quickly and securely with a credit/debit card. Friction-free direct purchases while maximising the value for your business thanks to guaranteed payments. If they have a Klarna account they can save their details and enjoy one-click purchases from then on.
=== Pay later (invoice) ===
Try it first, pay it later. Delayed payments for customers who like low friction purchases and to pay after delivery.
=== Slice it (installments) ===
Installment, revolving and other flexible financing plans let customers pay when they can and when they want.
=== How to Get Started ===
* [Sign up for Klarna](https://www.klarna.com/international/business/woocommerce/).
* [Install the plugin](https://wordpress.org/plugins/klarna-checkout-for-woocommerce/) on your site. During this process you will be asked to download [Klarna Order Management](https://wordpress.org/plugins/klarna-order-management-for-woocommerce/) so you can handle orders in Klarna directly from WooCommerce.
* Get your store approved by Klarna, and start selling.
=== What's the difference between Klarna Checkout and Klarna Payments? ===
Klarna as your single payment provider keeps everything under one roof. Youll have one agreement, one point of contact, one settlement file, one payout with __Klarna Checkout__. It only takes a single integration to deliver the full Klarna hosted checkout experience through a widget placed on your site.
__Klarna Payments__ removes the headaches of payments, for both consumers and merchants. Complement your checkout with a Klarna hosted widget located in your existing checkout which offers payment options for customers with a smooth user experience.
== Installation ==
1. Upload plugin folder to to the "/wp-content/plugins/" directory.
2. Activate the plugin through the "Plugins" menu in WordPress.
3. Go WooCommerce Settings > Payment Gateways and configure your Klarna Checkout settings.
4. Read more about the configuration process in the [plugin documentation](https://docs.woocommerce.com/document/klarna-checkout/).
== Frequently Asked Questions ==
= Which countries does this payment gateway support? =
Klarna Checkout works for merchants in Sweden, Finland, Norway, Germany, Austria, the Netherlands, UK and United States.
= Where can I find Klarna Checkout for WooCommerce documentation? =
For help setting up and configuring Klarna Payments for WooCommerce please refer to our [documentation](https://docs.woocommerce.com/document/klarna-checkout/).
= Are there any specific requirements? =
* WooCommerce 3.0 or newer is required.
* PHP 5.6 or higher is required.
* A SSL Certificate is required.
* This plugin integrates with Klarnas V3 platform. You need an agreement with Klarna specific to the V3 platform to use this plugin.
== Changelog ==
= 2018.05.29 - version 1.5.2 =
* Fix - Fixed error in get_purchase_locale() (caused checkout to be rendered in English even if local lang was used in store).
= 2018.05.25 - version 1.5.1 =
* Fix - Fixed a check on a definition.
* Fix - Fixed minor spelling error in privacy policy text.
* Fix - Prevent default on customer pressing enter on checkout page to prevent accidental order submit.
= 2018.05.24 - version 1.5.0 =
* Feature - Added support for validation of required WooCommerce checkout fields displayed in kco_wc_show_extra_fields().
* Feature - Added support for wp_add_privacy_policy_content (for GDPR compliance). More info: https://core.trac.wordpress.org/attachment/ticket/43473/PRIVACY-POLICY-CONTENT-HOOK.md.
* Feature - Added setting for displaying privacy policy checkout text (above or below KCO iframe).
* Feature - Possibility to add terms checkbox inside KCO iframe via plugin settings (GDPR compliance for some companies).
* Tweak - Changed what we base purchase locale on. Adds better support for WPML compatibility.
* Tweak - Added support for handling cart with virtual products in validation callback (check if order needs shipping).
* Tweak - Added Klarna icon next to payment method title in regular checkout page.
* Fix - Fixed issue in validation callback logic (where purchase could be finalized without a valid shipping method).
= 2018.04.27 - version 1.4.0 =
* Feature - Added facllback order creation if checkout form submission fails.
* Tweak - Acknowledge Klarna order and set WC order to Processing in thankyou page if possible.
* Tweak - Improved UI in settings page.
* Tweak - Improved logging.
* Tweak - Added error handling in 405 response from Klarna.
* Tweak - Updated Krokedil logger.
* Tweak - Change standard log event type to INFO (previously ERROR).
* Tweak - Function for hiding Klarna banner (displayed when in test mode).
* Tweak - Added PHP version to user agent sent in orders to Klarna.
= 2018.03.29 - version 1.3.0 =
* Update - Adds Krokedil logger class.
* Update - Adds status report on Woocommerce status page.
* Enhancement - Adds verify_national_identification_number alongside with national_identification_number_mandatory setting in order data sent to Klarna.
* Enhancement - Improved order note for orders created via API callback.
* Enhancement - Improved messaging in order note when order totals doesnt match.
* Enhancement - Display admin notice if https isnt enabled.
* Fix - Spelling fix in banner.
= 2018.03.14 - version 1.2.6 =
* Fix - Fixes how product name is fetched for Klarna.
* Update - Adds new mandatory PNO field.
* Update - Adds dashboard banners and Klarna information.
* Update - Adds exception error code to logger, in addition to error message.
* Update - Changes CSS selector from table to generic class for cart widget.
= 2018.02.26 - version 1.2.5 =
* Feature - Allows Klarna Checkout to be overwritten from the theme.
* Fix - Keeps extra checkout fields values on checkout page reload.
* Enhancement - Cleans up template files.
* Enhancement - Adds WC required and tested up to data to main plugin file.
* Enhancement - Allows English locale for non-english countries.
* Dev - Adds Gulp task for .pot file processing.
= 2018.01.31 - version 1.2.4 =
* Fix - Fixes backup order creation process to check for product SKU.
* Enhancement - Adds admin notice if Terms URL is not set in WooCommerce settings.
= 2018.01.29 - version 1.2.3 =
* Fix - Cleans up translation strings.
* Enhancement - Adds woocommerce_enable_order_notes_field to KCO checkout template.
= 2018.01.26 - version 1.2.2 =
* Fix - Removes email check on validation CB.
* Enhancement - Cleans up template files.
= 2018.01.25 - version 1.2.1 =
* Tweak - Saves KCO as payment method for orders with total equals zero.
* Tweak - Checks if email already exists when guest checkout is disabled and forces users to log in before checking out.
* Fix - Fixes empty JSON AJAX response.
* Enhancement - Improves order query to only retrieve IDs.
= 2018.01.22 - version 1.2 =
* Tweak - Switches to using store base country as purchase country in all cases.
* Tweak - Switches from using 'change' to 'shipping_address_change' for storing customer data.
* Fix - Prevents Klarna Checkout order update after iframe has been submitted.
= 2018.01.11 - version 1.1.1 =
* Tweak - Makes datepicker extra field work in checkout.
* Fix - Acknowledge order & set merchant reference in Klarnas system during backup order creation (on push notification).
* Fix - Fixes storing WC_Customer postal code.
= 2017.12.20 - version 1.1 =
* Tweak - Allows external payment method plugin to work.
* Tweak - Adds border-box to floated elements in KCO page.
* Fix - Adds 3-letter to 2-letter country code translation.
= 1.0 =
* Initial release.

View File

@@ -0,0 +1,15 @@
<?php
/**
* Klarna Checkout fallback order received page, used when WC checkout form submission fails.
*
* Overrides /checkout/thankyou.php.
*
* @package klarna-checkout-for-woocommerce
*/
if ( ! WC()->session->get( 'kco_wc_order_id' ) ) {
return;
}
wc_empty_cart();
kco_wc_show_snippet();

View File

@@ -0,0 +1,30 @@
<?php
/**
* Klarna Checkout page
*
* Overrides /checkout/form-checkout.php.
*
* @package klarna-checkout-for-woocommerce
*/
wc_print_notices();
do_action( 'kco_wc_before_checkout_form' );
?>
<form name="checkout" class="checkout woocommerce-checkout">
<div id="kco-wrapper">
<div id="kco-order-review">
<?php do_action( 'kco_wc_before_order_review' ); ?>
<?php woocommerce_order_review(); ?>
<?php do_action( 'kco_wc_after_order_review' ); ?>
</div>
<div id="kco-iframe">
<?php do_action( 'kco_wc_before_snippet' ); ?>
<?php kco_wc_show_snippet(); ?>
<?php do_action( 'kco_wc_after_snippet' ); ?>
</div>
</div>
</form>
<?php do_action( 'kco_wc_after_checkout_form' ); ?>

View File

@@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit03353625f04afb51760eeb064c3f6a02::getLoader();

View File

@@ -0,0 +1,445 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath.'\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

View File

@@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
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:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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.

View File

@@ -0,0 +1,9 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@@ -0,0 +1,10 @@
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'cad43f73916476d83565191d54c1f14b' => $vendorDir . '/krokedil/krokedil-logger/src/krokedil-order-event-log.php',
);

View File

@@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@@ -0,0 +1,9 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@@ -0,0 +1,70 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit03353625f04afb51760eeb064c3f6a02
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit03353625f04afb51760eeb064c3f6a02', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit03353625f04afb51760eeb064c3f6a02', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit03353625f04afb51760eeb064c3f6a02::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit03353625f04afb51760eeb064c3f6a02::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire03353625f04afb51760eeb064c3f6a02($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequire03353625f04afb51760eeb064c3f6a02($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}

View File

@@ -0,0 +1,19 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit03353625f04afb51760eeb064c3f6a02
{
public static $files = array (
'cad43f73916476d83565191d54c1f14b' => __DIR__ . '/..' . '/krokedil/krokedil-logger/src/krokedil-order-event-log.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
}, null, ClassLoader::class);
}
}

View File

@@ -0,0 +1,48 @@
[
{
"name": "krokedil/krokedil-logger",
"version": "1.0.6",
"version_normalized": "1.0.6.0",
"source": {
"type": "git",
"url": "https://github.com/krokedil/krokedil-logger.git",
"reference": "68fca237f3d53720d223373827531488d5ef7b21"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/krokedil/krokedil-logger/zipball/68fca237f3d53720d223373827531488d5ef7b21",
"reference": "68fca237f3d53720d223373827531488d5ef7b21",
"shasum": ""
},
"require": {
"php": ">=5.6.0"
},
"time": "2018-03-29T13:59:58+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"files": [
"src/krokedil-order-event-log.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Krokedil",
"email": "info@krokedil.se",
"homepage": "https://www.krokedil.se",
"role": "Developer"
}
],
"description": "Logging for WooCommerce Gateways",
"homepage": "https://www.krokedil.se",
"keywords": [
"log",
"logging",
"woocommerce"
]
}
]

View File

@@ -0,0 +1,52 @@
# krokedil-logger
## Installation
Install with [Composer](getcomposer.org).
Add these lines to your composer.json:
```
{
"require": {
"krokedil/krokedil-logger": "^1.0"
}
}
```
## Usage
### Log event
Use the function **krokedil_log_events**.
```
Example: krokedil_log_events( $order_id, $title, $data );
```
$order_id = The WooCommerce order id. Can be sent as null if you want to log events before an order exists.
$title = The title that you wish to have for the event.
$data = An **array** of the data that you want to log.
### Set the version used for order
Use the function **krokedil_set_order_gateway_version**.
```
Example: krokedil_set_order_gateway_version( $order_id, $version );
```
$order_id = The WooCommerce order id.
$version = The version that you want to log for the order.
Use this function at a point where an order exists, for example thank you page or process_order.
### Set display on/off
To switch between showing and not showing the logs on the order add a define for **KROKEDIL_LOGGER_ON** to turn it on.
```
Example: define( 'KROKEDIL_LOGGER_ON', true );
```
### Set gateway filter
You need to set what gateway the meta box should be allowed for. Do this using the define **KROKEDIL_LOGGER_GATEWAY**.
```
Example: define( 'KROKEDIL_LOGGER_GATEWAY', '$string' );
```
$string = A string or substring of the gateway id.
### Recognition
This plugin uses the renderjson JavaScript created by GitHub user [Caldwell](https://github.com/caldwell/). It can be found here: [RenderJSON](https://github.com/caldwell/renderjson).

View File

@@ -0,0 +1,22 @@
{
"name": "krokedil/krokedil-logger",
"type": "library",
"description": "Logging for WooCommerce Gateways",
"keywords": ["log","logging", "woocommerce"],
"homepage": "https://www.krokedil.se",
"license": "MIT",
"authors": [
{
"name": "Krokedil",
"email": "info@krokedil.se",
"homepage": "https://www.krokedil.se",
"role": "Developer"
}
],
"require": {
"php": ">=5.6.0"
},
"autoload": {
"files": ["src/krokedil-order-event-log.php"]
}
}

View File

@@ -0,0 +1,23 @@
.krokedil_event {
margin:5px;
background-color:#f9f9f9;
overflow-x:auto;
}
.krokedil_event_header * {
display:inline-block;
margin:5px;
}
.krokedil_event_header {
padding:5px;
background-color:#f1f1f1;
}
.krokedil_event_header h5 {
float:right;
}
.krokedil_hidden {
display:none;
}
.krokedil_shown{
clear:both;
display:block;
}

View File

@@ -0,0 +1,34 @@
jQuery( function( $ ) {
var krokedil_event_log = {
renderJson: function() {
$(".krokedil_json").each(function(){
var string = $( this ).html();
var json = JSON.parse( string );
renderjson;
$( this ).html( renderjson.set_show_to_level( '2' )( json ) )
});
},
toggleJson: function( event_nr ){
console.log( 'in function');
var event_id = '#krokedil_event_nr_' + event_nr;
console.log( event_id );
if( $( event_id ).hasClass( 'krokedil_hidden' ) ) {
$( event_id ).removeClass( 'krokedil_hidden' );
$( event_id ).addClass( 'krokedil_shown' );
} else {
$( event_id ).removeClass( 'krokedil_shown' );
$( event_id ).addClass( 'krokedil_hidden' );
}
}
}
$( document ).ready(function() {
krokedil_event_log.renderJson();
});
$('body').on('click', '.krokedil_timestamp', function() {
console.log( 'click' );
var event_nr = $(this).data('event-nr');
console.log(event_nr)
console.log($(this));
krokedil_event_log.toggleJson( event_nr );
});
});

View File

@@ -0,0 +1,208 @@
// Copyright © 2013-2017 David Caldwell <david@porkrind.org>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// Usage
// -----
// The module exports one entry point, the `renderjson()` function. It takes in
// the JSON you want to render as a single argument and returns an HTML
// element.
//
// Options
// -------
// renderjson.set_icons("+", "-")
// This Allows you to override the disclosure icons.
//
// renderjson.set_show_to_level(level)
// Pass the number of levels to expand when rendering. The default is 0, which
// starts with everything collapsed. As a special case, if level is the string
// "all" then it will start with everything expanded.
//
// renderjson.set_max_string_length(length)
// Strings will be truncated and made expandable if they are longer than
// `length`. As a special case, if `length` is the string "none" then
// there will be no truncation. The default is "none".
//
// renderjson.set_sort_objects(sort_bool)
// Sort objects by key (default: false)
//
// renderjson.set_replacer(replacer_function)
// Equivalent of JSON.stringify() `replacer` argument when it's a function
//
// renderjson.set_property_list(property_list)
// Equivalent of JSON.stringify() `replacer` argument when it's an array
//
// Theming
// -------
// The HTML output uses a number of classes so that you can theme it the way
// you'd like:
// .disclosure ("⊕", "⊖")
// .syntax (",", ":", "{", "}", "[", "]")
// .string (includes quotes)
// .number
// .boolean
// .key (object key)
// .keyword ("null", "undefined")
// .object.syntax ("{", "}")
// .array.syntax ("[", "]")
var module, window, define, renderjson=(function() {
var themetext = function(/* [class, text]+ */) {
var spans = [];
while (arguments.length)
spans.push(append(span(Array.prototype.shift.call(arguments)),
text(Array.prototype.shift.call(arguments))));
return spans;
};
var append = function(/* el, ... */) {
var el = Array.prototype.shift.call(arguments);
for (var a=0; a<arguments.length; a++)
if (arguments[a].constructor == Array)
append.apply(this, [el].concat(arguments[a]));
else
el.appendChild(arguments[a]);
return el;
};
var prepend = function(el, child) {
el.insertBefore(child, el.firstChild);
return el;
}
var isempty = function(obj, pl) { var keys = pl || Object.keys(obj);
for (var i in keys) if (Object.hasOwnProperty.call(obj, keys[i])) return false;
return true; }
var text = function(txt) { return document.createTextNode(txt) };
var div = function() { return document.createElement("div") };
var span = function(classname) { var s = document.createElement("span");
if (classname) s.className = classname;
return s; };
var A = function A(txt, classname, callback) { var a = document.createElement("a");
if (classname) a.className = classname;
a.appendChild(text(txt));
a.href = '#';
a.onclick = function(e) { callback(); if (e) e.stopPropagation(); return false; };
return a; };
function _renderjson(json, indent, dont_indent, show_level, options) {
var my_indent = dont_indent ? "" : indent;
var disclosure = function(open, placeholder, close, type, builder) {
var content;
var empty = span(type);
var show = function() { if (!content) append(empty.parentNode,
content = prepend(builder(),
A(options.hide, "disclosure",
function() { content.style.display="none";
empty.style.display="inline"; } )));
content.style.display="inline";
empty.style.display="none"; };
append(empty,
A(options.show, "disclosure", show),
themetext(type+ " syntax", open),
A(placeholder, null, show),
themetext(type+ " syntax", close));
var el = append(span(), text(my_indent.slice(0,-1)), empty);
if (show_level > 0 && type != "string")
show();
return el;
};
if (json === null) return themetext(null, my_indent, "keyword", "null");
if (json === void 0) return themetext(null, my_indent, "keyword", "undefined");
if (typeof(json) == "string" && json.length > options.max_string_length)
return disclosure('"', json.substr(0,options.max_string_length)+" ...", '"', "string", function () {
return append(span("string"), themetext(null, my_indent, "string", JSON.stringify(json)));
});
if (typeof(json) != "object" || [Number, String, Boolean, Date].indexOf(json.constructor) >= 0) // Strings, numbers and bools
return themetext(null, my_indent, typeof(json), JSON.stringify(json));
if (json.constructor == Array) {
if (json.length == 0) return themetext(null, my_indent, "array syntax", "[]");
return disclosure("[", " ... ", "]", "array", function () {
var as = append(span("array"), themetext("array syntax", "[", null, "\n"));
for (var i=0; i<json.length; i++)
append(as,
_renderjson(options.replacer.call(json, i, json[i]), indent+" ", false, show_level-1, options),
i != json.length-1 ? themetext("syntax", ",") : [],
text("\n"));
append(as, themetext(null, indent, "array syntax", "]"));
return as;
});
}
// object
if (isempty(json, options.property_list))
return themetext(null, my_indent, "object syntax", "{}");
return disclosure("{", "...", "}", "object", function () {
var os = append(span("object"), themetext("object syntax", "{", null, "\n"));
for (var k in json) var last = k;
var keys = options.property_list || Object.keys(json);
if (options.sort_objects)
keys = keys.sort();
for (var i in keys) {
var k = keys[i];
if (!(k in json)) continue;
append(os, themetext(null, indent+" ", "key", '"'+k+'"', "object syntax", ': '),
_renderjson(options.replacer.call(json, k, json[k]), indent+" ", true, show_level-1, options),
k != last ? themetext("syntax", ",") : [],
text("\n"));
}
append(os, themetext(null, indent, "object syntax", "}"));
return os;
});
}
var renderjson = function renderjson(json)
{
var options = Object.assign({}, renderjson.options);
options.replacer = typeof(options.replacer) == "function" ? options.replacer : function(k,v) { return v; };
var pre = append(document.createElement("pre"), _renderjson(json, "", false, options.show_to_level, options));
pre.className = "renderjson";
return pre;
}
renderjson.set_icons = function(show, hide) { renderjson.options.show = show;
renderjson.options.hide = hide;
return renderjson; };
renderjson.set_show_to_level = function(level) { renderjson.options.show_to_level = typeof level == "string" &&
level.toLowerCase() === "all" ? Number.MAX_VALUE
: level;
return renderjson; };
renderjson.set_max_string_length = function(length) { renderjson.options.max_string_length = typeof length == "string" &&
length.toLowerCase() === "none" ? Number.MAX_VALUE
: length;
return renderjson; };
renderjson.set_sort_objects = function(sort_bool) { renderjson.options.sort_objects = sort_bool;
return renderjson; };
renderjson.set_replacer = function(replacer) { renderjson.options.replacer = replacer;
return renderjson; };
renderjson.set_property_list = function(prop_list) { renderjson.options.property_list = prop_list;
return renderjson; };
// Backwards compatiblity. Use set_show_to_level() for new code.
renderjson.set_show_by_default = function(show) { renderjson.options.show_to_level = show ? Number.MAX_VALUE : 0;
return renderjson; };
renderjson.options = {};
renderjson.set_icons('⊕', '⊖');
renderjson.set_show_by_default(false);
renderjson.set_sort_objects(false);
renderjson.set_max_string_length("none");
renderjson.set_replacer(void 0);
renderjson.set_property_list(void 0);
return renderjson;
})();
if (define) define({renderjson:renderjson})
else (module||{}).exports = (window||{}).renderjson = renderjson;

View File

@@ -0,0 +1,155 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
if( ! function_exists( 'krokedil_log_events' ) ) {
define( 'KROKEDIL_LOGGER_VERSION', '1.0.4' );
add_action( 'admin_enqueue_scripts', 'krokedi_load_admin_scripts' );
if ( defined( 'KROKEDIL_LOGGER_ON' ) ) {
add_action( 'add_meta_boxes', 'krokedil_meta_box' );
}
add_action( 'woocommerce_new_order', 'krokedil_add_sessions_to_events', 10, 1 );
function krokedi_load_admin_scripts() {
wp_register_script(
'krokedil_event_log',
plugins_url( 'assets/js/krokedil-event-log.js', __FILE__ ),
array( 'jquery' ),
KROKEDIL_LOGGER_VERSION
);
$params = array(
'ajaxurl' => admin_url( 'admin-ajax.php' ),
);
wp_localize_script( 'krokedil_event_log', 'krokedil_event_log_params', $params );
wp_enqueue_script( 'krokedil_event_log' );
wp_register_script(
'render_json',
plugins_url( 'assets/js/renderjson.js', __FILE__ ),
array( 'jquery' ),
KROKEDIL_LOGGER_VERSION
);
$params = array();
wp_localize_script( 'render_json', 'render_json_params', $params );
wp_enqueue_script( 'render_json' );
wp_register_style(
'krokedil_events_style',
plugin_dir_url( __FILE__ ) . 'assets/css/krokedil-event-log.css',
array(),
KROKEDIL_LOGGER_VERSION
);
wp_enqueue_style( 'krokedil_events_style' );
}
function krokedil_log_events( $order_id, $title, $data ) {
if ( WC()->session ) {
if ( null === $order_id ) {
if ( WC()->session->get( '_krokedil_events_session' ) ) {
$events = WC()->session->get( '_krokedil_events_session' );
} else {
$events = array();
}
$event = array(
'title' => $title,
'data' => $data,
'timestamp' => current_time( 'Y-m-d H:i:s' )
);
$events[] = $event;
WC()->session->set( '_krokedil_events_session', $events );
} else {
if ( get_post_meta( $order_id, '_krokedil_order_events' ) ) {
$events = get_post_meta( $order_id, '_krokedil_order_events', true );
} else {
$events = array();
}
$event = array(
'title' => $title,
'data' => $data,
'timestamp' => current_time( 'Y-m-d H:i:s' )
);
$events[] = $event;
update_post_meta( $order_id, '_krokedil_order_events', $events );
}
}
}
function krokedil_log_response( $order_id, $response ) {
if ( WC()->session ) {
if ( null === $order_id ) {
$events = WC()->session->get( '_krokedil_events_session' );
end( $events );
$event = key( $events );
$events[ $event ]['response'] = $response;
WC()->session->set( '_krokedil_events_session', $events );
} else {
$events = get_post_meta( $order_id, '_krokedil_order_events', true );
end( $events );
$event = key( $events );
$events[ $event ]['response'] = $response;
update_post_meta( $order_id, '_krokedil_order_events', $events );
}
}
}
function krokedil_add_sessions_to_events( $order_id ) {
if ( WC()->session ) {
if ( WC()->session->get( '_krokedil_events_session' ) ) {
$session_events = WC()->session->get( '_krokedil_events_session' );
update_post_meta( $order_id, '_krokedil_order_events', $session_events );
WC()->session->__unset( '_krokedil_events_session' );
}
}
}
function krokedil_get_events() {
return get_post_meta( get_the_ID(), '_krokedil_order_events', true );
}
function krokedil_meta_box( $post_type ) {
if ( 'shop_order' === $post_type ) {
$order_id = get_the_ID();
$order = wc_get_order( $order_id );
if ( false !== strpos( $order->get_payment_method(), KROKEDIL_LOGGER_GATEWAY ) ) {
if ( get_post_meta( $order_id, '_krokedil_order_events' ) ) {
add_meta_box( 'krokedil_order_events', __( 'Events', 'krokedil-for-woocommerce' ), 'krokedil_meta_contents', 'shop_order', 'normal', 'core' );
}
}
}
}
function krokedil_set_order_gateway_version( $order_id, $version ) {
update_post_meta( $order_id, '_krokedil_order_gateway_version', $version );
}
function krokedil_get_order_version() {
$order_id = get_the_ID();
return get_post_meta( $order_id, '_krokedil_order_gateway_version', true );
}
function krokedil_meta_contents() {
$events = krokedil_get_events();
$i = 0;
foreach ( $events as $event ) {
$i += 1;
echo '<div class="krokedil_event">';
echo '<div class="krokedil_event_header">';
echo '<h4>' . $event['title'] . '</h4>';
echo '<h5 class="krokedil_timestamp" data-event-nr="' . $i . '"><a href="#krokedil_event_nr_' . $i . '">Time: ' . $event['timestamp'] . '</a></h5>';
echo '</div>';
echo '<div class="krokedil_json krokedil_hidden" id="krokedil_event_nr_' . $i . '">' . json_encode( $event['data'] ) . '</div>';
echo '</div>';
}
echo '<small>Version of plugin used for order: ' . krokedil_get_order_version() . '</small>';
}
}

View File

@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@@ -0,0 +1,105 @@
# MailChimp for Woocommerce Integration
In this article, youll learn how to connect MailChimp for WooCommerce.
## Before You Start
**Here are some things to know before you begin this process.**
- For the most up-to-date install instructions, read [Connect or Disconnect MailChimp for WooCommerce](http://kb.mailchimp.com/integrations/e-commerce/connect-or-disconnect-mailchimp-for-woocommerce).
- This plugin requires you to have the [WooCommerce plugin](https://wordpress.org/plugins/woocommerce) already installed and activated in WordPress.
- Your host environment must meet [WooCommerce's minimum requirements](https://docs.woocommerce.com/document/server-requirements), including PHP 7.0 or greater.
- We recommend you use this plugin in a staging environment before installing it on production servers.
- MailChimp for WooCommerce syncs the customers first name, last name, email address, and orders.
- WooCommerce customers who haven't signed up for marketing emails will appear in the Transactional portion of your list, and cannot be exported.
## A Note for Current WooCommerce Integration Users
This plugin supports our most powerful API 3.0 features, and is intended for users who have not yet integrated their WooCommerce stores with MailChimp. If your WooCommerce store is already integrated with MailChimp via an integration that runs on an older version of MailChimps API, consider your current sales volume before you make any changes that might disrupt business.
You can run this new integration at the same time as your current WooCommerce integration for MailChimp. However, data from the older integration will display separately in subscriber profiles, and cant be used with e-commerce features that require API 3.0.
## Task Roadmap
**Heres a brief overview of this multi-step process.**
- Install the plugin on your WordPress Admin site.
- Connect the plugin with your MailChimp API Key.
- Configure your list settings to complete the data sync process.
- Troubleshoot any sync or data feed issues by sharing logs with MailChimp support.
## Install the Plugin
**To install the plugin, follow these steps.**
1) Log in to your WordPress admin panel.
2) In the left navigation panel, click **Plugins**, and choose **Add New**.
![Add new](https://cloud.githubusercontent.com/assets/6547700/18677991/a7622bcc-7f28-11e6-8e8c-9bbdfa9861c7.png)
3) Click **Upload Plugin**.
![Upload](https://cloud.githubusercontent.com/assets/6547700/18677997/a76dab82-7f28-11e6-98e4-4309739cd840.png)
4) Click **Choose File** to select the ZIP file for the plugin, then click **Install Now**.
![Install Now](https://cloud.githubusercontent.com/assets/6547700/18677988/a760949c-7f28-11e6-9e13-13c23d044ad4.png)
5) Click **Activate Plugin**.
![Activate plugin](https://cloud.githubusercontent.com/assets/6547700/18677990/a760d7c2-7f28-11e6-8741-12c1efa7a991.png)
After you activate the plugin, youll be taken to the **Settings** page, where you will add your API key and configure your list settings.
## Configure and Sync
**To configure your MailChimp settings for WooCommerce customers and sync them to MailChimp, follow these steps.**
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**. To learn how to generate a MailChimp API Key, read [About API Keys](http://kb.mailchimp.com/integrations/api-integrations/about-api-keys).
![API key](https://cloud.githubusercontent.com/assets/19805049/18877771/3fca90e8-849c-11e6-9e3a-161a7b3936dd.png)
2) Navigate to the **Store Settings** tab.
![Store Settings](https://cloud.githubusercontent.com/assets/6547700/18677998/a76e5640-7f28-11e6-9fd3-d66949fa1413.png)
3) Enter the contact and location details for your WooCommerce Store, and click **Save all changes**.
![Save all changes](https://cloud.githubusercontent.com/assets/6547700/18677996/a76d126c-7f28-11e6-9150-4b289d20f057.png)
4) Navigate to the **List Settings** tab.
![List Settings tab](https://cloud.githubusercontent.com/assets/19805049/18878446/961221d0-849e-11e6-99bb-175c22bf921e.png)
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**.
![Save all changes](https://cloud.githubusercontent.com/assets/19805049/18877772/3fd24162-849c-11e6-8442-79ec4550b8ac.png)
All set! When you click **Save all changes**, well start syncing your WooCommerce customers to MailChimp. To view progress, check the **Sync Status** tab.
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 youre done. Well create a MailChimp list for you, and begin the data sync.
![List Defaults tab](https://cloud.githubusercontent.com/assets/19805049/18956260/cffd3926-8628-11e6-9c68-9fe3c964c75c.png)
## Next Steps
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.
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).
# Deactivate or Delete the Plugin
When you deactivate MailChimp for WooCommerce, it stops the sync but doesnt remove the plugin. You can always re-activate the sync, which will backfill data at a later point in time.
To deactivate MailChimp for WooCommerce, follow these steps.
1) Log in to your WordPress admin panel.
2) In the left navigation panel, click **Plugins**, and choose **Installed Plugins**.
![Installed Plugins](https://cloud.githubusercontent.com/assets/6547700/18677993/a76542ee-7f28-11e6-99dd-cfd6c1f5c24a.png)
3) Click the box next to the MailChimp for WooCommerce plugin, and click **Deactivate**.
![Deactivate](https://cloud.githubusercontent.com/assets/6547700/18677992/a762b844-7f28-11e6-9679-8d6c6a1d731d.png)
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.

View File

@@ -0,0 +1,232 @@
=== MailChimp for WooCommerce ===
Contributors: ryanhungate, MailChimp
Tags: ecommerce,email,workflows,mailchimp
Donate link: https://mailchimp.com
Requires at least: 4.3
Tested up to: 4.9.5
Stable tag: 2.1.7
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Connect your store to your MailChimp list to track sales, create targeted emails, send abandoned cart emails, and more.
== Description ==
Join the 17 million customers who use MailChimp, the world's largest marketing automation platform, to develop their e-commerce marketing strategy. With the official MailChimp for WooCommerce integration, your customers and their purchase data are automatically synced with your MailChimp account, making it easy to send targeted campaigns, automatically follow up with customers post-purchase, recommend products, recover abandoned carts, and measure the ROI of your marketing efforts. And it's completely free.
With MailChimp for WooCommerce, youll have the power to:
- Sync list and purchase data.
- Set up marketing automations to remind customers about items they left in their cart or viewed on your site, win back lapsed customers, and follow up post-purchase. (Now available for free accounts!)
- Showcase product recommendations.
- Track and segment customers based on purchase history and purchase frequency.
- View detailed data on your marketing performance in your MailChimp Dashboard.
- Find new customers, connect with current ones, and drive them all to your website with [Facebook](https://mailchimp.com/features/facebook-ads/) and [Instagram](https://mailchimp.com/features/instagram-ads/) ads. Then, set up [Google remarketing](https://mailchimp.com/features/google-remarketing-ads/) ads to turn your site visitors into shoppers.
- Automatically embed a pop-up form that converts your website visitors to subscribers.
- Add discount codes created in WooCommerce to your emails and automations with a Promo Code content block
- Create beautiful landing pages that make it easy to highlight your products, promote a sale or giveaway, and grow your list.
###Important Notes
This plugin supports our most powerful API 3.0 features, and is intended for users who have not yet integrated their WooCommerce stores with MailChimp.
You can run this new integration at the same time as your current WooCommerce integration for MailChimp. However, data from the older integration will display separately in subscriber profiles, and cant be used with e-commerce features that require API 3.0.
WordPress.com compatibility is limited to Business tier users only.
=== Installation ===
###Before You Start
Here are some things to know before you begin this process.
- This plugin requires you to have the [WooCommerce plugin](https://woocommerce.com/) already installed and activated in WordPress.
- Your hosting environment must meet [WooCommerce's minimum requirements](https://docs.woocommerce.com/document/server-requirements), including PHP 7.0 or greater.
- We recommend you use this plugin in a staging environment before installing it on production servers. To learn more about staging environments, [check out these related Wordpress plugins](https://wordpress.org/plugins/search.php?q=staging).
- MailChimp for WooCommerce syncs the customers first name, last name, email address, and orders.
- WooCommerce customers who haven't signed up for marketing emails will appear in the **Transactional** portion of your list, and cannot be exported.
###Task Roadmap
Youll need to do a few things to connect your WooCommerce store to MailChimp.
- Download the plugin.
- Install the plugin on your WordPress Admin site.
- Connect the plugin with your MailChimp API Key.
- Configure your list settings to complete the data sync process.
###Advanced Queue Setup In CLI mode
To optimize the performance of your MailChimp integration - it is recommended that you run the queue in CLI mode.
First define a constant in your config file
`define('DISABLE_WP_HTTP_WORKER', true);`
You have 2 options to run this process:
1. On a cron schedule every minute:
`* * * * * /usr/bin/wp --url=http://yourdomain.com --path=/full/path/to/install/ queue listen`
2. Using a process manager like Monit or Supervisord:
`/usr/bin/wp --url=http://yourdomain.com --path=/full/path/to/install/ queue listen`
== Changelog ==
= 2.1.7 =
* fix Queue_Command filepath in autoloader
= 2.1.6 =
* moved to an autoloader for performance enhancement
* flush database tables on un-installation to assist with stale records in the queue
* turn on standard debugging by default to help troubleshoot issues
* moved the plugin button to the left main navigation
* allow store owners to select the image size being used for products
* fix paypal new order bug where it did not send on initial placement
* add additional configuration success checks for the plugin being configured before pushing any jobs into the queue
* fix the multisite network activation issue
* hide the opt in checkbox for already logged in customers that were previously subscribed
* miscellaneous UI enhancements
= 2.1.5 =
* is_configured filters applied before certain jobs were firing and failing.
= 2.1.5 =
* added support for Polish (zloty - zł) and Moldovan Leu currencies
* update currency code for Belarusian Rouble
* queue performance enhancement
= 2.1.4 =
* updated wordpress compatibility
* updated sync details tab to show more informative stats
* queue job processing performance enhancement
* added an integrity check for queued jobs that were not getting processed
= 2.1.3 =
* Fix subscriber status for repeat transactional customers to stay transactional.
* Remove shipping and billing address requirements for order submission.
* Do not unsubscribe someone who has previously subscribed when unchecking the newsletter sign up box.
* Update newsletter checkbox style to be consistent with WooCommerce styles.
* Make sure WooCommerce plugin is running before running any plugin code.
* Fix compatibility issue with WP-Cron
= 2.1.2 =
* Fix store deletion on plugin deactivation
* Correct shipping name is now used on order notifications.
* Admin orders are now handled appropriately.
* Skip incomplete or cancelled orders from being submitted when new.
* fix hidden or inactive products from being recommended.
= 2.1.1 =
* To address performance issues previously reported, we've changed the action hook of "woocommerce_cart_updated" to use a filter "woocommerce_update_cart_action_cart_updated"
= 2.1.0 =
* Added Promo Code support.
= 2.0.2 =
* Added new logs feature to help troubleshoot isolated sync and data feed issues.
* Fixed bug with setting customers as Transactional during checkout if they had already opted in previously.
* Fixed bug where abandoned cart automation still fired after a customer completed an order.
= 2.0.1 =
* Added support for "Connected Site" scripts.
* Made physical address a required field for store setup.
* Fixed order, cart timestamps to begin using UTC.
= 2.0 =
* Support WooComerce 3.0
* Support for manually uploaded WooCommerce
* Fix for sync issues
* Fix for guest orders sync issue
* Remove MailChimp debug logger
= 1.1.1 =
* Support for site url changes
* Fix for WP Version 4.4 compatibility issues
= 1.1.0 =
* Fix for persisting opt-in status
* Pass order URLs to MailChimp
* Pass partial refund status to MailChimp
= 1.0.9 =
* billing and shipping address support for orders
= 1.0.8 =
* add landing_site, financial status and discount information for orders
* fix to support php 5.3
= 1.0.7 =
* add options to move, hide and change defaults for opt-in checkbox
* add ability to re-sync and display connection details
* support for subscriptions without orders
* additional small fixes and some internal logging removal
= 1.0.6 =
* fixed conflict with the plugin updater where the class could not be loaded correctly.
* fixed error validation for store name.
* fixed cross device abandoned cart url's
= 1.0.4 =
* fix for Abandoned Carts without cookies
= 1.0.3 =
* fixed cart posts on dollar amounts greater than 1000
= 1.0.2 =
* title correction for Product Variants
* added installation checks for WooCommerce and phone contact info
* support for free orders
= 1.0 =
* added is_synicng flag to prevent sends during backfill
* fix for conflicts with Gravity Forms Pro and installation issues
* skip all Amazon orders
* allow users to set opt-in for pre-existing customers during first sync
* add Plugin Updater
= 0.1.22 =
* flag quantity as 1 if the product does not manage inventory
= 0.1.21 =
* php version check to display warnings < 5.5
= 0.1.19 =
* fix campaign tracking on new orders
= 0.1.18 =
* check woocommerce dependency before activating the plugin
= 0.1.17 =
* fix php version syntax errors for array's
= 0.1.16 =
* fix namespace conflicts
* fix free order 0.00 issue
* fix product variant naming issue
= 0.1.15 =
* adding special MailChimp header to requests
= 0.1.14 =
* removing jquery dependencies
= 0.1.13 =
* fixing a number format issue on total_spent
= 0.1.12 =
* skipping orders placed through amazon due to seller agreements
= 0.1.11 =
* removed an extra debug log that was not needed
= 0.1.10 =
* altered debug logging and fixed store settings validation requirements
= 0.1.9 =
* using fallback to stream context during failed patch requests
= 0.1.8 =
* fixing http request header for larger patch requests
= 0.1.7 =
* fixing various bugs with the sync and product issues.
= 0.1.2 =
* fixed admin order update hook.

View File

@@ -0,0 +1,889 @@
<?php
/**
* The admin-specific functionality of the plugin.
*
* @link https://mailchimp.com
* @since 1.0.1
*
* @package MailChimp_WooCommerce
* @subpackage MailChimp_WooCommerce/admin
*/
/**
* The admin-specific functionality of the plugin.
*
* Defines the plugin name, version, and two examples hooks for how to
* enqueue the admin-specific stylesheet and JavaScript.
*
* @package MailChimp_WooCommerce
* @subpackage MailChimp_WooCommerce/admin
* @author Ryan Hungate <ryan@vextras.com>
*/
class MailChimp_WooCommerce_Admin extends MailChimp_WooCommerce_Options {
/**
* @return MailChimp_WooCommerce_Admin
*/
public static function connect()
{
$env = mailchimp_environment_variables();
return new self('mailchimp-woocommerce', $env->version);
}
/**
* Initialize the class and set its properties.
*
* @since 1.0.0
* @param string $plugin_name The name of this plugin.
* @param string $version The version of this plugin.
*/
public function __construct( $plugin_name, $version ) {
$this->plugin_name = $plugin_name;
$this->version = $version;
}
/**
* Register the stylesheets for the admin area.
*
* @since 1.0.0
*/
public function enqueue_styles() {
wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/mailchimp-woocommerce-admin.css', array(), $this->version, 'all' );
}
/**
* Register the JavaScript for the admin area.
*
* @since 1.0.0
*/
public function enqueue_scripts() {
wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/mailchimp-woocommerce-admin.js', array( 'jquery' ), $this->version, false );
}
/**
* Register the administration menu for this plugin into the WordPress Dashboard menu.
*
* @since 1.0.0
*/
public function add_plugin_admin_menu() {
add_menu_page(
'MailChimp - WooCommerce Setup',
'MailChimp',
'manage_options',
$this->plugin_name,
array($this, 'display_plugin_setup_page'),
'data:image/svg+xml;base64,'.$this->mailchimp_svg()
);
}
/**
* @return string
*/
protected function mailchimp_svg()
{
return base64_encode('<?xml version="1.0" encoding="UTF-8"?>
<svg width="111px" height="116px" viewBox="0 0 111 116" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 50 (54983) - http://www.bohemiancoding.com/sketch -->
<title>Group</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Group">
<path d="M76.5859,0.7017 L76.5849,0.7017 C71.6089,0.7017 65.7009,2.4987 59.7709,5.7927 C58.6459,4.8877 57.3339,3.8317 57.2839,3.7927 C55.0919,2.0667 52.4549,1.1907 49.4489,1.1897 C39.2019,1.1897 27.9399,11.1957 22.1389,17.1607 C12.9849,26.5727 5.6129,37.8427 2.4179,47.3087 C-1.5161,58.9627 1.8869,64.6287 5.4329,67.3307 L8.1899,69.4387 C6.2259,78.2517 11.1159,87.7027 19.6639,90.8437 C21.2849,91.4397 22.9629,91.7857 24.6639,91.8777 C28.2049,97.4237 41.6679,115.5347 66.4699,115.5417 C80.1529,115.5417 91.7799,109.9377 100.1089,99.3337 C105.7329,92.1747 108.1719,84.8867 108.8149,82.7097 C109.6379,81.3307 111.6759,77.2977 110.0739,73.4267 L110.0479,73.3637 L110.0189,73.3007 C109.1609,71.4697 107.6859,70.0227 105.8399,69.1577 C105.6629,68.5617 105.4829,68.0447 105.3149,67.6077 C106.7299,65.4787 107.0949,62.8477 106.2809,60.4207 C105.4089,57.8187 103.3169,55.7857 100.6029,54.8587 C98.0479,52.5407 95.7049,51.3697 93.9629,50.4987 L93.6009,50.3177 C93.1359,50.0857 92.8879,49.9617 92.6859,49.8647 C92.6089,49.1767 92.5369,48.3707 92.4619,47.5407 C92.2669,45.3737 92.0449,42.9177 91.6319,40.8417 C90.9369,37.3437 89.4899,34.6077 87.3279,32.6967 C86.8749,31.7957 86.2689,30.7457 85.4899,29.7127 C91.4609,19.6007 92.1439,10.1877 87.1759,4.8097 C84.6939,2.1217 81.0319,0.7017 76.5859,0.7017 M76.5859,3.7017 C80.0809,3.7017 83.0039,4.7137 84.9719,6.8447 C89.3919,11.6297 87.8029,20.7677 81.7689,30.0187 C83.0659,31.2507 84.1419,32.9257 84.9099,34.5887 C86.8279,36.0687 88.0709,38.3197 88.6899,41.4277 C89.3779,44.8837 89.5209,49.8057 89.9519,51.9207 C91.5789,52.6557 90.8869,52.3157 92.6209,53.1827 C94.4309,54.0867 96.6339,55.1877 99.0679,57.5307 C103.3969,58.6147 105.0489,63.2157 102.5169,66.3567 C102.4479,66.4407 102.1429,66.8027 101.8259,67.1627 C101.9009,67.3627 102.7319,68.7067 103.3309,71.4447 C105.1239,71.8687 106.5529,72.9727 107.3019,74.5737 C108.5989,77.7077 106.0479,81.4777 106.0479,81.4777 C105.9629,81.7547 97.9549,112.5417 66.4849,112.5417 L66.4709,112.5417 C39.2589,112.5337 26.3599,88.8807 26.3599,88.8807 C26.0989,88.8947 25.8389,88.9027 25.5799,88.9027 C23.8749,88.9027 22.2309,88.5907 20.6979,88.0277 C12.7149,85.0947 8.8839,75.9607 11.6389,68.2977 L7.2499,64.9447 C-8.0381,53.2977 29.3809,4.1887 49.4489,4.1897 C51.6919,4.1907 53.7189,4.8037 55.4309,6.1517 C55.5169,6.2197 59.4949,9.4217 59.4949,9.4217 C65.6079,5.7087 71.6629,3.7017 76.5849,3.7017 L76.5859,3.7017" id="Fill-40" fill="#FFFFFF"></path>
<path d="M79.1367,27.5845 L79.1377,27.5835 C79.1297,27.5625 79.0647,27.3825 79.0387,27.3335 C78.0087,25.1455 75.6057,23.6175 73.4647,23.0045 C73.8397,23.3955 74.3827,24.1095 74.6037,24.5345 C72.9877,23.4245 70.8397,22.4465 68.5987,21.9825 C68.5987,21.9825 68.8657,22.1745 68.9117,22.2165 C69.3587,22.6325 69.9527,23.2905 70.1967,23.8555 C68.0547,22.9955 65.3467,22.5135 63.0297,22.9595 C62.9957,22.9655 62.7397,23.0265 62.7397,23.0265 C62.7397,23.0265 63.0407,23.1015 63.1087,23.1215 C63.8767,23.3495 64.9737,23.8175 65.4567,24.4595 C61.6097,23.7825 57.3767,24.5315 55.0947,25.8085 C55.3887,25.7955 55.3857,25.7945 55.6337,25.8015 C56.4727,25.8235 58.1647,25.9375 58.8747,26.3895 C56.4487,26.8825 52.9367,27.9685 51.0457,29.5945 C51.3787,29.5555 53.2737,29.3145 54.0487,29.4385 C43.6427,35.3935 38.9067,44.3965 38.9067,44.3965 C41.9747,40.3985 45.9547,36.5505 51.3827,33.0325 C63.2387,25.3485 75.0797,24.4655 79.1367,27.5845" id="Fill-42" fill="#FFFFFE"></path>
<path d="M27.9473,80.7656 C26.3263,80.8026 25.4413,79.7896 25.6003,79.5686 C25.6743,79.4626 25.9213,79.5096 26.3013,79.5656 C28.3663,79.8866 29.6193,78.6266 29.9223,77.3066 C29.9263,77.2856 30.0083,76.9336 30.0043,76.6886 C30.0203,76.4766 29.9973,76.2606 29.9653,76.0676 C29.7203,74.7016 28.1543,74.4446 27.1473,73.3576 C26.2433,72.3756 26.4223,71.1166 26.9893,70.5126 C27.6723,69.8396 28.6483,70.0846 28.6353,70.3236 C28.6343,70.4496 28.3993,70.5446 28.1073,70.7446 C27.7283,71.0116 27.6783,71.2696 27.7743,71.7126 C27.8393,71.9556 27.9473,72.1156 28.1813,72.2996 C29.0283,72.9756 31.3043,73.4466 31.7183,75.7366 C32.1753,78.3656 30.2603,80.7166 27.9473,80.7656 M24.3623,68.1646 C23.9123,68.2606 24.1643,68.2056 23.7193,68.3306 C23.6503,68.3506 23.5873,68.3696 23.5323,68.3926 C23.3923,68.4586 23.2633,68.5036 23.1393,68.5696 C23.0363,68.6266 22.1513,69.0216 21.4313,69.9016 C20.4633,71.1046 20.1113,72.6796 20.1703,74.1956 C20.2283,75.6676 20.6603,76.4826 20.7403,76.6816 C21.0743,77.4006 20.2913,77.5476 19.5763,76.7756 L19.5743,76.7736 C19.0033,76.1676 18.6353,75.2456 18.4573,74.4276 L18.4573,74.4266 L18.4573,74.4276 C17.7313,71.0386 19.2443,67.6326 22.7353,66.2716 C22.9303,66.1946 23.1583,66.1466 23.3423,66.0926 L23.3413,66.0936 C23.6973,65.9876 24.9743,65.7286 26.2723,65.9306 C27.6973,66.1516 28.9563,66.8666 29.7543,67.7936 L29.7563,67.7956 C30.3703,68.4946 30.8333,69.4786 30.7503,70.3546 L30.7503,70.3566 C30.7193,70.7206 30.5573,71.2436 30.2293,71.3786 C30.1073,71.4286 29.9853,71.4026 29.8993,71.3186 C29.6623,71.0856 29.8453,70.6076 29.2663,69.7876 C28.4993,68.6996 26.7633,67.6566 24.3623,68.1646 M34.8423,74.1896 C34.9993,71.6386 34.5973,69.1156 33.8133,66.7166 C33.8133,66.7166 33.4683,68.1466 32.7823,69.8896 C32.1243,64.3126 29.7833,63.1316 29.7833,63.1316 C28.4263,62.5036 26.9273,62.1546 25.3503,62.1546 C19.2683,62.1546 14.3393,67.3446 14.3393,73.7476 C14.3393,80.1516 19.2683,85.3416 25.3503,85.3416 C32.2013,85.3416 37.4013,78.8196 36.1913,71.6936 C36.1913,71.6936 35.4923,73.3246 34.8423,74.1896" id="Fill-44" fill="#FFFFFF"></path>
<path d="M55.2637,15.4038 C54.6187,15.8608 54.0497,16.2798 53.5687,16.6428 L50.2657,13.8778 L55.2637,15.4038 Z M57.7737,13.7038 C57.2307,14.0548 56.7147,14.3998 56.2307,14.7308 L55.3507,11.6728 L57.7737,13.7038 Z M18.5667,60.1648 L18.3907,60.1708 C22.0977,47.2248 32.0597,33.9258 42.2127,25.2628 C44.7447,23.1018 49.8377,19.6738 49.8377,19.6738 L44.0837,14.6028 L43.6637,11.6738 L51.5817,18.2108 C51.5807,18.2118 51.5797,18.2128 51.5797,18.2128 C51.2217,18.5058 50.8627,18.8068 50.5027,19.1138 C49.7227,19.7818 48.9407,20.4808 48.1597,21.2078 C47.0237,22.2638 45.8907,23.3788 44.7697,24.5368 C42.3547,27.0338 39.9957,29.7368 37.7857,32.5168 C33.8777,37.4318 32.2517,39.7248 29.7837,44.3968 C29.7727,44.4108 43.2807,28.2128 46.6437,24.8758 C52.8167,18.7528 62.9197,11.9348 63.0257,11.8738 C67.3387,9.2338 70.9807,7.6838 73.9687,7.0118 C69.0587,7.5128 63.8067,10.0368 59.6847,12.5108 C59.6777,12.5048 53.6927,7.4468 53.6847,7.4408 L51.4907,8.2808 L52.6747,12.5858 L51.2097,12.1118 L50.7927,10.4258 L51.4907,8.2808 C43.5097,6.3878 25.9397,19.6458 15.3837,36.4458 C11.1127,43.2428 5.1097,55.2008 8.5607,61.0238 C9.1437,62.0148 13.2437,65.0248 13.2867,65.0358 C14.8877,62.8148 16.7207,61.1818 18.5667,60.1648 Z" id="Fill-46" fill="#FFFFFF"></path>
<path d="M62.7568,61.0156 C62.6058,60.8996 62.7548,60.4276 63.2788,59.9196 C63.7368,59.4816 64.2098,59.2346 64.7548,59.0046 C64.8408,58.9686 64.9288,58.9356 65.0208,58.9126 C65.2728,58.8456 65.5278,58.7596 65.8048,58.7176 C68.0218,58.3406 69.6438,59.5706 69.4328,59.9276 C69.3378,60.0936 68.9278,60.0566 68.3158,60.0156 C67.0468,59.9276 65.7148,59.9486 63.8318,60.7316 C63.2698,60.9576 62.9058,61.1346 62.7568,61.0156 M60.2968,57.2016 C59.4678,57.5176 58.8788,57.7626 58.5968,57.7206 C58.1368,57.6526 58.5818,56.8096 59.5878,55.9936 C61.6108,54.3786 64.3888,53.8776 66.7638,54.7616 C67.8028,55.1446 68.9698,55.9076 69.5798,56.8006 C69.8118,57.1386 69.8738,57.3946 69.7798,57.4996 C69.5938,57.7176 68.9288,57.4236 67.9448,57.0406 C65.4308,56.1026 63.5768,55.9596 60.2968,57.2016 M76.5508,58.4516 C76.8948,57.8226 77.8728,57.6966 78.7348,58.1676 C79.5958,58.6406 80.0158,59.5326 79.6708,60.1616 C79.3268,60.7886 78.3488,60.9156 77.4868,60.4436 C76.6258,59.9716 76.2058,59.0786 76.5508,58.4516 M80.1828,47.5176 L80.1818,47.5176 C80.1828,47.5176 80.1828,47.5176 80.1838,47.5176 C81.4978,47.0616 83.2388,50.4286 83.3188,53.4166 C82.2798,52.8976 81.0598,52.6766 79.8038,52.7856 C79.4878,51.8236 79.3368,50.9166 79.2998,49.9356 C79.2828,49.1196 79.4128,47.7686 80.1828,47.5176 M84.3258,57.6766 C84.1928,58.5366 83.5748,59.1536 82.9488,59.0556 C82.3218,58.9576 81.9228,58.1826 82.0578,57.3236 C82.1918,56.4636 82.8078,55.8466 83.4348,55.9446 C84.0608,56.0416 84.4598,56.8186 84.3258,57.6766 M101.6328,74.9486 C100.7498,74.9486 100.0828,75.1816 100.0828,75.1816 C100.0828,75.1816 100.0938,71.5046 98.3928,68.6416 C97.0818,70.1166 93.4438,73.0366 88.6678,75.3036 C84.1908,77.4296 78.2158,79.2906 70.8118,79.5376 C68.7378,79.6276 67.4548,79.2896 66.7338,81.7066 C66.6748,81.9226 66.6378,82.1646 66.6178,82.4116 C74.7338,84.9606 87.5698,79.9886 88.8158,79.6996 C88.8678,79.6876 88.9008,79.6806 88.9318,79.6796 C89.7358,79.7446 82.0398,84.5996 71.3608,84.5996 C69.5578,84.5996 68.0038,84.4426 66.7768,84.1966 C67.3608,86.1986 68.8858,87.0806 70.8948,87.5476 C72.4178,87.9026 74.0348,87.9366 74.0348,87.9366 C88.8308,88.3516 101.0058,76.8876 101.5088,76.2986 C101.5088,76.2986 101.3758,76.6096 101.3608,76.6446 C99.1918,81.5226 86.6338,90.3676 74.1338,90.1146 L74.0928,90.1276 C71.1948,90.1176 67.6718,89.3706 65.8408,87.1006 C62.9438,83.5076 64.4468,77.5356 69.0648,77.3946 C69.0698,77.3946 70.1318,77.3696 70.6048,77.3586 C82.0368,77.0066 92.2948,72.8066 99.6398,64.0356 C100.6498,62.7816 99.5218,61.0576 97.3328,61.0046 L97.3018,61.0046 C97.3018,61.0046 97.2888,60.9906 97.2808,60.9826 C94.6868,58.2026 92.4048,57.2216 90.3758,56.1906 C86.1208,54.0236 86.5218,55.8956 85.5308,45.3816 C85.2638,42.5476 84.7268,38.6816 82.2368,37.1996 C81.5838,36.8106 80.8708,36.6446 80.1298,36.6446 C79.3988,36.6446 79.0418,36.7936 78.8978,36.8256 C77.5338,37.1276 76.7578,37.9016 75.7858,38.7966 C71.1948,43.0306 67.5268,41.8816 62.0488,41.8326 C56.9048,41.7876 52.3628,45.3756 51.4948,50.8866 L51.4928,50.8896 C51.0708,53.7746 51.3448,56.7046 51.7958,58.0226 C51.7958,58.0226 50.4008,57.0936 49.7348,56.2716 C50.5398,61.3056 55.1328,64.5956 55.1328,64.5956 C54.4088,64.7616 53.3688,64.6926 53.3688,64.6926 C53.3728,64.6966 55.9898,66.7666 58.2618,67.4886 C57.6698,67.8566 54.6758,70.8106 53.1318,74.8776 C51.6878,78.6836 52.2758,83.2536 52.2758,83.2536 L53.5298,81.3826 C53.5298,81.3826 52.7108,85.5516 54.2968,89.5886 C54.8228,88.3886 55.9738,86.2446 55.9738,86.2446 C55.9738,86.2446 55.7908,90.7206 57.9458,94.3866 C58.0018,93.5406 58.3248,91.3866 58.3248,91.3866 C58.3248,91.3866 59.5628,95.2086 62.4188,97.8816 C67.7678,102.6276 81.9298,103.4856 92.7078,95.0156 C101.2448,88.3076 102.7358,80.2286 102.8788,79.9506 C104.9798,77.4306 104.4078,74.9486 101.6328,74.9486" id="Fill-48" fill="#FFFFFF"></path>
</g>
</g>
</svg>');
}
/**
* Add settings action link to the plugins page.
*
* @since 1.0.0
*/
public function add_action_links($links) {
$settings_link = array(
'<a href="' . admin_url( 'options-general.php?page=' . $this->plugin_name ) . '">' . __('Settings', $this->plugin_name) . '</a>',
);
return array_merge($settings_link, $links);
}
/**
* Render the settings page for this plugin.
*
* @since 1.0.0
*/
public function display_plugin_setup_page() {
include_once( 'partials/mailchimp-woocommerce-admin-tabs.php' );
}
/**
*
*/
public function options_update() {
$this->handle_abandoned_cart_table();
register_setting($this->plugin_name, $this->plugin_name, array($this, 'validate'));
}
/**
* Depending on the version we're on we may need to run some sort of migrations.
*/
public function update_db_check() {
// grab the current version set in the plugin variables
$version = mailchimp_environment_variables()->version;
// grab the saved version or default to 1.0.3 since that's when we first did this.
$saved_version = get_site_option('mailchimp_woocommerce_version', '1.0.3');
// if the saved version is less than the current version
if (version_compare($version, $saved_version) > 0) {
// resave the site option so this only fires once.
update_site_option('mailchimp_woocommerce_version', $version);
}
}
/**
* We need to do a tidy up function on the mailchimp_carts table to
* remove anything older than 30 days.
*
* Also if we don't have the configuration set, we need to create the table.
*/
protected function handle_abandoned_cart_table()
{
global $wpdb;
if (get_site_option('mailchimp_woocommerce_db_mailchimp_carts', false)) {
// need to tidy up the mailchimp_cart table and make sure we don't have anything older than 30 days old.
$date = gmdate( 'Y-m-d H:i:s', strtotime(date ("Y-m-d") ."-30 days"));
$sql = $wpdb->prepare("DELETE FROM {$wpdb->prefix}mailchimp_carts WHERE created_at <= %s", $date);
$wpdb->query($sql);
} else {
// create the table for the first time now.
$charset_collate = $wpdb->get_charset_collate();
$table = "{$wpdb->prefix}mailchimp_carts";
$sql = "CREATE TABLE IF NOT EXISTS $table (
id VARCHAR (255) NOT NULL,
email VARCHAR (100) NOT NULL,
user_id INT (11) DEFAULT NULL,
cart text NOT NULL,
created_at datetime NOT NULL
) $charset_collate;";
if (($result = $wpdb->query($sql)) > 0) {
update_site_option('mailchimp_woocommerce_db_mailchimp_carts', true);
}
}
}
/**
* @param $input
* @return array
*/
public function validate($input) {
$active_tab = isset($input['mailchimp_active_tab']) ? $input['mailchimp_active_tab'] : null;
if (empty($active_tab)) {
return $this->getOptions();
}
switch ($active_tab) {
case 'api_key':
$data = $this->validatePostApiKey($input);
break;
case 'store_info':
$data = $this->validatePostStoreInfo($input);
break;
case 'campaign_defaults' :
$data = $this->validatePostCampaignDefaults($input);
break;
case 'newsletter_settings':
$data = $this->validatePostNewsletterSettings($input);
break;
case 'sync':
// remove all the pointers to be sure
$service = new MailChimp_Service();
$service->removePointers(true, true);
$this->startSync();
$this->showSyncStartedMessage();
$this->setData('sync.config.resync', true);
break;
case 'logs':
if (isset($_POST['mc_action']) && in_array($_POST['mc_action'], array('view_log', 'remove_log'))) {
wp_redirect('options-general.php?page=mailchimp-woocommerce&tab=logs');
exit();
}
$data = array(
'mailchimp_logging' => isset($input['mailchimp_logging']) ? $input['mailchimp_logging'] : 'none',
);
break;
}
return (isset($data) && is_array($data)) ? array_merge($this->getOptions(), $data) : $this->getOptions();
}
/**
* STEP 1.
*
* Handle the 'api_key' tab post.
*
* @param $input
* @return array
*/
protected function validatePostApiKey($input)
{
$data = array(
'mailchimp_api_key' => isset($input['mailchimp_api_key']) ? $input['mailchimp_api_key'] : false,
'mailchimp_debugging' => isset($input['mailchimp_debugging']) ? $input['mailchimp_debugging'] : false,
'mailchimp_account_info_id' => null,
'mailchimp_account_info_username' => null,
);
$api = new MailChimp_WooCommerce_MailChimpApi($data['mailchimp_api_key']);
$valid = true;
if (empty($data['mailchimp_api_key']) || !($profile = $api->ping(true))) {
unset($data['mailchimp_api_key']);
$valid = false;
if (!$profile) {
add_settings_error('mailchimp_store_settings', '', 'API Key Invalid');
}
}
// tell our reporting system whether or not we had a valid ping.
$this->setData('validation.api.ping', $valid);
$data['active_tab'] = $valid ? 'store_info' : 'api_key';
if ($valid && isset($profile) && is_array($profile) && array_key_exists('account_id', $profile)) {
$data['mailchimp_account_info_id'] = $profile['account_id'];
$data['mailchimp_account_info_username'] = $profile['username'];
}
return $data;
}
/**
* STEP 2.
*
* Handle the 'store_info' tab post.
*
* @param $input
* @return array
*/
protected function validatePostStoreInfo($input)
{
$data = $this->compileStoreInfoData($input);
if (!$this->hasValidStoreInfo($data)) {
if ($this->hasInvalidStoreAddress($data)) {
$this->addInvalidAddressAlert();
}
if ($this->hasInvalidStorePhone($data)) {
$this->addInvalidPhoneAlert();
}
if ($this->hasInvalidStoreName($data)) {
$this->addInvalidStoreNameAlert();
}
$this->setData('validation.store_info', false);
$data['active_tab'] = 'store_info';
return array();
}
$this->setData('validation.store_info', true);
$data['active_tab'] = 'campaign_defaults';
if ($this->hasValidMailChimpList()) {
$this->syncStore(array_merge($this->getOptions(), $data));
}
return $data;
}
/**
* @param $input
* @return array
*/
protected function compileStoreInfoData($input)
{
return array(
// store basics
'store_name' => trim((isset($input['store_name']) ? $input['store_name'] : get_option('blogname'))),
'store_street' => isset($input['store_street']) ? $input['store_street'] : false,
'store_city' => isset($input['store_city']) ? $input['store_city'] : false,
'store_state' => isset($input['store_state']) ? $input['store_state'] : false,
'store_postal_code' => isset($input['store_postal_code']) ? $input['store_postal_code'] : false,
'store_country' => isset($input['store_country']) ? $input['store_country'] : false,
'store_phone' => isset($input['store_phone']) ? $input['store_phone'] : false,
// locale info
'store_locale' => isset($input['store_locale']) ? $input['store_locale'] : false,
'store_timezone' => isset($input['store_timezone']) ? $input['store_timezone'] : false,
'store_currency_code' => isset($input['store_currency_code']) ? $input['store_currency_code'] : false,
'admin_email' => isset($input['admin_email']) && is_email($input['admin_email']) ? $input['admin_email'] : $this->getOption('admin_email', false),
);
}
/**
* @param array $data
* @return array|bool
*/
protected function hasInvalidStoreAddress($data)
{
$address_keys = array(
'admin_email',
'store_city',
'store_state',
'store_postal_code',
'store_country',
'store_street'
);
$invalid = array();
foreach ($address_keys as $address_key) {
if (empty($data[$address_key])) {
$invalid[] = $address_key;
}
}
return empty($invalid) ? false : $invalid;
}
/**
* @param $data
* @return bool
*/
protected function hasInvalidStorePhone($data)
{
if (empty($data['store_phone']) || strlen($data['store_phone']) <= 6) {
return true;
}
return false;
}
/**
* @param $data
* @return bool
*/
protected function hasInvalidStoreName($data)
{
if (empty($data['store_name'])) {
return true;
}
return false;
}
/**
*
*/
protected function addInvalidAddressAlert()
{
add_settings_error('mailchimp_store_settings', '', 'As part of the MailChimp Terms of Use, we require a contact email and a physical mailing address.');
}
/**
*
*/
protected function addInvalidPhoneAlert()
{
add_settings_error('mailchimp_store_settings', '', 'As part of the MailChimp Terms of Use, we require a valid phone number for your store.');
}
/**
*
*/
protected function addInvalidStoreNameAlert()
{
add_settings_error('mailchimp_store_settings', '', 'MailChimp for WooCommerce requires a Store Name to connect your store.');
}
/**
* STEP 3.
*
* Handle the 'campaign_defaults' tab post.
*
* @param $input
* @return array
*/
protected function validatePostCampaignDefaults($input)
{
$data = array(
'campaign_from_name' => isset($input['campaign_from_name']) ? $input['campaign_from_name'] : false,
'campaign_from_email' => isset($input['campaign_from_email']) && is_email($input['campaign_from_email']) ? $input['campaign_from_email'] : false,
'campaign_subject' => isset($input['campaign_subject']) ? $input['campaign_subject'] : get_option('blogname'),
'campaign_language' => isset($input['campaign_language']) ? $input['campaign_language'] : 'en',
'campaign_permission_reminder' => isset($input['campaign_permission_reminder']) ? $input['campaign_permission_reminder'] : 'You were subscribed to the newsletter from '.get_option('blogname'),
);
if (!$this->hasValidCampaignDefaults($data)) {
$this->setData('validation.campaign_defaults', false);
return array('active_tab' => 'campaign_defaults');
}
$this->setData('validation.campaign_defaults', true);
$data['active_tab'] = 'newsletter_settings';
return $data;
}
/**
* STEP 4.
*
* Handle the 'newsletter_settings' tab post.
*
* @param $input
* @return array
*/
protected function validatePostNewsletterSettings($input)
{
// default value.
$checkbox = $this->getOption('mailchimp_checkbox_defaults', 'check');
// see if it's posted in the form.
if (isset($input['mailchimp_checkbox_defaults']) && !empty($input['mailchimp_checkbox_defaults'])) {
$checkbox = $input['mailchimp_checkbox_defaults'];
}
$data = array(
'mailchimp_list' => isset($input['mailchimp_list']) ? $input['mailchimp_list'] : $this->getOption('mailchimp_list', ''),
'newsletter_label' => isset($input['newsletter_label']) ? $input['newsletter_label'] : $this->getOption('newsletter_label', 'Subscribe to our newsletter'),
'mailchimp_auto_subscribe' => isset($input['mailchimp_auto_subscribe']) ? (bool) $input['mailchimp_auto_subscribe'] : $this->getOption('mailchimp_auto_subscribe', '0'),
'mailchimp_checkbox_defaults' => $checkbox,
'mailchimp_checkbox_action' => isset($input['mailchimp_checkbox_action']) ? $input['mailchimp_checkbox_action'] : $this->getOption('mailchimp_checkbox_action', 'woocommerce_after_checkout_billing_form'),
'mailchimp_product_image_key' => isset($input['mailchimp_product_image_key']) ? $input['mailchimp_product_image_key'] : 'medium',
);
if ($data['mailchimp_list'] === 'create_new') {
$data['mailchimp_list'] = $this->createMailChimpList(array_merge($this->getOptions(), $data));
}
// as long as we have a list set, and it's currently in MC as a valid list, let's sync the store.
if (!empty($data['mailchimp_list']) && $this->api()->hasList($data['mailchimp_list'])) {
$this->setData('validation.newsletter_settings', true);
// sync the store with MC
$this->syncStore(array_merge($this->getOptions(), $data));
// start the sync automatically if the sync is false
if ((bool) $this->getData('sync.started_at', false) === false) {
$this->startSync();
$this->showSyncStartedMessage();
}
$data['active_tab'] = 'sync';
return $data;
}
$this->setData('validation.newsletter_settings', false);
$data['active_tab'] = 'newsletter_settings';
return $data;
}
/**
* @param null|array $data
* @return bool
*/
public function hasValidStoreInfo($data = null)
{
return $this->validateOptions(array(
'store_name', 'store_street', 'store_city', 'store_state',
'store_postal_code', 'store_country', 'store_phone',
'store_locale', 'store_timezone', 'store_currency_code',
'store_phone',
), $data);
}
/**
* @param null|array $data
* @return bool
*/
public function hasValidCampaignDefaults($data = null)
{
return $this->validateOptions(array(
'campaign_from_name', 'campaign_from_email', 'campaign_subject', 'campaign_language',
'campaign_permission_reminder'
), $data);
}
/**
* @param null|array $data
* @return bool
*/
public function hasValidApiKey($data = null)
{
if (!$this->validateOptions(array('mailchimp_api_key'), $data)) {
return false;
}
if (($pinged = $this->getCached('api-ping-check', null)) === null) {
if (($pinged = $this->api()->ping())) {
$this->setCached('api-ping-check', true, 120);
}
return $pinged;
}
return $pinged;
}
/**
* @return bool
*/
public function hasValidMailChimpList()
{
if (!$this->hasValidApiKey()) {
add_settings_error('mailchimp_api_key', '', 'You must supply your MailChimp API key to pull the lists.');
return false;
}
if (!($this->validateOptions(array('mailchimp_list')))) {
return $this->api()->getLists(true);
}
return $this->api()->hasList($this->getOption('mailchimp_list'));
}
/**
* @return array|bool|mixed|null
*/
public function getAccountDetails()
{
if (!$this->hasValidApiKey()) {
return false;
}
try {
if (($account = $this->getCached('api-account-name', null)) === null) {
if (($account = $this->api()->getProfile())) {
$this->setCached('api-account-name', $account, 120);
}
}
return $account;
} catch (\Exception $e) {
return false;
}
}
/**
* @return array|bool
*/
public function getMailChimpLists()
{
if (!$this->hasValidApiKey()) {
return false;
}
try {
if (($pinged = $this->getCached('api-lists', null)) === null) {
$pinged = $this->api()->getLists(true);
if ($pinged) {
$this->setCached('api-lists', $pinged, 120);
}
return $pinged;
}
return $pinged;
} catch (\Exception $e) {
return array();
}
}
/**
* @return array|bool
*/
public function getListName()
{
if (!$this->hasValidApiKey()) {
return false;
}
if (!($list_id = $this->getOption('mailchimp_list', false))) {
return false;
}
try {
if (($lists = $this->getCached('api-lists', null)) === null) {
$lists = $this->api()->getLists(true);
if ($lists) {
$this->setCached('api-lists', $lists, 120);
}
}
return array_key_exists($list_id, $lists) ? $lists[$list_id] : false;
} catch (\Exception $e) {
return array();
}
}
/**
* @return bool
*/
public function isReadyForSync()
{
if (!$this->hasValidApiKey()) {
return false;
}
if (!$this->getOption('mailchimp_list', false)) {
return false;
}
if (!$this->api()->hasList($this->getOption('mailchimp_list'))) {
return false;
}
if (!$this->api()->getStore($this->getUniqueStoreID())) {
return false;
}
return true;
}
/**
* @param null|array $data
* @return bool|string
*/
private function createMailChimpList($data = null)
{
if (empty($data)) {
$data = $this->getOptions();
}
$required = array(
'store_name', 'store_street', 'store_city', 'store_state',
'store_postal_code', 'store_country', 'campaign_from_name',
'campaign_from_email', 'campaign_subject', 'campaign_permission_reminder',
);
foreach ($required as $requirement) {
if (!isset($data[$requirement]) || empty($data[$requirement])) {
return false;
}
}
$submission = new MailChimp_WooCommerce_CreateListSubmission();
// allow the subscribers to choose preferred email type (html or text).
$submission->setEmailTypeOption(true);
// set the store name
$submission->setName($data['store_name']);
// set the campaign defaults
$submission->setCampaignDefaults(
$data['campaign_from_name'],
$data['campaign_from_email'],
$data['campaign_subject'],
$data['campaign_language']
);
// set the permission reminder message.
$submission->setPermissionReminder($data['campaign_permission_reminder']);
if (isset($data['admin_email']) && !empty($data['admin_email'])) {
$submission->setNotifyOnSubscribe($data['admin_email']);
$submission->setNotifyOnUnSubscribe($data['admin_email']);
}
$submission->setContact($this->address($data));
try {
$response = $this->api()->createList($submission);
$list_id = array_key_exists('id', $response) ? $response['id'] : false;
$this->setData('errors.mailchimp_list', false);
return $list_id;
} catch (MailChimp_WooCommerce_Error $e) {
$this->setData('errors.mailchimp_list', $e->getMessage());
return false;
}
}
/**
* @param null $data
* @return bool
*/
private function syncStore($data = null)
{
if (empty($data)) {
$data = $this->getOptions();
}
$list_id = $this->array_get($data, 'mailchimp_list', false);
$site_url = $this->getUniqueStoreID();
if (empty($list_id) || empty($site_url)) {
return false;
}
$new = false;
if (!($store = $this->api()->getStore($site_url))) {
$new = true;
$store = new MailChimp_WooCommerce_Store();
}
$call = $new ? 'addStore' : 'updateStore';
$time_key = $new ? 'store_created_at' : 'store_updated_at';
$store->setId($site_url);
$store->setPlatform('woocommerce');
// set the locale data
$store->setPrimaryLocale($this->array_get($data, 'store_locale', 'en'));
$store->setTimezone($this->array_get($data, 'store_timezone', 'America\New_York'));
$store->setCurrencyCode($this->array_get($data, 'store_currency_code', 'USD'));
// set the basics
$store->setName($this->array_get($data, 'store_name'));
$store->setDomain(get_option('siteurl'));
// don't know why we did this before
//$store->setEmailAddress($this->array_get($data, 'campaign_from_email'));
$store->setEmailAddress($this->array_get($data, 'admin_email'));
$store->setAddress($this->address($data));
$store->setPhone($this->array_get($data, 'store_phone'));
$store->setListId($list_id);
try {
// let's create a new store for this user through the API
$this->api()->$call($store);
// apply extra meta for store created at
$this->setData('errors.store_info', false);
$this->setData($time_key, time());
// on a new store push, we need to make sure we save the site script into a local variable.
if ($new) {
mailchimp_update_connected_site_script();
}
return true;
} catch (\Exception $e) {
$this->setData('errors.store_info', $e->getMessage());
}
return false;
}
/**
* @param array $data
* @return MailChimp_WooCommerce_Address
*/
private function address(array $data)
{
$address = new MailChimp_WooCommerce_Address();
if (isset($data['store_street']) && $data['store_street']) {
$address->setAddress1($data['store_street']);
}
if (isset($data['store_city']) && $data['store_city']) {
$address->setCity($data['store_city']);
}
if (isset($data['store_state']) && $data['store_state']) {
$address->setProvince($data['store_state']);
}
if (isset($data['store_country']) && $data['store_country']) {
$address->setCountry($data['store_country']);
}
if (isset($data['store_postal_code']) && $data['store_postal_code']) {
$address->setPostalCode($data['store_postal_code']);
}
if (isset($data['store_name']) && $data['store_name']) {
$address->setCompany($data['store_name']);
}
if (isset($data['store_phone']) && $data['store_phone']) {
$address->setPhone($data['store_phone']);
}
$address->setCountryCode($this->array_get($data, 'store_currency_code', 'USD'));
return $address;
}
/**
* @param array $required
* @param null $options
* @return bool
*/
private function validateOptions(array $required, $options = null)
{
$options = is_array($options) ? $options : $this->getOptions();
foreach ($required as $requirement) {
if (!isset($options[$requirement]) || empty($options[$requirement])) {
return false;
}
}
return true;
}
/**
* Start the sync
*/
private function startSync()
{
mailchimp_flush_sync_pointers();
$coupon_sync = new MailChimp_WooCommerce_Process_Coupons();
wp_queue($coupon_sync);
$job = new MailChimp_WooCommerce_Process_Products();
$job->flagStartSync();
wp_queue($job);
}
/**
* Show the sync started message right when they sync things.
*/
private function showSyncStartedMessage()
{
$text = 'Starting the sync process…<br/>'.
'<p id="sync-status-message">Please hang tight while we work our mojo. Sometimes the sync can take a while, '.
'especially on sites with lots of orders and/or products. You may refresh this page at '.
'anytime to check on the progress.</p>';
add_settings_error('mailchimp-woocommerce_notice', $this->plugin_name, __($text), 'updated');
}
}

View File

@@ -0,0 +1,4 @@
/**
* All of the CSS for your admin-specific functionality should be
* included in this file.
*/

View File

@@ -0,0 +1 @@
<?php // Silence is golden

View File

@@ -0,0 +1,32 @@
(function( $ ) {
'use strict';
/**
* All of the code for your admin-facing JavaScript source
* should reside in this file.
*
* Note: It has been assumed you will write jQuery code here, so the
* $ function reference has been prepared for usage within the scope
* of this function.
*
* This enables you to define handlers, for when the DOM is ready:
*
* $(function() {
*
* });
*
* When the window is loaded:
*
* $( window ).load(function() {
*
* });
*
* ...and/or other possibilities.
*
* Ideally, it is not considered best practise to attach more than a
* single DOM-ready or window-load handler for a particular page.
* Although scripts in the WordPress core, Plugins and Themes may be
* practising this, we should strive to set a better example in our own work.
*/
})( jQuery );

View File

@@ -0,0 +1,143 @@
<?php
$active_tab = isset($_GET['tab']) ? $_GET['tab'] : 'api_key';
$is_mailchimp_post = isset($_POST['mailchimp_woocommerce_settings_hidden']) && $_POST['mailchimp_woocommerce_settings_hidden'] === 'Y';
$handler = MailChimp_WooCommerce_Admin::connect();
//Grab all options for this particular tab we're viewing.
$options = get_option($this->plugin_name, array());
if (!isset($_GET['tab']) && isset($options['active_tab'])) {
$active_tab = $options['active_tab'];
}
$show_sync_tab = isset($_GET['resync']) ? $_GET['resync'] === '1' : false;;
$show_campaign_defaults = true;
$has_valid_api_key = false;
$allow_new_list = true;
$clicked_sync_button = $is_mailchimp_post&& $active_tab == 'sync';
if (isset($options['mailchimp_api_key']) && $handler->hasValidApiKey()) {
$has_valid_api_key = true;
// if we don't have a valid api key we need to redirect back to the 'api_key' tab.
if (($mailchimp_lists = $handler->getMailChimpLists()) && is_array($mailchimp_lists)) {
$show_campaign_defaults = false;
$allow_new_list = false;
}
// only display this button if the data is not syncing and we have a valid api key
if ((bool) $this->getData('sync.started_at', false)) {
$show_sync_tab = true;
}
}
?>
<style>
#sync-status-message strong {
font-weight:inherit;
}
#log-viewer {
background: #fff;
border: 1px solid #e5e5e5;
box-shadow: 0 1px 1px rgba(0,0,0,.04);
padding: 5px 20px;
}
#log-viewer-select {
padding: 10px 0 8px;
line-height: 28px;
}
#log-viewer pre {
font-family: monospace;
white-space: pre-wrap;
}
user agent stylesheet
pre, xmp, plaintext, listing {
display: block;
font-family: monospace;
white-space: pre;
margin: 1em 0px;
}
</style>
<?php if (!defined('PHP_VERSION_ID') || (PHP_VERSION_ID < 70000)): ?>
<div data-dismissible="notice-php-version" class="error notice notice-error is-dismissible">
<p><?php _e('MailChimp says: Please upgrade your PHP version to a minimum of 7.0', 'mailchimp-woocommerce'); ?></p>
</div>
<?php endif; ?>
<!-- Create a header in the default WordPress 'wrap' container -->
<div class="wrap">
<div id="icon-themes" class="icon32"></div>
<h2>MailChimp Settings</h2>
<h2 class="nav-tab-wrapper">
<a href="?page=mailchimp-woocommerce&tab=api_key" class="nav-tab <?php echo $active_tab == 'api_key' ? 'nav-tab-active' : ''; ?>">Connect</a>
<?php if($has_valid_api_key): ?>
<a href="?page=mailchimp-woocommerce&tab=store_info" class="nav-tab <?php echo $active_tab == 'store_info' ? 'nav-tab-active' : ''; ?>">Store Settings</a>
<?php if ($handler->hasValidStoreInfo()) : ?>
<?php if($show_campaign_defaults): ?>
<a href="?page=mailchimp-woocommerce&tab=campaign_defaults" class="nav-tab <?php echo $active_tab == 'campaign_defaults' ? 'nav-tab-active' : ''; ?>">List Defaults</a>
<?php endif; ?>
<a href="?page=mailchimp-woocommerce&tab=newsletter_settings" class="nav-tab <?php echo $active_tab == 'newsletter_settings' ? 'nav-tab-active' : ''; ?>">List Settings</a>
<?php if($show_sync_tab): ?>
<a href="?page=mailchimp-woocommerce&tab=sync" class="nav-tab <?php echo $active_tab == 'sync' ? 'nav-tab-active' : ''; ?>">Sync</a>
<a href="?page=mailchimp-woocommerce&tab=logs" class="nav-tab <?php echo $active_tab == 'logs' ? 'nav-tab-active' : ''; ?>">Logs</a>
<?php endif; ?>
<?php endif;?>
<?php endif; ?>
</h2>
<form method="post" name="cleanup_options" action="options.php">
<input type="hidden" name="mailchimp_woocommerce_settings_hidden" value="Y">
<?php
if (!$clicked_sync_button) {
settings_fields($this->plugin_name);
do_settings_sections($this->plugin_name);
include('tabs/notices.php');
}
?>
<input type="hidden" name="<?php echo $this->plugin_name; ?>[mailchimp_active_tab]" value="<?php echo esc_attr($active_tab); ?>"/>
<?php if ($active_tab == 'api_key' ): ?>
<?php include_once 'tabs/api_key.php'; ?>
<?php endif; ?>
<?php if ($active_tab == 'store_info' && $has_valid_api_key): ?>
<?php include_once 'tabs/store_info.php'; ?>
<?php endif; ?>
<?php if ($active_tab == 'campaign_defaults' ): ?>
<?php include_once 'tabs/campaign_defaults.php'; ?>
<?php endif; ?>
<?php if ($active_tab == 'newsletter_settings' ): ?>
<?php include_once 'tabs/newsletter_settings.php'; ?>
<?php endif; ?>
<?php if ($active_tab == 'sync' && $show_sync_tab): ?>
<?php include_once 'tabs/store_sync.php'; ?>
<?php endif; ?>
<?php if ($active_tab == 'logs' && $show_sync_tab): ?>
<?php include_once 'tabs/logs.php'; ?>
<?php endif; ?>
<?php if ($active_tab !== 'sync' && $active_tab !== 'logs') submit_button('Save all changes', 'primary','submit', TRUE); ?>
</form>
<?php if ($active_tab == 'sync'): ?>
<h2 style="padding-top: 1em;">More Information</h2>
<ul>
<li>Have a larger store or having issues syncing? Consider using <a href="https://github.com/mailchimp/mc-woocommerce/issues/158" target="_blank">WP-CLI</a>.</li>
<li>Order and customer information will not sync if they contain an Amazon or generic email address.</li>
<li>Need help to connect your store? Visit the MailChimp <a href="http://kb.mailchimp.com/integrations/e-commerce/connect-or-disconnect-mailchimp-for-woocommerce/" target="_blank">Knowledge Base</a>.</li>
<li>Want to tell us how we're doing? <a href="https://wordpress.org/support/plugin/mailchimp-for-woocommerce/reviews/" target="_blank">Leave a review on Wordpress.org</a>.</li>
</ul>
<?php endif; ?>
</div><!-- /.wrap -->

View File

@@ -0,0 +1,25 @@
<?php
if (isset($options['mailchimp_api_key']) && !$handler->hasValidApiKey()) {
include_once __DIR__.'/errors/missing_api_key.php';
}
?>
<input type="hidden" name="mailchimp_active_settings_tab" value="api_key"/>
<h2 style="padding-top: 1em;">API Information</h2>
<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>
<!-- remove some meta and generators from the <head> -->
<fieldset>
<legend class="screen-reader-text">
<span>MailChimp API Key</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-mailchimp-api-key">
<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'] : '' ?>" />
<span><?php esc_attr_e('Enter your MailChimp API key.', $this->plugin_name); ?></span>
</label>
</fieldset>

View File

@@ -0,0 +1,74 @@
<?php
$handler = MailChimp_WooCommerce_Admin::connect();
// if we don't have valid campaign defaults we need to redirect back to the 'campaign_defaults' tab.
if (!$handler->hasValidApiKey()) {
wp_redirect('options-general.php?page=mailchimp-woocommerce&tab=api_key&error_notice=missing_api_key');
}
if (!$handler->hasValidStoreInfo()) {
wp_redirect('options-general.php?page=mailchimp-woocommerce&tab=store_info&error_notice=missing_store');
}
?>
<input type="hidden" name="mailchimp_active_settings_tab" value="campaign_defaults"/>
<h2 style="padding-top: 1em;">List Defaults</h2>
<p>Please fill out the default campaign information.</p>
<fieldset>
<legend class="screen-reader-text">
<span>Contact Name</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-campaign-from-name-label">
<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'] : '' ?>" />
<span><?php esc_attr_e('Default from name', $this->plugin_name); ?></span>
</label>
</fieldset>
<fieldset>
<legend class="screen-reader-text">
<span>From Email</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-campaign-from-email-label">
<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') ?>" />
<span><?php esc_attr_e('Default from email', $this->plugin_name); ?></span>
</label>
</fieldset>
<fieldset>
<legend class="screen-reader-text">
<span>Default Subject</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-campaign-subject-label">
<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') ?>" />
<span><?php esc_attr_e('Default subject', $this->plugin_name); ?></span>
</label>
</fieldset>
<fieldset>
<legend class="screen-reader-text">
<span>Default Language</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-campaign-language-label">
<select id="<?php echo $this->plugin_name; ?>-campaign-language-label" name="<?php echo $this->plugin_name; ?>[campaign_language]" style="width:30%" required>
<?php $selected_locale = isset($options['campaign_language']) && !empty($options['campaign_language']) ? $options['campaign_language'] : 'en'; ?>
<?php
foreach(MailChimp_Api_Locales::simple() as $locale_key => $local_value) {
echo '<option value="' . esc_attr( $locale_key ) . '" ' . selected($locale_key === $selected_locale, true, false ) . '>' . esc_html( $local_value ) . '</option>';
}
?>
</select>
<span><?php esc_attr_e('Default language', $this->plugin_name); ?></span>
</label>
</fieldset>
<fieldset>
<legend class="screen-reader-text">
<span>Permission Reminder</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-campaign-permission-reminder-label">
<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>
<span><?php esc_attr_e('Permission reminder message', $this->plugin_name); ?></span>
</label>
</fieldset>

View File

@@ -0,0 +1,81 @@
<?php
if (!empty( $_REQUEST['handle'])) {
if (!empty($_REQUEST['_wpnonce']) && wp_verify_nonce($_REQUEST['_wpnonce'], 'remove_log')) {
$log_handler = new WC_Log_Handler_File();
$log_handler->remove($_REQUEST['handle']);
wp_redirect('options-general.php?page=mailchimp-woocommerce&tab=logs');
}
}
$files = defined('WC_LOG_DIR') ? @scandir( WC_LOG_DIR ) : array();
$logs = array();
if (!empty($files)) {
foreach ($files as $key => $value) {
if (!in_array( $value, array( '.', '..' ))) {
if (!is_dir($value) && mailchimp_string_contains($value, 'mailchimp_woocommerce')) {
$logs[sanitize_title($value)] = $value;
}
}
}
}
if (!empty($_REQUEST['log_file']) && isset($logs[sanitize_title( $_REQUEST['log_file'])])) {
$viewed_log = $logs[sanitize_title($_REQUEST['log_file'])];
} elseif (!empty($logs)) {
$viewed_log = current( $logs );
}
$handle = !empty($viewed_log) ? substr($viewed_log, 0, strlen($viewed_log) > 37 ? strlen($viewed_log) - 37 : strlen($viewed_log) - 4) : '';
?>
<h2 style="padding-top: 1em;">Logging Preference</h2>
<p>
Advanced troubleshooting can be conducted with the logging capability turned on.
By default, its set to “none” and you may toggle to either “standard” or “debug” as needed.
With standard logging, you can see basic information about the data submission to MailChimp including any errors.
“Debug” gives a much deeper insight that is useful to share with support if problems arise.
</p>
<fieldset>
<legend class="screen-reader-text">
<span>Logging Preference</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-logging">
<select name="<?php echo $this->plugin_name; ?>[mailchimp_logging]" style="width:30%" required>
<?php $logging_preference = mailchimp_environment_variables()->logging; ?>
<?php
foreach(array('none' => 'None', 'debug' => 'Debug', 'standard' => 'Standard',) as $log_value => $log_label) {
echo '<option value="'.esc_attr($log_value).'" '.selected($log_value === $logging_preference, true, false ) . '>' . esc_html($log_label) . '</option>';
}
?>
</select>
</label>
</fieldset>
<?php submit_button('Save all changes', 'primary','submit', TRUE);?>
<?php if (isset($logs) && isset($viewed_log)) : ?>
<div id="log-viewer-select">
<div class="alignleft">
<h2>
<?php echo esc_html( $viewed_log ); ?>
<?php if ( ! empty( $handle ) ) : ?>
<a class="page-title-action" href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'handle' => $handle ), admin_url( 'options-general.php?page=mailchimp-woocommerce&tab=logs&mc_action=remove_log' ) ), 'remove_log' ) ); ?>" class="button"><?php esc_html_e( 'Delete log', 'woocommerce' );?></a>
<?php endif; ?>
</h2>
</div>
<div class="alignright">
<form action="<?php echo admin_url( 'options-general.php?page=mailchimp-woocommerce&tab=logs&mc_action=view_log' ); ?>" method="post">
<input type="hidden" name="<?php echo $this->plugin_name; ?>[mailchimp_active_tab]" value="logs"/>
<select name="log_file">
<?php foreach ( $logs as $log_key => $log_file ) : ?>
<option value="<?php echo esc_attr( $log_key ); ?>" <?php selected( sanitize_title( $viewed_log ), $log_key ); ?>><?php echo esc_html( $log_file ); ?> (<?php echo date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), filemtime( WC_LOG_DIR . $log_file ) ); ?>)</option>
<?php endforeach; ?>
</select>
<input type="submit" class="button" value="<?php esc_attr_e( 'View', 'woocommerce' ); ?>" />
</form>
</div>
<div class="clear"></div>
</div>
<div id="log-viewer">
<pre><?php echo esc_html( file_get_contents( WC_LOG_DIR . $viewed_log ) ); ?></pre>
</div>
<?php else : ?>
<div class="updated woocommerce-message inline"><p><?php _e( 'There are currently no logs to view.', 'woocommerce' ); ?></p></div>
<?php endif; ?>

View File

@@ -0,0 +1,149 @@
<?php
// if we don't have valid campaign defaults we need to redirect back to the 'campaign_defaults' tab.
if (!$handler->hasValidApiKey()) {
wp_redirect('options-general.php?page=mailchimp-woocommerce&tab=api_key&error_notice=missing_api_key');
}
// if we don't have valid store information, we need to redirect back to the 'store_info' tab.
if (!$handler->hasValidStoreInfo()) {
wp_redirect('options-general.php?page=mailchimp-woocommerce&tab=store_info&error_notice=missing_store');
}
// if we don't have a valid api key we need to redirect back to the 'api_key' tab.
if (!isset($mailchimp_lists) && ($mailchimp_lists = $handler->getMailChimpLists()) === false) {
wp_redirect('options-general.php?page=mailchimp-woocommerce&tab=api_key&error_notice=missing_api_key');
}
// if we don't have valid campaign defaults we need to redirect back to the 'campaign_defaults' tab.
if (empty($mailchimp_lists) && !$handler->hasValidCampaignDefaults()) {
wp_redirect('options-general.php?page=mailchimp-woocommerce&tab=campaign_defaults&error_notice=missing_campaign_defaults');
}
$list_is_configured = isset($options['mailchimp_list']) && (!empty($options['mailchimp_list'])) && array_key_exists($options['mailchimp_list'], $mailchimp_lists);
?>
<?php if(($newsletter_settings_error = $this->getData('errors.mailchimp_list', false))) : ?>
<div class="error notice is-dismissable">
<p><?php _e($newsletter_settings_error, 'mailchimp-woocommerce'); ?></p>
</div>
<?php endif; ?>
<input type="hidden" name="mailchimp_active_settings_tab" value="newsletter_settings"/>
<h2 style="padding-top: 1em;">List Settings</h2>
<p>Please apply your list settings. If you don't have a list, you can choose to create one.</p>
<fieldset>
<legend class="screen-reader-text">
<span>List Name</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-mailchimp-list-label">
<select name="<?php echo $this->plugin_name; ?>[mailchimp_list]" style="width:30%" required <?php if($list_is_configured): ?> disabled <?php endif; ?>>
<?php if(!isset($allow_new_list) || $allow_new_list === true): ?>
<option value="create_new">Create New List</option>
<?php endif ?>
<?php if(isset($allow_new_list) && $allow_new_list === false): ?>
<option value="">-- Select List --</option>
<?php endif; ?>
<?php
if (is_array($mailchimp_lists)) {
$selected_list = isset($options['mailchimp_list']) ? $options['mailchimp_list'] : null;
foreach ($mailchimp_lists as $key => $value ) {
echo '<option value="' . esc_attr( $key ) . '" ' . selected(((string) $key === (string) $selected_list), true, false) . '>' . esc_html( $value ) . '</option>';
}
}
?>
</select>
<span><?php esc_attr_e('Choose a list to sync with your store.', $this->plugin_name); ?></span>
</label>
</fieldset>
<fieldset>
<legend class="screen-reader-text">
<span>Auto Subscribe On Initial Sync</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-mailchimp-auto-subscribe">
<select name="<?php echo $this->plugin_name; ?>[mailchimp_auto_subscribe]" style="width:30%" required <?php if($list_is_configured): ?> disabled <?php endif; ?>>
<?php
$enable_auto_subscribe = (array_key_exists('mailchimp_auto_subscribe', $options) && !is_null($options['mailchimp_auto_subscribe'])) ? $options['mailchimp_auto_subscribe'] : '1';
foreach (array('0' => 'No', '1' => 'Yes') as $key => $value ) {
echo '<option value="' . esc_attr( $key ) . '" ' . selected($key == $enable_auto_subscribe, true, false ) . '>' . esc_html( $value ) . '</option>';
}
?>
</select>
<span><?php esc_attr_e('During initial sync, auto subscribe the existing customers.', $this->plugin_name); ?></span>
</label>
</fieldset>
<h2 style="padding-top: 1em;">Opt-in Settings</h2>
<p>Add text to go along with the opt-in checkbox, and choose a default display option. Customers can click a box at checkout to opt in to your newsletter. Write a signup message and choose how you want this checkbox to appear. </p>
<fieldset>
<legend class="screen-reader-text">
<span>Newsletter Label</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-newsletter-checkbox-label">
<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' ?>" />
<span><?php esc_attr_e('Enter text for the opt-in checkbox', $this->plugin_name); ?></span>
</label>
</fieldset>
<h4 style="padding-top: 1em;font-weight:normal;">Checkbox Display Options</h4>
<fieldset>
<legend class="screen-reader-text">
<span>Checkbox Display Options</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-newsletter-checkbox-defaults">
<?php $checkbox_default_settings = (array_key_exists('mailchimp_checkbox_defaults', $options) && !is_null($options['mailchimp_checkbox_defaults'])) ? $options['mailchimp_checkbox_defaults'] : 'check'; ?>
<input type="radio" name="<?php echo $this->plugin_name; ?>[mailchimp_checkbox_defaults]" value="check"<?php if($checkbox_default_settings === 'check') echo ' checked="checked" '; ?>>Visible, checked by default<br>
<input type="radio" name="<?php echo $this->plugin_name; ?>[mailchimp_checkbox_defaults]" value="uncheck"<?php if($checkbox_default_settings === 'uncheck') echo ' checked="checked" '; ?>>Visible, unchecked by default<br/>
<input type="radio" name="<?php echo $this->plugin_name; ?>[mailchimp_checkbox_defaults]" value="hide"<?php if($checkbox_default_settings === 'hide') echo ' checked="checked" '; ?>>Hidden, unchecked by default<br/>
</label>
</fieldset>
<h4 style="padding-top: 1em;font-weight:normal;">Advanced Checkbox Settings</h4>
<p>
To change the location of the opt-in checkbox at checkout, input one of the
<a href="https://docs.woocommerce.com/wc-apidocs/hook-docs.html" target="_blank">
available WooCommerce form actions.
</a>
</p>
<fieldset>
<legend class="screen-reader-text">
<span>Newsletter Checkbox Action</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-newsletter-checkbox-action">
<input style="width: 30%;" type="text" id="<?php echo $this->plugin_name; ?>-newsletter-checkbox-action" name="<?php echo $this->plugin_name; ?>[mailchimp_checkbox_action]" value="<?php echo isset($options['mailchimp_checkbox_action']) ? $options['mailchimp_checkbox_action'] : 'woocommerce_after_checkout_billing_form' ?>" />
<span><?php esc_attr_e('Enter a WooCommerce form action', $this->plugin_name); ?></span>
</label>
</fieldset>
<h2 style="padding-top: 1em;">Product Image Size</h2>
<p>Define the product image size used by abandoned carts, order notifications, and product recommendations.</p>
<fieldset>
<legend class="screen-reader-text">
<span>Product Image Size</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-mailchimp-product_image_key">
<select name="<?php echo $this->plugin_name; ?>[mailchimp_product_image_key]" style="width:30%">
<?php
$enable_auto_subscribe = (array_key_exists('mailchimp_product_image_key', $options) && !is_null($options['mailchimp_product_image_key'])) ? $options['mailchimp_product_image_key'] : 'medium';
foreach (mailchimp_woocommerce_get_all_image_sizes_list() as $key => $value ) {
echo '<option value="' . esc_attr( $key ) . '" ' . selected($key == $enable_auto_subscribe, true, false ) . '>' . esc_html( $value ) . '</option>';
}
?>
</select>
<span><?php esc_attr_e('Select an image size', $this->plugin_name); ?></span>
</label>
</fieldset>

View File

@@ -0,0 +1,39 @@
<?php if(isset($_GET['error_notice'])): ?>
<div class="error notice is-dismissable">
<?php
switch($_GET['error_notice']) {
case 'missing_api_key':
_e('MailChimp says: You must enter in a valid API key.', 'mailchimp-woocommerce');
break;
case 'missing_campaign_defaults':
_e('MailChimp says: Sorry you must set up your campaign defaults before you proceed!', 'mailchimp-woocommerce');
break;
case 'missing_list':
_e('MailChimp says: You must select a marketing list.', 'mailchimp-woocommerce');
break;
case 'missing_store':
_e('MailChimp says: Sorry you must set up your store before you proceed!', 'mailchimp-woocommerce');
break;
case 'not_ready_for_sync':
_e('MailChimp says: You are not fully ready to run the Store Sync, please verify your settings before proceeding.', 'mailchimp-woocommerce');
break;
default:
}
?>
</div>
<?php endif; ?>
<?php if (isset($_GET['success_notice'])): ?>
<div class="success notice is-dismissable">
<?php
switch($_GET['error_notice']) {
case 're-sync-started':
_e('MailChimp says: Your re-sync has been started!', 'mailchimp-woocommerce');
break;
default:
}
?>
</div>
<?php endif; ?>

View File

@@ -0,0 +1,215 @@
<?php
$handler = MailChimp_WooCommerce_Admin::connect();
// if we don't have valid campaign defaults we need to redirect back to the 'campaign_defaults' tab.
if (!$handler->hasValidApiKey()) {
wp_redirect('options-general.php?page=mailchimp-woocommerce&tab=api_key&error_notice=missing_api_key');
}
?>
<input type="hidden" name="mailchimp_active_settings_tab" value="store_info"/>
<h2 style="padding-top: 1em;">Store Settings</h2>
<p>Please provide the following information about your WooCommerce store.</p>
<fieldset>
<legend class="screen-reader-text">
<span>Store Name</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-store-name-label">
<input required 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') ?>" />
<span>
<?php
if (!empty($options['store_name']) ) {
esc_attr_e('Name', $this->plugin_name);
} else {
esc_attr_e('Name', $this->plugin_name); echo '<span style="color:red;">*</span>';
}
?>
</span>
</label>
</fieldset>
<fieldset>
<legend class="screen-reader-text">
<span>Email</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-admin-email-label">
<input required 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') ?>" />
<span>
<?php
if (!empty($options['admin_email']) ) {
esc_attr_e('Email', $this->plugin_name);
} else {
esc_attr_e('Email', $this->plugin_name); echo '<span style="color:red;">*</span>';
}
?>
</span>
</label>
</fieldset>
<fieldset>
<legend class="screen-reader-text">
<span>Street Address</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-store-address-label">
<input required 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'] : '' ?>" />
<span>
<?php
if (!empty($options['store_street']) ) {
esc_attr_e('Street address', $this->plugin_name);
} else {
esc_attr_e('Street address', $this->plugin_name); echo '<span style="color:red;">*</span>';
}
?>
</span>
</label>
</fieldset>
<fieldset>
<legend class="screen-reader-text">
<span>City</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-store-city-label">
<input required 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'] : '' ?>" />
<span>
<?php
if (!empty($options['store_city']) ) {
esc_attr_e('City', $this->plugin_name);
} else {
esc_attr_e('City', $this->plugin_name); echo '<span style="color:red;">*</span>';
}
?>
</span>
</label>
</fieldset>
<fieldset>
<legend class="screen-reader-text">
<span>State</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-store-state-label">
<input required 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'] : '' ?>" />
<span>
<?php
if (!empty($options['store_state']) ) {
esc_attr_e('State', $this->plugin_name);
} else {
esc_attr_e('State', $this->plugin_name); echo '<span style="color:red;">*</span>';
}
?>
</span>
</label>
</fieldset>
<fieldset>
<legend class="screen-reader-text">
<span>Postal Code</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-store-state-label">
<input required 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'] : '' ?>" />
<span>
<?php
if (!empty($options['store_postal_code']) ) {
esc_attr_e('Postal Code', $this->plugin_name);
} else {
esc_attr_e('Postal Code', $this->plugin_name); echo '<span style="color:red;">*</span>';
}
?>
</span>
</label>
</fieldset>
<fieldset>
<legend class="screen-reader-text">
<span>Country</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-store-country-label">
<input required 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' ?>" />
<span>
<?php
if (!empty($options['store_country'])) {
esc_attr_e('Country', $this->plugin_name);
} else {
esc_attr_e('Country', $this->plugin_name); echo '<span style="color:red;">*</span>';
}
?>
</span>
</label>
</fieldset>
<fieldset>
<legend class="screen-reader-text">
<span>Phone</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-store-phone-label">
<input required 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'] : '' ?>" />
<span>
<?php
if (!empty($options['store_phone']) ) {
esc_attr_e('Phone Number', $this->plugin_name);
} else {
esc_attr_e('Phone Number', $this->plugin_name); echo '<span style="color:red;">*</span>';
}
?>
</span>
</label>
</fieldset>
<h2 style="padding-top: 1em;">Locale Settings</h2>
<p>Please apply your locale settings. If you're unsure about these, use the defaults.</p>
<fieldset>
<legend class="screen-reader-text">
<span>Locale</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-store-locale-label">
<select name="<?php echo $this->plugin_name; ?>[store_locale]" style="width:30%" required>
<?php $selected_locale = isset($options['store_locale']) && !empty($options['store_locale']) ? $options['store_locale'] : 'en'; ?>
<?php
foreach(MailChimp_Api_Locales::simple() as $locale_key => $local_value) {
echo '<option value="' . esc_attr( $locale_key ) . '" ' . selected($locale_key === $selected_locale, true, false ) . '>' . esc_html( $local_value ) . '</option>';
}
?>
</select>
<span><?php esc_attr_e('Locale', $this->plugin_name); ?></span>
</label>
</fieldset>
<fieldset>
<legend class="screen-reader-text">
<span>Currency Code</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-store-currency-code-label">
<select name="<?php echo $this->plugin_name; ?>[store_currency_code]" style="width:30%" required>
<?php
$selected_currency_code = isset($options['store_currency_code']) && !empty($options['store_currency_code']) ? $options['store_currency_code'] : 'USD';
foreach (MailChimp_WooCommerce_CurrencyCodes::lists() as $key => $value ) {
echo '<option value="' . esc_attr( $key ) . '" ' . selected($key === $selected_currency_code, true, false ) . '>' . esc_html( $value ) . '</option>';
}
?>
</select>
<span><?php esc_attr_e('Currency', $this->plugin_name); ?></span>
</label>
</fieldset>
<fieldset>
<legend class="screen-reader-text">
<span>Timezone</span>
</legend>
<label for="<?php echo $this->plugin_name; ?>-store-timezone-label">
<select name="<?php echo $this->plugin_name; ?>[store_timezone]" style="width:30%" required>
<?php $selected_timezone = isset($options['store_timezone']) && !empty($options['store_timezone']) ? $options['store_timezone'] : 'America/New_York'; ?>
<?php
foreach(mailchimp_get_timezone_list() as $t) {
echo '<option value="' . esc_attr( $t['zone'] ) . '" ' . selected($t['zone'] === $selected_timezone, true, false ) . '>' . esc_html( $t['diff_from_GMT'] . ' - ' . $t['zone'] ) . '</option>';
}
?>
</select>
<span><?php esc_attr_e('Timezone', $this->plugin_name); ?></span>
</label>
</fieldset>

View File

@@ -0,0 +1,73 @@
<?php
$mailchimp_total_products = $mailchimp_total_orders = 0;
$store_id = mailchimp_get_store_id();
$product_count = mailchimp_get_product_count();
$order_count = mailchimp_get_order_count();
$store_syncing = false;
$last_updated_time = get_option('mailchimp-woocommerce-resource-last-updated');
$sync_started_at = get_option('mailchimp-woocommerce-sync.started_at');
if (!empty($sync_started_at)) {
$sync_started_at = mailchimp_date_local($sync_started_at);
} else {
$sync_started_at = new \DateTime();
}
$sync_completed_at = get_option('mailchimp-woocommerce-sync.completed_at');
if (!empty($sync_completed_at)) {
$sync_completed_at = mailchimp_date_local($sync_completed_at);
} else {
$sync_completed_at = false;
}
$account_name = 'n/a';
$mailchimp_list_name = 'n/a';
if (!empty($last_updated_time)) {
$last_updated_time = mailchimp_date_local($last_updated_time);
}
if (($mailchimp_api = mailchimp_get_api()) && ($store = $mailchimp_api->getStore($store_id))) {
$store_syncing = $store->isSyncing();
if (($account_details = $handler->getAccountDetails())) {
$account_name = $account_details['account_name'];
}
try {
$products = $mailchimp_api->products($store_id, 1, 1);
$mailchimp_total_products = $products['total_items'];
if ($mailchimp_total_products > $product_count) $mailchimp_total_products = $product_count;
} catch (\Exception $e) { $mailchimp_total_products = 0; }
try {
$orders = $mailchimp_api->orders($store_id, 1, 1);
$mailchimp_total_orders = $orders['total_items'];
if ($mailchimp_total_orders > $order_count) $mailchimp_total_orders = $order_count;
} catch (\Exception $e) { $mailchimp_total_orders = 0; }
$mailchimp_list_name = $handler->getListName();
}
?>
<input type="hidden" name="mailchimp_active_settings_tab" value="store_sync"/>
<h2 style="padding-top: 1em;">Sync Information</h2>
<?php if ($sync_started_at): ?>
<p><strong>Started:</strong> <i><?php echo $sync_started_at->format('D, M j, Y g:i A'); ?></i></p>
<?php endif; ?>
<?php if ($sync_completed_at): ?>
<p><strong>Finished:</strong> <i><?php echo $sync_completed_at->format('D, M j, Y g:i A'); ?></i></p>
<?php endif; ?>
<?php if ($last_updated_time): ?>
<p><strong>Last Updated:</strong> <i><?php echo $last_updated_time->format('D, M j, Y g:i A'); ?></i></p>
<?php endif; ?>
<p><strong>Account Connected:</strong> <?php echo $account_name; ?></p>
<p><strong>List Connected:</strong> <?php echo $mailchimp_list_name; ?></p>
<p><strong>Products Synced:</strong> <?php echo $mailchimp_total_products; ?></p>
<p><strong>Orders Synced:</strong> <?php echo $mailchimp_total_orders; ?></p>
<?php if($mailchimp_api && (!$store_syncing || isset($_GET['resync']) && $_GET['resync'] === '1')): ?>
<h2 style="padding-top: 1em;">Advanced</h2>
<p>
You can resync your list at any time without losing any of your e-commerce data.
</p>
<?php submit_button('Resync', 'primary','submit', TRUE); ?>
<?php endif; ?>

View File

@@ -0,0 +1,668 @@
<?php
// If this file is called directly, abort.
if (!defined( 'WPINC')) {
die;
}
$mailchimp_woocommerce_spl_autoloader = true;
spl_autoload_register(function($class) {
$classes = array(
// includes root
'MailChimp_Service' => 'includes/class-mailchimp-woocommerce-service.php',
'MailChimp_WooCommerce_Options' => 'includes/class-mailchimp-woocommerce-options.php',
'MailChimp_Newsletter' => 'includes/class-mailchimp-woocommerce-newsletter.php',
'MailChimp_WooCommerce_Loader' => 'includes/class-mailchimp-woocommerce-loader.php',
'MailChimp_WooCommerce_i18n' => 'includes/class-mailchimp-woocommerce-i18n.php',
'MailChimp_WooCommerce_Deactivator' => 'includes/class-mailchimp-woocommerce-deactivator.php',
'MailChimp_WooCommerce_Activator' => 'includes/class-mailchimp-woocommerce-activator.php',
'MailChimp_WooCommerce' => 'includes/class-mailchimp-woocommerce.php',
// includes/api/assets
'MailChimp_WooCommerce_Address' => 'includes/api/assets/class-mailchimp-address.php',
'MailChimp_WooCommerce_Cart' => 'includes/api/assets/class-mailchimp-cart.php',
'MailChimp_WooCommerce_Customer' => 'includes/api/assets/class-mailchimp-customer.php',
'MailChimp_WooCommerce_LineItem' => 'includes/api/assets/class-mailchimp-line-item.php',
'MailChimp_WooCommerce_Order' => 'includes/api/assets/class-mailchimp-order.php',
'MailChimp_WooCommerce_Product' => 'includes/api/assets/class-mailchimp-product.php',
'MailChimp_WooCommerce_ProductVariation' => 'includes/api/assets/class-mailchimp-product-variation.php',
'MailChimp_WooCommerce_PromoCode' => 'includes/api/assets/class-mailchimp-promo-code.php',
'MailChimp_WooCommerce_PromoRule' => 'includes/api/assets/class-mailchimp-promo-rule.php',
'MailChimp_WooCommerce_Store' => 'includes/api/assets/class-mailchimp-store.php',
// includes/api/errors
'MailChimp_WooCommerce_Error' => 'includes/api/errors/class-mailchimp-error.php',
'MailChimp_WooCommerce_ServerError' => 'includes/api/errors/class-mailchimp-server-error.php',
// includes/api/helpers
'MailChimp_WooCommerce_CurrencyCodes' => 'includes/api/helpers/class-mailchimp-woocommerce-api-currency-codes.php',
'MailChimp_Api_Locales' => 'includes/api/helpers/class-mailchimp-woocommerce-api-locales.php',
// includes/api
'MailChimp_WooCommerce_MailChimpApi' => 'includes/api/class-mailchimp-api.php',
'MailChimp_WooCommerce_Api' => 'includes/api/class-mailchimp-woocommerce-api.php',
'MailChimp_WooCommerce_CreateListSubmission' => 'includes/api/class-mailchimp-woocommerce-create-list-submission.php',
'MailChimp_WooCommerce_Transform_Coupons' => 'includes/api/class-mailchimp-woocommerce-transform-coupons.php',
'MailChimp_WooCommerce_Transform_Orders' => 'includes/api/class-mailchimp-woocommerce-transform-orders-wc3.php',
'MailChimp_WooCommerce_Transform_Products' => 'includes/api/class-mailchimp-woocommerce-transform-products.php',
// includes/processes
'MailChimp_WooCommerce_Abstract_Sync' => 'includes/processes/class-mailchimp-woocommerce-abstract-sync.php',
'MailChimp_WooCommerce_Cart_Update' => 'includes/processes/class-mailchimp-woocommerce-cart-update.php',
'MailChimp_WooCommerce_Process_Coupons' => 'includes/processes/class-mailchimp-woocommerce-process-coupons.php',
'MailChimp_WooCommerce_Process_Orders' => 'includes/processes/class-mailchimp-woocommerce-process-orders.php',
'MailChimp_WooCommerce_Process_Products' => 'includes/processes/class-mailchimp-woocommerce-process-products.php',
'MailChimp_WooCommerce_SingleCoupon' => 'includes/processes/class-mailchimp-woocommerce-single-coupon.php',
'MailChimp_WooCommerce_Single_Order' => 'includes/processes/class-mailchimp-woocommerce-single-order.php',
'MailChimp_WooCommerce_Single_Product' => 'includes/processes/class-mailchimp-woocommerce-single-product.php',
'MailChimp_WooCommerce_User_Submit' => 'includes/processes/class-mailchimp-woocommerce-user-submit.php',
'MailChimp_WooCommerce_Public' => 'public/class-mailchimp-woocommerce-public.php',
'MailChimp_WooCommerce_Admin' => 'admin/class-mailchimp-woocommerce-admin.php',
'WP_Job' => 'includes/vendor/queue/classes/wp-job.php',
'WP_Queue' => 'includes/vendor/queue/classes/wp-queue.php',
'WP_Http_Worker' => 'includes/vendor/queue/classes/worker/wp-http-worker.php',
'WP_Worker' => 'includes/vendor/queue/classes/worker/wp-worker.php',
'Queue_Command' => 'includes/vendor/queue/classes/cli/queue-command.php',
);
// if the file exists, require it
$path = plugin_dir_path( __FILE__ );
if (array_key_exists($class, $classes) && file_exists($path.$classes[$class])) {
require $path.$classes[$class];
}
});
/**
* @return object
*/
function mailchimp_environment_variables() {
global $wp_version;
$o = get_option('mailchimp-woocommerce', false);
return (object) array(
'repo' => 'master',
'environment' => 'production',
'version' => '2.1.7',
'php_version' => phpversion(),
'wp_version' => (empty($wp_version) ? 'Unknown' : $wp_version),
'wc_version' => class_exists('WC') ? WC()->version : null,
'logging' => ($o && is_array($o) && isset($o['mailchimp_logging'])) ? $o['mailchimp_logging'] : 'standard',
);
}
// Add WP CLI commands
if (defined( 'WP_CLI' ) && WP_CLI) {
try {
/**
* Service push to MailChimp
*
* <type>
* : product_sync order_sync order product
*/
function mailchimp_cli_push_command( $args, $assoc_args ) {
if (is_array($args) && isset($args[0])) {
switch($args[0]) {
case 'product_sync':
wp_queue(new MailChimp_WooCommerce_Process_Products());
WP_CLI::success("queued up the product sync!");
break;
case 'order_sync':
wp_queue(new MailChimp_WooCommerce_Process_Orders());
WP_CLI::success("queued up the order sync!");
break;
case 'order':
if (!isset($args[1])) {
wp_die('You must specify an order id as the 2nd parameter.');
}
wp_queue(new MailChimp_WooCommerce_Single_Order($args[1]));
WP_CLI::success("queued up the order {$args[1]}!");
break;
case 'product':
if (!isset($args[1])) {
wp_die('You must specify a product id as the 2nd parameter.');
}
wp_queue(new MailChimp_WooCommerce_Single_Product($args[1]));
WP_CLI::success("queued up the product {$args[1]}!");
break;
}
}
};
WP_CLI::add_command( 'mailchimp_push', 'mailchimp_cli_push_command');
WP_CLI::add_command( 'queue', 'Queue_Command' );
} catch (\Exception $e) {}
}
if (!function_exists( 'wp_queue')) {
/**
* WP queue.
*
* @param WP_Job $job
* @param int $delay
*/
function wp_queue( WP_Job $job, $delay = 0 ) {
global $wp_queue;
if (empty($wp_queue)) {
$wp_queue = new WP_Queue();
}
$wp_queue->push( $job, $delay );
do_action( 'wp_queue_job_pushed', $job );
}
}
/**
* @return bool
*/
function mailchimp_should_init_queue() {
return mailchimp_detect_admin_ajax() && mailchimp_is_configured() && !mailchimp_running_in_console() && !mailchimp_http_worker_is_running();
}
/**
* @return bool
*/
function mailchimp_is_configured() {
return (bool) (mailchimp_get_api_key() && mailchimp_get_list_id());
}
/**
* @return bool|int
*/
function mailchimp_get_api_key() {
return mailchimp_get_option('mailchimp_api_key', false);
}
/**
* @return bool|int
*/
function mailchimp_get_list_id() {
return mailchimp_get_option('mailchimp_list', false);
}
/**
* @return string
*/
function mailchimp_get_store_id() {
$store_id = mailchimp_get_data('store_id', false);
if (empty($store_id)) {
mailchimp_set_data('store_id', $store_id = uniqid(), 'yes');
}
return $store_id;
}
/**
* @return bool|MailChimp_WooCommerce_MailChimpApi
*/
function mailchimp_get_api() {
if (($key = mailchimp_get_api_key())) {
return new MailChimp_WooCommerce_MailChimpApi($key);
}
return false;
}
/**
* @param $key
* @param null $default
* @return null
*/
function mailchimp_get_option($key, $default = null) {
$options = get_option('mailchimp-woocommerce');
if (!is_array($options)) {
return $default;
}
if (!array_key_exists($key, $options)) {
return $default;
}
return $options[$key];
}
/**
* @param $key
* @param null $default
* @return mixed
*/
function mailchimp_get_data($key, $default = null) {
return get_option('mailchimp-woocommerce-'.$key, $default);
}
/**
* @param $key
* @param $value
* @param string $autoload
* @return bool
*/
function mailchimp_set_data($key, $value, $autoload = 'yes') {
return update_option('mailchimp-woocommerce-'.$key, $value, $autoload);
}
/**
* @param $date
* @return DateTime
*/
function mailchimp_date_utc($date) {
$timezone = wc_timezone_string();
if (is_numeric($date)) {
$stamp = $date;
$date = new \DateTime('now', new DateTimeZone($timezone));
$date->setTimestamp($stamp);
} else {
$date = new \DateTime($date, new DateTimeZone($timezone));
}
$date->setTimezone(new DateTimeZone('UTC'));
return $date;
}
/**
* @param $date
* @return DateTime
*/
function mailchimp_date_local($date) {
$timezone = mailchimp_get_option('store_timezone', 'America/New_York');
if (is_numeric($date)) {
$stamp = $date;
$date = new \DateTime('now', new DateTimeZone('UTC'));
$date->setTimestamp($stamp);
} else {
$date = new \DateTime($date, new DateTimeZone('UTC'));
}
$date->setTimezone(new DateTimeZone($timezone));
return $date;
}
/**
* @param array $data
* @return mixed
*/
function mailchimp_array_remove_empty($data) {
if (empty($data) || !is_array($data)) {
return array();
}
foreach ($data as $key => $value) {
if ($value === null || $value === '' || (is_array($value) && empty($value))) {
unset($data[$key]);
}
}
return $data;
}
/**
* @return array
*/
function mailchimp_get_timezone_list() {
$zones_array = array();
$timestamp = time();
$current = date_default_timezone_get();
foreach(timezone_identifiers_list() as $key => $zone) {
date_default_timezone_set($zone);
$zones_array[$key]['zone'] = $zone;
$zones_array[$key]['diff_from_GMT'] = 'UTC/GMT ' . date('P', $timestamp);
}
date_default_timezone_set($current);
return $zones_array;
}
/**
* @return bool
*/
function mailchimp_check_woocommerce_plugin_status()
{
// if you are using a custom folder name other than woocommerce just define the constant to TRUE
if (defined("RUNNING_CUSTOM_WOOCOMMERCE") && RUNNING_CUSTOM_WOOCOMMERCE === true) {
return true;
}
// it the plugin is active, we're good.
if (in_array('woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option('active_plugins')))) {
return true;
}
// lets check for network activation woo installs now too.
if (function_exists('is_plugin_active_for_network')) {
return is_plugin_active_for_network( 'woocommerce/woocommerce.php');
}
return false;
}
/**
* Get all the registered image sizes along with their dimensions
*
* @global array $_wp_additional_image_sizes
*
* @link http://core.trac.wordpress.org/ticket/18947 Reference ticket
*
* @return array $image_sizes The image sizes
*/
function mailchimp_woocommerce_get_all_image_sizes() {
global $_wp_additional_image_sizes;
$image_sizes = array();
$default_image_sizes = get_intermediate_image_sizes();
foreach ($default_image_sizes as $size) {
$image_sizes[$size]['width'] = intval( get_option("{$size}_size_w"));
$image_sizes[$size]['height'] = intval( get_option("{$size}_size_h"));
$image_sizes[$size]['crop'] = get_option("{$size}_crop") ? get_option("{$size}_crop") : false;
}
if (isset($_wp_additional_image_sizes) && count($_wp_additional_image_sizes)) {
$image_sizes = array_merge( $image_sizes, $_wp_additional_image_sizes );
}
return $image_sizes;
}
/**
* @return array
*/
function mailchimp_woocommerce_get_all_image_sizes_list() {
$response = array();
foreach (mailchimp_woocommerce_get_all_image_sizes() as $key => $data) {
$label = ucwords(str_replace('_', ' ', $key));
$response[$key] = "{$label} ({$data['width']} x {$data['height']})";
}
return $response;
}
/**
* The code that runs during plugin activation.
* This action is documented in includes/class-mailchimp-woocommerce-activator.php
*/
function activate_mailchimp_woocommerce() {
// if we don't have woocommerce we need to display a horrible error message before the plugin is installed.
if (!mailchimp_check_woocommerce_plugin_status()) {
// Deactivate the plugin
deactivate_plugins(__FILE__);
$error_message = __('The MailChimp For WooCommerce plugin requires the <a href="http://wordpress.org/extend/plugins/woocommerce/">WooCommerce</a> plugin to be active!', 'woocommerce');
wp_die($error_message);
}
MailChimp_WooCommerce_Activator::activate();
}
/**
* Create the queue tables
*/
function install_mailchimp_queue() {
MailChimp_WooCommerce_Activator::create_queue_tables();
}
/**
* The code that runs during plugin deactivation.
* This action is documented in includes/class-mailchimp-woocommerce-deactivator.php
*/
function deactivate_mailchimp_woocommerce() {
MailChimp_WooCommerce_Deactivator::deactivate();
}
/**
* @param $action
* @param $message
* @param null $data
*/
function mailchimp_debug($action, $message, $data = null) {
if (mailchimp_environment_variables()->logging === 'debug' && function_exists('wc_get_logger')) {
if (is_array($data) && !empty($data)) $message .= " :: ".wc_print_r($data, true);
wc_get_logger()->debug("{$action} :: {$message}", array('source' => 'mailchimp_woocommerce'));
}
}
/**
* @param $action
* @param $message
* @param array $data
* @return array|WP_Error
*/
function mailchimp_log($action, $message, $data = array()) {
if (mailchimp_environment_variables()->logging !== 'none' && function_exists('wc_get_logger')) {
if (is_array($data) && !empty($data)) $message .= " :: ".wc_print_r($data, true);
wc_get_logger()->notice("{$action} :: {$message}", array('source' => 'mailchimp_woocommerce'));
}
}
/**
* @param $action
* @param $message
* @param array $data
* @return array|WP_Error
*/
function mailchimp_error($action, $message, $data = array()) {
if (mailchimp_environment_variables()->logging !== 'none' && function_exists('wc_get_logger')) {
if ($message instanceof \Exception) $message = mailchimp_error_trace($message);
if (is_array($data) && !empty($data)) $message .= " :: ".wc_print_r($data, true);
wc_get_logger()->error("{$action} :: {$message}", array('source' => 'mailchimp_woocommerce'));
}
}
/**
* @param Exception $e
* @param string $wrap
* @return string
*/
function mailchimp_error_trace(\Exception $e, $wrap = "") {
$error = "{$e->getMessage()} on {$e->getLine()} in {$e->getFile()}";
if (empty($wrap)) return $error;
return "{$wrap} :: {$error}";
}
/**
* Determine if a given string contains a given substring.
*
* @param string $haystack
* @param string|array $needles
* @return bool
*/
function mailchimp_string_contains($haystack, $needles) {
foreach ((array) $needles as $needle) {
if ($needle != '' && mb_strpos($haystack, $needle) !== false) {
return true;
}
}
return false;
}
/**
* @return int
*/
function mailchimp_get_product_count() {
$posts = mailchimp_count_posts('product');
unset($posts['auto-draft'], $posts['trash']);
$total = 0;
foreach ($posts as $status => $count) {
$total += $count;
}
return $total;
}
/**
* @return int
*/
function mailchimp_get_order_count() {
$posts = mailchimp_count_posts('shop_order');
unset($posts['auto-draft'], $posts['trash']);
$total = 0;
foreach ($posts as $status => $count) {
$total += $count;
}
return $total;
}
/**
* @param $type
* @return array|null|object
*/
function mailchimp_count_posts($type) {
global $wpdb;
$query = "SELECT post_status, COUNT( * ) AS num_posts FROM {$wpdb->posts} WHERE post_type = %s GROUP BY post_status";
$posts = $wpdb->get_results( $wpdb->prepare($query, $type));
$response = array();
foreach ($posts as $post) {
$response[$post->post_status] = $post->num_posts;
}
return $response;
}
/**
* @return bool
*/
function mailchimp_update_connected_site_script() {
// pull the store ID
$store_id = mailchimp_get_store_id();
// if the api is configured
if ($store_id && ($api = mailchimp_get_api())) {
// if we have a store
if (($store = $api->getStore($store_id))) {
// handle the coupon sync if we don't have a flag that says otherwise.
$job = new MailChimp_WooCommerce_Process_Coupons();
if ($job->getData('sync.coupons.completed_at', false) === false) {
wp_queue($job);
}
// see if we have a connected site script url/fragment
$url = $store->getConnectedSiteScriptUrl();
$fragment = $store->getConnectedSiteScriptFragment();
// if it's not empty we need to set the values
if ($url && $fragment) {
// update the options for script_url and script_fragment
update_option('mailchimp-woocommerce-script_url', $url);
update_option('mailchimp-woocommerce-script_fragment', $fragment);
// check to see if the site is connected
if (!$api->checkConnectedSite($store_id)) {
// if it's not, connect it now.
$api->connectSite($store_id);
}
return true;
}
}
}
return false;
}
/**
* @return bool
*/
function mailchimp_detect_admin_ajax() {
if (defined('DOING_CRON') && DOING_CRON) return true;
if (!is_admin()) return false;
if (!defined('DOING_AJAX')) return false;
return DOING_AJAX;
}
/**
* @return string|false
*/
function mailchimp_get_connected_site_script_url() {
return get_option('mailchimp-woocommerce-script_url', false);
}
/**
* @return string|false
*/
function mailchimp_get_connected_site_script_fragment() {
return get_option('mailchimp-woocommerce-script_fragment', false);
}
/**
* @return bool
*/
function mailchimp_running_in_console() {
return (bool) (defined( 'DISABLE_WP_HTTP_WORKER' ) && true === DISABLE_WP_HTTP_WORKER);
}
/**
* @return bool
*/
function mailchimp_http_worker_is_running() {
return (bool) get_site_transient('http_worker_lock');
}
/**
* @return array|WP_Error
*/
function mailchimp_call_http_worker_manually() {
$action = 'http_worker';
$query_args = apply_filters('http_worker_query_args', array(
'action' => $action,
'nonce' => wp_create_nonce($action),
));
$query_url = apply_filters('http_worker_query_url', admin_url('admin-ajax.php'));
$post_args = apply_filters('http_worker_post_args', array(
'timeout' => 0.01,
'blocking' => false,
'cookies' => $_COOKIE,
'sslverify' => apply_filters('https_local_ssl_verify', false),
));
$url = add_query_arg($query_args, $query_url);
return wp_remote_post(esc_url_raw($url), $post_args);
}
function mailchimp_flush_queue_tables() {
try {
/** @var \ */
global $wpdb;
$wpdb->query($wpdb->prepare("TRUNCATE `{$wpdb->prefix}queue`", array()));
$wpdb->query($wpdb->prepare("TRUNCATE `{$wpdb->prefix}failed_jobs`", array()));
$wpdb->query($wpdb->prepare("TRUNCATE `{$wpdb->prefix}mailchimp_carts`", array()));
} catch (\Exception $e) {}
}
function mailchimp_flush_sync_pointers() {
// clean up the initial sync pointers
foreach (array('orders', 'products', 'coupons') as $resource_type) {
delete_option("mailchimp-woocommerce-sync.{$resource_type}.started_at");
delete_option("mailchimp-woocommerce-sync.{$resource_type}.completed_at");
delete_option("mailchimp-woocommerce-sync.{$resource_type}.current_page");
}
}
/**
* To be used when running clean up for uninstalls or re-installs.
*/
function mailchimp_clean_database() {
mailchimp_flush_queue_tables();
// clean up the initial sync pointers
mailchimp_flush_sync_pointers();
delete_option('mailchimp-woocommerce');
delete_option('mailchimp-woocommerce-store_id');
delete_option('mailchimp-woocommerce-sync.syncing');
delete_option('mailchimp-woocommerce-sync.started_at');
delete_option('mailchimp-woocommerce-sync.completed_at');
delete_option('mailchimp-woocommerce-validation.api.ping');
delete_option('mailchimp-woocommerce-cached-api-lists');
delete_option('mailchimp-woocommerce-cached-api-ping-check');
delete_option('mailchimp-woocommerce-errors.store_info');
}
function run_mailchimp_woocommerce() {
$env = mailchimp_environment_variables();
$plugin = new MailChimp_WooCommerce($env->environment, $env->version);
$plugin->run();
}
function mailchimp_woocommerce_add_meta_tags() {
echo '<meta name="referrer" content="always"/>';
}
function mailchimp_on_all_plugins_loaded() {
if (mailchimp_check_woocommerce_plugin_status()) {
add_action('wp_head', 'mailchimp_woocommerce_add_meta_tags');
run_mailchimp_woocommerce();
}
}

View File

@@ -0,0 +1,337 @@
<?php
/**
* Created by Vextras.
*
* Name: Ryan Hungate
* Email: ryan@vextras.com
* Date: 3/8/16
* Time: 2:22 PM
*/
class MailChimp_WooCommerce_Address
{
protected $type;
protected $name;
protected $address1;
protected $address2;
protected $city;
protected $province;
protected $province_code;
protected $postal_code;
protected $country;
protected $country_code;
protected $longitude;
protected $latitude;
protected $phone;
protected $company;
/**
* @return array
*/
public function getValidation()
{
return array(
'address1' => 'string',
'address2' => 'string',
'city' => 'string',
'province' => 'string',
'province_code' => 'string|digits:2',
'postal_code' => 'string',
'country' => 'string',
'country_code' => 'string|digits:2',
'latitude' => 'numeric',
'longitude' => 'numeric',
);
}
/**
* @return mixed
*/
public function getName()
{
return $this->name;
}
/**
* @param mixed $name
* @return MailChimp_WooCommerce_Address
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return mixed
*/
public function getAddress1()
{
return $this->address1;
}
/**
* @param mixed $address1
* @return MailChimp_WooCommerce_Address
*/
public function setAddress1($address1)
{
$this->address1 = $address1;
return $this;
}
/**
* @return mixed
*/
public function getAddress2()
{
return $this->address2;
}
/**
* @param mixed $address2
* @return MailChimp_WooCommerce_Address
*/
public function setAddress2($address2)
{
$this->address2 = $address2;
return $this;
}
/**
* @return mixed
*/
public function getCity()
{
return $this->city;
}
/**
* @param mixed $city
* @return MailChimp_WooCommerce_Address
*/
public function setCity($city)
{
$this->city = $city;
return $this;
}
/**
* @return mixed
*/
public function getProvince()
{
return $this->province;
}
/**
* @param mixed $province
* @return MailChimp_WooCommerce_Address
*/
public function setProvince($province)
{
$this->province = $province;
return $this;
}
/**
* @return mixed
*/
public function getProvinceCode()
{
return $this->province_code;
}
/**
* @param mixed $province_code
* @return MailChimp_WooCommerce_Address
*/
public function setProvinceCode($province_code)
{
$this->province_code = $province_code;
return $this;
}
/**
* @return mixed
*/
public function getPostalCode()
{
return $this->postal_code;
}
/**
* @param mixed $postal_code
* @return MailChimp_WooCommerce_Address
*/
public function setPostalCode($postal_code)
{
$this->postal_code = $postal_code;
return $this;
}
/**
* @return mixed
*/
public function getCountry()
{
return $this->country;
}
/**
* @param mixed $country
* @return MailChimp_WooCommerce_Address
*/
public function setCountry($country)
{
$this->country = $country;
return $this;
}
/**
* @return mixed
*/
public function getCountryCode()
{
return $this->country_code;
}
/**
* @param mixed $country_code
* @return MailChimp_WooCommerce_Address
*/
public function setCountryCode($country_code)
{
$this->country_code = $country_code;
return $this;
}
/**
* @return mixed
*/
public function getLongitude()
{
return $this->longitude;
}
/**
* @param mixed $longitude
* @return MailChimp_WooCommerce_Address
*/
public function setLongitude($longitude)
{
$this->longitude = $longitude;
return $this;
}
/**
* @return mixed
*/
public function getLatitude()
{
return $this->latitude;
}
/**
* @param mixed $latitude
* @return MailChimp_WooCommerce_Address
*/
public function setLatitude($latitude)
{
$this->latitude = $latitude;
return $this;
}
/**
* @return mixed
*/
public function getPhone()
{
return $this->phone;
}
/**
* @param mixed $phone
* @return MailChimp_WooCommerce_Address
*/
public function setPhone($phone)
{
$this->phone = $phone;
return $this;
}
/**
* @return mixed
*/
public function getCompany()
{
return $this->company;
}
/**
* @param mixed $company
* @return MailChimp_WooCommerce_Address
*/
public function setCompany($company)
{
$this->company = $company;
return $this;
}
/**
* @return array
*/
public function toArray()
{
return mailchimp_array_remove_empty(array(
'name' => (string) $this->name,
'address1' => (string) $this->address1,
'address2' => (string) $this->address2,
'city' => (string) $this->city,
'province' => (string) $this->province,
'province_code' => (string) $this->province_code,
'postal_code' => (string) $this->postal_code,
'country' => (string) $this->country,
'country_code' => (string) $this->country_code,
'longitude' => ($this->longitude ? (int) $this->longitude : null),
'latitude' => ($this->latitude ? (int) $this->latitude : null),
'phone' => (string) $this->phone,
'company' => (string) $this->company,
));
}
/**
* @param array $data
* @return MailChimp_WooCommerce_Address
*/
public function fromArray(array $data)
{
$singles = array(
'name', 'address1', 'address2', 'city',
'province', 'province_code', 'postal_code',
'country', 'country_code', 'longitude',
'phone', 'company',
);
foreach ($singles as $key) {
if (array_key_exists($key, $data)) {
$this->$key = $data[$key];
}
}
return $this;
}
}

View File

@@ -0,0 +1,277 @@
<?php
/**
* Created by Vextras.
*
* Name: Ryan Hungate
* Email: ryan@vextras.com
* Date: 7/15/16
* Time: 1:26 PM
*/
class MailChimp_WooCommerce_Cart
{
protected $store_id;
protected $id;
protected $customer;
protected $campaign_id;
protected $checkout_url;
protected $currency_code;
protected $order_total;
protected $tax_total;
protected $lines = array();
/**
* @param $unique_id
* @return $this
*/
public function setId($unique_id)
{
$this->id = $unique_id;
return $this;
}
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* @param $store_id
* @return $this
*/
public function setStoreID($store_id)
{
$this->store_id = $store_id;
return $this;
}
/**
* @return mixed
*/
public function getStoreID()
{
if (empty($this->store_id)) {
$this->store_id = mailchimp_get_store_id();
}
return $this->store_id;
}
/**
* @param MailChimp_WooCommerce_Customer $customer
* @return $this
*/
public function setCustomer(MailChimp_WooCommerce_Customer $customer)
{
$this->customer = $customer;
return $this;
}
/**
* @return MailChimp_WooCommerce_Customer
*/
public function getCustomer()
{
if (empty($this->customer)) {
$this->customer = new MailChimp_WooCommerce_Customer();
}
return $this->customer;
}
/**
* @param $id
* @return $this
*/
public function setCampaignID($id)
{
$this->campaign_id = $id;
return $this;
}
/**
* @return mixed
*/
public function getCampaignID()
{
return $this->campaign_id;
}
/**
* @param $url
* @return $this
*/
public function setCheckoutUrl($url)
{
$this->checkout_url = $url;
return $this;
}
/**
* @return string
*/
public function getCheckoutURL()
{
if (empty($this->checkout_url)) {
$this->checkout_url = wc_get_checkout_url();
}
return $this->checkout_url;
}
/**
* @param $code
* @return $this
*/
public function setCurrencyCode($code)
{
$this->currency_code = $code;
return $this;
}
/**
* @return string
*/
public function getCurrencyCode()
{
if (empty($this->currency_code)) {
$options = get_option('mailchimp-woocommerce', array());
$this->currency_code = isset($options['store_currency_code']) ? $options['store_currency_code'] : 'USD';
}
return $this->currency_code;
}
/**
* @param $total
* @return $this
*/
public function setOrderTotal($total)
{
$this->order_total = $total;
return $this;
}
/**
* @return float
*/
public function getOrderTotal()
{
return $this->order_total;
}
/**
* @param $total
* @return $this
*/
public function setTaxTotal($total)
{
$this->tax_total = $total;
return $this;
}
/**
* @return float
*/
public function getTaxTotal()
{
return $this->tax_total;
}
/**
* @param MailChimp_WooCommerce_LineItem $item
* @return $this
*/
public function addItem(MailChimp_WooCommerce_LineItem $item)
{
$this->lines[] = $item;
return $this;
}
/**
* @return array
*/
public function items()
{
return $this->lines;
}
/**
* @return mixed
*/
public function toArray()
{
return mailchimp_array_remove_empty(array(
'id' => (string) $this->getId(),
'customer' => $this->getCustomer()->toArray(),
'campaign_id' => (string) $this->getCampaignID(),
'checkout_url' => (string) $this->getCheckoutURL(),
'currency_code' => (string) $this->getCurrencyCode(),
'order_total' => floatval($this->getOrderTotal()),
'tax_total' => $this->getTaxTotal() > 0 ? floatval($this->getTaxTotal()) : null,
'lines' => array_map(function($item) {
return $item->toArray();
}, $this->items()),
));
}
/**
* @return array
*/
public function toArrayForUpdate()
{
return mailchimp_array_remove_empty(array(
'campaign_id' => (string) $this->getCampaignID(),
'checkout_url' => (string) $this->getCheckoutURL(),
'currency_code' => (string) $this->getCurrencyCode(),
'order_total' => $this->getOrderTotal(),
'tax_total' => ($this->getTaxTotal() > 0 ? $this->getTaxTotal() : null),
'lines' => array_map(function($item) {
return $item->toArray();
}, $this->items()),
));
}
/**
* @param array $data
* @return MailChimp_WooCommerce_Cart
*/
public function fromArray(array $data)
{
$singles = array(
'store_id', 'id', 'campaign_id', 'checkout_url',
'currency_code', 'order_total', 'tax_total',
);
foreach ($singles as $key) {
if (array_key_exists($key, $data)) {
$this->$key = $data[$key];
}
}
if (array_key_exists('customer', $data) && is_array($data['customer'])) {
$customer = new MailChimp_WooCommerce_Customer();
$this->customer = $customer->fromArray($data['customer']);
}
if (array_key_exists('lines', $data) && is_array($data['lines'])) {
foreach ($data['lines'] as $line_item) {
$item = new MailChimp_WooCommerce_LineItem();
$this->lines[] = $item->fromArray($line_item);
}
}
return $this;
}
}

Some files were not shown because too many files have changed in this diff Show More