initial commit

This commit is contained in:
2024-04-29 13:12:44 +05:45
commit 34887303c5
19300 changed files with 5268802 additions and 0 deletions

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,418 @@
#wpp-message { display: none; }
/*
* Navigation
*/
#wpp-menu {
float: none;
clear: both;
}
#wpp-menu ul {
text-align: center;
}
#wpp-menu ul li {
padding: 0 15px 0 10px;
margin: 0 0px;
display: inline;
border-right: #ddd 1px solid;
}
#wpp-menu ul li:first-child {
display: none;
}
#wpp-menu ul li:last-child {
border: none;
}
#wpp-menu ul li a {
}
#wpp-menu ul li.current a {
color: #00a0d2;
text-decoration: none;
}
.wpp-wrapper {
margin: 20px 20px 0 2px;
-webkit-font-smoothing: subpixel-antialiased;
}
.wpp-lightbox {
display: none;
position: fixed;
top: 50%;
left: 50%;
z-index: 999;
margin: -140px 0 0 -140px;
padding: 15px;
width: 250px;
max-height: 280px;
background: #fff;
-webkit-box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.25);
-moz-box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.25);
box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.25);
}
.wpp-lightbox-tabs {
margin: 0 0 25px 0;
padding: 0 0 0 0;
text-align: center;
}
.wpp-lightbox-tabs li {
display: inline;
list-style: none;
margin: 0;
padding: 0 15px 0 10px;
border-right: #ddd 1px solid;
}
.wpp-lightbox-tabs li:first-child {
padding: 0 15px 0 0;
}
.wpp-lightbox-tabs li:last-child {
padding: 0 0 0 10px;
border-right: none;
}
.wpp-lightbox-tabs li.active a {
color: #00a0d2;
text-decoration: none;
}
.wpp-lightbox-tab-content {
overflow: hidden;
display: none;
width: 100%;
}
.wpp-lightbox-tab-content-active {
display: block;
}
.wpp-lightbox form {
display: block;
}
.wpp-lightbox form input[type=text] {
display: block;
margin: 0 0 15px 0;
width: 100%;
box-sizing: border-box;
}
.wpp-section-stats {
position: relative;
}
.wpp-section-stats .dashicons-admin-generic {
position: absolute;
top: 22px;
left: 0;
width: 21px;
height: 21px;
transition: transform 0.3s;
}
.wpp-section-stats .dashicons-admin-generic:hover {
transform: rotate(360deg);
}
.wpp-header {
overflow: hidden;
display: block;
padding: 25px 0 0 0;
}
.wpp-header h2,
.wpp-header h3,
.wpp-header h4,
.wpp-header h5 {
text-align: center;
}
.wpp-header h2 {
margin: 0 0 5px 0;
font-size: 1.0em;
font-weight: 400;
line-height: 1.0em;
}
.wpp-header h3 {
margin: 0 0 30px 0;
font-size: 3.5em;
letter-spacing: -1px;
line-height: 1.0em;
}
.wpp-header h4,
#wpp-chart-wrapper h4 {
margin: 0 0 8px 0;
font-size: 2.0em;
font-weight: 100;
line-height: 1.0em;
letter-spacing: -2px;
}
.wpp-header h5,
#wpp-chart-wrapper h5 {
margin: 0 0 0 0;
font-size: 0.9em;
font-weight: 300;
line-height: 0.9em;
}
.wpp-header-nav {
margin: 55px auto 0px auto;
padding: 0;
text-align: center;
}
.wpp-header-nav li {
display: inline;
list-style: none;
margin: 0 4px;
padding: 0;
}
.wpp-header-nav li a {
display: inline-block;
padding: 6px 12px 7px;
line-height: 1.1em;
}
.wpp-section-stats .wpp-header-nav li a:hover,
.wpp-section-stats .wpp-header-nav li.current a {
color: #00a0d2;
}
.wpp-section-stats .wpp-header-nav li.current a {
text-decoration: none;
}
#wpp-chart-wrapper {
width: 100%;
}
#wpp-chart-wrapper h4,
#wpp-chart-wrapper h5 {
text-align: center;
}
#wpp-chart-wrapper #wpp-chart {
position: relative;
overflow: hidden;
margin: 20px auto 0 auto;
width: 100%;
height: 250px;
}
#wpp-chart-wrapper #wpp-chart p {
margin: 0 10px;
text-align: center;
}
#wpp-chart-wrapper #wpp-chart a:hover {
text-decoration: underline;
}
.wpp-content {
float: none;
clear: both;
width: 100%;
background: #fff;
}
.wpp-content .wpp-tabbed-nav {
overflow: hidden;
margin: 0 0 0 0;
padding: 0;
background: #f9f9f9;
}
.wpp-content .wpp-tabbed-nav li {
display: inline;
float: left;
margin: 0;
padding: 0;
width: 25%;
border-bottom: #f0f0f0 1px solid;
}
.wpp-content .wpp-tabbed-nav li.active {
border-bottom: #bbb 1px solid;
}
.wpp-content .wpp-tabbed-nav li a {
display: block;
padding: 15px;
font-size: 1.2em;
text-align: center;
text-decoration: none;
outline: none;
box-shadow: none;
}
#wpp-listing .wpp-tabbed-nav li a .fa {
display: inline-block;
margin: 0 8px 0 0;
}
.wpp-tab-content {
display: none;
overflow: hidden;
padding: 2% 30px;
width: 100%;
box-sizing: border-box;
}
.wpp-tab-content-active {
display: block;
}
.wpp-tab-content .spinner {
display: block;
visibility: visible;
float: none;
margin-left: auto;
margin-right: auto;
}
.popular-posts-list {
margin: 0 0 0 0;
padding: 0;
list-style: none;
counter-reset: wpp-counter;
font-size: 1rem;
line-height: 1.2;
}
.popular-posts-list li {
counter-increment: wpp-counter;
margin: 0 1% 15px 1%;
padding: 0 0 10px 0;
border-bottom: #f0f0f0 1px solid;
}
.popular-posts-list li::before {
display: inline-block;
width: 20px;
content: counter(wpp-counter) ". ";
}
.popular-posts-list li:last-child,
.popular-posts-list li:last-child {
margin: 0 1%;
border: none;
}
.popular-posts-list li a.wpp-title {
display: inline;
font-size: 1em;
text-decoration: none;
}
.popular-posts-list li:first-child,
.popular-posts-list li:first-child a.wpp-title {
font-size: 1.15em;
}
.popular-posts-list li:nth-child(2),
.popular-posts-list li:nth-child(2) a.wpp-title {
font-size: 1.05em;
}
.popular-posts-list li:first-child a.wpp-title,
.popular-posts-list li:nth-child(2) a.wpp-title,
.popular-posts-list li:nth-child(3) a.wpp-title {
font-weight: bold;
letter-spacing: -1px;
}
.popular-posts-list li a.wpp-title:hover {
text-decoration: underline;
}
.popular-posts-list li span {
color: #bbb;
font-size: 13px;
}
.popular-posts-list li small {
display: none;
font-size: 13px;
}
.popular-posts-list li:hover small {
display: inline;
}
#wpp_params {}
#wpp_params div {
}
#wpp_params div table {
width: 100%;
min-width: 800px;
background: white;
}
#wpp_params div table tr th {
font-size: 1.2em;
text-align: left;
border-bottom: #dedede 1px solid;
}
#wpp_params div table tr th,
#wpp_params div table tr td {
padding: 1em;
}
#wpp_params div table tr td {
position: static !important;
clear: none !important;
display: table-cell !important;
vertical-align: top;
}
.clear {
float:none;
clear:both;
width:100%;
}
/*
* Media Queries
*/
/* Small devices */
@media only screen and (max-width: 480px){
}
/* Tablets & small desktops */
@media only screen and (max-width: 768px) {
.wpp-wrapper {
margin: 20px 12px 0 2px;
}
#wpp-listing .wpp-tabbed-nav li a span:last-child {
display: none;
}
#wpp-listing .wpp-tabbed-nav li a .fa {
margin: 0 0 0 0;
}
}
/* Medium desktops, Large desktops & other devices */
@media only screen and (max-width: 992px) { /* 960 + 16 + 16 */
}

View File

@ -0,0 +1,399 @@
/*! jQuery UI - v1.12.1 - 2017-08-15
* http://jqueryui.com
* Includes: core.css, datepicker.css
* Copyright jQuery Foundation and other contributors; Licensed MIT */
/* Layout helpers
----------------------------------*/
.ui-helper-hidden {
display: none;
}
.ui-helper-hidden-accessible {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
.ui-helper-reset {
margin: 0;
padding: 0;
border: 0;
outline: 0;
line-height: 1.3;
text-decoration: none;
font-size: 100%;
list-style: none;
}
.ui-helper-clearfix:before,
.ui-helper-clearfix:after {
content: "";
display: table;
border-collapse: collapse;
}
.ui-helper-clearfix:after {
clear: both;
}
.ui-helper-zfix {
width: 100%;
height: 100%;
top: 0;
left: 0;
position: absolute;
opacity: 0;
filter:Alpha(Opacity=0); /* support: IE8 */
}
.ui-front {
z-index: 100;
}
/* Interaction Cues
----------------------------------*/
.ui-state-disabled {
cursor: default !important;
pointer-events: none;
}
/* Icons
----------------------------------*/
.ui-icon {
display: inline-block;
vertical-align: middle;
margin-top: -.25em;
position: relative;
text-indent: -99999px;
overflow: hidden;
background-repeat: no-repeat;
}
.ui-widget-icon-block {
left: 50%;
margin-left: -8px;
display: block;
}
/* Misc visuals
----------------------------------*/
/* Overlays */
.ui-widget-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.ui-datepicker {
width: 17em;
padding: .2em .2em 0;
display: none;
}
.ui-datepicker .ui-datepicker-header {
position: relative;
padding: .2em 0;
}
.ui-datepicker .ui-datepicker-prev,
.ui-datepicker .ui-datepicker-next {
position: absolute;
top: 2px;
width: 1.8em;
height: 1.8em;
}
.ui-datepicker .ui-datepicker-prev-hover,
.ui-datepicker .ui-datepicker-next-hover {
top: 1px;
}
.ui-datepicker .ui-datepicker-prev {
left: 2px;
}
.ui-datepicker .ui-datepicker-next {
right: 2px;
}
.ui-datepicker .ui-datepicker-prev-hover {
left: 1px;
}
.ui-datepicker .ui-datepicker-next-hover {
right: 1px;
}
.ui-datepicker .ui-datepicker-prev span,
.ui-datepicker .ui-datepicker-next span {
display: block;
position: absolute;
left: 50%;
margin-left: -8px;
top: 50%;
margin-top: -8px;
}
.ui-datepicker .ui-datepicker-title {
margin: 0 2.3em;
line-height: 1.8em;
text-align: center;
}
.ui-datepicker .ui-datepicker-title select {
font-size: 1em;
margin: 1px 0;
}
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year {
width: 45%;
}
.ui-datepicker table {
width: 100%;
font-size: .9em;
border-collapse: collapse;
margin: 0 0 .4em;
}
.ui-datepicker th {
padding: .7em .3em;
text-align: center;
font-weight: bold;
border: 0;
}
.ui-datepicker td {
border: 0;
padding: 1px;
}
.ui-datepicker td span,
.ui-datepicker td a {
display: block;
padding: .2em;
text-align: right;
text-decoration: none;
}
.ui-datepicker .ui-datepicker-buttonpane {
background-image: none;
margin: .7em 0 0 0;
padding: 0 .2em;
border-left: 0;
border-right: 0;
border-bottom: 0;
}
.ui-datepicker .ui-datepicker-buttonpane button {
float: right;
margin: .5em .2em .4em;
cursor: pointer;
padding: .2em .6em .3em .6em;
width: auto;
overflow: visible;
}
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
float: left;
}
/* with multiple calendars */
.ui-datepicker.ui-datepicker-multi {
width: auto;
}
.ui-datepicker-multi .ui-datepicker-group {
float: left;
}
.ui-datepicker-multi .ui-datepicker-group table {
width: 95%;
margin: 0 auto .4em;
}
.ui-datepicker-multi-2 .ui-datepicker-group {
width: 50%;
}
.ui-datepicker-multi-3 .ui-datepicker-group {
width: 33.3%;
}
.ui-datepicker-multi-4 .ui-datepicker-group {
width: 25%;
}
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
border-left-width: 0;
}
.ui-datepicker-multi .ui-datepicker-buttonpane {
clear: left;
}
.ui-datepicker-row-break {
clear: both;
width: 100%;
font-size: 0;
}
/* RTL support */
.ui-datepicker-rtl {
direction: rtl;
}
.ui-datepicker-rtl .ui-datepicker-prev {
right: 2px;
left: auto;
}
.ui-datepicker-rtl .ui-datepicker-next {
left: 2px;
right: auto;
}
.ui-datepicker-rtl .ui-datepicker-prev:hover {
right: 1px;
left: auto;
}
.ui-datepicker-rtl .ui-datepicker-next:hover {
left: 1px;
right: auto;
}
.ui-datepicker-rtl .ui-datepicker-buttonpane {
clear: right;
}
.ui-datepicker-rtl .ui-datepicker-buttonpane button {
float: left;
}
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
.ui-datepicker-rtl .ui-datepicker-group {
float: right;
}
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
border-right-width: 0;
border-left-width: 1px;
}
/* Icons */
.ui-datepicker .ui-icon {
display: block;
text-indent: -99999px;
overflow: hidden;
background-repeat: no-repeat;
left: .5em;
top: .3em;
}
/*
* Custom datepicker theme for WPP.
*
* @author Hector Cabrera
*/
.ui-datepicker {
background: #fff;
-webkit-box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.25);
-moz-box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.25);
box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.25);
border-radius: 5px;
}
.ui-datepicker-header {}
.ui-datepicker .ui-datepicker-prev {
left: 0;
}
.ui-datepicker .ui-datepicker-next {
right: 0;
}
.ui-datepicker .ui-datepicker-prev span,
.ui-datepicker .ui-datepicker-next span {
display: block;
position: absolute;
/*left: 50%;
margin-left: -8px;
top: 50%;
margin-top: -8px;*/
}
.ui-datepicker-prev:before,
.ui-datepicker-next:after {
position: absolute;
top: 6px;
display: inline-block;
font-family: 'FontAwesome';
line-height: 1.1em;
}
.ui-datepicker-prev:before {
left: 9px;
content: "\f060";
}
.ui-datepicker-next:after {
right: 9px;
content: "\f061";
}
.ui-datepicker .ui-datepicker-prev-hover,
.ui-datepicker .ui-datepicker-next-hover {
top: 2px !important;
}
.ui-datepicker-calendar {}
.ui-datepicker-calendar tr td {
text-align: center;
}
.ui-datepicker-unselectable span {
color: #ccc;
text-align: center !important;
}
.ui-datepicker-calendar tr td a {
text-align: center;
background: #fcfcfc;
}
.ui-datepicker-calendar tr td.date-range-selected a {
color: #fff;
text-shadow: 0 -1px 1px #ba281e, 1px 0 1px #ba281e, 0 1px 1px #ba281e, -1px 0 1px #ba281e;
background: #e14d43;
}
.ui-datepicker-buttonpane {}
.ui-datepicker-buttonpane button {
display: inline-block;
text-decoration: none;
font-size: 13px;
line-height: 26px;
height: 28px;
margin: 0;
padding: 0 10px 1px !important;
border-color: #ccc;
background: #f7f7f7;
vertical-align: top;
cursor: pointer;
border-width: 1px;
border-style: solid;
-webkit-appearance: none;
-webkit-border-radius: 3px;
border-radius: 3px;
white-space: nowrap;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
/* Today button */
.ui-datepicker-buttonpane .ui-datepicker-current {
display: none;
}
.ui-datepicker-buttonpane .ui-priority-primary {
color: #fff;
background: #e14d43;
border-color: #d02c21 #ba281e #ba281e;
-webkit-box-shadow: 0 1px 0 #ba281e;
box-shadow: 0 1px 0 #ba281e;
text-shadow: 0 -1px 1px #ba281e, 1px 0 1px #ba281e, 0 1px 1px #ba281e, -1px 0 1px #ba281e;
}
.ui-datepicker-buttonpane .ui-priority-primary:hover {
background: #e35950;
border-color: #ba281e;
}
.ui-datepicker-buttonpane .ui-priority-secondary {
color: #555;
border-color: #ccc;
background: #f7f7f7;
-webkit-box-shadow: 0 1px 0 #ccc;
box-shadow: 0 1px 0 #ccc;
}

View File

@ -0,0 +1,50 @@
@font-face {
font-family: 'fontello';
src: url('../fonts/fontello.eot?57329479');
src: url('../fonts/fontello.eot?57329479#iefix') format('embedded-opentype'),
url('../fonts/fontello.woff2?57329479') format('woff2'),
url('../fonts/fontello.woff?57329479') format('woff'),
url('../fonts/fontello.ttf?57329479') format('truetype'),
url('../fonts/fontello.svg?57329479#fontello') format('svg');
font-weight: normal;
font-style: normal;
}
[class^="wpp-icon-"]:before, [class*=" icon-"]:before {
font-family: "fontello";
font-style: normal;
font-weight: normal;
speak: none;
display: inline-block;
text-decoration: inherit;
width: 1em;
margin-right: .2em;
text-align: center;
/* opacity: .8; */
/* For safety - reset parent styles, that can break glyph codes*/
font-variant: normal;
text-transform: none;
/* fix buttons height, for twitter bootstrap */
line-height: 1em;
/* Animation center compensation - margins should be symmetric */
/* remove if not needed */
margin-left: .2em;
/* you can be more comfortable with increased icons size */
/* font-size: 120%; */
/* Font smoothing. That was taken from TWBS */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* Uncomment for 3D effect */
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
}
.wpp-icon-eye:before { content: '\e800'; }
.wpp-icon-award:before { content: '\e801'; }
.wpp-icon-comment:before { content: '\f0e5'; }
.wpp-icon-rocket:before { content: '\f135'; }

View File

@ -0,0 +1,81 @@
/*
Wordpress Popular Posts plugin stylesheet
Developed by Hector Cabrera
cabrerahector.com | @cabrerahector
Use the following classes to style your popular posts list as you like.
*/
/* Styles the "Sorry, no data so far" message */
.wpp-no-data {
}
/* UL - Popular Posts container styles */
.wpp-list {
}
/* LI - Post container styles */
.wpp-list li {
overflow: hidden;
float: none;
clear: both;
margin-bottom: 1rem;
}
.wpp-list li:last-of-type {
margin-bottom: 0;
}
/* Styles for the popular post in view */
.wpp-list li.current {
}
/* Thumbnail styles */
.wpp-thumbnail {
display: inline;
float: left;
margin: 0 1rem 0 0;
border: none;
}
/* Title styles */
.wpp-post-title {
}
/* Excerpt styles */
.wpp-excerpt {
}
/* Stats tag styles */
.wpp-meta, .post-stats {
display: block;
font-size: 0.8em;
}
.wpp-meta:empty, .post-stats:empty {
display: none;
}
/* Comments count styles */
.wpp-comments {
}
/* Views count styles */
.wpp-views {
}
/* Author styles */
.wpp-author {
}
/* Post date styles */
.wpp-date {
}
/* Post category styles */
.wpp-category {
}
/* WP-PostRatings styles */
.wpp-rating {
}

View File

@ -0,0 +1,18 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Copyright (C) 2019 by original authors @ fontello.com</metadata>
<defs>
<font id="fontello" horiz-adv-x="1000" >
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
<missing-glyph horiz-adv-x="1000" />
<glyph glyph-name="eye" unicode="&#xe800;" d="M929 314q-85 132-213 197 34-58 34-125 0-103-73-177t-177-73-177 73-73 177q0 67 34 125-128-65-213-197 75-114 187-182t242-68 243 68 186 182z m-402 215q0 11-8 19t-19 7q-70 0-120-50t-50-119q0-11 8-19t19-8 19 8 8 19q0 48 34 82t82 34q11 0 19 8t8 19z m473-215q0-19-11-38-78-129-210-206t-279-77-279 77-210 206q-11 19-11 38t11 39q78 128 210 205t279 78 279-78 210-205q11-20 11-39z" horiz-adv-x="1000" />
<glyph glyph-name="award" unicode="&#xe801;" d="M256 357q-42 91-42 207h-143v-53q0-44 53-91t132-63z m601 154v53h-143q0-116-41-207 79 16 131 63t53 91z m72 71v-71q0-40-24-80t-62-73-97-54-120-25q-23-30-53-53-21-19-29-40t-8-50q0-30 17-51t54-21q42 0 75-25t32-64v-36q0-8-5-13t-13-5h-464q-8 0-13 5t-5 13v36q0 39 33 64t74 25q38 0 55 21t17 51q0 28-8 50t-29 40q-30 23-53 53-64 3-121 25t-96 54-63 73-23 80v71q0 23 16 38t38 16h160v53q0 37 27 63t63 27h321q37 0 63-27t26-63v-53h161q22 0 38-16t16-38z" horiz-adv-x="928.6" />
<glyph glyph-name="comment-empty" unicode="&#xf0e5;" d="M500 636q-114 0-213-39t-157-105-59-142q0-62 40-119t113-98l48-28-15-53q-13-51-39-97 85 36 154 96l24 21 32-3q38-5 72-5 114 0 213 39t157 105 59 142-59 142-157 105-213 39z m500-286q0-97-67-179t-182-130-251-48q-39 0-81 4-110-97-257-135-27-8-63-12h-3q-8 0-15 6t-9 15v1q-2 2 0 6t1 6 2 5l4 5t4 5 4 5q4 5 17 19t20 22 17 22 18 28 15 33 15 42q-88 50-138 123t-51 157q0 97 67 179t182 130 251 48 251-48 182-130 67-179z" horiz-adv-x="1000" />
<glyph glyph-name="rocket" unicode="&#xf135;" d="M804 600q0 22-16 38t-38 16-38-16-16-38 16-38 38-16 38 16 16 38z m125 161q0-139-43-240t-141-202q-45-44-109-98l-11-211q-1-9-9-15l-214-125q-4-2-9-2-7 0-13 5l-36 36q-7 7-4 17l47 155-156 156-154-47q-2-1-6-1-7 0-12 5l-36 36q-10 11-3 22l125 214q6 8 15 9l211 11q54 64 98 109 105 104 200 144t241 40q7 0 13-6t6-12z" horiz-adv-x="928.6" />
</font>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,94 @@
document.addEventListener('DOMContentLoaded', function(){
// Performance Nag event handlers
let btn_nag_dismiss = document.querySelector('.wpp-dismiss-performance-notice'),
btn_nag_remind = document.querySelector('.wpp-remind-performance-notice');
btn_nag_dismiss.addEventListener('click', function(e){
e.preventDefault();
let me = e.target;
me.classList.add('disabled');
btn_nag_remind.classList.add('disabled');
me.parentNode.querySelector('.spinner').classList.add('is-active');
ajax(
'POST',
ajaxurl,
'action=wpp_handle_performance_notice&dismiss=1&token=' + wpp_admin_notices_params.nonce_performance_nag,
function(response) {
let json = JSON.parse(response);
if ( 'success' == json.status ) {
let notice = me.parentNode.parentNode;
notice.parentNode.removeChild(notice);
} else {
alert('Something went wrong, please try again later');
}
me.classList.remove('disabled');
btn_nag_remind.classList.remove('disabled');
me.parentNode.querySelector('.spinner').classList.remove('is-active');
}
);
});
btn_nag_remind.addEventListener('click', function(e){
e.preventDefault();
let me = e.target;
me.classList.add('disabled');
btn_nag_dismiss.classList.add('disabled');
me.parentNode.querySelector('.spinner').classList.add('is-active');
ajax(
'POST',
ajaxurl,
'action=wpp_handle_performance_notice&dismiss=-1&token=' + wpp_admin_notices_params.nonce_performance_nag,
function(response) {
let json = JSON.parse(response);
if ( 'success' == json.status ) {
let notice = me.parentNode.parentNode;
notice.parentNode.removeChild(notice);
} else {
alert('Something went wrong, please try again later');
}
me.classList.remove('disabled');
btn_nag_remind.classList.remove('disabled');
me.parentNode.querySelector('.spinner').classList.remove('is-active');
}
);
});
/** Helper functions */
function ajax(method, url, params, callback) {
let xhr = new XMLHttpRequest(),
target = url,
args = params,
valid_methods = ["GET", "POST"];
method = -1 != valid_methods.indexOf( method ) ? method : "GET";
/* Set request method and target URL */
xhr.open( method, target + ( "GET" == method ? '?' + args : '' ), true );
/* Set request headers */
if ( "POST" == method ) {
xhr.setRequestHeader( "Content-type", "application/x-www-form-urlencoded" );
}
xhr.setRequestHeader( "X-Requested-With","XMLHttpRequest" );
/* Hook into onreadystatechange */
xhr.onreadystatechange = function() {
if ( 4 === xhr.readyState && 200 <= xhr.status && 300 > xhr.status ) {
if ( 'function' === typeof callback ) {
callback.call( undefined, xhr.response );
}
}
};
/* Send request */
xhr.send( ( "POST" == method ? args : null ) );
}
});

View File

@ -0,0 +1,493 @@
(function ($) {
"use strict";
$(function () {
if ( $('#wpp-chart').length && WPPChart.canRender() ) {
$("#wpp-chart p").remove();
WPPChart.init('wpp-chart');
}
// Stats config
$("#wpp-stats-config-btn, #wpp_stats_options .button-secondary").on("click", function(e){
e.preventDefault();
// Hide custom range modal box
if ( $("#wpp-stats-range").is(":visible") ) {
$("#wpp-stats-range").hide();
}
if ( $("#wpp-stats-config").is(":visible") ) {
$("#wpp-stats-config").hide();
}
else{
$("#wpp-stats-config").show();
}
});
// Stats range
$("#wpp-stats-range form").on('submit', function(e){
e.preventDefault();
if ( $("#wpp-stats-range").is(":visible") ) {
$("#wpp-stats-range").hide();
}
get_chart_data( $("#wpp-time-ranges li a[data-range='custom']") );
$(".wpp-lightbox-tabs li:eq(0) a").trigger("click");
});
$("#wpp-stats-range form .button-secondary").on('click', function(e){
e.preventDefault();
if ( $("#wpp-stats-range").is(":visible") ) {
$("#wpp-stats-range").hide();
}
$("#stats_range_date").val('');
$(".wpp-lightbox-tabs li:eq(0) a").trigger("click");
});
function get_chart_data(me) {
var args = {
action: 'wpp_update_chart',
nonce: wpp_admin_params.nonce,
range: me.data("range"),
time_quantity: $("#stats_range_time_quantity").val(),
time_unit: $("#stats_range_time_unit").val()
};
if ( '' != $("#stats_range_date").val() ){
args.dates = $("#stats_range_date").val();
}
$.get(
ajaxurl,
args,
function( response ){
if ( 'ok' == response.status ) {
me.parent().addClass("current").siblings().removeClass("current");
// Update titles
$("#wpp-chart-wrapper h4").html( response.data.totals.label_summary );
$("#wpp-chart-wrapper h5").html( response.data.totals.label_date_range );
// Update chart
WPPChart.populate(response.data);
$("#wpp-listing .wpp-tabbed-nav li:eq(0) a").trigger("click");
// Update lists
args = {
action: 'wpp_get_most_viewed',
nonce: wpp_admin_params.nonce,
items: 'most-viewed'
};
if ( '' != $("#stats_range_date").val() ){
args.dates = $("#stats_range_date").val();
}
$("#wpp-listing .wpp-tab-content:eq(0)").html('<span class="spinner"></span>');
$.get(
ajaxurl,
args,
function( response ){
$("#wpp-listing .wpp-tab-content:eq(0)").html(response);
}
);
args = {
action: 'wpp_get_most_commented',
nonce: wpp_admin_params.nonce,
items: 'most-commented'
};
if ( '' != $("#stats_range_date").val() ){
args.dates = $("#stats_range_date").val();
}
$("#wpp-listing .wpp-tab-content:eq(1)").html('<span class="spinner"></span>');
$.get(
ajaxurl,
args,
function( response ){
$("#wpp-listing .wpp-tab-content:eq(1)").html(response);
}
);
args = {
action: 'wpp_get_trending',
nonce: wpp_admin_params.nonce,
items: 'trending'
};
if ( '' != $("#stats_range_date").val() ){
args.dates = $("#stats_range_date").val();
}
$("#wpp-listing .wpp-tab-content:eq(2)").html('<span class="spinner"></span>');
$.get(
ajaxurl,
args,
function( response ){
$("#wpp-listing .wpp-tab-content:eq(2)").html(response);
}
);
// Unset date range
$("#stats_range_date").val('');
}
}
);
}
$("#wpp-time-ranges li a").on("click", function(e){
e.preventDefault();
var me = $(this);
// Update chart
if ( WPPChart.canRender() ) {
if ( 'custom' != me.data("range") ) {
get_chart_data(me);
}
else {
// Hide Config modal box
if ( $("#wpp-stats-config").is(":visible") ) {
$("#wpp-stats-config").hide();
}
if ( !$("#wpp-stats-range").is(":visible") ) {
$("#wpp-stats-range").show();
}
}
}
});
$("#wpp-time-ranges li.current a").trigger("click");
$(".wpp-lightbox-tabs li a").on("click", function(e){
e.preventDefault();
var me = $(this);
me.parent().addClass("active").siblings().removeClass("active");
me.closest(".wpp-lightbox").find(".wpp-lightbox-tab-content").removeClass("wpp-lightbox-tab-content-active").filter(function( index ) {
return me.parent().index() == index;
}).addClass("wpp-lightbox-tab-content-active");
});
// Datepicker
$.datepicker._defaults.onAfterUpdate = null;
var datepicker__updateDatepicker = $.datepicker._updateDatepicker;
$.datepicker._updateDatepicker = function( instance ){
datepicker__updateDatepicker.call( this, instance );
var onAfterUpdate = this._get( instance, 'onAfterUpdate' );
if ( onAfterUpdate ) {
onAfterUpdate.apply( ( instance.input ? instance.input[0] : null ), [( instance.input ? instance.input.val() : '' ), instance] );
}
};
var curr = -1,
prev = -1;
var dp_field = $("#stats_range_date");
var wpp_datepicker = dp_field.datepicker({
maxDate: 0,
dateFormat: 'yy-mm-dd',
showButtonPanel: true,
beforeShowDay: function(date){
return [true, ( (date.getTime() >= Math.min(prev, curr) && date.getTime() <= Math.max(prev, curr) ) ? 'date-range-selected' : '' )]
},
onSelect: function(dateText, instance){
var d1, d2;
prev = curr;
curr = ( new Date(instance.selectedYear, instance.selectedMonth, instance.selectedDay) ).getTime();
if (
-1 == prev
|| prev == curr
) {
prev = curr;
dp_field.val( dateText );
}
else {
d1 = $.datepicker.formatDate('yy-mm-dd', new Date( Math.min(prev, curr) ), {});
d2 = $.datepicker.formatDate('yy-mm-dd', new Date( Math.max(prev, curr) ), {});
dp_field.val( d1 + ' ~ ' + d2 );
}
$(this).data('datepicker').inline = true;
},
onClose: function(){
$(this).data('datepicker').inline = false;
},
onAfterUpdate: function( instance ){
var calendar = $(this);
if (
prev > -1
&& curr > -1
){
$('<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" data-handler="hide" data-event="click">OK</button>').appendTo( $(".ui-datepicker-buttonpane") ).on('click', function(){
dp_field.datepicker('hide');
});
}
}
});
// STATISTICS TABS
$("#wpp-listing .wpp-tabbed-nav li a").on("click", function(e){
e.preventDefault();
var me = $(this),
target = me.parent().index();
me.parent().addClass("active").siblings().removeClass("active");
me.closest("#wpp-listing").children(".wpp-tab-content:eq(" + target + ")").addClass("wpp-tab-content-active").siblings().removeClass("wpp-tab-content-active");
});
// TOOLS
// thumb source selection
$("#thumb_source").change(function() {
if ($(this).val() == "custom_field") {
$("#lbl_field, #thumb_field, #row_custom_field, #row_custom_field_resize").show();
} else {
$("#lbl_field, #thumb_field, #row_custom_field, #row_custom_field_resize").hide();
}
});
// file upload
$('#upload_thumb_button').click(function(e) {
e.preventDefault();
var custom_uploader = wp.media({
title: 'WordPress Popular Posts',
library: { type : 'image' },
button: { text: wpp_admin_params.label_media_upload_button },
id: 'library-' + (Math.random() * 10),
multiple: false
}).on('select', function() {
var attachment = custom_uploader.state().get('selection').first().toJSON();
$('#upload_thumb_src').val( attachment.url );
var img = new Image();
img.onload = function() {
$("#thumb-review").html( this ).parent().fadeIn();
}
img.src = attachment.url;
})
.open();
});
$('#wpp-reset-image-cache').on('click', function(e){
e.preventDefault();
confirm_clear_image_cache();
});
// log limit
$("#log_limit").change(function(){
var me = $(this);
if (me.val() == 1) {
me.parent().children("label, .description").show();
me.parent().children("br").hide();
} else {
me.parent().children("label, .description").hide();
me.parent().children("br").show();
}
});
// cache interval
$("#cache").change(function() {
if ($(this).val() == 1) {
$("#cache_refresh_interval").show();
} else {
$("#cache_refresh_interval, #cache_too_long").hide();
}
});
// interval
$("#cache_interval_time").change(function() {
var value = parseInt( $("#cache_interval_value").val() );
var time = $(this).val();
if ( time == "hour" && value > 72 ) {
$("#cache_too_long").show();
} else if ( time == "day" && value > 3 ) {
$("#cache_too_long").show();
} else if ( time == "week" && value > 1 ) {
$("#cache_too_long").show();
} else if ( time == "month" && value >= 1 ) {
$("#cache_too_long").show();
} else if ( time == "year" && value >= 1 ) {
$("#cache_too_long").show();
} else {
$("#cache_too_long").hide();
}
});
$("#cache_interval_value").change(function() {
var value = parseInt( $(this).val() );
var time = $("#cache_interval_time").val();
if ( time == "hour" && value > 72 ) {
$("#cache_too_long").show();
} else if ( time == "day" && value > 3 ) {
$("#cache_too_long").show();
} else if ( time == "week" && value > 1 ) {
$("#cache_too_long").show();
} else if ( time == "month" && value >= 1 ) {
$("#cache_too_long").show();
} else if ( time == "year" && value >= 1 ) {
$("#cache_too_long").show();
} else {
$("#cache_too_long").hide();
}
});
$("#wpp-reset-cache").on("click", function(e){
e.preventDefault();
confirm_reset_cache();
});
$("#wpp-reset-all").on("click", function(e){
e.preventDefault();
confirm_reset_all();
});
});
// TOOLS
function confirm_reset_cache() {
if ( confirm(wpp_admin_params.text_confirm_reset_cache_table + " \n\n" + wpp_admin_params.text_continue) ) {
jQuery.post(
ajaxurl,
{
action: 'wpp_clear_data',
token: wpp_admin_params.nonce_reset_data,
clear: 'cache'
}, function(data){
var response = "";
switch( data ) {
case "1":
response = wpp_admin_params.text_cache_table_cleared;
break;
case "2":
response = wpp_admin_params.text_cache_table_missing;
break;
case "3":
response = wpp_admin_params.text_invalid_action;
break;
case "4":
response = wpp_admin_params.text_insufficient_permissions;
break;
default:
response = wpp_admin_params.text_invalid_action;
break;
}
alert(response);
}
);
}
}
function confirm_reset_all() {
if ( confirm(wpp_admin_params.text_confirm_reset_all_tables + " \n\n" + wpp_admin_params.text_continue) ) {
jQuery.post(
ajaxurl,
{
action: 'wpp_clear_data',
token: wpp_admin_params.nonce_reset_data,
clear: 'all'
}, function(data){
var response = "";
switch( data ) {
case "1":
response = wpp_admin_params.text_all_table_cleared;
break;
case "2":
response = wpp_admin_params.text_tables_missing;
break;
case "3":
response = wpp_admin_params.text_invalid_action;
break;
case "4":
response = wpp_admin_params.text_insufficient_permissions;
break;
default:
response = wpp_admin_params.text_invalid_action;
break;
}
alert(response);
}
);
}
}
function confirm_clear_image_cache() {
if ( confirm(wpp_admin_params.text_confirm_image_cache_reset + " \n\n" + wpp_admin_params.text_continue) ) {
jQuery.post(
ajaxurl,
{
action: 'wpp_clear_thumbnail',
token: wpp_admin_params.nonce_reset_thumbnails
}, function(data){
var response = "";
switch( data ) {
case "1":
response = wpp_admin_params.text_image_cache_cleared;
break;
case "2":
response = wpp_admin_params.text_image_cache_already_empty;
break;
case "3":
response = wpp_admin_params.text_invalid_action;
break;
case "4":
response = wpp_admin_params.text_insufficient_permissions;
break;
default:
response = wpp_admin_params.text_invalid_action;
break;
}
alert(response);
}
);
}
}
}(jQuery));

View File

@ -0,0 +1 @@
<?php return array('dependencies' => array(), 'version' => 'bd6367a58d4de200ed825558aaf46c2c');

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,192 @@
var WPPChart = (function() {
"use strict";
/**
* Private functions and variables
*/
var defaults = {
type: 'line',
data: {
labels: [],
datasets: [
{
label: "",
fill: true,
lineTension: 0.2,
borderWidth: 3,
backgroundColor: "rgba(221, 66, 66, 0.8)",
borderColor: "#881111",
borderCapStyle: 'butt',
borderDash: [],
borderDashOffset: 0.0,
borderJoinStyle: 'miter',
pointBorderColor: "#881111",
pointBackgroundColor: "#fff",
pointBorderWidth: 2,
pointHoverRadius: 4,
pointHoverBackgroundColor: "#881111",
pointHoverBorderColor: "#881111",
pointHoverBorderWidth: 3,
pointRadius: 3,
pointHitRadius: 10,
data: [],
},
{
label: "",
fill: true,
lineTension: 0.2,
borderWidth: 3,
backgroundColor: "rgba(136, 17, 17, 0.3)",
borderColor: "#a80000",
borderCapStyle: 'butt',
borderDash: [],
borderDashOffset: 0.0,
borderJoinStyle: 'miter',
pointBorderColor: "#a80000",
pointBackgroundColor: "#fff",
pointBorderWidth: 2,
pointHoverRadius: 4,
pointHoverBackgroundColor: "#a80000",
pointHoverBorderColor: "#a80000",
pointHoverBorderWidth: 3,
pointRadius: 3,
pointHitRadius: 10,
data: [],
}
]
},
options: {
legend: {
display: true,
labels: {
fontColor: '#23282d',
fontFamily: '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif',
fontSize: 12
}
},
responsive: true,
maintainAspectRatio: false,
layout: {
padding: {
top: 2,
right: 5,
bottom: 0,
left: 5
}
},
scales: {
xAxes: [{
display: true,
gridLines: {
display: false,
},
ticks: {
fontSize: 10,
fontColor: '#23282d',
autoSkip: false,
maxRotation: 90,
minRotation: 90
}
}],
yAxes: [{
display: false
}]
}
}
},
chart = null,
canRender = !! window.CanvasRenderingContext2D,
element = null,
cvs = null;
var canRender = function(){
return canRender;
};
// Source: http://stackoverflow.com/a/5624139
var HexToRGB = function( hex ){
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function( m, r, g, b ) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt( result[1], 16 ),
g: parseInt( result[2], 16 ),
b: parseInt( result[3], 16 )
} : null;
};
/**
* Public functions
*/
var init = function(container, options){
if ( ! canRender() ) {
throw new Error('Your browser is too old, WPPChart cannot create its data chart.');
}
if ( 'undefined' == typeof container ) {
throw new Error('Please tell WPPChart where to inject the chart.');
}
element = document.getElementById(container);
if ( ! element ) {
throw new Error('WPPChart cannot find ' + container);
}
if ( 'undefined' == typeof Chart ) {
throw new Error('ChartJS library not found');
}
cvs = document.createElement('canvas');
element.appendChild(cvs);
};
var populate = function(data){
if ( chart ) {
chart.destroy();
}
var config = defaults;
config.data.labels = data.labels;
config.data.datasets[0].label = data.datasets[0].label;
config.data.datasets[0].data = data.datasets[0].data;
config.data.datasets[1].label = data.datasets[1].label;
config.data.datasets[1].data = data.datasets[1].data;
var colors_arr = wpp_chart_params.colors.slice(-2);
var rgb_comments = HexToRGB(colors_arr[0]);
config.data.datasets[1].backgroundColor = "rgba(" + rgb_comments.r + ", " + rgb_comments.g + ", " + rgb_comments.b + ", 0.9)";
config.data.datasets[1].borderColor = colors_arr[0];
config.data.datasets[1].pointBorderColor = colors_arr[0];
config.data.datasets[1].pointHoverBackgroundColor = colors_arr[0];
config.data.datasets[1].pointHoverBorderColor = colors_arr[0];
var rgb_views = HexToRGB(colors_arr[1]);
config.data.datasets[0].backgroundColor = "rgba(" + rgb_views.r + ", " + rgb_views.g + ", " + rgb_views.b + ", 0.7)";
config.data.datasets[0].borderColor = colors_arr[1];
config.data.datasets[0].pointBorderColor = colors_arr[1];
config.data.datasets[0].pointHoverBackgroundColor = colors_arr[1];
config.data.datasets[0].pointHoverBorderColor = colors_arr[1];
chart = new Chart(cvs, config);
};
/**
* Provide access to public methods
*/
return {
init: init,
populate: populate,
canRender: canRender
};
})();

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,198 @@
var wpp_params = null;
var WordPressPopularPosts = (function(){
"use strict";
var noop = function(){};
var supportsShadowDOMV1 = !! HTMLElement.prototype.attachShadow;
var get = function( url, params, callback, additional_headers ){
callback = ( 'function' === typeof callback ) ? callback : noop;
ajax( "GET", url, params, callback, additional_headers );
};
var post = function( url, params, callback, additional_headers ){
callback = ( 'function' === typeof callback ) ? callback : noop;
ajax( "POST", url, params, callback, additional_headers );
};
var ajax = function( method, url, params, callback, additional_headers ){
/* Create XMLHttpRequest object and set variables */
var xhr = new XMLHttpRequest(),
target = url,
args = params,
valid_methods = ["GET", "POST"],
headers = {
'X-Requested-With': 'XMLHttpRequest'
};
method = -1 != valid_methods.indexOf( method ) ? method : "GET";
/* Set request headers */
if ( 'POST' == method ) {
headers['Content-Type'] = 'application/x-www-form-urlencoded';
}
if ( 'object' == typeof additional_headers && Object.keys(additional_headers).length ) {
headers = Object.assign({}, headers, additional_headers);
}
/* Set request method and target URL */
xhr.open( method, target + ( 'GET' == method ? '?' + args : '' ), true );
for (const key in headers) {
if ( headers.hasOwnProperty(key) ) {
xhr.setRequestHeader( key, headers[key] );
}
}
/* Hook into onreadystatechange */
xhr.onreadystatechange = function() {
if ( 4 === xhr.readyState && 200 <= xhr.status && 300 > xhr.status ) {
if ( 'function' === typeof callback ) {
callback.call( undefined, xhr.response );
}
}
};
/* Send request */
xhr.send( ( 'POST' == method ? args : null ) );
};
var theme = function(wpp_list) {
if ( supportsShadowDOMV1 ) {
let base_styles = document.createElement('style'),
dummy_list = document.createElement('ul');
dummy_list.innerHTML = '<li><a href="#"></a></li>';
wpp_list.parentNode.appendChild(dummy_list);
let dummy_list_item_styles = getComputedStyle(dummy_list.querySelector('li')),
dummy_link_item_styles = getComputedStyle(dummy_list.querySelector('li a'));
base_styles.innerHTML = '.wpp-list li {font-size: '+ dummy_list_item_styles.fontSize +'}';
base_styles.innerHTML += '.wpp-list li a {color: '+ dummy_link_item_styles.color +'}';
wpp_list.parentNode.removeChild(dummy_list);
let wpp_list_sr = wpp_list.attachShadow({mode: "open"});
wpp_list_sr.append(base_styles);
while(wpp_list.firstElementChild) {
wpp_list_sr.append(wpp_list.firstElementChild);
}
}
};
return {
get: get,
post: post,
ajax: ajax,
theme: theme
};
})();
(function(){
try {
var wpp_json = document.querySelector("script#wpp-json"),
do_request = true;
wpp_params = JSON.parse(wpp_json.textContent);
if ( wpp_params.ID ) {
if ( '1' == wpp_params.sampling_active ) {
var num = Math.floor(Math.random() * wpp_params.sampling_rate) + 1;
do_request = ( 1 === num );
}
if ( do_request ) {
WordPressPopularPosts.post(
wpp_params.ajax_url,
"_wpnonce=" + wpp_params.token + "&wpp_id=" + wpp_params.ID + "&sampling=" + wpp_params.sampling_active + "&sampling_rate=" + wpp_params.sampling_rate,
function( response ) {
wpp_params.debug&&window.console&&window.console.log&&window.console.log(JSON.parse(response));
}
);
}
}
} catch (err) {
console.error("WPP: Couldn't read JSON data");
}
})();
document.addEventListener('DOMContentLoaded', function() {
var widget_placeholders = document.querySelectorAll('.wpp-widget-placeholder, .wpp-widget-block-placeholder'),
w = 0;
while ( w < widget_placeholders.length ) {
fetchWidget(widget_placeholders[w]);
w++;
}
var sr = document.querySelectorAll('.popular-posts-sr');
if ( sr.length ) {
for( var s = 0; s < sr.length; s++ ) {
WordPressPopularPosts.theme(sr[s]);
}
}
function fetchWidget(widget_placeholder) {
let widget_id_attr = widget_placeholder.getAttribute('data-widget-id'),
method = 'GET',
url = '',
headers = {},
params = '';
if ( widget_id_attr ) {
url = wpp_params.ajax_url + '/widget/' + widget_id_attr.split('-')[1];
params = 'is_single=' + wpp_params.ID + ( wpp_params.lang ? '&lang=' + wpp_params.lang : '' );
} else {
method = 'POST';
url = wpp_params.api_url + '/v2/widget?is_single=' + wpp_params.ID + ( wpp_params.lang ? '&lang=' + wpp_params.lang : '' );
headers = {
'Content-Type': 'application/json'
};
let json_tag = widget_placeholder.parentNode.querySelector('script[type="application/json"]');
if ( json_tag ) {
let args = JSON.parse(json_tag.textContent);
params = JSON.stringify(args);
}
}
WordPressPopularPosts.ajax(
method,
url,
params,
function(response) {
renderWidget(response, widget_placeholder);
},
headers
);
}
function renderWidget(response, widget_placeholder) {
widget_placeholder.insertAdjacentHTML('afterend', JSON.parse(response).widget);
let parent = widget_placeholder.parentNode,
sr = parent.querySelector('.popular-posts-sr'),
json_tag = parent.querySelector('script[type="application/json"]');
if ( json_tag )
parent.removeChild(json_tag);
parent.removeChild(widget_placeholder);
parent.classList.add('wpp-ajax');
if ( sr ) {
WordPressPopularPosts.theme(sr);
}
let event = new Event("wpp-onload", {"bubbles": true, "cancelable": false});
parent.dispatchEvent(event);
}
});

View File

@ -0,0 +1,7 @@
var wpp_params=null,WordPressPopularPosts=function(){var m=function(){},h=!!HTMLElement.prototype.attachShadow,f=function(b,c,a,g,k){var e=new XMLHttpRequest,d={"X-Requested-With":"XMLHttpRequest"};b=-1!=["GET","POST"].indexOf(b)?b:"GET";"POST"==b&&(d["Content-Type"]="application/x-www-form-urlencoded");"object"==typeof k&&Object.keys(k).length&&(d=Object.assign({},d,k));e.open(b,c+("GET"==b?"?"+a:""),!0);for(var l in d)d.hasOwnProperty(l)&&e.setRequestHeader(l,d[l]);e.onreadystatechange=function(){4===
e.readyState&&200<=e.status&&300>e.status&&"function"===typeof g&&g.call(void 0,e.response)};e.send("POST"==b?a:null)};return{get:function(b,c,a,g){a="function"===typeof a?a:m;f("GET",b,c,a,g)},post:function(b,c,a,g){a="function"===typeof a?a:m;f("POST",b,c,a,g)},ajax:f,theme:function(b){if(h){var c=document.createElement("style"),a=document.createElement("ul");a.innerHTML='<li><a href="#"></a></li>';b.parentNode.appendChild(a);var g=getComputedStyle(a.querySelector("li")),k=getComputedStyle(a.querySelector("li a"));
c.innerHTML=".wpp-list li {font-size: "+g.fontSize+"}";c.innerHTML+=".wpp-list li a {color: "+k.color+"}";b.parentNode.removeChild(a);a=b.attachShadow({mode:"open"});for(a.append(c);b.firstElementChild;)a.append(b.firstElementChild)}}}}();
(function(){try{var m=document.querySelector("script#wpp-json"),h=!0;wpp_params=JSON.parse(m.textContent);wpp_params.ID&&("1"==wpp_params.sampling_active&&(h=1===Math.floor(Math.random()*wpp_params.sampling_rate)+1),h&&WordPressPopularPosts.post(wpp_params.ajax_url,"_wpnonce="+wpp_params.token+"&wpp_id="+wpp_params.ID+"&sampling="+wpp_params.sampling_active+"&sampling_rate="+wpp_params.sampling_rate,function(f){wpp_params.debug&&window.console&&window.console.log&&window.console.log(JSON.parse(f))}))}catch(f){console.error("WPP: Couldn't read JSON data")}})();
document.addEventListener("DOMContentLoaded",function(){function m(b){var c=b.getAttribute("data-widget-id"),a="GET",g="",k={},e="";if(c)g=wpp_params.ajax_url+"/widget/"+c.split("-")[1],e="is_single="+wpp_params.ID+(wpp_params.lang?"&lang="+wpp_params.lang:"");else if(a="POST",g=wpp_params.api_url+"/v2/widget?is_single="+wpp_params.ID+(wpp_params.lang?"&lang="+wpp_params.lang:""),k={"Content-Type":"application/json"},c=b.parentNode.querySelector('script[type="application/json"]'))e=JSON.parse(c.textContent),
e=JSON.stringify(e);WordPressPopularPosts.ajax(a,g,e,function(d){b.insertAdjacentHTML("afterend",JSON.parse(d).widget);d=b.parentNode;var l=d.querySelector(".popular-posts-sr"),n=d.querySelector('script[type="application/json"]');n&&d.removeChild(n);d.removeChild(b);d.classList.add("wpp-ajax");l&&WordPressPopularPosts.theme(l);l=new Event("wpp-onload",{bubbles:!0,cancelable:!1});d.dispatchEvent(l)},k)}for(var h=document.querySelectorAll(".wpp-widget-placeholder, .wpp-widget-block-placeholder"),f=
0;f<h.length;)m(h[f]),f++;h=document.querySelectorAll(".popular-posts-sr");if(h.length)for(f=0;f<h.length;f++)WordPressPopularPosts.theme(h[f])});

View File

@ -0,0 +1,50 @@
{
"name": "Cards Compact",
"description": "A compact card-style popular posts list.",
"authors": [
{
"name": "Hector Cabrera",
"email": "me@cabrerahector.com",
"role": "Creator / Main Developer"
}
],
"config": {
"shorten_title": {
"active": false,
"length": 25,
"words": false
},
"post-excerpt": {
"active": false,
"length": 75,
"keep_format": false,
"words": false
},
"thumbnail": {
"active": true,
"build": "manual",
"width": 75,
"height": 75,
"crop": true
},
"rating": false,
"stats_tag": {
"comment_count": false,
"views": false,
"author": false,
"date": {
"active": false,
"format": "F j, Y"
},
"taxonomy": {
"active": true,
"name": "category"
}
},
"markup": {
"wpp-start": "<ul class=\"wpp-list wpp-cards-compact\">",
"wpp-end": "</ul>",
"post-html": "<li class=\"{current_class}\">{thumb_img}<div class=\"wpp-item-data\"><div class=\"taxonomies\">{taxonomy}</div>{title}</div></li>"
}
}
}

View File

@ -0,0 +1,69 @@
.wpp-cards-compact {
margin-left: 0;
margin-right: 0;
padding: 0;
}
.wpp-cards-compact li {
display: flex;
align-items: center;
list-style: none;
margin: 0 0 1.2em 0;
padding: 0 0 1em 0;
border-bottom: #ddd 1px solid;
}
.in-preview-mode .wpp-cards-compact li {
margin: 0 0 1.2em 0;
padding: 0 0 1em 0;
}
.wpp-cards-compact li:last-of-type,
.wpp-cards-compact li:only-child {
margin: 0;
padding: 0;
border-bottom: none;
}
.wpp-cards-compact li .wpp-thumbnail {
overflow: hidden;
display: inline-block;
flex-grow: 0;
flex-shrink: 0;
margin-right: 1em;
font-size: 0.8em;
line-height: 1;
background: #f0f0f0;
border: none;
}
.wpp-cards-compact li a {
text-decoration: none;
}
.wpp-cards-compact li a:hover {
text-decoration: underline;
}
.wpp-cards-compact li .taxonomies,
.wpp-cards-compact li .wpp-post-title {
display: block;
font-weight: bold;
}
.wpp-cards-compact li .taxonomies {
margin-bottom: 0.25em;
font-size: 0.7em;
line-height: 1;
}
.wpp-cards-compact li .wpp-post-title {
margin-bottom: 0.5em;
font-size: 1.2em;
line-height: 1.2;
}
.wpp-cards-compact li .wpp-excerpt {
margin: 0;
font-size: 0.8em;
}

View File

@ -0,0 +1,50 @@
{
"name": "Cards",
"description": "A card-style popular posts list.",
"authors": [
{
"name": "Hector Cabrera",
"email": "me@cabrerahector.com",
"role": "Creator / Main Developer"
}
],
"config": {
"shorten_title": {
"active": false,
"length": 25,
"words": false
},
"post-excerpt": {
"active": true,
"length": 75,
"keep_format": false,
"words": false
},
"thumbnail": {
"active": true,
"build": "manual",
"width": 75,
"height": 75,
"crop": true
},
"rating": false,
"stats_tag": {
"comment_count": false,
"views": false,
"author": false,
"date": {
"active": false,
"format": "F j, Y"
},
"taxonomy": {
"active": true,
"name": "category"
}
},
"markup": {
"wpp-start": "<ul class=\"wpp-list wpp-cards\">",
"wpp-end": "</ul>",
"post-html": "<li class=\"{current_class}\">{thumb_img} <div class=\"wpp-item-data\"><div class=\"taxonomies\">{taxonomy}</div>{title} <p class=\"wpp-excerpt\">{excerpt}</p></div></li>"
}
}
}

View File

@ -0,0 +1,69 @@
.wpp-cards {
margin-left: 0;
margin-right: 0;
padding: 0;
}
.wpp-cards li {
display: flex;
list-style: none;
margin: 0 0 1.2em 0;
padding: 0 0 1em 0;
border-bottom: #ddd 1px solid;
}
.in-preview-mode .wpp-cards li {
margin: 0 0 1.2em 0;
padding: 0 0 1em 0;
}
.wpp-cards li:last-of-type,
.wpp-cards li:only-child {
margin: 0;
padding: 0;
border-bottom: none;
}
.wpp-cards li .wpp-thumbnail {
overflow: hidden;
display: inline-block;
align-self: flex-start;
flex-grow: 0;
flex-shrink: 0;
margin-right: 1em;
font-size: 0.8em;
line-height: 1;
background: #f0f0f0;
border: none;
}
.wpp-cards li a {
text-decoration: none;
}
.wpp-cards li a:hover {
text-decoration: underline;
}
.wpp-cards li .taxonomies,
.wpp-cards li .wpp-post-title {
display: block;
font-weight: bold;
}
.wpp-cards li .taxonomies {
margin-bottom: 0.25em;
font-size: 0.7em;
line-height: 1;
}
.wpp-cards li .wpp-post-title {
margin-bottom: 0.5em;
font-size: 1.2em;
line-height: 1.2;
}
.wpp-cards li .wpp-excerpt {
margin: 0;
font-size: 0.8em;
}

View File

@ -0,0 +1,50 @@
{
"name": "Cardview Compact",
"description": "A compact cardview-style popular posts list.",
"authors": [
{
"name": "Hector Cabrera",
"email": "me@cabrerahector.com",
"role": "Creator / Main Developer"
}
],
"config": {
"shorten_title": {
"active": false,
"length": 25,
"words": false
},
"post-excerpt": {
"active": false,
"length": 75,
"keep_format": false,
"words": false
},
"thumbnail": {
"active": true,
"build": "manual",
"width": 320,
"height": 165,
"crop": true
},
"rating": false,
"stats_tag": {
"comment_count": false,
"views": false,
"author": false,
"date": {
"active": false,
"format": "F j, Y"
},
"taxonomy": {
"active": true,
"name": "category"
}
},
"markup": {
"wpp-start": "<ul class=\"wpp-list wpp-cardview-compact\">",
"wpp-end": "</ul>",
"post-html": "<li class=\"{current_class}\"><div class=\"wpp-thumbnail-container\">{thumb}<div class=\"taxonomies\">{taxonomy}</div></div> <div class=\"wpp-item-data\">{title}</div></li>"
}
}
}

View File

@ -0,0 +1,77 @@
.wpp-cardview-compact {
margin-left: 0;
margin-right: 0;
padding: 0;
}
.wpp-cardview-compact li {
list-style: none;
margin: 0 0 1.2em 0;
padding: 0 0 1em 0;
border-bottom: #ddd 1px solid;
}
.in-preview-mode .wpp-cardview-compact li {
margin: 0 0 1.2em 0;
padding: 0 0 1em 0;
}
.wpp-cardview-compact li:last-of-type,
.wpp-cardview-compact li:only-child {
margin: 0;
padding: 0;
border-bottom: none;
}
.wpp-cardview-compact .wpp-thumbnail-container {
position: relative;
margin-bottom: .8em;
}
.wpp-cardview-compact li .wpp-thumbnail {
overflow: hidden;
display: block;
float: none;
margin: 0;
width: 100%;
height: auto;
font-size: 0.8em;
line-height: 1;
background: #f0f0f0;
border: none;
}
.wpp-cardview-compact li a {
text-decoration: none;
}
.wpp-cardview-compact li a:hover {
text-decoration: underline;
}
.wpp-cardview-compact li .taxonomies,
.wpp-cardview-compact li .wpp-post-title {
display: block;
font-weight: bold;
}
.wpp-cardview-compact li .taxonomies {
position: absolute;
bottom: 0;
left: 0;
padding: 1em;
color: #aaa;
font-size: 0.7em;
line-height: 1;
background: rgba(0, 0, 0, 0.5);
}
.wpp-cardview-compact li .taxonomies a {
color: #fff;
}
.wpp-cardview-compact li .wpp-post-title {
margin: 0 0 0.5em;
font-size: 1.2em;
line-height: 1.2;
}

View File

@ -0,0 +1,50 @@
{
"name": "Cardview",
"description": "A cardview-style popular posts list.",
"authors": [
{
"name": "Hector Cabrera",
"email": "me@cabrerahector.com",
"role": "Creator / Main Developer"
}
],
"config": {
"shorten_title": {
"active": false,
"length": 25,
"words": false
},
"post-excerpt": {
"active": true,
"length": 75,
"keep_format": false,
"words": false
},
"thumbnail": {
"active": true,
"build": "manual",
"width": 320,
"height": 165,
"crop": true
},
"rating": false,
"stats_tag": {
"comment_count": false,
"views": false,
"author": false,
"date": {
"active": false,
"format": "F j, Y"
},
"taxonomy": {
"active": true,
"name": "category"
}
},
"markup": {
"wpp-start": "<ul class=\"wpp-list wpp-cardview\">",
"wpp-end": "</ul>",
"post-html": "<li class=\"{current_class}\"><div class=\"wpp-thumbnail-container\">{thumb}<div class=\"taxonomies\">{taxonomy}</div></div> <div class=\"wpp-item-data\">{title} <p class=\"wpp-excerpt\">{excerpt}</p></div></li>"
}
}
}

View File

@ -0,0 +1,82 @@
.wpp-cardview {
margin-left: 0;
margin-right: 0;
padding: 0;
}
.wpp-cardview li {
list-style: none;
margin: 0 0 1.2em 0;
padding: 0 0 1em 0;
border-bottom: #ddd 1px solid;
}
.in-preview-mode .wpp-cardview li {
margin: 0 0 1.2em 0;
padding: 0 0 1em 0;
}
.wpp-cardview li:last-of-type,
.wpp-cardview li:only-child {
margin: 0;
padding: 0;
border-bottom: none;
}
.wpp-cardview .wpp-thumbnail-container {
position: relative;
margin-bottom: .8em;
}
.wpp-cardview li .wpp-thumbnail {
overflow: hidden;
display: block;
float: none;
margin: 0;
width: 100%;
height: auto;
font-size: 0.8em;
line-height: 1;
background: #f0f0f0;
border: none;
}
.wpp-cardview li a {
text-decoration: none;
}
.wpp-cardview li a:hover {
text-decoration: underline;
}
.wpp-cardview li .taxonomies,
.wpp-cardview li .wpp-post-title {
display: block;
font-weight: bold;
}
.wpp-cardview li .taxonomies {
position: absolute;
bottom: 0;
left: 0;
padding: 1em;
color: #aaa;
font-size: 0.7em;
line-height: 1;
background: rgba(0, 0, 0, 0.5);
}
.wpp-cardview li .taxonomies a {
color: #fff;
}
.wpp-cardview li .wpp-post-title {
margin: 0 0 0.5em;
font-size: 1.2em;
line-height: 1.2;
}
.wpp-cardview li .wpp-excerpt {
margin: 0;
font-size: 0.8em;
}

View File

@ -0,0 +1,50 @@
{
"name": "Evergreen",
"description": "A simple list of popular posts.",
"authors": [
{
"name": "Hector Cabrera",
"email": "me@cabrerahector.com",
"role": "Creator / Main Developer"
}
],
"config": {
"shorten_title": {
"active": false,
"length": 25,
"words": false
},
"post-excerpt": {
"active": false,
"length": 75,
"keep_format": false,
"words": false
},
"thumbnail": {
"active": false,
"build": "manual",
"width": 75,
"height": 75,
"crop": true
},
"rating": false,
"stats_tag": {
"comment_count": false,
"views": false,
"author": false,
"date": {
"active": false,
"format": "F j, Y"
},
"taxonomy": {
"active": false,
"name": "category"
}
},
"markup": {
"wpp-start": "<ul class=\"wpp-list wpp-evergreen\">",
"wpp-end": "</ul>",
"post-html": "<li class=\"{current_class}\" style=\"--item-position: {item_position}; --total-items: {total_items};\"><div class=\"item-position\"></div> <div class=\"item-data\">{title}</div></li>"
}
}
}

View File

@ -0,0 +1,54 @@
.wpp-evergreen {
counter-reset: wpp-counter;
margin-left: 0;
margin-right: 0;
padding: 0;
border-right: 6px solid rgb(49, 188, 7);
box-sizing: border-box;
}
.wpp-evergreen li {
position: relative;
display: flex;
align-items: center;
counter-increment: wpp-counter;
position: relative;
list-style: none;
margin: 0;
padding: 15px 15px 15px 0;
background: rgba(49, 188, 7, calc((((var(--total-items) - (var(--item-position) - 1)) * 100)/var(--total-items))/100));
box-sizing: border-box;
}
.in-preview-mode .wpp-evergreen li {
margin: 0;
padding: 15px 15px 15px 0;
}
.wpp-evergreen li .item-position::before {
display: inline-block;
flex: 1 0 0;
content: counter(wpp-counter);
color: rgba(0, 0, 0, 0.15);
width: 40px;
font-size: 18px;
font-weight: 700;
letter-spacing: -1px;
text-align: center;
}
.wpp-evergreen li .item-data .wpp-post-title {
display: block;
font-size: 15px;
font-weight: 700;
line-height: 1.3;
}
.wpp-evergreen li .item-data a {
color: #0f470e;
text-decoration: none;
}
.wpp-evergreen li .item-data a:hover {
text-decoration: underline;
}

View File

@ -0,0 +1,50 @@
{
"name": "Midnight",
"description": "A simple list of popular posts.",
"authors": [
{
"name": "Hector Cabrera",
"email": "me@cabrerahector.com",
"role": "Creator / Main Developer"
}
],
"config": {
"shorten_title": {
"active": false,
"length": 25,
"words": false
},
"post-excerpt": {
"active": false,
"length": 75,
"keep_format": false,
"words": false
},
"thumbnail": {
"active": false,
"build": "manual",
"width": 75,
"height": 75,
"crop": true
},
"rating": false,
"stats_tag": {
"comment_count": false,
"views": false,
"author": false,
"date": {
"active": false,
"format": "F j, Y"
},
"taxonomy": {
"active": false,
"name": "category"
}
},
"markup": {
"wpp-start": "<ul class=\"wpp-list wpp-midnight\">",
"wpp-end": "</ul>",
"post-html": "<li class=\"{current_class}\" style=\"--item-position: {item_position}; --total-items: {total_items};\"><div class=\"item-position\"></div> <div class=\"item-data\">{title}</div></li>"
}
}
}

View File

@ -0,0 +1,54 @@
.wpp-midnight {
counter-reset: wpp-counter;
margin-left: 0;
margin-right: 0;
padding: 0;
border-right: 6px solid rgb(0, 51, 51);
box-sizing: border-box;
}
.wpp-midnight li {
position: relative;
display: flex;
align-items: center;
counter-increment: wpp-counter;
position: relative;
list-style: none;
margin: 0;
padding: 15px 15px 15px 0;
background: rgba(0, 51, 51, calc((((var(--total-items) - (var(--item-position) - 1)) * 100)/var(--total-items))/100));
box-sizing: border-box;
}
.in-preview-mode .wpp-midnight li {
margin: 0;
padding: 15px 15px 15px 0;
}
.wpp-midnight li .item-position::before {
display: inline-block;
flex: 1 0 0;
content: counter(wpp-counter);
color: rgba(255, 255, 255, 0.3);
width: 40px;
font-size: 18px;
font-weight: 700;
letter-spacing: -1px;
text-align: center;
}
.wpp-midnight li .item-data .wpp-post-title {
display: block;
font-size: 15px;
font-weight: 700;
line-height: 1.3;
}
.wpp-midnight li .item-data a {
color: #eee;
text-decoration: none;
}
.wpp-midnight li .item-data a:hover {
text-decoration: underline;
}

View File

@ -0,0 +1,50 @@
{
"name": "Sunrise",
"description": "A simple list of popular posts.",
"authors": [
{
"name": "Hector Cabrera",
"email": "me@cabrerahector.com",
"role": "Creator / Main Developer"
}
],
"config": {
"shorten_title": {
"active": false,
"length": 25,
"words": false
},
"post-excerpt": {
"active": false,
"length": 75,
"keep_format": false,
"words": false
},
"thumbnail": {
"active": false,
"build": "manual",
"width": 75,
"height": 75,
"crop": true
},
"rating": false,
"stats_tag": {
"comment_count": false,
"views": false,
"author": false,
"date": {
"active": false,
"format": "F j, Y"
},
"taxonomy": {
"active": false,
"name": "category"
}
},
"markup": {
"wpp-start": "<ul class=\"wpp-list wpp-sunrise\">",
"wpp-end": "</ul>",
"post-html": "<li class=\"{current_class}\" style=\"--item-position: {item_position}; --total-items: {total_items};\"><div class=\"item-position\"></div> <div class=\"item-data\">{title}</div></li>"
}
}
}

View File

@ -0,0 +1,54 @@
.wpp-sunrise {
counter-reset: wpp-counter;
margin-left: 0;
margin-right: 0;
padding: 0;
border-right: 6px solid rgb(255, 255, 124);
box-sizing: border-box;
}
.wpp-sunrise li {
position: relative;
display: flex;
align-items: center;
counter-increment: wpp-counter;
position: relative;
list-style: none;
margin: 0;
padding: 15px 15px 15px 0;
background: rgba(255, 255, 124, calc((((var(--total-items) - (var(--item-position) - 1)) * 100)/var(--total-items))/100));
box-sizing: border-box;
}
.in-preview-mode .wpp-sunrise li {
margin: 0;
padding: 15px 15px 15px 0;
}
.wpp-sunrise li .item-position::before {
display: inline-block;
flex: 1 0 0;
content: counter(wpp-counter);
color: rgba(0, 0, 0, 0.15);
width: 40px;
font-size: 18px;
font-weight: 700;
letter-spacing: -1px;
text-align: center;
}
.wpp-sunrise li .item-data .wpp-post-title {
display: block;
font-size: 15px;
font-weight: 700;
line-height: 1.3;
}
.wpp-sunrise li .item-data a {
color: #2b2b09;
text-decoration: none;
}
.wpp-sunrise li .item-data a:hover {
text-decoration: underline;
}

View File

@ -0,0 +1,50 @@
{
"name": "Sunset",
"description": "A simple list of popular posts.",
"authors": [
{
"name": "Hector Cabrera",
"email": "me@cabrerahector.com",
"role": "Creator / Main Developer"
}
],
"config": {
"shorten_title": {
"active": false,
"length": 25,
"words": false
},
"post-excerpt": {
"active": false,
"length": 75,
"keep_format": false,
"words": false
},
"thumbnail": {
"active": false,
"build": "manual",
"width": 75,
"height": 75,
"crop": true
},
"rating": false,
"stats_tag": {
"comment_count": false,
"views": false,
"author": false,
"date": {
"active": false,
"format": "F j, Y"
},
"taxonomy": {
"active": false,
"name": "category"
}
},
"markup": {
"wpp-start": "<ul class=\"wpp-list wpp-sunset\">",
"wpp-end": "</ul>",
"post-html": "<li class=\"{current_class}\" style=\"--item-position: {item_position}; --total-items: {total_items};\"><div class=\"item-position\"></div> <div class=\"item-data\">{title}</div></li>"
}
}
}

View File

@ -0,0 +1,54 @@
.wpp-sunset {
counter-reset: wpp-counter;
margin-left: 0;
margin-right: 0;
padding: 0;
border-right: 6px solid rgb(234, 51, 51);
box-sizing: border-box;
}
.wpp-sunset li {
position: relative;
display: flex;
align-items: center;
counter-increment: wpp-counter;
position: relative;
list-style: none;
margin: 0;
padding: 15px 15px 15px 0;
background: rgba(234, 51, 51, calc((((var(--total-items) - (var(--item-position) - 1)) * 100)/var(--total-items))/100));
box-sizing: border-box;
}
.in-preview-mode .wpp-sunset li {
margin: 0;
padding: 15px 15px 15px 0;
}
.wpp-sunset li .item-position::before {
display: inline-block;
flex: 1 0 0;
content: counter(wpp-counter);
color: rgba(0, 0, 0, 0.15);
width: 40px;
font-size: 18px;
font-weight: 700;
letter-spacing: -1px;
text-align: center;
}
.wpp-sunset li .item-data .wpp-post-title {
display: block;
font-size: 15px;
font-weight: 700;
line-height: 1.3;
}
.wpp-sunset li .item-data a {
color: #222;
text-decoration: none;
}
.wpp-sunset li .item-data a:hover {
text-decoration: underline;
}

View File

@ -0,0 +1,50 @@
{
"name": "Tiles",
"description": "A Tile-styled popular posts list.",
"authors": [
{
"name": "Hector Cabrera",
"email": "me@cabrerahector.com",
"role": "Creator / Main Developer"
}
],
"config": {
"shorten_title": {
"active": false,
"length": 25,
"words": false
},
"post-excerpt": {
"active": false,
"length": 75,
"keep_format": false,
"words": false
},
"thumbnail": {
"active": true,
"build": "manual",
"width": 320,
"height": 160,
"crop": true
},
"rating": false,
"stats_tag": {
"comment_count": false,
"views": false,
"author": false,
"date": {
"active": false,
"format": "F j, Y"
},
"taxonomy": {
"active": true,
"name": "category"
}
},
"markup": {
"wpp-start": "<ul class=\"wpp-list wpp-tiles\">",
"wpp-end": "</ul>",
"post-html": "<li class=\"{current_class}\">{thumb}<div class=\"wpp-post-data\">{taxonomy} {title}</div></li>"
}
}
}

View File

@ -0,0 +1,70 @@
.wpp-tiles {
margin-left: 0;
margin-right: 0;
padding: 0;
}
.wpp-tiles li {
position: relative;
list-style: none;
margin: 0 0 1.2em 0;
padding: 0;
}
.in-preview-mode .wpp-tiles li {
margin: 0 0 1.2em 0;
padding: 0;
}
.wpp-tiles li:last-of-type {
margin: 0;
}
.wpp-tiles li .wpp-thumbnail {
overflow: hidden;
display: block;
margin: 0;
width: 100%;
height: auto;
font-size: 0.8em;
line-height: 1;
background: #f0f0f0;
border: none;
}
.wpp-tiles li a {
text-decoration: none;
}
.wpp-tiles li a:hover {
text-decoration: underline;
}
.wpp-tiles li .wpp-post-data {
position: absolute;
left: 0;
bottom: 0;
padding: .75em 1em 1em;
width: 100%;
box-sizing: border-box;
background: rgba(0, 0, 0, 0.5);
}
.wpp-tiles li .wpp-post-data .category,
.wpp-tiles li .wpp-post-data .wpp-post-title {
color: #fff;
font-weight: bold;
}
.wpp-tiles li .wpp-post-data .category {
margin: 0;
font-size: 0.7em;
line-height: 1;
}
.wpp-tiles li .wpp-post-data .wpp-post-title {
display: block;
margin: 0 0 0;
font-size: 1em;
line-height: 1.2;
}

View File

@ -0,0 +1,41 @@
{
"name": "Tiny",
"description": "A very compact popular posts list.",
"authors": [
{
"name": "Hector Cabrera",
"email": "me@cabrerahector.com",
"role": "Creator / Main Developer"
}
],
"config": {
"title" : "Trending",
"shorten_title": {
"active": false
},
"post-excerpt": {
"active": false
},
"thumbnail": {
"active": false
},
"rating": false,
"stats_tag": {
"comment_count": false,
"views": false,
"author": false,
"date": {
"active": false
},
"taxonomy": {
"active": true,
"name": "category"
}
},
"markup": {
"wpp-start": "<ul class=\"wpp-list wpp-tiny\">",
"wpp-end": "</ul>",
"post-html": "<li class=\"{current_class}\"><div class=\"wpp-item-data\">{category}</div> {title}</li>"
}
}
}

View File

@ -0,0 +1,41 @@
.wpp-tiny {
margin-left: 0;
margin-right: 0;
padding: 0;
}
.wpp-tiny li {
list-style: none;
margin: 0 0 1.2rem 0;
padding: 0;
}
.in-preview-mode .wpp-tiny li {
margin: 0 0 1.2em 0;
padding: 0;
}
.wpp-tiny li:last-of-type {
margin: 0;
}
.wpp-tiny li a {
text-decoration: none;
}
.wpp-tiny li a:hover {
text-decoration: underline;
}
.wpp-tiny li .wpp-post-title {
font-size: 1.125rem;
font-weight: 700;
letter-spacing: -1px;
line-height: 1.1;
}
.wpp-tiny li .wpp-item-data {
opacity: 0.5;
font-size: 0.65rem;
text-transform: capitalize;
}

View File

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

View File

@ -0,0 +1,173 @@
=== WordPress Popular Posts ===
Contributors: hcabrera
Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=hcabrerab%40gmail%2ecom&lc=GB&item_name=WordPress%20Popular%20Posts%20Plugin&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG_global%2egif%3aNonHosted
Tags: popular, posts, widget, popularity, top
Requires at least: 4.9
Tested up to: 5.9
Requires PHP: 5.4
Stable tag: 5.5.1
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
A highly customizable, easy-to-use popular posts widget!
== Description ==
WordPress Popular Posts is a highly customizable widget that displays your most popular posts.
= Main Features =
* **Multi-widget capable** - You can have several widgets of WordPress Popular Posts on your blog, each with its own settings!
* **Time Range** - List those posts of your blog that have been the most popular ones within a specific time range (eg. last 24 hours, last 7 days, last 30 days, etc)!
* **Custom Post-type support** - Want to show other stuff than just posts and pages, eg. Popular *Products*? [You can](https://github.com/cabrerahector/wordpress-popular-posts/wiki/5.-FAQ#i-want-to-have-a-popular-list-of-my-custom-post-type-how-can-i-do-that)!
* **Thumbnails!** - Display a thumbnail of your posts! (*see the [FAQ section](https://github.com/cabrerahector/wordpress-popular-posts/wiki/5.-FAQ#how-does-wordpress-popular-posts-pick-my-posts-thumbnails) for more details*.)
* **Statistics dashboard** - See how your popular posts are doing directly from your admin area.
* **Sorting options** - Order your popular list by comments, views (default) or average views per day!
* **Custom themes** - Out of the box, WordPress Popular Posts includes some themes so you can style your popular posts list (see [Widget Themes](https://github.com/cabrerahector/wordpress-popular-posts/wiki/6.-Styling-the-list#themes) for more details).
* **Use your own layout!** - WPP is flexible enough to let you customize the look and feel of your popular posts! (see [customizing WPP's HTML markup](https://github.com/cabrerahector/wordpress-popular-posts/wiki/5.-FAQ#how-can-i-use-my-own-html-markup-with-your-plugin) and [How to style WordPress Popular Posts](https://github.com/cabrerahector/wordpress-popular-posts/wiki/6.-Styling-the-list) for more.)
* **Advanced caching features!** - WordPress Popular Posts includes a few options to make sure your site's performance stays as good as ever! (see [Performance](https://github.com/cabrerahector/wordpress-popular-posts/wiki/7.-Performance) for more details.)
* **REST API Support** - Embed your popular posts in your (web) app! (see [REST API Endpoints](https://github.com/cabrerahector/wordpress-popular-posts/wiki/8.-REST-API-Endpoints) for more.)
* **Disqus support** - Sort your popular posts by Disqus comments count!
* **Polylang & WPML 3.2+ support** - Show the translated version of your popular posts!
* **WordPress Multisite support** - Each site on the network can have its own popular posts list!
= Other Features =
* **Shortcode support** - Use the [wpp] shortcode to showcase your most popular posts on pages, too! For usage and instructions, please refer to the [Installation section](https://wordpress.org/plugins/wordpress-popular-posts/#installation).
* **Template tags** - Don't feel like using widgets? No problem! You can still embed your most popular entries on your theme using the `wpp_get_mostpopular()` template tag. Additionally, the `wpp_get_views()` template tag allows you to retrieve the views count for a particular post. For usage and instructions, please refer to the [Installation section](https://wordpress.org/plugins/wordpress-popular-posts/#installation).
* **Localization** - [Translate WPP into your own language](https://github.com/cabrerahector/wordpress-popular-posts/wiki/5.-FAQ#i-want-to-translate-your-plugin-into-my-language--help-you-update-a-translation-what-do-i-need-to-do).
* **[WP-PostRatings](https://wordpress.org/plugins/wp-postratings/) support** - Show your visitors how your readers are rating your posts!
= PSA: do not use the classic WordPress Popular Posts widget with the new Widgets screen! =
The classic WordPress Popular Posts widget doesn't work very well / at all with the new Widgets screen introduced with WordPress 5.8.
This new Widgets screen expects WordPress blocks instead of regular WordPress widgets. If you're using the WordPress Popular Posts widget on your block-based Widgets screen please consider replacing it with the [WordPress Popular Posts block](https://cabrerahector.com/wordpress/wordpress-popular-posts-5-3-improved-php-8-support-retina-display-support-and-more/#block-editor-support) instead - it has the same features as the "classic" widget and will likely end up replacing it entirely in the future.
Bjorn from wplearninglab.com was kind enough to create a video explaining how to use the new block for all of you visual learners:
[youtube https://www.youtube.com/watch?v=mtzk6yNEaFs]
If for some reason you prefer using the "classic" WordPress Popular Posts widget with WordPress 5.8 and beyond please install the [Classic Widgets](https://wordpress.org/plugins/classic-widgets/) plugin.
= Support the Project! =
If you'd like to support my work and efforts to creating and maintaining more open source projects your donations and messages of support mean a lot!
[Ko-fi](https://ko-fi.com/cabrerahector) | [Buy me a coffee](https://www.buymeacoffee.com/cabrerahector) | [PayPal Me](https://paypal.me/cabrerahector)
**WordPress Popular Posts** is now also on [GitHub](https://github.com/cabrerahector/wordpress-popular-posts)!
Looking for a **Recent Posts** widget just as featured-packed as WordPress Popular Posts? **Try [Recently](https://wordpress.org/plugins/recently/)**!
== Installation ==
Please make sure your site meets the [minimum requirements](https://github.com/cabrerahector/wordpress-popular-posts#requirements) before proceeding.
= Automatic installation =
1. Log in into your WordPress dashboard.
2. Go to Plugins > Add New.
3. In the "Search Plugins" field, type in **WordPress Popular Posts** and hit Enter.
4. Find the plugin in the search results list and click on the "Install Now" button.
= Manual installation =
1. Download the plugin and extract its contents.
2. Upload the `wordpress-popular-posts` folder to the `/wp-content/plugins/` directory.
3. Activate the **WordPress Popular Posts** plugin through the "Plugins" menu in WordPress.
= Done! What's next? =
1. Go to Appearance > Widgets, drag and drop the **WordPress Popular Posts** widget to your sidebar. Once you're done configuring it, hit the Save button.
2. If you have a caching plugin installed on your site, flush its cache now so WPP can start tracking your site.
3. If you have a plugin that minifies JavaScript (JS) installed on your site please read this FAQ: [Is WordPress Popular Posts compatible with plugins that minify/bundle JavaScript code?](https://github.com/cabrerahector/wordpress-popular-posts/wiki/5.-FAQ#is-wordpress-popular-posts-compatible-with-plugins-that-minifybundle-javascript-code)
4. If you have a security / firewall plugin installed on your site, make sure you [allow WPP access to the REST API](https://wordpress.org/support/topic/wpp-does-not-count-properly/#post-10411163) so it can start tracking your site.
5. Go to Appearance > Editor. Under "Templates", click on `header.php` and make sure that the `<?php wp_head(); ?>` tag is present (should be right before the closing `</head>` tag).
6. (Optional but highly recommended) Are you running a medium/high traffic site? If so, it might be a good idea to check [these suggestions](https://github.com/cabrerahector/wordpress-popular-posts/wiki/7.-Performance) to make sure your site's performance stays up to par.
That's it!
= USAGE =
WordPress Popular Posts can be used in three different ways:
1. As a [widget](https://wordpress.org/support/article/wordpress-widgets/): simply drag and drop it into your theme's sidebar and configure it.
2. As a template tag: you can place it anywhere on your theme with [`wpp_get_mostpopular()`](https://github.com/cabrerahector/wordpress-popular-posts/wiki/2.-Template-tags#wpp_get_mostpopular).
3. Via [shortcode](https://github.com/cabrerahector/wordpress-popular-posts/wiki/1.-Using-WPP-on-posts-&-pages), so you can embed it inside a post or a page.
Make sure to stop by the **[Wiki](https://github.com/cabrerahector/wordpress-popular-posts/wiki)** as well, you'll find even more info there!
== Frequently Asked Questions ==
The FAQ section has been moved [here](https://github.com/cabrerahector/wordpress-popular-posts/wiki/5.-FAQ).
== Screenshots ==
1. The WordPress Popular Posts Widget.
2. The WordPress Popular Posts Widget on theme's sidebar.
3. Dashboard widget.
4. Statistics panel.
== Changelog ==
= 5.5.1 =
**If you're using a caching plugin flushing its cache after upgrading to this version is highly recommended.**
- Fixes an issue where the WordPress Popular Posts block would, under specific conditions, output code that may break the page layout (thanks @shoelaced and @themosaurus for the report!)
- Fixes minor variable declaration bug in wpp.js.min.
- Cleans up inline style tag (thanks @gabu69 and Lohen Florent for the suggestions!)
- Other minor fixes & changes.
[Release notes](https://cabrerahector.com/wordpress/wordpress-popular-posts-5-5-qol-improvements-and-php-5-announcement/#minor-updates-and-hotfixes)
= 5.5.0 =
- Adds the Sort by Average Daily Views option from the classic widget to the WordPress Popular Posts block.
- Adds the WordPress Date Format option from the classic widget to the WordPress Popular Posts block (please see announcements for details).
- Adds new Content Tags: author_name and author_url.
- Improves cached data garbage collection.
- Other minor performances improvements and fixes.
[Release notes](https://cabrerahector.com/wordpress/wordpress-popular-posts-5-5-qol-improvements-and-php-5-announcement/)
= 5.4.2 =
- Fixes a rare issue that prevented some users from seeing the Statistics chart (thanks Zsolt!)
[Release notes](https://cabrerahector.com/wordpress/wordpress-popular-posts-5-4-widget-block-improvements-plus-prep-work-for-csp-support/#5.4.2)
= 5.4.1 =
**If you're using a caching plugin flushing its cache after upgrading to this version is highly recommended.**
- Widget block: fixes WPP block loading block editor JS files on the front end.
- Adds filter hook to disable Block editor support.
- Improves logic of filter hooks `wpp_excerpt_more` and `wpp_title_more`.
- The Parameters section has been moved to the Wiki (see https://git.io/JEOrX).
[Release notes](https://cabrerahector.com/wordpress/wordpress-popular-posts-5-4-widget-block-improvements-plus-prep-work-for-csp-support/#5.4.1)
= 5.4.0 =
**If you're using a caching plugin flushing its cache after upgrading to this version is highly recommended.**
- The widget block is no longer an experimental feature and is now available to everyone (but it's still a WIP).
- Widget block: adds AJAX support to prevent caching plugins from caching your popular posts block.
- Widget block: adds WPML/Polylang support.
- Widget block: fixes widget heading not displaying.
- Widget themes: allow themes to detect the current post so it can be styled differently.
- Fixes `wpp_excerpt_more` filter hook not working (props to SchweizerSchoggi!)
- Adds filter hook `wpp_title_more` to allow customization of the prefix added to shortened post titles.
- Removes inline JavaScript code from WPP's dashboard in preparation for [WordPress' CSP adoption](https://core.trac.wordpress.org/ticket/51407).
[Release notes](https://cabrerahector.com/wordpress/wordpress-popular-posts-5-4-widget-block-improvements-plus-prep-work-for-csp-support/)
[Full Changelog](https://github.com/cabrerahector/wordpress-popular-posts/blob/master/changelog.md)
== Credits ==
* Flame graphic by freevector/Vecteezy.com.
== Upgrade Notice ==
= 5.5.1 =
If you're using a caching plugin flushing its cache after upgrading to this version is highly recommended.

View File

@ -0,0 +1,136 @@
<?php
/**
* Fired during plugin activation
*
* @since 1.0.0
* @package WordPressPopularPosts
*/
/**
* Fired during plugin activation.
*
* This class defines all code necessary to run during the plugin's activation.
*
* @since 1.0.0
* @package WordPressPopularPosts
* @author Hector Cabrera <me@cabrerahector.com>
*/
namespace WordPressPopularPosts\Activation;
class Activator {
/**
* Fired when the plugin is activated.
*
* @since 1.0.0
* @param bool $network_wide True if WPMU superadmin uses "Network Activate" action, false if WPMU is disabled or plugin is activated on an individual blog.
* @global object $wpdb
*/
public static function activate($network_wide)
{
global $wpdb;
if ( function_exists('is_multisite') && \is_multisite() ) {
// run activation for each blog in the network
if ( $network_wide ) {
$original_blog_id = \get_current_blog_id();
$blogs_ids = $wpdb->get_col("SELECT blog_id FROM {$wpdb->blogs}");
foreach( $blogs_ids as $blog_id ) {
\switch_to_blog($blog_id);
self::plugin_activate();
}
// switch back to current blog
\switch_to_blog($original_blog_id);
return;
}
}
self::plugin_activate();
}
/**
* When a new MU site is added, generate its WPP DB tables.
*
* @since 1.0.0
*/
public static function track_new_site()
{
self::plugin_activate();
}
/**
* On plugin activation, checks that the WPP database tables are present.
*
* @since 1.0.0
* @global object $wpdb
*/
private static function plugin_activate()
{
$wpp_ver = \get_option('wpp_ver');
if (
! $wpp_ver
|| version_compare($wpp_ver, WPP_VERSION, '<')
|| ( defined('WPP_DO_DB_TABLES') && WPP_DO_DB_TABLES )
) {
global $wpdb;
$prefix = $wpdb->prefix . "popularposts";
self::do_db_tables($prefix);
}
}
/**
* Creates/updates the database tables.
*
* @since 1.0.0
* @param string $prefix
* @global object $wpdb
*/
private static function do_db_tables($prefix)
{
global $wpdb;
$charset_collate = "";
if ( !empty($wpdb->charset) )
$charset_collate = "DEFAULT CHARACTER SET {$wpdb->charset} ";
if ( !empty($wpdb->collate) )
$charset_collate .= "COLLATE {$wpdb->collate}";
$sql = "
CREATE TABLE {$prefix}data (
postid bigint(20) NOT NULL,
day datetime NOT NULL,
last_viewed datetime NOT NULL,
pageviews bigint(20) DEFAULT 1,
PRIMARY KEY (postid)
) {$charset_collate} ENGINE=InnoDB;
CREATE TABLE {$prefix}summary (
ID bigint(20) NOT NULL AUTO_INCREMENT,
postid bigint(20) NOT NULL,
pageviews bigint(20) NOT NULL DEFAULT 1,
view_date date NOT NULL,
view_datetime datetime NOT NULL,
PRIMARY KEY (ID),
KEY postid (postid),
KEY view_date (view_date),
KEY view_datetime (view_datetime)
) {$charset_collate} ENGINE=InnoDB;
CREATE TABLE {$prefix}transients (
ID bigint(20) NOT NULL AUTO_INCREMENT,
tkey varchar(191) NOT NULL,
tkey_date datetime NOT NULL,
PRIMARY KEY (ID)
) {$charset_collate} ENGINE=InnoDB";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
\dbDelta($sql);
\update_option('wpp_ver', WPP_VERSION);
}
}

View File

@ -0,0 +1,66 @@
<?php
/**
* Fired during plugin deactivation
*
* @link https://cabrerahector.com
* @since 4.0.0
*
* @package WordPressPopularPosts
*/
/**
* Fired during plugin deactivation.
*
* This class defines all code necessary to run during the plugin's deactivation.
*
* @since 4.0.0
* @package WordPressPopularPosts
* @author Hector Cabrera <me@cabrerahector.com>
*/
namespace WordPressPopularPosts\Activation;
class Deactivator {
/**
* Fired when the plugin is deactivated.
*
* @since 1.0.0
* @global object wpbd
* @param bool network_wide True if WPMU superadmin uses "Network Activate" action, false if WPMU is disabled or plugin is activated on an individual blog
*/
public static function deactivate($network_wide)
{
global $wpdb;
if ( function_exists('is_multisite') && is_multisite() ) {
// Run deactivation for each blog in the network
if ( $network_wide ) {
$original_blog_id = get_current_blog_id();
$blogs_ids = $wpdb->get_col("SELECT blog_id FROM {$wpdb->blogs}");
foreach( $blogs_ids as $blog_id ) {
switch_to_blog($blog_id);
self::plugin_deactivate();
}
// Switch back to current blog
switch_to_blog($original_blog_id);
return;
}
}
self::plugin_deactivate();
}
/**
* On plugin deactivation, disables the shortcode and removes the scheduled task.
*
* @since 2.4.0
*/
private static function plugin_deactivate()
{
remove_shortcode('wpp');
wp_clear_scheduled_hook('wpp_cache_event');
wp_clear_scheduled_hook('wpp_maybe_performance_nag');
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,580 @@
<?php
if ( basename($_SERVER['SCRIPT_NAME']) == basename(__FILE__) )
exit('Please do not load this page directly');
$tabs = [
'stats' => __('Stats', 'wordpress-popular-posts'),
'tools' => __('Tools', 'wordpress-popular-posts'),
'params' => __('Parameters', 'wordpress-popular-posts'),
'debug' => 'Debug'
];
// Set active tab
if ( isset($_GET['tab'] ) && isset($tabs[$_GET['tab']] ) )
$current = $_GET['tab'];
else
$current = 'stats';
// Update options on form submission
if ( isset($_POST['section']) ) {
if ( "stats" == $_POST['section'] ) {
$current = 'stats';
if ( isset($_POST['wpp-update-stats-options-token']) && wp_verify_nonce($_POST['wpp-update-stats-options-token'], 'wpp-update-stats-options') ) {
$this->config['stats']['limit'] = ( \WordPressPopularPosts\Helper::is_number($_POST['stats_limit']) && $_POST['stats_limit'] > 0 ) ? $_POST['stats_limit'] : 10;
$this->config['stats']['post_type'] = empty($_POST['stats_type']) ? "post,page" : $_POST['stats_type'];
$this->config['stats']['freshness'] = empty($_POST['stats_freshness']) ? false : $_POST['stats_freshness'];
update_option('wpp_settings_config', $this->config);
echo "<div class=\"notice notice-success is-dismissible\"><p><strong>" . __('Settings saved.', 'wordpress-popular-posts') . "</strong></p></div>";
}
}
elseif ( "misc" == $_POST['section'] ) {
$current = 'tools';
if ( isset($_POST['wpp-update-misc-options-token'] ) && wp_verify_nonce($_POST['wpp-update-misc-options-token'], 'wpp-update-misc-options') ) {
$this->config['tools']['link']['target'] = $_POST['link_target'];
$this->config['tools']['css'] = $_POST['css'];
$this->config['tools']['experimental'] = empty($_POST['experimental_features']) ? false : $_POST['experimental_features'];
update_option('wpp_settings_config', $this->config);
echo "<div class=\"notice notice-success is-dismissible\"><p><strong>" . __('Settings saved.', 'wordpress-popular-posts') . "</strong></p></div>";
}
}
elseif ( "thumb" == $_POST['section'] ) {
$current = 'tools';
if ( isset($_POST['wpp-update-thumbnail-options-token']) && wp_verify_nonce($_POST['wpp-update-thumbnail-options-token'], 'wpp-update-thumbnail-options') ) {
if (
$_POST['thumb_source'] == "custom_field"
&& ( ! isset($_POST['thumb_field']) || empty($_POST['thumb_field']) )
) {
echo '<div id="wpp-message" class="error fade"><p>' . __('Please provide the name of your custom field.', 'wordpress-popular-posts') . '</p></div>';
}
else {
// thumbnail settings changed, flush transients
if ( $this->config['tools']['cache']['active'] ) {
$this->flush_transients();
}
$this->config['tools']['thumbnail']['source'] = $_POST['thumb_source'];
$this->config['tools']['thumbnail']['field'] = ( ! empty($_POST['thumb_field']) ) ? $_POST['thumb_field'] : "wpp_thumbnail";
$this->config['tools']['thumbnail']['default'] = ( ! empty($_POST['upload_thumb_src']) ) ? $_POST['upload_thumb_src'] : "";
$this->config['tools']['thumbnail']['resize'] = $_POST['thumb_field_resize'];
$this->config['tools']['thumbnail']['lazyload'] = (bool) $_POST['thumb_lazy_load'];
update_option('wpp_settings_config', $this->config );
echo "<div class=\"notice notice-success is-dismissible\"><p><strong>" . __('Settings saved.', 'wordpress-popular-posts') . "</strong></p></div>";
}
}
}
elseif ( "data" == $_POST['section'] && current_user_can('manage_options') ) {
$current = 'tools';
if ( isset($_POST['wpp-update-data-options-token'] ) && wp_verify_nonce($_POST['wpp-update-data-options-token'], 'wpp-update-data-options') ) {
$this->config['tools']['log']['level'] = $_POST['log_option'];
$this->config['tools']['log']['limit'] = $_POST['log_limit'];
$this->config['tools']['log']['expires_after'] = ( \WordPressPopularPosts\Helper::is_number($_POST['log_expire_time']) && $_POST['log_expire_time'] > 0 )
? $_POST['log_expire_time']
: 180;
$this->config['tools']['ajax'] = $_POST['ajax'];
// if any of the caching settings was updated, destroy all transients created by the plugin
if (
$this->config['tools']['cache']['active'] != $_POST['cache']
|| $this->config['tools']['cache']['interval']['time'] != $_POST['cache_interval_time']
|| $this->config['tools']['cache']['interval']['value'] != $_POST['cache_interval_value']
) {
$this->flush_transients();
}
$this->config['tools']['cache']['active'] = $_POST['cache'];
$this->config['tools']['cache']['interval']['time'] = $_POST['cache_interval_time'];
$this->config['tools']['cache']['interval']['value'] = ( isset($_POST['cache_interval_value']) && \WordPressPopularPosts\Helper::is_number($_POST['cache_interval_value']) && $_POST['cache_interval_value'] > 0 )
? $_POST['cache_interval_value']
: 1;
$this->config['tools']['sampling']['active'] = $_POST['sampling'];
$this->config['tools']['sampling']['rate'] = ( isset($_POST['sample_rate']) && \WordPressPopularPosts\Helper::is_number($_POST['sample_rate']) && $_POST['sample_rate'] > 0 )
? $_POST['sample_rate']
: 100;
update_option('wpp_settings_config', $this->config);
echo "<div class=\"notice notice-success is-dismissible\"><p><strong>" . __('Settings saved.', 'wordpress-popular-posts') . "</strong></p></div>";
}
}
}
?>
<nav id="wpp-menu">
<ul>
<li><a href="#" title="<?php esc_attr_e('Menu'); ?>"><span><?php _e('Menu'); ?></span></a></li>
<li <?php echo ('stats' == $current ) ? 'class="current"' : ''; ?>><a href="<?php echo admin_url('options-general.php?page=wordpress-popular-posts&tab=stats'); ?>" title="<?php esc_attr_e('Stats', 'wordpress-popular-posts'); ?>"><span><?php _e('Stats', 'wordpress-popular-posts'); ?></span></a></li>
<li <?php echo ('tools' == $current ) ? 'class="current"' : ''; ?>><a href="<?php echo admin_url('options-general.php?page=wordpress-popular-posts&tab=tools'); ?>" title="<?php esc_attr_e('Tools', 'wordpress-popular-posts'); ?>"><span><?php _e('Tools', 'wordpress-popular-posts'); ?></span></a></li>
<li <?php echo ('debug' == $current ) ? 'class="current"' : ''; ?>><a href="<?php echo admin_url('options-general.php?page=wordpress-popular-posts&tab=debug'); ?>" title="Debug"><span>Debug</span></a></li>
</ul>
</nav>
<div class="wpp-wrapper wpp-section-<?php echo $current; ?>">
<div class="wpp-header">
<h2>WordPress Popular Posts</h2>
<h3><?php echo $tabs[$current]; ?></h3>
</div>
<?php
// Stats chart
if ( 'stats' == $current ) {
$chart_data = json_decode(
$this->get_chart_data($this->config['stats']['range'], strtoupper($this->config['stats']['time_unit']), $this->config['stats']['time_quantity']),
true
);
?>
<a href="#" id="wpp-stats-config-btn" class="dashicons dashicons-admin-generic"></a>
<div id="wpp-stats-config" class="wpp-lightbox">
<form action="" method="post" id="wpp_stats_options" name="wpp_stats_options">
<label for="stats_type"><?php _e("Post type", 'wordpress-popular-posts'); ?>:</label>
<input type="text" name="stats_type" value="<?php echo esc_attr($this->config['stats']['post_type']); ?>" size="15">
<label for="stats_limits"><?php _e("Limit", 'wordpress-popular-posts'); ?>:</label>
<input type="text" name="stats_limit" value="<?php echo $this->config['stats']['limit']; ?>" size="5">
<label for="stats_freshness"><input type="checkbox" class="checkbox" <?php echo ($this->config['stats']['freshness']) ? 'checked="checked"' : ''; ?> id="stats_freshness" name="stats_freshness"> <small><?php _e('Display only posts published within the selected Time Range', 'wordpress-popular-posts'); ?></small></label>
<div class="clear"></div>
<br /><br />
<input type="hidden" name="section" value="stats">
<button type="submit" class="button-primary action"><?php _e("Apply", 'wordpress-popular-posts'); ?></button>
<button class="button-secondary action right"><?php _e("Cancel"); ?></button>
<?php wp_nonce_field('wpp-update-stats-options', 'wpp-update-stats-options-token'); ?>
</form>
</div>
<div id="wpp-stats-range" class="wpp-lightbox">
<form action="" method="post">
<ul class="wpp-lightbox-tabs">
<li class="active"><a href="#"><?php _e('Custom Time Range', 'wordpress-popular-posts'); ?></a></li>
<li><a href="#"><?php _e('Date Range', 'wordpress-popular-posts'); ?></a></li>
</ul>
<div class="wpp-lightbox-tab-content wpp-lightbox-tab-content-active" id="custom-time-range">
<input type="text" id="stats_range_time_quantity" name="stats_range_time_quantity" value="<?php echo $this->config['stats']['time_quantity']; ?>">
<select id="stats_range_time_unit" name="stats_range_time_unit">
<option <?php if ($this->config['stats']['time_unit'] == "minute") { ?>selected="selected"<?php } ?> value="minute"><?php _e("Minute(s)", 'wordpress-popular-posts'); ?></option>
<option <?php if ($this->config['stats']['time_unit'] == "hour") { ?>selected="selected"<?php } ?> value="hour"><?php _e("Hour(s)", 'wordpress-popular-posts'); ?></option>
<option <?php if ($this->config['stats']['time_unit'] == "day") { ?>selected="selected"<?php } ?> value="day"><?php _e("Day(s)", 'wordpress-popular-posts'); ?></option>
</select>
</div>
<div class="wpp-lightbox-tab-content" id="custom-date-range">
<input type="text" id="stats_range_date" name="stats_range_date" value="" placeholder="<?php esc_attr_e('Select a date...', 'wordpress-popular-posts'); ?>" readonly>
</div>
<div class="clear"></div>
<br />
<button type="submit" class="button-primary action">
<?php _e("Apply", 'wordpress-popular-posts'); ?>
</button>
<button class="button-secondary action right">
<?php _e("Cancel"); ?>
</button>
</form>
</div>
<div id="wpp-chart-wrapper">
<h4><?php echo $chart_data['totals']['label_summary']; ?></h4>
<h5><?php echo $chart_data['totals']['label_date_range']; ?></h5>
<ul class="wpp-header-nav" id="wpp-time-ranges">
<li <?php echo ('daily' == $this->config['stats']['range'] || 'today' == $this->config['stats']['range'] ) ? ' class="current"' : ''; ?>><a href="#" data-range="today" title="<?php esc_attr_e('Today', 'wordpress-popular-posts'); ?>"><?php _e('Today', 'wordpress-popular-posts'); ?></a></li>
<li <?php echo ('daily' == $this->config['stats']['range'] || 'last24hours' == $this->config['stats']['range'] ) ? ' class="current"' : ''; ?>><a href="#" data-range="last24hours" title="<?php esc_attr_e('Last 24 hours', 'wordpress-popular-posts'); ?>">24h</a></li>
<li <?php echo ('weekly' == $this->config['stats']['range'] || 'last7days' == $this->config['stats']['range'] ) ? ' class="current"' : ''; ?>><a href="#" data-range="last7days" title="<?php esc_attr_e('Last 7 days', 'wordpress-popular-posts'); ?>">7d</a></li>
<li <?php echo ('monthly' == $this->config['stats']['range'] || 'last30days' == $this->config['stats']['range'] ) ? ' class="current"' : ''; ?>><a href="#" data-range="last30days" title="<?php esc_attr_e('Last 30 days', 'wordpress-popular-posts'); ?>">30d</a></li>
<li <?php echo ('custom' == $this->config['stats']['range'] ) ? ' class="current"' : ''; ?>><a href="#" data-range="custom" title="<?php esc_attr_e('Custom', 'wordpress-popular-posts'); ?>"><?php _e('Custom', 'wordpress-popular-posts'); ?></a></li>
</ul>
<div id="wpp-chart">
<p><?php echo sprintf( __('Err... A nice little chart is supposed to be here, instead you are seeing this because your browser is too old. <br /> Please <a href="%s" target="_blank">get a better browser</a>.', 'wordpress-popular-posts'), 'https://browsehappy.com/'); ?></p>
</div>
</div>
<?php
} // End stats chart
?>
<div id="wpp-listing" class="wpp-content"<?php echo ('stats' == $current ) ? '' : ' style="display: none;"'; ?>>
<ul class="wpp-tabbed-nav">
<li class="active"><a href="#" title="<?php esc_attr_e('See your most viewed posts within the selected time range', 'wordpress-popular-posts'); ?>"><span class="wpp-icon-eye"></span><span><?php _e('Most viewed', 'wordpress-popular-posts'); ?></span></a></li>
<li><a href="#" title="<?php esc_attr_e('See your most commented posts within the selected time range', 'wordpress-popular-posts'); ?>"><span class="wpp-icon-comment"></span><span><?php _e('Most commented', 'wordpress-popular-posts'); ?></span></a></li>
<li><a href="#" title="<?php esc_attr_e('See your most viewed posts within the last hour', 'wordpress-popular-posts'); ?>"><span class="wpp-icon-rocket"></span><span><?php _e('Trending now', 'wordpress-popular-posts'); ?></span></a></li>
<li><a href="#" title="<?php esc_attr_e('See your most viewed posts of all time', 'wordpress-popular-posts'); ?>"><span class="wpp-icon-award"></span><span><?php _e('Hall of Fame', 'wordpress-popular-posts'); ?></span></a></li>
</ul>
<div class="wpp-tab-content wpp-tab-content-active">
<span class="spinner"></span>
</div>
<div class="wpp-tab-content">
<span class="spinner"></span>
</div>
<div class="wpp-tab-content">
<span class="spinner"></span>
</div>
<div class="wpp-tab-content">
<?php
$args = [
'range' => 'all',
'post_type' => $this->config['stats']['post_type'],
'order_by' => 'views',
'limit' => $this->config['stats']['limit'],
'stats_tag' => [
'comment_count' => 1,
'views' => 1,
'date' => [
'active' => 1
]
]
];
$hof = new \WordPressPopularPosts\Query($args);
$posts = $hof->get_posts();
$this->render_list($posts, 'hof');
?>
</div>
</div>
<!-- Start tools -->
<div id="wpp_tools" <?php echo ( "tools" == $current ) ? '' : ' style="display: none;"'; ?>>
<h3 class="wmpp-subtitle"><?php _e("Thumbnails", 'wordpress-popular-posts'); ?></h3>
<form action="" method="post" id="wpp_thumbnail_options" name="wpp_thumbnail_options">
<table class="form-table">
<tbody>
<tr valign="top">
<th scope="row"><label for="thumb_default"><?php _e("Default thumbnail", 'wordpress-popular-posts'); ?>:</label></th>
<td>
<?php
$fallback_thumbnail_url = trim($this->config['tools']['thumbnail']['default']);
if ( ! $fallback_thumbnail_url )
$fallback_thumbnail_url = $this->thumbnail->get_default_url();
$fallback_thumbnail_url = str_replace(
parse_url(
$fallback_thumbnail_url
, PHP_URL_SCHEME
) . ':',
'',
$fallback_thumbnail_url
);
?>
<div id="thumb-review">
<img src="<?php echo esc_url($fallback_thumbnail_url); ?>" alt="" />
</div>
<input id="upload_thumb_button" type="button" class="button" value="<?php _e("Change thumbnail", 'wordpress-popular-posts'); ?>">
<input type="hidden" id="upload_thumb_src" name="upload_thumb_src" value="">
<p class="description"><?php _e("This image will be displayed when no thumbnail is available", 'wordpress-popular-posts'); ?>.</p>
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="thumb_source"><?php _e("Pick image from", 'wordpress-popular-posts'); ?>:</label></th>
<td>
<select name="thumb_source" id="thumb_source">
<option <?php if ($this->config['tools']['thumbnail']['source'] == "featured") { ?>selected="selected"<?php } ?> value="featured"><?php _e("Featured image", 'wordpress-popular-posts'); ?></option>
<option <?php if ($this->config['tools']['thumbnail']['source'] == "first_image") { ?>selected="selected"<?php } ?> value="first_image"><?php _e("First image on post", 'wordpress-popular-posts'); ?></option>
<option <?php if ($this->config['tools']['thumbnail']['source'] == "first_attachment") { ?>selected="selected"<?php } ?> value="first_attachment"><?php _e("First attachment", 'wordpress-popular-posts'); ?></option>
<option <?php if ($this->config['tools']['thumbnail']['source'] == "custom_field") { ?>selected="selected"<?php } ?> value="custom_field"><?php _e("Custom field", 'wordpress-popular-posts'); ?></option>
</select>
<br />
<p class="description"><?php _e("Tell WordPress Popular Posts where it should get thumbnails from", 'wordpress-popular-posts'); ?>.</p>
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="thumb_lazy_load"><?php _e("Lazy load", 'wordpress-popular-posts'); ?>:</label> <small>[<a href="https://github.com/cabrerahector/wordpress-popular-posts/wiki/7.-Performance#lazy-loading" target="_blank" title="<?php _e('What is this?', 'wordpress-popular-posts'); ?>">?</a>]</small></th>
<td>
<select name="thumb_lazy_load" id="thumb_lazy_load">
<option <?php if ( ! $this->config['tools']['thumbnail']['lazyload'] ) { ?>selected="selected"<?php } ?> value="0"><?php _e("No", 'wordpress-popular-posts'); ?></option>
<option <?php if ( $this->config['tools']['thumbnail']['lazyload'] ) { ?>selected="selected"<?php } ?> value="1"><?php _e("Yes", 'wordpress-popular-posts'); ?></option>
</select>
</td>
</tr>
<tr valign="top" <?php if ($this->config['tools']['thumbnail']['source'] != "custom_field") { ?>style="display: none;"<?php } ?> id="row_custom_field">
<th scope="row"><label for="thumb_field"><?php _e("Custom field name", 'wordpress-popular-posts'); ?>:</label></th>
<td>
<input type="text" id="thumb_field" name="thumb_field" value="<?php echo esc_attr($this->config['tools']['thumbnail']['field']); ?>" size="10" <?php if ($this->config['tools']['thumbnail']['source'] != "custom_field") { ?>style="display: none;"<?php } ?> />
</td>
</tr>
<tr valign="top" <?php if ($this->config['tools']['thumbnail']['source'] != "custom_field") { ?>style="display: none;"<?php } ?> id="row_custom_field_resize">
<th scope="row"><label for="thumb_field_resize"><?php _e("Resize image from Custom field?", 'wordpress-popular-posts'); ?>:</label></th>
<td>
<select name="thumb_field_resize" id="thumb_field_resize">
<option <?php if ( !$this->config['tools']['thumbnail']['resize'] ) { ?>selected="selected"<?php } ?> value="0"><?php _e("No, use image as is", 'wordpress-popular-posts'); ?></option>
<option <?php if ($this->config['tools']['thumbnail']['resize'] == 1 ) { ?>selected="selected"<?php } ?> value="1"><?php _e("Yes", 'wordpress-popular-posts'); ?></option>
</select>
</td>
</tr>
<?php
$wp_upload_dir = wp_get_upload_dir();
if ( is_dir($wp_upload_dir['basedir'] . "/" . 'wordpress-popular-posts') ) :
?>
<tr valign="top">
<th scope="row"></th>
<td>
<input type="button" name="wpp-reset-image-cache" id="wpp-reset-image-cache" class="button-secondary" value="<?php _e("Empty image cache", 'wordpress-popular-posts'); ?>">
<p class="description"><?php _e("Use this button to clear WPP's thumbnails cache", 'wordpress-popular-posts'); ?>.</p>
</td>
</tr>
<?php
endif;
?>
<tr valign="top">
<td colspan="2">
<input type="hidden" name="section" value="thumb">
<input type="submit" class="button-primary action" id="btn_th_ops" value="<?php _e("Apply", 'wordpress-popular-posts'); ?>" name="">
</td>
</tr>
</tbody>
</table>
<?php wp_nonce_field('wpp-update-thumbnail-options', 'wpp-update-thumbnail-options-token'); ?>
</form>
<br />
<p style="display: <?php echo ( current_user_can('manage_options') ) ? 'block' : 'none'; ?>; float:none; clear:both;">&nbsp;</p>
<h3 class="wmpp-subtitle" style="display: <?php echo ( current_user_can('manage_options') ) ? 'block' : 'none'; ?>"><?php _e("Data", 'wordpress-popular-posts'); ?></h3>
<form action="" method="post" id="wpp_ajax_options" name="wpp_ajax_options" style="display: <?php echo ( current_user_can('manage_options') ) ? 'block' : 'none'; ?>">
<table class="form-table">
<tbody>
<tr valign="top">
<th scope="row"><label for="log_option"><?php _e("Log views from", 'wordpress-popular-posts'); ?>:</label></th>
<td>
<select name="log_option" id="log_option">
<option <?php if ($this->config['tools']['log']['level'] == 0) { ?>selected="selected"<?php } ?> value="0"><?php _e("Visitors only", 'wordpress-popular-posts'); ?></option>
<option <?php if ($this->config['tools']['log']['level'] == 2) { ?>selected="selected"<?php } ?> value="2"><?php _e("Logged-in users only", 'wordpress-popular-posts'); ?></option>
<option <?php if ($this->config['tools']['log']['level'] == 1) { ?>selected="selected"<?php } ?> value="1"><?php _e("Everyone", 'wordpress-popular-posts'); ?></option>
</select>
<br />
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="log_limit"><?php _e("Log limit", 'wordpress-popular-posts'); ?>:</label></th>
<td>
<select name="log_limit" id="log_limit">
<option <?php if ($this->config['tools']['log']['limit'] == 0) { ?>selected="selected"<?php } ?> value="0"><?php _e("Disabled", 'wordpress-popular-posts'); ?></option>
<option <?php if ($this->config['tools']['log']['limit'] == 1) { ?>selected="selected"<?php } ?> value="1"><?php _e("Keep data for", 'wordpress-popular-posts'); ?></option>
</select>
<label for="log_expire_time"<?php echo ($this->config['tools']['log']['limit'] == 0) ? ' style="display: none;"' : ''; ?>>
<input type="text" id="log_expire_time" name="log_expire_time" value="<?php echo esc_attr($this->config['tools']['log']['expires_after']); ?>" size="3"> <?php _e("day(s)", 'wordpress-popular-posts'); ?>
</label>
<p class="description"<?php echo ($this->config['tools']['log']['limit'] == 0) ? ' style="display: none;"' : ''; ?>><?php _e("Data older than the specified time frame will be automatically discarded", 'wordpress-popular-posts'); ?>.</p>
<br <?php echo (1 == $this->config['tools']['log']['limit']) ? 'style="display: none;"' : ''; ?>/>
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="ajax"><?php _e("Ajaxify widget", 'wordpress-popular-posts'); ?>:</label></th>
<td>
<select name="ajax" id="ajax">
<option <?php if (! $this->config['tools']['ajax']) { ?>selected="selected"<?php } ?> value="0"><?php _e("Disabled", 'wordpress-popular-posts'); ?></option>
<option <?php if ($this->config['tools']['ajax']) { ?>selected="selected"<?php } ?> value="1"><?php _e("Enabled", 'wordpress-popular-posts'); ?></option>
</select>
<br />
<p class="description"><?php _e("If you are using a caching plugin such as WP Super Cache, enabling this feature will keep the popular list from being cached by it", 'wordpress-popular-posts'); ?>.</p>
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="cache"><?php _e("Data Caching", 'wordpress-popular-posts'); ?>:</label> <small>[<a href="https://github.com/cabrerahector/wordpress-popular-posts/wiki/7.-Performance#caching-db-queries-results" target="_blank" title="<?php _e('What is this?', 'wordpress-popular-posts'); ?>">?</a>]</small></th>
<td>
<select name="cache" id="cache">
<option <?php if ( ! $this->config['tools']['cache']['active'] ) { ?>selected="selected"<?php } ?> value="0"><?php _e("Never cache", 'wordpress-popular-posts'); ?></option>
<option <?php if ( $this->config['tools']['cache']['active'] ) { ?>selected="selected"<?php } ?> value="1"><?php _e("Enable caching", 'wordpress-popular-posts'); ?></option>
</select>
<br />
<p class="description"><?php _e("WPP can cache the popular list for a specified amount of time. Recommended for large / high traffic sites", 'wordpress-popular-posts'); ?>.</p>
</td>
</tr>
<tr valign="top" <?php if ( ! $this->config['tools']['cache']['active'] ) { ?>style="display: none;"<?php } ?> id="cache_refresh_interval">
<th scope="row"><label for="cache_interval_value"><?php _e("Refresh cache every", 'wordpress-popular-posts'); ?>:</label></th>
<td>
<input name="cache_interval_value" type="text" id="cache_interval_value" value="<?php echo ( isset($this->config['tools']['cache']['interval']['value']) ) ? (int) $this->config['tools']['cache']['interval']['value'] : 1; ?>" class="small-text">
<select name="cache_interval_time" id="cache_interval_time">
<option <?php if ($this->config['tools']['cache']['interval']['time'] == "minute") { ?>selected="selected"<?php } ?> value="minute"><?php _e("Minute(s)", 'wordpress-popular-posts'); ?></option>
<option <?php if ($this->config['tools']['cache']['interval']['time'] == "hour") { ?>selected="selected"<?php } ?> value="hour"><?php _e("Hour(s)", 'wordpress-popular-posts'); ?></option>
<option <?php if ($this->config['tools']['cache']['interval']['time'] == "day") { ?>selected="selected"<?php } ?> value="day"><?php _e("Day(s)", 'wordpress-popular-posts'); ?></option>
<option <?php if ($this->config['tools']['cache']['interval']['time'] == "week") { ?>selected="selected"<?php } ?> value="week"><?php _e("Week(s)", 'wordpress-popular-posts'); ?></option>
<option <?php if ($this->config['tools']['cache']['interval']['time'] == "month") { ?>selected="selected"<?php } ?> value="month"><?php _e("Month(s)", 'wordpress-popular-posts'); ?></option>
<option <?php if ($this->config['tools']['cache']['interval']['time'] == "year") { ?>selected="selected"<?php } ?> value="month"><?php _e("Year(s)", 'wordpress-popular-posts'); ?></option>
</select>
<br />
<p class="description" style="display: none;" id="cache_too_long"><?php _e("Really? That long?", 'wordpress-popular-posts'); ?></p>
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="sampling"><?php _e("Data Sampling", 'wordpress-popular-posts'); ?>:</label> <small>[<a href="https://github.com/cabrerahector/wordpress-popular-posts/wiki/7.-Performance#data-sampling" target="_blank" title="<?php _e('What is this?', 'wordpress-popular-posts'); ?>">?</a>]</small></th>
<td>
<select name="sampling" id="sampling">
<option <?php if ( !$this->config['tools']['sampling']['active'] ) { ?>selected="selected"<?php } ?> value="0"><?php _e("Disabled", 'wordpress-popular-posts'); ?></option>
<option <?php if ( $this->config['tools']['sampling']['active'] ) { ?>selected="selected"<?php } ?> value="1"><?php _e("Enabled", 'wordpress-popular-posts'); ?></option>
</select>
<br />
<p class="description"><?php echo sprintf( __('By default, WordPress Popular Posts stores in database every single visit your site receives. For small / medium sites this is generally OK, but on large / high traffic sites the constant writing to the database may have an impact on performance. With <a href="%1$s" target="_blank">data sampling</a>, WordPress Popular Posts will store only a subset of your traffic and report on the tendencies detected in that sample set (for more, <a href="%2$s" target="_blank">please read here</a>)', 'wordpress-popular-posts'), 'http://en.wikipedia.org/wiki/Sample_%28statistics%29', 'https://github.com/cabrerahector/wordpress-popular-posts/wiki/7.-Performance#data-sampling'); ?>.</p>
</td>
</tr>
<tr valign="top" <?php if ( ! $this->config['tools']['sampling']['active'] ) { ?>style="display: none;"<?php } ?>>
<th scope="row"><label for="sample_rate"><?php _e("Sample Rate", 'wordpress-popular-posts'); ?>:</label></th>
<td>
<input name="sample_rate" type="text" id="sample_rate" value="<?php echo ( isset($this->config['tools']['sampling']['rate']) ) ? (int) $this->config['tools']['sampling']['rate'] : 100; ?>" class="small-text">
<br />
<p class="description"><?php echo sprintf(__("A sampling rate of %d is recommended for large / high traffic sites. For lower traffic sites, you should lower the value", 'wordpress-popular-posts'), 100); ?>.</p>
</td>
</tr>
<tr valign="top">
<td colspan="2">
<input type="hidden" name="section" value="data">
<input type="submit" class="button-primary action" id="btn_ajax_ops" value="<?php _e("Apply", 'wordpress-popular-posts'); ?>" name="">
</td>
</tr>
</tbody>
</table>
<?php wp_nonce_field('wpp-update-data-options', 'wpp-update-data-options-token'); ?>
</form>
<br />
<p style="display: block; float:none; clear: both;">&nbsp;</p>
<h3 class="wmpp-subtitle"><?php _e("Miscellaneous", 'wordpress-popular-posts'); ?></h3>
<form action="" method="post" id="wpp_link_options" name="wpp_link_options">
<table class="form-table">
<tbody>
<tr valign="top">
<th scope="row"><label for="link_target"><?php _e("Open links in", 'wordpress-popular-posts'); ?>:</label></th>
<td>
<select name="link_target" id="link_target">
<option <?php if ($this->config['tools']['link']['target'] == '_self') { ?>selected="selected"<?php } ?> value="_self"><?php _e("Current window", 'wordpress-popular-posts'); ?></option>
<option <?php if ($this->config['tools']['link']['target'] == '_blank') { ?>selected="selected"<?php } ?> value="_blank"><?php _e("New tab/window", 'wordpress-popular-posts'); ?></option>
</select>
<br />
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="css"><?php _e("Use plugin's stylesheet", 'wordpress-popular-posts'); ?>:</label></th>
<td>
<select name="css" id="css">
<option <?php if ($this->config['tools']['css']) { ?>selected="selected"<?php } ?> value="1"><?php _e("Enabled", 'wordpress-popular-posts'); ?></option>
<option <?php if (!$this->config['tools']['css']) { ?>selected="selected"<?php } ?> value="0"><?php _e("Disabled", 'wordpress-popular-posts'); ?></option>
</select>
<br />
<p class="description"><?php _e("By default, the plugin includes a stylesheet called wpp.css which you can use to style your popular posts listing. If you wish to use your own stylesheet or do not want it to have it included in the header section of your site, use this.", 'wordpress-popular-posts'); ?></p>
</td>
</tr>
<tr valign="top">
<th scope="row"><label for="experimental_features"><?php _e("Enable experimental features", 'wordpress-popular-posts'); ?>:</label></th>
<td>
<input type="checkbox" class="checkbox" id="experimental_features" name="experimental_features" <?php echo ($this->config['tools']['experimental']) ? 'checked="checked"' : ''; ?>>
</td>
</tr>
<tr valign="top">
<td colspan="2">
<input type="hidden" name="section" value="misc">
<input type="submit" class="button-primary action" value="<?php _e("Apply", 'wordpress-popular-posts'); ?>" name="">
</td>
</tr>
</tbody>
</table>
<?php wp_nonce_field('wpp-update-misc-options', 'wpp-update-misc-options-token'); ?>
</form>
<br />
<p style="display: block; float: none; clear: both;">&nbsp;</p>
<div style="display: <?php echo ( current_user_can('manage_options') ) ? 'block' : 'none'; ?>">
<br /><br />
<p><?php _e('WordPress Popular Posts maintains data in two separate tables: one for storing the most popular entries on a daily basis (from now on, "cache"), and another one to keep the All-time data (from now on, "historical data" or just "data"). If for some reason you need to clear the cache table, or even both historical and cache tables, please use the buttons below to do so.', 'wordpress-popular-posts') ?></p>
<p><input type="button" name="wpp-reset-cache" id="wpp-reset-cache" class="button-secondary" value="<?php _e("Empty cache", 'wordpress-popular-posts'); ?>"> <label for="wpp-reset-cache"><small><?php _e('Use this button to manually clear entries from WPP cache only', 'wordpress-popular-posts'); ?></small></label></p>
<p><input type="button" name="wpp-reset-all" id="wpp-reset-all" class="button-secondary" value="<?php _e("Clear all data", 'wordpress-popular-posts'); ?>"> <label for="wpp-reset-all"><small><?php _e('Use this button to manually clear entries from all WPP data tables', 'wordpress-popular-posts'); ?></small></label></p>
</div>
</div>
<!-- End tools -->
<!-- Start debug -->
<?php
global $wpdb, $wp_version;
$my_theme = wp_get_theme();
$site_plugins = get_plugins();
$plugin_names = [];
$performance_nag = get_option('wpp_performance_nag');
if ( ! $performance_nag ) {
$performance_nag = [
'status' => 0,
'last_checked' => null
];
}
switch($performance_nag['status']) {
case 0:
$performance_nag_status = 'Inactive';
break;
case 1:
$performance_nag_status = 'Active';
break;
case 2:
$performance_nag_status = 'Remind me later';
break;
case 3:
$performance_nag_status = 'Dismissed';
break;
default:
$performance_nag_status = 'Inactive';
break;
}
foreach( $site_plugins as $main_file => $plugin_meta ) :
if ( ! is_plugin_active($main_file) )
continue;
$plugin_names[] = sanitize_text_field($plugin_meta['Name'] . ' ' . $plugin_meta['Version']);
endforeach;
?>
<div id="wpp_debug" <?php echo ( "debug" == $current ) ? '' : ' style="display: none;"'; ?>>
<h3>Plugin Configuration</h3>
<p><strong>Performance Nag:</strong> <?php echo $performance_nag_status; ?></p>
<p><strong>Log Limit:</strong> <?php echo ( $this->config['tools']['log']['limit'] ) ? 'Yes, keep data for ' . $this->config['tools']['log']['expires_after'] . ' days' : 'No'; ?></p>
<p><strong>Log Views From:</strong> <?php echo ( 0 == $this->config['tools']['log']['level'] ) ? 'Visitors only' : ( (2 == $this->config['tools']['log']['level']) ? 'Logged-in users only' : 'Everyone' ); ?></p>
<p><strong>Data Caching:</strong> <?php echo ( $this->config['tools']['cache']['active'] ) ? 'Yes, ' . $this->config['tools']['cache']['interval']['value'] . ' ' . $this->config['tools']['cache']['interval']['time'] : 'No'; ?></p>
<p><strong>Data Sampling:</strong> <?php echo ( $this->config['tools']['sampling']['active'] ) ? 'Yes, with a rate of ' . $this->config['tools']['sampling']['rate'] : 'No'; ?></p>
<p><strong>External object cache:</strong> <?php echo ( wp_using_ext_object_cache() ) ? 'Yes' : 'No'; ?></p>
<p><strong>WPP_CACHE_VIEWS:</strong> <?php echo ( defined('WPP_CACHE_VIEWS') && WPP_CACHE_VIEWS ) ? 'Yes' : 'No'; ?></p>
<br />
<h3>System Info</h3>
<p><strong>PHP version:</strong> <?php echo phpversion(); ?></p>
<p><strong>PHP extensions:</strong> <?php echo implode(', ', get_loaded_extensions()); ?></p>
<p><strong>Database version:</strong> <?php echo $wpdb->get_var("SELECT VERSION();"); ?></p>
<p><strong>InnoDB availability:</strong> <?php echo $wpdb->get_var("SELECT SUPPORT FROM INFORMATION_SCHEMA.ENGINES WHERE ENGINE = 'InnoDB';"); ?></p>
<p><strong>WordPress version:</strong> <?php echo $wp_version; ?></p>
<p><strong>Multisite:</strong> <?php echo ( function_exists('is_multisite') && is_multisite() ) ? 'Yes' : 'No'; ?></p>
<p><strong>Active plugins:</strong> <?php echo implode(', ', $plugin_names); ?></p>
<p><strong>Theme:</strong> <?php echo $my_theme->get('Name') . ' (' . $my_theme->get('Version') . ') by ' . $my_theme->get('Author'); ?></p>
</div>
<!-- End debug -->
</div>

View File

@ -0,0 +1,27 @@
<?php
/**
* Contract to build blocks.
*/
namespace WordPressPopularPosts\Block;
abstract class Block {
/**
*
*/
public function hooks()
{
add_action('init', [$this, 'register']);
}
/**
*
*/
abstract function register();
/**
*
*/
abstract function render(array $attributes);
}

View File

@ -0,0 +1,522 @@
<?php
namespace WordPressPopularPosts\Block\Widget;
use WordPressPopularPosts\Helper;
use WordPressPopularPosts\Query;
use WordPressPopularPosts\Output;
use WordPressPopularPosts\Block\Block;
class Widget extends Block
{
/**
* Administrative settings.
*
* @since 5.4.0
* @var array
* @access private
*/
private $admin_options = [];
/**
* Image object.
*
* @since 5.4.0
* @var WordPressPopularPosts\Image
* @access private
*/
private $thumbnail;
/**
* Output object.
*
* @since 5.4.0
* @var \WordPressPopularPosts\Output
* @access private
*/
private $output;
/**
* Translate object.
*
* @since 5.4.0
* @var \WordPressPopularPosts\Translate $translate
* @access private
*/
private $translate;
/**
* Themer object.
*
* @since 5.4.0
* @var \WordPressPopularPosts\Themer $themer
* @access private
*/
private $themer;
/**
* Default attributes.
*
* @since 5.4.0
* @var array $defaults
* @access private
*/
private $defaults;
/**
* Construct.
*
* @since 5.4.0
* @param array $config
* @param \WordPressPopularPosts\Output $output
* @param \WordPressPopularPosts\Image $image
* @param \WordPressPopularPosts\Translate $translate
* @param \WordPressPopularPosts\Themer $themer
*/
public function __construct(array $config, \WordPressPopularPosts\Output $output, \WordPressPopularPosts\Image $thumbnail, \WordPressPopularPosts\Translate $translate, \WordPressPopularPosts\Themer $themer)
{
$this->admin_options = $config;
$this->output = $output;
$this->thumbnail = $thumbnail;
$this->translate = $translate;
$this->themer = $themer;
$this->defaults = [
'title' => '',
'limit' => 10,
'offset' => 0,
'range' => 'daily',
'time_unit' => 'hour',
'time_quantity' => 24,
'freshness' => false,
'order_by' => 'views',
'post_type' => 'post',
'pid' => '',
'cat' => '',
'taxonomy' => 'category',
'tax' => '',
'term_id' => '',
'author' => '',
'title_length' => 0,
'title_by_words' => 0,
'excerpt_length' => 0,
'excerpt_format' => 0,
'excerpt_by_words' => 0,
'thumbnail_width' => 0,
'thumbnail_height' => 0,
'thumbnail_build' => 'manual',
'thumbnail_size' => '',
'rating' => false,
'stats_comments' => false,
'stats_views' => true,
'stats_author' => false,
'stats_date' => false,
'stats_date_format' => 'F j, Y',
'stats_category' => false,
'stats_taxonomy' => false,
'custom_html' => false,
'wpp_start' => '<ul class="wpp-list">',
'wpp_end' => '</ul>',
'header_start' => '<h2>',
'header_end' => '</h2>',
'post_html' => '',
'theme' => ''
];
}
/**
* Registers the block.
*
* @since 5.4.0
*/
public function register()
{
// Block editor is not available, bail.
if ( ! function_exists('register_block_type') ) {
return;
}
$block_editor_support = apply_filters('wpp_block_editor_support', true);
if ( ! $block_editor_support ) {
return;
}
wp_register_script(
'block-wpp-widget-js',
plugin_dir_url(dirname(dirname(dirname(__FILE__)))) . 'assets/js/blocks/block-wpp-widget.js',
['wp-blocks', 'wp-i18n', 'wp-element', 'wp-block-editor', 'wp-server-side-render'],
filemtime(plugin_dir_path(dirname(dirname(dirname(__FILE__)))) . 'assets/js/blocks/block-wpp-widget.js')
);
wp_register_style(
'block-wpp-editor-css',
plugins_url('editor.css', __FILE__),
[],
filemtime(plugin_dir_path(__FILE__) . 'editor.css')
);
register_block_type(
'wordpress-popular-posts/widget',
[
'editor_style' => 'block-wpp-editor-css',
'editor_script' => 'block-wpp-widget-js',
'render_callback' => [$this, 'render'],
'attributes' => [
'_editMode' => [
'type' => 'boolean',
'default' => true
],
'_isSelected' => [
'type' => 'boolean',
'default' => false
],
'title' => [
'type' => 'string',
'default' => ''
],
'limit' => [
'type' =>'number',
'default' => 10
],
'offset' => [
'type' => 'number',
'default' => 0
],
'order_by' => [
'type' => 'string',
'default' => 'views'
],
'range' => [
'type' => 'string',
'default' => 'last24hours'
],
'time_quantity' => [
'type' => 'number',
'default' => 24
],
'time_unit' => [
'type' => 'string',
'default' => 'hour'
],
'freshness' => [
'type' => 'boolean',
'default' => false
],
/* filters */
'post_type' => [
'type' => 'string',
'default' => 'post'
],
'pid' => [
'type' => 'string',
'default' => ''
],
'author' => [
'type' => 'string',
'default' => ''
],
'tax' => [
'type' => 'string',
'default' => ''
],
'term_id' => [
'type' => 'string',
'default' => ''
],
/* post settings */
'shorten_title' => [
'type' => 'boolean',
'default' => false
],
'title_length' => [
'type' =>'number',
'default' => 0
],
'title_by_words' => [
'type' =>'number',
'default' => 0
],
'display_post_excerpt' => [
'type' => 'boolean',
'default' => false
],
'excerpt_format' => [
'type' => 'boolean',
'default' => false
],
'excerpt_length' => [
'type' =>'number',
'default' => 0
],
'excerpt_by_words' => [
'type' =>'number',
'default' => 0
],
'display_post_thumbnail' => [
'type' => 'boolean',
'default' => false
],
'thumbnail_width' => [
'type' =>'number',
'default' => 0
],
'thumbnail_height' => [
'type' =>'number',
'default' => 0
],
'thumbnail_build' => [
'type' => 'string',
'default' => 'manual'
],
'thumbnail_size' => [
'type' => 'string',
'default' => ''
],
/* stats tag settings */
'stats_comments' => [
'type' => 'boolean',
'default' => false
],
'stats_views' => [
'type' => 'boolean',
'default' => true
],
'stats_author' => [
'type' => 'boolean',
'default' => false
],
'stats_date' => [
'type' => 'boolean',
'default' => false
],
'stats_date_format' => [
'type' => 'string',
'default' => 'F j, Y'
],
'stats_taxonomy' => [
'type' => 'boolean',
'default' => false
],
'taxonomy' => [
'type' => 'string',
'default' => 'category'
],
/* HTML markup settings */
'custom_html' => [
'type' => 'boolean',
'default' => false
],
'header_start' => [
'type' => 'string',
'default' => '<h2>'
],
'header_end' => [
'type' => 'string',
'default' => '</h2>'
],
'wpp_start' => [
'type' => 'string',
'default' => '<ul class="wpp-list">'
],
'wpp_end' => [
'type' => 'string',
'default' => '</ul>'
],
'post_html' => [
'type' => 'string',
'default' => '<li>{thumb} {title} <span class="wpp-meta post-stats">{stats}</span></li>'
],
'theme' => [
'type' => 'string',
'default' => ''
],
]
]
);
}
/**
* Renders the block.
*
* @since 5.4.0
* @param array
* @return string
*/
public function render(array $attributes)
{
extract($this->parse_attributes($attributes));
$html = '<div class="widget popular-posts' . (( isset($attributes['className']) && $attributes['className'] ) ? ' '. esc_attr($attributes['className']) : '') . '">';
// possible values for "Time Range" and "Order by"
$time_units = ["minute", "hour", "day", "week", "month"];
$range_values = ["daily", "last24hours", "weekly", "last7days", "monthly", "last30days", "all", "custom"];
$order_by_values = ["comments", "views", "avg"];
$theme_data = $this->themer->get_theme($theme);
if ( ! isset($theme_data['json']) ) {
$theme = '';
}
$query_args = [
'title' => strip_tags($title),
'limit' => ( ! empty($limit) && Helper::is_number($limit) && $limit > 0 ) ? $limit : 10,
'offset' => ( ! empty($offset) && Helper::is_number($offset) && $offset >= 0 ) ? $offset : 0,
'range' => ( in_array($range, $range_values) ) ? $range : 'daily',
'time_quantity' => ( ! empty($time_quantity) && Helper::is_number($time_quantity) && $time_quantity > 0 ) ? $time_quantity : 24,
'time_unit' => ( in_array($time_unit, $time_units) ) ? $time_unit : 'hour',
'freshness' => empty($freshness) ? false : $freshness,
'order_by' => ( in_array($order_by, $order_by_values) ) ? $order_by : 'views',
'post_type' => empty($post_type) ? 'post' : $post_type,
'pid' => rtrim(preg_replace('|[^0-9,]|', '', $pid), ","),
'cat' => rtrim(preg_replace('|[^0-9,-]|', '', $cat), ","),
'taxonomy' => empty($tax) ? 'category' : $tax,
'term_id' => rtrim(preg_replace('|[^0-9,;-]|', '', $term_id), ","),
'author' => rtrim(preg_replace('|[^0-9,]|', '', $author), ","),
'shorten_title' => [
'active' => ( ! empty($title_length) && Helper::is_number($title_length) && $title_length > 0 ),
'length' => ( ! empty($title_length) && Helper::is_number($title_length) ) ? $title_length : 0,
'words' => (( ! empty($title_by_words) && Helper::is_number($title_by_words) && $title_by_words > 0 )),
],
'post-excerpt' => [
'active' => ( ! empty($excerpt_length) && Helper::is_number($excerpt_length) && $excerpt_length > 0 ),
'length' => ( ! empty($excerpt_length) && Helper::is_number($excerpt_length) ) ? $excerpt_length : 0,
'keep_format' => ( ! empty($excerpt_format) && Helper::is_number($excerpt_format) && $excerpt_format > 0 ),
'words' => ( ! empty($excerpt_by_words) && Helper::is_number($excerpt_by_words) && $excerpt_by_words > 0 ),
],
'thumbnail' => [
'active' => ( ! empty($thumbnail_width) && Helper::is_number($thumbnail_width) && $thumbnail_width > 0 ),
'width' => ( ! empty($thumbnail_width) && Helper::is_number($thumbnail_width) && $thumbnail_width > 0 ) ? $thumbnail_width : 0,
'height' => ( ! empty($thumbnail_height) && Helper::is_number($thumbnail_height) && $thumbnail_height > 0 ) ? $thumbnail_height : 0,
'build' => 'predefined' == $thumbnail_build ? 'predefined' : 'manual',
'size' => empty($thumbnail_size) ? '' : $thumbnail_size,
],
'rating' => empty($rating) ? false : $rating,
'stats_tag' => [
'comment_count' => empty($stats_comments) ? false : $stats_comments,
'views' => empty($stats_views) ? false : $stats_views,
'author' => empty($stats_author) ? false : $stats_author,
'date' => [
'active' => empty($stats_date) ? false : $stats_date,
'format' => empty($stats_date_format) ? 'F j, Y' : $stats_date_format
],
'category' => empty($stats_category) ? false : $stats_category,
'taxonomy' => [
'active' => empty($stats_taxonomy) ? false : $stats_taxonomy,
'name' => empty($taxonomy) ? 'category' : $taxonomy,
]
],
'markup' => [
'custom_html' => empty($custom_html) ? false : $custom_html,
'wpp-start' => empty($wpp_start) ? '' : $wpp_start,
'wpp-end' => empty($wpp_end) ? '' : $wpp_end,
'title-start' => empty($header_start) ? '' : $header_start,
'title-end' => empty($header_end) ? '' : $header_end,
'post-html' => empty($post_html) ? '<li>{thumb} {title} <span class="wpp-meta post-stats">{stats}</span></li>' : $post_html
],
'theme' => [
'name' => empty($theme) ? '' : $theme
]
];
// Post / Page / CTP filter
$ids = array_filter(explode(",", $query_args['pid']), 'is_numeric');
// Got no valid IDs, clear
if ( empty($ids) ) {
$query_args['pid'] = '';
}
// Category filter
$ids = array_filter(explode(",", $query_args['cat']), 'is_numeric');
// Got no valid IDs, clear
if ( empty($ids) ) {
$query_args['cat'] = '';
}
// Author filter
$ids = array_filter(explode(",", $query_args['author']), 'is_numeric');
// Got no valid IDs, clear
if ( empty($ids) ) {
$query_args['author'] = '';
}
// Has user set a title?
if (
! empty($query_args['title'])
&& ! empty($query_args['markup']['title-start'])
&& ! empty($query_args['markup']['title-end'])
) {
$html .= htmlspecialchars_decode($query_args['markup']['title-start'], ENT_QUOTES) . $query_args['title'] . htmlspecialchars_decode($query_args['markup']['title-end'], ENT_QUOTES);
}
$isAdmin = isset($_GET['isSelected']) ? $_GET['isSelected'] : false;
if ( $this->admin_options['tools']['ajax'] && ! is_customize_preview() && ! $isAdmin ) {
$html .= '<script type="application/json">' . json_encode($query_args) . '</script>';
$html .= '<div class="wpp-widget-block-placeholder"></div>';
return $html . '</div>';
}
// Return cached results
if ( $this->admin_options['tools']['cache']['active'] ) {
$key = md5(json_encode($query_args));
$popular_posts = \WordPressPopularPosts\Cache::get($key);
if ( false === $popular_posts ) {
$popular_posts = new Query($query_args);
$time_value = $this->admin_options['tools']['cache']['interval']['value']; // eg. 5
$time_unit = $this->admin_options['tools']['cache']['interval']['time']; // eg. 'minute'
// No popular posts found, check again in 1 minute
if ( ! $popular_posts->get_posts() ) {
$time_value = 1;
$time_unit = 'minute';
}
\WordPressPopularPosts\Cache::set(
$key,
$popular_posts,
$time_value,
$time_unit
);
}
} // Get popular posts
else {
$popular_posts = new Query($query_args);
}
$this->output->set_data($popular_posts->get_posts());
$this->output->set_public_options($query_args);
$this->output->build_output();
$html .= $this->output->get_output();
$html .= '</div>';
return $html;
}
/**
* Parses attributes.
*
* @since 5.4.0
* @param array
* @return array
*/
private function parse_attributes($atts = [])
{
$out = array();
foreach ( $this->defaults as $name => $default ) {
$out[$name] = array_key_exists($name, $atts) ? trim($atts[$name]) : $default;
}
return $out;
}
}

View File

@ -0,0 +1,755 @@
import { escape_html, unescape_html } from '../utils';
const { serverSideRender: ServerSideRender } = wp;
const { Component, Fragment } = wp.element;
const { BlockControls } = wp.blockEditor;
const { Button, CheckboxControl, Disabled, SelectControl, Spinner, TextareaControl, TextControl, Toolbar } = wp.components;
const { __ } = wp.i18n;
const endpoint = 'wordpress-popular-posts/v1';
export class WPPWidgetBlockEdit extends Component
{
constructor(props)
{
super(props);
this.state = {
error: null,
editMode: true,
themes: null,
imgSizes: null,
taxonomies: null
}
}
componentDidMount()
{
const { attributes } = this.props;
this.getThemes();
this.getImageSizes();
this.getTaxonomies();
this.setState({ editMode: attributes._editMode });
}
getThemes()
{
wp.apiFetch({ path: endpoint + '/themes' })
.then(
( themes ) => {
this.setState({
themes
});
},
( error ) => {
this.setState({
error,
themes: null
});
}
);
}
getImageSizes()
{
wp.apiFetch({ path: endpoint + '/thumbnails' })
.then(
( imgSizes ) => {
this.setState({
imgSizes
});
},
( error ) => {
this.setState({
error,
imgSizes: null
});
}
);
}
getTaxonomies()
{
const { attributes } = this.props;
wp.apiFetch({ path: endpoint + '/taxonomies' })
.then(
( taxonomies ) => {
if ( taxonomies ) {
let tax = attributes.tax.split(';'),
term_id = attributes.term_id.split(';');
if ( tax.length && tax.length == term_id.length ) {
let selected_taxonomies = {};
for( var t = 0; t < tax.length; t++ ) {
selected_taxonomies[tax[t]] = term_id[t];
}
for( const tax in taxonomies ) {
taxonomies[tax]._terms = 'undefined' != typeof selected_taxonomies[tax] ? selected_taxonomies[tax] : '';
}
}
}
this.setState({
taxonomies
});
},
( error ) => {
this.setState({
error,
taxonomies: null
});
}
);
}
getBlockControls()
{
const { setAttributes } = this.props;
const _self = this;
function onPreviewChange()
{
let editMode = ! _self.state.editMode;
_self.setState({ editMode: editMode });
setAttributes({ _editMode: editMode });
}
return (
<BlockControls>
<Toolbar>
<Button
label={ this.state.editMode ? __('Preview', 'wordpress-popular-posts') : __('Edit', 'wordpress-popular-posts') }
icon={ this.state.editMode ? "format-image" : "edit" }
onClick={onPreviewChange}
/>
</Toolbar>
</BlockControls>
);
}
getMainFields()
{
const { attributes, setAttributes } = this.props;
function onTitleChange(value)
{
value = escape_html(unescape_html(value));
setAttributes({ title: value });
}
function onLimitChange(value)
{
let limit = Number.isInteger(Number(value)) && Number(value) > 0 ? value : 10;
setAttributes({ limit: Number(limit) });
}
function onOrderByChange(value)
{
setAttributes({ order_by: value });
}
function onTimeRangeChange(value)
{
setAttributes({ range: value });
}
function onTimeQuantityChange(value) {
let qty = Number.isInteger(Number(value)) && Number(value) > 0 ? value : 24;
setAttributes({ time_quantity: Number(qty) });
}
function onTimeUnitChange(value) {
setAttributes({ time_unit: value });
}
function onFreshnessChange(value)
{
setAttributes({ freshness: value });
}
return <Fragment>
<TextControl
label={__('Title', 'wordpress-popular-posts')}
value={attributes.title}
onChange={onTitleChange}
/>
<TextControl
label={__('Limit', 'wordpress-popular-posts')}
value={attributes.limit}
onChange={onLimitChange}
/>
<SelectControl
label={__('Sort posts by', 'wordpress-popular-posts')}
value={attributes.order_by}
options={[
{label: __('Total views', 'wordpress-popular-posts'), value: 'views'},
{label: __('Avg. daily views', 'wordpress-popular-posts'), value: 'avg'},
{label: __('Comments', 'wordpress-popular-posts'), value: 'comments'}
]}
onChange={onOrderByChange}
/>
<SelectControl
label={__('Time Range', 'wordpress-popular-posts')}
value={attributes.range}
options={[
{label: __('Last 24 Hours', 'wordpress-popular-posts'), value: 'last24hours'},
{label: __('Last 7 days', 'wordpress-popular-posts'), value: 'last7days'},
{label: __('Last 30 days', 'wordpress-popular-posts'), value: 'last30days'},
{label: __('All-time', 'wordpress-popular-posts'), value: 'all'},
{label: __('Custom', 'wordpress-popular-posts'), value: 'custom'},
]}
onChange={onTimeRangeChange}
/>
{ 'custom' == attributes.range &&
<div className='option-subset'>
<TextControl
label={__('Time Quantity', 'wordpress-popular-posts')}
value={attributes.time_quantity}
onChange={onTimeQuantityChange}
/>
<SelectControl
label={__('Time Unit', 'wordpress-popular-posts')}
value={attributes.time_unit}
options={[
{label: __('Minute(s)', 'wordpress-popular-posts'), value: 'minute'},
{label: __('Hour(s)', 'wordpress-popular-posts'), value: 'hour'},
{label: __('Day(s)', 'wordpress-popular-posts'), value: 'day'}
]}
onChange={onTimeUnitChange}
/>
</div>
}
<CheckboxControl
label={__('Display only posts published within the selected Time Range', 'wordpress-popular-posts')}
checked={attributes.freshness}
onChange={onFreshnessChange}
/>
</Fragment>;
}
getFiltersFields()
{
const { attributes, setAttributes } = this.props;
const _self = this;
function onPostTypeChange(value)
{
let new_value = value.replace(/[^a-z0-9-_\,]+/gi, '');
setAttributes({ post_type: new_value });
}
function onPostIDExcludeChange(value)
{
let new_value = value.replace(/[^0-9\,]/g, '');
setAttributes({ pid: new_value });
}
function onAuthorChange(value)
{
let new_value = value.replace(/[^0-9\,]/g, '');
setAttributes({ author: new_value });
}
function onTaxChange(taxonomy_name, terms)
{
let taxonomies = _self.state.taxonomies;
terms = terms.replace(/[^0-9-\,]/g, '');
if ( taxonomies && 'undefined' != typeof taxonomies[taxonomy_name] ) {
taxonomies[taxonomy_name]._terms = terms;
_self.setState({ taxonomies: taxonomies });
}
}
function onTaxBlur(taxonomy_name)
{
let taxonomies = _self.state.taxonomies;
if ( taxonomies && 'undefined' != typeof taxonomies[taxonomy_name] ) {
let terms_arr = taxonomies[taxonomy_name]._terms.split(',');
// Remove invalid values
if ( terms_arr.length )
terms_arr = terms_arr.map((term) => term.trim())
.filter((term) => '' != term && '-' != term);
// Remove duplicates
if ( terms_arr.length )
terms_arr = Array.from(new Set(terms_arr));
taxonomies[taxonomy_name]._terms = terms_arr.join(',');
_self.setState({ taxonomies });
let tax = '',
term_id = '';
for ( let key in _self.state.taxonomies ) {
if ( _self.state.taxonomies.hasOwnProperty(key) ) {
if ( ! _self.state.taxonomies[key]._terms.length )
continue;
tax += key + ';';
term_id += _self.state.taxonomies[key]._terms + ';';
}
}
// Remove trailing semicolon
if ( tax && term_id ) {
tax = tax.replace(new RegExp(';$'), '');
term_id = term_id.replace(new RegExp(';$'), '');
}
setAttributes({ tax: tax, term_id: term_id });
}
}
let taxonomies = [];
if ( this.state.taxonomies ) {
for( const tax in this.state.taxonomies ) {
taxonomies.push(
{
name: this.state.taxonomies[tax].name,
label: this.state.taxonomies[tax].labels.singular_name + ' (' + this.state.taxonomies[tax].name + ')',
terms: this.state.taxonomies[tax]._terms
}
);
}
}
return <Fragment>
<p className='not-a-legend'><strong>{__('Filters', 'wordpress-popular-posts')}</strong></p>
<TextControl
label={__('Post type(s)', 'wordpress-popular-posts')}
help={__('Post types must be comma separated.', 'wordpress-popular-posts')}
value={attributes.post_type}
onChange={onPostTypeChange}
/>
<TextControl
label={__('Post ID(s) to exclude', 'wordpress-popular-posts')}
help={__('IDs must be comma separated.', 'wordpress-popular-posts')}
value={attributes.pid}
onChange={onPostIDExcludeChange}
/>
<TextControl
label={__('Author ID(s)', 'wordpress-popular-posts')}
help={__('IDs must be comma separated.', 'wordpress-popular-posts')}
value={attributes.author}
onChange={onAuthorChange}
/>
{ taxonomies && taxonomies.filter((tax) => 'post_format' != tax.name).map((tax) =>
{
return (
<TextControl
label={tax.label}
help={__('Term IDs must be comma separated, prefix a minus sign to exclude.', 'wordpress-popular-posts')}
value={tax.terms}
onChange={(terms) => onTaxChange(tax.name, terms)}
onBlur={() => onTaxBlur(tax.name)}
/>
);
}
)}
</Fragment>;
}
getPostSettingsFields()
{
const { attributes, setAttributes } = this.props;
const _self = this;
function onShortenTitleChange(value) {
if ( false == value )
setAttributes({ title_length: 0, title_by_words: 0, shorten_title: value });
else
setAttributes({ shorten_title: value, title_length: 25 });
}
function onTitleLengthChange(value)
{
let length = Number.isInteger(Number(value)) && Number(value) >= 0 ? value : 0;
setAttributes({ title_length: Number(length) });
}
function onDisplayExcerptChange(value) {
if ( false == value )
setAttributes({ excerpt_length: 0, excerpt_by_words: 0, display_post_excerpt: value });
else
setAttributes({ display_post_excerpt: value, excerpt_length: 55 });
}
function onExcerptLengthChange(value)
{
let length = Number.isInteger(Number(value)) && Number(value) >= 0 ? value : 0;
setAttributes({ excerpt_length: Number(length) });
}
function onDisplayThumbnailChange(value) {
if ( false == value )
setAttributes({ thumbnail_width: 0, thumbnail_height: 0, display_post_thumbnail: value });
else
setAttributes({ thumbnail_width: 75, thumbnail_height: 75, display_post_thumbnail: value });
}
function onThumbnailDimChange(dim, value)
{
let width = Number.isInteger(Number(value)) && Number(value) >= 0 ? value : 0;
setAttributes(( 'width' == dim ? { thumbnail_width: Number(width) } : { thumbnail_height: Number(width) } ));
}
function onThumbnailBuildChange(value)
{
if ( 'predefined' == value ) {
let fallback = 0;
setAttributes({
thumbnail_width: _self.state.imgSizes[sizes[fallback].value].width,
thumbnail_height: _self.state.imgSizes[sizes[fallback].value].height,
thumbnail_size: sizes[fallback].value
});
}
setAttributes({ thumbnail_build: value });
}
function onThumbnailSizeChange(value) {
setAttributes({
thumbnail_width: _self.state.imgSizes[value].width,
thumbnail_height: _self.state.imgSizes[value].height,
thumbnail_size: value
});
}
let sizes = [];
if ( this.state.imgSizes ) {
for( const size in this.state.imgSizes ) {
sizes.push(
{
label: size,
value: size
},
);
}
}
return <Fragment>
<p className='not-a-legend'><strong>{__('Posts settings', 'wordpress-popular-posts')}</strong></p>
<CheckboxControl
label={__('Shorten title', 'wordpress-popular-posts')}
checked={attributes.shorten_title}
onChange={onShortenTitleChange}
/>
{ attributes.shorten_title &&
<div className='option-subset'>
<TextControl
label={__('Shorten title to', 'wordpress-popular-posts')}
value={attributes.title_length}
onChange={onTitleLengthChange}
/>
<SelectControl
value={attributes.title_by_words}
options={[
{ label: __('characters', 'wordpress-popular-posts'), value: 0 },
{ label: __('words', 'wordpress-popular-posts'), value: 1 },
]}
onChange={(value) => setAttributes({ title_by_words: Number(value) })}
/>
</div>
}
<CheckboxControl
label={__('Display post excerpt', 'wordpress-popular-posts')}
checked={attributes.display_post_excerpt}
onChange={onDisplayExcerptChange}
/>
{ attributes.display_post_excerpt &&
<div className='option-subset'>
<CheckboxControl
label={__('Keep text format and links', 'wordpress-popular-posts')}
checked={attributes.excerpt_format}
onChange={(value) => setAttributes({ excerpt_format: value })}
/>
<TextControl
label={__('Excerpt length', 'wordpress-popular-posts')}
value={attributes.excerpt_length}
onChange={onExcerptLengthChange}
/>
<SelectControl
value={attributes.excerpt_by_words}
options={[
{ label: __('characters', 'wordpress-popular-posts'), value: 0 },
{ label: __('words', 'wordpress-popular-posts'), value: 1 },
]}
onChange={(value) => setAttributes({ excerpt_by_words: Number(value) })}
/>
</div>
}
<CheckboxControl
label={__('Display post thumbnail', 'wordpress-popular-posts')}
checked={attributes.display_post_thumbnail}
onChange={onDisplayThumbnailChange}
/>
{ attributes.display_post_thumbnail &&
<div className='option-subset'>
<SelectControl
value={attributes.thumbnail_build}
options={[
{ label: __('Set size manually', 'wordpress-popular-posts'), value: 'manual' },
{ label: __('Use predefined size', 'wordpress-popular-posts'), value: 'predefined' },
]}
onChange={onThumbnailBuildChange}
/>
{ 'manual' == attributes.thumbnail_build &&
<Fragment>
<TextControl
label={__('Thumbnail width', 'wordpress-popular-posts')}
help={__('Size in px units (pixels)', 'wordpress-popular-posts')}
value={attributes.thumbnail_width}
onChange={(value) => onThumbnailDimChange('width', value)}
/>
<TextControl
label={__('Thumbnail height', 'wordpress-popular-posts')}
help={__('Size in px units (pixels)', 'wordpress-popular-posts')}
value={attributes.thumbnail_height}
onChange={(value) => onThumbnailDimChange('height', value)}
/>
</Fragment>
}
{ 'predefined' == attributes.thumbnail_build &&
<Fragment>
<SelectControl
value={attributes.thumbnail_size}
options={sizes}
onChange={onThumbnailSizeChange}
/>
</Fragment>
}
</div>
}
</Fragment>;
}
getStatsTagFields()
{
const { attributes, setAttributes } = this.props;
let taxonomies = [];
if ( this.state.taxonomies ) {
for( const tax in this.state.taxonomies ) {
taxonomies.push(
{
label: this.state.taxonomies[tax].labels.singular_name + ' (' + this.state.taxonomies[tax].name + ')',
value: this.state.taxonomies[tax].name
},
);
}
}
return <Fragment>
<p className='not-a-legend'><strong>{__('Stats Tag settings', 'wordpress-popular-posts')}</strong></p>
<CheckboxControl
label={__('Display comments count', 'wordpress-popular-posts')}
checked={attributes.stats_comments}
onChange={(value) => setAttributes({ stats_comments: value })}
/>
<CheckboxControl
label={__('Display views', 'wordpress-popular-posts')}
checked={attributes.stats_views}
onChange={(value) => setAttributes({ stats_views: value })}
/>
<CheckboxControl
label={__('Display author', 'wordpress-popular-posts')}
checked={attributes.stats_author}
onChange={(value) => setAttributes({ stats_author: value })}
/>
<CheckboxControl
label={__('Display date', 'wordpress-popular-posts')}
checked={attributes.stats_date}
onChange={(value) => setAttributes({ stats_date: value })}
/>
{ attributes.stats_date &&
<div className='option-subset'>
<SelectControl
label={__('Date Format', 'wordpress-popular-posts')}
value={attributes.stats_date_format}
options={[
{ label: __('Relative', 'wordpress-popular-posts'), value: 'relative' },
{ label: __('Month Day, Year', 'wordpress-popular-posts'), value: 'F j, Y' },
{ label: __('yyyy/mm/dd', 'wordpress-popular-posts'), value: 'Y/m/d' },
{ label: __('mm/dd/yyyy', 'wordpress-popular-posts'), value: 'm/d/Y' },
{ label: __('dd/mm/yyyy', 'wordpress-popular-posts'), value: 'd/m/Y' },
{ label: __('WordPress Date Format', 'wordpress-popular-posts'), value: 'wp_date_format' },
]}
onChange={(value) => setAttributes({ stats_date_format: value })}
/>
</div>
}
<CheckboxControl
label={__('Display taxonomy', 'wordpress-popular-posts')}
checked={attributes.stats_taxonomy}
onChange={(value) => setAttributes({ stats_taxonomy: value })}
/>
{ attributes.stats_taxonomy &&
<div className='option-subset'>
<SelectControl
label={__('Taxonomy', 'wordpress-popular-posts')}
value={attributes.taxonomy}
options={taxonomies}
onChange={(value) => setAttributes({ taxonomy: value })}
/>
</div>
}
</Fragment>;
}
getHTMLMarkupFields()
{
const { attributes, setAttributes } = this.props;
const _self = this;
function onThemeChange(value)
{
if ( 'undefined' != typeof _self.state.themes[value] ) {
let config = _self.state.themes[value].json.config;
setAttributes({
shorten_title: config.shorten_title.active,
title_length: config.shorten_title.title_length,
title_by_words: config.shorten_title.words ? 1 : 0,
display_post_excerpt: config['post-excerpt'].active,
excerpt_format: config['post-excerpt'].format,
excerpt_length: config['post-excerpt'].length,
excerpt_by_words: config['post-excerpt'].words ? 1 : 0,
display_post_thumbnail: config.thumbnail.active,
thumbnail_build: config.thumbnail.build,
thumbnail_width: config.thumbnail.width,
thumbnail_height: config.thumbnail.height,
stats_comments: config.stats_tag.comment_count,
stats_views: config.stats_tag.views,
stats_author: config.stats_tag.author,
stats_date: config.stats_tag.date.active,
stats_date_format: config.stats_tag.date.format,
stats_taxonomy: config.stats_tag.taxonomy.active,
taxonomy: config.stats_tag.taxonomy.name,
custom_html: true,
wpp_start: config.markup['wpp-start'],
wpp_end: config.markup['wpp-end'],
post_html: config.markup['post-html'],
theme: value
});
} else {
setAttributes({ theme: value });
}
}
let themes = [
{
label: __('None', 'wordpress-popular-posts'),
value: ''
},
];
if ( this.state.themes ) {
for( const theme in this.state.themes ) {
themes.push(
{
label: this.state.themes[theme].json.name,
value: theme
},
);
}
}
return <Fragment>
<p className='not-a-legend'><strong>{__('HTML Markup settings', 'wordpress-popular-posts')}</strong></p>
<CheckboxControl
label={__('Use custom HTML Markup', 'wordpress-popular-posts')}
checked={attributes.custom_html}
onChange={(value) => setAttributes({ custom_html: value })}
/>
{ attributes.custom_html &&
<div className='option-subset'>
<TextareaControl
rows="1"
label={__('Before title', 'wordpress-popular-posts')}
value={attributes.header_start}
onChange={(value) => setAttributes({ header_start: value })}
/>
<TextareaControl
rows="1"
label={__('After title', 'wordpress-popular-posts')}
value={attributes.header_end}
onChange={(value) => setAttributes({ header_end: value })}
/>
<TextareaControl
rows="1"
label={__('Before popular posts', 'wordpress-popular-posts')}
value={attributes.wpp_start}
onChange={(value) => setAttributes({ wpp_start: value })}
/>
<TextareaControl
rows="1"
label={__('After popular posts', 'wordpress-popular-posts')}
value={attributes.wpp_end}
onChange={(value) => setAttributes({ wpp_end: value })}
/>
<TextareaControl
label={__('Post HTML markup', 'wordpress-popular-posts')}
value={attributes.post_html}
onChange={(value) => setAttributes({ post_html: value })}
/>
</div>
}
<SelectControl
label={__('Theme', 'wordpress-popular-posts')}
value={attributes.theme}
options={themes}
onChange={onThemeChange}
/>
</Fragment>;
}
render()
{
if ( ! this.state.taxonomies || ! this.state.themes || ! this.state.imgSizes )
return <Spinner />;
const { isSelected, className, attributes } = this.props;
let classes = className;
classes += this.state.editMode ? ' in-edit-mode' : ' in-preview-mode';
classes += isSelected ? ' is-selected' : '';
return ([
this.getBlockControls(),
<div className={classes}>
{ this.state.editMode &&
<Fragment>
{this.getMainFields()}
{this.getFiltersFields()}
{this.getPostSettingsFields()}
{this.getStatsTagFields()}
{this.getHTMLMarkupFields()}
</Fragment>
}
{ ! this.state.editMode &&
<Disabled>
<ServerSideRender
block={this.props.name}
className={className}
attributes={attributes}
urlQueryArgs={{isSelected: isSelected}}
/>
</Disabled>
}
</div>
]);
}
}

View File

@ -0,0 +1,42 @@
.wp-block-wordpress-popular-posts-widget.in-edit-mode {
padding: 1.8em 1.5em;
background: #fafafa;
}
.wp-block-wordpress-popular-posts-widget .wpp-list {
margin-left: 0 !important;
margin-right: 0 !important;
padding-left: 0 !important;
padding-right: 0 !important;
}
.wp-block-wordpress-popular-posts-widget .option-subset {
margin: 0 0 1em;
padding: 1.25em 1.5em;
background: #f0f0f0;
}
.wp-block-wordpress-popular-posts-widget .components-base-control {
}
.wp-block-wordpress-popular-posts-widget .components-base-control .components-base-control__help {
margin: 0 0 1em;
font-size: 0.9em;
}
.wp-block-wordpress-popular-posts-widget .components-base-control .components-base-control__field {
}
.wp-block-wordpress-popular-posts-widget .components-base-control .components-base-control__field label {
font-weight: 600;
}
.wp-block-wordpress-popular-posts-widget .components-base-control .components-base-control__field .components-select-control {
}
.wp-block-wordpress-popular-posts-widget .components-base-control .components-base-control__field .components-select-control .components-input-control__container {
}
.wp-block-wordpress-popular-posts-widget .components-base-control .components-base-control__field .components-select-control .components-input-control__container .components-select-control__input {
max-width: none;
}

View File

@ -0,0 +1,213 @@
import icons from '../icons';
import { WPPWidgetBlockEdit } from './edit';
const { registerBlockType } = wp.blocks;
const { __ } = wp.i18n;
registerBlockType('wordpress-popular-posts/widget', {
title: 'WordPress Popular Posts',
category: 'widgets',
icon: icons.flame,
description: __('A highly customizable block that displays your most popular posts.', 'wordpress-popular-posts'),
keywords: ['popular', 'posts', 'trending', 'popularity'],
attributes: {
_editMode: {
type: 'boolean',
default: true
},
_isSelected: {
type: 'boolean',
default: false
},
title: {
type: 'string',
},
limit: {
type: 'number',
default: 10
},
offset: {
type: 'number',
default: 0
},
order_by: {
type: 'string',
default: 'views'
},
range: {
type: 'string',
default: 'last24hours'
},
time_quantity: {
type: 'number',
default: 24
},
time_unit: {
type: 'string',
default: 'hour'
},
freshness: {
type: 'boolean',
default: false
},
/* filters */
post_type: {
type: 'string',
default: 'post'
},
pid: {
type: 'string',
default: ''
},
author: {
type: 'string',
default: ''
},
tax: {
type: 'string',
default: ''
},
term_id: {
type: 'string',
default: ''
},
/* post settings */
shorten_title: {
type: 'boolean',
default: false
},
title_length: {
type: 'number',
default: 0
},
title_by_words: {
type: 'number',
default: 0
},
display_post_excerpt: {
type: 'boolean',
default: false
},
excerpt_format: {
type: 'boolean',
default: false
},
excerpt_length: {
type: 'number',
default: 0
},
excerpt_by_words: {
type: 'number',
default: 0
},
display_post_thumbnail: {
type: 'boolean',
default: false
},
thumbnail_width: {
type: 'number',
default: 0
},
thumbnail_height: {
type: 'number',
default: 0
},
thumbnail_build: {
type: 'string',
default: 'manual'
},
thumbnail_size: {
type: 'string',
default: ''
},
/* stats tag settings */
stats_comments: {
type: 'boolean',
default: false
},
stats_views: {
type: 'boolean',
default: true
},
stats_author: {
type: 'boolean',
default: false
},
stats_date: {
type: 'boolean',
default: false
},
stats_date_format: {
type: 'string',
default: 'F j, Y'
},
stats_taxonomy: {
type: 'boolean',
default: false
},
taxonomy: {
type: 'string',
default: ''
},
/* HTML markup settings */
custom_html: {
type: 'boolean',
default: false
},
header_start: {
type: 'string',
default: '<h2>'
},
header_end: {
type: 'string',
default: '</h2>'
},
wpp_start: {
type: 'string',
default: '<ul class="wpp-list">'
},
wpp_end: {
type: 'string',
default: '</ul>'
},
post_html: {
type: 'string',
default: '<li>{thumb} {title} <span class="wpp-meta post-stats">{stats}</span></li>'
},
theme: {
type: 'string',
default: ''
},
},
supports: {
anchor: true,
align: true,
html: false
},
example: {
attributes: {
_editMode: false,
title: 'Popular Posts',
limit: 3,
range: 'last7days',
display_post_excerpt: true,
excerpt_length: 75,
display_post_thumbnail: true,
thumbnail_width: 75,
thumbnail_height: 75,
stats_views: false,
stats_taxonomy: true,
custom_html: true,
wpp_start: '<ul class="wpp-list wpp-cards">',
post_html: '<li>{thumb_img} <div class="wpp-item-data"><div class="taxonomies">{taxonomy}</div>{title} <p class="wpp-excerpt">{excerpt}</p></div></li>',
theme: 'cards'
}
},
edit: WPPWidgetBlockEdit,
save: () => {
return null;
}
});

View File

@ -0,0 +1,5 @@
const icons = {};
icons.flame = <svg viewBox="0 0 248 379"><path fill="#fff" d="M-83,96q0-122.5,0-245H165q0,131,0,262a31.87,31.87,0,0,1-.95-4.33A123.87,123.87,0,0,0,153.47,68.3c-12.28-27.74-31.1-50.64-53-71.21C75.67-26.13,55.85-52,54.32-87.87c-.79-18.47.81-36.24,11.59-52.15,1.08-1.59.38-4.4.5-6.64-2.43.1-5.5-.7-7.18.47a140.91,140.91,0,0,0-17.12,13.72C19.49-110.67,3-84.6-9.51-56A149,149,0,0,0-21.86-3.77c-2,39.4,11.38,73.46,36.17,103.51,1.74,2.11,3.51,4.2,5.27,6.3l-.67,1.07c-3.94-1.07-8-1.83-11.82-3.24C-25.17,91.94-52.36,58.57-51.12,21c.1-2.91.21-6.45-3.51-6.49-2,0-4.76,2.16-5.79,4.09-9.4,17.55-16.35,36-19.73,55.73C-81.38,81.49-82.07,88.76-83,96Z" transform="translate(83 149)"/><path fill="#ba2f2f" d="M-83,96c.93-7.24,1.62-14.51,2.85-21.7,3.38-19.69,10.33-38.18,19.73-55.73,1-1.93,3.83-4.11,5.79-4.09,3.72,0,3.61,3.58,3.51,6.49-1.25,37.59,25.94,71,58.2,82.89,3.82,1.41,7.87,2.18,11.82,3.24l.67-1.07c-1.76-2.1-3.52-4.19-5.27-6.3C-10.49,69.68-23.88,35.63-21.86-3.77A149,149,0,0,1-9.51-56c12.48-28.62,29-54.69,51.62-76.5a140.91,140.91,0,0,1,17.12-13.72c1.68-1.18,4.75-.37,7.18-.47-.13,2.24.58,5-.5,6.64-10.78,15.9-12.37,33.68-11.59,52.15,1.53,35.89,21.35,61.74,46.11,85,21.94,20.57,40.76,43.47,53,71.21a123.87,123.87,0,0,1,10.59,40.36A31.87,31.87,0,0,0,165,113v9c-.7,4.24-1.17,8.54-2.13,12.73-10.74,46.51-37.08,78.75-84.34,91.58C72.16,228,65.52,228.79,59,230H43a25.19,25.19,0,0,0-3.12-1.18c-10-2.37-20.21-4.12-30-7.12-45.83-14-75.19-44.64-89-90.24-2.28-7.52-2.64-15.63-3.88-23.46Q-83,102-83,96ZM61.63-143.61c-6.24,5.39-12.87,10.38-18.64,16.22A229,229,0,0,0-8.77-46.26,138.37,138.37,0,0,0-16.63,23c4.69,32.54,20.21,59.59,42.4,83.23,1.34,1.43,2.7,2.83,4.8,5-15.23,1-28-3.3-39.74-10.64-29.74-18.62-46-45.23-46.8-81a138.75,138.75,0,0,0-7.46,14.67A178.29,178.29,0,0,0-78.24,93.09C-80.9,129.7-68,160.25-42.78,185.71c28.91,29.16,65.19,41.42,105.43,38.91,43.82-2.73,80.34-35.08,93.53-79.39,8.68-29.18,3.11-56.71-10.29-83.15C134.15,38.92,117.71,19.34,99,1.57,85-11.65,71.34-25.28,62.72-42.69,46.33-75.79,44.36-109.22,61.63-143.61Z" transform="translate(83 149)"/><path fill="#fff" d="M-83,108c1.25,7.84,1.61,15.94,3.88,23.46,13.79,45.6,43.15,76.21,89,90.24,9.82,3,20,4.76,30,7.12A25.19,25.19,0,0,1,43,230H-83Q-83,169-83,108Z" transform="translate(83 149)"/><path fill="#fff" d="M59,230c6.52-1.21,13.16-2,19.53-3.69,47.26-12.83,73.6-45.07,84.34-91.58,1-4.18,1.43-8.48,2.13-12.73V230Z" transform="translate(83 149)"/><path fill="#ba2f2f" d="M61.63-143.61c-17.28,34.39-15.3,67.82,1.09,100.92C71.34-25.28,85-11.65,99,1.57c18.75,17.77,35.2,37.35,46.94,60.51,13.4,26.44,19,54,10.29,83.15-13.18,44.31-49.71,76.66-93.53,79.39-40.25,2.51-76.52-9.75-105.43-38.91C-68,160.25-80.9,129.7-78.24,93.09A178.29,178.29,0,0,1-63.45,34.31,138.75,138.75,0,0,1-56,19.64c.77,35.79,17.06,62.4,46.8,81C2.54,108,15.33,112.3,30.56,111.3c-2.1-2.21-3.46-3.62-4.8-5C3.57,82.62-11.94,55.57-16.63,23A138.37,138.37,0,0,1-8.77-46.26,229,229,0,0,1,43-127.38C48.76-133.23,55.39-138.22,61.63-143.61Z" transform="translate(83 149)"/></svg>
export default icons;

View File

@ -0,0 +1,20 @@
export function escape_html(value) {
const map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;',
"/": '&#x2F;',
'`': '&grave;',
};
const reg = /[&<>"'/]/ig;
return value.replace(reg, (match)=>(map[match]));
}
export function unescape_html(value) {
var div = document.createElement('div');
div.innerHTML = value;
var child = div.childNodes[0];
return child ? child.nodeValue : '';
}

View File

@ -0,0 +1,25 @@
<?php
/**
* Plugin bootstrap file.
*/
namespace WordPressPopularPosts;
/** Composer autoloder */
require __DIR__ . '/../vendor/autoload.php';
register_activation_hook($wpp_main_plugin_file, [__NAMESPACE__ . '\Activation\Activator', 'activate']);
register_deactivation_hook($wpp_main_plugin_file, [__NAMESPACE__ . '\Activation\Deactivator', 'deactivate']);
$container = new Container\Container();
$container->configure([
new Container\WordPressPopularPostsConfiguration()
]);
$WordPressPopularPosts = $container['wpp'];
add_action('plugins_loaded', [$WordPressPopularPosts, 'init']);
// WPP's template tags
require __DIR__ . '/template-tags.php';
// Deprecated functions/classes
require __DIR__ . '/deprecated.php';

View File

@ -0,0 +1,111 @@
<?php
/**
* Helper class to store data in cache for a fixed amount of time.
*
* @link https://cabrerahector.com
* @since 4.1.2
*
* @package WordPressPopularPosts
* @subpackage WordPressPopularPosts/includes
*/
/**
* Helper class to store data in cache for a fixed amount of time.
*
* Stores data in cache via WordPress Transients (or any other available
* method in the future) for a fixed amount of time to reduce the number
* of database calls.
*
* @package WordPressPopularPosts
* @subpackage WordPressPopularPosts/includes
* @author Hector Cabrera <me@cabrerahector.com>
*/
namespace WordPressPopularPosts;
class Cache {
/**
* Retrieves cached data.
*
* @since 4.1.2
* @access public
* @param string $key The name of the cached data.
* @return mixed
*/
public static function get($key)
{
return get_transient($key);
}
/**
* Retrieves cached data.
*
* @since 4.1.2
* @access public
* @param string $key The name of the cached data.
* @param mixed $data The data being stored.
*/
public static function set($key = null, $data = [], $time_value = 1, $time_unit = 'minute')
{
if ( !$key )
return false;
if (
false === filter_var($time_value, FILTER_VALIDATE_INT)
|| $time_value <= 0
) {
$time_value = 1;
}
switch( $time_unit ){
case 'minute':
$time = 60;
break;
case 'hour':
$time = 60 * 60;
break;
case 'day':
$time = 60 * 60 * 24;
break;
case 'week':
$time = 60 * 60 * 24 * 7;
break;
case 'month':
$time = 60 * 60 * 24 * 30;
break;
case 'year':
$time = 60 * 60 * 24 * 365;
break;
default:
$time = 60;
break;
}
$expiration = $time * $time_value;
// Store transient
set_transient($key, $data, $expiration);
// Store transient keys in WPP's transients table for garbage collection
global $wpdb;
$wpdb->insert(
$wpdb->prefix . "popularpoststransients",
[
'tkey' => $key,
'tkey_date' => Helper::now()
],
['%s', '%s']
);
}
}

View File

@ -0,0 +1,110 @@
<?php
/**
* A Simple Dependency Injector Container (DIC).
*
* Largely based on Carl Alexander's Dependency Injection Container.
* @link https://carlalexander.ca/dependency-injection-wordpress/
*/
namespace WordPressPopularPosts\Container;
class Container implements \ArrayAccess
{
/**
* Values stored inside the container.
*
* @var array
*/
private $values;
/**
* Constructor.
*
* @param array $values
*/
public function __construct(array $values = [])
{
$this->values = $values;
}
/**
* Configure the container using the given container configuration objects.
*
* @param array $configurations
*/
public function configure(array $configurations)
{
foreach ($configurations as $configuration) {
if ( $configuration instanceof ContainerConfigurationInterface )
$configuration->modify($this);
}
}
/**
* Checks if there's a value in the container for the given key.
*
* @param mixed $key
*
* @return bool
*/
public function offsetExists($key)
{
return array_key_exists($key, $this->values);
}
/**
* Sets a value inside of the container.
*
* @param mixed $key
* @param mixed $value
*/
public function offsetSet($key, $value)
{
$this->values[$key] = $value;
}
/**
* Unset the value in the container for the given key.
*
* @param mixed $key
*/
public function offsetUnset($key)
{
unset($this->values[$key]);
}
/**
* Get a value from the container.
*
* @param mixed $key
*
* @return mixed
*/
public function offsetGet($key)
{
if ( ! $this->offsetExists($key) ) {
throw new \InvalidArgumentException(sprintf('Container doesn\'t have a value stored for the "%s" key.', $key));
}
return $this->values[$key] instanceof \Closure ? $this->values[$key]($this) : $this->values[$key];
}
/**
* Creates a closure used for creating a service using the given callable.
*
* @param \Closure $closure
*
* @return \Closure
*/
public function service(\Closure $closure)
{
return function(Container $container) use ($closure) {
static $object;
if ( null === $object ) {
$object = $closure($container);
}
return $object;
};
}
}

View File

@ -0,0 +1,17 @@
<?php
/**
* Contract to modify the DIC.
*/
namespace WordPressPopularPosts\Container;
interface ContainerConfigurationInterface
{
/**
* Modifies the given dependency injection container.
*
* @since 5.0.0
* @param Container $container
*/
public function modify(Container $container);
}

View File

@ -0,0 +1,87 @@
<?php
namespace WordPressPopularPosts\Container;
use WordPressPopularPosts\Settings;
class WordPressPopularPostsConfiguration implements ContainerConfigurationInterface
{
/**
* Modifies the given dependency injection container.
*
* @since 5.0.0
* @param Container $container
*/
public function modify(Container $container)
{
$container['admin_options'] = Settings::get('admin_options');
$container['widget_options'] = Settings::get('widget_options');
$container['i18n'] = $container->service(function(Container $container) {
return new \WordPressPopularPosts\I18N($container['admin_options']);
});
$container['translate'] = $container->service(function(Container $container) {
return new \WordPressPopularPosts\Translate();
});
$container['image'] = $container->service(function(Container $container) {
return new \WordPressPopularPosts\Image($container['admin_options']);
});
$container['themer'] = $container->service(function(Container $container) {
return new \WordPressPopularPosts\Themer();
});
$container['output'] = $container->service(function(Container $container) {
return new \WordPressPopularPosts\Output($container['widget_options'], $container['admin_options'], $container['image'], $container['translate'], $container['themer']);
});
$container['widget'] = $container->service(function(Container $container) {
return new \WordPressPopularPosts\Widget\Widget($container['widget_options'], $container['admin_options'], $container['output'], $container['image'], $container['translate'], $container['themer']);
});
$container['block_widget'] = $container->service(function(Container $container) {
return new \WordPressPopularPosts\Block\Widget\Widget($container['admin_options'], $container['output'], $container['image'], $container['translate'], $container['themer']);
});
$container['posts_endpoint'] = $container->service(function(Container $container) {
return new \WordPressPopularPosts\Rest\PostsEndpoint($container['admin_options'], $container['translate']);
});
$container['view_logger_endpoint'] = $container->service(function(Container $container) {
return new \WordPressPopularPosts\Rest\ViewLoggerEndpoint($container['admin_options'], $container['translate']);
});
$container['taxonomies_endpoint'] = $container->service(function(Container $container) {
return new \WordPressPopularPosts\Rest\TaxonomiesEndpoint($container['admin_options'], $container['translate']);
});
$container['themes_endpoint'] = $container->service(function(Container $container) {
return new \WordPressPopularPosts\Rest\ThemesEndpoint($container['admin_options'], $container['translate'], $container['themer']);
});
$container['thumbnails_endpoint'] = $container->service(function(Container $container) {
return new \WordPressPopularPosts\Rest\ThumbnailsEndpoint($container['admin_options'], $container['translate']);
});
$container['widget_endpoint'] = $container->service(function(Container $container) {
return new \WordPressPopularPosts\Rest\WidgetEndpoint($container['admin_options'], $container['translate'], $container['output']);
});
$container['rest'] = $container->service(function(Container $container) {
return new \WordPressPopularPosts\Rest\Controller($container['posts_endpoint'], $container['view_logger_endpoint'], $container['widget_endpoint'], $container['themes_endpoint'], $container['thumbnails_endpoint'], $container['taxonomies_endpoint']);
});
$container['admin'] = $container->service(function(Container $container) {
return new \WordPressPopularPosts\Admin\Admin($container['admin_options'], $container['image']);
});
$container['front'] = $container->service(function(Container $container) {
return new \WordPressPopularPosts\Front\Front($container['admin_options'], $container['translate'], $container['output']);
});
$container['wpp'] = $container->service(function(Container $container) {
return new \WordPressPopularPosts\WordPressPopularPosts($container['i18n'], $container['rest'], $container['admin'], $container['front'], $container['widget'], $container['block_widget']);
});
}
}

View File

@ -0,0 +1,509 @@
<?php
/**
* The public-facing functionality of the plugin.
*
* Defines hooks to enqueue the public-specific stylesheet and JavaScript.
*
* @package WordPressPopularPosts
* @subpackage WordPressPopularPosts/Front
* @author Hector Cabrera <me@cabrerahector.com>
*/
namespace WordPressPopularPosts\Front;
use WordPressPopularPosts\Helper;
use WordPressPopularPosts\Output;
use WordPressPopularPosts\Query;
class Front {
/**
* Plugin options.
*
* @var array $config
* @access private
*/
private $config;
/**
* Translate object.
*
* @var \WordPressPopularPosts\Translate $translate
* @access private
*/
private $translate;
/**
* Output object.
*
* @var \WordPressPopularPosts\Output $output
* @access private
*/
private $output;
/**
* Construct.
*
* @since 5.0.0
* @param array $config Admin settings.
* @param \WordPressPopularPosts\Translate $translate Translate class.
*/
public function __construct(array $config, \WordPressPopularPosts\Translate $translate, \WordPressPopularPosts\Output $output)
{
$this->config = $config;
$this->translate = $translate;
$this->output = $output;
}
/**
* WordPress public-facing hooks.
*
* @since 5.0.0
*/
public function hooks()
{
add_shortcode('wpp', [$this, 'wpp_shortcode']);
add_action('wp_head', [$this, 'inline_loading_css']);
add_action('wp_ajax_update_views_ajax', [$this, 'update_views']);
add_action('wp_ajax_nopriv_update_views_ajax', [$this, 'update_views']);
add_action('wp_enqueue_scripts', [$this, 'enqueue_assets']);
add_filter('script_loader_tag', [$this, 'convert_inline_js_into_json'], 10, 3);
}
/**
* Inserts CSS related to the loading animation into <head>
*
* @since 5.3.0
*/
public function inline_loading_css()
{
$wpp_insert_loading_animation_styles = apply_filters('wpp_insert_loading_animation_styles', true);
if ( $wpp_insert_loading_animation_styles ) :
?>
<style id="wpp-loading-animation-styles">@-webkit-keyframes bgslide{from{background-position-x:0}to{background-position-x:-200%}}@keyframes bgslide{from{background-position-x:0}to{background-position-x:-200%}}.wpp-widget-placeholder,.wpp-widget-block-placeholder{margin:0 auto;width:60px;height:3px;background:#dd3737;background:linear-gradient(90deg,#dd3737 0%,#571313 10%,#dd3737 100%);background-size:200% auto;border-radius:3px;-webkit-animation:bgslide 1s infinite linear;animation:bgslide 1s infinite linear}</style>
<?php
endif;
}
/**
* Enqueues public facing assets.
*
* @since 5.0.0
*/
public function enqueue_assets()
{
// Enqueue WPP's stylesheet.
if ( $this->config['tools']['css'] ) {
$theme_file = get_stylesheet_directory() . '/wpp.css';
if ( @is_file($theme_file) ) {
wp_enqueue_style('wordpress-popular-posts-css', get_stylesheet_directory_uri() . "/wpp.css", [], WPP_VERSION, 'all');
} // Load stock stylesheet
else {
wp_enqueue_style('wordpress-popular-posts-css', plugin_dir_url(dirname(dirname(__FILE__))) . 'assets/css/wpp.css', [], WPP_VERSION, 'all');
}
}
// Enqueue WPP's library.
$is_single = 0;
if (
( 0 == $this->config['tools']['log']['level'] && ! is_user_logged_in() )
|| ( 1 == $this->config['tools']['log']['level'] )
|| ( 2 == $this->config['tools']['log']['level'] && is_user_logged_in() )
) {
$is_single = Helper::is_single();
}
wp_register_script('wpp-js', plugin_dir_url(dirname(dirname(__FILE__))) . 'assets/js/wpp.min.js', [], WPP_VERSION, false);
$params = [
'sampling_active' => (int) $this->config['tools']['sampling']['active'],
'sampling_rate' => (int) $this->config['tools']['sampling']['rate'],
'ajax_url' => esc_url_raw(rest_url('wordpress-popular-posts/v1/popular-posts')),
'api_url' => esc_url_raw(rest_url('wordpress-popular-posts')),
'ID' => (int) $is_single,
'token' => wp_create_nonce('wp_rest'),
'lang' => function_exists('PLL') ? $this->translate->get_current_language() : 0,
'debug' => (int) WP_DEBUG
];
wp_enqueue_script('wpp-js');
wp_add_inline_script('wpp-js', json_encode($params), 'before');
}
/**
* Converts inline script tag into type=application/json.
*
* This function mods the original script tag as printed
* by WordPress which contains the data for the wpp_params
* object into a JSON script. This improves compatibility
* with Content Security Policy (CSP).
*
* @since 5.2.0
* @param string $tag
* @param string $handle
* @param string $src
* @return string $tag
*/
function convert_inline_js_into_json($tag, $handle, $src)
{
if ( 'wpp-js' === $handle ) {
// id attribute found, replace it
if ( false !== strpos($tag, 'wpp-js-js-before') ) {
$tag = str_replace('wpp-js-js-before', 'wpp-json', $tag);
} // id attribute missing, let's add it
else {
$pos = strpos($tag, '>');
$tag = substr_replace($tag, ' id="wpp-json">', $pos, 1);
}
// type attribute found, replace it
if ( false !== strpos($tag, 'type') ) {
$pos = strpos($tag, 'text/javascript');
if ( false !== $pos )
$tag = substr_replace($tag, 'application/json', $pos, strlen('text/javascript'));
} // type attribute missing, let's add it
else {
$pos = strpos($tag, '>');
$tag = substr_replace($tag, ' type="application/json">', $pos, 1);
}
}
return $tag;
}
/**
* Updates views count on page load via AJAX.
*
* @since 2.0.0
*/
public function update_views()
{
if ( ! wp_verify_nonce($_POST['token'], 'wpp-token') || ! Helper::is_number($_POST['wpp_id']) ) {
die( "WPP: Oops, invalid request!" );
}
$nonce = $_POST['token'];
$post_ID = $_POST['wpp_id'];
$exec_time = 0;
$start = Helper::microtime_float();
$result = $this->update_views_count($post_ID);
$end = Helper::microtime_float();
$exec_time += round($end - $start, 6);
if ( $result ) {
die("WPP: OK. Execution time: " . $exec_time . " seconds");
}
die("WPP: Oops, could not update the views count!");
}
/**
* Updates views count.
*
* @since 1.4.0
* @access private
* @global object $wpdb
* @param int $post_ID
* @return bool|int FALSE if query failed, TRUE on success
*/
private function update_views_count($post_ID) {
/*
TODO:
For WordPress Multisite, we must define the DIEONDBERROR constant for database errors to display like so:
<?php define( 'DIEONDBERROR', true ); ?>
*/
global $wpdb;
$table = $wpdb->prefix . "popularposts";
$wpdb->show_errors();
// Get translated object ID
$post_ID = $this->translate->get_object_id(
$post_ID,
get_post_type($post_ID),
true,
$this->translate->get_default_language()
);
$now = Helper::now();
$curdate = Helper::curdate();
$views = ( $this->config['tools']['sampling']['active'] )
? $this->config['tools']['sampling']['rate']
: 1;
// Allow WP themers / coders perform an action
// before updating views count
if ( has_action('wpp_pre_update_views') )
do_action('wpp_pre_update_views', $post_ID, $views);
// Update all-time table
$result1 = $wpdb->query($wpdb->prepare(
"INSERT INTO {$table}data
(postid, day, last_viewed, pageviews) VALUES (%d, %s, %s, %d)
ON DUPLICATE KEY UPDATE pageviews = pageviews + %d, last_viewed = %s;",
$post_ID,
$now,
$now,
$views,
$views,
$now
));
// Update range (summary) table
$result2 = $wpdb->query($wpdb->prepare(
"INSERT INTO {$table}summary
(postid, pageviews, view_date, view_datetime) VALUES (%d, %d, %s, %s)
ON DUPLICATE KEY UPDATE pageviews = pageviews + %d, view_datetime = %s;",
$post_ID,
$views,
$curdate,
$now,
$views,
$now
));
if ( !$result1 || !$result2 )
return false;
// Allow WP themers / coders perform an action
// after updating views count
if ( has_action('wpp_post_update_views' ))
do_action('wpp_post_update_views', $post_ID);
return true;
}
/**
* WPP shortcode handler.
*
* @since 1.4.0
* @param array $atts User defined attributes in shortcode tag
* @return string
*/
public function wpp_shortcode($atts = null) {
/**
* @var string $header
* @var int $limit
* @var int $offset
* @var string $range
* @var bool $freshness
* @var string $order_by
* @var string $post_type
* @var string $pid
* @var string $cat
* @var string $author
* @var int $title_length
* @var int $title_by_words
* @var int $excerpt_length
* @var int $excerpt_format
* @var int $excerpt_by_words
* @var int $thumbnail_width
* @var int $thumbnail_height
* @var bool $rating
* @var bool $stats_comments
* @var bool $stats_views
* @var bool $stats_author
* @var bool $stats_date
* @var string $stats_date_format
* @var bool $stats_category
* @var string $wpp_start
* @var string $wpp_end
* @var string $header_start
* @var string $header_end
* @var string $post_html
* @var bool $php
*/
extract(shortcode_atts([
'header' => '',
'limit' => 10,
'offset' => 0,
'range' => 'daily',
'time_unit' => 'hour',
'time_quantity' => 24,
'freshness' => false,
'order_by' => 'views',
'post_type' => 'post',
'pid' => '',
'cat' => '',
'taxonomy' => 'category',
'term_id' => '',
'author' => '',
'title_length' => 0,
'title_by_words' => 0,
'excerpt_length' => 0,
'excerpt_format' => 0,
'excerpt_by_words' => 0,
'thumbnail_width' => 0,
'thumbnail_height' => 0,
'rating' => false,
'stats_comments' => false,
'stats_views' => true,
'stats_author' => false,
'stats_date' => false,
'stats_date_format' => 'F j, Y',
'stats_category' => false,
'stats_taxonomy' => false,
'wpp_start' => '<ul class="wpp-list">',
'wpp_end' => '</ul>',
'header_start' => '<h2>',
'header_end' => '</h2>',
'post_html' => '',
'theme' => '',
'php' => false
], $atts, 'wpp'));
// possible values for "Time Range" and "Order by"
$time_units = ["minute", "hour", "day", "week", "month"];
$range_values = ["daily", "last24hours", "weekly", "last7days", "monthly", "last30days", "all", "custom"];
$order_by_values = ["comments", "views", "avg"];
$shortcode_ops = [
'title' => strip_tags($header),
'limit' => ( ! empty($limit ) && Helper::is_number($limit) && $limit > 0 ) ? $limit : 10,
'offset' => ( ! empty($offset) && Helper::is_number($offset) && $offset >= 0 ) ? $offset : 0,
'range' => ( in_array($range, $range_values) ) ? $range : 'daily',
'time_quantity' => ( ! empty($time_quantity ) && Helper::is_number($time_quantity) && $time_quantity > 0 ) ? $time_quantity : 24,
'time_unit' => ( in_array($time_unit, $time_units) ) ? $time_unit : 'hour',
'freshness' => empty($freshness) ? false : $freshness,
'order_by' => ( in_array($order_by, $order_by_values) ) ? $order_by : 'views',
'post_type' => empty($post_type) ? 'post,page' : $post_type,
'pid' => rtrim(preg_replace('|[^0-9,]|', '', $pid), ","),
'cat' => rtrim(preg_replace('|[^0-9,-]|', '', $cat), ","),
'taxonomy' => empty($taxonomy) ? 'category' : $taxonomy,
'term_id' => rtrim(preg_replace('|[^0-9,;-]|', '', $term_id), ","),
'author' => rtrim(preg_replace('|[^0-9,]|', '', $author), ","),
'shorten_title' => [
'active' => ( ! empty($title_length) && Helper::is_number($title_length) && $title_length > 0 ),
'length' => ( ! empty($title_length) && Helper::is_number($title_length) ) ? $title_length : 0,
'words' => ( ! empty($title_by_words) && Helper::is_number($title_by_words) && $title_by_words > 0 ),
],
'post-excerpt' => [
'active' => ( ! empty($excerpt_length) && Helper::is_number($excerpt_length) && $excerpt_length > 0 ),
'length' => ( ! empty($excerpt_length) && Helper::is_number($excerpt_length) ) ? $excerpt_length : 0,
'keep_format' => ( ! empty($excerpt_format) && Helper::is_number($excerpt_format) && $excerpt_format > 0 ),
'words' => ( ! empty($excerpt_by_words) && Helper::is_number($excerpt_by_words) && $excerpt_by_words > 0 ),
],
'thumbnail' => [
'active' => ( ! empty($thumbnail_width) && Helper::is_number($thumbnail_width) && $thumbnail_width > 0 ),
'width' => ( ! empty($thumbnail_width) && Helper::is_number($thumbnail_width) && $thumbnail_width > 0 ) ? $thumbnail_width : 0,
'height' => ( ! empty($thumbnail_height) && Helper::is_number($thumbnail_height) && $thumbnail_height > 0 ) ? $thumbnail_height : 0,
],
'rating' => empty($rating) ? false : $rating,
'stats_tag' => [
'comment_count' => empty($stats_comments) ? false : $stats_comments,
'views' => empty($stats_views) ? false : $stats_views,
'author' => empty($stats_author) ? false : $stats_author,
'date' => [
'active' => empty($stats_date) ? false : $stats_date,
'format' => empty($stats_date_format) ? 'F j, Y' : $stats_date_format
],
'category' => empty($stats_category) ? false : $stats_category,
'taxonomy' => [
'active' => empty($stats_taxonomy) ? false : $stats_taxonomy,
'name' => empty($taxonomy) ? 'category' : $taxonomy,
]
],
'markup' => [
'custom_html' => true,
'wpp-start' => empty($wpp_start) ? '' : $wpp_start,
'wpp-end' => empty($wpp_end) ? '' : $wpp_end,
'title-start' => empty($header_start) ? '' : $header_start,
'title-end' => empty($header_end) ? '' : $header_end,
'post-html' => empty($post_html) ? '<li>{thumb} {title} <span class="wpp-meta post-stats">{stats}</span></li>' : $post_html
],
'theme' => [
'name' => trim($theme)
]
];
// Post / Page / CTP filter
$ids = array_filter(explode(",", $shortcode_ops['pid']), 'is_numeric');
// Got no valid IDs, clear
if ( empty($ids) ) {
$shortcode_ops['pid'] = '';
}
// Category filter
$ids = array_filter(explode(",", $shortcode_ops['cat']), 'is_numeric');
// Got no valid IDs, clear
if ( empty($ids) ) {
$shortcode_ops['cat'] = '';
}
// Author filter
$ids = array_filter(explode( ",", $shortcode_ops['author']), 'is_numeric');
// Got no valid IDs, clear
if ( empty($ids) ) {
$shortcode_ops['author'] = '';
}
$shortcode_content = '';
$cached = false;
// is there a title defined by user?
if (
! empty($header)
&& ! empty($header_start)
&& ! empty($header_end)
) {
$shortcode_content .= htmlspecialchars_decode($header_start, ENT_QUOTES) . $header . htmlspecialchars_decode($header_end, ENT_QUOTES);
}
// Return cached results
if ( $this->config['tools']['cache']['active'] ) {
$key = md5(json_encode($shortcode_ops));
$popular_posts = \WordPressPopularPosts\Cache::get($key);
if ( false === $popular_posts ) {
$popular_posts = new Query($shortcode_ops);
$time_value = $this->config['tools']['cache']['interval']['value']; // eg. 5
$time_unit = $this->config['tools']['cache']['interval']['time']; // eg. 'minute'
// No popular posts found, check again in 1 minute
if ( ! $popular_posts->get_posts() ) {
$time_value = 1;
$time_unit = 'minute';
}
\WordPressPopularPosts\Cache::set(
$key,
$popular_posts,
$time_value,
$time_unit
);
}
$cached = true;
} // Get popular posts
else {
$popular_posts = new Query($shortcode_ops);
}
$this->output->set_data($popular_posts->get_posts());
$this->output->set_public_options($shortcode_ops);
$this->output->build_output();
$shortcode_content .= $this->output->get_output();
// Sanitize and return shortcode HTML
$allowed_tags = wp_kses_allowed_html('post');
if ( isset($allowed_tags['form']) ) {
unset($allowed_tags['form']);
}
if ( ! empty($shortcode_ops['theme']['name']) ) {
$allowed_tags['style'] = [
'id' => 1,
'nonce' => 1,
];
}
return wp_kses($shortcode_content, $allowed_tags);
}
}

View File

@ -0,0 +1,376 @@
<?php
namespace WordPressPopularPosts;
class Helper {
/**
* Checks for valid number.
*
* @since 2.1.6
* @param int number
* @return bool
*/
public static function is_number($number)
{
return !empty($number) && is_numeric($number) && (intval($number) == floatval($number));
}
/**
* Converts a number into a short version, eg: 1000 -> 1k
*
* @see https://gist.github.com/RadGH/84edff0cc81e6326029c
* @since 5.2.0
* @param int
* @param int
* @return mixed string|bool
*/
public static function prettify_number($number, $precision = 1)
{
if ( ! is_numeric($number) )
return false;
if ( $number < 900 ) {
// 0 - 900
$n_format = number_format($number, $precision);
$suffix = '';
} elseif ( $number < 900000 ) {
// 0.9k-850k
$n_format = number_format($number / 1000, $precision);
$suffix = 'k';
} elseif ( $number < 900000000 ) {
// 0.9m-850m
$n_format = number_format($number / 1000000, $precision);
$suffix = 'm';
} elseif ( $number < 900000000000 ) {
// 0.9b-850b
$n_format = number_format($number / 1000000000, $precision);
$suffix = 'b';
} else {
// 0.9t+
$n_format = number_format($number / 1000000000000, $precision);
$suffix = 't';
}
// Remove unnecessary zeroes after decimal. "1.0" -> "1"; "1.00" -> "1"
// Intentionally does not affect partials, eg "1.50" -> "1.50"
if ( $precision > 0 ) {
$dotzero = '.' . str_repeat('0', $precision);
$n_format = str_replace($dotzero, '', $n_format);
}
return $n_format . $suffix;
}
/**
* Checks for valid date.
*
* @since 4.0.0
* @param string $date
* @param string $format
* @return bool
*/
public static function is_valid_date($date = null, $format = 'Y-m-d')
{
$d = \DateTime::createFromFormat($format, $date);
return $d && $d->format($format) === $date;
}
/**
* Returns an array of dates between two dates.
*
* @since 4.0.0
* @param string $start_date
* @param string $end_date
* @param string $format
* @return array|bool
*/
public static function get_date_range($start_date = null, $end_date = null, $format = 'Y-m-d')
{
if (
self::is_valid_date($start_date, $format)
&& self::is_valid_date($end_date, $format)
) {
$dates = [];
$begin = new \DateTime($start_date, new \DateTimeZone(Helper::get_timezone()));
$end = new \DateTime($end_date, new \DateTimeZone(Helper::get_timezone()));
if ( $begin < $end ) {
while( $begin <= $end ) {
$dates[] = $begin->format($format);
$begin->modify('+1 day');
}
}
else {
while( $begin >= $end ) {
$dates[] = $begin->format($format);
$begin->modify('-1 day');
}
}
return $dates;
}
return false;
}
/**
* Returns server date.
*
* @since 2.1.6
* @access private
* @return string
*/
public static function curdate()
{
return current_time('Y-m-d', false);
}
/**
* Returns mysql datetime.
*
* @since 2.1.6
* @access private
* @return string
*/
public static function now()
{
return current_time('mysql');
}
/**
* Returns current timestamp.
*
* @since 5.0.2
* @return string
*/
public static function timestamp()
{
// current_datetime() is WP 5.3+
return ( function_exists('current_datetime') ) ? current_datetime()->getTimestamp() : current_time('timestamp');
}
/**
* Checks whether a string is a valid timestamp.
*
* @since 5.2.0
* @param string $string
* @return bool
*/
public static function is_timestamp($string)
{
if (
( is_int($string) || ctype_digit($string) )
&& strtotime(date('Y-m-d H:i:s', $string)) === (int) $string
) {
return true;
}
return false;
}
/**
* Returns site's timezone.
*
* Code borrowed from Rarst's awesome WpDateTime class: https://github.com/Rarst/wpdatetime
*
* @since 5.0.0
* @return string
*/
public static function get_timezone()
{
$timezone_string = get_option('timezone_string');
if ( ! empty($timezone_string) ) {
return $timezone_string;
}
$offset = get_option('gmt_offset');
$sign = $offset < 0 ? '-' : '+';
$hours = (int) $offset;
$minutes = abs(($offset - (int) $offset) * 60);
$offset = sprintf('%s%02d:%02d', $sign, abs($hours), $minutes);
return $offset;
}
/**
* Returns time.
*
* @since 2.3.0
* @return string
*/
public static function microtime_float()
{
list($msec, $sec) = explode(' ', microtime());
return (float) $msec + (float) $sec;
}
/**
* Merges two associative arrays recursively.
*
* @since 2.3.4
* @link http://www.php.net/manual/en/function.array-merge-recursive.php#92195
* @param array array1
* @param array array2
* @return array
*/
public static function merge_array_r(array $array1, array $array2)
{
$merged = $array1;
foreach( $array2 as $key => &$value ) {
if ( is_array($value) && isset($merged[$key]) && is_array($merged[$key]) ) {
$merged[$key] = self::merge_array_r($merged[$key], $value);
} else {
$merged[$key] = $value;
}
}
return $merged;
}
/**
* Debug function.
*
* @since 3.0.0
* @param mixed $v variable to display with var_dump()
* @param mixed $v,... unlimited optional number of variables to display with var_dump()
*/
public static function debug($v)
{
if ( !defined('WPP_DEBUG') || !WPP_DEBUG )
return;
foreach( func_get_args() as $arg ) {
print "<pre>";
var_dump($arg);
print "</pre>";
}
}
/**
* Truncates text.
*
* @since 4.0.0
* @param string $text
* @param int $length
* @param bool $truncate_by_words
* @return string
*/
public static function truncate($text = '', $length = 25, $truncate_by_words = false, $more = '...')
{
if ( '' !== $text ) {
$charset = get_bloginfo('charset');
// Truncate by words
if ( $truncate_by_words ) {
$words = explode(" ", $text, $length + 1);
if ( count($words) > $length ) {
array_pop($words);
$text = rtrim(implode(" ", $words), ",.") . $more;
}
}
// Truncate by characters
elseif ( mb_strlen($text, $charset) > $length ) {
$text = rtrim(mb_substr($text, 0, $length , $charset), " ,.") . $more;
}
}
return $text;
}
/**
* Gets post/page ID if current page is singular
*
* @since 3.1.2
*/
public static function is_single()
{
$trackable = [];
$registered_post_types = get_post_types(['public' => true], 'names');
foreach( $registered_post_types as $post_type ) {
$trackable[] = $post_type;
}
$trackable = apply_filters('wpp_trackable_post_types', $trackable);
if (
is_singular($trackable)
&& !is_front_page()
&& !is_preview()
&& !is_trackback()
&& !is_feed()
&& !is_robots()
&& !is_customize_preview()
) {
return get_queried_object_id();
}
return false;
}
/**
* Adds scheme to a given URL.
*
* @since 5.0.0
* @param string $url
* @param string $scheme
* @return string|bool
*/
static function add_scheme($url = null, $scheme = 'https://')
{
$url_args = parse_url($url);
if ( $url_args ) {
// No need to do anything, URL is fine
if ( isset($url_args['scheme']) )
return $url;
// Return URL with scheme
return $scheme . $url_args['host'] . $url_args['path'];
}
// Invalid/malformed URL
return false;
}
/**
* Checks whether an URL points to an actual image.
*
* This function used to live in src/Image, moved it here
* on version 5.4.0 to use it where needed.
*
* @since 5.0.0
* @access private
* @param string
* @return array|bool
*/
static function is_image_url($url)
{
$path = parse_url($url, PHP_URL_PATH);
$encoded_path = array_map('urlencode', explode('/', $path));
$parse_url = str_replace($path, implode('/', $encoded_path), $url);
if ( ! filter_var($parse_url, FILTER_VALIDATE_URL) )
return false;
// Check extension
$file_name = basename($path);
$file_name = sanitize_file_name($file_name);
$ext = strtolower(pathinfo($file_name, PATHINFO_EXTENSION));
$allowed_ext = ['jpg', 'jpeg', 'png', 'gif'];
if ( ! in_array($ext, $allowed_ext) )
return false;
// sanitize URL, just in case
$image_url = esc_url($url);
// remove querystring
preg_match('/[^\?]+\.(jpg|JPG|jpe|JPE|jpeg|JPEG|gif|GIF|png|PNG)/', $image_url, $matches);
return ( is_array($matches) && ! empty($matches) ) ? $matches : false;
}
}

View File

@ -0,0 +1,49 @@
<?php
/**
* Define the internationalization functionality
*
* Loads and defines the internationalization files for this plugin
* so that it is ready for translation.
*
* @since 4.0.0
*
* @package WordPressPopularPosts
*/
namespace WordPressPopularPosts;
class I18N {
/**
* Plugin options.
*
* @var array $config
* @access private
*/
private $config;
/**
* Construct.
*
* @since 5.3.0
*/
public function __construct(array $config)
{
$this->config = $config;
}
/**
* Load the plugin text domain for translation.
*
* @since 1.0.0
*/
public function load_plugin_textdomain()
{
// This is basically a "hack" and should be removed in the future
// if/when we figure out why Polylang doesn't load WPP's mo files
// while WPML does that automatically.
if ( ! is_admin() && ! $this->config['tools']['ajax'] && function_exists('PLL') ) {
unload_textdomain('wordpress-popular-posts');
load_textdomain('wordpress-popular-posts', WP_LANG_DIR . '/plugins/wordpress-popular-posts-' . get_locale() . '.mo');
}
}
}

View File

@ -0,0 +1,920 @@
<?php
/**
* This class builds/retrieves the thumbnail image of each popular posts.
*
*
* @package WordPressPopularPosts
* @author Hector Cabrera <me@cabrerahector.com>
*/
namespace WordPressPopularPosts;
class Image {
/**
* Default thumbnail.
*
* @since 2.2.0
* @var string
*/
private $default_thumbnail = '';
/**
* Plugin uploads directory.
*
* @since 3.0.4
* @var array
*/
private $uploads_dir = [];
/**
* Admin settings.
*
* @since 5.0.0
* @var array
*/
private $admin_options = [];
/**
* Available image sizes.
*
* @since 5.0.0
* @var array
*/
private $available_sizes = [];
/**
* Available image descriptors.
*
* @since 5.3.0
* @var array
*/
private $descriptors = [];
/**
* Construct.
*
* @since 4.0.0
* @param array $admin_options
*/
public function __construct(array $admin_options)
{
$this->admin_options = $admin_options;
// Set default thumbnail
$this->default_thumbnail = plugins_url() . "/wordpress-popular-posts/assets/images/no_thumb.jpg";
if ( Helper::is_image_url($this->admin_options['tools']['thumbnail']['default']) )
$this->default_thumbnail = $this->admin_options['tools']['thumbnail']['default'];
// Set uploads folder
$wp_upload_dir = wp_get_upload_dir();
$this->uploads_dir['basedir'] = $wp_upload_dir['basedir'] . "/" . 'wordpress-popular-posts';
$this->uploads_dir['baseurl'] = $wp_upload_dir['baseurl'] . "/" . 'wordpress-popular-posts';
if ( ! is_dir($this->uploads_dir['basedir']) ) {
// Couldn't create the folder, store thumbnails in Uploads
if ( ! wp_mkdir_p($this->uploads_dir['basedir']) ) {
$this->uploads_dir['basedir'] = $wp_upload_dir['basedir'];
$this->uploads_dir['baseurl'] = $wp_upload_dir['baseurl'];
}
}
// Set descriptors
$this->descriptors = ['1.5', '2', '2.5', '3'];
}
/**
* Get WPP's uploads folder.
*
* @since 4.0.0
* @access public
* @return array|bool
*/
public function get_plugin_uploads_dir()
{
if ( is_array($this->uploads_dir) && ! empty($this->uploads_dir) )
return $this->uploads_dir;
return false;
}
/**
* Returns an image.
*
* @since 5.0.0
* @param int $post_id Post ID
* @param array $size Image size (width & height)
* @param string $source Image source
* @param bool $crop Whether to crop the image or not
* @param string $build Whether to build the image or get an existing one
* @return string
*/
public function get($post_id, $size, $source, $crop = true, $build = 'manual')
{
// Bail, $post_id is not an integer
if ( ! is_numeric($post_id) ) {
return '';
}
$alt = '';
$classes = ['wpp-thumbnail', 'wpp_' . $source];
$filename = $post_id . '-' . $source . '-' . $size[0] . 'x' . $size[1];
$cached = $this->exists($filename);
// We have a thumbnail already, return it
if ( $cached ) {
$classes[] = 'wpp_cached_thumb';
/**
* Filters CSS classes assigned to the thumbnail
*
* @since 5.0.0
* @param array CSS classes
* @param int The post ID
* @return array The new CSS classes
*/
$classes = apply_filters(
'wpp_thumbnail_class_attribute',
$classes,
$post_id
);
/**
* Filters ALT attribute assigned to the thumbnail
*
* @since 5.0.0
* @param string Original ALT attribute
* @param int The post ID
* @return string The new ALT attribute
*/
$alt = apply_filters(
'wpp_thumbnail_alt_attribute',
$this->get_alt_attribute($post_id, $source),
$post_id
);
return $this->render(
$cached,
$size,
is_array($classes) ? implode(' ', $classes) : 'wpp-thumbnail wpp_' . $source,
is_string($alt) ? $alt : ''
);
}
$thumb_url = null;
// Return image as-is, no need to create a new thumbnail
if (
( 'custom_field' == $source && ! $this->admin_options['tools']['thumbnail']['resize'] )
|| ( 'featured' == $source && 'predefined' == $build )
){
// Get custom field image URL
if ( 'custom_field' == $source && ! $this->admin_options['tools']['thumbnail']['resize'] ) {
$thumb_url = get_post_meta(
$post_id,
$this->admin_options['tools']['thumbnail']['field'],
true
);
if ( ! $thumb_url || ! Helper::is_image_url($thumb_url) ) {
// Is this an attachment ID instead of an image URL?
if ( Helper::is_number($thumb_url) ) {
$thumb_url = wp_get_attachment_image_src($thumb_url, 'full');
$thumb_url = is_array($thumb_url) ? $thumb_url[0] : null;
} else {
$thumb_url = null;
}
}
}
// Get Post Thumbnail
else {
if (
current_theme_supports('post-thumbnails')
&& has_post_thumbnail($post_id)
) {
// Find corresponding image size
$stock_size = null;
$images_sizes = $this->get_sizes();
foreach ( $images_sizes as $name => $attr ) :
if (
$attr['width'] == $size[0]
&& $attr['height'] == $size[1]
&& $attr['crop'] == $crop
) {
$stock_size = $name;
break;
}
endforeach;
// Couldn't find a matching size so let's go with width/height combo instead
// (this should never happen but better safe than sorry!)
if ( null == $stock_size ) {
$stock_size = $size;
}
/**
* Filters CSS classes assigned to the thumbnail
*
* @since 5.0.0
* @param array CSS classes
* @param int The post ID
* @return array The new CSS classes
*/
$classes = apply_filters(
'wpp_thumbnail_class_attribute',
$classes,
$post_id
);
$featured_image = get_the_post_thumbnail(
$post_id,
$stock_size
);
if ( strpos($featured_image, 'class="') && is_array($classes) && ! empty($classes) )
$featured_image = str_replace('class="', 'class="'. esc_attr(implode(' ', $classes)) . ' ', $featured_image);
if ( $this->admin_options['tools']['thumbnail']['lazyload'] && false == strpos($featured_image, 'loading="lazy"') ) {
$featured_image = str_replace('src="', 'loading="lazy" src="', $featured_image);
}
return $featured_image;
}
}
}
// Build a new thumbnail and return it
else {
$file_path = null;
if ( 'custom_field' == $source && $this->admin_options['tools']['thumbnail']['resize'] ) {
$thumb_url = get_post_meta(
$post_id,
$this->admin_options['tools']['thumbnail']['field'],
true
);
if ( ! $thumb_url || ! Helper::is_image_url($thumb_url) ) {
// Is this an attachment ID instead of an image URL?
// If so, try to fetch the image
if ( Helper::is_number($thumb_url) ) {
$thumb_url = wp_get_attachment_image_src($thumb_url, 'full');
$thumb_url = is_array($thumb_url) ? $thumb_url[0] : null;
} else {
$thumb_url = null;
}
}
if ( $thumb_url && Helper::is_image_url($thumb_url) ) {
$file_path = $this->url_to_path($thumb_url, $post_id);
}
} else {
$file_meta = $this->get_file_meta($post_id, $source);
if ( is_array($file_meta) && isset($file_meta['path']) ) {
$alt = isset($file_meta['alt']) ? $file_meta['alt'] : '';
$file_path = $file_meta['path'];
}
}
if ( $file_path ) {
$extension = pathinfo($file_path, PATHINFO_EXTENSION);
$thumb_url = $this->resize(
$file_path,
$filename . '.' . $extension,
$size,
$crop
);
}
}
if ( ! $thumb_url ) {
$classes[] = 'wpp_def_no_src';
$thumb_url = $this->get_default_url($post_id);
}
/**
* Filters CSS classes assigned to the thumbnail
*
* @since 5.0.0
* @param array CSS classes
* @param int The post ID
* @return array The new CSS classes
*/
$classes = apply_filters(
'wpp_thumbnail_class_attribute',
$classes,
$post_id
);
/**
* Filters ALT attribute assigned to the thumbnail
*
* @since 5.0.0
* @param string Original ALT attribute
* @param int The post ID
* @return string The new ALT attribute
*/
$alt = apply_filters(
'wpp_thumbnail_alt_attribute',
$this->get_alt_attribute($post_id, $source),
$post_id
);
return $this->render(
$thumb_url,
$size,
is_array($classes) ? implode(' ', $classes) : 'wpp-thumbnail wpp_' . $source,
is_string($alt) ? $alt : ''
);
}
/**
* Checks whether a given thumbnail exists.
*
* @since 5.0.0
* @access private
* @param string $filename
* @return string|bool Full URL to image
*/
private function exists($filename)
{
// Do we have thumbnail already?
$file = $this->resolve(trailingslashit($this->get_plugin_uploads_dir()['basedir']) . $filename);
if ( $file && is_file($file) ) {
$extension = pathinfo($file, PATHINFO_EXTENSION);
return trailingslashit($this->get_plugin_uploads_dir()['baseurl']) . $filename . '.' . $extension;
}
return false;
}
/**
* Resolves filename.
*
* @since 5.0.0
* @access private
* @author Ioan Chiriac
* @link https://stackoverflow.com/a/29468093/9131961
* @param string $name
* @return string|bool Resolved path, or false if not found
*/
private function resolve($name)
{
$info = pathinfo($name);
// File already contains an extension, return it
if ( isset($info['extension']) && ! empty($info['extension']) ) {
return $name;
}
$filename = $info['filename'];
$len = strlen($filename);
// open the folder
$dh = opendir($info['dirname']);
if ( ! $dh ) {
return false;
}
// scan each file in the folder
while ( ($file = readdir($dh)) !== false ) {
if ( strncmp($file, $filename, $len) === 0 ) {
if ( strlen($name) > $len ) {
// if name contains a directory part
$name = substr($name, 0, strlen($name) - $len) . $file;
} else {
// if the name is at the path root
$name = $file;
}
closedir($dh);
return $name;
}
}
// file not found
closedir($dh);
return false;
}
/**
* Retrieves local path to image.
*
* @since 5.0.0
* @access private
* @param string $url
* @param integer $post_ID
* @return string|boolean Path to image, or false if not found
*/
private function url_to_path($url, $post_ID = null)
{
if ( Helper::is_image_url($url) ) {
$attachment_id = $this->get_attachment_id($url);
// Image is hosted locally
if ( $attachment_id ) {
return get_attached_file($attachment_id);
}
// Image hosted elsewhere?
if ( $post_ID && Helper::is_number($post_ID) )
return $this->fetch_external_image($post_ID, $url);
}
return false;
}
/**
* Gets image meta.
*
* @since 5.0.0
* @access private
* @param int $id Post ID
* @param string $source Image source
* @return array|bool
*/
private function get_file_meta($id, $source)
{
// get thumbnail path from the Featured Image
if ( "featured" == $source ) {
if ( $thumbnail_id = get_post_thumbnail_id($id) ) {
// image path
return [
'path' => get_attached_file($thumbnail_id),
'alt' => get_post_meta($thumbnail_id, '_wp_attachment_image_alt', true)
];
}
}
// get thumbnail path from first image attachment
elseif ( "first_attachment" == $source ) {
$args = [
'numberposts' => 1,
'order' => 'ASC',
'post_parent' => $id,
'post_type' => 'attachment',
'post_mime_type' => 'image'
];
$post_attachments = get_children($args);
if ( ! empty($post_attachments) ) {
$first_img = array_shift($post_attachments);
return [
'path' => get_attached_file($first_img->ID),
'alt' => get_post_meta($first_img->ID, '_wp_attachment_image_alt', true)
];
}
}
// get thumbnail path from post content
elseif ( "first_image" == $source ) {
/** @var wpdb $wpdb */
global $wpdb;
if ( $content = $wpdb->get_var("SELECT post_content FROM {$wpdb->posts} WHERE ID = {$id};") ) {
// at least one image has been found
if ( preg_match('/<img[^>]+>/i', $content, $img) ) {
// get img src attribute from the first image found
preg_match('/(src)="([^"]*)"/i', $img[0], $src_attr);
if ( isset($src_attr[2]) && ! empty($src_attr[2]) ) {
// get img alt attribute from the first image found
$alt = '';
preg_match('/(alt)="([^"]*)"/i', $img[0], $alt_attr);
if ( isset($alt_attr[2]) && !empty($alt_attr[2]) ) {
$alt = $alt_attr[2];
}
// image from Media Library
if ( $attachment_id = $this->get_attachment_id($src_attr[2]) ) {
return [
'path' => get_attached_file($attachment_id),
'alt' => $alt
];
} // external image?
else {
return [
'path' => $this->fetch_external_image($id, $src_attr[2]),
'alt' => $alt
];
}
}
}
}
}
return false;
}
/**
* Gets image ALT attribute.
*
* @since 5.0.0
* @access private
* @param int $id Post ID
* @param string $source Image source
* @return string
*/
private function get_alt_attribute($id, $source)
{
$alt = '';
// get thumbnail path from the Featured Image
if ( "featured" == $source ) {
if ( $thumbnail_id = get_post_thumbnail_id($id) ) {
// image path
$alt = get_post_meta($thumbnail_id, '_wp_attachment_image_alt', true);
}
}
// get thumbnail path from first image attachment
elseif ( "first_attachment" == $source ) {
$args = [
'numberposts' => 1,
'order' => 'ASC',
'post_parent' => $id,
'post_type' => 'attachment',
'post_mime_type' => 'image'
];
$post_attachments = get_children($args);
if ( ! empty($post_attachments) ) {
$first_img = array_shift($post_attachments);
$alt = get_post_meta($first_img->ID, '_wp_attachment_image_alt', true);
}
}
// get thumbnail path from post content
elseif ( "first_image" == $source ) {
/** @var wpdb $wpdb */
global $wpdb;
if ( $content = $wpdb->get_var("SELECT post_content FROM {$wpdb->posts} WHERE ID = {$id};") ) {
// at least one image has been found
if ( preg_match('/<img[^>]+>/i', $content, $img) ) {
// get img alt attribute from the first image found
preg_match('/(alt)="([^"]*)"/i', $img[0], $alt_attr);
if ( isset($alt_attr[2]) && !empty($alt_attr[2]) ) {
$alt = $alt_attr[2];
}
}
}
}
return $alt;
}
/**
* Get the Attachment ID for a given image URL.
*
* @since 3.0.0
* @access private
* @author Frankie Jarrett
* @link http://frankiejarrett.com/get-an-attachment-id-by-url-in-wordpress/
* @param string $url
* @return int|null
*/
private function get_attachment_id($url)
{
$url = Helper::add_scheme(
$url,
is_ssl() ? 'https://' : 'http://'
);
// Split the $url into two parts with the wp-content directory as the separator.
$parse_url = explode(parse_url(WP_CONTENT_URL, PHP_URL_PATH), $url);
// Get the host of the current site and the host of the $url, ignoring www.
$this_host = str_ireplace('www.', '', parse_url(home_url(), PHP_URL_HOST));
$file_host = str_ireplace('www.', '', parse_url($url, PHP_URL_HOST));
// Return nothing if there aren't any $url parts or if the current host and $url host do not match.
if (
! isset($parse_url[1])
|| empty($parse_url[1])
|| ($this_host != $file_host)
) {
return false;
}
// Now we're going to quickly search the DB for any attachment GUID with a partial path match.
// Example: /uploads/2013/05/test-image.jpg
global $wpdb;
if ( ! $attachment = $wpdb->get_col($wpdb->prepare("SELECT ID FROM {$wpdb->prefix}posts WHERE guid RLIKE %s;", $parse_url[1])) ) {
// Maybe it's a resized image, so try to get the full one
$parse_url[1] = preg_replace('/-[0-9]{1,4}x[0-9]{1,4}\.(jpg|jpeg|png|gif|bmp)$/i', '.$1', $parse_url[1]);
$attachment = $wpdb->get_col($wpdb->prepare("SELECT ID FROM {$wpdb->prefix}posts WHERE guid RLIKE %s;", $parse_url[1]));
}
// Returns null if no attachment is found.
return isset($attachment[0]) ? $attachment[0] : NULL;
}
/**
* Fetchs external images.
*
* @since 2.3.3
* @access private
* @param int $id Post ID.
* @param string $url Image url.
* @return string|bool Image path, or false on failure.
*/
private function fetch_external_image($id, $url)
{
if ( ! Helper::is_image_url($url) )
return false;
$full_image_path = trailingslashit($this->get_plugin_uploads_dir()['basedir']) . "{$id}_" . sanitize_file_name(rawurldecode(wp_basename($url)));
// if the file exists already, return URL and path
if ( file_exists($full_image_path) )
return $full_image_path;
$url = Helper::add_scheme(
$url,
is_ssl() ? 'https://' : 'http://'
);
$accepted_status_codes = [200, 301, 302];
$response = wp_remote_head($url, ['timeout' => 5, 'sslverify' => false]);
if (
! is_wp_error($response)
&& in_array(wp_remote_retrieve_response_code($response), $accepted_status_codes)
) {
require_once(ABSPATH . 'wp-admin/includes/file.php');
$url = str_replace('https://', 'http://', $url);
$tmp = download_url($url);
// File was downloaded successfully
if ( ! is_wp_error($tmp) ) {
// Determine image type
if ( function_exists('exif_imagetype') ) {
$image_type = exif_imagetype($tmp);
} else {
$image_type = getimagesize($tmp);
$image_type = ( isset($image_type[2]) ) ? $image_type[2] : NULL;
}
// Valid image, save it
if ( in_array($image_type, [IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG]) ) {
// move file to Uploads
if ( @rename($tmp, $full_image_path) ) {
// borrowed from WP - set correct file permissions
$stat = stat(dirname($full_image_path));
$perms = $stat['mode'] & 0000644;
@chmod($full_image_path, $perms);
return $full_image_path;
}
}
// Invalid file, remove it
@unlink($tmp);
}
}
return false;
}
/**
* Creates thumbnails.
*
* @since 3.0.0
* @access private
* @param string $path Image path
* @param string $filename Image filename
* @param array $size Image size
* @param bool $crop Whether to crop the image or not
* @return string|bool Image URL on success, false on error
*/
private function resize($path, $filename, $size, $crop = true)
{
$image = wp_get_image_editor($path);
// valid image, create thumbnails
if ( ! is_wp_error($image) ) {
$original_size = $image->get_size();
$sizes = [
'1x' => $size
];
$thumbnail = '';
/**
* Hook to enable/disable retina support.
* @since 5.3.0
*/
$retina_support = apply_filters('wpp_retina_support', true);
if ( $retina_support ) {
// Calculate thumbnail sizes
foreach( $this->descriptors as $descriptor ) {
$new_size_width = floor($descriptor * $size[0]);
$new_size_height = floor($descriptor * $size[1]);
if (
$new_size_width <= $original_size['width']
&& $new_size_height <= $original_size['height']
) {
$sizes[$descriptor . 'x'] = [$new_size_width, $new_size_height];
}
}
}
$path_parts = null;
// Generate thumbnails
foreach( $sizes as $d => $s ) {
if ( '1x' == $d ) {
$thumbnail = $this->generate_thumbnail($path, $filename, $s, $crop);
// Image could not be generated, let's bail early.
if ( ! $thumbnail )
break;
} else {
if ( ! $path_parts )
$path_parts = pathinfo($filename);
$filename_with_descriptor = $path_parts['filename'] . "@{$d}." . $path_parts['extension'];
$this->generate_thumbnail($path, $filename_with_descriptor, $s, $crop);
}
}
return $thumbnail;
}
return false;
}
/**
* Creates image.
*
* @since 5.3.0
* @access private
* @param string $path Image path
* @param string $filename Image filename
* @param array $size Image size
* @param bool $crop Whether to crop the image or not
* @return string|bool Image URL on success, false on error
*/
private function generate_thumbnail($path, $filename, $size, $crop = true)
{
$image = wp_get_image_editor($path);
// valid image, create thumbnail
if ( ! is_wp_error($image) ) {
/**
* Hook to change the image compression quality of WPP's thumbnails.
* @since 4.2.1
*/
$quality = apply_filters('wpp_thumbnail_compression_quality', null);
if ( ! ctype_digit($quality) )
$quality = null; // Fallback to core's default
$image->set_quality($quality);
$image->resize($size[0], $size[1], $crop);
$new_img = $image->save(trailingslashit($this->get_plugin_uploads_dir()['basedir']) . $filename);
if ( ! is_wp_error($new_img) )
return trailingslashit($this->get_plugin_uploads_dir()['baseurl']) . $filename;
}
return false;
}
/**
* Generates srcset attribute for this image.
*
* @since 5.3.0
* @param string $src
* @return string
*/
private function get_srcset($src)
{
/**
* Hook to enable/disable retina support.
* @since 5.3.0
*/
$retina_support = apply_filters('wpp_retina_support', true);
if ( ! $retina_support )
return '';
$path_parts = pathinfo($src);
$srcset = [$src];
foreach( $this->descriptors as $descriptor ) {
$d = "{$descriptor}x";
$filename = $path_parts['filename'] . "@{$d}." . $path_parts['extension'];
if ( @file_exists(trailingslashit($this->get_plugin_uploads_dir()['basedir']) . $filename) ) {
$srcset[] = $path_parts['dirname'] . '/' . $filename . ' ' . $d;
}
}
return ( count($srcset) > 1 ) ? ' srcset="' . implode(', ', $srcset) . '" ' : '';
}
/**
* Render image tag.
*
* @since 3.0.0
* @access public
* @param string $src Image URL
* @param array $dimension Image's width and height
* @param string $class CSS class
* @param object $alt Alternative text
* @param string $error Error, if the image could not be created
* @return string
*/
public function render($src, $size, $class, $alt = '', $error = null)
{
$img_tag = '';
if ( $error ) {
$img_tag = '<!-- ' . $error . ' --> ';
}
// Make sure we use the right protocol
$src = esc_url(is_ssl() ? str_ireplace("http://", "https://", $src) : $src);
// Get srcset, if available
$srcset = $this->get_srcset($src);
$src = 'src="' . $src. '"' . $srcset;
// Lazy Load attribute, if enabled
$lazyload = ( $this->admin_options['tools']['thumbnail']['lazyload'] ) ? ' loading="lazy"' : '';
$img_tag .= '<img ' . $src . ' width="' . $size[0] . '" height="' . $size[1] . '" alt="' . esc_attr($alt) . '" class="' . esc_attr($class) . '"' . $lazyload . ' />';
return apply_filters('wpp_render_image', $img_tag);
}
/**
* Gets list of available thumbnails sizes
*
* @since 3.2.0
* @link http://codex.wordpress.org/Function_Reference/get_intermediate_image_sizes
* @param string $size
* @return array|bool
*/
public function get_sizes($size = '')
{
if ( ! is_array($this->available_sizes) || empty($this->available_sizes) ) {
global $_wp_additional_image_sizes;
$this->available_sizes = [];
$get_intermediate_image_sizes = get_intermediate_image_sizes();
// Create the full array with sizes and crop info
foreach( $get_intermediate_image_sizes as $_size ) {
if ( in_array($_size, ['thumbnail', 'medium', 'large']) ) {
$this->available_sizes[$_size]['width'] = get_option($_size . '_size_w');
$this->available_sizes[$_size]['height'] = get_option($_size . '_size_h');
$this->available_sizes[$_size]['crop'] = (bool) get_option($_size . '_crop');
} elseif ( isset($_wp_additional_image_sizes[$_size]) ) {
$this->available_sizes[$_size] = [
'width' => $_wp_additional_image_sizes[$_size]['width'],
'height' => $_wp_additional_image_sizes[$_size]['height'],
'crop' => $_wp_additional_image_sizes[$_size]['crop']
];
}
}
}
// Get only 1 size if found
if ( $size ) {
if ( isset($this->available_sizes[$size]) ) {
return $this->available_sizes[$size];
}
return false;
}
return $this->available_sizes;
}
/**
* Returns the URL of the default thumbnail image.
*
* @since 5.0.0
* @param int|null
* @return string
*/
public function get_default_url($post_ID = null)
{
if ( has_filter('wpp_default_thumbnail_url') ) {
$default_thumbnail_url = apply_filters('wpp_default_thumbnail_url', $this->default_thumbnail, $post_ID);
if ( $default_thumbnail_url != $this->default_thumbnail && Helper::is_image_url($default_thumbnail_url) )
return $default_thumbnail_url;
}
return $this->default_thumbnail;
}
}

View File

@ -0,0 +1,976 @@
<?php
/**
* This class formats the HTML output of every popular posts listing.
*
*
* @package WordPressPopularPosts
* @author Hector Cabrera <me@cabrerahector.com>
*/
namespace WordPressPopularPosts;
class Output {
/**
* Popular posts data.
*
* @since 4.0.0
* @var string
*/
private $data;
/**
* HTML output.
*
* @since 4.0.0
* @var string
*/
private $output;
/**
* Widget / shortcode settings.
*
* @since 4.0.0
* @var array
*/
private $public_options = [];
/**
* Administrative settings.
*
* @since 2.3.3
* @var array
*/
private $admin_options = [];
/**
* Default excerpt 'more' string.
*
* @since 4.2.1
* @var string
*/
private $more;
/**
* Image object
*
* @since 4.0.2
* @var WordPressPopularPosts\Image
*/
private $thumbnail;
/**
* Translate object.
*
* @var \WordPressPopularPosts\Translate $translate
* @access private
*/
private $translate;
/**
* Themer object.
*
* @var \WordPressPopularPosts\Themer $themer
* @access private
*/
private $themer;
/**
* WordPress Date format.
*
* @var string
* @access private
*/
private $wp_date_format;
/**
* Constructor.
*
* @since 4.0.0
* @param array $public_options
* @param array $admin_options
* @param WordPressPopularPosts\Image $thumbnail
* @param WordPressPopularPosts\Translate $translate
* @param \WordPressPopularPosts\Themer $themer
*/
public function __construct(array $public_options, array $admin_options, Image $thumbnail, Translate $translate, \WordPressPopularPosts\Themer $themer)
{
$this->public_options = $public_options;
$this->admin_options = $admin_options;
$this->thumbnail = $thumbnail;
$this->translate = $translate;
$this->themer = $themer;
$this->more = '...';
$this->wp_date_format = get_option('date_format');
if ( ! $this->wp_date_format )
$this->wp_date_format = 'F j, Y';
}
/**
* Sets data.
*
* @since 5.0.0
* @param array
*/
public function set_data(array $data = [])
{
$this->data = $data;
}
/**
* Sets public options.
*
* @since 5.0.0
* @param array
*/
public function set_public_options(array $public_options = [])
{
$this->public_options = Helper::merge_array_r(
Settings::get('widget_options'),
$public_options
);
}
/**
* Output the HTML.
*
* @since 4.0.0
*/
public function output()
{
echo $this->get_output();
}
/**
* Return the HTML.
*
* @since 4.0.0
* @return string
*/
public function get_output()
{
$this->output = "\n" . ( WP_DEBUG ? '<!-- WordPress Popular Posts v' . WPP_VERSION . ( $this->admin_options['tools']['cache']['active'] ? ' - cached' : '' ) . ' -->' : '' ) . "\n" . $this->output;
return $this->output;
}
/**
* Build the HTML output.
*
* @since 4.0.0
*/
public function build_output()
{
// Got some posts, format 'em!
if ( ! empty($this->data) ) {
$this->output = '';
// Allow WP themers / coders access to raw data
// so they can build their own output
if ( has_filter('wpp_custom_html') ) {
$this->output .= apply_filters('wpp_custom_html', $this->data, $this->public_options);
return;
}
if (
isset($this->public_options['theme']['name'])
&& $this->public_options['theme']['name']
) {
$this->output .= '<div class="popular-posts-sr">';
if ( @file_exists(get_template_directory() . '/wordpress-popular-posts/themes/' . $this->public_options['theme']['name'] . '/style.css') ) {
$theme_stylesheet = get_template_directory() . '/wordpress-popular-posts/themes/' . $this->public_options['theme']['name'] . '/style.css';
} else {
$theme_stylesheet = $this->themer->get_theme($this->public_options['theme']['name'])['path'] . '/style.css';
}
$theme_css_rules = wp_strip_all_tags(file_get_contents($theme_stylesheet), true);
$additional_styles = '';
if ( has_filter('wpp_additional_theme_styles') ) {
$additional_styles = wp_strip_all_tags(apply_filters('wpp_additional_theme_styles', '', $this->public_options['theme']['name']), true);
if ( $additional_styles )
$additional_styles = ' /* additional rules */ ' . $additional_styles;
}
$this->output .= '<style>' . $theme_css_rules . $additional_styles . '</style>';
}
/* Open HTML wrapper */
// Output a custom wrapper
if (
isset($this->public_options['markup']['custom_html'])
&& $this->public_options['markup']['custom_html']
&& isset($this->public_options['markup']['wpp-start'])
&& isset($this->public_options['markup']['wpp-end'])
){
$this->output .= "\n" . htmlspecialchars_decode($this->public_options['markup']['wpp-start'], ENT_QUOTES) ."\n";
}
// Output the default wrapper
else {
$classes = "wpp-list";
if ( $this->public_options['thumbnail']['active'] )
$classes .= " wpp-list-with-thumbnails";
$this->output .= "\n" . "<ul class=\"{$classes}\">" . "\n";
}
$position = 0;
// Format each post
foreach( $this->data as $post_object ) {
$position++;
$this->output .= $this->render_post($post_object, $position);
}
/* Close HTML wrapper */
// Output a custom wrapper
if (
isset($this->public_options['markup']['custom_html'])
&& $this->public_options['markup']['custom_html']
&& isset($this->public_options['markup']['wpp-start'])
&& isset($this->public_options['markup']['wpp-end'])
){
$this->output .= "\n" . htmlspecialchars_decode($this->public_options['markup']['wpp-end'], ENT_QUOTES) ."\n";
}
// Output default wrapper
else {
$this->output .= "</ul>" . "\n";
}
if (
isset($this->public_options['theme']['name'])
&& $this->public_options['theme']['name']
) {
$this->output .= "</div>";
}
}
// Got nothing to show, give 'em the old "Sorry. No data so far." message!
else {
$this->output = apply_filters('wpp_no_data', "<p class=\"wpp-no-data\">" . __('Sorry. No data so far.', 'wordpress-popular-posts') . "</p>");
}
}
/**
* Build the HTML markup for a single post.
*
* @since 4.0.0
* @access private
* @param object $post_object
* @param integer $position
* @return string
*/
private function render_post(\stdClass $post_object, $position = 1)
{
$is_single = $this->is_single();
$post = '';
$post_id = $post_object->id;
$trid = $this->translate->get_object_id(
$post_object->id,
get_post_type($post_object->id)
);
if ( $post_id != $trid ) {
$post_id = $trid;
}
$is_current_post = ( $is_single && ($is_single == $post_id || $is_single == $post_object->id) ) ? true : false;
// Permalink
$permalink = $this->get_permalink($post_object, $post_id);
// Post title (and title attribute)
$post_title_attr = esc_attr(wp_strip_all_tags($this->get_title($post_object, $post_id)));
$post_title = $this->get_title($post_object, $post_id);
if ( $this->public_options['shorten_title']['active'] ) {
$length = ( filter_var($this->public_options['shorten_title']['length'], FILTER_VALIDATE_INT) && $this->public_options['shorten_title']['length'] > 0 )
? $this->public_options['shorten_title']['length']
: 25;
$more = $this->public_options['shorten_title']['words'] ? ' ' . $this->more : $this->more;
$more = apply_filters('wpp_title_more', $more);
$post_title = Helper::truncate($post_title, $length, $this->public_options['shorten_title']['words'], $more);
}
// Thumbnail
$post_thumbnail = $this->get_thumbnail($post_id);
// Post excerpt
$post_excerpt = $this->get_excerpt($post_object, $post_id);
// Post rating
$post_rating = $this->get_rating($post_object);
/**
* Post meta
*/
// Post date
$post_date = $this->get_date($post_object);
// Post taxonomies
$post_taxonomies = $this->get_taxonomies($post_id);
// Post author
$post_author = $this->get_author($post_object, $post_id);
// Post views count
$post_views = $this->get_pageviews($post_object);
// Post comments count
$post_comments = $this->get_comments($post_object);
// Post meta
$meta_arr = $this->get_metadata(
$post_object,
$post_id,
$post_date,
$post_taxonomies,
$post_author,
$post_views,
$post_comments
);
if (
is_array($meta_arr)
&& ! empty($meta_arr)
&& "views" == $this->public_options['order_by']
) {
$keys = ['views', 'comments', 'author', 'date', 'taxonomy'];
$new_meta_arr = [];
foreach($keys as $key) {
if ( isset($meta_arr[$key]))
$new_meta_arr[$key] = $meta_arr[$key];
}
if ( ! empty($new_meta_arr) )
$meta_arr = $new_meta_arr;
}
$post_meta_separator = apply_filters('wpp_post_meta_separator', ' | ');
$post_meta = join($post_meta_separator, $meta_arr);
$prettify_numbers = apply_filters('wpp_pretiffy_numbers', true);
// Build custom HTML output
if ( $this->public_options['markup']['custom_html'] ) {
$data = [
'id' => $post_id,
'is_current_post' => $is_current_post,
'title' => '<a href="' . $permalink . '" ' . ($post_title_attr !== $post_title ? 'title="' . $post_title_attr . '" ' : '' ) . 'class="wpp-post-title" target="' . $this->admin_options['tools']['link']['target'] . '">' . $post_title . '</a>',
'title_attr' => $post_title_attr,
'summary' => $post_excerpt,
'stats' => $post_meta,
'img' => ( ! empty($post_thumbnail) ) ? '<a href="' . $permalink . '" ' . ($post_title_attr !== $post_title ? 'title="' . $post_title_attr . '" ' : '' ) . 'target="' . $this->admin_options['tools']['link']['target'] . '">' . $post_thumbnail . '</a>' : '',
'img_no_link' => $post_thumbnail,
'url' => $permalink,
'text_title' => $post_title,
'taxonomy' => $post_taxonomies,
'taxonomy_copy' => isset($meta_arr['taxonomy']) ? $meta_arr['taxonomy'] : null,
'author' => ( ! empty($post_author) ) ? '<a href="' . get_author_posts_url($post_object->uid != $post_id ? get_post_field('post_author', $post_id) : $post_object->uid ) . '">' . $post_author . '</a>' : '',
'author_copy' => isset($meta_arr['author']) ? $meta_arr['author'] : null,
'author_name' => $post_author,
'author_url' => ( ! empty($post_author) ) ? get_author_posts_url($post_object->uid != $post_id ? get_post_field('post_author', $post_id) : $post_object->uid ) : '',
'views' => ( $this->public_options['order_by'] == "views" || $this->public_options['order_by'] == "comments" ) ? ($prettify_numbers ? Helper::prettify_number($post_views) : number_format_i18n($post_views)) : ($prettify_numbers ? Helper::prettify_number($post_views, 2) : number_format_i18n($post_views, 2)),
'views_copy' => isset($meta_arr['views']) ? $meta_arr['views'] : null,
'comments' => $prettify_numbers ? Helper::prettify_number($post_comments) : number_format_i18n($post_comments),
'comments_copy' => isset($meta_arr['comments']) ? $meta_arr['comments'] : null,
'date' => $post_date,
'date_copy' => isset($meta_arr['date']) ? $meta_arr['date'] : null,
'total_items' => count($this->data),
'item_position' => $position
];
$post = $this->format_content(htmlspecialchars_decode($this->public_options['markup']['post-html'], ENT_QUOTES), $data, $this->public_options['rating']). "\n";
} // Use the "stock" HTML output
else {
$wpp_post_class = [];
if ( $is_current_post ) {
$wpp_post_class[] = "current";
}
// Allow themers / plugin developer
// to add custom classes to each post
$wpp_post_class = apply_filters("wpp_post_class", $wpp_post_class, $post_id);
$post_thumbnail = ( ! empty($post_thumbnail) )
? "<a href=\"{$permalink}\" " . ($post_title_attr !== $post_title ? "title=\"{$post_title_attr}\" " : "") . "target=\"{$this->admin_options['tools']['link']['target']}\">{$post_thumbnail}</a>\n"
: "";
$post_excerpt = ( ! empty($post_excerpt) )
? " <span class=\"wpp-excerpt\">{$post_excerpt}</span>\n"
: "";
$post_meta = ( ! empty($post_meta) )
? " <span class=\"wpp-meta post-stats\">{$post_meta}</span>\n"
: '';
$post_rating = ( ! empty($post_rating) )
? " <span class=\"wpp-rating\">{$post_rating}</span>\n"
: "";
$post =
"<li" . ( ( is_array($wpp_post_class) && ! empty($wpp_post_class) ) ? ' class="' . esc_attr(implode(" ", $wpp_post_class)) . '"' : '') . ">\n"
. $post_thumbnail
. "<a href=\"{$permalink}\" " . ($post_title_attr !== $post_title ? "title=\"{$post_title_attr}\" " : "") . "class=\"wpp-post-title\" target=\"{$this->admin_options['tools']['link']['target']}\">{$post_title}</a>\n"
. $post_excerpt
. $post_meta
. $post_rating
. "</li>\n";
}
return apply_filters('wpp_post', $post, $post_object, $this->public_options);
}
/**
* Return the processed post/page title.
*
* @since 3.0.0
* @access private
* @param object $post_object
* @param integer $post_id
* @return string
*/
private function get_title(\stdClass $post_object, $post_id)
{
if ( $post_object->id != $post_id ) {
$title = get_the_title($post_id);
} else {
$title = $post_object->title;
}
// Run the_title filter so core/plugin title hooks can
// be applied to the post title
$title = apply_filters('the_title', $title, $post_object->id);
return apply_filters('wpp_the_title', $title, $post_object->id, $post_id);
}
/**
* Return the permalink.
*
* @since 4.0.12
* @access private
* @param object $post_object
* @param integer $post_id
* @return string
*/
private function get_permalink(\stdClass $post_object, $post_id) {
if ( $post_object->id != $post_id ) {
return get_permalink($post_id);
}
return get_permalink($post_object->id);
}
/**
* Return the processed thumbnail.
*
* @since 3.0.0
* @access private
* @param int $post_id
* @return string
*/
private function get_thumbnail($post_id)
{
$thumbnail = '';
if ( $this->public_options['thumbnail']['active'] ) {
$thumbnail = $this->thumbnail->get(
$post_id,
[
$this->public_options['thumbnail']['width'],
$this->public_options['thumbnail']['height']
],
$this->admin_options['tools']['thumbnail']['source'],
$this->public_options['thumbnail']['crop'],
$this->public_options['thumbnail']['build']
);
}
return $thumbnail;
}
/**
* Return post excerpt.
*
* @since 3.0.0
* @access private
* @param object $post_object
* @param integer $post_id
* @return string
*/
private function get_excerpt(\stdClass $post_object, $post_id)
{
$excerpt = '';
if ( $this->public_options['post-excerpt']['active'] ) {
if ( $post_object->id != $post_id ) {
$the_post = get_post($post_id);
$excerpt = ( empty($the_post->post_excerpt) )
? $the_post->post_content
: $the_post->post_excerpt;
}
else {
$excerpt = ( empty($post_object->post_excerpt) )
? $post_object->post_content
: $post_object->post_excerpt;
}
// remove caption tags
$excerpt = preg_replace("/\[caption.*\[\/caption\]/", "", $excerpt);
// remove Flash objects
$excerpt = preg_replace("/<object[0-9 a-z_?*=\":\-\/\.#\,\\n\\r\\t]+/smi", "", $excerpt);
// remove iframes
$excerpt = preg_replace("/<iframe.*?\/iframe>/i", "", $excerpt);
// remove WP shortcodes
$excerpt = strip_shortcodes($excerpt);
// remove style/script tags
$excerpt = preg_replace('@<(script|style)[^>]*?>.*?</\\1>@si', '', $excerpt);
// remove HTML tags if requested
if ( $this->public_options['post-excerpt']['keep_format'] ) {
$excerpt = strip_tags($excerpt, '<a><b><i><em><strong>');
} else {
$excerpt = strip_tags($excerpt);
// remove URLs, too
$excerpt = preg_replace('_^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)(?:\.(?:[a-z\x{00a1}-\x{ffff}0-9]+-?)*[a-z\x{00a1}-\x{ffff}0-9]+)*(?:\.(?:[a-z\x{00a1}-\x{ffff}]{2,})))(?::\d{2,5})?(?:/[^\s]*)?$_iuS', '', $excerpt);
}
$excerpt = trim($excerpt);
}
// Balance tags, if needed
if ( '' !== $excerpt ) {
$more = $this->public_options['post-excerpt']['words'] ? ' ' . $this->more : $this->more;
$more = apply_filters('wpp_excerpt_more', $more);
$excerpt = Helper::truncate($excerpt, $this->public_options['post-excerpt']['length'], $this->public_options['post-excerpt']['words'], $more);
if ( $this->public_options['post-excerpt']['keep_format'] )
$excerpt = force_balance_tags($excerpt);
}
return $excerpt;
}
/**
* Return post rating.
*
* @since 3.0.0
* @access private
* @param object $post_object
* @return string
*/
private function get_rating(\stdClass $post_object)
{
$rating = '';
if ( function_exists('the_ratings_results') && $this->public_options['rating'] ) {
$rating = the_ratings_results($post_object->id);
}
return $rating;
}
/**
* Get post date.
*
* @since 3.0.0
* @access private
* @param object $post_object
* @return string
*/
private function get_date(\stdClass $post_object)
{
$date = '';
if ( $this->public_options['stats_tag']['date']['active'] ) {
if ( 'relative' == $this->public_options['stats_tag']['date']['format'] ) {
$date = sprintf(
__('%s ago', 'wordpress-popular-posts'),
human_time_diff(
strtotime($post_object->date),
current_time('timestamp')
)
);
} else {
$date = date_i18n(
( 'wp_date_format' == $this->public_options['stats_tag']['date']['format'] ? $this->wp_date_format : $this->public_options['stats_tag']['date']['format'] ),
strtotime($post_object->date)
);
}
}
return $date;
}
/**
* Get post taxonomies.
*
* @since 3.0.0
* @access private
* @param integer $post_id
* @return string
*/
private function get_taxonomies($post_id)
{
$post_tax = '';
if (
(isset($this->public_options['stats_tag']['category']) && $this->public_options['stats_tag']['category'])
|| $this->public_options['stats_tag']['taxonomy']['active']
) {
$taxonomy = 'category';
if (
$this->public_options['stats_tag']['taxonomy']['active']
&& ! empty($this->public_options['stats_tag']['taxonomy']['name'])
) {
$taxonomy = $this->public_options['stats_tag']['taxonomy']['name'];
}
$terms = wp_get_post_terms($post_id, $taxonomy);
if ( ! is_wp_error($terms) ) {
// Usage: https://wordpress.stackexchange.com/a/46824
if ( has_filter('wpp_post_exclude_terms') ) {
$args = apply_filters('wpp_post_exclude_terms', []);
$terms = wp_list_filter($terms, $args, 'NOT');
}
$terms = apply_filters('wpp_post_terms', $terms);
if (
is_array($terms)
&& ! empty($terms)
) {
$taxonomy_separator = apply_filters('wpp_taxonomy_separator', ', ');
foreach ($terms as $term) {
$term_link = get_term_link($term);
if ( is_wp_error($term_link) )
continue;
$term_link = $this->translate->url($term_link, $this->translate->get_current_language());
$post_tax .= "<a href=\"{$term_link}\" class=\"wpp-taxonomy {$taxonomy} {$taxonomy}-{$term->term_id}\">{$term->name}</a>" . $taxonomy_separator;
}
}
}
if ( '' != $post_tax )
$post_tax = rtrim($post_tax, $taxonomy_separator);
}
return $post_tax;
}
/**
* Get post author.
*
* @since 3.0.0
* @access private
* @param object $post_object
* @param integer $post_id
* @return string
*/
private function get_author(\stdClass $post_object, $post_id)
{
$author = ( $this->public_options['stats_tag']['author'] )
? get_the_author_meta('display_name', $post_object->uid != $post_id ? get_post_field('post_author', $post_id) : $post_object->uid)
: "";
return $author;
}
/**
* Return post views count.
*
* @since 3.0.0
* @access private
* @param object $post_object
* @return int|float
*/
private function get_pageviews(\stdClass $post_object)
{
$pageviews = 0;
if (
(
$this->public_options['order_by'] == "views"
|| $this->public_options['order_by'] == "avg"
|| $this->public_options['stats_tag']['views']
)
&& ( isset($post_object->pageviews) || isset($post_object->avg_views) )
) {
$pageviews = ( $this->public_options['order_by'] == "views" || $this->public_options['order_by'] == "comments" )
? $post_object->pageviews
: $post_object->avg_views;
}
return $pageviews;
}
/**
* Return post comment count.
*
* @since 3.0.0
* @access private
* @param object $post_object
* @return int
*/
private function get_comments(\stdClass $post_object)
{
$comments = ( ( $this->public_options['order_by'] == "comments" || $this->public_options['stats_tag']['comment_count'] ) && isset($post_object->comment_count) )
? $post_object->comment_count
: 0;
return $comments;
}
/**
* Return post metadata.
*
* @since 3.0.0
* @access private
* @param object $post_object
* @param integer $post_id
* @return array
*/
//private function get_metadata(\stdClass $post_object, $post_id)
private function get_metadata(\stdClass $post_object, $post_id, $date, $post_tax, $author, $pageviews, $comments)
{
$stats = [];
$prettify_numbers = apply_filters('wpp_pretiffy_numbers', true);
// comments
if ( $this->public_options['stats_tag']['comment_count'] ) {
$comments_text = sprintf(
_n('%s comment', '%s comments', $comments, 'wordpress-popular-posts'),
$prettify_numbers ? Helper::prettify_number($comments) : number_format_i18n($comments)
);
$stats['comments'] = '<span class="wpp-comments">' . $comments_text . '</span>';
}
// views
if ( $this->public_options['stats_tag']['views'] ) {
if ( $this->public_options['order_by'] == 'avg' ) {
$views_text = sprintf(
_n('%s view per day', '%s views per day', $pageviews, 'wordpress-popular-posts'),
$prettify_numbers ? Helper::prettify_number($pageviews, 2) : number_format_i18n($pageviews, (fmod($pageviews, 1) !== 0.0 ? 2 : 0))
);
}
else {
$views_text = sprintf(
_n('%s view', '%s views', $pageviews, 'wordpress-popular-posts'),
$prettify_numbers ? Helper::prettify_number($pageviews) : number_format_i18n($pageviews)
);
}
$stats['views'] = '<span class="wpp-views">' . $views_text . "</span>";
}
// author
if ( $this->public_options['stats_tag']['author'] ) {
$author_url = get_author_posts_url($post_object->uid != $post_id ? get_post_field('post_author', $post_id) : $post_object->uid);
$display_name = '<a href="' . $this->translate->url($author_url, $this->translate->get_current_language()) . '">' . $author . '</a>';
$stats['author'] = '<span class="wpp-author">' . sprintf(__('by %s', 'wordpress-popular-posts'), $display_name) . '</span>';
}
// date
if ( $this->public_options['stats_tag']['date']['active'] ) {
$stats['date'] = '<span class="wpp-date">' . ( 'relative' == $this->public_options['stats_tag']['date']['format'] ? sprintf(__('posted %s', 'wordpress-popular-posts'), $date) : sprintf(__('posted on %s', 'wordpress-popular-posts'), $date) ) . '</span>';
}
// taxonomy
if ( ($this->public_options['stats_tag']['category'] || $this->public_options['stats_tag']['taxonomy']['active']) && $post_tax != '' ) {
$stats['taxonomy'] = '<span class="wpp-category">' . sprintf(__('under %s', 'wordpress-popular-posts'), $post_tax) . '</span>';
}
return $stats;
}
/**
* Parse content tags.
*
* @since 1.4.6
* @access private
* @param string HTML string with content tags
* @param array Post data
* @param bool Used to display post rating (if functionality is available)
* @return string
*/
private function format_content($string, $data, $rating) {
if ( empty($string) || ( empty($data) || ! is_array($data) ) )
return false;
$params = [];
$pattern = '/\{(pid|current_class|excerpt|summary|meta|stats|title|title_attr|image|thumb|thumb_img|thumb_url|rating|score|url|text_title|author|author_copy|author_name|author_url|taxonomy|taxonomy_copy|category|category_copy|views|views_copy|comments|comments_copy|date|date_copy|total_items|item_position)\}/i';
preg_match_all($pattern, $string, $matches);
array_map('strtolower', $matches[0]);
if ( in_array("{pid}", $matches[0]) ) {
$string = str_replace("{pid}", $data['id'], $string);
}
if ( in_array("{current_class}", $matches[0]) ) {
$string = str_replace("{current_class}", ( $data['is_current_post'] ? 'current' : '' ), $string);
}
if ( in_array("{title}", $matches[0]) ) {
$string = str_replace("{title}", $data['title'], $string);
}
if ( in_array("{title_attr}", $matches[0]) ) {
$string = str_replace("{title_attr}", $data['title_attr'], $string);
}
if ( in_array("{meta}", $matches[0]) || in_array("{stats}", $matches[0]) ) {
$string = str_replace(["{meta}", "{stats}"], $data['stats'], $string);
}
if ( in_array("{excerpt}", $matches[0]) || in_array("{summary}", $matches[0]) ) {
$string = str_replace(["{excerpt}", "{summary}"], $data['summary'], $string);
}
if ( in_array("{image}", $matches[0]) || in_array("{thumb}", $matches[0]) ) {
$string = str_replace(["{image}", "{thumb}"], $data['img'], $string);
}
if ( in_array("{thumb_img}", $matches[0]) ) {
$string = str_replace("{thumb_img}", $data['img_no_link'], $string);
}
if ( in_array("{thumb_url}", $matches[0]) && ! empty($data['img_no_link']) ) {
$dom = new \DOMDocument;
if ( $dom->loadHTML($data['img_no_link']) ) {
$img_tag = $dom->getElementsByTagName('img');
if ( $img_tag->length ) {
foreach( $img_tag as $node ) {
if ( $node->hasAttribute('src') ) {
$src = $node->getAttribute('src');
$string = str_replace("{thumb_url}", $src, $string);
}
}
}
}
}
// WP-PostRatings check
if ( $rating ) {
if ( function_exists('the_ratings_results') && in_array("{rating}", $matches[0]) ) {
$string = str_replace("{rating}", the_ratings_results($data['id']), $string);
}
if ( function_exists('expand_ratings_template') && in_array("{score}", $matches[0]) ) {
$string = str_replace("{score}", expand_ratings_template('%RATINGS_SCORE%', $data['id']), $string);
// removing the redundant plus sign
$string = str_replace('+', '', $string);
}
}
if ( in_array("{url}", $matches[0]) ) {
$string = str_replace("{url}", $data['url'], $string);
}
if ( in_array("{text_title}", $matches[0]) ) {
$string = str_replace("{text_title}", $data['text_title'], $string);
}
if ( in_array("{author}", $matches[0]) ) {
$string = str_replace("{author}", $data['author'], $string);
}
if ( in_array("{author_copy}", $matches[0]) ) {
$string = str_replace("{author_copy}", $data['author_copy'], $string);
}
if ( in_array("{author_name}", $matches[0]) ) {
$string = str_replace("{author_name}", $data['author_name'], $string);
}
if ( in_array("{author_url}", $matches[0]) ) {
$string = str_replace("{author_url}", $data['author_url'], $string);
}
if ( in_array("{taxonomy}", $matches[0]) || in_array("{category}", $matches[0]) ) {
$string = str_replace(["{taxonomy}", "{category}"], $data['taxonomy'], $string);
}
if ( in_array("{taxonomy_copy}", $matches[0]) || in_array("{category_copy}", $matches[0]) ) {
$string = str_replace(["{taxonomy_copy}", "{category_copy}"], $data['taxonomy_copy'], $string);
}
if ( in_array("{views}", $matches[0]) ) {
$string = str_replace("{views}", $data['views'], $string);
}
if ( in_array("{views_copy}", $matches[0]) ) {
$string = str_replace("{views_copy}", $data['views_copy'], $string);
}
if ( in_array("{comments}", $matches[0]) ) {
$string = str_replace("{comments}", $data['comments'], $string);
}
if ( in_array("{comments_copy}", $matches[0]) ) {
$string = str_replace("{comments_copy}", $data['comments_copy'], $string);
}
if ( in_array("{date}", $matches[0]) ) {
$string = str_replace("{date}", $data['date'], $string);
}
if ( in_array("{date_copy}", $matches[0]) ) {
$string = str_replace("{date_copy}", $data['date_copy'], $string);
}
if ( in_array("{total_items}", $matches[0]) ) {
$string = str_replace("{total_items}", $data['total_items'], $string);
}
if ( in_array("{item_position}", $matches[0]) ) {
$string = str_replace("{item_position}", $data['item_position'], $string);
}
return apply_filters("wpp_parse_custom_content_tags", $string, $data['id']);
}
/**
* Checks whether we're currently seeing a single post/page/CPT.
*
* @since 5.0.0
* @return int
*/
public function is_single()
{
return apply_filters('wpp_is_single', Helper::is_single());
}
}

View File

@ -0,0 +1,513 @@
<?php
/**
* Queries the database for popular posts.
*
* To use this class, you must pass it an array of parameters (mostly the same ones used with
* the wpp_get_mostpopular() template tag).
*
* eg.: $popular_posts = new Query(['range' => 'last7days', 'order_by' => 'views', 'limit' => 5]);
*
* @since 4.0.0
* @package WordPressPopularPosts
*/
namespace WordPressPopularPosts;
class Query {
/**
* Database query string.
*
* @since 4.0.0
* @access private
* @var string $query
*/
private $query;
/**
* List of posts.
*
* @since 4.0.0
* @access private
* @var array $posts
*/
private $posts = [];
/**
* Plugin options.
*
* @since 4.0.0
* @access private
* @var array $options
*/
private $options;
/**
* Constructor.
*
* @since 4.0.0
* @param array $options
*/
public function __construct(array $options = [])
{
$this->options = $options;
$this->build_query();
$this->run_query();
}
/**
* Sets class options.
*
* @since 5.0.0
* @param array $options
*/
public function set_options(array $options = [])
{
$this->options = $options;
}
/**
* Builds the database query.
*
* @since 4.0.0
* @access private
*/
private function build_query()
{
/**
* @var wpdb $wpdb
*/
global $wpdb;
if ( isset($wpdb) ) {
$this->options = Helper::merge_array_r(
Settings::get('widget_options'),
(array) $this->options
);
$now = new \DateTime(Helper::now(), new \DateTimeZone(Helper::get_timezone()));
$args = [];
$fields = "p.ID AS id, p.post_title AS title, p.post_author AS uid";
$table = "";
$join = "";
$where = "WHERE 1 = 1";
$groupby = "";
$orderby = "";
$limit = "LIMIT " . (filter_var($this->options['limit'], FILTER_VALIDATE_INT) && $this->options['limit'] > 0 ? $this->options['limit'] : 10) . (isset($this->options['offset']) && filter_var($this->options['offset'], FILTER_VALIDATE_INT) !== false && $this->options['offset'] >= 0 ? " OFFSET {$this->options['offset']}" : "");
// Get post date
if ( isset($this->options['stats_tag']['date']['active']) && $this->options['stats_tag']['date']['active'] ) {
$fields .= ", p.post_date AS date";
}
// Get post excerpt $instance
if ( isset($this->options['post-excerpt']['active']) && $this->options['post-excerpt']['active'] ) {
$fields .= ", p.post_excerpt AS post_excerpt, p.post_content AS post_content";
}
// Get entries from these post types
$post_types = ['post', 'page'];
if ( isset($this->options['post_type']) && ! empty($this->options['post_type']) ) {
$post_types = explode(",", $this->options['post_type']);
$pt = '';
$where .= " AND p.post_type IN(";
foreach( $post_types as $post_type ) {
$pt .= "%s, ";
array_push($args, trim($post_type));
}
$where .= rtrim($pt, ", ") . ")";
}
else {
$where .= " AND p.post_type IN('post', 'page')";
}
// Get entries from these authors
if ( isset($this->options['author']) && ! empty($this->options['author']) ) {
$author_IDs = explode(",", $this->options['author']);
$uid = '';
$where .= " AND p.post_author IN(";
foreach( $author_IDs as $author_ID ) {
$uid .= "%d, ";
array_push($args, trim($author_ID));
}
$where .= rtrim($uid, ", ") . ")";
}
// Get / exclude entries from this taxonomies
if (
( isset($this->options['taxonomy']) && ! empty($this->options['taxonomy']) ) &&
( ( isset($this->options['cat']) && ! empty($this->options['cat']) )
|| ( isset($this->options['term_id']) && ! empty($this->options['term_id']) ) )
) {
if ( isset($this->options['cat']) && ! empty($this->options['cat']) ) {
$this->options['term_id'] = $this->options['cat'];
}
// Let's do some cleanup before attempting the filtering
$this->options['taxonomy'] = trim($this->options['taxonomy'], ' ;');
$this->options['term_id'] = trim($this->options['term_id'], ' ;');
if (
$this->options['taxonomy']
&& $this->options['term_id']
) {
$taxonomies = array_map('trim', explode(';', $this->options['taxonomy']));
$term_IDs_for_taxonomies = array_map('trim', explode(';', $this->options['term_id']));
// Apparently we have at least one taxonomy and matching term ID(s)
if ( count($taxonomies) && count($term_IDs_for_taxonomies) ) {
// Parameters mismatch: we either have too little taxonomies
// or too little term ID groups, let's trim the excess
if ( count($taxonomies) != count($term_IDs_for_taxonomies) ) {
// We have more taxonomies than term ID groups,
// let's remove some taxonomies
if ( count($taxonomies) > count($term_IDs_for_taxonomies) ) {
$taxonomies = array_slice($taxonomies, 0, count($term_IDs_for_taxonomies));
}
// We have more term ID groups than taxonomies,
// let's remove some term ID groups
else {
$term_IDs_for_taxonomies = array_slice($term_IDs_for_taxonomies, 0, count($taxonomies));
}
}
$registered_taxonomies = get_taxonomies(['public' => true]);
foreach ( $taxonomies as $index => $taxonomy ) {
// Invalid taxonomy, discard the taxonomy
if ( ! isset($registered_taxonomies[$taxonomy]) ) {
unset($taxonomies[$index]);
unset($term_IDs_for_taxonomies[$index]);
}
}
// If we still have something we can use
// for filtering, let's use it
if (
! empty($taxonomies)
&& ! empty($term_IDs_for_taxonomies)
) {
$term_IDs = array();
foreach( $term_IDs_for_taxonomies as $term_IDs_for_single_taxonomy ) {
$term_IDs[] = explode(",", $term_IDs_for_single_taxonomy);
}
$in_term_IDs_for_taxonomies = array();
$out_term_IDs_for_taxonomies = array();
foreach( $term_IDs as $term_IDs_for_single_taxonomy ) {
$in_term_IDs_for_taxonomy = [];
$out_term_IDs_for_taxonomy = [];
foreach ( $term_IDs_for_single_taxonomy as $term_ID ) {
if ( $term_ID >= 0 )
$in_term_IDs_for_taxonomy[] = trim($term_ID);
else
$out_term_IDs_for_taxonomy[] = trim($term_ID) * -1;
}
$in_term_IDs_for_taxonomies[] = $in_term_IDs_for_taxonomy;
$out_term_IDs_for_taxonomies[] = $out_term_IDs_for_taxonomy;
}
foreach( $taxonomies as $taxIndex => $taxonomy ) {
$in_term_IDs = $in_term_IDs_for_taxonomies[$taxIndex];
$out_term_IDs = $out_term_IDs_for_taxonomies[$taxIndex];
if ( ! empty($in_term_IDs) ) {
$where .= " AND p.ID IN (
SELECT object_id
FROM `{$wpdb->term_relationships}` AS r
JOIN `{$wpdb->term_taxonomy}` AS x ON x.term_taxonomy_id = r.term_taxonomy_id
WHERE x.taxonomy = %s";
array_push($args, $taxonomy);
$inTID = '';
foreach ($in_term_IDs as $in_term_ID) {
$inTID .= "%d, ";
array_push($args, $in_term_ID);
}
$where .= " AND x.term_id IN(" . rtrim($inTID, ", ") . ") )";
}
if ( ! empty($out_term_IDs) ) {
$post_ids = get_posts(
[
'post_type' => $post_types,
'posts_per_page' => -1,
'tax_query' => [
[
'taxonomy' => $taxonomy,
'field' => 'id',
'terms' => $out_term_IDs,
],
],
'fields' => 'ids'
]
);
if ( is_array($post_ids) && ! empty($post_ids) ) {
if ( isset($this->options['pid']) && ! empty($this->options['pid']) ) {
$this->options['pid'] .= "," . implode(",", $post_ids);
} else {
$this->options['pid'] = implode(",", $post_ids);
}
}
}
}
}
}
}
}
// Exclude these entries from the listing
if ( isset($this->options['pid']) && ! empty($this->options['pid']) ) {
$excluded_post_IDs = explode(",", $this->options['pid']);
$xpid = '';
$where .= " AND p.ID NOT IN(";
foreach( $excluded_post_IDs as $excluded_post_ID ) {
$xpid .= "%d, ";
array_push($args, trim($excluded_post_ID));
}
$where .= rtrim($xpid, ", ") . ")";
}
$table = "`{$wpdb->posts}` p";
// All-time range
if ( "all" == $this->options['range'] ) {
// Order by views count
if ( "comments" != $this->options['order_by'] ) {
$join = "INNER JOIN `{$wpdb->prefix}popularpostsdata` v ON p.ID = v.postid";
// Order by views
if ( "views" == $this->options['order_by'] ) {
if ( ! isset($this->options['stats_tag']['views']) || $this->options['stats_tag']['views'] ) {
$fields .= ", v.pageviews";
}
$orderby = "ORDER BY v.pageviews DESC";
}
// Order by average views
else {
$fields .= ", ( v.pageviews/(IF ( DATEDIFF('{$now->format('Y-m-d')}', MIN(v.day)) > 0, DATEDIFF('{$now->format('Y-m-d')}', MIN(v.day)), 1) ) ) AS avg_views";
$groupby = "GROUP BY v.postid";
$orderby = "ORDER BY avg_views DESC";
}
// Display comments count, too
if ( isset($this->options['stats_tag']['comment_count']) && $this->options['stats_tag']['comment_count'] ) {
$fields .= ", p.comment_count";
}
}
// Order by comments count
else {
$where .= " AND p.comment_count > 0";
$orderby = "ORDER BY p.comment_count DESC";
// Display comment count
if ( isset($this->options['stats_tag']['comment_count']) && $this->options['stats_tag']['comment_count'] ) {
$fields .= ", p.comment_count";
}
// Display views count, too
if ( isset($this->options['stats_tag']['views']) && $this->options['stats_tag']['views'] ) {
$fields .= ", IFNULL(v.pageviews, 0) AS pageviews";
$join = "INNER JOIN `{$wpdb->prefix}popularpostsdata` v ON p.ID = v.postid";
}
}
}
// Custom time range
else {
$start_date = clone $now;
// Determine time range
switch( $this->options['range'] ){
case "last24hours":
case "daily":
$start_date = $start_date->sub(new \DateInterval('P1D'));
$start_datetime = $start_date->format('Y-m-d H:i:s');
$views_time_range = "view_datetime >= '{$start_datetime}'";
break;
case "last7days":
case "weekly":
$start_date = $start_date->sub(new \DateInterval('P6D'));
$start_datetime = $start_date->format('Y-m-d');
$views_time_range = "view_date >= '{$start_datetime}'";
break;
case "last30days":
case "monthly":
$start_date = $start_date->sub(new \DateInterval('P29D'));
$start_datetime = $start_date->format('Y-m-d');
$views_time_range = "view_date >= '{$start_datetime}'";
break;
case "custom":
$time_units = ["MINUTE", "HOUR", "DAY", "WEEK", "MONTH"];
// Valid time unit
if (
isset($this->options['time_unit'])
&& in_array(strtoupper($this->options['time_unit']), $time_units)
&& isset($this->options['time_quantity'])
&& filter_var($this->options['time_quantity'], FILTER_VALIDATE_INT)
&& $this->options['time_quantity'] > 0
) {
$time_quantity = $this->options['time_quantity'];
$time_unit = strtoupper($this->options['time_unit']);
if ( 'MINUTE' == $time_unit ) {
$start_date = $start_date->sub(new \DateInterval('PT' . (60 * $time_quantity) . 'S'));
$start_datetime = $start_date->format('Y-m-d H:i:s');
$views_time_range = "view_datetime >= '{$start_datetime}'";
} elseif ( 'HOUR' == $time_unit ) {
$start_date = $start_date->sub(new \DateInterval('PT' . ((60 * $time_quantity) - 1) . 'M59S'));
$start_datetime = $start_date->format('Y-m-d H:i:s');
$views_time_range = "view_datetime >= '{$start_datetime}'";
} elseif ( 'DAY' == $time_unit ) {
$start_date = $start_date->sub(new \DateInterval('P' . ($time_quantity - 1) . 'D'));
$start_datetime = $start_date->format('Y-m-d');
$views_time_range = "view_date >= '{$start_datetime}'";
} elseif ( 'WEEK' == $time_unit ) {
$start_date = $start_date->sub(new \DateInterval('P' . ((7 * $time_quantity) - 1) . 'D'));
$start_datetime = $start_date->format('Y-m-d');
$views_time_range = "view_date >= '{$start_datetime}'";
} else {
$start_date = $start_date->sub(new \DateInterval('P' . ((30 * $time_quantity) - 1) . 'D'));
$start_datetime = $start_date->format('Y-m-d');
$views_time_range = "view_date >= '{$start_datetime}'";
}
} // Invalid time unit, default to last 24 hours
else {
$start_date = $start_date->sub(new \DateInterval('P1D'));
$start_datetime = $start_date->format('Y-m-d H:i:s');
$views_time_range = "view_datetime >= '{$start_datetime}'";
}
break;
default:
$start_date = $start_date->sub(new \DateInterval('P1D'));
$start_datetime = $start_date->format('Y-m-d H:i:s');
$views_time_range = "view_datetime >= '{$start_datetime}'";
break;
}
// Get entries published within the specified time range
if ( isset($this->options['freshness']) && $this->options['freshness'] ) {
$where .= " AND p.post_date >= '{$start_datetime}'";
}
// Order by views count
if ( "comments" != $this->options['order_by'] ) {
// Order by views
if ( "views" == $this->options['order_by'] ) {
$fields .= ", v.pageviews";
$join = "INNER JOIN (SELECT SUM(pageviews) AS pageviews, postid FROM `{$wpdb->prefix}popularpostssummary` WHERE {$views_time_range} GROUP BY postid) v ON p.ID = v.postid";
$orderby = "ORDER BY pageviews DESC";
}
// Order by average views
else {
$fields .= ", v.avg_views";
$join = "INNER JOIN (SELECT SUM(pageviews)/(IF ( DATEDIFF('{$now->format('Y-m-d H:i:s')}', '{$start_datetime}') > 0, DATEDIFF('{$now->format('Y-m-d H:i:s')}', '{$start_datetime}'), 1) ) AS avg_views, postid FROM `{$wpdb->prefix}popularpostssummary` WHERE {$views_time_range} GROUP BY postid) v ON p.ID = v.postid";
$orderby = "ORDER BY avg_views DESC";
}
// Display comments count, too
if ( isset($this->options['stats_tag']['comment_count']) && $this->options['stats_tag']['comment_count'] ) {
$fields .= ", IFNULL(c.comment_count, 0) AS comment_count";
$join .= " LEFT JOIN (SELECT comment_post_ID, COUNT(comment_post_ID) AS comment_count FROM `{$wpdb->comments}` WHERE comment_date_gmt >= '{$start_datetime}' AND comment_approved = '1' GROUP BY comment_post_ID) c ON p.ID = c.comment_post_ID";
}
}
// Order by comments count
else {
$fields .= ", c.comment_count";
$join = "INNER JOIN (SELECT COUNT(comment_post_ID) AS comment_count, comment_post_ID FROM `{$wpdb->comments}` WHERE comment_date_gmt >= '{$start_datetime}' AND comment_approved = '1' GROUP BY comment_post_ID) c ON p.ID = c.comment_post_ID";
$orderby = "ORDER BY comment_count DESC";
// Display views count, too
if ( isset($this->options['stats_tag']['views']) && $this->options['stats_tag']['views'] ) {
$fields .= ", v.pageviews";
$join .= " INNER JOIN (SELECT SUM(pageviews) AS pageviews, postid FROM `{$wpdb->prefix}popularpostssummary` WHERE {$views_time_range} GROUP BY postid) v ON p.ID = v.postid";
}
}
}
// List only published, non password-protected posts
$where .= " AND p.post_password = '' AND p.post_status = 'publish'";
if ( !empty($args) ) {
$where = $wpdb->prepare($where, $args);
}
$fields = apply_filters('wpp_query_fields', $fields, $this->options);
$table = apply_filters('wpp_query_table', $table, $this->options);
$join = apply_filters('wpp_query_join', $join, $this->options);
$where = apply_filters('wpp_query_where', $where, $this->options);
$groupby = apply_filters('wpp_query_group_by', $groupby, $this->options);
$orderby = apply_filters('wpp_query_order_by', $orderby, $this->options);
$limit = apply_filters('wpp_query_limit', $limit, $this->options);
// Finally, build the query
$query = "SELECT {$fields} FROM {$table} {$join} {$where} {$groupby} {$orderby} {$limit};";
$this->query = $query;
}
}
/**
* Queries the database.
*
* @since 4.0.0
* @access private
*/
private function run_query()
{
/**
* @var wpdb $wpdb
*/
global $wpdb;
if ( isset($wpdb) && !empty($this->query) && !is_wp_error($this->query) ) {
$this->posts = $wpdb->get_results($this->query);
}
}
/**
* Returns the query string.
*
* @since 4.0.0
* @return WP_Error|string Query string on success, WP_Error on failure
*/
public function get_query()
{
return $this->query;
}
/**
* Returns the list of posts.
*
* @since 4.0.0
* @return array
*/
public function get_posts()
{
return $this->posts;
}
}

View File

@ -0,0 +1,96 @@
<?php
namespace WordPressPopularPosts\Rest;
class Controller {
/**
* Posts Endpoint.
*
* @var \WordPressPopularPosts\Rest\PostsEndpoint
* @access private
*/
private $posts_endpoint;
/**
* View Logger Endpoint.
*
* @var \WordPressPopularPosts\Rest\ViewLoggerEndpoint
* @access private
*/
private $view_logger_endpoint;
/**
* View Logger Endpoint.
*
* @var \WordPressPopularPosts\Rest\WidgetEndpoint
* @access private
*/
private $widget_endpoint;
/**
* Themes Endpoint.
*
* @var \WordPressPopularPosts\Rest\ThemesEndpoint
* @access private
*/
private $themes_endpoint;
/**
* Themes Endpoint.
*
* @var \WordPressPopularPosts\Rest\ThumbnailsEndpoint
* @access private
*/
private $thumbnails_endpoint;
/**
* Themes Endpoint.
*
* @var \WordPressPopularPosts\Rest\TaxonomiesEndpoint
* @access private
*/
private $taxonomies_endpoint;
/**
* Initialize class.
*
* @param \WordPressPopularPosts\Rest\PostsEndpoint
* @param \WordPressPopularPosts\Rest\ViewLoggerEndpoint
* @param \WordPressPopularPosts\Rest\WidgetEndpoint
* @param \WordPressPopularPosts\Rest\ThemesEndpoint
* @param \WordPressPopularPosts\Rest\ThumbnailsEndpoint
* @param \WordPressPopularPosts\Rest\TaxonomiesEndpoint
*/
public function __construct(\WordPressPopularPosts\Rest\PostsEndpoint $posts_endpoint, \WordPressPopularPosts\Rest\ViewLoggerEndpoint $view_logger_endpoint, \WordPressPopularPosts\Rest\WidgetEndpoint $widget_endpoint, \WordPressPopularPosts\Rest\ThemesEndpoint $themes_endpoint, \WordPressPopularPosts\Rest\ThumbnailsEndpoint $thumbnails_endpoint, \WordPressPopularPosts\Rest\TaxonomiesEndpoint $taxonomies_endpoint)
{
$this->posts_endpoint = $posts_endpoint;
$this->view_logger_endpoint = $view_logger_endpoint;
$this->widget_endpoint = $widget_endpoint;
$this->themes_endpoint = $themes_endpoint;
$this->thumbnails_endpoint = $thumbnails_endpoint;
$this->taxonomies_endpoint = $taxonomies_endpoint;
}
/**
* WordPress hooks.
*
* @since 5.0.0
*/
public function hooks()
{
add_action('rest_api_init', [$this, 'register_routes']);
}
/**
* Registers REST endpoints.
*/
public function register_routes()
{
$this->posts_endpoint->register();
$this->view_logger_endpoint->register();
$this->widget_endpoint->register();
$this->themes_endpoint->register();
$this->thumbnails_endpoint->register();
$this->taxonomies_endpoint->register();
}
}

View File

@ -0,0 +1,113 @@
<?php
namespace WordPressPopularPosts\Rest;
use WordPressPopularPosts\Query;
abstract class Endpoint extends \WP_REST_Controller {
/**
* Plugin options.
*
* @var array $config
* @access private
*/
protected $config;
/**
* Translate object.
*
* @var \WordPressPopularPosts\Translate $translate
* @access private
*/
protected $translate;
/**
* Initializes class.
*
* @param array
* @param \WordPressPopularPosts\Translate
* @param \WordPressPopularPosts\Output
*/
public function __construct(array $config, \WordPressPopularPosts\Translate $translate)
{
$this->config = $config;
$this->translate = $translate;
}
/**
* Registers the endpoint(s).
*
* @since 5.3.0
*/
abstract public function register();
/**
* Gets Query object from cache if it exists,
* otherwise a new Query object will be
* instantiated and returned.
*
* @since 5.0.3
* @param array
* @return Query
*/
protected function maybe_query(array $params)
{
// Return cached results
if ( $this->config['tools']['cache']['active'] ) {
$key = 'wpp_' . md5(json_encode($params));
$query = \WordPressPopularPosts\Cache::get($key);
if ( false === $query ) {
$query = new Query($params);
$time_value = $this->config['tools']['cache']['interval']['value'];
$time_unit = $this->config['tools']['cache']['interval']['time'];
// No popular posts found, check again in 1 minute
if ( ! $query->get_posts() ) {
$time_value = 1;
$time_unit = 'minute';
}
\WordPressPopularPosts\Cache::set(
$key,
$query,
$time_value,
$time_unit
);
}
} // Get real-time popular posts
else {
$query = new Query($params);
}
return $query;
}
/**
* Sets language/locale.
*
* @since 5.3.0
*/
protected function set_lang($lang)
{
// Multilang support
if ( $lang ) {
$current_locale = get_locale();
$locale = null;
// Polylang support
if ( function_exists('PLL') ) {
$lang_object = PLL()->model->get_language($lang);
$locale = ( $lang_object && isset($lang_object->locale) ) ? $lang_object->locale : null;
}
// Reload locale if needed
if ( $locale && $locale != $current_locale ) {
$this->translate->set_current_language($lang);
unload_textdomain('wordpress-popular-posts');
load_textdomain('wordpress-popular-posts', WP_LANG_DIR . '/plugins/wordpress-popular-posts-' . $locale . '.mo');
}
}
}
}

View File

@ -0,0 +1,193 @@
<?php
namespace WordPressPopularPosts\Rest;
class PostsEndpoint extends Endpoint {
/**
* Registers the endpoint(s).
*
* @since 5.3.0
*/
public function register()
{
$version = '1';
$namespace = 'wordpress-popular-posts/v' . $version;
register_rest_route($namespace, '/popular-posts', [
[
'methods' => \WP_REST_Server::READABLE,
'callback' => [$this, 'get_items'],
'permission_callback' => '__return_true',
'args' => $this->get_collection_params()
]
]);
}
/**
* Gets popular posts.
*
* @since 5.3.0
* @param \WP_REST_Request $request Full data about the request.
* @return \WP_REST_Response
*/
public function get_items($request)
{
$params = $request->get_params();
$lang = isset($params['lang']) ? $params['lang'] : null;
$popular_posts = [];
// Multilang support
$this->set_lang($lang);
$query = $this->maybe_query($params);
$results = $query->get_posts();
if ( is_array($results) && ! empty($results) ) {
foreach( $results as $popular_post ) {
$popular_posts[] = $this->prepare_item($popular_post, $request);
}
}
return new \WP_REST_Response($popular_posts, 200);
}
/**
* Retrieves the popular post's WP_Post object and formats it for the REST response.
*
* @since 4.1.0
*
* @param object $popular_post The popular post object.
* @param \WP_REST_Request $request Full details about the request.
* @return array|mixed The formatted WP_Post object.
*/
private function prepare_item($popular_post, $request)
{
if ( $request->get_param('lang') ) {
$post_ID = $this->translate->get_object_id(
$popular_post->id,
get_post_type($popular_post->id)
);
} else {
$post_ID = $popular_post->id;
}
$wp_post = get_post($post_ID);
// Borrow prepare_item_for_response method from WP_REST_Posts_Controller.
$posts_controller = new \WP_REST_Posts_Controller($wp_post->post_type, $request);
$data = $posts_controller->prepare_item_for_response($wp_post, $request);
// Add pageviews from popular_post object to response.
$data->data['pageviews'] = $popular_post->pageviews;
return $this->prepare_response_for_collection($data);
}
/**
* Retrieves the query params for the collections.
*
* @since 4.1.0
*
* @return array Query parameters for the collection.
*/
public function get_collection_params()
{
return [
'post_type' => [
'description' => __('Return popular posts from specified custom post type(s).'),
'type' => 'string',
'default' => 'post',
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => 'rest_validate_request_arg',
],
'limit' => [
'description' => __('The maximum number of popular posts to return.'),
'type' => 'integer',
'default' => 10,
'sanitize_callback' => 'absint',
'validate_callback' => 'rest_validate_request_arg',
'minimum' => 1,
],
'freshness' => [
'description' => __('Retrieve the most popular entries published within the specified time range.'),
'type' => 'string',
'enum' => ['0', '1'],
'default' => '0',
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => 'rest_validate_request_arg',
],
'offset' => [
'description' => __('An offset point for the collection.'),
'type' => 'integer',
'default' => 0,
'minimum' => 0,
'sanitize_callback' => 'absint',
'validate_callback' => 'rest_validate_request_arg',
],
'order_by' => [
'description' => __('Set the sorting option of the popular posts.'),
'type' => 'string',
'enum' => ['views', 'comments'],
'default' => 'views',
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => 'rest_validate_request_arg',
],
'range' => [
'description' => __('Return popular posts from a specified time range.'),
'type' => 'string',
'enum' => ['last24hours', 'last7days', 'last30days', 'all', 'custom'],
'default' => 'last24hours',
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => 'rest_validate_request_arg',
],
'time_unit' => [
'description' => __('Specifies the time unit of the custom time range.'),
'type' => 'string',
'enum' => ['minute', 'hour', 'day', 'week', 'month'],
'default' => 'hour',
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => 'rest_validate_request_arg',
],
'time_quantity' => [
'description' => __('Specifies the number of time units of the custom time range.'),
'type' => 'integer',
'default' => 24,
'minimum' => 1,
'sanitize_callback' => 'absint',
'validate_callback' => 'rest_validate_request_arg',
],
'pid' => [
'description' => __('Post IDs to exclude from the listing.'),
'type' => 'string',
'sanitize_callback' => function($pid) {
return rtrim(preg_replace('|[^0-9,]|', '', $pid), ',');
},
'validate_callback' => 'rest_validate_request_arg',
],
'taxonomy' => [
'description' => __('Include posts in a specified taxonomy.'),
'type' => 'string',
'sanitize_callback' => function($taxonomy) {
return empty($taxonomy) ? 'category' : $taxonomy;
},
'validate_callback' => 'rest_validate_request_arg',
],
'term_id' => [
'description' => __('Taxonomy IDs, separated by comma (prefix a minus sign to exclude).'),
'type' => 'string',
'sanitize_callback' => function($term_id) {
return rtrim(preg_replace('|[^0-9,;-]|', '', $term_id), ',');
},
'validate_callback' => 'rest_validate_request_arg',
],
'author' => [
'description' => __('Include popular posts from author ID(s).'),
'type' => 'string',
'sanitize_callback' => function($author) {
return rtrim(preg_replace('|[^0-9,]|', '', $author), ',');
},
'validate_callback' => 'rest_validate_request_arg',
],
];
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace WordPressPopularPosts\Rest;
class TaxonomiesEndpoint extends Endpoint {
/**
* Registers the endpoint(s).
*
* @since 5.4.0
*/
public function register()
{
$version = '1';
$namespace = 'wordpress-popular-posts/v' . $version;
register_rest_route($namespace, '/taxonomies', [
[
'methods' => \WP_REST_Server::READABLE,
'callback' => [$this, 'get_items'],
'permission_callback' => function() {
return current_user_can('edit_posts');
}
]
]);
}
/**
* Gets popular posts.
*
* @since 5.4.0
* @param \WP_REST_Request $request Full data about the request.
* @return \WP_REST_Response
*/
public function get_items($request)
{
$taxonomies = get_taxonomies(['public' => true], 'objects');
return new \WP_REST_Response($taxonomies, 200);
}
}

View File

@ -0,0 +1,63 @@
<?php
namespace WordPressPopularPosts\Rest;
class ThemesEndpoint extends Endpoint {
/**
* Themer object.
*
* @var \WordPressPopularPosts\Themer $themer
* @access private
*/
private $themer;
/**
* Initializes class.
*
* @param array
* @param \WordPressPopularPosts\Translate
* @param \WordPressPopularPosts\Themer
*/
public function __construct(array $config, \WordPressPopularPosts\Translate $translate, \WordPressPopularPosts\Themer $themer)
{
$this->config = $config;
$this->translate = $translate;
$this->themer = $themer;
}
/**
* Registers the endpoint(s).
*
* @since 5.4.0
*/
public function register()
{
$version = '1';
$namespace = 'wordpress-popular-posts/v' . $version;
register_rest_route($namespace, '/themes', [
[
'methods' => \WP_REST_Server::READABLE,
'callback' => [$this, 'get_items'],
'permission_callback' => function() {
return current_user_can('edit_posts');
}
]
]);
}
/**
* Gets popular posts.
*
* @since 5.4.0
* @param \WP_REST_Request $request Full data about the request.
* @return \WP_REST_Response
*/
public function get_items($request)
{
$registered_themes = $this->themer->get_themes();
ksort($registered_themes);
return new \WP_REST_Response($registered_themes, 200);
}
}

View File

@ -0,0 +1,58 @@
<?php
namespace WordPressPopularPosts\Rest;
class ThumbnailsEndpoint extends Endpoint {
/**
* Registers the endpoint(s).
*
* @since 5.4.0
*/
public function register()
{
$version = '1';
$namespace = 'wordpress-popular-posts/v' . $version;
register_rest_route($namespace, '/thumbnails', [
[
'methods' => \WP_REST_Server::READABLE,
'callback' => [$this, 'get_items'],
'permission_callback' => function() {
return current_user_can('edit_posts');
}
]
]);
}
/**
* Gets popular posts.
*
* @since 5.4.0
* @param \WP_REST_Request $request Full data about the request.
* @return \WP_REST_Response
*/
public function get_items($request)
{
global $_wp_additional_image_sizes;
$available_sizes = [];
$get_intermediate_image_sizes = get_intermediate_image_sizes();
// Create the full array with sizes and crop info
foreach( $get_intermediate_image_sizes as $_size ) {
if ( in_array($_size, ['thumbnail', 'medium', 'large']) ) {
$available_sizes[$_size]['width'] = get_option($_size . '_size_w');
$available_sizes[$_size]['height'] = get_option($_size . '_size_h');
$available_sizes[$_size]['crop'] = (bool) get_option($_size . '_crop');
} elseif ( isset($_wp_additional_image_sizes[$_size]) ) {
$available_sizes[$_size] = [
'width' => $_wp_additional_image_sizes[$_size]['width'],
'height' => $_wp_additional_image_sizes[$_size]['height'],
'crop' => $_wp_additional_image_sizes[$_size]['crop']
];
}
}
return new \WP_REST_Response($available_sizes, 200);
}
}

View File

@ -0,0 +1,242 @@
<?php
namespace WordPressPopularPosts\Rest;
use WordPressPopularPosts\Helper;
class ViewLoggerEndpoint extends Endpoint {
/**
* Registers the endpoint(s).
*
* @since 5.3.0
*/
public function register()
{
$version = '1';
$namespace = 'wordpress-popular-posts/v' . $version;
register_rest_route($namespace, '/popular-posts', [
[
'methods' => \WP_REST_Server::CREATABLE,
'callback' => [$this, 'update_views_count'],
'permission_callback' => '__return_true',
'args' => $this->get_tracking_params(),
]
]);
}
/**
* Updates the views count of a post / page.
*
* @since 4.1.0
*
* @param \WP_REST_Request $request Full details about the request.
* @return string
*/
public function update_views_count($request){
global $wpdb;
$post_ID = $request->get_param('wpp_id');
$sampling = $request->get_param('sampling');
$sampling_rate = $request->get_param('sampling_rate');
$table = $wpdb->prefix . "popularposts";
$wpdb->show_errors();
// Get translated object ID
$post_ID = $this->translate->get_object_id(
$post_ID,
get_post_type($post_ID),
true,
$this->translate->get_default_language()
);
$now = Helper::now();
$curdate = Helper::curdate();
$views = ($sampling)
? $sampling_rate
: 1;
// Allow WP themers / coders perform an action
// before updating views count
if ( has_action('wpp_pre_update_views') )
do_action('wpp_pre_update_views', $post_ID, $views);
$result1 = $result2 = false;
$exec_time = 0;
$start = Helper::microtime_float();
// Store views data in persistent object cache
if (
wp_using_ext_object_cache()
&& defined('WPP_CACHE_VIEWS')
&& WPP_CACHE_VIEWS
) {
$now_datetime = new \DateTime($now, new \DateTimeZone(Helper::get_timezone()));
$timestamp = $now_datetime->getTimestamp();
$date_time = $now_datetime->format('Y-m-d H:i');
$date_time_with_seconds = $now_datetime->format('Y-m-d H:i:s');
$high_accuracy = false;
$key = $high_accuracy ? $timestamp : $date_time;
if ( ! $wpp_cache = wp_cache_get('_wpp_cache', 'transient') ) {
$wpp_cache = [
'last_updated' => $date_time_with_seconds,
'data' => [
$post_ID => [
$key => 1
]
]
];
} else {
if ( ! isset($wpp_cache['data'][$post_ID][$key]) ) {
$wpp_cache['data'][$post_ID][$key] = 1;
} else {
$wpp_cache['data'][$post_ID][$key] += 1;
}
}
// Update cache
wp_cache_set('_wpp_cache', $wpp_cache, 'transient', 0);
// How long has it been since the last time we saved to the database?
$last_update = $now_datetime->diff(new \DateTime($wpp_cache['last_updated'], new \DateTimeZone(Helper::get_timezone())));
$diff_in_minutes = $last_update->days * 24 * 60;
$diff_in_minutes += $last_update->h * 60;
$diff_in_minutes += $last_update->i;
// It's been more than 2 minutes, save everything to DB
if ( $diff_in_minutes > 2 ) {
$query_data = "INSERT INTO {$table}data (`postid`,`day`,`last_viewed`,`pageviews`) VALUES ";
$query_summary = "INSERT INTO {$table}summary (`postid`,`pageviews`,`view_date`,`view_datetime`) VALUES ";
foreach( $wpp_cache['data'] as $pid => $data ) {
$views_count = 0;
foreach( $data as $ts => $cached_views ){
$views_count += $cached_views;
$ts = Helper::is_timestamp($ts) ? $ts : strtotime($ts);
$query_summary .= $wpdb->prepare("(%d,%d,%s,%s),", [
$pid,
$cached_views,
date("Y-m-d", $ts),
date("Y-m-d H:i:s", $ts)
]);
}
$query_data .= $wpdb->prepare( "(%d,%s,%s,%s),", [
$pid,
$date_time_with_seconds,
$date_time_with_seconds,
$views_count
]);
}
$query_data = rtrim($query_data, ",") . " ON DUPLICATE KEY UPDATE pageviews=pageviews+VALUES(pageviews),last_viewed=VALUES(last_viewed);";
$query_summary = rtrim($query_summary, ",") . ";";
// Clear cache
$wpp_cache['last_updated'] = $date_time_with_seconds;
$wpp_cache['data'] = [];
wp_cache_set('_wpp_cache', $wpp_cache, 'transient', 0);
// Save
$result1 = $wpdb->query($query_data);
$result2 = $wpdb->query($query_summary);
}
else {
$result1 = $result2 = true;
}
} // Live update to the DB
else {
// Update all-time table
$result1 = $wpdb->query($wpdb->prepare(
"INSERT INTO {$table}data
(postid, day, last_viewed, pageviews) VALUES (%d, %s, %s, %d)
ON DUPLICATE KEY UPDATE pageviews = pageviews + %d, last_viewed = %s;",
$post_ID,
$now,
$now,
$views,
$views,
$now
));
// Update range (summary) table
$result2 = $wpdb->query($wpdb->prepare(
"INSERT INTO {$table}summary
(postid, pageviews, view_date, view_datetime) VALUES (%d, %d, %s, %s)
ON DUPLICATE KEY UPDATE pageviews = pageviews + %d, view_datetime = %s;",
$post_ID,
$views,
$curdate,
$now,
$views,
$now
));
}
$end = Helper::microtime_float();
$exec_time += round($end - $start, 6);
$response = ['results' => ''];
if ( ! $result1 || ! $result2 ) {
$response['results'] = 'WPP: failed to update views count!';
return new \WP_REST_Response($response, 500);
}
// Allow WP themers / coders perform an action
// after updating views count
if ( has_action('wpp_post_update_views') )
do_action('wpp_post_update_views', $post_ID);
$response['results'] = "WPP: OK. Execution time: " . $exec_time . " seconds";
return new \WP_REST_Response($response, 201);
}
/**
* Retrieves the query params for tracking views count.
*
* @since 4.1.0
*
* @return array Query parameters for tracking views count.
*/
public function get_tracking_params()
{
return [
'token' => [
'description' => __('Security nonce.'),
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => 'rest_validate_request_arg',
],
'wpp_id' => [
'description' => __('The post / page ID.'),
'type' => 'integer',
'default' => 0,
'sanitize_callback' => 'absint',
'validate_callback' => 'rest_validate_request_arg',
],
'sampling' => [
'description' => __('Enables Data Sampling.'),
'type' => 'integer',
'default' => 0,
'sanitize_callback' => 'absint',
'validate_callback' => 'rest_validate_request_arg',
],
'sampling_rate' => [
'description' => __('Sets the Sampling Rate.'),
'type' => 'integer',
'default' => 100,
'sanitize_callback' => 'absint',
'validate_callback' => 'rest_validate_request_arg',
]
];
}
}

View File

@ -0,0 +1,183 @@
<?php
namespace WordPressPopularPosts\Rest;
class WidgetEndpoint extends Endpoint {
/**
* Output object.
*
* @var \WordPressPopularPosts\Output $output
* @access private
*/
protected $output;
/**
* Initializes class.
*
* @param array
* @param \WordPressPopularPosts\Translate
* @param \WordPressPopularPosts\Output
*/
public function __construct(array $config, \WordPressPopularPosts\Translate $translate, \WordPressPopularPosts\Output $output)
{
$this->config = $config;
$this->translate = $translate;
$this->output = $output;
}
/**
* Registers the endpoint(s).
*
* @since 5.3.0
*/
public function register()
{
$version = '1';
$namespace = 'wordpress-popular-posts/v' . $version;
register_rest_route($namespace, '/popular-posts/widget/(?P<id>[\d]+)', [
[
'methods' => \WP_REST_Server::READABLE,
'callback' => [$this, 'get_widget'],
'permission_callback' => '__return_true',
'args' => $this->get_widget_params(),
]
]);
$version = '2';
$namespace = 'wordpress-popular-posts/v' . $version;
register_rest_route($namespace, '/widget/', [
[
'methods' => 'POST',
'callback' => [$this, 'get_widget_block'],
'permission_callback' => '__return_true',
'args' => $this->get_widget_params(),
]
]);
}
/**
* Retrieves a popular posts widget for display.
*
* @since 4.1.0
*
* @param \WP_REST_Request $request Full details about the request.
* @return \WP_Error|\WP_REST_Response Response object on success, or WP_Error object on failure.
*/
public function get_widget($request)
{
$instance_id = $request->get_param('id');
$is_single = $request->get_param('is_single');
$lang = $request->get_param('lang');
$widget = get_option('widget_wpp');
if ( $data = $this->prepare_widget_item_for_response($instance_id, $is_single, $lang, $widget, $request) )
return new \WP_REST_Response($data, 200);
return new \WP_Error('invalid_instance', __('Invalid Widget Instance ID', 'wordpress-popular-posts'));
}
/**
* Prepares widget instance for response.
*
* @since 5.0.0
* @param int
* @param int
* @param string
* @param array
* @param \WP_REST_Request
* @return array|boolean
*/
public function prepare_widget_item_for_response($instance_id, $is_single, $lang, $widget, $request)
{
// Valid instance
if ( $widget && isset($widget[$instance_id]) ) {
$instance = $widget[$instance_id];
// Expose widget ID for customization
if ( ! isset($instance['widget_id']) )
$instance['widget_id'] = 'wpp-' . $instance_id;
// Multilang support
$this->set_lang($lang);
$popular_posts = $this->maybe_query($instance);
if ( is_numeric($is_single) && $is_single > 0 ) {
add_filter('wpp_is_single', function($id) use ($is_single) {
return $is_single;
});
}
$this->output->set_data($popular_posts->get_posts());
$this->output->set_public_options($instance);
$this->output->build_output();
return [
'widget' => ( $this->config['tools']['cache']['active'] ? '<!-- cached -->' : '' ) . $this->output->get_output()
];
}
return false;
}
/**
* Retrieves a popular posts widget for display.
*
* @since 5.4.0
*
* @param \WP_REST_Request $request Full details about the request.
* @return \WP_Error|\WP_REST_Response Response object on success, or WP_Error object on failure.
*/
public function get_widget_block($request)
{
$instance = $request->get_params();
$is_single = $request->get_param('is_single');
$lang = $request->get_param('lang');
// Multilang support
$this->set_lang($lang);
$popular_posts = $this->maybe_query($instance);
if ( is_numeric($is_single) && $is_single > 0 ) {
add_filter('wpp_is_single', function($id) use ($is_single) {
return $is_single;
});
}
$this->output->set_data($popular_posts->get_posts());
$this->output->set_public_options($instance);
$this->output->build_output();
return [
'widget' => ( $this->config['tools']['cache']['active'] ? '<!-- cached -->' : '' ) . $this->output->get_output()
];
}
/**
* Retrieves the query params for getting a widget instance.
*
* @since 4.1.0
*
* @return array Query parameters for getting a widget instance.
*/
public function get_widget_params()
{
return [
'is_single' => [
'type' => 'integer',
'default' => null,
'sanitize_callback' => 'absint'
],
'lang' => [
'type' => 'string',
'default' => null,
'sanitize_callback' => 'sanitize_text_field'
],
];
}
}

View File

@ -0,0 +1,167 @@
<?php
/**
* Set / get plugin default options.
*
* @link http://cabrerahector.com
* @since 4.0.0
*
* @package WordPressPopularPosts
*/
/**
* Set / get plugin default options.
*
* @package WordPressPopularPosts
* @author Hector Cabrera <me@cabrerahector.com>
*/
namespace WordPressPopularPosts;
class Settings {
/**
* Plugin uploads directory.
*
* @since 3.0.4
* @var array
*/
private static $defaults = [
'widget_options' => [
'title' => '',
'limit' => 10,
'offset' => 0,
'range' => 'daily',
'time_unit' => 'hour',
'time_quantity' => 24,
'freshness' => false,
'order_by' => 'views',
'post_type' => 'post',
'pid' => '',
'author' => '',
'cat' => '',
'taxonomy' => 'category',
'term_id' => '',
'shorten_title' => [
'active' => false,
'length' => 25,
'words' => false
],
'post-excerpt' => [
'active' => false,
'length' => 55,
'keep_format' => false,
'words' => false
],
'thumbnail' => [
'active' => false,
'build' => 'manual',
'width' => 75,
'height' => 75,
'crop' => true
],
'rating' => false,
'stats_tag' => [
'comment_count' => false,
'views' => true,
'author' => false,
'date' => [
'active' => false,
'format' => 'F j, Y'
],
'category' => false,
'taxonomy' => [
'active' => false,
'name' => 'category'
]
],
'markup' => [
'custom_html' => false,
'title-start' => '<h2>',
'title-end' => '</h2>',
'wpp-start' => '<ul class="wpp-list">',
'wpp-end' => '</ul>',
'post-html' => '<li>{thumb} {title} <span class="wpp-meta post-stats">{stats}</span></li>'
],
'theme' => [
'name' => '',
'applied' => false
]
],
'admin_options' => [
'stats' => [
'range' => 'last7days',
'time_unit' => 'hour',
'time_quantity' => 24,
'order_by' => 'views',
'limit' => 10,
'post_type' => 'post,page',
'freshness' => false
],
'tools' => [
'experimental' => false,
'ajax' => true,
'css' => true,
'link' => [
'target' => '_self'
],
'thumbnail' => [
'source' => 'featured',
'field' => '',
'resize' => false,
'default' => '',
'lazyload' => true
],
'log' => [
'level' => 1,
'limit' => 0,
'expires_after' => 180
],
'cache' => [
'active' => true,
'interval' => [
'time' => 'minute',
'value' => 1
]
],
'sampling' => [
'active' => false,
'rate' => 100
]
]
]
];
/**
* Returns plugin options.
*
* @since 4.0.0
* @access public
* @param string $option_set
* @return array
*/
public static function get($option_set = null)
{
$options = self::$defaults;
if ( 'widget_options' == $option_set ) {
return $options['widget_options'];
}
if ( ! $admin_options = get_option('wpp_settings_config') ) {
$admin_options = $options['admin_options'];
add_option('wpp_settings_config', $admin_options);
}
else {
$options['admin_options'] = Helper::merge_array_r(
$options['admin_options'],
(array) $admin_options
);
}
if ( 'admin_options' == $option_set ) {
return $options['admin_options'];
}
return $options;
}
}

View File

@ -0,0 +1,145 @@
<?php
/**
* Class that loads WPP's themes.
*
* @package WordPressPopularPosts
* @author Hector Cabrera <me@cabrerahector.com>
*/
namespace WordPressPopularPosts;
class Themer {
/**
* Path to themes files.
*
* @var string $config
* @access private
*/
private $path;
/**
* Registered themes.
*
* @var array $config
* @access private
*/
private $themes;
/**
* Construct function.
*
* @since 5.0.0
*/
public function __construct()
{
$this->themes = [];
$this->path = plugin_dir_path(dirname(__FILE__)) . 'assets/themes';
$this->hooks();
}
/**
* Themer's hooks.
*
* @since 5.0.0
*/
public function hooks()
{
add_action('after_setup_theme', [$this, 'read']);
}
/**
* Loads information about existing themes.
*
* @since 5.0.0
*/
public function read()
{
$directories = new \DirectoryIterator($this->path);
foreach( $directories as $fileinfo ) {
if ( $fileinfo->isDot() || $fileinfo->isFile() )
continue;
$this->load_theme($fileinfo->getPathName());
}
if ( has_filter('wpp_additional_themes') ) {
$additional_themes = apply_filters('wpp_additional_themes', []);
if ( is_array($additional_themes) && ! empty($additional_themes) ) {
foreach( $additional_themes as $additional_theme ) {
$this->load_theme($additional_theme);
}
}
}
}
/**
* Reads and loads theme into the class.
*
* @since 5.0.0
* @param string $path Path to theme folder
*/
private function load_theme($path)
{
$theme_folder = is_string($path) && is_dir($path) && is_readable($path) ? basename($path) : null;
$theme_folder = $theme_folder ? preg_replace("/[^a-z0-9\_\-\.]/i", '', $theme_folder) : null;
$theme_path = $theme_folder ? $path : null;
if (
$theme_path
&& '.' != $theme_folder
&& '..' != $theme_folder
&& false === strpos($theme_path, '..')
&& ! isset($this->themes[$theme_folder])
&& file_exists($theme_path . '/config.json')
&& file_exists($theme_path . '/style.css')
) {
$str = file_get_contents($theme_path . '/config.json');
$json = json_decode($str, true);
if ( $this->is_valid_config($json) ) {
$this->themes[$theme_folder] = [
'json' => $json,
'path' => $theme_path
];
}
}
}
/**
* Returns an array of available themes.
*
* @since 5.0.0
* @return array
*/
public function get_themes()
{
return $this->themes;
}
/**
* Returns data of a specific theme (if found).
*
* @since 5.0.0
* @param string $theme
* @return array|bool
*/
public function get_theme($theme)
{
return isset($this->themes[$theme]) ? $this->themes[$theme] : false;
}
/**
* Checks whether a $json array is a valid theme config.
*
* @since 5.0.0
* @param array
* @return bool
*/
public function is_valid_config($json = [])
{
return is_array($json) && ! empty($json) && isset($json['name']) && isset($json['config']) && is_array($json['config']);
}
}

View File

@ -0,0 +1,157 @@
<?php
/**
* Obtains translation data from objects.
*
* @link http://cabrerahector.com
* @since 4.0.0
*
* @package WordPressPopularPosts
*/
namespace WordPressPopularPosts;
class Translate {
/**
* Default language code.
*
* @since 4.0.0
* @access private
* @var string
*/
private $default_language;
/**
* Current language code.
*
* @since 4.0.0
* @access private
* @var string
*/
private $current_language;
/**
* Initialize the collections used to maintain the actions and filters.
*
* @since 4.0.0
*/
public function __construct()
{
//
}
/**
* Retrieves the code of the default language.
*
* @since 4.0.0
* @return string|null
*/
public function get_default_language()
{
if ( ! $this->default_language )
$this->default_language = ( function_exists('pll_default_language') ) ? pll_default_language() : apply_filters('wpml_default_language', NULL);
return $this->default_language;
}
/**
* Retrieves the code of the currently active language.
*
* @since 4.0.0
* @return string|null
*/
public function get_current_language()
{
if ( ! $this->current_language )
$this->current_language = ( function_exists('pll_current_language') ) ? pll_current_language() : apply_filters('wpml_current_language', NULL);
return $this->current_language;
}
/**
* Sets the code of the currently active language.
*
* @since 4.0.0
* @return string|null
*/
public function set_current_language($code = null)
{
$this->current_language = $code;
}
/**
* Gets language locale.
*
* @since 5.0.0
* @param string $lang Language code (eg. 'es')
* @return string|bool
*/
public function get_locale($lang = null)
{
// Polylang support
if ( function_exists('PLL') ) {
$lang_object = PLL()->model->get_language($lang);
if ( $lang_object && isset($lang_object->locale) )
return $lang_object->locale;
} else {
// WPML support
global $sitepress;
if ( is_object($sitepress) && method_exists($sitepress, 'get_locale_from_language_code') ) {
return $sitepress->get_locale_from_language_code($lang);
}
}
return false;
}
/**
* Retrieves the ID of an object.
*
* @since 4.0.0
* @param integer $object_id
* @param string $object_type
* @param boolean $return_original_if_missing
* @param string $lang_code
* @return integer
*/
public function get_object_id($object_id = null, $object_type = 'post', $return_original_if_missing = true, $lang_code = null)
{
return apply_filters(
'wpml_object_id',
$object_id,
$object_type,
$return_original_if_missing,
null == $lang_code ? $this->get_current_language() : $lang_code
);
}
/**
* Translates URL.
*
* @since 5.0.0
* @param string $original_permalink
* @param string $lang
* @return string
*/
public function url($original_permalink, $lang)
{
return apply_filters('wpml_permalink', $original_permalink, $lang);
}
/*
* Retrieves the language code of an object.
*
* @since 4.0.0
* @param integer $object_id
* @param string $object_type
* @return string|null
*/
public function get_object_lang_code($object_id = null, $object_type = 'post')
{
return apply_filters(
'wpml_element_language_code',
null,
[
'element_id' => $object_id,
'element_type' => $object_type
]
);
}
}

View File

@ -0,0 +1,462 @@
<?php
namespace WordPressPopularPosts\Widget;
use WordPressPopularPosts\Helper;
use WordPressPopularPosts\Query;
class Widget extends \WP_Widget {
/**
* Default options.
*
* @since 5.0.0
* @var array
*/
private $defaults = [];
/**
* Administrative settings.
*
* @since 2.3.3
* @var array
*/
private $admin_options = [];
/**
* Image object.
*
* @since 5.0.0
* @var WordPressPopularPosts\Image
*/
private $thumbnail;
/**
* Output object.
*
* @var \WordPressPopularPosts\Output
* @access private
*/
private $output;
/**
* Translate object.
*
* @var \WordPressPopularPosts\Translate $translate
* @access private
*/
private $translate;
/**
* Themer object.
*
* @var \WordPressPopularPosts\Themer $themer
* @access private
*/
private $themer;
/**
* Construct.
*
* @since 1.0.0
* @param array $options
* @param array $config
* @param \WordPressPopularPosts\Output $output
* @param \WordPressPopularPosts\Image $image
* @param \WordPressPopularPosts\Translate $translate
* @param \WordPressPopularPosts\Themer $themer
*/
public function __construct(array $options, array $config, \WordPressPopularPosts\Output $output, \WordPressPopularPosts\Image $thumbnail, \WordPressPopularPosts\Translate $translate, \WordPressPopularPosts\Themer $themer)
{
// Create the widget
parent::__construct(
'wpp',
'WordPress Popular Posts',
[
'classname' => 'popular-posts',
'description' => __('The most Popular Posts on your blog.', 'wordpress-popular-posts')
]
);
$this->defaults = $options;
$this->admin_options = $config;
$this->output = $output;
$this->thumbnail = $thumbnail;
$this->translate = $translate;
$this->themer = $themer;
}
/**
* Widget hooks.
*
* @since 5.0.0
*/
public function hooks()
{
// Register the widget
add_action('widgets_init', [$this, 'register']);
// Remove widget from Legacy Widget block
add_filter('widget_types_to_hide_from_legacy_widget_block', [$this, 'remove_from_legacy_widget_block']);
}
/**
* Registers the widget.
*
* @since 5.0.0
*/
public function register()
{
register_widget($this);
}
/**
* Outputs the content of the widget.
*
* @since 1.0.0
* @param array $args The array of form elements.
* @param array $instance The current instance of the widget.
*/
public function widget($args, $instance)
{
/**
* @var string $name
* @var string $id
* @var string $description
* @var string $class
* @var string $before_widget
* @var string $after_widget
* @var string $before_title
* @var string $after_title
* @var string $widget_id
* @var string $widget_name
*/
extract($args, EXTR_SKIP);
$instance = Helper::merge_array_r(
$this->defaults,
(array) $instance
);
echo "\n" . $before_widget . "\n";
// Has user set a title?
if ( '' != $instance['title'] ) {
$title = apply_filters('widget_title', $instance['title'], $instance, $this->id_base);
if (
$instance['markup']['custom_html']
&& $instance['markup']['title-start'] != ""
&& $instance['markup']['title-end'] != ""
) {
echo htmlspecialchars_decode($instance['markup']['title-start'], ENT_QUOTES) . $title . htmlspecialchars_decode($instance['markup']['title-end'], ENT_QUOTES);
} else {
echo $before_title . $title . $after_title;
}
}
// Expose Widget ID & base for customization
$instance['widget_id'] = $widget_id;
$instance['id_base'] = $this->id_base;
// Get posts
if ( $this->admin_options['tools']['ajax'] && ! is_customize_preview() ) {
?>
<div class="wpp-widget-placeholder" data-widget-id="<?php echo esc_attr($widget_id); ?>"></div>
<?php
} else {
$this->get_popular($instance);
}
echo "\n" . $after_widget . "\n";
}
/**
* Generates the administration form for the widget.
*
* @since 1.0.0
* @param array $instance The array of keys and values for the widget.
*/
public function form($instance)
{
$instance = Helper::merge_array_r(
$this->defaults,
(array) $instance
);
require plugin_dir_path(__FILE__) . '/form.php';
}
/**
* Processes the widget's options to be saved.
*
* @since 1.0.0
* @param array $new_instance The previous instance of values before the update.
* @param array $old_instance The new instance of values to be generated via the update.
* @return array $instance Updated instance.
*/
public function update($new_instance, $old_instance)
{
if ( empty($old_instance) ) {
$old_instance = $this->defaults;
} else {
$old_instance = Helper::merge_array_r(
$this->defaults,
(array) $old_instance
);
}
$instance = $old_instance;
$instance['title'] = htmlspecialchars(stripslashes_deep(strip_tags($new_instance['title'])), ENT_QUOTES);
$instance['limit'] = ( Helper::is_number($new_instance['limit']) && $new_instance['limit'] > 0 )
? $new_instance['limit']
: 10;
$instance['range'] = $new_instance['range'];
$instance['time_quantity'] = ( Helper::is_number($new_instance['time_quantity']) && $new_instance['time_quantity'] > 0 )
? $new_instance['time_quantity']
: 24;
$instance['time_unit'] = $new_instance['time_unit'];
$instance['order_by'] = $new_instance['order_by'];
// FILTERS
// user did not set a post type name, so we fall back to default
$instance['post_type'] = ( '' == $new_instance['post_type'] )
? 'post,page'
: $new_instance['post_type'];
$instance['freshness'] = isset($new_instance['freshness']);
// Post / Page / CTP filter
$ids = array_filter(explode(",", rtrim(preg_replace('|[^0-9,]|', '', $new_instance['pid']), ",")), 'is_numeric');
// Got no valid IDs, clear
if ( empty($ids) ) {
$instance['pid'] = '';
}
else {
$instance['pid'] = implode(",", $ids);
}
// Taxonomy filter
$taxonomies = $new_instance['taxonomy'];
if ( isset($taxonomies['names']) ) {
// Remove taxonomies that don't have any valid term IDs
foreach( $taxonomies['terms'] as $taxonomy => $terms ) {
$taxonomies['terms'][$taxonomy] = array_filter(
explode(",", trim(preg_replace('|[^0-9,-]|', '', $taxonomies['terms'][$taxonomy]), ", ")),
'is_numeric'
);
if (
empty($taxonomies['terms'][$taxonomy])
|| ! in_array($taxonomy, $taxonomies['names'])
) {
unset($taxonomies['terms'][$taxonomy]);
} else {
$taxonomies['terms'][$taxonomy] = implode(',', $taxonomies['terms'][$taxonomy]);
}
}
if ( ! empty($taxonomies['terms']) ) {
$instance['taxonomy'] = implode(';', array_keys($taxonomies['terms']));
$instance['term_id'] = implode(';', array_values($taxonomies['terms']));
}
} // Discard everything
else {
$instance['taxonomy'] = '';
$instance['term_id'] = '';
}
// Author filter
$ids = array_filter(explode(",", rtrim(preg_replace('|[^0-9,]|', '', $new_instance['uid']), ",")), 'is_numeric');
// Got no valid IDs, clear
if ( empty($ids) ) {
$instance['author'] = '';
}
else {
$instance['author'] = implode( ",", $ids );
}
$instance['shorten_title']['words'] = $new_instance['shorten_title-words'];
$instance['shorten_title']['active'] = isset($new_instance['shorten_title-active']);
$instance['shorten_title']['length'] = ( Helper::is_number($new_instance['shorten_title-length']) && $new_instance['shorten_title-length'] > 0 )
? $new_instance['shorten_title-length']
: 25;
$instance['post-excerpt']['keep_format'] = isset($new_instance['post-excerpt-format']);
$instance['post-excerpt']['words'] = $new_instance['post-excerpt-words'];
$instance['post-excerpt']['active'] = isset($new_instance['post-excerpt-active']);
$instance['post-excerpt']['length'] = ( Helper::is_number($new_instance['post-excerpt-length']) && $new_instance['post-excerpt-length'] > 0 )
? $new_instance['post-excerpt-length']
: 55;
$instance['thumbnail']['active'] = isset($new_instance['thumbnail-active']);
$instance['thumbnail']['build'] = $new_instance['thumbnail-size-source'];
$instance['thumbnail']['width'] = 75;
$instance['thumbnail']['height'] = 75;
// Use predefined thumbnail sizes
if ( 'predefined' == $new_instance['thumbnail-size-source'] ) {
$default_thumbnail_sizes = $this->thumbnail->get_sizes();
$size = $default_thumbnail_sizes[$new_instance['thumbnail-size']];
$instance['thumbnail']['width'] = $size['width'];
$instance['thumbnail']['height'] = $size['height'];
$instance['thumbnail']['crop'] = $size['crop'];
} // Set thumbnail size manually
else {
if ( Helper::is_number($new_instance['thumbnail-width']) && Helper::is_number($new_instance['thumbnail-height']) ) {
$instance['thumbnail']['width'] = $new_instance['thumbnail-width'];
$instance['thumbnail']['height'] = $new_instance['thumbnail-height'];
$instance['thumbnail']['crop'] = true;
}
}
$instance['rating'] = isset($new_instance['rating']);
$instance['stats_tag']['comment_count'] = isset($new_instance['comment_count']);
$instance['stats_tag']['views'] = isset($new_instance['views']);
$instance['stats_tag']['author'] = isset($new_instance['author']);
$instance['stats_tag']['date']['active'] = isset($new_instance['date']);
$instance['stats_tag']['date']['format'] = empty($new_instance['date_format'])
? 'F j, Y'
: $new_instance['date_format'];
$instance['stats_tag']['taxonomy']['active'] = isset($new_instance['stats_taxonomy']);
$instance['stats_tag']['taxonomy']['name'] = isset($new_instance['stats_taxonomy_name']) ? $new_instance['stats_taxonomy_name'] : 'category';
$instance['stats_tag']['category'] = isset($new_instance['stats_taxonomy'] ); // Deprecated in 4.0.0!
$instance['markup']['custom_html'] = isset($new_instance['custom_html']);
$instance['markup']['wpp-start'] = empty($new_instance['wpp-start'])
? ! $old_instance['markup']['custom_html'] && $instance['markup']['custom_html'] ? htmlspecialchars('<ul class="wpp-list">', ENT_QUOTES) : ''
: htmlspecialchars($new_instance['wpp-start'], ENT_QUOTES);
$instance['markup']['wpp-end'] = empty($new_instance['wpp-end'])
? ! $old_instance['markup']['custom_html'] && $instance['markup']['custom_html'] ? htmlspecialchars('</ul>', ENT_QUOTES) : ''
: htmlspecialchars($new_instance['wpp-end'], ENT_QUOTES);
$instance['markup']['post-html'] = empty($new_instance['post-html'])
? htmlspecialchars('<li>{thumb} {title} {stats}</li>', ENT_QUOTES)
: htmlspecialchars($new_instance['post-html'], ENT_QUOTES);
$instance['markup']['title-start'] = empty($new_instance['title-start'])
? ! $old_instance['markup']['custom_html'] && $instance['markup']['custom_html'] ? '<h2>' : ''
: htmlspecialchars($new_instance['title-start'], ENT_QUOTES);
$instance['markup']['title-end'] = empty($new_instance['title-end'])
? ! $old_instance['markup']['custom_html'] && $instance['markup']['custom_html'] ? '</h2>' : '' :
htmlspecialchars($new_instance['title-end'], ENT_QUOTES);
$instance['theme'] = [
'name' => isset($new_instance['theme']) ? $new_instance['theme'] : '',
'applied' => isset($new_instance['theme']) ? (bool) $new_instance['theme-applied'] : false
];
if ( ! isset($new_instance['theme']) || $old_instance['theme']['name'] != $new_instance['theme'] ) {
$instance['theme']['applied'] = false;
}
$theme = $instance['theme']['name'] ? $this->themer->get_theme($instance['theme']['name']) : null;
if (
is_array($theme)
&& isset($theme['json'])
&& isset($theme['json']['config'])
&& is_array($theme['json']['config'])
&& ! $instance['theme']['applied']
) {
$instance = Helper::merge_array_r(
$instance,
$theme['json']['config']
);
$instance['markup']['custom_html'] = true;
$instance['theme']['applied'] = true;
$current_sidebar_data = $this->get_sidebar_data();
if ( $current_sidebar_data ) {
$instance['markup']['title-start'] = htmlspecialchars($current_sidebar_data['before_title'], ENT_QUOTES);
$instance['markup']['title-end'] = htmlspecialchars($current_sidebar_data['after_title'], ENT_QUOTES);
}
}
return $instance;
}
/**
* Returns HTML list.
*
* @since 2.3.3
*/
public function get_popular($instance = null)
{
if ( is_array($instance) && ! empty($instance) ) {
// Return cached results
if ( $this->admin_options['tools']['cache']['active'] ) {
$key = md5(json_encode($instance));
$popular_posts = \WordPressPopularPosts\Cache::get($key);
if ( false === $popular_posts ) {
$popular_posts = new Query($instance);
$time_value = $this->admin_options['tools']['cache']['interval']['value']; // eg. 5
$time_unit = $this->admin_options['tools']['cache']['interval']['time']; // eg. 'minute'
// No popular posts found, check again in 1 minute
if ( ! $popular_posts->get_posts() ) {
$time_value = 1;
$time_unit = 'minute';
}
\WordPressPopularPosts\Cache::set(
$key,
$popular_posts,
$time_value,
$time_unit
);
}
} // Get popular posts
else {
$popular_posts = new Query($instance);
}
$this->output->set_data($popular_posts->get_posts());
$this->output->set_public_options($instance);
$this->output->build_output();
$this->output->output();
}
}
/**
* Returns data on the current sidebar.
*
* @since 5.0.0
* @access private
* @return array|null
*/
private function get_sidebar_data()
{
global $wp_registered_sidebars;
$sidebars = wp_get_sidebars_widgets();
foreach ( (array) $sidebars as $sidebar_id => $sidebar ) {
if ( in_array($this->id, (array) $sidebar, true ) )
return $wp_registered_sidebars[$sidebar_id];
}
return null;
}
/**
* Removes the standard widget from the Legacy Widget block.
*
* @param array
* @return array
*/
public function remove_from_legacy_widget_block($widget_types)
{
$widget_types[] = 'wpp';
return $widget_types;
}
}

View File

@ -0,0 +1,237 @@
<?php
$current_sidebar_data = $this->get_sidebar_data();
$current_sidebar = $current_sidebar_data ? $current_sidebar_data['id'] : null;
?>
<!-- Widget title -->
<p>
<label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title', 'wordpress-popular-posts'); ?>:</label> <small>[<a href="https://github.com/cabrerahector/wordpress-popular-posts/wiki/5.-FAQ#what-does-title-do" title="<?php _e('What is this?', 'wordpress-popular-posts'); ?>" target="_blank">?</a>]</small> <br />
<input type="text" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" value="<?php echo $instance['title']; ?>" class="widefat" />
</p>
<!-- Limit -->
<p>
<label for="<?php echo $this->get_field_id('limit'); ?>"><?php _e('Show up to', 'wordpress-popular-posts'); ?>:</label><br />
<input type="text" id="<?php echo $this->get_field_id('limit'); ?>" name="<?php echo $this->get_field_name('limit'); ?>" value="<?php echo $instance['limit']; ?>" class="widefat" style="width:50px!important" /> <?php _e('posts', 'wordpress-popular-posts'); ?>
</p>
<!-- Order by -->
<p>
<label for="<?php echo $this->get_field_id('order_by'); ?>"><?php _e('Sort posts by', 'wordpress-popular-posts'); ?>:</label><br />
<select id="<?php echo $this->get_field_id('order_by'); ?>" name="<?php echo $this->get_field_name('order_by'); ?>" class="widefat">
<option value="comments" <?php if ('comments' == $instance['order_by'] ) echo 'selected="selected"'; ?>><?php _e('Comments', 'wordpress-popular-posts'); ?></option>
<option value="views" <?php if ('views' == $instance['order_by'] ) echo 'selected="selected"'; ?>><?php _e('Total views', 'wordpress-popular-posts'); ?></option>
<option value="avg" <?php if ('avg' == $instance['order_by'] ) echo 'selected="selected"'; ?>><?php _e('Avg. daily views', 'wordpress-popular-posts'); ?></option>
</select>
</p>
<!-- Filters -->
<br /><hr /><br />
<legend><strong><?php _e('Filters', 'wordpress-popular-posts'); ?></strong></legend><br />
<label for="<?php echo $this->get_field_id('range'); ?>"><?php _e('Time Range', 'wordpress-popular-posts'); ?>:</label><br />
<select id="<?php echo $this->get_field_id('range'); ?>" name="<?php echo $this->get_field_name('range'); ?>" class="widefat" style="margin-bottom:5px;">
<option value="daily" <?php if ('daily' == $instance['range'] || 'last24hours' == $instance['range'] ) echo 'selected="selected"'; ?>><?php _e('Last 24 hours', 'wordpress-popular-posts'); ?></option>
<option value="weekly" <?php if ('weekly' == $instance['range'] || 'last7days' == $instance['range'] ) echo 'selected="selected"'; ?>><?php _e('Last 7 days', 'wordpress-popular-posts'); ?></option>
<option value="monthly" <?php if ('monthly' == $instance['range'] || 'last30days' == $instance['range'] ) echo 'selected="selected"'; ?>><?php _e('Last 30 days', 'wordpress-popular-posts'); ?></option>
<option value="all" <?php if ('all' == $instance['range'] ) echo 'selected="selected"'; ?>><?php _e('All-time', 'wordpress-popular-posts'); ?></option>
<option value="custom" <?php if ('custom' == $instance['range'] ) echo 'selected="selected"'; ?>><?php _e('Custom', 'wordpress-popular-posts'); ?></option>
</select><br />
<div style="display: <?php echo ('custom' == $instance['range'] ) ? "block" : "none"; ?>">
<input type="text" id="<?php echo $this->get_field_id('time_quantity'); ?>" name="<?php echo $this->get_field_name('time_quantity'); ?>" value="<?php echo $instance['time_quantity']; ?>" style="display: inline; float: left; width: 50px!important;" />
<select id="<?php echo $this->get_field_id('time_unit'); ?>" name="<?php echo $this->get_field_name('time_unit'); ?>" style="margin-bottom: 5px;">
<option <?php if ($instance['time_unit'] == "minute") {?>selected="selected"<?php } ?> value="minute"><?php _e("Minute(s)", 'wordpress-popular-posts'); ?></option>
<option <?php if ($instance['time_unit'] == "hour") {?>selected="selected"<?php } ?> value="hour"><?php _e("Hour(s)", 'wordpress-popular-posts'); ?></option>
<option <?php if ($instance['time_unit'] == "day") {?>selected="selected"<?php } ?> value="day"><?php _e("Day(s)", 'wordpress-popular-posts'); ?></option>
</select>
</div>
<div class="clearfix"></div>
<input type="checkbox" class="checkbox" <?php echo ($instance['freshness']) ? 'checked="checked"' : ''; ?> id="<?php echo $this->get_field_id('freshness'); ?>" name="<?php echo $this->get_field_name('freshness'); ?>" /> <label for="<?php echo $this->get_field_id('freshness'); ?>"><small><?php _e('Display only posts published within the selected Time Range', 'wordpress-popular-posts'); ?></small></label><br /><br />
<label for="<?php echo $this->get_field_id('post_type'); ?>"><?php _e('Post type(s)', 'wordpress-popular-posts'); ?>:</label> <small>[<a href="https://github.com/cabrerahector/wordpress-popular-posts/wiki/5.-FAQ#what-is-post-type-for" title="<?php _e('What is this?', 'wordpress-popular-posts'); ?>" target="_blank">?</a>]</small>
<input type="text" id="<?php echo $this->get_field_id('post_type'); ?>" name="<?php echo $this->get_field_name('post_type'); ?>" value="<?php echo esc_attr($instance['post_type']); ?>" class="widefat" /><br /><br />
<label for="<?php echo $this->get_field_id('pid'); ?>"><?php _e('Post ID(s) to exclude', 'wordpress-popular-posts'); ?>:</label>
<input type="text" id="<?php echo $this->get_field_id('pid'); ?>" name="<?php echo $this->get_field_name('pid'); ?>" value="<?php echo $instance['pid']; ?>" class="widefat" /><br /><br />
<label for="<?php echo $this->get_field_id('tax_id'); ?>"><?php _e('Taxonomy', 'wordpress-popular-posts'); ?>:</label> <small>[<a href="https://github.com/cabrerahector/wordpress-popular-posts/wiki/5.-FAQ#what-is-taxonomy-for" title="<?php _e('What is this?', 'wordpress-popular-posts'); ?>" target="_blank">?</a>]</small><br style="margin-bottom: 0.5rem" />
<?php
$selected_taxonomies = explode(';', $instance['taxonomy']);
$selected_terms = explode(';', $instance['term_id']);
$tax_filter = [];
if ( ! empty($selected_taxonomies) ) {
foreach( $selected_taxonomies as $index => $selected_taxonomy ) {
if ( isset($selected_terms[$index]) ) {
$tax_filter[$selected_taxonomy] = $selected_terms[$index];
}
}
}
// Taxonomy filter
if ( $taxonomies = get_taxonomies(['public' => true], 'objects') ) {
foreach ( $taxonomies as $taxonomy ) {
if ( 'post_format' == $taxonomy->name )
continue;
echo '<label><input type="checkbox" name="' . $this->get_field_name('taxonomy') . '[names][]" value="' . $taxonomy->name . '"' . ( isset($tax_filter[$taxonomy->name]) ? ' checked' : '') . '> ' . $taxonomy->labels->singular_name . ' <small>('. $taxonomy->name .')</small></label><br>';
echo '<input type="text" name="' . $this->get_field_name('taxonomy') . '[terms][' . $taxonomy->name . ']" value="' . ( isset($tax_filter[$taxonomy->name]) ? esc_attr($tax_filter[$taxonomy->name]) : '') . '" class="widefat" style="margin-top: 4px;" /><br />';
/* translators: %s here represents the singular name of the taxonomy (eg. Category) */
$taxonomy_instructions = __('%s IDs, separated by comma (prefix a minus sign to exclude)', 'wordpress-popular-posts');
echo '<small>' . sprintf($taxonomy_instructions, $taxonomy->labels->singular_name) . '</small><br /><br />';
}
}
?>
<label for="<?php echo $this->get_field_id('uid'); ?>"><?php _e('Author ID(s)', 'wordpress-popular-posts'); ?>:</label> <small>[<a href="https://github.com/cabrerahector/wordpress-popular-posts/wiki/5.-FAQ#what-is-author-ids-for" title="<?php _e('What is this?', 'wordpress-popular-posts'); ?>" target="_blank">?</a>]</small>
<input type="text" id="<?php echo $this->get_field_id('uid'); ?>" name="<?php echo $this->get_field_name('uid'); ?>" value="<?php echo $instance['author']; ?>" class="widefat" /><br /><br />
<!-- Post features -->
<br /><hr /><br />
<legend><strong><?php _e('Posts settings', 'wordpress-popular-posts'); ?></strong></legend>
<br />
<div style="display:<?php if ( function_exists('the_ratings_results') ) : ?>block<?php else: ?>none<?php endif; ?>;">
<input type="checkbox" class="checkbox" <?php echo ($instance['rating']) ? 'checked="checked"' : ''; ?> id="<?php echo $this->get_field_id('rating'); ?>" name="<?php echo $this->get_field_name('rating'); ?>" /> <label for="<?php echo $this->get_field_id('rating'); ?>"><?php _e('Display post rating', 'wordpress-popular-posts'); ?></label> <small>[<a href="https://github.com/cabrerahector/wordpress-popular-posts/wiki/5.-FAQ#what-does-display-post-rating-do" title="<?php _e('What is this?', 'wordpress-popular-posts'); ?>" target="_blank">?</a>]</small>
</div>
<input type="checkbox" class="checkbox" <?php echo ($instance['shorten_title']['active']) ? 'checked="checked"' : ''; ?> id="<?php echo $this->get_field_id('shorten_title-active'); ?>" name="<?php echo $this->get_field_name('shorten_title-active'); ?>" /> <label for="<?php echo $this->get_field_id('shorten_title-active'); ?>"><?php _e('Shorten title', 'wordpress-popular-posts'); ?></label><br />
<div style="display:<?php if ($instance['shorten_title']['active']) : ?>block<?php else: ?>none<?php endif; ?>; width:90%; margin:10px 0; padding:3% 5%; background:#f5f5f5;">
<label for="<?php echo $this->get_field_id('shorten_title-length'); ?>"><?php _e('Shorten title to', 'wordpress-popular-posts'); ?> <input type="text" id="<?php echo $this->get_field_id('shorten_title-length'); ?>" name="<?php echo $this->get_field_name('shorten_title-length'); ?>" value="<?php echo $instance['shorten_title']['length']; ?>" class="widefat" style="width:50px!important" /></label><br />
<label><input type="radio" name="<?php echo $this->get_field_name('shorten_title-words'); ?>" value="0" <?php echo (!isset($instance['shorten_title']['words']) || !$instance['shorten_title']['words']) ? 'checked="checked"' : ''; ?> /> <?php _e('characters', 'wordpress-popular-posts'); ?></label><br />
<label><input type="radio" name="<?php echo $this->get_field_name('shorten_title-words'); ?>" value="1" <?php echo (isset($instance['shorten_title']['words']) && $instance['shorten_title']['words']) ? 'checked="checked"' : ''; ?> /> <?php _e('words', 'wordpress-popular-posts'); ?></label>
</div>
<input type="checkbox" class="checkbox" <?php echo ($instance['post-excerpt']['active']) ? 'checked="checked"' : ''; ?> id="<?php echo $this->get_field_id('post-excerpt-active'); ?>" name="<?php echo $this->get_field_name('post-excerpt-active'); ?>" /> <label for="<?php echo $this->get_field_id('post-excerpt-active'); ?>"><?php _e('Display post excerpt', 'wordpress-popular-posts'); ?></label><br />
<div style="display:<?php if ($instance['post-excerpt']['active']) : ?>block<?php else: ?>none<?php endif; ?>; width:90%; margin:10px 0; padding:3% 5%; background:#f5f5f5;">
<input type="checkbox" class="checkbox" id="<?php echo $this->get_field_id('post-excerpt-format'); ?>" name="<?php echo $this->get_field_name('post-excerpt-format'); ?>" <?php echo ($instance['post-excerpt']['keep_format']) ? 'checked="checked"' : ''; ?> /> <label for="<?php echo $this->get_field_id('post-excerpt-format'); ?>"><?php _e('Keep text format and links', 'wordpress-popular-posts'); ?></label><br /><br />
<label for="<?php echo $this->get_field_id('post-excerpt-length'); ?>"><?php _e('Excerpt length', 'wordpress-popular-posts'); ?>: <input type="text" id="<?php echo $this->get_field_id('post-excerpt-length'); ?>" name="<?php echo $this->get_field_name('post-excerpt-length'); ?>" value="<?php echo $instance['post-excerpt']['length']; ?>" class="widefat" style="width:50px!important" /></label><br />
<label><input type="radio" name="<?php echo $this->get_field_name('post-excerpt-words'); ?>" value="0" <?php echo (!isset($instance['post-excerpt']['words']) || !$instance['post-excerpt']['words']) ? 'checked="checked"' : ''; ?> /> <?php _e('characters', 'wordpress-popular-posts'); ?></label><br />
<label><input type="radio" name="<?php echo $this->get_field_name('post-excerpt-words'); ?>" value="1" <?php echo (isset($instance['post-excerpt']['words']) && $instance['post-excerpt']['words']) ? 'checked="checked"' : ''; ?> /> <?php _e('words', 'wordpress-popular-posts'); ?></label>
</div>
<input type="checkbox" class="checkbox" <?php echo ($instance['thumbnail']['active']) ? 'checked="checked"' : ''; ?> id="<?php echo $this->get_field_id('thumbnail-active'); ?>" name="<?php echo $this->get_field_name('thumbnail-active'); ?>" /> <label for="<?php echo $this->get_field_id('thumbnail-active'); ?>"><?php _e('Display post thumbnail', 'wordpress-popular-posts'); ?></label>
<div style="display:<?php if ($instance['thumbnail']['active']) : ?>block<?php else: ?>none<?php endif; ?>; width:90%; margin:10px 0; padding:3% 5%; background:#f5f5f5;">
<label><input type='radio' id='thumbnail-predefined-size' name='<?php echo $this->get_field_name('thumbnail-size-source'); ?>' value='predefined' <?php echo ($instance['thumbnail']['build'] == 'predefined') ? 'checked="checked"' : ''; ?> /><?php _e('Use predefined size', 'wordpress-popular-posts'); ?></label><br />
<select id="<?php echo $this->get_field_id('thumbnail-size'); ?>" name="<?php echo $this->get_field_name('thumbnail-size'); ?>" class="widefat" style="margin:5px 0;">
<?php
foreach ( $this->thumbnail->get_sizes() as $name => $attr ) :
echo '<option value="' . $name . '"' . ( ($instance['thumbnail']['build'] == 'predefined' && $attr['width'] == $instance['thumbnail']['width'] && $attr['height'] == $instance['thumbnail']['height'] ) ? ' selected="selected"' : '') . '>' . $name . ' (' . $attr['width'] . ' x ' . $attr['height'] . ( $attr['crop'] ? ', hard crop' : ', soft crop') . ')</option>';
endforeach;
?>
</select>
<hr />
<label><input type='radio' id='thumbnail-manual-size' name='<?php echo $this->get_field_name('thumbnail-size-source'); ?>' value='manual' <?php echo ($instance['thumbnail']['build'] == 'manual') ? 'checked="checked"' : ''; ?> /><?php _e('Set size manually', 'wordpress-popular-posts'); ?></label><br />
<label for="<?php echo $this->get_field_id('thumbnail-width'); ?>"><?php _e('Width', 'wordpress-popular-posts'); ?>:</label>
<input type="text" id="<?php echo $this->get_field_id('thumbnail-width'); ?>" name="<?php echo $this->get_field_name('thumbnail-width'); ?>" value="<?php echo $instance['thumbnail']['width']; ?>" class="widefat" style="margin:3px 0; width:50px!important" /> px<br />
<label for="<?php echo $this->get_field_id('thumbnail-height'); ?>"><?php _e('Height', 'wordpress-popular-posts'); ?>:</label>
<input type="text" id="<?php echo $this->get_field_id('thumbnail-height'); ?>" name="<?php echo $this->get_field_name('thumbnail-height'); ?>" value="<?php echo $instance['thumbnail']['height']; ?>" class="widefat" style="width:50px!important" /> px
</div><br />
<!-- Stats tag options -->
<br /><hr /><br />
<legend><strong><?php _e('Stats Tag settings', 'wordpress-popular-posts'); ?></strong></legend><br />
<input type="checkbox" class="checkbox" <?php echo ($instance['stats_tag']['comment_count']) ? 'checked="checked"' : ''; ?> id="<?php echo $this->get_field_id('comment_count'); ?>" name="<?php echo $this->get_field_name('comment_count'); ?>" /> <label for="<?php echo $this->get_field_id('comment_count'); ?>"><?php _e('Display comment count', 'wordpress-popular-posts'); ?></label><br />
<input type="checkbox" class="checkbox" <?php echo ($instance['stats_tag']['views']) ? 'checked="checked"' : ''; ?> id="<?php echo $this->get_field_id('views'); ?>" name="<?php echo $this->get_field_name('views'); ?>" /> <label for="<?php echo $this->get_field_id('views'); ?>"><?php _e('Display views', 'wordpress-popular-posts'); ?></label><br />
<input type="checkbox" class="checkbox" <?php echo ($instance['stats_tag']['author']) ? 'checked="checked"' : ''; ?> id="<?php echo $this->get_field_id('author'); ?>" name="<?php echo $this->get_field_name('author'); ?>" /> <label for="<?php echo $this->get_field_id('author'); ?>"><?php _e('Display author', 'wordpress-popular-posts'); ?></label><br />
<input type="checkbox" class="checkbox" <?php echo ($instance['stats_tag']['date']['active']) ? 'checked="checked"' : ''; ?> id="<?php echo $this->get_field_id('date'); ?>" name="<?php echo $this->get_field_name('date'); ?>" /> <label for="<?php echo $this->get_field_id('date'); ?>"><?php _e('Display date', 'wordpress-popular-posts'); ?></label><br />
<div style="display:<?php if ($instance['stats_tag']['date']['active']) : ?>block<?php else: ?>none<?php endif; ?>; width:90%; margin:10px 0; padding:3% 5%; background:#f5f5f5;">
<legend><strong><?php _e('Date Format', 'wordpress-popular-posts'); ?></strong></legend><br />
<label title='m/d/Y'><input type='radio' name='<?php echo $this->get_field_name('date_format'); ?>' value='relative' <?php echo ($instance['stats_tag']['date']['format'] == 'relative') ? 'checked="checked"' : ''; ?> /><?php _e('Relative', 'wordpress-popular-posts'); ?></label><br />
<label title='<?php echo get_option('date_format'); ?>'><input type='radio' name='<?php echo $this->get_field_name('date_format'); ?>' value='wp_date_format' <?php echo ($instance['stats_tag']['date']['format'] == 'wp_date_format') ? 'checked="checked"' : ''; ?> /><?php echo date_i18n(get_option('date_format'), time()); ?></label> <small>(<a href="<?php echo admin_url('options-general.php'); ?>" title="<?php _e('WordPress Date Format', 'wordpress-popular-posts'); ?>" target="_blank"><?php _e('WordPress Date Format', 'wordpress-popular-posts'); ?></a>)</small><br />
<label title='F j, Y'><input type='radio' name='<?php echo $this->get_field_name('date_format'); ?>' value='F j, Y' <?php echo ($instance['stats_tag']['date']['format'] == 'F j, Y') ? 'checked="checked"' : ''; ?> /><?php echo date_i18n('F j, Y', time()); ?></label><br />
<label title='Y/m/d'><input type='radio' name='<?php echo $this->get_field_name('date_format'); ?>' value='Y/m/d' <?php echo ($instance['stats_tag']['date']['format'] == 'Y/m/d') ? 'checked="checked"' : ''; ?> /><?php echo date_i18n('Y/m/d', time()); ?></label><br />
<label title='m/d/Y'><input type='radio' name='<?php echo $this->get_field_name('date_format'); ?>' value='m/d/Y' <?php echo ($instance['stats_tag']['date']['format'] == 'm/d/Y') ? 'checked="checked"' : ''; ?> /><?php echo date_i18n('m/d/Y', time()); ?></label><br />
<label title='d/m/Y'><input type='radio' name='<?php echo $this->get_field_name('date_format'); ?>' value='d/m/Y' <?php echo ($instance['stats_tag']['date']['format'] == 'd/m/Y') ? 'checked="checked"' : ''; ?> /><?php echo date_i18n('d/m/Y', time()); ?></label>
</div>
<input type="checkbox" class="checkbox" <?php echo ($instance['stats_tag']['taxonomy']['active'] || $instance['stats_tag']['category']) ? 'checked="checked"' : ''; ?> id="<?php echo $this->get_field_id('stats_taxonomy'); ?>" name="<?php echo $this->get_field_name('stats_taxonomy'); ?>" /> <label for="<?php echo $this->get_field_id('stats_taxonomy'); ?>"><?php _e('Display taxonomy', 'wordpress-popular-posts'); ?></label><br />
<?php
if ( $taxonomies ) {
?>
<div style="display:<?php if ($instance['stats_tag']['taxonomy']['active'] || $instance['stats_tag']['category']) : ?>block<?php else: ?>none<?php endif; ?>; width:90%; margin:10px 0; padding:3% 5%; background:#f5f5f5;">
<?php
foreach ( $taxonomies as $taxonomy ) {
if ('post_format' == $taxonomy->name )
continue;
echo '<label><input type="radio" name="' . $this->get_field_name('stats_taxonomy_name') . '" value="' . $taxonomy->name . '"' . ( ( $instance['stats_tag']['taxonomy']['name'] == $taxonomy->name ) ? ' checked' : '') . '> ' . $taxonomy->labels->singular_name . '</label><br>';
}
?>
</div>
<?php
}
?>
<!-- HTML Markup options -->
<br /><hr /><br />
<legend><strong><?php _e('HTML Markup settings', 'wordpress-popular-posts'); ?></strong></legend><br />
<input type="checkbox" class="checkbox" <?php echo ($instance['markup']['custom_html']) ? 'checked="checked"' : ''; ?> id="<?php echo $this->get_field_id('custom_html'); ?>" name="<?php echo $this->get_field_name('custom_html'); ?>" /> <label for="<?php echo $this->get_field_id('custom_html'); ?>"><?php _e('Use custom HTML Markup', 'wordpress-popular-posts'); ?></label> <small>[<a href="https://github.com/cabrerahector/wordpress-popular-posts/wiki/5.-FAQ#what-does-use-custom-html-markup-do" title="<?php _e('What is this?', 'wordpress-popular-posts'); ?>" target="_blank">?</a>]</small><br />
<div style="display:<?php if ($instance['markup']['custom_html']) : ?>block<?php else: ?>none<?php endif; ?>; width:90%; margin:10px 0; padding:3% 5%; background:#f5f5f5;">
<?php
if (
$current_sidebar
&& ! $instance['markup']['custom_html']
) {
$wpp_title_start = htmlspecialchars($current_sidebar_data['before_title'], ENT_QUOTES);
$wpp_title_end = htmlspecialchars($current_sidebar_data['after_title'], ENT_QUOTES);
} else {
$wpp_title_start = $instance['markup']['title-start'];
$wpp_title_end = $instance['markup']['title-end'];
}
?>
<p style="font-size:11px"><label for="<?php echo $this->get_field_id('title-start'); ?>"><?php _e('Before / after title', 'wordpress-popular-posts'); ?>:</label> <br />
<input type="text" id="<?php echo $this->get_field_id('title-start'); ?>" name="<?php echo $this->get_field_name('title-start'); ?>" value="<?php echo $wpp_title_start; ?>" class="widefat" style="width:49%!important" /> <input type="text" id="<?php echo $this->get_field_id('title-end'); ?>" name="<?php echo $this->get_field_name('title-end'); ?>" value="<?php echo $wpp_title_end; ?>" class="widefat" style="width:49%!important" /></p>
<p style="font-size:11px"><label for="<?php echo $this->get_field_id('wpp-start'); ?>"><?php _e('Before / after Popular Posts', 'wordpress-popular-posts'); ?>:</label> <br />
<input type="text" id="<?php echo $this->get_field_id('wpp-start'); ?>" name="<?php echo $this->get_field_name('wpp-start'); ?>" value="<?php echo esc_attr($instance['markup']['wpp-start']); ?>" class="widefat" style="width:49%!important" /> <input type="text" id="<?php echo $this->get_field_id('wpp-end'); ?>" name="<?php echo $this->get_field_name('wpp-end'); ?>" value="<?php echo $instance['markup']['wpp-end']; ?>" class="widefat" style="width:49%!important" /></p>
<p style="font-size:11px"><label for="<?php echo $this->get_field_id('post-html'); ?>"><?php _e('Post HTML Markup', 'wordpress-popular-posts'); ?>:</label> <br />
<textarea class="widefat" rows="10" id="<?php echo $this->get_field_id('post-html'); ?>" name="<?php echo $this->get_field_name('post-html'); ?>"><?php echo $instance['markup']['post-html']; ?></textarea>
</div>
<!-- Theme -->
<br /><hr /><br />
<legend style="display: inline;"><strong><?php _e('Theme', 'wordpress-popular-posts'); ?></strong></legend><small>(<?php printf(__('see a <a href="%s">list of supported browsers</a>'), 'https://caniuse.com/#feat=shadowdomv1'); ?>)</small><br /><br />
<?php
$registered_themes = $this->themer->get_themes();
ksort($registered_themes);
?>
<select id="<?php echo $this->get_field_id('theme'); ?>" name="<?php echo $this->get_field_name('theme'); ?>" class="widefat" style="margin-bottom: 5px;"<?php echo ( ! $current_sidebar ) ? ' disabled="disabled"' : ''; ?>>
<option value="" <?php if ( '' == $instance['theme']['name'] || ! $current_sidebar ) echo 'selected="selected"'; ?>><?php _e("None", 'wordpress-popular-posts'); ?></option>
<?php foreach ($registered_themes as $theme => $data) : ?>
<option value="<?php echo esc_attr($theme); ?>" <?php if ( $theme == $instance['theme']['name'] && $current_sidebar ) echo 'selected="selected"'; ?>><?php echo esc_html($data['json']['name']); ?></option>
<?php endforeach; ?>
</select>
<input type="hidden" id="<?php echo $this->get_field_id('theme-applied'); ?>" name="<?php echo $this->get_field_name('theme-applied'); ?>" value="<?php echo ($instance['theme']['applied'] && $current_sidebar) ? 1 : 0; ?>" />
<?php if ( ! $current_sidebar ) : ?>
<p style="color: red;"><?php _e('Please save this widget (or reload this page) to enable WPP themes.', 'wordpress-popular-posts'); ?></p>
<?php endif; ?>
<br /><br />

View File

@ -0,0 +1,93 @@
<?php
/**
* Plugin's main class.
*
* Here everything gets initialized/loaded.
*/
namespace WordPressPopularPosts;
class WordPressPopularPosts {
/**
* I18N class.
*
* @var I18N $i18n
* @access private
*/
private $i18n;
/**
* REST controller class.
*
* @var Rest\Controller $rest
* @access private
*/
private $rest;
/**
* Admin class.
*
* @var Admin\Admin $front
* @access private
*/
private $admin;
/**
* Front class.
*
* @var Front\Front $front
* @access private
*/
private $front;
/**
* Widget class.
*
* @var Widget\Widget $widget
* @access private
*/
private $widget;
/**
* Block Widget class.
*
* @var Block\Widget $widget
* @access private
*/
private $block_widget;
/**
* Constructor.
*
* @since 5.0.0
* @param I18N $i18n
* @param Rest\Controller $rest
* @param Admin\Admin $admin
* @param Front\Front $front
* @param Widget\Widget $widget
*/
public function __construct(I18N $i18n, Rest\Controller $rest, Admin\Admin $admin, Front\Front $front, Widget\Widget $widget, Block\Widget\Widget $block_widget)
{
$this->i18n = $i18n;
$this->rest = $rest;
$this->admin = $admin;
$this->front = $front;
$this->widget = $widget;
$this->block_widget = $block_widget;
}
/**
* Initializes plugin.
*
* @since 5.0.0
*/
public function init()
{
$this->i18n->load_plugin_textdomain();
$this->rest->hooks();
$this->admin->hooks();
$this->front->hooks();
$this->widget->hooks();
$this->block_widget->hooks();
}
}

View File

@ -0,0 +1,45 @@
<?php
/**
* Deprecated functions.
*/
/**
* Template tag - gets popular posts. Deprecated in 2.0.3, use wpp_get_mostpopular instead.
*
* @since 1.0
* @param mixed $args
*/
function get_mostpopular($args = NULL) {
trigger_error( 'The get_mostpopular() template tag has been deprecated since 2.0.3. Please use wpp_get_mostpopular() instead.', E_USER_WARNING );
}
/**
* Deprecated classes.
*/
/**
* Queries the database for popular posts. Deprecated since 5.0.0, use \WordPressPopularPosts\Query instead.
*
* To use this class, you must pass it an array of parameters (mostly the same ones used with
* the wpp_get_mostpopular() template tag). The very minimum required parameters are 'range', 'order_by'
* and 'limit'.
*
* eg.: $popular_posts = new WPP_Query( array('range' => 'last7days', 'order_by' => 'views', 'limit' => 5) );
*
* @since 4.0.0
* @package WordPressPopularPosts
*/
class WPP_Query extends \WordPressPopularPosts\Query {
/**
* Constructor.
*
* @since 4.0.0
* @param array $options
*/
public function __construct(array $options = [])
{
parent::__construct($options);
trigger_error('The WPP_Query class has been deprecated since 5.0.0. Please use \WordPressPopularPosts\Query instead.', E_USER_NOTICE);
}
}

View File

@ -0,0 +1,172 @@
<?php
/**
* WordPress Popular Posts template tags for use in themes.
*/
/**
* Template tag - gets views count.
*
* @link https://github.com/cabrerahector/wordpress-popular-posts/wiki/2.-Template-tags#wpp_get_views
* @since 2.0.3
* @param int $id The Post ID.
* @param string|array $range Either an string (eg. 'last7days') or -since 5.3- an array (eg. ['range' => 'custom', 'time_unit' => 'day', 'time_quantity' => 7])
* @param bool $number_format Whether to format the number (eg. 9,999) or not (eg. 9999)
* @return string
*/
function wpp_get_views($id = NULL, $range = NULL, $number_format = true)
{
// have we got an id?
if ( empty($id) || is_null($id) || ! is_numeric($id) )
return "-1";
global $wpdb;
$table_name = $wpdb->prefix . "popularposts";
$args = [
'range' => 'all',
'time_unit' => 'hour',
'time_quantity' => 24,
'_postID' => $id
];
if (
is_array($range)
) {
$args = \WordPressPopularPosts\Helper::merge_array_r($args, $range);
} else {
$range = is_string($range) ? trim($range) : null;
$args['range'] = ! $range ? 'all' : $range;
}
$args['range'] = strtolower($args['range']);
// Get all-time views count
if ( 'all' == $args['range'] ) {
$query = "SELECT pageviews FROM {$table_name}data WHERE postid = '{$id}'";
} // Get views count within time range
else {
$start_date = new \DateTime(
\WordPressPopularPosts\Helper::now(),
new \DateTimeZone(\WordPressPopularPosts\Helper::get_timezone())
);
// Determine time range
switch( $args['range'] ){
case "last24hours":
case "daily":
$start_date = $start_date->sub(new \DateInterval('P1D'));
$start_datetime = $start_date->format('Y-m-d H:i:s');
$views_time_range = "view_datetime >= '{$start_datetime}'";
break;
case "last7days":
case "weekly":
$start_date = $start_date->sub(new \DateInterval('P6D'));
$start_datetime = $start_date->format('Y-m-d');
$views_time_range = "view_date >= '{$start_datetime}'";
break;
case "last30days":
case "monthly":
$start_date = $start_date->sub(new \DateInterval('P29D'));
$start_datetime = $start_date->format('Y-m-d');
$views_time_range = "view_date >= '{$start_datetime}'";
break;
case "custom":
$time_units = ["MINUTE", "HOUR", "DAY", "WEEK", "MONTH"];
// Valid time unit
if (
isset($args['time_unit'])
&& in_array(strtoupper($args['time_unit']), $time_units)
&& isset($args['time_quantity'])
&& filter_var($args['time_quantity'], FILTER_VALIDATE_INT)
&& $args['time_quantity'] > 0
) {
$time_quantity = $args['time_quantity'];
$time_unit = strtoupper($args['time_unit']);
if ( 'MINUTE' == $time_unit ) {
$start_date = $start_date->sub(new \DateInterval('PT' . (60 * $time_quantity) . 'S'));
$start_datetime = $start_date->format('Y-m-d H:i:s');
$views_time_range = "view_datetime >= '{$start_datetime}'";
} elseif ( 'HOUR' == $time_unit ) {
$start_date = $start_date->sub(new \DateInterval('PT' . ((60 * $time_quantity) - 1) . 'M59S'));
$start_datetime = $start_date->format('Y-m-d H:i:s');
$views_time_range = "view_datetime >= '{$start_datetime}'";
} elseif ( 'DAY' == $time_unit ) {
$start_date = $start_date->sub(new \DateInterval('P' . ($time_quantity - 1) . 'D'));
$start_datetime = $start_date->format('Y-m-d');
$views_time_range = "view_date >= '{$start_datetime}'";
} elseif ( 'WEEK' == $time_unit ) {
$start_date = $start_date->sub(new \DateInterval('P' . ((7 * $time_quantity) - 1) . 'D'));
$start_datetime = $start_date->format('Y-m-d');
$views_time_range = "view_date >= '{$start_datetime}'";
} else {
$start_date = $start_date->sub(new \DateInterval('P' . ((30 * $time_quantity) - 1) . 'D'));
$start_datetime = $start_date->format('Y-m-d');
$views_time_range = "view_date >= '{$start_datetime}'";
}
} // Invalid time unit, default to last 24 hours
else {
$start_date = $start_date->sub(new \DateInterval('P1D'));
$start_datetime = $start_date->format('Y-m-d H:i:s');
$views_time_range = "view_datetime >= '{$start_datetime}'";
}
break;
default:
$start_date = $start_date->sub(new \DateInterval('P1D'));
$start_datetime = $start_date->format('Y-m-d H:i:s');
$views_time_range = "view_datetime >= '{$start_datetime}'";
break;
}
$query = $wpdb->prepare(
"SELECT SUM(pageviews) AS pageviews FROM `{$wpdb->prefix}popularpostssummary` WHERE {$views_time_range} AND postid = %d;",
$args['_postID']
);
}
$results = $wpdb->get_var($query);
if ( ! $results )
return 0;
return $number_format ? number_format_i18n(intval($results)) : $results;
}
/**
* Template tag - gets popular posts.
*
* @link https://github.com/cabrerahector/wordpress-popular-posts/wiki/2.-Template-tags#wpp_get_mostpopular
* @since 2.0.3
* @param mixed $args
*/
function wpp_get_mostpopular($args = NULL)
{
$shortcode = '[wpp';
if ( is_null($args) ) {
$shortcode .= ']';
} else {
if ( is_array($args) ) {
$atts = '';
foreach( $args as $key => $arg ){
if (
is_array($arg)
&& ('post_type' == $key || 'cat' == $key || 'term_id' == $key || 'pid' == $key || 'author' == $key)
) {
$arg = array_filter($arg, 'is_int');
$arg = join(',', $arg);
}
$atts .= ' ' . $key . '="' . htmlspecialchars($arg, ENT_QUOTES, $encoding = ini_get("default_charset"), false) . '"';
}
} else {
$atts = trim(str_replace("&", " ", $args));
}
$shortcode .= ' ' . $atts . ' php=true]';
}
echo do_shortcode($shortcode);
}

View File

@ -0,0 +1,81 @@
<?php
/**
* Fired when the plugin is uninstalled.
*
* @package WordpressPopularPosts
* @author Hector Cabrera <me@cabrerahector.com>
* @license GPL-2.0+
* @link https://cabrerahector.com
* @copyright 2008-2021 Hector Cabrera
*/
// If uninstall is not called from WordPress, exit
if ( ! defined('WP_UNINSTALL_PLUGIN') ) {
exit;
}
// Run uninstall for each blog in the network
if (
function_exists('is_multisite')
&& is_multisite()
) {
global $wpdb;
$original_blog_id = get_current_blog_id();
$blogs_ids = $wpdb->get_col("SELECT blog_id FROM {$wpdb->blogs}");
foreach( $blogs_ids as $blog_id ) {
switch_to_blog($blog_id);
// delete tables and options
uninstall();
// delete thumbnails cache and its directory
delete_thumb_cache();
}
// Switch back to current blog
switch_to_blog($original_blog_id);
} else {
// delete tables and options
uninstall();
// delete thumbnails cache and its directory
delete_thumb_cache();
}
function delete_thumb_cache() {
$wp_upload_dir = wp_get_upload_dir();
if ( is_dir($wp_upload_dir['basedir'] . "/wordpress-popular-posts") ) {
$files = glob($wp_upload_dir['basedir'] . "/wordpress-popular-posts/*"); // get all file names
if ( is_array($files) && ! empty($files) ) {
foreach( $files as $file ){ // iterate files
if ( is_file($file) )
@unlink($file); // delete file
}
}
// Finally, delete WPP's upload directory
@rmdir($wp_upload_dir['basedir'] . "/wordpress-popular-posts");
}
}
function uninstall() {
global $wpdb;
// Delete plugin's options
delete_option('wpp_ver');
delete_option('wpp_update');
delete_option('wpp_settings_config');
delete_option('wpp_rand');
delete_option('wpp_transients');
delete_option('wpp_performance_nag');
// Delete WPP's DB tables
$prefix = $wpdb->prefix . "popularposts";
$wpdb->query("DROP TABLE IF EXISTS {$prefix}data;");
$wpdb->query("DROP TABLE IF EXISTS {$prefix}datacache;");
$wpdb->query("DROP TABLE IF EXISTS {$prefix}datacache_backup;");
$wpdb->query("DROP TABLE IF EXISTS {$prefix}log;");
$wpdb->query("DROP TABLE IF EXISTS {$prefix}summary;");
$wpdb->query("DROP TABLE IF EXISTS {$prefix}transients;");
}

View File

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

View File

@ -0,0 +1,479 @@
<?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 https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
private $vendorDir;
// 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;
private static $registeredLoaders = array();
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($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') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $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);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* 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;
}
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
*
* @return self[]
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
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,10 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.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,10 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'WordPressPopularPosts\\' => array($baseDir . '/src'),
);

View File

@ -0,0 +1,57 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit2a403b03a84e333364c96d224aa62f5c
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit2a403b03a84e333364c96d224aa62f5c', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
spl_autoload_unregister(array('ComposerAutoloaderInit2a403b03a84e333364c96d224aa62f5c', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit2a403b03a84e333364c96d224aa62f5c::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);
return $loader;
}
}

View File

@ -0,0 +1,36 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit2a403b03a84e333364c96d224aa62f5c
{
public static $prefixLengthsPsr4 = array (
'W' =>
array (
'WordPressPopularPosts\\' => 22,
),
);
public static $prefixDirsPsr4 = array (
'WordPressPopularPosts\\' =>
array (
0 => __DIR__ . '/../..' . '/src',
),
);
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit2a403b03a84e333364c96d224aa62f5c::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit2a403b03a84e333364c96d224aa62f5c::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit2a403b03a84e333364c96d224aa62f5c::$classMap;
}, null, ClassLoader::class);
}
}

View File

@ -0,0 +1,26 @@
<?php
// platform_check.php @generated by Composer
$issues = array();
if (!(PHP_VERSION_ID >= 50400)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 5.4.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
);
}

View File

@ -0,0 +1,60 @@
<?php
/**
* The plugin bootstrap file
*
* This file is read by WordPress to generate the plugin information in the plugin
* admin area. This file also includes all of the dependencies used by the plugin,
* registers the activation and deactivation functions, and defines a function
* that starts the plugin.
*
* @link https://cabrerahector.com/
* @since 4.0.0
* @package WordPressPopularPosts
*
* @wordpress-plugin
* Plugin Name: WordPress Popular Posts
* Plugin URI: https://wordpress.org/plugins/wordpress-popular-posts/
* Description: A highly customizable widget that displays the most popular posts on your blog.
* Version: 5.5.1
* Author: Hector Cabrera
* Author URI: https://cabrerahector.com/
* License: GPL-2.0+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
* Text Domain: wordpress-popular-posts
* Domain Path: /languages
*/
if ( ! defined( 'WPINC' ) ) {
die();
}
define('WPP_VERSION', '5.5.1');
define('WPP_MIN_PHP_VERSION', '5.4');
define('WPP_MIN_WP_VERSION', '4.9');
/** Requirements check */
global $wp_version;
// We're good, continue!
if ( version_compare(PHP_VERSION, WPP_MIN_PHP_VERSION, '>=') && version_compare($wp_version, WPP_MIN_WP_VERSION, '>=') ) {
$wpp_main_plugin_file = __FILE__;
// Load plugin bootstrap
require __DIR__ . '/src/Bootstrap.php';
} // Nope.
else {
if ( isset($_GET['activate']) )
unset($_GET['activate']);
function wpp_render_min_requirements_notice() {
global $wp_version;
echo '<div class="notice notice-error"><p>' . sprintf(
__('WordPress Popular Posts requires at least PHP %1$s and WordPress %2$s to function correctly. Your site uses PHP %3$s and WordPress %4$s.', 'wordpress-popular-posts'),
WPP_MIN_PHP_VERSION,
WPP_MIN_WP_VERSION,
PHP_VERSION,
$wp_version
) . '</p></div>';
}
add_action('admin_notices', 'wpp_render_min_requirements_notice');
}