Merged in feature/plugins-update (pull request #9)
wp plugin updates from pantheon * wp plugin updates from pantheon
3
wp/wp-content/plugins/facetwp/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
yarn.lock
|
||||
1110
wp/wp-content/plugins/facetwp/assets/css/admin.css
Normal file
327
wp/wp-content/plugins/facetwp/assets/css/front.css
Normal file
@@ -0,0 +1,327 @@
|
||||
.facetwp-facet {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.facetwp-facet.is-loading {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.facetwp-overlay {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.facetwp-pager-label {
|
||||
display: inline-block;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.facetwp-page {
|
||||
display: inline-block;
|
||||
padding: 0px 4px;
|
||||
margin-right: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.facetwp-page.dots {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.facetwp-page.active {
|
||||
font-weight: bold;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* Checkboxes */
|
||||
|
||||
.facetwp-type-checkboxes .facetwp-depth {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.facetwp-type-checkboxes .facetwp-depth.visible {
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
.facetwp-checkbox {
|
||||
background: url('../images/checkbox.png') 0 50% no-repeat;
|
||||
background-size: 14px 14px;
|
||||
margin-bottom: 4px;
|
||||
padding-left: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.facetwp-checkbox.checked {
|
||||
background-image: url('../images/checkbox-on.png');
|
||||
}
|
||||
|
||||
.facetwp-checkbox.disabled,
|
||||
.facetwp-radio.disabled {
|
||||
opacity: 0.4;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.facetwp-checkbox .facetwp-expand {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.facetwp-display-value {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
/* Radio */
|
||||
|
||||
.facetwp-radio {
|
||||
background: url('../images/radio.png') 0 50% no-repeat;
|
||||
background-size: 14px 14px;
|
||||
margin-bottom: 4px;
|
||||
padding-left: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.facetwp-radio.checked {
|
||||
background-image: url('../images/radio-on.png');
|
||||
}
|
||||
|
||||
/* fSelect */
|
||||
|
||||
.facetwp-type-fselect.is-loading {
|
||||
opacity: 1; /* prevent stack order issues */
|
||||
}
|
||||
|
||||
.facetwp-type-fselect.is-loading .fs-label-wrap,
|
||||
.facetwp-type-fselect.is-loading .fs-search,
|
||||
.facetwp-type-fselect.is-loading .fs-no-results,
|
||||
.facetwp-type-fselect.is-loading .fs-options {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.facetwp-type-fselect.is-loading .fs-option {
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
.facetwp-type-fselect .fs-wrap.fs-disabled .fs-option {
|
||||
opacity: 0.4;
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
.facetwp-type-fselect .fs-option .fs-option-label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.facetwp-type-fselect .fs-option.d1 .fs-option-label {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.facetwp-type-fselect .fs-option.d2 .fs-option-label {
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
.facetwp-type-fselect .fs-option.d3 .fs-option-label {
|
||||
padding-left: 60px;
|
||||
}
|
||||
|
||||
/* Hierarchy */
|
||||
|
||||
.facetwp-depth {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.facetwp-link {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.facetwp-link.checked {
|
||||
font-weight: bold;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.facetwp-toggle {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.facetwp-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Slider */
|
||||
|
||||
.facetwp-slider-wrap {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.facetwp-slider-reset {
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 3px;
|
||||
background: #fff;
|
||||
box-shadow: inset 0 0 1px #fff, inset 0 1px 7px #ebebeb, 0 3px 6px -3px #bbb;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.facetwp-slider[data-disabled="true"] {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.facetwp-slider[data-disabled="true"] .noUi-handle {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Search */
|
||||
|
||||
.facetwp-input-wrap {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.facetwp-facet input.facetwp-search,
|
||||
.facetwp-facet input.facetwp-location {
|
||||
margin: 0;
|
||||
padding-right: 30px;
|
||||
min-width: 240px;
|
||||
}
|
||||
|
||||
.facetwp-icon {
|
||||
right: 0;
|
||||
height: 100%;
|
||||
line-height: 1;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.facetwp-icon:before {
|
||||
display: inline-block;
|
||||
content: '';
|
||||
width: 30px;
|
||||
height: 100%;
|
||||
background: url('../images/icon-search.png') no-repeat;
|
||||
background-position: 5px 50%;
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
/* Proximity */
|
||||
|
||||
.location-results {
|
||||
position: absolute;
|
||||
background: #fff;
|
||||
border-left: 1px solid #ddd;
|
||||
border-right: 1px solid #ddd;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.location-result {
|
||||
font-size: 11px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding: 5px;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.location-result:hover {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.location-result.active {
|
||||
background-color: #EBF2FE;
|
||||
}
|
||||
|
||||
.location-result .result-main {
|
||||
font-size: 13px;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.facetwp-icon.locate-me:before {
|
||||
background-image: url('../images/icon-locate.png');
|
||||
}
|
||||
|
||||
.facetwp-icon.f-reset:before {
|
||||
background-image: url('../images/icon-close.png');
|
||||
}
|
||||
|
||||
.facetwp-icon.f-loading:before {
|
||||
background-image: url('../images/loading.png');
|
||||
animation: spin 700ms infinite linear;
|
||||
}
|
||||
|
||||
.location-attribution {
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.powered-by-google {
|
||||
height: 15px;
|
||||
background: url('../images/powered-by-google.png') top right no-repeat;
|
||||
background-size: auto 15px;
|
||||
}
|
||||
|
||||
/* Rating */
|
||||
|
||||
.facetwp-stars {
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
padding-right: 4px;
|
||||
user-select: none;
|
||||
unicode-bidi: bidi-override;
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.facetwp-star {
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.facetwp-star:hover,
|
||||
.facetwp-star:hover ~ .facetwp-star,
|
||||
.facetwp-star.selected,
|
||||
.facetwp-star.selected ~ .facetwp-star {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.facetwp-star.selected:hover,
|
||||
.facetwp-star.selected:hover ~ .facetwp-star {
|
||||
color: red;
|
||||
}
|
||||
|
||||
/* CSS animations */
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Selections shortcode */
|
||||
|
||||
.facetwp-selections li {
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.facetwp-selections .facetwp-selection-value {
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
|
||||
padding-right: 16px;
|
||||
background-image: url('../images/icon-close.png');
|
||||
background-size: 12px 12px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: right center;
|
||||
}
|
||||
|
||||
/* Layout builder */
|
||||
|
||||
@media (max-width: 480px) {
|
||||
body .facetwp-template .fwpl-layout,
|
||||
body .facetwp-template-static .fwpl-layout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
BIN
wp/wp-content/plugins/facetwp/assets/images/checkbox-on.png
Normal file
|
After Width: | Height: | Size: 128 B |
BIN
wp/wp-content/plugins/facetwp/assets/images/checkbox.png
Normal file
|
After Width: | Height: | Size: 272 B |
BIN
wp/wp-content/plugins/facetwp/assets/images/icon-close.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
wp/wp-content/plugins/facetwp/assets/images/icon-locate.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
wp/wp-content/plugins/facetwp/assets/images/icon-search.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
wp/wp-content/plugins/facetwp/assets/images/loading.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
5
wp/wp-content/plugins/facetwp/assets/images/logo.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg width="100" height="78" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#ffffff">
|
||||
<path d="M99.991,26.908c0.017-0.176,0.009-0.353-0.02-0.527c-0.009-0.06-0.02-0.119-0.033-0.178c-0.05-0.213-0.122-0.422-0.235-0.616L89.269,7.73c-0.011-0.019-0.029-0.032-0.039-0.052c-0.011-0.014-0.014-0.032-0.021-0.047c-0.014-0.021-0.034-0.034-0.051-0.055c-0.033-0.048-0.074-0.092-0.111-0.138c-0.076-0.091-0.153-0.174-0.241-0.252c-0.048-0.042-0.095-0.083-0.145-0.12c-0.034-0.027-0.067-0.055-0.103-0.079c-0.097-0.064-0.2-0.111-0.31-0.159c-0.056-0.027-0.108-0.057-0.167-0.079c-0.041-0.015-0.082-0.031-0.124-0.044c-0.014-0.004-0.022-0.012-0.033-0.017L64.389,0.084C64.189,0.028,63.982,0,63.776,0H34.784c-0.216,0-0.431,0.032-0.638,0.093L11.637,6.698C11.626,6.7,11.617,6.71,11.606,6.713C11.571,6.724,11.535,6.736,11.5,6.75c-0.06,0.021-0.114,0.056-0.172,0.083c-0.111,0.052-0.22,0.104-0.32,0.171c-0.031,0.022-0.059,0.045-0.088,0.068c-0.052,0.038-0.098,0.083-0.146,0.126c-0.089,0.08-0.17,0.166-0.247,0.259c-0.035,0.045-0.074,0.084-0.106,0.132c-0.012,0.02-0.031,0.03-0.045,0.049c-0.012,0.019-0.015,0.04-0.025,0.059c-0.015,0.024-0.037,0.043-0.052,0.069L0.275,25.621c-0.106,0.187-0.171,0.387-0.22,0.593c-0.013,0.056-0.02,0.111-0.029,0.167C0,26.554-0.007,26.728,0.006,26.902c0.004,0.048,0.001,0.095,0.008,0.143c0.027,0.191,0.072,0.38,0.148,0.563c0.008,0.02,0.022,0.035,0.03,0.056c0.019,0.042,0.046,0.079,0.068,0.12c0.086,0.164,0.19,0.312,0.312,0.449c0.022,0.026,0.034,0.058,0.057,0.083l47.748,48.979c0.113,0.114,0.235,0.215,0.365,0.303c0.012,0.008,0.025,0.013,0.038,0.021c0.155,0.1,0.32,0.173,0.49,0.23c0.027,0.01,0.05,0.027,0.078,0.035c0.027,0.007,0.053,0.007,0.08,0.013c0.187,0.05,0.377,0.082,0.57,0.085c0,0,0.001,0,0.002,0l0,0l0,0l0,0l0,0c0,0,0.001,0,0.003,0c0.193-0.003,0.384-0.034,0.57-0.085c0.027-0.006,0.053-0.005,0.081-0.013c0.026-0.008,0.05-0.026,0.076-0.035c0.17-0.058,0.337-0.131,0.49-0.23c0.011-0.006,0.024-0.009,0.032-0.019c0.001,0,0.001,0,0.001,0c0.002,0,0.003-0.002,0.004-0.002c0.13-0.087,0.252-0.188,0.366-0.303l47.747-48.979c0.026-0.026,0.035-0.058,0.058-0.084c0.12-0.135,0.222-0.281,0.308-0.441c0.025-0.045,0.053-0.087,0.074-0.134c0.011-0.022,0.026-0.041,0.035-0.064c0.073-0.179,0.119-0.366,0.143-0.553C99.994,26.995,99.989,26.953,99.991,26.908z M81.532,12.666l-2.729,3.546L66.955,31.606l-12.8-12.06l13.981-3.514L81.532,12.666z M48.795,67.046c-3.293-7.789-8.291-19.604-11.321-26.771c-0.442-1.047-0.837-1.98-1.185-2.801c-0.032-0.079-0.066-0.156-0.097-0.23h27.611L53.173,62.381l-3.175,7.509C49.634,69.029,49.226,68.063,48.795,67.046z M43.744,20.945L32.985,31.577L18.17,12.713l26.968,6.854L43.744,20.945z M73.118,31.036l7.756-10.079l6.179-8.029l7.241,12.387L73.118,31.036z M35.109,4.539h28.354L78.49,8.756l-7.704,1.937l-21.198,5.328l-28.727-7.3L35.109,4.539z M9.674,31.099l21.464,5.834c0.108,0.255,0.24,0.568,0.404,0.958c1.244,2.941,4.275,10.111,11.682,27.625L9.674,31.099z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
BIN
wp/wp-content/plugins/facetwp/assets/images/radio-on.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
wp/wp-content/plugins/facetwp/assets/images/radio.png
Normal file
|
After Width: | Height: | Size: 957 B |
1684
wp/wp-content/plugins/facetwp/assets/js/dist/admin.min.js
vendored
Normal file
1
wp/wp-content/plugins/facetwp/assets/js/dist/front.min.js
vendored
Normal file
104
wp/wp-content/plugins/facetwp/assets/js/src/accessibility.js
Normal file
@@ -0,0 +1,104 @@
|
||||
(function($) {
|
||||
var last_checked = null;
|
||||
|
||||
if ('undefined' !== typeof FWP.hooks) {
|
||||
FWP.hooks.addAction('facetwp/loaded', function() {
|
||||
|
||||
// checkbox, radio, fselect
|
||||
$('.facetwp-checkbox, .facetwp-radio, .fs-option').each(function() {
|
||||
let $el = $(this);
|
||||
if (! $el.hasClass('disabled')) {
|
||||
$el.attr('role', 'checkbox');
|
||||
$el.attr('aria-checked', $el.hasClass('checked') ? 'true' : 'false');
|
||||
$el.attr('aria-label', $el.text());
|
||||
$el.attr('tabindex', 0);
|
||||
}
|
||||
});
|
||||
|
||||
// pager, show more, user selections, hierarchy
|
||||
$('.facetwp-page, .facetwp-toggle, .facetwp-selection-value, .facetwp-link').each(function() {
|
||||
let $el = $(this);
|
||||
let label = $el.text();
|
||||
|
||||
if ($el.hasClass('facetwp-page')) {
|
||||
label = FWP_JSON.a11y.label_page + ' ' + label;
|
||||
|
||||
if ($el.hasClass('next')) {
|
||||
label = FWP_JSON.a11y.label_page_next;
|
||||
}
|
||||
else if ($el.hasClass('prev')) {
|
||||
label = FWP_JSON.a11y.label_page_prev;
|
||||
}
|
||||
}
|
||||
|
||||
$el.attr('role', 'link');
|
||||
$el.attr('aria-label', label);
|
||||
$el.attr('tabindex', 0);
|
||||
});
|
||||
|
||||
// dropdown, sort facet, old sort feature
|
||||
$('.facetwp-type-dropdown select, .facetwp-type-sort select, .facetwp-sort-select select').each(function() {
|
||||
$(this).attr('aria-label', $(this).find('option:selected').text());
|
||||
});
|
||||
|
||||
// search, date
|
||||
$('.facetwp-search, .facetwp-date').each(function() {
|
||||
$(this).attr('aria-label', $(this).attr('placeholder'));
|
||||
});
|
||||
|
||||
// checkbox group
|
||||
$('.facetwp-type-checkboxes').each(function() {
|
||||
let facet_name = $(this).attr('data-name');
|
||||
$(this).attr('aria-label', FWP.settings.labels[facet_name]);
|
||||
$(this).attr('role', 'group');
|
||||
});
|
||||
|
||||
// fselect
|
||||
$('.fs-wrap').each(function() {
|
||||
$(this).attr('role', 'button');
|
||||
$(this).attr('aria-haspopup', 'true');
|
||||
$(this).attr('aria-expanded', $(this).hasClass('fs-open') ? 'true' : 'false');
|
||||
});
|
||||
|
||||
$('.facetwp-type-fselect .facetwp-dropdown').attr('aria-hidden', 'true');
|
||||
|
||||
// pager
|
||||
$('.facetwp-pager').attr('role', 'navigation');
|
||||
$('.facetwp-page.active').attr('aria-current', 'true');
|
||||
|
||||
// focus on selection
|
||||
if (null != last_checked) {
|
||||
var $el = $('.facetwp-facet [data-value="' + last_checked + '"]');
|
||||
if ($el.len()) {
|
||||
$el.nodes[0].focus();
|
||||
}
|
||||
last_checked = null;
|
||||
}
|
||||
}, 999);
|
||||
}
|
||||
|
||||
// keyboard support
|
||||
$().on('keydown', '.facetwp-checkbox, .facetwp-radio, .facetwp-link', function(e) {
|
||||
if (32 == e.keyCode || 13 == e.keyCode) {
|
||||
last_checked = $(this).attr('data-value');
|
||||
e.preventDefault();
|
||||
this.click();
|
||||
}
|
||||
});
|
||||
|
||||
$().on('keydown', '.facetwp-page, .facetwp-toggle, .facetwp-selection-value', function(e) {
|
||||
if (32 == e.keyCode || 13 == e.keyCode) {
|
||||
e.preventDefault();
|
||||
this.click();
|
||||
}
|
||||
});
|
||||
|
||||
// fselect - determine "aria-expanded"
|
||||
function toggleExpanded(e) {
|
||||
var $fs = $(e.detail[0]);
|
||||
$fs.attr('aria-expanded', $fs.hasClass('fs-open') ? 'true' : 'false');
|
||||
}
|
||||
|
||||
$().on('fs:opened', toggleExpanded);
|
||||
$().on('fs:closed', toggleExpanded);
|
||||
})(fUtil);
|
||||
2189
wp/wp-content/plugins/facetwp/assets/js/src/admin.js
Normal file
10
wp/wp-content/plugins/facetwp/assets/js/src/deprecated.js
Normal file
@@ -0,0 +1,10 @@
|
||||
FWP.deprecated = (old_method, new_method, ...args) => {
|
||||
console.warn('FWP.' + old_method + '() has changed to FWP.' + new_method + '()');
|
||||
return FWP[new_method](...args);
|
||||
};
|
||||
FWP.build_post_data = (...args) => FWP.deprecated('build_post_data', 'buildPostData', ...args);
|
||||
FWP.build_query_string = (...args) => FWP.deprecated('build_query_string', 'buildQueryString', ...args);
|
||||
FWP.fetch_data = (...args) => FWP.deprecated('fetch_data', 'fetchData', ...args);
|
||||
FWP.load_from_hash = (...args) => FWP.deprecated('load_from_hash', 'loadFromHash', ...args);
|
||||
FWP.parse_facets = (...args) => FWP.deprecated('parse_facets', 'parseFacets', ...args);
|
||||
FWP.set_hash = (...args) => FWP.deprecated('set_hash', 'setHash', ...args);
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* WP-JS-Hooks
|
||||
* @version 1.0.0
|
||||
* @author Carl Danley & 10up
|
||||
*/
|
||||
!function(t,n){"use strict";t.FWP=t.FWP||{},t.FWP.hooks=t.FWP.hooks||new function(){function t(t,n,r,i){var e,o,c;if(f[t][n])if(r)if(e=f[t][n],i)for(c=e.length;c--;)(o=e[c]).callback===r&&o.context===i&&e.splice(c,1);else for(c=e.length;c--;)e[c].callback===r&&e.splice(c,1);else f[t][n]=[]}function n(t,n,i,e,o){var c={callback:i,priority:e,context:o},l=f[t][n];l?(l.push(c),l=r(l)):l=[c],f[t][n]=l}function r(t){for(var n,r,i,e=1,o=t.length;e<o;e++){for(n=t[e],r=e;(i=t[r-1])&&i.priority>n.priority;)t[r]=t[r-1],--r;t[r]=n}return t}function i(t,n,r){var i,e,o=f[t][n];if(!o)return"filters"===t&&r[0];if(e=o.length,"filters"===t)for(i=0;i<e;i++)r[0]=o[i].callback.apply(o[i].context,r);else for(i=0;i<e;i++)o[i].callback.apply(o[i].context,r);return"filters"!==t||r[0]}var e=Array.prototype.slice,o={removeFilter:function(n,r){return"string"==typeof n&&t("filters",n,r),o},applyFilters:function(){var t=e.call(arguments),n=t.shift();return"string"==typeof n?i("filters",n,t):o},addFilter:function(t,r,i,e){return"string"==typeof t&&"function"==typeof r&&n("filters",t,r,i=parseInt(i||10,10),e),o},removeAction:function(n,r){return"string"==typeof n&&t("actions",n,r),o},doAction:function(){var t=e.call(arguments),n=t.shift();return"string"==typeof n&&i("actions",n,t),o},addAction:function(t,r,i,e){return"string"==typeof t&&"function"==typeof r&&n("actions",t,r,i=parseInt(i||10,10),e),o}},f={actions:{},filters:{}};return o}}(window);
|
||||
1013
wp/wp-content/plugins/facetwp/assets/js/src/front-facets.js
Normal file
659
wp/wp-content/plugins/facetwp/assets/js/src/front.js
Normal file
@@ -0,0 +1,659 @@
|
||||
window.FWP = (($) => {
|
||||
|
||||
class FacetWP {
|
||||
constructor() {
|
||||
this.import();
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
import() {
|
||||
if ('undefined' !== typeof FWP) {
|
||||
$.each(FWP, (val, key) => this[key] = val);
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
var FWP = this;
|
||||
|
||||
this.setDefaults();
|
||||
|
||||
if (0 < $('.facetwp-sort').len()) {
|
||||
FWP.extras.sort = 'default';
|
||||
}
|
||||
|
||||
if (0 < $('.facetwp-pager').len()) {
|
||||
FWP.extras.pager = true;
|
||||
}
|
||||
|
||||
if (0 < $('.facetwp-per-page').len()) {
|
||||
FWP.extras.per_page = 'default';
|
||||
}
|
||||
|
||||
if (0 < $('.facetwp-counts').len()) {
|
||||
FWP.extras.counts = true;
|
||||
}
|
||||
|
||||
if (0 < $('.facetwp-selections').len()) {
|
||||
FWP.extras.selections = true;
|
||||
}
|
||||
|
||||
// Make sure there's a template
|
||||
var has_template = $('.facetwp-template').len() > 0;
|
||||
|
||||
if (! has_template) {
|
||||
var has_loop = FWP.helper.detectLoop(document.body);
|
||||
|
||||
if (has_loop) {
|
||||
$(has_loop).addClass('facetwp-template');
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var $div = $('.facetwp-template').first();
|
||||
FWP.template = $div.attr('data-name') ? $div.attr('data-name') : 'wp';
|
||||
|
||||
// Facets inside the template?
|
||||
if ($div.find('.facetwp-facet').len() > 0) {
|
||||
console.error('Facets should not be inside the "facetwp-template" container');
|
||||
}
|
||||
|
||||
FWP.hooks.doAction('facetwp/ready');
|
||||
|
||||
// Generate the user selections
|
||||
if (FWP.extras.selections) {
|
||||
FWP.hooks.addAction('facetwp/loaded', () => {
|
||||
|
||||
var selections = '';
|
||||
var skipped = ['pager', 'reset', 'sort'];
|
||||
|
||||
$.each(FWP.facets, (val, key) => {
|
||||
if (val.length < 1 || ! $.isset(FWP.settings.labels[key]) || skipped.includes(FWP.facet_type[key])) {
|
||||
return true; // skip facet
|
||||
}
|
||||
|
||||
var choices = val;
|
||||
var $el = $('.facetwp-facet-' + key);
|
||||
var facet_type = $el.attr('data-ui') || $el.attr('data-type');
|
||||
choices = FWP.hooks.applyFilters('facetwp/selections/' + facet_type, choices, {
|
||||
'el': $el,
|
||||
'selected_values': choices
|
||||
});
|
||||
|
||||
if (choices.length) {
|
||||
if ('string' === typeof choices) {
|
||||
choices = [{ value: '', label: choices }];
|
||||
}
|
||||
else if (! $.isset(choices[0].label)) {
|
||||
choices = [{ value: '', label: choices[0] }];
|
||||
}
|
||||
}
|
||||
|
||||
var values = '';
|
||||
$.each(choices, (choice) => {
|
||||
values += '<span class="facetwp-selection-value" data-value="' + choice.value + '">' + FWP.helper.escapeHtml(choice.label) + '</span>';
|
||||
});
|
||||
|
||||
selections += '<li data-facet="' + key + '"><span class="facetwp-selection-label">' + FWP.settings.labels[key] + ':</span> ' + values + '</li>';
|
||||
});
|
||||
|
||||
if ('' !== selections) {
|
||||
selections = '<ul>' + selections + '</ul>';
|
||||
}
|
||||
|
||||
$('.facetwp-selections').html(selections);
|
||||
});
|
||||
}
|
||||
|
||||
FWP.refresh();
|
||||
}
|
||||
|
||||
setDefaults() {
|
||||
let defaults = {
|
||||
'facets': {},
|
||||
'template': null,
|
||||
'settings': {},
|
||||
'is_reset': false,
|
||||
'is_refresh': false,
|
||||
'is_bfcache': false,
|
||||
'is_hash_click': false,
|
||||
'is_load_more': false,
|
||||
'auto_refresh': true,
|
||||
'soft_refresh': false,
|
||||
'frozen_facets': {},
|
||||
'active_facet': null,
|
||||
'facet_type': {},
|
||||
'loaded': false,
|
||||
'extras': {},
|
||||
'paged': 1
|
||||
};
|
||||
|
||||
for (var prop in defaults) {
|
||||
if (!$.isset(this[prop])) {
|
||||
this[prop] = defaults[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refresh() {
|
||||
FWP.is_refresh = true;
|
||||
|
||||
// Add the loading overlay
|
||||
FWP.toggleOverlay('on');
|
||||
|
||||
// Load facet DOM values
|
||||
if (! FWP.is_reset) {
|
||||
FWP.parseFacets();
|
||||
}
|
||||
|
||||
// Check the URL on pageload
|
||||
if (! FWP.loaded) {
|
||||
FWP.loadFromHash();
|
||||
}
|
||||
|
||||
// Fire a notification event
|
||||
$().trigger('facetwp-refresh');
|
||||
|
||||
// Trigger window.onpopstate
|
||||
if (FWP.loaded && ! FWP.is_popstate && ! FWP.is_load_more) {
|
||||
FWP.setHash();
|
||||
}
|
||||
|
||||
// Preload?
|
||||
if (! FWP.loaded && ! FWP.is_bfcache && $.isset(FWP_JSON.preload_data)) {
|
||||
FWP.render(FWP_JSON.preload_data);
|
||||
}
|
||||
else {
|
||||
FWP.fetchData();
|
||||
}
|
||||
|
||||
// Unfreeze any soft-frozen facets
|
||||
$.each(FWP.frozen_facets, (type, name) => {
|
||||
if ('hard' !== type) {
|
||||
delete FWP.frozen_facets[name];
|
||||
}
|
||||
});
|
||||
|
||||
// Cleanup
|
||||
FWP.paged = 1;
|
||||
FWP.soft_refresh = false;
|
||||
FWP.is_refresh = false;
|
||||
FWP.is_reset = false;
|
||||
}
|
||||
|
||||
autoload() {
|
||||
if (FWP.auto_refresh && ! FWP.is_refresh) {
|
||||
FWP.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
parseFacets() {
|
||||
FWP.facets = {};
|
||||
|
||||
$('.facetwp-facet').each(function() {
|
||||
var $this = $(this);
|
||||
var facet_name = $this.attr('data-name');
|
||||
var facet_type = $this.attr('data-type');
|
||||
var is_ignored = $this.hasClass('facetwp-ignore');
|
||||
|
||||
if (null !== $this.attr('data-ui')) {
|
||||
facet_type = $this.attr('data-ui');
|
||||
}
|
||||
|
||||
// Store the facet type
|
||||
FWP.facet_type[facet_name] = facet_type;
|
||||
|
||||
// Plugin hook
|
||||
if (! is_ignored) {
|
||||
FWP.hooks.doAction('facetwp/refresh/' + facet_type, $this, facet_name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
buildQueryString() {
|
||||
var query_string = '';
|
||||
|
||||
// Non-FacetWP URL variables
|
||||
var hash = [];
|
||||
var get_str = window.location.search.replace('?', '').split('&');
|
||||
$.each(get_str, (val) => {
|
||||
var param_name = val.split('=')[0];
|
||||
if (0 !== param_name.indexOf(FWP_JSON.prefix)) {
|
||||
hash.push(val);
|
||||
}
|
||||
});
|
||||
hash = hash.join('&');
|
||||
|
||||
// FacetWP URL variables
|
||||
var fwp_vars = Object.assign({}, FWP.facets);
|
||||
|
||||
// Add pagination to the URL hash
|
||||
if (1 < FWP.paged) {
|
||||
fwp_vars['paged'] = FWP.paged;
|
||||
}
|
||||
|
||||
// Add "per page" to the URL hash
|
||||
if (FWP.extras.per_page && 'default' !== FWP.extras.per_page) {
|
||||
fwp_vars['per_page'] = FWP.extras.per_page;
|
||||
}
|
||||
|
||||
// Add sorting to the URL hash
|
||||
if (FWP.extras.sort && 'default' !== FWP.extras.sort) {
|
||||
fwp_vars['sort'] = FWP.extras.sort;
|
||||
}
|
||||
|
||||
fwp_vars = FWP.helper.serialize(fwp_vars, FWP_JSON.prefix);
|
||||
|
||||
if ('' !== hash) {
|
||||
query_string += hash;
|
||||
}
|
||||
if ('' !== fwp_vars) {
|
||||
query_string += ('' !== hash ? '&' : '') + fwp_vars;
|
||||
}
|
||||
|
||||
return query_string;
|
||||
}
|
||||
|
||||
setHash() {
|
||||
var query_string = FWP.buildQueryString();
|
||||
|
||||
if ('' !== query_string) {
|
||||
query_string = '?' + query_string;
|
||||
}
|
||||
|
||||
if (history.pushState) {
|
||||
history.pushState(null, null, window.location.pathname + query_string);
|
||||
}
|
||||
|
||||
// Update FWP_HTTP.get
|
||||
FWP_HTTP.get = {};
|
||||
window.location.search.replace('?', '').split('&').forEach((el) => {
|
||||
var item = el.split('=');
|
||||
|
||||
if ('' != item[0]) {
|
||||
FWP_HTTP.get[item[0]] = item[1];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
loadFromHash() {
|
||||
var hash = [];
|
||||
var get_str = window.location.search.replace('?', '').split('&');
|
||||
$.each(get_str, (val) => {
|
||||
var param_name = val.split('=')[0];
|
||||
if (0 === param_name.indexOf(FWP_JSON.prefix)) {
|
||||
hash.push(val.replace(FWP_JSON.prefix, ''));
|
||||
}
|
||||
});
|
||||
hash = hash.join('&');
|
||||
|
||||
// Reset facet values
|
||||
$.each(FWP.facets, (val, key) => {
|
||||
FWP.facets[key] = [];
|
||||
});
|
||||
|
||||
FWP.paged = 1;
|
||||
FWP.extras.sort = 'default';
|
||||
|
||||
if ('' !== hash) {
|
||||
hash = hash.split('&');
|
||||
$.each(hash, (chunk) => {
|
||||
var obj = chunk.split('=')[0];
|
||||
var val = chunk.split('=')[1];
|
||||
|
||||
if ('paged' === obj) {
|
||||
FWP.paged = val;
|
||||
}
|
||||
else if ('per_page' === obj || 'sort' === obj) {
|
||||
FWP.extras[obj] = val;
|
||||
}
|
||||
else if ('' !== val) {
|
||||
var type = $.isset(FWP.facet_type[obj]) ? FWP.facet_type[obj] : '';
|
||||
if ('search' === type || 'autocomplete' === type) {
|
||||
FWP.facets[obj] = decodeURIComponent(val);
|
||||
}
|
||||
else {
|
||||
FWP.facets[obj] = decodeURIComponent(val).split(',');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
buildPostData() {
|
||||
return {
|
||||
'facets': FWP.facets,
|
||||
'frozen_facets': FWP.frozen_facets,
|
||||
'http_params': FWP_HTTP,
|
||||
'template': FWP.template,
|
||||
'extras': FWP.extras,
|
||||
'soft_refresh': FWP.soft_refresh ? 1 : 0,
|
||||
'is_bfcache': FWP.is_bfcache ? 1 : 0,
|
||||
'first_load': FWP.loaded ? 0 : 1,
|
||||
'paged': FWP.paged
|
||||
};
|
||||
}
|
||||
|
||||
fetchData() {
|
||||
var endpoint = ('wp' === FWP.template) ? document.URL : FWP_JSON.ajaxurl;
|
||||
var data = {
|
||||
action: 'facetwp_refresh',
|
||||
data: FWP.buildPostData()
|
||||
};
|
||||
|
||||
var settings = {
|
||||
dataType: 'text', // better JSON error handling
|
||||
done: (resp) => {
|
||||
try {
|
||||
var json = JSON.parse(resp);
|
||||
FWP.render(json);
|
||||
}
|
||||
catch(e) {
|
||||
var pos = resp.indexOf('{"facets');
|
||||
if (-1 < pos) {
|
||||
var json = JSON.parse(resp.substr(pos));
|
||||
FWP.render(json);
|
||||
}
|
||||
else {
|
||||
$('.facetwp-template').text('FacetWP was unable to auto-detect the post listing');
|
||||
console.log(resp);
|
||||
}
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
settings = FWP.hooks.applyFilters('facetwp/ajax_settings', settings);
|
||||
|
||||
$.post(endpoint, data, settings);
|
||||
}
|
||||
|
||||
render(response) {
|
||||
FWP.response = response;
|
||||
|
||||
// Don't render CSS-based (or empty) templates on pageload
|
||||
// The template has already been pre-loaded
|
||||
if (('wp' === FWP.template || '' === response.template) && ! FWP.loaded && ! FWP.is_bfcache) {
|
||||
var inject = false;
|
||||
}
|
||||
else {
|
||||
var inject = response.template;
|
||||
|
||||
if ('wp' === FWP.template) {
|
||||
var obj = $(response.template);
|
||||
var $tpl = obj.find('.facetwp-template');
|
||||
|
||||
if ($tpl.len() < 1) {
|
||||
var loop = FWP.helper.detectLoop(obj.nodes[0]);
|
||||
|
||||
if (loop) {
|
||||
$tpl = $(loop).addClass('facetwp-template');
|
||||
}
|
||||
}
|
||||
|
||||
if ($tpl.len() > 0) {
|
||||
var inject = $tpl.html();
|
||||
}
|
||||
else {
|
||||
// Fallback until "loop_no_results" action is added to WP core
|
||||
var inject = FWP_JSON['no_results_text'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (false !== inject) {
|
||||
if (! FWP.hooks.applyFilters('facetwp/template_html', false, { 'response': response, 'html': inject })) {
|
||||
$('.facetwp-template').html(inject);
|
||||
}
|
||||
}
|
||||
|
||||
// Populate each facet box
|
||||
$.each(response.facets, (val, name) => {
|
||||
$('.facetwp-facet-' + name).html(val);
|
||||
});
|
||||
|
||||
// Populate the counts
|
||||
if ($.isset(response.counts)) {
|
||||
$('.facetwp-counts').html(response.counts);
|
||||
}
|
||||
|
||||
// Populate the pager
|
||||
if ($.isset(response.pager)) {
|
||||
$('.facetwp-pager').html(response.pager);
|
||||
}
|
||||
|
||||
// Populate the "per page" box
|
||||
if ($.isset(response.per_page)) {
|
||||
$('.facetwp-per-page').html(response.per_page);
|
||||
if ('default' !== FWP.extras.per_page) {
|
||||
$('.facetwp-per-page-select').val(FWP.extras.per_page);
|
||||
}
|
||||
}
|
||||
|
||||
// Populate the sort box
|
||||
if ($.isset(response.sort)) {
|
||||
$('.facetwp-sort').html(response.sort);
|
||||
$('.facetwp-sort-select').val(FWP.extras.sort);
|
||||
}
|
||||
|
||||
// Populate the settings object (iterate to preserve static facet settings)
|
||||
$.each(response.settings, (val, key) => {
|
||||
FWP.settings[key] = val;
|
||||
});
|
||||
|
||||
// WP Playlist support
|
||||
if ('function' === typeof WPPlaylistView) {
|
||||
$('.facetwp-template .wp-playlist').each((item) => {
|
||||
return new WPPlaylistView({ el: item });
|
||||
});
|
||||
}
|
||||
|
||||
// Fire a notification event
|
||||
$().trigger('facetwp-loaded');
|
||||
|
||||
// Allow final actions
|
||||
FWP.hooks.doAction('facetwp/loaded');
|
||||
|
||||
// Remove the loading overlay
|
||||
FWP.toggleOverlay('off');
|
||||
|
||||
// Clear the active facet
|
||||
FWP.active_facet = null;
|
||||
|
||||
// Detect "back-forward" cache
|
||||
FWP.is_bfcache = true;
|
||||
|
||||
// Done loading?
|
||||
FWP.loaded = true;
|
||||
}
|
||||
|
||||
reset(facets) {
|
||||
FWP.parseFacets();
|
||||
|
||||
var opts = {};
|
||||
|
||||
if ('string' === typeof facets) {
|
||||
opts[facets] = '';
|
||||
}
|
||||
else if (Array.isArray(facets)) {
|
||||
$.each(facets, (facet_name) => {
|
||||
opts[facet_name] = '';
|
||||
});
|
||||
}
|
||||
else if ('object' === typeof facets && !! facets) {
|
||||
opts = facets;
|
||||
}
|
||||
|
||||
var reset_all = Object.keys(opts).length < 1;
|
||||
|
||||
$.each(FWP.facets, (vals, facet_name) => {
|
||||
var has_reset = $.isset(opts[facet_name]);
|
||||
var selected_vals = Array.isArray(vals) ? vals : [vals];
|
||||
|
||||
if (has_reset && -1 < selected_vals.indexOf(opts[facet_name])) {
|
||||
var pos = selected_vals.indexOf(opts[facet_name]);
|
||||
selected_vals.splice(pos, 1); // splice() is mutable!
|
||||
FWP.facets[facet_name] = selected_vals;
|
||||
}
|
||||
|
||||
if (has_reset && (selected_vals.length < 1 || '' === opts[facet_name])) {
|
||||
delete FWP.frozen_facets[facet_name];
|
||||
}
|
||||
|
||||
if (reset_all || (has_reset && '' === opts[facet_name])) {
|
||||
FWP.facets[facet_name] = [];
|
||||
}
|
||||
});
|
||||
|
||||
if (reset_all) {
|
||||
FWP.extras.per_page = 'default';
|
||||
FWP.extras.sort = 'default';
|
||||
FWP.frozen_facets = {};
|
||||
}
|
||||
|
||||
FWP.hooks.doAction('facetwp/reset');
|
||||
FWP.is_reset = true;
|
||||
FWP.refresh();
|
||||
}
|
||||
|
||||
toggleOverlay(which) {
|
||||
var method = ('on' === which) ? 'addClass' : 'removeClass';
|
||||
$('.facetwp-facet')[method]('is-loading');
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
window.addEventListener('popstate', () => {
|
||||
|
||||
// Detect browser "back-foward" cache
|
||||
if (FWP.is_bfcache) {
|
||||
FWP.loaded = false;
|
||||
}
|
||||
|
||||
if ((FWP.loaded || FWP.is_bfcache) && ! FWP.is_refresh && ! FWP.is_hash_click) {
|
||||
FWP.is_popstate = true;
|
||||
FWP.refresh();
|
||||
FWP.is_popstate = false;
|
||||
}
|
||||
|
||||
FWP.is_hash_click = false;
|
||||
});
|
||||
|
||||
// Prevent hash clicks from triggering a refresh
|
||||
$().on('click', 'a[href^="#"]', () => {
|
||||
FWP.is_hash_click = true;
|
||||
});
|
||||
|
||||
// Click on a user selection
|
||||
$().on('click', '.facetwp-selections .facetwp-selection-value', function() {
|
||||
if (FWP.is_refresh) {
|
||||
return;
|
||||
}
|
||||
|
||||
var facet_name = $(this).closest('li').attr('data-facet');
|
||||
var facet_value = $(this).attr('data-value');
|
||||
|
||||
if ('' != facet_value) {
|
||||
var obj = {};
|
||||
obj[facet_name] = facet_value;
|
||||
FWP.reset(obj);
|
||||
}
|
||||
else {
|
||||
FWP.reset(facet_name);
|
||||
}
|
||||
});
|
||||
|
||||
// Pagination
|
||||
$().on('click', '.facetwp-page[data-page]', function() {
|
||||
$('.facetwp-page').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
|
||||
FWP.paged = $(this).attr('data-page');
|
||||
FWP.soft_refresh = true;
|
||||
FWP.refresh();
|
||||
});
|
||||
|
||||
// Use jQuery if available for select2
|
||||
var $f = ('function' === typeof jQuery) ? jQuery : fUtil;
|
||||
|
||||
// Per page
|
||||
$f(document).on('change', '.facetwp-per-page-select', function() {
|
||||
FWP.extras.per_page = $(this).val();
|
||||
FWP.soft_refresh = true;
|
||||
FWP.autoload();
|
||||
});
|
||||
|
||||
// Sorting
|
||||
$f(document).on('change', '.facetwp-sort-select', function() {
|
||||
FWP.extras.sort = $(this).val();
|
||||
FWP.soft_refresh = true;
|
||||
FWP.autoload();
|
||||
});
|
||||
|
||||
$f(() => {
|
||||
this.init();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
FacetWP.prototype.helper = {
|
||||
getUrlVar: (name) => {
|
||||
var name = FWP_JSON.prefix + name;
|
||||
var url_vars = window.location.search.replace('?', '').split('&');
|
||||
for (var i = 0; i < url_vars.length; i++) {
|
||||
var item = url_vars[i].split('=');
|
||||
if (item[0] === name) {
|
||||
return item[1];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
debounce: (func, wait) => {
|
||||
var timeout;
|
||||
return function(...args) {
|
||||
var boundFunc = func.bind(this, ...args);
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(boundFunc, wait);
|
||||
};
|
||||
},
|
||||
serialize: (obj, prefix) => {
|
||||
var str = [];
|
||||
var prefix = $.isset(prefix) ? prefix : '';
|
||||
for (var p in obj) {
|
||||
if ('' != obj[p]) { // Needs to be "!=" instead of "!=="
|
||||
str.push(prefix + encodeURIComponent(p) + '=' + encodeURIComponent(obj[p]));
|
||||
}
|
||||
}
|
||||
return str.join('&');
|
||||
},
|
||||
escapeHtml: (text) => {
|
||||
var map = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": '''
|
||||
};
|
||||
return text.replace(/[&<>"']/g,(m) => map[m]).trim();
|
||||
},
|
||||
detectLoop: (node) => {
|
||||
var curNode = null;
|
||||
var iterator = document.createNodeIterator(node, NodeFilter.SHOW_COMMENT, () => {
|
||||
return NodeFilter.FILTER_ACCEPT; /* IE expects a function */
|
||||
}, false);
|
||||
|
||||
while (curNode = iterator.nextNode()) {
|
||||
if (8 === curNode.nodeType && 'fwp-loop' === curNode.nodeValue) {
|
||||
return curNode.parentNode;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
return new FacetWP();
|
||||
|
||||
})(fUtil);
|
||||
1
wp/wp-content/plugins/facetwp/assets/js/src/sample.json
Normal file
@@ -0,0 +1 @@
|
||||
{"facets":[{"name":"categories","label":"Categories","type":"checkboxes","source":"tax/category","parent_term":"","hierarchical":"no","show_expanded":"no","ghosts":"no","preserve_ghosts":"no","operator":"and","orderby":"count","count":"10","soft_limit":"5"}],"templates":[{"name":"blog_posts","label":"Blog posts","query":"","template":"","layout":{"items":[{"type":"row","items":[{"type":"col","items":[{"type":"item","source":"post_title","settings":{"link":{"type":"post","href":"","target":""},"prefix":"","suffix":"","border":{"style":"none","color":"","width":{"unit":"px","top":0,"right":0,"bottom":0,"left":0}},"background_color":"","padding":{"unit":"px","top":0,"right":0,"bottom":0,"left":0},"text_color":"","text_style":{"align":"","bold":false,"italic":false},"font_size":{"unit":"px","size":0},"name":"el-fz703r","css_class":"","is_hidden":""}}],"settings":{"border":{"style":"none","color":"","width":{"unit":"px","top":0,"right":0,"bottom":0,"left":0}},"background_color":"","padding":{"unit":"px","top":0,"right":0,"bottom":0,"left":0},"text_color":"","text_style":{"align":"","bold":false,"italic":false},"font_size":{"unit":"px","size":0},"name":"el-1tvcxf","css_class":""}}],"settings":{"grid_template_columns":"1fr","border":{"style":"none","color":"","width":{"unit":"px","top":0,"right":0,"bottom":0,"left":0}},"background_color":"","padding":{"unit":"px","top":0,"right":0,"bottom":0,"left":0},"text_color":"","text_style":{"align":"","bold":false,"italic":false},"font_size":{"unit":"px","size":0},"name":"el-8cjrpw","css_class":""}}],"settings":{"num_columns":1,"grid_gap":10,"border":{"style":"none","color":"","width":{"unit":"px","top":0,"right":0,"bottom":0,"left":0}},"background_color":"","padding":{"unit":"px","top":0,"right":0,"bottom":0,"left":0},"text_color":"","text_style":{"align":"","bold":false,"italic":false},"font_size":{"unit":"px","size":0},"name":"el-hkhimk","css_class":"","custom_css":""}},"query_obj":{"post_type":[{"label":"Posts","value":"post"}],"posts_per_page":10,"orderby":[{"key":"date","order":"DESC","type":"CHAR"}],"filters":[]},"modes":{"display":"visual","query":"visual"}}],"settings":{"thousands_separator":",","decimal_separator":".","prefix":"_"}}
|
||||
29
wp/wp-content/plugins/facetwp/assets/vendor/fComplete/fComplete.css
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
.fcomplete-wrap {
|
||||
position: absolute;
|
||||
border: 1px solid #ddd;
|
||||
border-top: none;
|
||||
background-color: #fff;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.fcomplete-result,
|
||||
.fcomplete-status {
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.fcomplete-result {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.fcomplete-result:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.fcomplete-status {
|
||||
font-size: 13px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.fcomplete-hidden {
|
||||
display: none;
|
||||
}
|
||||
283
wp/wp-content/plugins/facetwp/assets/vendor/fComplete/fComplete.js
vendored
Normal file
@@ -0,0 +1,283 @@
|
||||
window.fComplete = (() => {
|
||||
|
||||
class fComplete {
|
||||
|
||||
constructor(selector, options) {
|
||||
let that = this;
|
||||
|
||||
var defaults = {
|
||||
data: [],
|
||||
minChars: 3,
|
||||
maxResults: 10,
|
||||
searchDelay: 200,
|
||||
loadingText: 'Loading...',
|
||||
minCharsText: 'Enter {n} or more characters',
|
||||
noResultsText: 'No results',
|
||||
beforeRender: null,
|
||||
onSelect: null
|
||||
};
|
||||
|
||||
that.settings = Object.assign({}, defaults, options);
|
||||
that.settings.minChars = Math.max(1, that.settings.minChars);
|
||||
that.settings.maxResults = Math.max(1, that.settings.maxResults);
|
||||
that.settings.searchDelay = Math.max(0, that.settings.searchDelay);
|
||||
|
||||
if ('string' === typeof selector) {
|
||||
var nodes = Array.from(document.querySelectorAll(selector));
|
||||
}
|
||||
else if (selector instanceof Node) {
|
||||
var nodes = [selector];
|
||||
}
|
||||
else if (Array.isArray(selector)) {
|
||||
var nodes = selector;
|
||||
}
|
||||
else {
|
||||
var nodes = [];
|
||||
}
|
||||
|
||||
if ('undefined' === typeof window.fCompleteInit) {
|
||||
window.fCompleteInit = {
|
||||
lastFocus: null,
|
||||
eventsBound: true
|
||||
};
|
||||
that.bindEvents();
|
||||
}
|
||||
|
||||
nodes.forEach((input) => {
|
||||
that.input = input;
|
||||
input.fcomplete = that;
|
||||
input.classList.add('fcomplete-enabled');
|
||||
that.create();
|
||||
});
|
||||
}
|
||||
|
||||
create() {
|
||||
var that = this;
|
||||
|
||||
var html = `
|
||||
<div class="fcomplete-wrap fcomplete-hidden">
|
||||
<div class="fcomplete-status"></div>
|
||||
<div class="fcomplete-results"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
var rect = that.input.getBoundingClientRect();
|
||||
|
||||
var tpl = document.createElement('template');
|
||||
tpl.innerHTML = html;
|
||||
var wrap = tpl.content.querySelector('.fcomplete-wrap');
|
||||
wrap.style.minWidth = rect.width + 'px';
|
||||
that.input.parentNode.insertBefore(wrap, that.input.nextSibling);
|
||||
|
||||
// add a relationship link
|
||||
that.input._rel = wrap;
|
||||
wrap._rel = that.input;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.input._rel.remove();
|
||||
delete this.input._rel;
|
||||
}
|
||||
|
||||
reload() {
|
||||
this.destroy();
|
||||
this.create();
|
||||
}
|
||||
|
||||
open() {
|
||||
this.input._rel.classList.remove('fcomplete-hidden');
|
||||
}
|
||||
|
||||
close() {
|
||||
window.fCompleteInit.lastFocus = null;
|
||||
this.input._rel.classList.add('fcomplete-hidden');
|
||||
}
|
||||
|
||||
setStatus(text) {
|
||||
var text = text.replace('{n}', this.settings.minChars);
|
||||
var node = this.input._rel.querySelector('.fcomplete-status');
|
||||
node.innerHTML = text;
|
||||
|
||||
var method = (text) ? 'remove' : 'add';
|
||||
node.classList[method]('fcomplete-hidden');
|
||||
}
|
||||
|
||||
render(data) {
|
||||
var data = (this.settings.beforeRender) ? this.settings.beforeRender(data) : data;
|
||||
var wrap = this.input._rel;
|
||||
|
||||
if (data.length) {
|
||||
var html = '';
|
||||
var len = Math.min(data.length, this.settings.maxResults);
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
html += `<div class="fcomplete-result" data-value="${data[i].value}" tabindex="-1">${data[i].label}</div>`;
|
||||
}
|
||||
|
||||
wrap.querySelector('.fcomplete-results').innerHTML = html;
|
||||
this.setStatus('');
|
||||
}
|
||||
else {
|
||||
wrap.querySelector('.fcomplete-results').innerHTML = '';
|
||||
this.setStatus(this.settings.noResultsText);
|
||||
}
|
||||
|
||||
this.input.fcomplete.open();
|
||||
}
|
||||
|
||||
getAdjacentSibling(which) {
|
||||
var that = this;
|
||||
var which = which || 'next';
|
||||
var sibling = window.fCompleteInit.lastFocus;
|
||||
var selector = '.fcomplete-result';
|
||||
|
||||
if (sibling) {
|
||||
sibling = sibling[which + 'ElementSibling'];
|
||||
|
||||
while (sibling) {
|
||||
if (sibling.matches(selector)) break;
|
||||
sibling = sibling[which + 'ElementSibling'];
|
||||
}
|
||||
|
||||
return sibling;
|
||||
}
|
||||
else if ('next' == which) {
|
||||
sibling = that.input._rel.querySelector(selector);
|
||||
}
|
||||
|
||||
return sibling;
|
||||
}
|
||||
|
||||
debounce(func, wait) {
|
||||
var timeout;
|
||||
return (...args) => {
|
||||
var boundFunc = func.bind(this, ...args);
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(boundFunc, wait);
|
||||
}
|
||||
}
|
||||
|
||||
trigger(eventName, ...args) {
|
||||
document.dispatchEvent(new CustomEvent(eventName, {detail: [...args]}));
|
||||
}
|
||||
|
||||
on(eventName, elementSelector, handler) {
|
||||
document.addEventListener(eventName, function(e) {
|
||||
// loop parent nodes from the target to the delegation node
|
||||
for (var target = e.target; target && target != this; target = target.parentNode) {
|
||||
if (target.matches(elementSelector)) {
|
||||
handler.call(target, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
let that = this;
|
||||
|
||||
that.on('click', '*', function(e) {
|
||||
var wrap = this.closest('.fcomplete-wrap');
|
||||
var isInput = this.classList.contains('fcomplete-enabled');
|
||||
|
||||
if (isInput) {
|
||||
var input = this;
|
||||
var settings = input.fcomplete.settings;
|
||||
var status = (settings.minChars > input.value.length) ? settings.minCharsText : '';
|
||||
input.fcomplete.setStatus(status);
|
||||
this.fcomplete.open();
|
||||
}
|
||||
else if (!wrap && !isInput) {
|
||||
document.querySelectorAll('.fcomplete-wrap').forEach((node) => node.classList.add('fcomplete-hidden'));
|
||||
}
|
||||
});
|
||||
|
||||
that.on('click', '.fcomplete-result', function(e) {
|
||||
var wrap = e.target.closest('.fcomplete-wrap');
|
||||
var input = wrap._rel;
|
||||
input.value = e.target.getAttribute('data-value');
|
||||
|
||||
if (typeof input.fcomplete.settings.onSelect === 'function') {
|
||||
input.fcomplete.settings.onSelect();
|
||||
}
|
||||
|
||||
input.fcomplete.close();
|
||||
});
|
||||
that.on('keydown', '*', function(e) {
|
||||
var wrap = this.closest('.fcomplete-wrap');
|
||||
var isInput = this.classList.contains('fcomplete-enabled');
|
||||
|
||||
if (!wrap && !isInput) return;
|
||||
|
||||
var input = (wrap) ? wrap._rel : this;
|
||||
wrap = (wrap) ? wrap : input._rel;
|
||||
|
||||
if (-1 < [13, 38, 40, 27].indexOf(e.which)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
if (13 == e.which) { // enter
|
||||
if (this.classList.contains('fcomplete-result')) {
|
||||
this.click();
|
||||
}
|
||||
}
|
||||
else if (38 == e.which) { // up
|
||||
if (this.classList.contains('fcomplete-result')) {
|
||||
var sibling = wrap._rel.fcomplete.getAdjacentSibling('previous');
|
||||
window.fCompleteInit.lastFocus = sibling; // stop at the search box
|
||||
(sibling) ? sibling.focus() : input.focus();
|
||||
}
|
||||
}
|
||||
else if (40 == e.which) { // down
|
||||
if (this === input) {
|
||||
var firstResult = wrap.querySelector('.fcomplete-result');
|
||||
|
||||
if (firstResult) {
|
||||
firstResult.focus();
|
||||
window.fCompleteInit.lastFocus = firstResult;
|
||||
}
|
||||
}
|
||||
else if (this.classList.contains('fcomplete-result')) {
|
||||
var sibling = wrap._rel.fcomplete.getAdjacentSibling('next');
|
||||
|
||||
if (sibling) {
|
||||
sibling.focus();
|
||||
window.fCompleteInit.lastFocus = sibling; // stop at the bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (9 == e.which || 27 == e.which) { // tab, esc
|
||||
wrap._rel.fcomplete.close();
|
||||
}
|
||||
});
|
||||
|
||||
that.on('keyup', '.fcomplete-enabled', that.debounce(function(e) {
|
||||
if (-1 < [13, 38, 40, 27].indexOf(e.which)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var input = e.target;
|
||||
var settings = input.fcomplete.settings;
|
||||
|
||||
if (settings.minChars <= input.value.length) {
|
||||
if (Array.isArray(settings.data)) {
|
||||
input.fcomplete.render(settings.data);
|
||||
}
|
||||
else if (typeof settings.data === 'function') {
|
||||
input.fcomplete.setStatus(settings.loadingText);
|
||||
settings.data();
|
||||
}
|
||||
}
|
||||
else {
|
||||
input.fcomplete.render([]);
|
||||
input.fcomplete.setStatus(settings.minCharsText);
|
||||
}
|
||||
}, that.settings.searchDelay));
|
||||
}
|
||||
}
|
||||
|
||||
var $ = (selector, options) => new fComplete(selector, options);
|
||||
|
||||
return $;
|
||||
|
||||
})();
|
||||
7
wp/wp-content/plugins/facetwp/assets/vendor/fDate/LICENSE.md
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright 2021 FacetWP, LLC
|
||||
|
||||
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.
|
||||
87
wp/wp-content/plugins/facetwp/assets/vendor/fDate/fDate.css
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
.fdate-input {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.fdate-wrap {
|
||||
width: 300px;
|
||||
display: none;
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ddd;
|
||||
font-size: 14px;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
.fdate-wrap.opened {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.fdate-wrap .disabled {
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
.fdate-nav {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 5fr 1fr;
|
||||
}
|
||||
|
||||
.fdate-nav > div,
|
||||
.fdate-clear {
|
||||
padding: 10px 0;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.fdate-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fdate-grid.grid-day {
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
}
|
||||
|
||||
.fdate-grid > div {
|
||||
padding: 20px 0;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.fdate-grid > div:hover {
|
||||
background-color: #ddd;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.fdate-grid .fdate-day {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.fdate-grid .weekday,
|
||||
.fdate-grid .inner {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.fdate-grid .today {
|
||||
background-color: #F8F8F8;
|
||||
}
|
||||
|
||||
.fdate-grid .selected {
|
||||
background-color: #DDD6FE;
|
||||
}
|
||||
|
||||
.fdate-day.weekday {
|
||||
font-weight: bold;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.fdate-grid .weekday:hover,
|
||||
.fdate-grid .disabled:hover {
|
||||
background-color: transparent;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.fdate-wrap .disabled:hover {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
679
wp/wp-content/plugins/facetwp/assets/vendor/fDate/fDate.js
vendored
Normal file
@@ -0,0 +1,679 @@
|
||||
window.fDate = (() => {
|
||||
|
||||
var qs = (selector) => document.querySelector(selector);
|
||||
var isset = (input) => 'undefined' !== typeof input;
|
||||
var ymd = (...args) => {
|
||||
var d = new Date(...args);
|
||||
var zeroed = (num) => (num > 9) ? num : '0' + num;
|
||||
// toJSON() produces unexpected results due to timezones
|
||||
return d.getFullYear() + '-' + zeroed(d.getMonth() + 1) + '-' + zeroed(d.getDate());
|
||||
};
|
||||
|
||||
class fDate {
|
||||
|
||||
constructor(selector, options) {
|
||||
let that = this;
|
||||
|
||||
var defaults = {
|
||||
i18n: {
|
||||
weekdays_short: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
|
||||
months_short: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
|
||||
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
||||
clearText: 'Clear',
|
||||
firstDayOfWeek: 0
|
||||
},
|
||||
minDate: '',
|
||||
maxDate: '',
|
||||
altFormat: '',
|
||||
onChange: null
|
||||
};
|
||||
|
||||
that.settings = Object.assign({}, defaults, options);
|
||||
|
||||
if ('string' === typeof selector) {
|
||||
var inputs = document.querySelectorAll(selector);
|
||||
}
|
||||
else if (selector instanceof Node) {
|
||||
var inputs = [selector];
|
||||
}
|
||||
else {
|
||||
var inputs = selector;
|
||||
}
|
||||
|
||||
if (inputs.length) {
|
||||
inputs.forEach(function(input) {
|
||||
input.setAttribute('readonly', 'readonly');
|
||||
|
||||
if ('' !== that.settings.altFormat) {
|
||||
that.el = input;
|
||||
|
||||
let altInput = input.cloneNode();
|
||||
altInput.classList.add('fdate-alt-input');
|
||||
altInput.value = that.getAltDate();
|
||||
altInput._input = input;
|
||||
|
||||
input._altInput = altInput;
|
||||
input.setAttribute('type', 'hidden');
|
||||
input.parentNode.insertBefore(altInput, input.nextSibling); // append()
|
||||
}
|
||||
|
||||
input.classList.add('fdate-input');
|
||||
input._input = input;
|
||||
input.fdate = {
|
||||
settings: that.settings,
|
||||
refresh() {
|
||||
input.click();
|
||||
},
|
||||
open() {
|
||||
input.click();
|
||||
},
|
||||
close() {
|
||||
that.setCalVisibility('hide');
|
||||
},
|
||||
clear() {
|
||||
input.value = '';
|
||||
|
||||
if (isset(input._altInput)) {
|
||||
input._altInput.value = '';
|
||||
}
|
||||
|
||||
that.triggerEvent('onChange');
|
||||
},
|
||||
destroy() {
|
||||
input.classList.remove('fdate-input');
|
||||
delete input._altInput;
|
||||
delete input._input;
|
||||
delete input.fdate;
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (null === qs('.fdate-wrap')) {
|
||||
this.initCalendar();
|
||||
this.bindEvents();
|
||||
}
|
||||
}
|
||||
|
||||
initCalendar() {
|
||||
var html = `
|
||||
<div class="fdate-wrap">
|
||||
<div class="fdate-nav">
|
||||
<div class="fdate-nav-prev"><</div>
|
||||
<div class="fdate-nav-label" tabindex="-1"></div>
|
||||
<div class="fdate-nav-next">></div>
|
||||
</div>
|
||||
<div class="fdate-grid"></div>
|
||||
<div class="fdate-clear" tabindex="-1">${this.settings.i18n.clearText}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.insertAdjacentHTML('beforeend', html);
|
||||
}
|
||||
|
||||
setInput(input) {
|
||||
this.el = input;
|
||||
this.mode = 'day';
|
||||
this.settings = input.fdate.settings;
|
||||
|
||||
this.setDateBounds();
|
||||
|
||||
// valid YYYY-MM-DD?
|
||||
if (null !== input.value.match(/^\d{4}-\d{2}-\d{2}$/)) {
|
||||
var date_str = input.value;
|
||||
}
|
||||
// use the min date or today, whichever is higher
|
||||
else {
|
||||
var today = ymd();
|
||||
var date_str = (this.min.str < today) ? today : this.min.str;
|
||||
}
|
||||
|
||||
// rewind the calendar if beyond the maxDate
|
||||
if (date_str < this.max.str) {
|
||||
var temp_date = new Date(date_str + 'T00:00');
|
||||
this.year = temp_date.getFullYear();
|
||||
this.month = temp_date.getMonth();
|
||||
}
|
||||
else {
|
||||
this.year = this.max.year;
|
||||
this.month = this.max.month;
|
||||
}
|
||||
}
|
||||
|
||||
setDateBounds() {
|
||||
let min = this.settings.minDate || '1000-01-01';
|
||||
let max = this.settings.maxDate || '3000-01-01';
|
||||
let minDate = new Date(min + 'T00:00');
|
||||
let maxDate = new Date(max + 'T00:00');
|
||||
|
||||
this.min = {
|
||||
year: minDate.getFullYear(),
|
||||
month: minDate.getMonth(),
|
||||
str: min
|
||||
};
|
||||
|
||||
this.max = {
|
||||
year: maxDate.getFullYear(),
|
||||
month: maxDate.getMonth(),
|
||||
str: max
|
||||
};
|
||||
}
|
||||
|
||||
isInBounds(val) {
|
||||
if ('year' == this.mode) {
|
||||
let year = parseInt(val);
|
||||
|
||||
if (year < this.min.year || year > this.max.year) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ('month' == this.mode) {
|
||||
let month = parseInt(val);
|
||||
let valStr = ymd(this.year, month).substr(0, 7);
|
||||
let monthMin = this.min.str.substr(0, 7);
|
||||
let monthMax = this.max.str.substr(0, 7);
|
||||
|
||||
if (valStr < monthMin || valStr > monthMax) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ('day' == this.mode) {
|
||||
if (val < this.min.str || val > this.max.str) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
isNavAllowed(type) {
|
||||
if ('year' == this.mode) {
|
||||
let decade = parseInt(this.year.toString().substr(0, 3) + '0');
|
||||
|
||||
return ('next' == type) ?
|
||||
decade < parseInt(this.max.str.substr(0, 4)) :
|
||||
decade > parseInt(this.min.str.substr(0, 4));
|
||||
}
|
||||
else if ('month' == this.mode) {
|
||||
return ('next' == type) ?
|
||||
ymd(this.year + 1, 0, 0) < this.max.str :
|
||||
ymd(this.year, 0) > this.min.str;
|
||||
}
|
||||
else if ('day' == this.mode) {
|
||||
return ('next' == type) ?
|
||||
ymd(this.year, this.month + 1, 0) < this.max.str :
|
||||
ymd(this.year, this.month) > this.min.str;
|
||||
}
|
||||
}
|
||||
|
||||
setDisplay(which) {
|
||||
var that = this;
|
||||
|
||||
this.mode = which;
|
||||
|
||||
qs('.fdate-grid').classList.remove('grid-day');
|
||||
|
||||
// show or hide the nav arrows
|
||||
qs('.fdate-nav-prev').classList.add('disabled');
|
||||
qs('.fdate-nav-next').classList.add('disabled');
|
||||
|
||||
if (that.isNavAllowed('prev')) {
|
||||
qs('.fdate-nav-prev').classList.remove('disabled');
|
||||
}
|
||||
|
||||
if ( that.isNavAllowed('next')) {
|
||||
qs('.fdate-nav-next').classList.remove('disabled');
|
||||
}
|
||||
|
||||
// month
|
||||
if ('month' == which) {
|
||||
var output = '';
|
||||
this.settings.i18n.months_short.forEach(function(item, index) {
|
||||
var css = that.isInBounds(index) ? ' inner' : ' disabled';
|
||||
output += '<div class="fdate-month' + css + '" data-value="' + index + '" tabindex="-1">' + item + '</div>';
|
||||
});
|
||||
|
||||
qs('.fdate-grid').innerHTML = output;
|
||||
qs('.fdate-nav-label').innerHTML = this.year;
|
||||
}
|
||||
// year
|
||||
else if ('year' == which) {
|
||||
var output = '';
|
||||
var decade = parseInt(this.year.toString().substr(0, 3) + '0');
|
||||
for (var i = 0; i < 10; i++) {
|
||||
var css = that.isInBounds(decade + i) ? ' inner' : ' disabled';
|
||||
output += '<div class="fdate-year' + css + '" data-value="' + (decade + i) + '" tabindex="-1">' + (decade + i) + '</div>';
|
||||
}
|
||||
|
||||
qs('.fdate-grid').innerHTML = output;
|
||||
|
||||
var prefix = this.year.toString().substr(0, 3);
|
||||
var decade = prefix + '0 - ' + prefix + '9';
|
||||
qs('.fdate-nav-label').innerHTML = decade;
|
||||
}
|
||||
// day
|
||||
else {
|
||||
qs('.fdate-grid').classList.add('grid-day');
|
||||
|
||||
var output = '';
|
||||
var days = this.generateDays(this.year, this.month);
|
||||
days.forEach(function(item) {
|
||||
output += '<div class="fdate-day' + item.class + '" data-value="' + item.value + '" tabindex="-1">' + item.text + '</div>';
|
||||
});
|
||||
|
||||
qs('.fdate-grid').innerHTML = output;
|
||||
qs('.fdate-nav-label').innerHTML = this.settings.i18n.months[this.month] + ' ' + this.year;
|
||||
}
|
||||
}
|
||||
|
||||
generateDays(year, month) {
|
||||
let that = this;
|
||||
var output = [];
|
||||
let i18n = that.settings.i18n;
|
||||
let weekdays = i18n.weekdays_short;
|
||||
let firstDayOfWeek = i18n.firstDayOfWeek; // 0 = Sunday
|
||||
let firstDayNum = new Date(year, month).getDay(); // between 0 and 6
|
||||
let offset = firstDayNum - firstDayOfWeek;
|
||||
offset = (offset < 0) ? 7 + offset : offset; // negative offset (e.g. August 2021)
|
||||
let num_days = new Date(year, month + 1, 0).getDate();
|
||||
let today = ymd();
|
||||
|
||||
// shift weekdays according to firstDayOfWeek
|
||||
if (0 < firstDayOfWeek) {
|
||||
let temp = JSON.parse(JSON.stringify(weekdays));
|
||||
let append = temp.splice(0, firstDayOfWeek);
|
||||
weekdays = temp.concat(append);
|
||||
}
|
||||
|
||||
// get weekdays
|
||||
weekdays.forEach(function(item) {
|
||||
output.push({
|
||||
text: item,
|
||||
value: '',
|
||||
class: ' weekday'
|
||||
});
|
||||
});
|
||||
|
||||
// get days from the previous month
|
||||
if (0 < offset) {
|
||||
let year_prev = (0 == month) ? year - 1 : year;
|
||||
let month_prev = (0 == month) ? 11 : month - 1;
|
||||
let num_days_prev = new Date(year_prev, month_prev + 1, 0).getDate();
|
||||
|
||||
for (var i = (num_days_prev - offset + 1); i <= num_days_prev; i++) {
|
||||
var val = ymd(year_prev, month_prev, i);
|
||||
var css = that.isInBounds(val) ? '' : ' disabled';
|
||||
output.push({
|
||||
text: i,
|
||||
value: val,
|
||||
class: css
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// get days from the current month
|
||||
for (var i = 1; i <= num_days; i++) {
|
||||
var val = ymd(year, month, i);
|
||||
|
||||
if ( that.isInBounds(val)) {
|
||||
var css = ' inner';
|
||||
css += (val == today) ? ' today' : '';
|
||||
css += (val == this.el.value) ? ' selected' : '';
|
||||
}
|
||||
else {
|
||||
var css = ' disabled';
|
||||
}
|
||||
|
||||
output.push({
|
||||
text: i,
|
||||
value: val,
|
||||
class: css
|
||||
});
|
||||
}
|
||||
|
||||
// get days from the next month
|
||||
let year_next = (11 == month) ? year + 1 : year;
|
||||
let month_next = (11 == month) ? 0 : month + 1;
|
||||
let num_filler = 42 - num_days - offset;
|
||||
|
||||
for (var i = 1; i <= num_filler; i++) {
|
||||
var val = ymd(year_next, month_next, i);
|
||||
var css = that.isInBounds(val) ? '' : ' disabled';
|
||||
output.push({
|
||||
text: i,
|
||||
value: val,
|
||||
class: css
|
||||
});
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
adjustDate(increment, unit) {
|
||||
var temp_year = ('year' == unit) ? this.year + increment : this.year;
|
||||
var temp_month = ('month' == unit) ? this.month + increment : this.month;
|
||||
var temp_date = new Date(temp_year, temp_month);
|
||||
|
||||
this.year = temp_date.getFullYear();
|
||||
this.month = temp_date.getMonth();
|
||||
}
|
||||
|
||||
on(eventName, elementSelector, handler) {
|
||||
document.addEventListener(eventName, function(e) {
|
||||
// loop parent nodes from the target to the delegation node
|
||||
for (var target = e.target; target && target != this; target = target.parentNode) {
|
||||
if (target.matches(elementSelector)) {
|
||||
handler.call(target, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
getAltDate() {
|
||||
let that = this;
|
||||
|
||||
if ('' === that.el.value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let date_array = that.el.value.split('-');
|
||||
let format_array = that.settings.altFormat.split('');
|
||||
let output = '';
|
||||
|
||||
let escaped = false;
|
||||
format_array.forEach(function(token) {
|
||||
if ('\\' === token) {
|
||||
escaped = true;
|
||||
return;
|
||||
}
|
||||
|
||||
output += escaped ? token : that.parseDateToken(token, date_array);
|
||||
escaped = false;
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
parseDateToken(token, date_array) {
|
||||
let i18n = this.settings.i18n;
|
||||
|
||||
let tokens = {
|
||||
'd': () => date_array[2],
|
||||
'j': () => parseInt(date_array[2]),
|
||||
'm': () => date_array[1],
|
||||
'n': () => parseInt(date_array[1]),
|
||||
'F': () => i18n.months[parseInt(date_array[1]) - 1],
|
||||
'M': () => i18n.months_short[parseInt(date_array[1]) - 1],
|
||||
'y': () => date_array[0].substring(2),
|
||||
'Y': () => date_array[0]
|
||||
};
|
||||
|
||||
return isset(tokens[token]) ? tokens[token]() : token;
|
||||
}
|
||||
|
||||
setPosition(input) {
|
||||
let wrap = qs('.fdate-wrap');
|
||||
let inputBounds = input.getBoundingClientRect();
|
||||
let calendarWidth = wrap.getBoundingClientRect().width;
|
||||
let calendarHeight = wrap.getBoundingClientRect().height;
|
||||
let distanceFromRight = document.body.clientWidth - inputBounds.left;
|
||||
let distanceFromBottom = document.body.clientHeight - inputBounds.bottom;
|
||||
let showOnTop = (distanceFromBottom < calendarHeight && inputBounds.top > calendarHeight);
|
||||
let showOnLeft = (distanceFromRight < calendarWidth && inputBounds.left > calendarWidth);
|
||||
|
||||
let top = window.pageYOffset + inputBounds.top + (!showOnTop ? input.offsetHeight + 2 : -calendarHeight - 2);
|
||||
let left = window.pageXOffset + inputBounds.left;
|
||||
let right = window.pageXOffset + inputBounds.right - calendarWidth;
|
||||
let pixels = showOnLeft ? right : left;
|
||||
|
||||
wrap.style.position = 'absolute';
|
||||
wrap.style.top = top + 'px';
|
||||
wrap.style.left = pixels + 'px';
|
||||
}
|
||||
|
||||
setCalVisibility(which) {
|
||||
var wrap = qs('.fdate-wrap');
|
||||
|
||||
if ('hide' === which) {
|
||||
if (wrap.classList.contains('opened')) {
|
||||
wrap.classList.remove('opened');
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (! wrap.classList.contains('opened')) {
|
||||
wrap.classList.add('opened');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
triggerEvent(name) {
|
||||
if (typeof this.settings[name] === 'function') {
|
||||
this.settings[name](this);
|
||||
}
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
var that = this;
|
||||
|
||||
that.on('click', '.fdate-day:not(.disabled):not(.weekday)', function(e) {
|
||||
that.el.value = e.target.getAttribute('data-value');
|
||||
|
||||
if (isset(that.el._altInput)) {
|
||||
that.el._altInput.value = that.getAltDate();
|
||||
}
|
||||
|
||||
that.triggerEvent('onChange');
|
||||
that.setCalVisibility('hide');
|
||||
e.stopImmediatePropagation(); // important
|
||||
});
|
||||
|
||||
that.on('click', '.fdate-month:not(.disabled)', function(e) {
|
||||
that.month = parseInt(e.target.getAttribute('data-value'));
|
||||
that.setDisplay('day');
|
||||
e.stopImmediatePropagation(); // important
|
||||
});
|
||||
|
||||
that.on('click', '.fdate-year:not(.disabled)', function(e) {
|
||||
that.year = parseInt(e.target.getAttribute('data-value'));
|
||||
that.setDisplay('month');
|
||||
e.stopImmediatePropagation(); // important
|
||||
});
|
||||
|
||||
that.on('click', '.fdate-nav-prev:not(.disabled)', function() {
|
||||
var incr = ('year' == that.mode) ? -10 : -1;
|
||||
var unit = ('day' == that.mode) ? 'month' : 'year';
|
||||
|
||||
that.adjustDate(incr, unit);
|
||||
that.setDisplay(that.mode);
|
||||
});
|
||||
|
||||
that.on('click', '.fdate-nav-next:not(.disabled)', function() {
|
||||
var incr = ('year' == that.mode) ? 10 : 1;
|
||||
var unit = ('day' == that.mode) ? 'month' : 'year';
|
||||
|
||||
that.adjustDate(incr, unit);
|
||||
that.setDisplay(that.mode);
|
||||
});
|
||||
|
||||
that.on('click', '.fdate-nav-label', function() {
|
||||
if ('day' == that.mode) {
|
||||
that.setDisplay('month');
|
||||
}
|
||||
else if ('month' == that.mode) {
|
||||
that.setDisplay('year');
|
||||
}
|
||||
else if ('year' == that.mode) {
|
||||
that.setDisplay('day');
|
||||
}
|
||||
});
|
||||
|
||||
that.on('click', '.fdate-clear', function() {
|
||||
that.el.fdate.clear();
|
||||
});
|
||||
|
||||
that.on('click', '*', function(e) {
|
||||
var is_input = e.target.classList.contains('fdate-input') || e.target.classList.contains('fdate-alt-input');
|
||||
var is_cal = (null !== e.target.closest('.fdate-wrap'));
|
||||
var is_clear = e.target.classList.contains('fdate-clear');
|
||||
|
||||
if (is_input || (is_cal && ! is_clear)) {
|
||||
that.setCalVisibility('show');
|
||||
|
||||
// set position and render calendar
|
||||
if (is_input) {
|
||||
let visibleInput = e.target._altInput || e.target;
|
||||
that.setInput(e.target._input);
|
||||
that.setDisplay('day');
|
||||
that.setPosition(visibleInput);
|
||||
}
|
||||
}
|
||||
else {
|
||||
that.setCalVisibility('hide');
|
||||
}
|
||||
});
|
||||
|
||||
// a11y support
|
||||
window.addEventListener('keyup', function(e) {
|
||||
if ('Tab' === e.key) {
|
||||
if (e.target.classList.contains('fdate-input') || e.target.classList.contains('fdate-alt-input')) {
|
||||
e.target._input.click();
|
||||
}
|
||||
else {
|
||||
that.setCalVisibility('hide');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('keydown', function(e) {
|
||||
if ('Enter' === e.key) {
|
||||
if (e.target.closest('.fdate-grid')) {
|
||||
qs('.fdate-nav-label').focus();
|
||||
}
|
||||
if (e.target.closest('.fdate-wrap')) {
|
||||
e.target.click();
|
||||
}
|
||||
}
|
||||
else if ('Escape' === e.key) {
|
||||
if (e.target.closest('.fdate-wrap') || e.target.classList.contains('fdate-input') || e.target.classList.contains('fdate-alt-input')) {
|
||||
that.el.fdate.close();
|
||||
}
|
||||
}
|
||||
else if ('ArrowUp' === e.key) {
|
||||
if (e.target.classList.contains('fdate-input') || e.target.classList.contains('fdate-alt-input')) { // from input
|
||||
qs('.fdate-clear').focus();
|
||||
e.preventDefault();
|
||||
}
|
||||
else if (e.target.classList.contains('fdate-nav-label')) {
|
||||
that.el.focus();
|
||||
e.preventDefault();
|
||||
}
|
||||
else if (e.target.classList.contains('fdate-clear')) {
|
||||
let days = document.querySelectorAll('.fdate-day.inner');
|
||||
let item = (days.length) ? days[days.length - 1] : qs('.fdate-nav-label');
|
||||
item.focus();
|
||||
|
||||
e.preventDefault();
|
||||
}
|
||||
else if (e.target.closest('.fdate-grid')) {
|
||||
let offset = ('day' === that.mode) ? -7 : -4;
|
||||
let el = that.getSibling(e.target, offset);
|
||||
|
||||
if (el) {
|
||||
el.focus();
|
||||
}
|
||||
else {
|
||||
qs('.fdate-nav-label').focus();
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
else if ('ArrowDown' === e.key) {
|
||||
if (e.target.classList.contains('fdate-input') || e.target.classList.contains('fdate-alt-input')) { // from input
|
||||
let selected = qs('.fdate-grid .selected');
|
||||
let today = qs('.fdate-grid .today');
|
||||
|
||||
if (selected) {
|
||||
selected.focus();
|
||||
}
|
||||
else if (today) {
|
||||
today.focus();
|
||||
}
|
||||
else {
|
||||
qs('.fdate-nav-label').focus();
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
}
|
||||
else if (e.target.classList.contains('fdate-nav-label')) { // from nav
|
||||
qs('.fdate-grid .inner').focus();
|
||||
e.preventDefault();
|
||||
}
|
||||
else if (e.target.classList.contains('fdate-clear')) {
|
||||
that.el.focus();
|
||||
e.preventDefault();
|
||||
}
|
||||
else if (e.target.closest('.fdate-grid')) { // from grid
|
||||
let offset = ('day' === that.mode) ? 7 : 4;
|
||||
let el = that.getSibling(e.target, offset);
|
||||
|
||||
if (el) {
|
||||
el.focus();
|
||||
}
|
||||
else {
|
||||
qs('.fdate-clear').focus();
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
else if ('ArrowLeft' === e.key) {
|
||||
if (e.target.classList.contains('fdate-nav-label')) { // into the past
|
||||
qs('.fdate-nav-prev').click();
|
||||
e.preventDefault();
|
||||
}
|
||||
if (e.target.closest('.fdate-grid')) { // previous grid item
|
||||
let prev = e.target.previousElementSibling;
|
||||
if (prev && prev.classList.contains('inner')) {
|
||||
prev.focus();
|
||||
}
|
||||
else {
|
||||
let days = document.querySelectorAll('.fdate-day.inner');
|
||||
days[days.length - 1].focus(); // last valid day of month
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
else if ('ArrowRight' === e.key) {
|
||||
if (e.target.classList.contains('fdate-nav-label')) { // into the future
|
||||
qs('.fdate-nav-next').click();
|
||||
e.preventDefault();
|
||||
}
|
||||
if (e.target.closest('.fdate-grid')) { // next grid item
|
||||
let next = e.target.nextElementSibling;
|
||||
if (next && next.classList.contains('inner')) {
|
||||
next.focus();
|
||||
}
|
||||
else {
|
||||
qs('.fdate-day.inner').focus(); // first valid day of month
|
||||
}
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getSibling(orig, offset) {
|
||||
let el = orig;
|
||||
for (var i = 0; i < Math.abs(offset); i++) {
|
||||
el = (0 < offset) ? el.nextElementSibling : el.previousElementSibling;
|
||||
if (null === el || !el.classList.contains('inner')) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
}
|
||||
}
|
||||
|
||||
return fDate;
|
||||
})();
|
||||
1
wp/wp-content/plugins/facetwp/assets/vendor/fDate/fDate.min.js
vendored
Normal file
147
wp/wp-content/plugins/facetwp/assets/vendor/fSelect/fSelect.css
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
.fs-wrap {
|
||||
width: 220px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.fs-label-wrap {
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ddd;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.fs-label-wrap,
|
||||
.fs-dropdown {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.fs-label-wrap .fs-label {
|
||||
padding: 6px 22px 6px 8px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fs-arrow {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
border-top: 5px solid #333;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 5px;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
transition: ease-in 0.15s;
|
||||
}
|
||||
|
||||
.fs-open .fs-arrow {
|
||||
transform: rotate(-180deg);
|
||||
}
|
||||
|
||||
.fs-dropdown {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-top: none;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.fs-dropdown .fs-options {
|
||||
max-height: 200px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.fs-search {
|
||||
background-color: #f8f8f8;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.fs-wrap .fs-search input {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background-color: transparent;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.fs-option,
|
||||
.fs-search,
|
||||
.fs-optgroup-label {
|
||||
padding: 6px 8px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.fs-option:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.fs-no-results {
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.fs-option {
|
||||
cursor: pointer;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.fs-option.disabled {
|
||||
opacity: 0.4;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.fs-wrap.single .fs-option.selected {
|
||||
background-color: #dff3ff;
|
||||
}
|
||||
|
||||
.fs-wrap.multiple .fs-option {
|
||||
position: relative;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.fs-wrap.multiple .fs-checkbox {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 30px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.fs-wrap.multiple .fs-option .fs-checkbox i {
|
||||
position: absolute;
|
||||
margin: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border: 1px solid #aeaeae;
|
||||
border-radius: 2px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.fs-wrap.multiple .fs-option.selected .fs-checkbox i {
|
||||
background-color: rgb(108, 138, 255);
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.fs-optgroup-label {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.fs-hidden {
|
||||
display: none;
|
||||
}
|
||||
420
wp/wp-content/plugins/facetwp/assets/vendor/fSelect/fSelect.js
vendored
Normal file
@@ -0,0 +1,420 @@
|
||||
window.fSelect = (() => {
|
||||
|
||||
var build = {};
|
||||
|
||||
class fSelect {
|
||||
|
||||
constructor(selector, options) {
|
||||
let that = this;
|
||||
|
||||
var defaults = {
|
||||
placeholder: 'Select some options',
|
||||
numDisplayed: 3,
|
||||
overflowText: '{n} selected',
|
||||
searchText: 'Search',
|
||||
noResultsText: 'No results found',
|
||||
showSearch: true,
|
||||
optionFormatter: false
|
||||
};
|
||||
|
||||
that.settings = Object.assign({}, defaults, options);
|
||||
build = {output: '', optgroup: 0, idx: 0};
|
||||
|
||||
if ('string' === typeof selector) {
|
||||
var nodes = Array.from(document.querySelectorAll(selector));
|
||||
}
|
||||
else if (selector instanceof Node) {
|
||||
var nodes = [selector];
|
||||
}
|
||||
else if (Array.isArray(selector)) {
|
||||
var nodes = selector;
|
||||
}
|
||||
else {
|
||||
var nodes = [];
|
||||
}
|
||||
|
||||
if ('undefined' === typeof window.fSelectInit) {
|
||||
window.fSelectInit = {
|
||||
searchCache: '',
|
||||
lastChoice: null,
|
||||
lastFocus: null,
|
||||
activeEl: null
|
||||
};
|
||||
that.bindEvents();
|
||||
}
|
||||
|
||||
nodes.forEach((input) => {
|
||||
if (typeof input.fselect === 'object') {
|
||||
input.fselect.destroy();
|
||||
}
|
||||
|
||||
that.settings.multiple = input.matches('[multiple]');
|
||||
input.fselect = that;
|
||||
that.input = input;
|
||||
that.create();
|
||||
});
|
||||
}
|
||||
|
||||
create() {
|
||||
var that = this;
|
||||
var options = that.buildOptions();
|
||||
var label = that.getDropdownLabel();
|
||||
var mode = (that.settings.multiple) ? 'multiple' : 'single';
|
||||
var searchClass = (that.settings.showSearch) ? '' : ' fs-hidden';
|
||||
var noResultsClass = (build.idx < 2) ? '' : ' fs-hidden';
|
||||
|
||||
var html = `
|
||||
<div class="fs-wrap ${mode}" tabindex="0">
|
||||
<div class="fs-label-wrap">
|
||||
<div class="fs-label">${label}</div>
|
||||
<span class="fs-arrow"></span>
|
||||
</div>
|
||||
<div class="fs-dropdown fs-hidden">
|
||||
<div class="fs-search${searchClass}">
|
||||
<input type="text" placeholder="${that.settings.searchText}" />
|
||||
</div>
|
||||
<div class="fs-no-results${noResultsClass}">${that.settings.noResultsText}</div>
|
||||
<div class="fs-options">${options}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
var tpl = document.createElement('template');
|
||||
tpl.innerHTML = html;
|
||||
var wrap = tpl.content.querySelector('.fs-wrap');
|
||||
that.input.parentNode.insertBefore(wrap, that.input.nextSibling);
|
||||
that.input.classList.add('fs-hidden');
|
||||
|
||||
// add a relationship link
|
||||
that.input._rel = wrap;
|
||||
wrap._rel = that.input;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.input._rel.remove();
|
||||
this.input.classList.remove('fs-hidden');
|
||||
delete this.input._rel;
|
||||
}
|
||||
|
||||
reload() {
|
||||
this.destroy();
|
||||
this.create();
|
||||
}
|
||||
|
||||
open() {
|
||||
var wrap = this.input._rel;
|
||||
wrap.classList.add('fs-open');
|
||||
wrap.querySelector('.fs-dropdown').classList.remove('fs-hidden');
|
||||
|
||||
// don't auto-focus for touch devices
|
||||
if (! window.matchMedia("(pointer: coarse)").matches) {
|
||||
wrap.querySelector('.fs-search input').focus();
|
||||
}
|
||||
|
||||
window.fSelectInit.lastChoice = this.getSelectedOptions('value');
|
||||
window.fSelectInit.activeEl = wrap;
|
||||
|
||||
this.trigger('fs:opened', wrap);
|
||||
}
|
||||
|
||||
close() {
|
||||
this.input._rel.classList.remove('fs-open');
|
||||
this.input._rel.querySelector('.fs-dropdown').classList.add('fs-hidden');
|
||||
|
||||
window.fSelectInit.searchCache = '';
|
||||
window.fSelectInit.lastChoice = null;
|
||||
window.fSelectInit.lastFocus = null;
|
||||
window.fSelectInit.activeEl = null;
|
||||
|
||||
this.trigger('fs:closed', this.input._rel);
|
||||
}
|
||||
|
||||
buildOptions(parent) {
|
||||
var that = this;
|
||||
var parent = parent || that.input;
|
||||
|
||||
Array.from(parent.children).forEach((node) => {
|
||||
if ('optgroup' === node.nodeName.toLowerCase()) {
|
||||
var opt = `<div class="fs-optgroup-label" data-group="${build.optgroup}">${node.label}</div>`;
|
||||
build.output += opt;
|
||||
that.buildOptions(node);
|
||||
build.optgroup++;
|
||||
}
|
||||
else {
|
||||
var val = node.value;
|
||||
|
||||
// skip the first choice in multi-select mode
|
||||
if (0 === build.idx && '' === val && that.settings.multiple) {
|
||||
build.idx++;
|
||||
return;
|
||||
}
|
||||
|
||||
var classes = ['fs-option', 'g' + build.optgroup];
|
||||
|
||||
// append existing classes
|
||||
node.className.split(' ').forEach((className) => {
|
||||
if ('' !== className) {
|
||||
classes.push(className);
|
||||
}
|
||||
});
|
||||
|
||||
if (node.matches('[disabled]')) classes.push('disabled');
|
||||
if (node.matches('[selected]')) classes.push('selected');
|
||||
classes = classes.join(' ');
|
||||
|
||||
if ('function' === typeof that.settings.optionFormatter) {
|
||||
node.label = that.settings.optionFormatter(node.label, node);
|
||||
}
|
||||
|
||||
var opt = `<div class="${classes}" data-value="${val}" data-idx="${build.idx}" tabindex="-1"><span class="fs-checkbox"><i></i></span><div class="fs-option-label">${node.label}</div></div>`;
|
||||
|
||||
build.output += opt;
|
||||
build.idx++;
|
||||
}
|
||||
});
|
||||
|
||||
return build.output;
|
||||
}
|
||||
|
||||
getSelectedOptions(field, context) {
|
||||
var context = context || this.input;
|
||||
return Array.from(context.selectedOptions).map((opt) => {
|
||||
return (field) ? opt[field] : opt;
|
||||
});
|
||||
}
|
||||
|
||||
getAdjacentSibling(which) {
|
||||
var that = this;
|
||||
var which = which || 'next';
|
||||
var sibling = window.fSelectInit.lastFocus;
|
||||
var selector = '.fs-option:not(.fs-hidden):not(.disabled)';
|
||||
|
||||
if (sibling) {
|
||||
sibling = sibling[which + 'ElementSibling'];
|
||||
|
||||
while (sibling) {
|
||||
if (sibling.matches(selector)) break;
|
||||
sibling = sibling[which + 'ElementSibling'];
|
||||
}
|
||||
|
||||
return sibling;
|
||||
}
|
||||
else if ('next' == which) {
|
||||
sibling = that.input._rel.querySelector(selector);
|
||||
}
|
||||
|
||||
return sibling;
|
||||
}
|
||||
|
||||
getDropdownLabel() {
|
||||
var that = this;
|
||||
var labelText = that.getSelectedOptions('text');
|
||||
|
||||
if (labelText.length < 1) {
|
||||
labelText = that.settings.placeholder;
|
||||
}
|
||||
else if (labelText.length > that.settings.numDisplayed) {
|
||||
labelText = that.settings.overflowText.replace('{n}', labelText.length);
|
||||
}
|
||||
else {
|
||||
labelText = labelText.join(', ');
|
||||
}
|
||||
|
||||
return labelText;
|
||||
}
|
||||
|
||||
debounce(func, wait) {
|
||||
var timeout;
|
||||
return (...args) => {
|
||||
var boundFunc = func.bind(this, ...args);
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(boundFunc, wait);
|
||||
}
|
||||
}
|
||||
|
||||
trigger(eventName, ...args) {
|
||||
document.dispatchEvent(new CustomEvent(eventName, {detail: [...args]}));
|
||||
}
|
||||
|
||||
on(eventName, elementSelector, handler) {
|
||||
document.addEventListener(eventName, function(e) {
|
||||
// loop parent nodes from the target to the delegation node
|
||||
for (var target = e.target; target && target != this; target = target.parentNode) {
|
||||
if (target.matches(elementSelector)) {
|
||||
handler.call(target, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
var that = this;
|
||||
var optionSelector = '.fs-option:not(.fs-hidden):not(.disabled)';
|
||||
var unaccented = (str) => str.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
||||
|
||||
// debounce search for better performance
|
||||
that.on('keyup', '.fs-search input', that.debounce(function(e) {
|
||||
var wrap = e.target.closest('.fs-wrap');
|
||||
var options = wrap._rel.options;
|
||||
|
||||
var matchOperators = /[|\\{}()[\]^$+*?.]/g;
|
||||
var keywords = e.target.value.replace(matchOperators, '\\$&');
|
||||
keywords = unaccented(keywords);
|
||||
|
||||
// if the searchCache already has a prefixed version of this search
|
||||
// then don't un-hide the existing exclusions
|
||||
if (0 !== keywords.indexOf(window.fSelectInit.searchCache)) {
|
||||
wrap.querySelectorAll('.fs-option, .fs-optgroup-label').forEach((node) => node.classList.remove('fs-hidden'));
|
||||
}
|
||||
|
||||
window.fSelectInit.searchCache = keywords;
|
||||
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
if ('' === options[i].value) continue;
|
||||
|
||||
var needle = new RegExp(keywords, 'gi');
|
||||
var haystack = unaccented(options[i].text);
|
||||
|
||||
if (null === haystack.match(needle)) {
|
||||
wrap.querySelector('.fs-option[data-idx="' + i + '"]').classList.add('fs-hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// hide optgroups if no choices
|
||||
wrap.querySelectorAll('.fs-optgroup-label').forEach((node) => {
|
||||
var group = node.getAttribute('data-group');
|
||||
var container = node.closest('.fs-options');
|
||||
var count = container.querySelectorAll('.fs-option.g' + group + ':not(.fs-hidden)').length;
|
||||
|
||||
if (count < 1) {
|
||||
node.classList.add('fs-hidden');
|
||||
}
|
||||
});
|
||||
|
||||
// toggle the noResultsText div
|
||||
if (wrap.querySelectorAll('.fs-option:not(.fs-hidden').length) {
|
||||
wrap.querySelector('.fs-no-results').classList.add('fs-hidden');
|
||||
}
|
||||
else {
|
||||
wrap.querySelector('.fs-no-results').classList.remove('fs-hidden');
|
||||
}
|
||||
|
||||
}, 100));
|
||||
|
||||
that.on('click', optionSelector, function(e) {
|
||||
var wrap = this.closest('.fs-wrap');
|
||||
var value = this.getAttribute('data-value');
|
||||
var input = wrap._rel;
|
||||
var isMultiple = wrap.classList.contains('multiple');
|
||||
|
||||
if (!isMultiple) {
|
||||
input.value = value;
|
||||
wrap.querySelectorAll('.fs-option.selected').forEach((node) => node.classList.remove('selected'));
|
||||
}
|
||||
else {
|
||||
var idx = parseInt(this.getAttribute('data-idx'));
|
||||
input.options[idx].selected = !this.classList.contains('selected');
|
||||
}
|
||||
|
||||
this.classList.toggle('selected');
|
||||
|
||||
var label = input.fselect.getDropdownLabel();
|
||||
wrap.querySelector('.fs-label').innerHTML = label;
|
||||
|
||||
// fire a change event
|
||||
input.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
input.fselect.trigger('fs:changed', wrap);
|
||||
|
||||
if (!isMultiple) {
|
||||
input.fselect.close();
|
||||
}
|
||||
|
||||
e.stopImmediatePropagation();
|
||||
});
|
||||
|
||||
that.on('keydown', '*', function(e) {
|
||||
var wrap = this.closest('.fs-wrap');
|
||||
|
||||
if (!wrap) return;
|
||||
|
||||
if (-1 < [38, 40, 27].indexOf(e.which)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
if (32 == e.which || 13 == e.which) { // space, enter
|
||||
if (e.target.closest('.fs-search')) {
|
||||
// preserve spaces for search
|
||||
}
|
||||
else if (e.target.matches(optionSelector)) {
|
||||
e.target.click();
|
||||
e.preventDefault();
|
||||
}
|
||||
else {
|
||||
wrap.querySelector('.fs-label').click();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
else if (38 == e.which) { // up
|
||||
var sibling = wrap._rel.fselect.getAdjacentSibling('previous');
|
||||
window.fSelectInit.lastFocus = sibling; // stop at the search box
|
||||
|
||||
if (sibling) {
|
||||
sibling.focus();
|
||||
}
|
||||
else {
|
||||
wrap.querySelector('.fs-search input').focus();
|
||||
}
|
||||
}
|
||||
else if (40 == e.which) { // down
|
||||
var sibling = wrap._rel.fselect.getAdjacentSibling('next');
|
||||
|
||||
if (sibling) {
|
||||
sibling.focus();
|
||||
window.fSelectInit.lastFocus = sibling; // stop at the bottom
|
||||
}
|
||||
}
|
||||
else if (9 == e.which || 27 == e.which) { // tab, esc
|
||||
wrap._rel.fselect.close();
|
||||
}
|
||||
});
|
||||
|
||||
that.on('click', '*', function(e) {
|
||||
var wrap = this.closest('.fs-wrap');
|
||||
var lastActive = window.fSelectInit.activeEl;
|
||||
|
||||
if (wrap) {
|
||||
var labelWrap = this.closest('.fs-label-wrap');
|
||||
|
||||
if (labelWrap) {
|
||||
if (lastActive) {
|
||||
lastActive._rel.fselect.close();
|
||||
}
|
||||
if (wrap !== lastActive) {
|
||||
wrap._rel.fselect.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (lastActive) {
|
||||
lastActive._rel.fselect.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var $ = (selector, options) => new fSelect(selector, options);
|
||||
|
||||
return $;
|
||||
|
||||
})();
|
||||
|
||||
if ('undefined' !== typeof fUtil) {
|
||||
fUtil.fn.fSelect = function(opts) {
|
||||
this.each(function() { // no arrow function to preserve "this"
|
||||
fSelect(this, opts);
|
||||
});
|
||||
return this;
|
||||
};
|
||||
}
|
||||
18
wp/wp-content/plugins/facetwp/assets/vendor/fTip/fTip.css
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
.ftip-wrap {
|
||||
position: absolute;
|
||||
display: none;
|
||||
max-width: 400px;
|
||||
padding: 6px 8px;
|
||||
background-color: #222;
|
||||
border-radius: 6px;
|
||||
opacity: 0;
|
||||
color: #fff;
|
||||
cursor: default;
|
||||
transition: height 0s, opacity 1s;
|
||||
}
|
||||
|
||||
.ftip-wrap.active {
|
||||
height: auto;
|
||||
display: inline-block;
|
||||
opacity: 0.9;
|
||||
}
|
||||
100
wp/wp-content/plugins/facetwp/assets/vendor/fTip/fTip.js
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
window.fTip = (() => {
|
||||
|
||||
var qs = (selector) => document.querySelector(selector);
|
||||
|
||||
class fTip {
|
||||
|
||||
constructor(selector, options) {
|
||||
let that = this;
|
||||
|
||||
var defaults = {
|
||||
content: (node) => node.getAttribute('title')
|
||||
};
|
||||
|
||||
that.settings = Object.assign({}, defaults, options);
|
||||
|
||||
var inputs = [];
|
||||
|
||||
if ('string' === typeof selector) {
|
||||
var inputs = Array.from(document.querySelectorAll(selector));
|
||||
}
|
||||
else if (selector instanceof Node) {
|
||||
var inputs = [selector];
|
||||
}
|
||||
else if (Array.isArray(selector)) {
|
||||
var inputs = selector;
|
||||
}
|
||||
|
||||
if (null === qs('.ftip-wrap')) {
|
||||
that.buildHtml();
|
||||
that.bindEvents();
|
||||
}
|
||||
|
||||
inputs.forEach(function(input) {
|
||||
that.input = input;
|
||||
input.ftip = that;
|
||||
input.classList.add('ftip-enabled');
|
||||
});
|
||||
}
|
||||
|
||||
open() {
|
||||
let input = this.input;
|
||||
let wrap = qs('.ftip-wrap');
|
||||
|
||||
wrap.innerHTML = this.settings.content(input);
|
||||
wrap.classList.add('active');
|
||||
|
||||
let wrapBounds = wrap.getBoundingClientRect();
|
||||
let inputBounds = input.getBoundingClientRect();
|
||||
let top = window.pageYOffset + inputBounds.top;
|
||||
let left = window.pageXOffset + inputBounds.right;
|
||||
let centered = ((inputBounds.height - wrapBounds.height) / 2);
|
||||
|
||||
wrap.style.top = (top + centered) + 'px';
|
||||
wrap.style.left = (left + 10) + 'px';
|
||||
}
|
||||
|
||||
buildHtml() {
|
||||
let html = '<div class="ftip-wrap"></div>';
|
||||
document.body.insertAdjacentHTML('beforeend', html);
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
var that = this;
|
||||
let wrap = qs('.ftip-wrap');
|
||||
|
||||
var delayHandler = () => {
|
||||
that.delay = setTimeout(() => {
|
||||
wrap.classList.remove('active');
|
||||
}, 250);
|
||||
};
|
||||
|
||||
that.on('mouseover', '.ftip-enabled', function(e) {
|
||||
clearTimeout(that.delay);
|
||||
this.ftip.open();
|
||||
});
|
||||
|
||||
that.on('mouseout', '.ftip-enabled', delayHandler);
|
||||
that.on('mouseout', '.ftip-wrap', delayHandler);
|
||||
that.on('mouseover', '.ftip-wrap', () => {
|
||||
clearTimeout(that.delay);
|
||||
});
|
||||
}
|
||||
|
||||
on(eventName, elementSelector, handler) {
|
||||
document.addEventListener(eventName, function(e) {
|
||||
// loop parent nodes from the target to the delegation node
|
||||
for (var target = e.target; target && target != this; target = target.parentNode) {
|
||||
if (target.matches(elementSelector)) {
|
||||
handler.call(target, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
|
||||
var $ = (selector, options) => new fTip(selector, options);
|
||||
|
||||
return $;
|
||||
})();
|
||||
7
wp/wp-content/plugins/facetwp/assets/vendor/fUtil/LICENSE.md
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright 2021 FacetWP, LLC
|
||||
|
||||
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.
|
||||
451
wp/wp-content/plugins/facetwp/assets/vendor/fUtil/fUtil.js
vendored
Normal file
@@ -0,0 +1,451 @@
|
||||
window.fUtil = (() => {
|
||||
|
||||
class fUtil {
|
||||
|
||||
constructor(selector) {
|
||||
if (typeof selector === 'string' || selector instanceof String) { // string
|
||||
var selector = selector.replace(':selected', ':checked');
|
||||
|
||||
if ('' === selector) {
|
||||
this.nodes = [];
|
||||
}
|
||||
else if (this.isValidSelector(selector)) {
|
||||
this.nodes = Array.from(document.querySelectorAll(selector));
|
||||
}
|
||||
else {
|
||||
var tpl = document.createElement('template');
|
||||
tpl.innerHTML = selector;
|
||||
this.nodes = [tpl.content];
|
||||
}
|
||||
}
|
||||
else if (Array.isArray(selector)) { // array of nodes
|
||||
this.nodes = selector;
|
||||
}
|
||||
else if (typeof selector === 'object' && selector.nodeName) { // node
|
||||
this.nodes = [selector];
|
||||
}
|
||||
else if (typeof selector === 'function') { // function
|
||||
this.ready(selector);
|
||||
}
|
||||
else if (selector === window) { // window
|
||||
this.nodes = [window];
|
||||
}
|
||||
else { // document
|
||||
this.nodes = [document];
|
||||
}
|
||||
|
||||
// custom plugins
|
||||
$.each($.fn, (handler, method) => {
|
||||
this[method] = handler;
|
||||
});
|
||||
}
|
||||
|
||||
static isset(input) {
|
||||
return typeof input !== 'undefined';
|
||||
}
|
||||
|
||||
static post(url, data, settings) {
|
||||
var settings = Object.assign({}, {
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
headers: {},
|
||||
done: () => {},
|
||||
fail: () => {}
|
||||
}, settings);
|
||||
|
||||
settings.headers['Content-Type'] = settings.contentType;
|
||||
|
||||
data = ('application/json' === settings.contentType) ? JSON.stringify(data) : $.toEncoded(data);
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: settings.headers,
|
||||
body: data
|
||||
})
|
||||
.then(response => response[settings.dataType]())
|
||||
.then(json => settings.done(json))
|
||||
.catch(err => settings.fail(err));
|
||||
}
|
||||
|
||||
static toEncoded(obj, prefix, out) {
|
||||
var out = out || [];
|
||||
var prefix = prefix || '';
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
if (obj.length) {
|
||||
obj.forEach((val) => {
|
||||
$.toEncoded(val, prefix + '[]', out);
|
||||
});
|
||||
}
|
||||
else {
|
||||
$.toEncoded('', prefix, out);
|
||||
}
|
||||
}
|
||||
else if (typeof obj === 'object' && obj !== null) {
|
||||
Object.keys(obj).forEach((key) => {
|
||||
var new_prefix = prefix ? prefix + '[' + key + ']' : key;
|
||||
$.toEncoded(obj[key], new_prefix, out);
|
||||
});
|
||||
}
|
||||
else {
|
||||
out.push(encodeURIComponent(prefix) + '=' + encodeURIComponent(obj));
|
||||
}
|
||||
|
||||
return out.join('&');
|
||||
}
|
||||
|
||||
static forEach(items, callback) {
|
||||
if (typeof items === 'object' && items !== null) {
|
||||
if (Array.isArray(items)) {
|
||||
items.forEach((val, key) => callback.bind(val)(val, key));
|
||||
}
|
||||
else {
|
||||
Object.keys(items).forEach(key => {
|
||||
var val = items[key];
|
||||
callback.bind(val)(val, key);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
isValidSelector(string) {
|
||||
try {
|
||||
document.createDocumentFragment().querySelector(string);
|
||||
}
|
||||
catch(err) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
clone() {
|
||||
return $(this.nodes);
|
||||
}
|
||||
|
||||
len() {
|
||||
return this.nodes.length;
|
||||
}
|
||||
|
||||
each(callback) {
|
||||
this.nodes.forEach((node, key) => {
|
||||
let func = callback.bind(node); // set "this"
|
||||
func(node, key);
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
ready(callback) {
|
||||
if (typeof callback !== 'function') return;
|
||||
|
||||
if (document.readyState === 'complete') {
|
||||
return callback();
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', callback, false);
|
||||
}
|
||||
|
||||
addClass(className) {
|
||||
this.each(node => node.classList.add(className));
|
||||
return this;
|
||||
}
|
||||
|
||||
removeClass(className) {
|
||||
this.each(node => node.classList.remove(className));
|
||||
return this;
|
||||
}
|
||||
|
||||
hasClass(className) {
|
||||
return $.isset(this.nodes.find(node => node.classList.contains(className)));
|
||||
}
|
||||
|
||||
toggleClass(className) {
|
||||
this.each(node => node.classList.toggle(className));
|
||||
return this;
|
||||
}
|
||||
|
||||
is(selector) {
|
||||
for (let i = 0; i < this.len(); i++) { // forEach prevents loop exiting
|
||||
if (this.nodes[i].matches(selector)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
find(selector) {
|
||||
var selector = selector.replace(':selected', ':checked');
|
||||
let nodes = [];
|
||||
let clone = this.clone();
|
||||
|
||||
clone.each(node => {
|
||||
nodes = nodes.concat(Array.from(node.querySelectorAll(selector)));
|
||||
});
|
||||
|
||||
clone.nodes = nodes;
|
||||
return clone;
|
||||
}
|
||||
|
||||
first() {
|
||||
let clone = this.clone();
|
||||
if (clone.len()) {
|
||||
clone.nodes = this.nodes.slice(0, 1);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
last() {
|
||||
let clone = this.clone();
|
||||
if (clone.len()) {
|
||||
clone.nodes = this.nodes.slice(-1);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
prev(selector) {
|
||||
let nodes = [];
|
||||
let clone = this.clone();
|
||||
|
||||
clone.each(node => {
|
||||
let sibling = node.previousElementSibling;
|
||||
|
||||
while (sibling) {
|
||||
if (!$.isset(selector) || sibling.matches(selector)) break;
|
||||
sibling = sibling.previousElementSibling;
|
||||
}
|
||||
|
||||
if (sibling) {
|
||||
nodes.push(sibling);
|
||||
}
|
||||
});
|
||||
|
||||
clone.nodes = nodes;
|
||||
return clone;
|
||||
}
|
||||
|
||||
next(selector) {
|
||||
let nodes = [];
|
||||
let clone = this.clone();
|
||||
|
||||
clone.each(node => {
|
||||
let sibling = node.nextElementSibling;
|
||||
|
||||
while (sibling) {
|
||||
if (!$.isset(selector) || sibling.matches(selector)) break;
|
||||
sibling = sibling.nextElementSibling;
|
||||
}
|
||||
|
||||
if (sibling) {
|
||||
nodes.push(sibling);
|
||||
}
|
||||
});
|
||||
|
||||
clone.nodes = nodes;
|
||||
return clone;
|
||||
}
|
||||
|
||||
prepend(html) {
|
||||
this.each(node => node.insertAdjacentHTML('afterbegin', html));
|
||||
return this;
|
||||
}
|
||||
|
||||
append(html) {
|
||||
this.each(node => node.insertAdjacentHTML('beforeend', html));
|
||||
return this;
|
||||
}
|
||||
|
||||
parents(selector) {
|
||||
let parents = [];
|
||||
let clone = this.clone();
|
||||
|
||||
clone.each(node => {
|
||||
let parent = node.parentNode;
|
||||
while (parent && parent !== document) {
|
||||
if (parent.matches(selector)) parents.push(parent);
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
});
|
||||
|
||||
clone.nodes = [...new Set(parents)]; // remove dupes
|
||||
return clone;
|
||||
}
|
||||
|
||||
closest(selector) {
|
||||
let nodes = [];
|
||||
let clone = this.clone();
|
||||
|
||||
clone.each(node => {
|
||||
let closest = node.closest(selector);
|
||||
|
||||
if (closest) {
|
||||
nodes.push(closest);
|
||||
}
|
||||
});
|
||||
|
||||
clone.nodes = nodes;
|
||||
return clone;
|
||||
}
|
||||
|
||||
remove() {
|
||||
this.each(node => node.remove());
|
||||
return this;
|
||||
}
|
||||
|
||||
on(eventName, selector, callback) {
|
||||
if (!$.isset(selector)) return;
|
||||
if (!$.isset(callback)) {
|
||||
var callback = selector;
|
||||
var selector = null;
|
||||
}
|
||||
|
||||
// Reusable callback
|
||||
var checkForMatch = (e) => {
|
||||
if (null === selector || e.target.matches(selector)) {
|
||||
callback.bind(e.target)(e);
|
||||
}
|
||||
else if (e.target.closest(selector)) {
|
||||
var $this = e.target.closest(selector);
|
||||
callback.bind($this)(e);
|
||||
}
|
||||
};
|
||||
|
||||
this.each(node => {
|
||||
|
||||
// Attach a unique ID to each node
|
||||
if (!$.isset(node._id)) {
|
||||
node._id = $.event.count;
|
||||
$.event.store[$.event.count] = node;
|
||||
$.event.count++;
|
||||
}
|
||||
|
||||
var id = node._id;
|
||||
|
||||
// Store the raw callback, needed for .off()
|
||||
checkForMatch._str = callback.toString();
|
||||
|
||||
if (!$.isset($.event.map[id])) {
|
||||
$.event.map[id] = {};
|
||||
}
|
||||
if (!$.isset($.event.map[id][eventName])) {
|
||||
$.event.map[id][eventName] = {};
|
||||
}
|
||||
if (!$.isset($.event.map[id][eventName][selector])) {
|
||||
$.event.map[id][eventName][selector] = [];
|
||||
}
|
||||
|
||||
// Use $.event.map to store event references
|
||||
// removeEventListener needs named callbacks, so we're creating
|
||||
// one for every handler
|
||||
let length = $.event.map[id][eventName][selector].push(checkForMatch);
|
||||
|
||||
node.addEventListener(eventName, $.event.map[id][eventName][selector][length - 1]);
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
off(eventName, selector, callback) {
|
||||
if (!$.isset(callback)) {
|
||||
var callback = selector;
|
||||
var selector = null;
|
||||
}
|
||||
|
||||
this.each(node => {
|
||||
var id = node._id;
|
||||
|
||||
$.each($.event.map[id], (selectors, theEventName) => {
|
||||
$.each(selectors, (callbacks, theSelector) => {
|
||||
$.each(callbacks, (theCallback, index) => {
|
||||
if (
|
||||
(!eventName || theEventName === eventName) &&
|
||||
(!selector || theSelector === selector) &&
|
||||
(!callback || theCallback._str === callback.toString())
|
||||
) {
|
||||
node.removeEventListener(theEventName, $.event.map[id][theEventName][theSelector][index]);
|
||||
delete $.event.map[id][theEventName][theSelector][index];
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
trigger(eventName, extraData) {
|
||||
this.each(node => node.dispatchEvent(new CustomEvent(eventName, {
|
||||
detail: extraData,
|
||||
bubbles: true
|
||||
})));
|
||||
return this;
|
||||
}
|
||||
|
||||
attr(attributeName, value) {
|
||||
if (!$.isset(value)) {
|
||||
return (this.len()) ? this.nodes[0].getAttribute(attributeName) : null;
|
||||
}
|
||||
|
||||
this.each(node => node.setAttribute(attributeName, value));
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
data(key, value) {
|
||||
if (!$.isset(value)) {
|
||||
return (this.len()) ? this.nodes[0]._fdata[key] : null;
|
||||
}
|
||||
|
||||
this.each(node => node._fdata[key] = value);
|
||||
return this;
|
||||
}
|
||||
|
||||
html(htmlString) {
|
||||
if (!$.isset(htmlString)) {
|
||||
return (this.len()) ? this.nodes[0].innerHTML : null;
|
||||
}
|
||||
|
||||
this.each(node => node.innerHTML = htmlString);
|
||||
return this;
|
||||
}
|
||||
|
||||
text(textString) {
|
||||
if (!$.isset(textString)) {
|
||||
return (this.len()) ? this.nodes[0].textContent : null;
|
||||
}
|
||||
else {
|
||||
this.each(node => node.textContent = textString);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
val(value) {
|
||||
if (!$.isset(value)) {
|
||||
if (this.len()) {
|
||||
var field = this.nodes[0];
|
||||
if (field.nodeName.toLowerCase() === 'select' && field.multiple) {
|
||||
return [...field.options].filter((x) => x.selected).map((x) => x.value);
|
||||
}
|
||||
return field.value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
this.each(node => node.value = value);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var $ = selector => new fUtil(selector);
|
||||
|
||||
// Set object methods
|
||||
$.fn = {};
|
||||
$.post = fUtil.post;
|
||||
$.isset = fUtil.isset;
|
||||
$.each = fUtil.forEach;
|
||||
$.toEncoded = fUtil.toEncoded;
|
||||
$.event = {map: {}, store: [], count: 0};
|
||||
return $;
|
||||
})();
|
||||
150
wp/wp-content/plugins/facetwp/assets/vendor/noUiSlider/nouislider.css
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
.noUi-target,
|
||||
.noUi-target * {
|
||||
touch-action: none;
|
||||
user-select: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.noUi-target {
|
||||
position: relative;
|
||||
}
|
||||
.noUi-base,
|
||||
.noUi-connects {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.noUi-connects {
|
||||
overflow: hidden;
|
||||
z-index: 0;
|
||||
}
|
||||
.noUi-connect,
|
||||
.noUi-origin {
|
||||
will-change: transform;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
transform-origin: 0 0;
|
||||
transform-style: flat;
|
||||
}
|
||||
.noUi-txt-dir-rtl.noUi-horizontal .noUi-origin {
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
.noUi-horizontal .noUi-origin {
|
||||
height: 0;
|
||||
}
|
||||
.noUi-handle {
|
||||
backface-visibility: hidden;
|
||||
position: absolute;
|
||||
}
|
||||
.noUi-touch-area {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.noUi-state-tap .noUi-connect,
|
||||
.noUi-state-tap .noUi-origin {
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
.noUi-horizontal {
|
||||
height: 14px;
|
||||
}
|
||||
.noUi-horizontal .noUi-handle {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
right: -10px;
|
||||
top: -4px;
|
||||
}
|
||||
.noUi-target {
|
||||
background: #FAFAFA;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #D3D3D3;
|
||||
padding: 0 8px;
|
||||
}
|
||||
.noUi-connects {
|
||||
border-radius: 3px;
|
||||
}
|
||||
.noUi-connect {
|
||||
background: #ddd;
|
||||
}
|
||||
.noUi-handle {
|
||||
border: 1px solid #D9D9D9;
|
||||
border-radius: 3px;
|
||||
background: #FFF;
|
||||
cursor: default;
|
||||
}
|
||||
.noUi-pips,
|
||||
.noUi-pips * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.noUi-pips {
|
||||
position: absolute;
|
||||
color: #999;
|
||||
}
|
||||
.noUi-value {
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
.noUi-value-sub {
|
||||
color: #ccc;
|
||||
font-size: 10px;
|
||||
}
|
||||
.noUi-marker {
|
||||
position: absolute;
|
||||
background: #CCC;
|
||||
}
|
||||
.noUi-marker-sub {
|
||||
background: #AAA;
|
||||
}
|
||||
.noUi-marker-large {
|
||||
background: #AAA;
|
||||
}
|
||||
.noUi-pips-horizontal {
|
||||
padding: 10px 0;
|
||||
height: 80px;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.noUi-value-horizontal {
|
||||
transform: translate(-50%, 50%);
|
||||
}
|
||||
.noUi-rtl .noUi-value-horizontal {
|
||||
transform: translate(50%, 50%);
|
||||
}
|
||||
.noUi-marker-horizontal.noUi-marker {
|
||||
margin-left: -1px;
|
||||
width: 2px;
|
||||
height: 5px;
|
||||
}
|
||||
.noUi-marker-horizontal.noUi-marker-sub {
|
||||
height: 10px;
|
||||
}
|
||||
.noUi-marker-horizontal.noUi-marker-large {
|
||||
height: 15px;
|
||||
}
|
||||
.noUi-tooltip {
|
||||
display: block;
|
||||
position: absolute;
|
||||
border: 1px solid #D9D9D9;
|
||||
border-radius: 3px;
|
||||
background: #fff;
|
||||
color: #000;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.noUi-horizontal .noUi-tooltip {
|
||||
transform: translate(-50%, 0);
|
||||
left: 50%;
|
||||
bottom: 120%;
|
||||
}
|
||||
.noUi-horizontal .noUi-origin > .noUi-tooltip {
|
||||
transform: translate(50%, 0);
|
||||
left: auto;
|
||||
bottom: 10px;
|
||||
}
|
||||
2
wp/wp-content/plugins/facetwp/assets/vendor/noUiSlider/nouislider.min.js
vendored
Normal file
87
wp/wp-content/plugins/facetwp/assets/vendor/nummy/nummy.js
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Nummy.js - A (very) lightweight number formatter
|
||||
* @link https://github.com/mgibbs189/nummy
|
||||
*/
|
||||
(function() {
|
||||
function isValid(input) {
|
||||
return !isNaN(parseFloat(input)) && isFinite(input);
|
||||
}
|
||||
|
||||
function toFixed(value, precision) {
|
||||
var pow = Math.pow(10, precision);
|
||||
return (Math.round(value * pow) / pow).toFixed(precision);
|
||||
}
|
||||
|
||||
class Nummy {
|
||||
constructor(value) {
|
||||
this._value = isValid(value) ? value : 0;
|
||||
}
|
||||
|
||||
format(format, opts) {
|
||||
var value = this._value,
|
||||
negative = false,
|
||||
precision = 0,
|
||||
valueStr = '',
|
||||
wholeStr = '',
|
||||
decimalStr = '',
|
||||
abbr = '';
|
||||
|
||||
var opts = Object.assign({}, {
|
||||
'thousands_separator': ',',
|
||||
'decimal_separator': '.'
|
||||
}, opts);
|
||||
|
||||
if (-1 < format.indexOf('a')) {
|
||||
var abbrevs = ['K', 'M', 'B', 't', 'q', 'Q'];
|
||||
var exp = Math.floor(Math.log(Math.abs(value)) * Math.LOG10E); // log10 polyfill
|
||||
var nearest_exp = (exp - (exp % 3)); // nearest exponent divisible by 3
|
||||
|
||||
if (3 <= exp) {
|
||||
value = value / Math.pow(10, nearest_exp);
|
||||
abbr += abbrevs[Math.floor(exp / 3) - 1];
|
||||
}
|
||||
|
||||
format = format.replace('a', '');
|
||||
}
|
||||
|
||||
// Check for decimals format
|
||||
if (-1 < format.indexOf('.')) {
|
||||
precision = format.split('.')[1].length;
|
||||
}
|
||||
|
||||
value = toFixed(value, precision);
|
||||
valueStr = value.toString();
|
||||
|
||||
// Handle negative number
|
||||
if (value < 0) {
|
||||
negative = true;
|
||||
value = Math.abs(value);
|
||||
valueStr = valueStr.slice(1);
|
||||
}
|
||||
|
||||
wholeStr = valueStr.split('.')[0] || '';
|
||||
decimalStr = valueStr.split('.')[1] || '';
|
||||
|
||||
// Handle decimals
|
||||
decimalStr = (0 < precision && '' != decimalStr) ? '.' + decimalStr : '';
|
||||
|
||||
// Use thousands separators
|
||||
if (-1 < format.indexOf(',')) {
|
||||
wholeStr = wholeStr.replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
|
||||
}
|
||||
|
||||
var output = (negative ? '-' : '') + wholeStr + decimalStr + abbr;
|
||||
|
||||
output = output.replace(/\./g, '{d}');
|
||||
output = output.replace(/\,/g, '{t}');
|
||||
output = output.replace(/{d}/g, opts.decimal_separator);
|
||||
output = output.replace(/{t}/g, opts.thousands_separator);
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
window.nummy = function(input) {
|
||||
return new Nummy(input);
|
||||
}
|
||||
})();
|
||||
1
wp/wp-content/plugins/facetwp/assets/vendor/nummy/nummy.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!function(){"use strict";var a;(a=function(a){var t;this._value=(t=a,!isNaN(parseFloat(t))&&isFinite(t)?a:0)}).prototype.format=function(a,t){var e=this._value,r=!1,i=0,n="",o="",s="",l="";if(t=Object.assign({},{thousands_separator:",",decimal_separator:"."},t),-1<a.indexOf("a")){var p=Math.floor(Math.log(Math.abs(e))*Math.LOG10E),c=p-p%3;3<=p&&(e/=Math.pow(10,c),l+=["K","M","B","t","q","Q"][Math.floor(p/3)-1]),a=a.replace("a","")}-1<a.indexOf(".")&&(i=a.split(".")[1].length),e=function(a,t){var e=Math.pow(10,t);return(Math.round(a*e)/e).toFixed(t)}(e,i),n=e.toString(),e<0&&(r=!0,e=Math.abs(e),n=n.slice(1)),o=n.split(".")[0]||"",s=n.split(".")[1]||"",s=0<i&&""!=s?"."+s:"",-1<a.indexOf(",")&&(o=o.replace(/(\d)(?=(\d{3})+(?!\d))/g,"$1,"));var d=(r?"-":"")+o+s+l;return d=(d=(d=(d=d.replace(/\./g,"{d}")).replace(/\,/g,"{t}")).replace(/{d}/g,t.decimal_separator)).replace(/{t}/g,t.thousands_separator)},window.nummy=function(t){return new a(t)}}();
|
||||
8
wp/wp-content/plugins/facetwp/assets/vendor/vanilla-picker-mini/vanilla-picker-mini.min.js
vendored
Normal file
2
wp/wp-content/plugins/facetwp/assets/vendor/vue/Sortable.min.js
vendored
Normal file
1
wp/wp-content/plugins/facetwp/assets/vendor/vue/vue-select/vue-select.css
vendored
Normal file
2
wp/wp-content/plugins/facetwp/assets/vendor/vue/vue-select/vue-select.js
vendored
Normal file
6
wp/wp-content/plugins/facetwp/assets/vendor/vue/vue.min.js
vendored
Normal file
2
wp/wp-content/plugins/facetwp/assets/vendor/vue/vuedraggable.min.js
vendored
Normal file
180
wp/wp-content/plugins/facetwp/includes/api/fetch.php
Normal file
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_API_Fetch
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
add_action( 'rest_api_init', [ $this, 'register' ] );
|
||||
}
|
||||
|
||||
|
||||
// PHP < 5.3
|
||||
function register() {
|
||||
register_rest_route( 'facetwp/v1', '/fetch', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [ $this, 'callback' ],
|
||||
'permission_callback' => [ $this, 'permission_callback' ]
|
||||
] );
|
||||
}
|
||||
|
||||
|
||||
// PHP < 5.3
|
||||
function callback( $request ) {
|
||||
$data = $request->get_param( 'data' );
|
||||
|
||||
if ( ! $request->is_json_content_type()) {
|
||||
$data = empty( $data ) ? [] : json_decode( $data, true );
|
||||
}
|
||||
|
||||
return $this->process_request( $data );
|
||||
}
|
||||
|
||||
|
||||
// PHP < 5.3
|
||||
function permission_callback( $request ) {
|
||||
return apply_filters( 'facetwp_api_can_access', false, $request );
|
||||
}
|
||||
|
||||
|
||||
function process_request( $params = [] ) {
|
||||
global $wpdb;
|
||||
|
||||
$defaults = [
|
||||
'facets' => [
|
||||
// 'category' => [ 'acf' ]
|
||||
],
|
||||
'query_args' => [
|
||||
'post_type' => 'post',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => 10,
|
||||
'paged' => 1,
|
||||
],
|
||||
'settings' => [
|
||||
'first_load' => true
|
||||
]
|
||||
];
|
||||
|
||||
$params = array_merge( $defaults, $params );
|
||||
$facet_types = FWP()->helper->facet_types;
|
||||
$valid_facets = [];
|
||||
$facets = [];
|
||||
|
||||
// Validate input
|
||||
$page = (int) ( $params['query_args']['paged'] ?? 1 );
|
||||
$per_page = (int) ( $params['query_args']['posts_per_page'] ?? 10 );
|
||||
|
||||
$page = max( $page, 1 );
|
||||
$per_page = ( 0 === $per_page ) ? 10 : $per_page;
|
||||
$per_page = ( -1 > $per_page ) ? absint( $per_page ) : $per_page;
|
||||
|
||||
$params['query_args']['paged'] = $page;
|
||||
$params['query_args']['posts_per_page'] = $per_page;
|
||||
|
||||
// Generate FWP()->facet->facets
|
||||
// Required by FWP()->helper->facet_setting_exists()
|
||||
foreach ( $params['facets'] as $facet_name => $facet_value ) {
|
||||
$facet = FWP()->helper->get_facet_by_name( $facet_name );
|
||||
if ( false !== $facet ) {
|
||||
$facet['selected_values'] = (array) $facet_value;
|
||||
$valid_facets[ $facet_name ] = $facet;
|
||||
FWP()->facet->facets[ $facet_name ] = $facet;
|
||||
}
|
||||
}
|
||||
|
||||
// Get bucket of post IDs
|
||||
$query_args = $params['query_args'];
|
||||
FWP()->facet->query_args = $query_args;
|
||||
$post_ids = FWP()->facet->get_filtered_post_ids( $query_args );
|
||||
|
||||
// SQL WHERE used by facets
|
||||
$where_clause = ' AND post_id IN (' . implode( ',', $post_ids ) . ')';
|
||||
|
||||
// Check if empty
|
||||
if ( 0 === $post_ids[0] && 1 === count( $post_ids ) ) {
|
||||
$post_ids = [];
|
||||
}
|
||||
|
||||
// get_where_clause() needs "found_posts" (keep this BELOW the empty check)
|
||||
FWP()->facet->query = (object) [ 'found_posts' => count( $post_ids ) ];
|
||||
|
||||
// Get valid facets and their values
|
||||
foreach ( $valid_facets as $facet_name => $facet ) {
|
||||
$args = [
|
||||
'facet' => $facet,
|
||||
'where_clause' => $where_clause,
|
||||
'selected_values' => $facet['selected_values'],
|
||||
];
|
||||
|
||||
$facet_data = [
|
||||
'name' => $facet['name'],
|
||||
'label' => $facet['label'],
|
||||
'type' => $facet['type'],
|
||||
'selected' => $facet['selected_values'],
|
||||
];
|
||||
|
||||
// Load facet choices if available
|
||||
if ( method_exists( $facet_types[ $facet['type'] ], 'load_values' ) ) {
|
||||
$choices = $facet_types[ $facet['type'] ]->load_values( $args );
|
||||
foreach ( $choices as $key => $choice ) {
|
||||
$row = [
|
||||
'value' => $choice['facet_value'],
|
||||
'label' => $choice['facet_display_value'],
|
||||
'depth' => (int) $choice['depth'],
|
||||
'count' => (int) $choice['counter'],
|
||||
];
|
||||
|
||||
if ( isset( $choice['term_id'] ) ) {
|
||||
$row['term_id'] = (int) $choice['term_id'];
|
||||
}
|
||||
|
||||
if ( isset( $choice['parent_id'] ) ) {
|
||||
$row['parent_id'] = (int) $choice['parent_id'];
|
||||
}
|
||||
|
||||
$choices[ $key ] = $row;
|
||||
}
|
||||
|
||||
$facet_data['choices'] = $choices;
|
||||
}
|
||||
|
||||
// Load facet settings if available
|
||||
if ( method_exists( $facet_types[ $facet['type'] ], 'settings_js' ) ) {
|
||||
$facet_data['settings'] = $facet_types[ $facet['type'] ]->settings_js( $args );
|
||||
}
|
||||
|
||||
$facets[ $facet_name ] = $facet_data;
|
||||
}
|
||||
|
||||
$total_rows = count( $post_ids );
|
||||
|
||||
// Paginate?
|
||||
if ( 0 < $per_page ) {
|
||||
$total_pages = ceil( $total_rows / $per_page );
|
||||
|
||||
if ( $page > $total_pages ) {
|
||||
$post_ids = [];
|
||||
}
|
||||
else {
|
||||
$offset = ( $per_page * ( $page - 1 ) );
|
||||
$post_ids = array_slice( $post_ids, $offset, $per_page );
|
||||
}
|
||||
}
|
||||
else {
|
||||
$total_pages = ( 0 < $total_rows ) ? 1 : 0;
|
||||
}
|
||||
|
||||
// Generate the output
|
||||
$output = [
|
||||
'results' => $post_ids,
|
||||
'facets' => $facets,
|
||||
'pager' => [
|
||||
'page' => $page,
|
||||
'per_page' => $per_page,
|
||||
'total_rows' => $total_rows,
|
||||
'total_pages' => $total_pages,
|
||||
]
|
||||
];
|
||||
|
||||
return apply_filters( 'facetwp_api_output', $output );
|
||||
}
|
||||
}
|
||||
27
wp/wp-content/plugins/facetwp/includes/api/refresh.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
add_action( 'rest_api_init', function() {
|
||||
register_rest_route( 'facetwp/v1', '/refresh', [
|
||||
'methods' => 'POST',
|
||||
'callback' => 'facetwp_api_refresh',
|
||||
'permission_callback' => '__return_true'
|
||||
] );
|
||||
});
|
||||
|
||||
function facetwp_api_refresh( $request ) {
|
||||
$params = $request->get_params();
|
||||
$action = $params['action'] ?? '';
|
||||
|
||||
$valid_actions = [
|
||||
'facetwp_refresh',
|
||||
'facetwp_autocomplete_load'
|
||||
];
|
||||
|
||||
$valid_actions = apply_filters( 'facetwp_api_valid_actions', $valid_actions );
|
||||
|
||||
if ( in_array( $action, $valid_actions ) ) {
|
||||
do_action( $action );
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
321
wp/wp-content/plugins/facetwp/includes/class-ajax.php
Normal file
@@ -0,0 +1,321 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Ajax
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
add_action( 'init', [ $this, 'switchboard' ], 1000 );
|
||||
}
|
||||
|
||||
|
||||
function switchboard() {
|
||||
$valid_actions = [
|
||||
'resume_index',
|
||||
'save_settings',
|
||||
'rebuild_index',
|
||||
'get_info',
|
||||
'get_query_args',
|
||||
'heartbeat',
|
||||
'license',
|
||||
'backup'
|
||||
];
|
||||
|
||||
$action = isset( $_POST['action'] ) ? sanitize_key( $_POST['action'] ) : '';
|
||||
$is_valid = false;
|
||||
|
||||
if ( 0 === strpos( $action, 'facetwp_' ) ) {
|
||||
$action = substr( $action, 8 );
|
||||
$is_valid = in_array( $action, $valid_actions );
|
||||
}
|
||||
|
||||
if ( $is_valid ) {
|
||||
|
||||
// Non-authenticated
|
||||
if ( in_array( $action, [ 'resume_index' ] ) ) {
|
||||
$this->$action();
|
||||
}
|
||||
|
||||
// Authenticated
|
||||
elseif ( current_user_can( 'manage_options' ) ) {
|
||||
if ( wp_verify_nonce( $_POST['nonce'], 'fwp_admin_nonce' ) ) {
|
||||
$this->$action();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for API refresh call
|
||||
add_action( 'facetwp_refresh', [ $this, 'refresh' ] );
|
||||
|
||||
// Backwards compatibility
|
||||
$this->url_vars = FWP()->request->url_vars;
|
||||
$this->is_preload = FWP()->request->is_preload;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save admin settings
|
||||
*/
|
||||
function save_settings() {
|
||||
$settings = $_POST['data'];
|
||||
|
||||
if ( isset( $settings['settings'] ) ) {
|
||||
update_option( 'facetwp_settings', json_encode( $settings ), 'no' );
|
||||
|
||||
if ( FWP()->diff->is_reindex_needed() ) {
|
||||
$response = [
|
||||
'code' => 'error',
|
||||
'message' => __( 'Settings saved, please re-index', 'fwp' )
|
||||
];
|
||||
}
|
||||
else {
|
||||
$response = [
|
||||
'code' => 'success',
|
||||
'message' => __( 'Settings saved', 'fwp' )
|
||||
];
|
||||
}
|
||||
}
|
||||
else {
|
||||
$response = [
|
||||
'code' => 'error',
|
||||
'message' => __( 'Error: invalid JSON', 'fwp' )
|
||||
];
|
||||
}
|
||||
|
||||
wp_send_json( $response );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rebuild the index table
|
||||
*/
|
||||
function rebuild_index() {
|
||||
update_option( 'facetwp_indexing_cancelled', 'no', 'no' );
|
||||
FWP()->indexer->index();
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
function get_info() {
|
||||
$type = $_POST['type'];
|
||||
|
||||
if ( 'post_types' == $type ) {
|
||||
$post_types = get_post_types( [ 'exclude_from_search' => false, '_builtin' => false ] );
|
||||
$post_types = [ 'post', 'page' ] + $post_types;
|
||||
sort( $post_types );
|
||||
|
||||
$response = [
|
||||
'code' => 'success',
|
||||
'message' => implode( ', ', $post_types )
|
||||
];
|
||||
}
|
||||
elseif ( 'indexer_stats' == $type ) {
|
||||
$last_indexed = get_option( 'facetwp_last_indexed' );
|
||||
$last_indexed = $last_indexed ? human_time_diff( $last_indexed ) . ' ago' : 'never';
|
||||
|
||||
$response = [
|
||||
'code' => 'success',
|
||||
'message' => "last indexed: $last_indexed"
|
||||
];
|
||||
}
|
||||
elseif ( 'cancel_reindex' == $type ) {
|
||||
update_option( 'facetwp_indexing_cancelled', 'yes' );
|
||||
|
||||
$response = [
|
||||
'code' => 'success',
|
||||
'message' => 'Indexing cancelled'
|
||||
];
|
||||
}
|
||||
elseif ( 'purge_index_table' == $type ) {
|
||||
global $wpdb;
|
||||
|
||||
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}facetwp_index" );
|
||||
delete_option( 'facetwp_version' );
|
||||
delete_option( 'facetwp_indexing' );
|
||||
delete_option( 'facetwp_transients' );
|
||||
|
||||
$response = [
|
||||
'code' => 'success',
|
||||
'message' => __( 'Done, please re-index', 'fwp' )
|
||||
];
|
||||
}
|
||||
|
||||
wp_send_json( $response );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return query arguments based on a Query Builder object
|
||||
*/
|
||||
function get_query_args() {
|
||||
$query_obj = $_POST['query_obj'];
|
||||
|
||||
if ( is_array( $query_obj ) ) {
|
||||
$query_args = FWP()->builder->parse_query_obj( $query_obj );
|
||||
}
|
||||
|
||||
wp_send_json( $query_args );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Keep track of indexing progress
|
||||
*/
|
||||
function heartbeat() {
|
||||
$output = [
|
||||
'pct' => FWP()->indexer->get_progress()
|
||||
];
|
||||
|
||||
if ( -1 == $output['pct'] ) {
|
||||
$output['rows'] = FWP()->helper->get_row_counts();
|
||||
}
|
||||
|
||||
wp_send_json( $output );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* License activation
|
||||
*/
|
||||
function license() {
|
||||
$license = sanitize_key( $_POST['license'] );
|
||||
|
||||
$request = wp_remote_post( 'https://api.facetwp.com', [
|
||||
'body' => [
|
||||
'action' => 'activate',
|
||||
'slug' => 'facetwp',
|
||||
'license' => $license,
|
||||
'host' => FWP()->helper->get_http_host(),
|
||||
]
|
||||
] );
|
||||
|
||||
if ( ! is_wp_error( $request ) || 200 == wp_remote_retrieve_response_code( $request ) ) {
|
||||
update_option( 'facetwp_license', $license );
|
||||
update_option( 'facetwp_activation', $request['body'] );
|
||||
update_option( 'facetwp_updater_last_checked', 0 );
|
||||
echo $request['body'];
|
||||
}
|
||||
else {
|
||||
echo json_encode( [
|
||||
'status' => 'error',
|
||||
'message' => __( 'Error', 'fwp' ) . ': ' . $request->get_error_message(),
|
||||
] );
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Import / export functionality
|
||||
*/
|
||||
function backup() {
|
||||
$action_type = $_POST['action_type'];
|
||||
$output = [];
|
||||
|
||||
if ( 'export' == $action_type ) {
|
||||
$items = $_POST['items'];
|
||||
|
||||
if ( ! empty( $items ) ) {
|
||||
foreach ( $items as $item ) {
|
||||
if ( 'facet' == substr( $item, 0, 5 ) ) {
|
||||
$item_name = substr( $item, 6 );
|
||||
$output['facets'][] = FWP()->helper->get_facet_by_name( $item_name );
|
||||
}
|
||||
elseif ( 'template' == substr( $item, 0, 8 ) ) {
|
||||
$item_name = substr( $item, 9 );
|
||||
$output['templates'][] = FWP()->helper->get_template_by_name( $item_name );
|
||||
}
|
||||
}
|
||||
}
|
||||
echo json_encode( $output );
|
||||
}
|
||||
elseif ( 'import' == $action_type ) {
|
||||
$settings = FWP()->helper->settings;
|
||||
$import_code = $_POST['import_code'];
|
||||
$overwrite = (int) $_POST['overwrite'];
|
||||
|
||||
if ( empty( $import_code ) || ! is_array( $import_code ) ) {
|
||||
_e( 'Nothing to import', 'fwp' );
|
||||
exit;
|
||||
}
|
||||
|
||||
$status = [
|
||||
'imported' => [],
|
||||
'skipped' => [],
|
||||
];
|
||||
|
||||
foreach ( $import_code as $object_type => $object_items ) {
|
||||
foreach ( $object_items as $object_item ) {
|
||||
$is_match = false;
|
||||
foreach ( $settings[$object_type] as $key => $settings_item ) {
|
||||
if ( $object_item['name'] == $settings_item['name'] ) {
|
||||
if ( $overwrite ) {
|
||||
$settings[$object_type][$key] = $object_item;
|
||||
$status['imported'][] = $object_item['label'];
|
||||
}
|
||||
else {
|
||||
$status['skipped'][] = $object_item['label'];
|
||||
}
|
||||
$is_match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $is_match ) {
|
||||
$settings[$object_type][] = $object_item;
|
||||
$status['imported'][] = $object_item['label'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_option( 'facetwp_settings', json_encode( $settings ) );
|
||||
|
||||
if ( ! empty( $status['imported'] ) ) {
|
||||
echo ' [<strong>' . __( 'Imported', 'fwp' ) . '</strong>] ' . implode( ', ', $status['imported'] );
|
||||
}
|
||||
if ( ! empty( $status['skipped'] ) ) {
|
||||
echo ' [<strong>' . __( 'Skipped', 'fwp' ) . '</strong>] ' . implode( ', ', $status['skipped'] );
|
||||
}
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The AJAX facet refresh handler
|
||||
*/
|
||||
function refresh() {
|
||||
global $wpdb;
|
||||
|
||||
$params = FWP()->request->process_post_data();
|
||||
$output = FWP()->facet->render( $params );
|
||||
$data = stripslashes_deep( $_POST['data'] );
|
||||
|
||||
// Ignore invalid UTF-8 characters in PHP 7.2+
|
||||
if ( version_compare( phpversion(), '7.2', '<' ) ) {
|
||||
$output = json_encode( $output );
|
||||
}
|
||||
else {
|
||||
$output = json_encode( $output, JSON_INVALID_UTF8_IGNORE );
|
||||
}
|
||||
|
||||
echo apply_filters( 'facetwp_ajax_response', $output, [
|
||||
'data' => $data
|
||||
] );
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resume stalled indexer
|
||||
*/
|
||||
function resume_index() {
|
||||
$touch = (int) FWP()->indexer->get_transient( 'touch' );
|
||||
if ( 0 < $touch && $_POST['touch'] == $touch ) {
|
||||
FWP()->indexer->index();
|
||||
}
|
||||
exit;
|
||||
}
|
||||
}
|
||||
839
wp/wp-content/plugins/facetwp/includes/class-builder.php
Normal file
@@ -0,0 +1,839 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Builder
|
||||
{
|
||||
|
||||
public $css = [];
|
||||
public $data = [];
|
||||
public $custom_css;
|
||||
|
||||
|
||||
function __construct() {
|
||||
add_filter( 'facetwp_query_args', [ $this, 'hydrate_date_values' ], 999 );
|
||||
add_filter( 'facetwp_builder_dynamic_tag_value', [ $this, 'dynamic_tag_value' ], 0, 3 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate CSS class string (helper method)
|
||||
* @since 3.9.3
|
||||
*/
|
||||
function get_classes( $type, $settings ) {
|
||||
$classes = [ $type, $settings['name'], $settings['css_class'] ];
|
||||
return trim( implode( ' ', $classes ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the layout HTML
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function render_layout( $layout ) {
|
||||
global $wp_query, $post;
|
||||
|
||||
$counter = 0;
|
||||
$settings = $layout['settings'];
|
||||
$this->custom_css = $settings['custom_css'];
|
||||
|
||||
$selector = '.fwpl-layout';
|
||||
$selector .= empty( $settings['name'] ) ? '' : '.' . $settings['name'];
|
||||
|
||||
$this->css = [
|
||||
'.fwpl-layout, .fwpl-row' => [
|
||||
'display' => 'grid'
|
||||
],
|
||||
$selector => [
|
||||
'grid-template-columns' => 'repeat(' . $settings['num_columns'] . ', 1fr)',
|
||||
'grid-gap' => $settings['grid_gap'] . 'px'
|
||||
],
|
||||
$selector . ' .fwpl-result' => $this->build_styles( $settings )
|
||||
];
|
||||
|
||||
$classes = $this->get_classes( 'fwpl-layout', $settings );
|
||||
|
||||
$output = '<div class="' . $classes . '">';
|
||||
|
||||
if ( have_posts() ) {
|
||||
while ( have_posts() ) : the_post();
|
||||
$counter++;
|
||||
|
||||
// Default dynamic tags
|
||||
$tags = [
|
||||
'post:id' => $post->ID,
|
||||
'post:name' => $post->post_name,
|
||||
'post:type' => $post->post_type,
|
||||
'post:title' => $post->post_title,
|
||||
'post:url' => get_permalink()
|
||||
];
|
||||
|
||||
$params = [
|
||||
'layout' => $layout,
|
||||
'post' => $post
|
||||
];
|
||||
|
||||
$this->data = apply_filters( 'facetwp_builder_dynamic_tags', $tags, $params );
|
||||
|
||||
$output .= '<div class="fwpl-result r' . $counter . '">';
|
||||
|
||||
foreach ( $layout['items'] as $row ) {
|
||||
$output .= $this->render_row( $row );
|
||||
}
|
||||
|
||||
$output .= '</div>';
|
||||
|
||||
$output = $this->parse_dynamic_tags( $output, $params );
|
||||
|
||||
endwhile;
|
||||
}
|
||||
else {
|
||||
$no_results_text = $settings['no_results_text'] ?? '';
|
||||
$output .= do_shortcode( $no_results_text );
|
||||
}
|
||||
|
||||
$output .= '</div>';
|
||||
|
||||
$output .= $this->render_css();
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the row HTML
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function render_row( $row ) {
|
||||
$settings = $row['settings'];
|
||||
|
||||
$this->css['.fwpl-row.' . $settings['name'] ] = $this->build_styles( $settings );
|
||||
|
||||
$classes = $this->get_classes( 'fwpl-row', $settings );
|
||||
|
||||
$output = '<div class="' . $classes . '">';
|
||||
|
||||
foreach ( $row['items'] as $col ) {
|
||||
$output .= $this->render_col( $col );
|
||||
}
|
||||
|
||||
$output .= '</div>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the col HTML
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function render_col( $col ) {
|
||||
$settings = $col['settings'];
|
||||
|
||||
$this->css['.fwpl-col.' . $settings['name'] ] = $this->build_styles( $settings );
|
||||
|
||||
$classes = $this->get_classes( 'fwpl-col', $settings );
|
||||
|
||||
$output = '<div class="fwpl-col ' . $classes . '">';
|
||||
|
||||
foreach ( $col['items'] as $item ) {
|
||||
if ( 'row' == $item['type'] ) {
|
||||
$output .= $this->render_row( $item );
|
||||
}
|
||||
elseif ( 'item' == $item['type'] ) {
|
||||
$output .= $this->render_item( $item );
|
||||
}
|
||||
}
|
||||
|
||||
$output .= '</div>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the item HTML
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function render_item( $item ) {
|
||||
global $post;
|
||||
|
||||
$settings = $item['settings'];
|
||||
$name = $settings['name'];
|
||||
$source = $item['source'];
|
||||
$value = $source;
|
||||
|
||||
$selector = '.fwpl-item.' . $name;
|
||||
$selector = ( 'button' == $source ) ? $selector . ' button' : $selector;
|
||||
$this->css[ $selector ] = $this->build_styles( $settings );
|
||||
|
||||
if ( 0 === strpos( $source, 'post_' ) || 'ID' == $source ) {
|
||||
if ( 'post_title' == $source ) {
|
||||
$value = $this->linkify( $post->$source, $settings['link'] );
|
||||
}
|
||||
elseif ( 'post_excerpt' == $source ) {
|
||||
$value = get_the_excerpt( $post->ID );
|
||||
}
|
||||
elseif ( 'post_content' == $source ) {
|
||||
$value = apply_filters( 'the_content', $post->post_content );
|
||||
}
|
||||
elseif ( 'post_author' == $source ) {
|
||||
$field = $settings['author_field'];
|
||||
$user = get_user_by( 'id', $post->$source );
|
||||
$value = $user->$field;
|
||||
}
|
||||
elseif ( 'post_type' == $source ) {
|
||||
$pt_obj = get_post_type_object( $post->$source );
|
||||
$value = $pt_obj->labels->singular_name ?? $post->$source;
|
||||
}
|
||||
else {
|
||||
$value = $post->$source;
|
||||
}
|
||||
}
|
||||
elseif ( 0 === strpos( $source, 'cf/' ) ) {
|
||||
$value = get_post_meta( $post->ID, substr( $source, 3 ), true );
|
||||
$value = $this->linkify( $value, $settings['link'] );
|
||||
}
|
||||
elseif ( 0 === strpos( $source, 'tax/' ) ) {
|
||||
$temp = [];
|
||||
$taxonomy = substr( $source, 4 );
|
||||
$terms = get_the_terms( $post->ID, $taxonomy );
|
||||
|
||||
if ( is_array( $terms ) ) {
|
||||
foreach ( $terms as $term_obj ) {
|
||||
$term = $this->linkify( $term_obj->name, $settings['term_link'], [
|
||||
'term_id' => $term_obj->term_id,
|
||||
'taxonomy' => $taxonomy
|
||||
] );
|
||||
|
||||
$temp[] = '<span class="fwpl-term fwpl-term-' . $term_obj->slug . ' fwpl-tax-' . $taxonomy . '">' . $term . '</span>';
|
||||
}
|
||||
}
|
||||
|
||||
$value = implode( $settings['separator'], $temp );
|
||||
}
|
||||
elseif ( 0 === strpos( $source, 'woo/' ) ) {
|
||||
$field = substr( $source, 4 );
|
||||
$product = wc_get_product( $post->ID );
|
||||
|
||||
// Invalid product
|
||||
if ( ! is_object( $product ) ) {
|
||||
$value = '';
|
||||
}
|
||||
|
||||
// Price
|
||||
elseif ( 'price' == $field || 'sale_price' == $field || 'regular_price' == $field ) {
|
||||
if ( $product->is_type( 'variable' ) ) {
|
||||
$method_name = "get_variation_$field";
|
||||
$value = $product->$method_name( 'min' ); // get_variation_price()
|
||||
}
|
||||
else {
|
||||
$method_name = "get_$field";
|
||||
$value = $product->$method_name(); // get_price()
|
||||
}
|
||||
}
|
||||
|
||||
// Average Rating
|
||||
elseif ( 'average_rating' == $field ) {
|
||||
$value = $product->get_average_rating();
|
||||
}
|
||||
|
||||
// Stock Status
|
||||
elseif ( 'stock_status' == $field ) {
|
||||
$value = $product->is_in_stock() ? __( 'In Stock', 'fwp' ) : __( 'Out of Stock', 'fwp' );
|
||||
}
|
||||
|
||||
// On Sale
|
||||
elseif ( 'on_sale' == $field ) {
|
||||
$value = $product->is_on_sale() ? __( 'On Sale', 'fwp' ) : '';
|
||||
}
|
||||
|
||||
// Product Type
|
||||
elseif ( 'product_type' == $field ) {
|
||||
$value = $product->get_type();
|
||||
}
|
||||
}
|
||||
elseif ( 0 === strpos( $source, 'acf/' ) && isset( FWP()->acf ) ) {
|
||||
$value = FWP()->acf->get_field( $source, $post->ID );
|
||||
}
|
||||
elseif ( 'featured_image' == $source ) {
|
||||
$value = get_the_post_thumbnail( $post->ID, $settings['image_size'] );
|
||||
$value = $this->linkify( $value, $settings['link'] );
|
||||
}
|
||||
elseif ( 'button' == $source ) {
|
||||
$value = '<button>' . $settings['button_text'] . '</button>';
|
||||
$value = $this->linkify( $value, $settings['link'] );
|
||||
}
|
||||
elseif ( 'html' == $source ) {
|
||||
$value = do_shortcode( $settings['content'] );
|
||||
}
|
||||
|
||||
// Date format
|
||||
if ( ! empty( $settings['date_format'] ) && ! empty( $value ) ) {
|
||||
if ( ! empty( $settings['input_format'] ) ) {
|
||||
$date = DateTime::createFromFormat( $settings['input_format'], $value );
|
||||
}
|
||||
else {
|
||||
$date = new DateTime( $value );
|
||||
}
|
||||
|
||||
// Use wp_date() to support i18n
|
||||
if ( $date ) {
|
||||
$value = wp_date( $settings['date_format'], $date->getTimestamp(), new DateTimeZone( 'UTC' ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Number format
|
||||
if ( ! empty( $settings['number_format'] ) && ! empty( $value ) ) {
|
||||
$decimals = 2;
|
||||
$format = $settings['number_format'];
|
||||
$decimal_sep = FWP()->helper->get_setting( 'decimal_separator' );
|
||||
$thousands_sep = FWP()->helper->get_setting( 'thousands_separator' );
|
||||
|
||||
// No thousands separator
|
||||
if ( false === strpos( $format, ',' ) ) {
|
||||
$thousands_sep = '';
|
||||
}
|
||||
|
||||
// Handle decimals
|
||||
if ( false === ( $pos = strpos( $format, '.' ) ) ) {
|
||||
$decimals = 0;
|
||||
}
|
||||
else {
|
||||
$decimals = strlen( $format ) - $pos - 1;
|
||||
}
|
||||
|
||||
$value = number_format( $value, $decimals, $decimal_sep, $thousands_sep );
|
||||
}
|
||||
|
||||
$output = '';
|
||||
$prefix = $settings['prefix'] ?? '';
|
||||
$suffix = $settings['suffix'] ?? '';
|
||||
|
||||
// Allow value hooks
|
||||
$value = apply_filters( 'facetwp_builder_item_value', $value, $item );
|
||||
|
||||
// Convert array to string
|
||||
if ( is_array( $value ) ) {
|
||||
$value = implode( ', ', $value );
|
||||
}
|
||||
|
||||
// Store the RAW short-tag
|
||||
$this->data[ "$name:raw" ] = $value;
|
||||
|
||||
// Attach the prefix / suffix to the value
|
||||
if ( '' != $value ) {
|
||||
$value = $prefix . $value . $suffix;
|
||||
}
|
||||
|
||||
// Store the short-tag
|
||||
$this->data[ $name ] = $value;
|
||||
|
||||
// Build the list of CSS classes
|
||||
$classes = $this->get_classes( 'fwpl-item', $settings );
|
||||
|
||||
if ( '' == $value ) {
|
||||
$classes .= ' is-empty';
|
||||
}
|
||||
|
||||
// Prevent output
|
||||
if ( ! $settings['is_hidden'] ) {
|
||||
$output = '<div class="' . $classes . '">' . $value . '</div>';
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse dynamic tags, e.g. {{ first_name }}
|
||||
*/
|
||||
function parse_dynamic_tags( $output, $params ) {
|
||||
$pattern = '/({{[ ]?(.*?)[ ]?}})/s';
|
||||
|
||||
return preg_replace_callback( $pattern, function( $matches ) use( $params ) {
|
||||
$tag_name = $matches[2];
|
||||
$tag_value = $this->data[ $tag_name ] ?? '';
|
||||
return apply_filters( 'facetwp_builder_dynamic_tag_value', $tag_value, $tag_name, $params );
|
||||
}, $output );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate some dynamic tag values on-the-fly, to prevent
|
||||
* unnecessary queries and extra load time
|
||||
*/
|
||||
function dynamic_tag_value( $tag_value, $tag_name, $params ) {
|
||||
if ( 'post:image' == $tag_name ) {
|
||||
$tag_value = get_the_post_thumbnail_url( $params['post']->ID, 'full' );
|
||||
}
|
||||
|
||||
return $tag_value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build the redundant styles (border, padding,etc)
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function build_styles( $settings ) {
|
||||
$styles = [];
|
||||
|
||||
if ( isset( $settings['grid_template_columns'] ) ) {
|
||||
$styles['grid-template-columns'] = $settings['grid_template_columns'];
|
||||
}
|
||||
if ( isset( $settings['border'] ) ) {
|
||||
$styles['border-style'] = $settings['border']['style'];
|
||||
$styles['border-color'] = $settings['border']['color'];
|
||||
$styles['border-width'] = $this->get_widths( $settings['border']['width'] );
|
||||
}
|
||||
if ( isset( $settings['background_color'] ) ) {
|
||||
$styles['background-color'] = $settings['background_color'];
|
||||
}
|
||||
if ( isset( $settings['padding'] ) ) {
|
||||
$styles['padding'] = $this->get_widths( $settings['padding'] );
|
||||
}
|
||||
if ( isset( $settings['text_style'] ) ) {
|
||||
$styles['text-align'] = $settings['text_style']['align'];
|
||||
$styles['font-weight'] = $settings['text_style']['bold'] ? 'bold' : '';
|
||||
$styles['font-style'] = $settings['text_style']['italic'] ? 'italic' : '';
|
||||
}
|
||||
if ( isset( $settings['font_size'] ) ) {
|
||||
$styles['font-size'] = $settings['font_size']['size'] . $settings['font_size']['unit'];
|
||||
}
|
||||
if ( isset( $settings['text_color'] ) ) {
|
||||
$styles['color'] = $settings['text_color'];
|
||||
}
|
||||
if ( isset( $settings['button_border'] ) ) {
|
||||
$border = $settings['button_border'];
|
||||
$width = $border['width'];
|
||||
$unit = $width['unit'];
|
||||
|
||||
$styles['color'] = $settings['button_text_color'];
|
||||
$styles['background-color'] = $settings['button_color'];
|
||||
$styles['padding'] = $this->get_widths( $settings['button_padding'] );
|
||||
$styles['border-style'] = $border['style'];
|
||||
$styles['border-color'] = $border['color'];
|
||||
$styles['border-top-width'] = $width['top'] . $unit;
|
||||
$styles['border-right-width'] = $width['right'] . $unit;
|
||||
$styles['border-bottom-width'] = $width['bottom'] . $unit;
|
||||
$styles['border-left-width'] = $width['left'] . $unit;
|
||||
}
|
||||
|
||||
return $styles;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build the CSS widths, e.g. for "padding" or "border-width"
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function get_widths( $data ) {
|
||||
$unit = $data['unit'];
|
||||
$top = $data['top'];
|
||||
$right = $data['right'];
|
||||
$bottom = $data['bottom'];
|
||||
$left = $data['left'];
|
||||
|
||||
if ( $top == $right && $right == $bottom && $bottom == $left ) {
|
||||
return "$top$unit";
|
||||
}
|
||||
elseif ( $top == $bottom && $left == $right ) {
|
||||
return "$top$unit $left$unit";
|
||||
}
|
||||
|
||||
return "$top$unit $right$unit $bottom$unit $left$unit";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a value into a link
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function linkify( $value, $link_data, $term_data = [] ) {
|
||||
global $post;
|
||||
|
||||
$type = $link_data['type'];
|
||||
$href = $link_data['href'];
|
||||
$target = $link_data['target'];
|
||||
|
||||
if ( 'none' !== $type ) {
|
||||
if ( 'post' == $type ) {
|
||||
$href = get_permalink();
|
||||
}
|
||||
if ( 'term' == $type ) {
|
||||
$href = get_term_link( $term_data['term_id'], $term_data['taxonomy'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $target ) ) {
|
||||
$target = ' target="' . $target . '"';
|
||||
}
|
||||
|
||||
$value = '<a href="' . $href . '"' . $target . '>' . $value . '</a>';
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Turn the CSS array into valid CSS
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function render_css() {
|
||||
$output = "\n<style>\n";
|
||||
|
||||
foreach ( $this->css as $selector => $props ) {
|
||||
$valid_rules = $this->get_valid_css_rules( $props );
|
||||
|
||||
if ( ! empty( $valid_rules ) ) {
|
||||
$output .= $selector . " {\n";
|
||||
foreach ( $valid_rules as $prop => $value ) {
|
||||
$output .= " $prop: $value;\n";
|
||||
}
|
||||
$output .= "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $this->custom_css ) ) {
|
||||
$output .= $this->custom_css . "\n";
|
||||
}
|
||||
|
||||
$output .= "</style>\n";
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter out empty or invalid rules
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function get_valid_css_rules( $props ) {
|
||||
$rules = [];
|
||||
|
||||
foreach ( $props as $prop => $value ) {
|
||||
if ( $this->is_valid_css_rule( $prop, $value ) ) {
|
||||
$rules[ $prop ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Optimize CSS rules
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function is_valid_css_rule( $prop, $value ) {
|
||||
$return = true;
|
||||
|
||||
if ( empty( $value ) || 'px' === $value || '0px' === $value || 'none' === $value ) {
|
||||
$return = false;
|
||||
}
|
||||
|
||||
if ( 'font-size' === $prop && '0px' === $value ) {
|
||||
$return = false;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make sure the query is valid
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function parse_query_obj( $query_obj ) {
|
||||
$output = [];
|
||||
$tax_query = [];
|
||||
$meta_query = [];
|
||||
$date_query = [];
|
||||
$post_type = 'any';
|
||||
$post_status = [ 'publish' ];
|
||||
$posts_per_page = 10;
|
||||
$post_in = [];
|
||||
$post_not_in = [];
|
||||
$author_in = [];
|
||||
$author_not_in = [];
|
||||
$orderby = [];
|
||||
|
||||
if ( ! empty( $query_obj['posts_per_page'] ) ) {
|
||||
$posts_per_page = (int) $query_obj['posts_per_page'];
|
||||
}
|
||||
|
||||
if ( ! empty( $query_obj['post_type'] ) ) {
|
||||
$post_type = array_column( $query_obj['post_type'], 'value' );
|
||||
}
|
||||
|
||||
if ( empty( $query_obj['filters'] ) ) {
|
||||
$query_obj['filters'] = [];
|
||||
}
|
||||
|
||||
if ( empty( $query_obj['orderby'] ) ) {
|
||||
$query_obj['orderby'] = [];
|
||||
}
|
||||
|
||||
foreach ( $query_obj['filters'] as $filter ) {
|
||||
$key = $filter['key'];
|
||||
$value = $filter['value'];
|
||||
$compare = $filter['compare'];
|
||||
$type = $filter['type'];
|
||||
|
||||
// Cast as decimal for more accuracy
|
||||
$type = ( 'NUMERIC' == $type ) ? 'DECIMAL(16,4)' : $type;
|
||||
$exists_bypass = false;
|
||||
$value_bypass = false;
|
||||
|
||||
// Clear the value for certain compare types
|
||||
if ( in_array( $compare, [ 'EXISTS', 'NOT EXISTS', 'EMPTY', 'NOT EMPTY' ] ) ) {
|
||||
$value_bypass = true;
|
||||
$value = '';
|
||||
}
|
||||
|
||||
if ( in_array( $compare, [ 'EXISTS', 'NOT EXISTS' ] ) ) {
|
||||
$exists_bypass = true;
|
||||
}
|
||||
|
||||
// If "EMPTY", use "=" compare type w/ empty string value
|
||||
if ( in_array( $compare, [ 'EMPTY', 'NOT EMPTY' ] ) ) {
|
||||
$compare = ( 'EMPTY' == $compare ) ? '=' : '!=';
|
||||
}
|
||||
|
||||
// Handle multiple values
|
||||
if ( is_array( $value ) ) {
|
||||
if ( in_array( $compare, [ '=', '!=' ] ) ) {
|
||||
$compare = ( '=' == $compare ) ? 'IN' : 'NOT IN';
|
||||
}
|
||||
|
||||
if ( ! in_array( $compare, [ 'IN', 'NOT IN' ] ) ) {
|
||||
$value = $value[0];
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $value ) && ! $value_bypass ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Support dynamic URL vars
|
||||
$value = $this->parse_uri_tags( $value );
|
||||
|
||||
// Prepend with "date|" so we can populate with hydrate_date_values()
|
||||
if ( 'DATE' == $type ) {
|
||||
$value = 'date|' . $value;
|
||||
}
|
||||
|
||||
if ( 'ID' == $key ) {
|
||||
$arg_name = ( 'IN' == $compare ) ? 'post_in' : 'post_not_in';
|
||||
$$arg_name = $value;
|
||||
}
|
||||
elseif ( 'post_author' == $key ) {
|
||||
$arg_name = ( 'IN' == $compare ) ? 'author_in' : 'author_not_in';
|
||||
$$arg_name = $value;
|
||||
}
|
||||
elseif ( 'post_status' == $key ) {
|
||||
$post_status = $value;
|
||||
}
|
||||
elseif ( 'post_date' == $key || 'post_modified' == $key ) {
|
||||
if ( '>' == $compare || '>=' == $compare ) {
|
||||
$date_query[] = [
|
||||
'after' => $value,
|
||||
'inclusive' => ( '>=' == $compare )
|
||||
];
|
||||
}
|
||||
if ( '<' == $compare || '<=' == $compare ) {
|
||||
$date_query[] = [
|
||||
'before' => $value,
|
||||
'inclusive' => ( '<=' == $compare )
|
||||
];
|
||||
}
|
||||
}
|
||||
elseif ( 0 === strpos( $key, 'tax/' ) ) {
|
||||
$temp = [
|
||||
'taxonomy' => substr( $key, 4 ),
|
||||
'field' => 'slug',
|
||||
'operator' => $compare
|
||||
];
|
||||
|
||||
if ( ! $exists_bypass ) {
|
||||
$temp['terms'] = $value;
|
||||
}
|
||||
|
||||
$tax_query[] = $temp;
|
||||
}
|
||||
else {
|
||||
$temp = [
|
||||
'key' => substr( $key, strpos( $key, '/' ) + 1 ),
|
||||
'compare' => $compare,
|
||||
'type' => $type
|
||||
];
|
||||
|
||||
if ( ! $exists_bypass ) {
|
||||
$temp['value'] = $value;
|
||||
}
|
||||
|
||||
$meta_query[] = $temp;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $query_obj['orderby'] as $index => $data ) {
|
||||
if ( 'cf/' == substr( $data['key'], 0, 3 ) ) {
|
||||
$type = $data['type'];
|
||||
|
||||
// Cast as decimal for more accuracy
|
||||
$type = ( 'NUMERIC' == $type ) ? 'DECIMAL(16,4)' : $type;
|
||||
|
||||
$meta_query['sort_' . $index] = [
|
||||
'key' => substr( $data['key'], 3 ),
|
||||
'type' => $type
|
||||
];
|
||||
|
||||
$orderby['sort_' . $index] = $data['order'];
|
||||
}
|
||||
else {
|
||||
$orderby[ $data['key'] ] = $data['order'];
|
||||
}
|
||||
}
|
||||
|
||||
$temp = [
|
||||
'post_type' => $post_type,
|
||||
'post_status' => $post_status,
|
||||
'meta_query' => $meta_query,
|
||||
'tax_query' => $tax_query,
|
||||
'date_query' => $date_query,
|
||||
'post__in' => $post_in,
|
||||
'post__not_in' => $post_not_in,
|
||||
'author__in' => $author_in,
|
||||
'author__not_in' => $author_not_in,
|
||||
'orderby' => $orderby,
|
||||
'posts_per_page' => $posts_per_page
|
||||
];
|
||||
|
||||
foreach ( $temp as $key => $val ) {
|
||||
if ( ! empty( $val ) ) {
|
||||
$output[ $key ] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get necessary values for the layout builder
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function get_layout_data() {
|
||||
$sources = FWP()->helper->get_data_sources();
|
||||
unset( $sources['post'] );
|
||||
|
||||
// Static options
|
||||
$output = [
|
||||
'row' => 'Child Row',
|
||||
'html' => 'HTML',
|
||||
'button' => 'Button',
|
||||
'featured_image' => 'Featured Image',
|
||||
'ID' => 'Post ID',
|
||||
'post_title' => 'Post Title',
|
||||
'post_name' => 'Post Name',
|
||||
'post_content' => 'Post Content',
|
||||
'post_excerpt' => 'Post Excerpt',
|
||||
'post_date' => 'Post Date',
|
||||
'post_modified' => 'Post Modified',
|
||||
'post_author' => 'Post Author',
|
||||
'post_type' => 'Post Type'
|
||||
];
|
||||
|
||||
foreach ( $sources as $group ) {
|
||||
foreach ( $group['choices'] as $name => $label ) {
|
||||
$output[ $name ] = $label;
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get necessary data for the query builder
|
||||
* @since 3.0.0
|
||||
*/
|
||||
function get_query_data() {
|
||||
$builder_post_types = [];
|
||||
|
||||
$post_types = get_post_types( [ 'public' => true ], 'objects' );
|
||||
$data_sources = FWP()->helper->get_data_sources( 'builder' );
|
||||
|
||||
// Remove ACF choices
|
||||
unset( $data_sources['acf'] );
|
||||
|
||||
foreach ( $post_types as $type ) {
|
||||
$builder_post_types[] = [
|
||||
'label' => $type->labels->name,
|
||||
'value' => $type->name
|
||||
];
|
||||
}
|
||||
|
||||
$data_sources['posts']['choices'] = [
|
||||
'ID' => 'ID',
|
||||
'post_author' => 'Post Author',
|
||||
'post_status' => 'Post Status',
|
||||
'post_date' => 'Post Date',
|
||||
'post_modified' => 'Post Modified'
|
||||
];
|
||||
|
||||
return apply_filters( 'facetwp_builder_query_data', [
|
||||
'post_types' => $builder_post_types,
|
||||
'filter_by' => $data_sources
|
||||
] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace "date|" placeholders with actual dates
|
||||
*/
|
||||
function hydrate_date_values( $query_args ) {
|
||||
if ( isset( $query_args['meta_query'] ) ) {
|
||||
foreach ( $query_args['meta_query'] as $index => $row ) {
|
||||
if ( isset( $row['value'] ) && is_string( $row['value'] ) && 0 === strpos( $row['value'], 'date|' ) ) {
|
||||
$value = trim( substr( $row['value'], 5 ) );
|
||||
$value = date( 'Y-m-d', strtotime( $value ) );
|
||||
$query_args['meta_query'][ $index ]['value'] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $query_args;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Let users pull URI or GET params into the query builder
|
||||
* E.g. "http:uri", "http:uri:0", or "http:get:year"
|
||||
* @since 3.6.0
|
||||
*/
|
||||
function parse_uri_tags( $values ) {
|
||||
$temp = (array) $values;
|
||||
|
||||
foreach ( $temp as $key => $value ) {
|
||||
if ( 0 === strpos( $value, 'http:uri' ) ) {
|
||||
$uri = FWP()->helper->get_uri();
|
||||
$uri_parts = explode( '/', $uri );
|
||||
$tag_parts = explode( ':', $value );
|
||||
if ( isset( $tag_parts[2] ) ) {
|
||||
$index = (int) $tag_parts[2];
|
||||
$index = ( $index < 0 ) ? count( $uri_parts ) + $index : $index;
|
||||
$temp[ $key ] = $uri_parts[ $index ] ?? '';
|
||||
}
|
||||
else {
|
||||
$temp[ $key ] = $uri;
|
||||
}
|
||||
}
|
||||
elseif ( 0 === strpos( $value, 'http:get:' ) ) {
|
||||
$tag_parts = explode( ':', $value );
|
||||
$temp[ $key ] = $_GET[ $tag_parts[2] ] ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
return is_array( $values ) ? $temp : $temp[0];
|
||||
}
|
||||
}
|
||||
73
wp/wp-content/plugins/facetwp/includes/class-diff.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Diff
|
||||
{
|
||||
|
||||
/**
|
||||
* Compare "facetwp_settings" with "facetwp_settings_last_index" to determine
|
||||
* whether the user needs to rebuild the index
|
||||
* @since 3.0.9
|
||||
*/
|
||||
function is_reindex_needed() {
|
||||
$s1 = FWP()->helper->load_settings();
|
||||
$s2 = FWP()->helper->load_settings( true );
|
||||
|
||||
// Compare settings
|
||||
$to_check = [ 'thousands_separator', 'decimal_separator', 'wc_enable_variations', 'wc_index_all' ];
|
||||
|
||||
foreach ( $to_check as $name ) {
|
||||
$attr1 = $this->get_attr( $name, $s1['settings'] );
|
||||
$attr2 = $this->get_attr( $name, $s2['settings'] );
|
||||
if ( $attr1 !== $attr2 ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Get facets, removing non-indexable ones
|
||||
$f1 = array_filter( $s1['facets'], [ $this, 'is_indexable' ] );
|
||||
$f2 = array_filter( $s2['facets'], [ $this, 'is_indexable' ] );
|
||||
|
||||
// The facet count is different
|
||||
if ( count( $f1 ) !== count( $f2 ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sort the facets alphabetically
|
||||
usort( $f1, function( $a, $b ) {
|
||||
return strcmp( $a['name'], $b['name'] );
|
||||
});
|
||||
|
||||
usort( $f2, function( $a, $b ) {
|
||||
return strcmp( $a['name'], $b['name'] );
|
||||
});
|
||||
|
||||
// Compare facet properties
|
||||
$to_check = [ 'name', 'type', 'source', 'source_other', 'parent_term', 'hierarchical', 'modifier_type', 'modifier_values' ];
|
||||
|
||||
foreach ( $f1 as $index => $facet ) {
|
||||
foreach ( $to_check as $attr ) {
|
||||
$attr1 = $this->get_attr( $attr, $facet );
|
||||
$attr2 = $this->get_attr( $attr, $f2[ $index ] );
|
||||
if ( $attr1 !== $attr2 ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function is_indexable( $facet ) {
|
||||
return ! in_array( $facet['type'], [ 'search', 'pager', 'reset', 'sort' ] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array element
|
||||
* @since 3.0.9
|
||||
*/
|
||||
function get_attr( $name, $collection ) {
|
||||
return $collection[ $name ] ?? false;
|
||||
}
|
||||
}
|
||||
244
wp/wp-content/plugins/facetwp/includes/class-display.php
Normal file
@@ -0,0 +1,244 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Display
|
||||
{
|
||||
|
||||
/* (array) Facet types being used on the page */
|
||||
public $active_types = [];
|
||||
|
||||
/* (array) Facets being used on the page */
|
||||
public $active_facets = [];
|
||||
|
||||
/* (array) Extra features used on the page */
|
||||
public $active_extras = [];
|
||||
|
||||
/* (array) Saved shortcode attributes */
|
||||
public $shortcode_atts = [];
|
||||
|
||||
/* (boolean) Whether to enable FacetWP for the current page */
|
||||
public $load_assets = false;
|
||||
|
||||
/* (array) Scripts and stylesheets to enqueue */
|
||||
public $assets = [];
|
||||
|
||||
/* (array) Data to pass to front-end JS */
|
||||
public $json = [];
|
||||
|
||||
|
||||
function __construct() {
|
||||
add_filter( 'widget_text', 'do_shortcode' );
|
||||
add_action( 'loop_start', [ $this, 'add_template_tag' ] );
|
||||
add_action( 'loop_no_results', [ $this, 'add_template_tag' ] );
|
||||
add_action( 'wp_footer', [ $this, 'front_scripts' ], 25 );
|
||||
add_shortcode( 'facetwp', [ $this, 'shortcode' ] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Detect the loop container if the "facetwp-template" class is missing
|
||||
*/
|
||||
function add_template_tag( $wp_query ) {
|
||||
if ( true === $wp_query->get( 'facetwp' ) && did_action( 'wp_head' ) ) {
|
||||
echo "<!--fwp-loop-->\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set default values for atts
|
||||
*
|
||||
* Old: [facetwp template="foo" static]
|
||||
* New: [facetwp template="foo" static="true"]
|
||||
*/
|
||||
function normalize_atts( $atts ) {
|
||||
foreach ( $atts as $key => $val ) {
|
||||
if ( is_int( $key ) ) {
|
||||
$atts[ $val ] = true;
|
||||
unset( $atts[ $key ] );
|
||||
}
|
||||
}
|
||||
return $atts;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register shortcodes
|
||||
*/
|
||||
function shortcode( $atts ) {
|
||||
$atts = $this->normalize_atts( $atts );
|
||||
$this->shortcode_atts[] = $atts;
|
||||
|
||||
$output = '';
|
||||
if ( isset( $atts['facet'] ) ) {
|
||||
$facet = FWP()->helper->get_facet_by_name( $atts['facet'] );
|
||||
|
||||
if ( $facet ) {
|
||||
$ui = empty( $facet['ui_type'] ) ? $facet['type'] : $facet['ui_type'];
|
||||
$ui_attr = empty( $facet['ui_type'] ) ? '' : ' data-ui="' . $ui . '"';
|
||||
$output = '<div class="facetwp-facet facetwp-facet-' . $facet['name'] . ' facetwp-type-' . $ui . '" data-name="' . $facet['name'] . '" data-type="' . $facet['type'] . '"' . $ui_attr . '></div>';
|
||||
|
||||
// Build list of active facet types
|
||||
$this->active_types[ $facet['type'] ] = $facet['type'];
|
||||
$this->active_facets[ $facet['name'] ] = $facet['name'];
|
||||
$this->load_assets = true;
|
||||
}
|
||||
}
|
||||
elseif ( isset( $atts['template'] ) ) {
|
||||
$template = FWP()->helper->get_template_by_name( $atts['template'] );
|
||||
|
||||
if ( $template ) {
|
||||
$class_name = 'facetwp-template';
|
||||
|
||||
// Static template
|
||||
if ( isset( $atts['static'] ) ) {
|
||||
$renderer = new FacetWP_Renderer();
|
||||
$renderer->template = $template;
|
||||
$renderer->query_args = $renderer->get_query_args();
|
||||
$renderer->query = new WP_Query( $renderer->query_args );
|
||||
$html = $renderer->get_template_html();
|
||||
$class_name .= '-static';
|
||||
}
|
||||
// Preload template (search engine visible)
|
||||
else {
|
||||
global $wp_query;
|
||||
|
||||
$temp_query = $wp_query;
|
||||
$args = FWP()->request->process_preload_data( $template['name'] );
|
||||
$preload_data = FWP()->facet->render( $args );
|
||||
$html = $preload_data['template'];
|
||||
$wp_query = $temp_query;
|
||||
}
|
||||
|
||||
$output = '<div class="{class}" data-name="{name}">{html}</div>';
|
||||
$output = str_replace( '{class}', $class_name, $output );
|
||||
$output = str_replace( '{name}', $atts['template'], $output );
|
||||
$output = str_replace( '{html}', $html, $output );
|
||||
|
||||
$this->load_assets = true;
|
||||
}
|
||||
}
|
||||
elseif ( isset( $atts['sort'] ) ) {
|
||||
$this->active_extras['sort'] = true;
|
||||
$output = '<div class="facetwp-sort"></div>';
|
||||
}
|
||||
elseif ( isset( $atts['selections'] ) ) {
|
||||
$output = '<div class="facetwp-selections"></div>';
|
||||
}
|
||||
elseif ( isset( $atts['counts'] ) ) {
|
||||
$this->active_extras['counts'] = true;
|
||||
$output = '<div class="facetwp-counts"></div>';
|
||||
}
|
||||
elseif ( isset( $atts['pager'] ) ) {
|
||||
$this->active_extras['pager'] = true;
|
||||
$output = '<div class="facetwp-pager"></div>';
|
||||
}
|
||||
elseif ( isset( $atts['per_page'] ) ) {
|
||||
$this->active_extras['per_page'] = true;
|
||||
$output = '<div class="facetwp-per-page"></div>';
|
||||
}
|
||||
|
||||
$output = apply_filters( 'facetwp_shortcode_html', $output, $atts );
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Output facet scripts
|
||||
*/
|
||||
function front_scripts() {
|
||||
|
||||
// Not enqueued - front.js needs to load before front_scripts()
|
||||
if ( apply_filters( 'facetwp_load_assets', $this->load_assets ) ) {
|
||||
|
||||
// Load CSS?
|
||||
if ( apply_filters( 'facetwp_load_css', true ) ) {
|
||||
$this->assets['front.css'] = FACETWP_URL . '/assets/css/front.css';
|
||||
}
|
||||
|
||||
// Load required JS
|
||||
$this->assets['front.js'] = FACETWP_URL . '/assets/js/dist/front.min.js';
|
||||
|
||||
// Backwards compat?
|
||||
if ( apply_filters( 'facetwp_load_deprecated', false ) ) {
|
||||
$this->assets['front-deprecated.js'] = FACETWP_URL . '/assets/js/src/deprecated.js';
|
||||
}
|
||||
|
||||
// Load a11y?
|
||||
$a11y = FWP()->helper->get_setting( 'load_a11y', 'no' );
|
||||
$a11y_hook = apply_filters( 'facetwp_load_a11y', false );
|
||||
|
||||
if ( 'yes' == $a11y || $a11y_hook ) {
|
||||
$this->assets['accessibility.js'] = FACETWP_URL . '/assets/js/src/accessibility.js';
|
||||
$this->json['a11y'] = [
|
||||
'label_page' => __( 'Go to page', 'fwp-front' ),
|
||||
'label_page_next' => __( 'Go to next page', 'fwp-front' ),
|
||||
'label_page_prev' => __( 'Go to previous page', 'fwp-front' )
|
||||
];
|
||||
}
|
||||
|
||||
// Pass GET and URI params
|
||||
$http_params = [
|
||||
'get' => $_GET,
|
||||
'uri' => FWP()->helper->get_uri(),
|
||||
'url_vars' => FWP()->request->url_vars,
|
||||
];
|
||||
|
||||
// See FWP()->facet->get_query_args()
|
||||
if ( ! empty( FWP()->facet->archive_args ) ) {
|
||||
$http_params['archive_args'] = FWP()->facet->archive_args;
|
||||
}
|
||||
|
||||
// Populate the FWP_JSON object
|
||||
$this->json['prefix'] = FWP()->helper->get_setting( 'prefix' );
|
||||
$this->json['no_results_text'] = __( 'No results found', 'fwp-front' );
|
||||
$this->json['ajaxurl'] = get_rest_url() . 'facetwp/v1/refresh';
|
||||
$this->json['nonce'] = wp_create_nonce( 'wp_rest' );
|
||||
|
||||
if ( apply_filters( 'facetwp_use_preloader', true ) ) {
|
||||
$overrides = FWP()->request->process_preload_overrides([
|
||||
'facets' => $this->active_facets,
|
||||
'extras' => $this->active_extras,
|
||||
]);
|
||||
$args = FWP()->request->process_preload_data( false, $overrides );
|
||||
$this->json['preload_data'] = FWP()->facet->render( $args );
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
foreach ( $this->active_types as $type ) {
|
||||
$facet_class = FWP()->helper->facet_types[ $type ];
|
||||
if ( method_exists( $facet_class, 'front_scripts' ) ) {
|
||||
$facet_class->front_scripts();
|
||||
}
|
||||
}
|
||||
|
||||
$inline_scripts = ob_get_clean();
|
||||
$assets = apply_filters( 'facetwp_assets', $this->assets );
|
||||
|
||||
foreach ( $assets as $slug => $data ) {
|
||||
$data = (array) $data;
|
||||
$is_css = ( 'css' == substr( $slug, -3 ) );
|
||||
$version = empty( $data[1] ) ? FACETWP_VERSION : $data[1];
|
||||
$url = $data[0];
|
||||
|
||||
if ( false !== strpos( $url, 'facetwp' ) ) {
|
||||
$prefix = ( false !== strpos( $url, '?' ) ) ? '&' : '?';
|
||||
$url .= $prefix . 'ver=' . $version;
|
||||
}
|
||||
|
||||
$html = $is_css ? '<link href="{url}" rel="stylesheet">' : '<script src="{url}"></script>';
|
||||
$html = apply_filters( 'facetwp_asset_html', $html, $url );
|
||||
echo str_replace( '{url}', $url, $html ) . "\n";
|
||||
}
|
||||
|
||||
echo $inline_scripts;
|
||||
?>
|
||||
<script>
|
||||
window.FWP_JSON = <?php echo json_encode( $this->json ); ?>;
|
||||
window.FWP_HTTP = <?php echo json_encode( $http_params ); ?>;
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
||||
600
wp/wp-content/plugins/facetwp/includes/class-helper.php
Normal file
@@ -0,0 +1,600 @@
|
||||
<?php
|
||||
|
||||
final class FacetWP_Helper
|
||||
{
|
||||
|
||||
/* (array) The facetwp_settings option (after hooks) */
|
||||
public $settings;
|
||||
|
||||
/* (array) Associative array of facet objects */
|
||||
public $facet_types;
|
||||
|
||||
/* (array) Cached data sources */
|
||||
public $data_sources;
|
||||
|
||||
/* (array) Cached terms */
|
||||
public $term_cache;
|
||||
|
||||
/* (array) Index table row counts */
|
||||
public $row_counts;
|
||||
|
||||
|
||||
function __construct() {
|
||||
$this->facet_types = $this->get_facet_types();
|
||||
$this->settings = $this->load_settings();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse the URL hostname
|
||||
*/
|
||||
function get_http_host() {
|
||||
return parse_url( get_option( 'home' ), PHP_URL_HOST );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the current page URI
|
||||
*/
|
||||
function get_uri() {
|
||||
if ( isset( FWP()->facet->http_params ) ) {
|
||||
return FWP()->facet->http_params['uri'];
|
||||
}
|
||||
|
||||
$uri = parse_url( $_SERVER['REQUEST_URI'] );
|
||||
return isset( $uri['path'] ) ? trim( $uri['path'], '/' ) : '';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get available facet types
|
||||
*/
|
||||
function get_facet_types() {
|
||||
if ( ! empty( $this->facet_types ) ) {
|
||||
return $this->facet_types;
|
||||
}
|
||||
|
||||
include( FACETWP_DIR . '/includes/facets/base.php' );
|
||||
|
||||
$types = [
|
||||
'checkboxes' => 'Facetwp_Facet_Checkboxes',
|
||||
'dropdown' => 'Facetwp_Facet_Dropdown',
|
||||
'radio' => 'Facetwp_Facet_Radio_Core',
|
||||
'fselect' => 'Facetwp_Facet_fSelect',
|
||||
'hierarchy' => 'Facetwp_Facet_Hierarchy',
|
||||
'slider' => 'Facetwp_Facet_Slider',
|
||||
'search' => 'Facetwp_Facet_Search',
|
||||
'autocomplete' => 'Facetwp_Facet_Autocomplete',
|
||||
'date_range' => 'Facetwp_Facet_Date_Range',
|
||||
'number_range' => 'Facetwp_Facet_Number_Range',
|
||||
'rating' => 'FacetWP_Facet_Rating',
|
||||
'proximity' => 'Facetwp_Facet_Proximity_Core',
|
||||
'pager' => 'FacetWP_Facet_Pager',
|
||||
'reset' => 'FacetWP_Facet_Reset',
|
||||
'sort' => 'FacetWP_Facet_Sort'
|
||||
];
|
||||
|
||||
$facet_types = [];
|
||||
|
||||
foreach ( $types as $slug => $class_name ) {
|
||||
include( FACETWP_DIR . "/includes/facets/$slug.php" );
|
||||
$facet_types[ $slug ] = new $class_name();
|
||||
}
|
||||
|
||||
return apply_filters( 'facetwp_facet_types', $facet_types );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get settings and allow for developer hooks
|
||||
*/
|
||||
function load_settings( $last_index = false ) {
|
||||
$name = $last_index ? 'facetwp_settings_last_index' : 'facetwp_settings';
|
||||
$option = get_option( $name );
|
||||
|
||||
$defaults = [
|
||||
'facets' => [],
|
||||
'templates' => [],
|
||||
'settings' => [
|
||||
'thousands_separator' => ',',
|
||||
'decimal_separator' => '.',
|
||||
'prefix' => '_',
|
||||
'load_jquery' => 'no'
|
||||
]
|
||||
];
|
||||
|
||||
$settings = ( false !== $option ) ? json_decode( $option, true ) : [];
|
||||
$settings = array_merge( $defaults, $settings );
|
||||
$settings['settings'] = array_merge( $defaults['settings'], $settings['settings'] );
|
||||
|
||||
// Store DB-based facet & template names
|
||||
$db_names = [];
|
||||
|
||||
foreach ( $settings['facets'] as $facet ) {
|
||||
$db_names[ 'facet-' . $facet['name'] ] = true;
|
||||
}
|
||||
|
||||
foreach ( $settings['templates'] as $template ) {
|
||||
$db_names[ 'template-' . $template['name'] ] = true;
|
||||
}
|
||||
|
||||
// Programmatically registered
|
||||
$facets = apply_filters( 'facetwp_facets', $settings['facets'] );
|
||||
$templates = apply_filters( 'facetwp_templates', $settings['templates'] );
|
||||
|
||||
$tmp_facets = [];
|
||||
$tmp_templates = [];
|
||||
|
||||
// Merge DB + code-based facets
|
||||
foreach ( $facets as $facet ) {
|
||||
$name = $facet['name'];
|
||||
$is_db_based = isset( $db_names[ "facet-$name" ] );
|
||||
|
||||
if ( ! $is_db_based ) {
|
||||
$facet['_code'] = true;
|
||||
}
|
||||
|
||||
if ( ! $is_db_based || empty( $tmp_facets[ $name ] ) ) {
|
||||
|
||||
// Valid facet type?
|
||||
if ( in_array( $facet['type'], array_keys( $this->facet_types ) ) ) {
|
||||
$tmp_facets[ $name ] = $facet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge DB + code-based templates
|
||||
foreach ( $templates as $template ) {
|
||||
$name = $template['name'];
|
||||
$is_db_based = isset( $db_names[ "template-$name" ] );
|
||||
|
||||
if ( ! $is_db_based ) {
|
||||
$template['_code'] = true;
|
||||
}
|
||||
|
||||
if ( ! $is_db_based || empty( $tmp_templates[ $name ] ) ) {
|
||||
$tmp_templates[ $name ] = $template;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert back to numerical arrays
|
||||
$settings['facets'] = array_values( $tmp_facets );
|
||||
$settings['templates'] = array_values( $tmp_templates );
|
||||
|
||||
// Filtered settings
|
||||
return $settings;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a general setting value
|
||||
*
|
||||
* @param string $name The setting name
|
||||
* @param mixed $default The default value
|
||||
* @since 1.9
|
||||
*/
|
||||
function get_setting( $name, $default = '' ) {
|
||||
return $this->settings['settings'][ $name ] ?? $default;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of all facets
|
||||
* @return array
|
||||
*/
|
||||
function get_facets() {
|
||||
return $this->settings['facets'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of all templates
|
||||
* @return array
|
||||
*/
|
||||
function get_templates() {
|
||||
return $this->settings['templates'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all properties for a single facet
|
||||
* @param string $facet_name
|
||||
* @return mixed An array of facet info, or false
|
||||
*/
|
||||
function get_facet_by_name( $facet_name ) {
|
||||
foreach ( $this->get_facets() as $facet ) {
|
||||
if ( $facet_name == $facet['name'] ) {
|
||||
return $facet;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all properties for a single template
|
||||
*
|
||||
* @param string $template_name
|
||||
* @return mixed An array of template info, or false
|
||||
*/
|
||||
function get_template_by_name( $template_name ) {
|
||||
foreach ( $this->get_templates() as $template ) {
|
||||
if ( $template_name == $template['name'] ) {
|
||||
return $template;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetch facets using one of its settings
|
||||
* @param string $setting_name
|
||||
* @param mixed $setting_value
|
||||
* @return array
|
||||
*/
|
||||
function get_facets_by( $setting, $value ) {
|
||||
$matches = [];
|
||||
|
||||
foreach ( $this->get_facets() as $facet ) {
|
||||
if ( isset( $facet[ $setting ] ) && $value === $facet[ $setting ] ) {
|
||||
$matches[] = $facet;
|
||||
}
|
||||
}
|
||||
|
||||
return $matches;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get terms across all languages (thanks, WPML)
|
||||
* @since 3.8.5
|
||||
*/
|
||||
function get_terms( $taxonomy ) {
|
||||
global $wpdb;
|
||||
|
||||
$sql = "
|
||||
SELECT t.term_id, t.name, t.slug, tt.parent FROM {$wpdb->term_taxonomy} tt
|
||||
INNER JOIN {$wpdb->terms} t ON t.term_id = tt.term_id
|
||||
WHERE tt.taxonomy = %s";
|
||||
|
||||
return $wpdb->get_results( $wpdb->prepare( $sql, $taxonomy ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of term information, including depth
|
||||
* @param string $taxonomy The taxonomy name
|
||||
* @return array Term information
|
||||
* @since 0.9.0
|
||||
*/
|
||||
function get_term_depths( $taxonomy ) {
|
||||
|
||||
if ( isset( $this->term_cache[ $taxonomy ] ) ) {
|
||||
return $this->term_cache[ $taxonomy ];
|
||||
}
|
||||
|
||||
$output = [];
|
||||
$parents = [];
|
||||
|
||||
$terms = $this->get_terms( $taxonomy );
|
||||
|
||||
// Get term parents
|
||||
foreach ( $terms as $term ) {
|
||||
$parents[ $term->term_id ] = $term->parent;
|
||||
}
|
||||
|
||||
// Build the term array
|
||||
foreach ( $terms as $term ) {
|
||||
$output[ $term->term_id ] = [
|
||||
'term_id' => $term->term_id,
|
||||
'name' => $term->name,
|
||||
'slug' => $term->slug,
|
||||
'parent_id' => $term->parent,
|
||||
'depth' => 0
|
||||
];
|
||||
|
||||
$current_parent = $term->parent;
|
||||
while ( 0 < (int) $current_parent ) {
|
||||
$current_parent = $parents[ $current_parent ];
|
||||
$output[ $term->term_id ]['depth']++;
|
||||
|
||||
// Prevent an infinite loop
|
||||
if ( 50 < $output[ $term->term_id ]['depth'] ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->term_cache[ $taxonomy ] = $output;
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finish sorting the facet values
|
||||
* The results are already sorted by depth and (name OR count), we just need
|
||||
* to move the children directly below their parents
|
||||
*/
|
||||
function sort_taxonomy_values( $values = [], $orderby = 'count' ) {
|
||||
$final = [];
|
||||
$cache = [];
|
||||
|
||||
// Create an "order" sort value based on the top-level items
|
||||
foreach ( $values as $key => $val ) {
|
||||
if ( 0 == $val['depth'] ) {
|
||||
$val['order'] = $key;
|
||||
$cache[ $val['term_id'] ] = $key;
|
||||
$final[] = $val;
|
||||
}
|
||||
elseif ( isset( $cache[ $val['parent_id'] ] ) ) { // skip orphans
|
||||
$val['order'] = $cache[ $val['parent_id'] ] . ".$key"; // dot-separated hierarchy string
|
||||
$cache[ $val['term_id'] ] = $val['order'];
|
||||
$final[] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the array based on the new "order" element
|
||||
// Since this is a dot-separated hierarchy string, use version_compare
|
||||
usort( $final, function( $a, $b ) {
|
||||
return version_compare( $a['order'], $b['order'] );
|
||||
});
|
||||
|
||||
return $final;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sanitize SQL data
|
||||
* @return mixed The sanitized value(s)
|
||||
* @since 3.0.7
|
||||
*/
|
||||
function sanitize( $input ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( is_array( $input ) ) {
|
||||
$output = [];
|
||||
|
||||
foreach ( $input as $key => $val ) {
|
||||
$output[ $key ] = $this->sanitize( $val );
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( $wpdb->dbh && $wpdb->use_mysqli ) {
|
||||
$output = mysqli_real_escape_string( $wpdb->dbh, $input );
|
||||
}
|
||||
else {
|
||||
$output = addslashes( $input );
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Does an active facet with the specified setting exist?
|
||||
* @return boolean
|
||||
* @since 1.4.0
|
||||
*/
|
||||
function facet_setting_exists( $setting_name, $setting_value ) {
|
||||
foreach ( FWP()->facet->facets as $f ) {
|
||||
if ( isset( $f[ $setting_name ] ) && $f[ $setting_name ] == $setting_value ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Does this facet have a setting with the specified value?
|
||||
* @return boolean
|
||||
* @since 2.3.4
|
||||
*/
|
||||
function facet_is( $facet, $setting_name, $setting_value ) {
|
||||
if ( is_string( $facet ) ) {
|
||||
$facet = $this->get_facet_by_name( $facet );
|
||||
}
|
||||
|
||||
if ( isset( $facet[ $setting_name ] ) && $facet[ $setting_name ] == $setting_value ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hash a facet value if needed
|
||||
* @return string
|
||||
* @since 2.1
|
||||
*/
|
||||
function safe_value( $value ) {
|
||||
$value = remove_accents( $value );
|
||||
|
||||
if ( preg_match( '/[^a-z0-9_.\- ]/i', $value ) ) {
|
||||
if ( ! preg_match( '/^\d{4}-(0[1-9]|1[012])-([012]\d|3[01])/', $value ) ) {
|
||||
$value = md5( $value );
|
||||
}
|
||||
}
|
||||
|
||||
$value = str_replace( ' ', '-', strtolower( $value ) );
|
||||
$value = preg_replace( '/[-]{2,}/', '-', $value );
|
||||
$value = ( 50 < strlen( $value ) ) ? substr( $value, 0, 50 ) : $value;
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Properly format numbers, taking separators into account
|
||||
* @return number
|
||||
* @since 2.7.5
|
||||
*/
|
||||
function format_number( $num ) {
|
||||
$sep_decimal = $this->get_setting( 'decimal_separator' );
|
||||
$sep_thousands = $this->get_setting( 'thousands_separator' );
|
||||
|
||||
$num = str_replace( $sep_thousands, '', $num );
|
||||
$num = ( ',' == $sep_decimal ) ? str_replace( ',', '.', $num ) : $num;
|
||||
$num = preg_replace( '/[^0-9-.]/', '', $num );
|
||||
|
||||
return $num;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get facet data sources
|
||||
* @return array
|
||||
* @since 2.2.1
|
||||
*/
|
||||
function get_data_sources( $context = 'default' ) {
|
||||
global $wpdb;
|
||||
|
||||
// Cached?
|
||||
if ( ! empty( $this->data_sources ) ) {
|
||||
$sources = $this->data_sources;
|
||||
}
|
||||
else {
|
||||
|
||||
// Get excluded meta keys
|
||||
$excluded_fields = apply_filters( 'facetwp_excluded_custom_fields', [
|
||||
'_edit_last',
|
||||
'_edit_lock',
|
||||
] );
|
||||
|
||||
// Get taxonomies
|
||||
$taxonomies = get_taxonomies( [], 'object' );
|
||||
|
||||
// Get custom fields
|
||||
$meta_keys = $wpdb->get_col( "SELECT DISTINCT meta_key FROM {$wpdb->postmeta} ORDER BY meta_key" );
|
||||
$custom_fields = array_diff( $meta_keys, $excluded_fields );
|
||||
|
||||
$sources = [
|
||||
'posts' => [
|
||||
'label' => __( 'Posts', 'fwp' ),
|
||||
'choices' => [
|
||||
'post_type' => __( 'Post Type', 'fwp' ),
|
||||
'post_date' => __( 'Post Date', 'fwp' ),
|
||||
'post_modified' => __( 'Post Modified', 'fwp' ),
|
||||
'post_title' => __( 'Post Title', 'fwp' ),
|
||||
'post_author' => __( 'Post Author', 'fwp' )
|
||||
],
|
||||
'weight' => 10
|
||||
],
|
||||
'taxonomies' => [
|
||||
'label' => __( 'Taxonomies', 'fwp' ),
|
||||
'choices' => [],
|
||||
'weight' => 20
|
||||
],
|
||||
'custom_fields' => [
|
||||
'label' => __( 'Custom Fields', 'fwp' ),
|
||||
'choices' => [],
|
||||
'weight' => 30
|
||||
]
|
||||
];
|
||||
|
||||
foreach ( $taxonomies as $tax ) {
|
||||
$sources['taxonomies']['choices'][ 'tax/' . $tax->name ] = $tax->labels->name . ' (' . $tax->name . ')';
|
||||
}
|
||||
|
||||
foreach ( $custom_fields as $cf ) {
|
||||
if ( 0 !== strpos( $cf, '_oembed_' ) ) {
|
||||
$sources['custom_fields']['choices'][ 'cf/' . $cf ] = $cf;
|
||||
}
|
||||
}
|
||||
|
||||
$this->data_sources = $sources;
|
||||
}
|
||||
|
||||
$sources = apply_filters( 'facetwp_facet_sources', $sources, $context );
|
||||
|
||||
uasort( $sources, [ $this, 'sort_by_weight' ] );
|
||||
|
||||
return $sources;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort facetwp_facet_sources by weight
|
||||
* @since 2.7.5
|
||||
*/
|
||||
function sort_by_weight( $a, $b ) {
|
||||
$a['weight'] = $a['weight'] ?? 10;
|
||||
$b['weight'] = $b['weight'] ?? 10;
|
||||
|
||||
if ( $a['weight'] == $b['weight'] ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ( $a['weight'] < $b['weight'] ) ? -1 : 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get row counts for all facets
|
||||
* @since 3.3.4
|
||||
*/
|
||||
function get_row_counts() {
|
||||
if ( isset( $this->row_counts ) ) {
|
||||
return $this->row_counts;
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$output = [];
|
||||
$results = $wpdb->get_results( "SELECT facet_name, COUNT(*) AS row_count FROM {$wpdb->prefix}facetwp_index GROUP BY facet_name" );
|
||||
|
||||
foreach ( $results as $result ) {
|
||||
$output[ $result->facet_name ] = (int) $result->row_count;
|
||||
}
|
||||
|
||||
$this->row_counts = $output;
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grab the license key
|
||||
* @since 3.0.3
|
||||
*/
|
||||
function get_license_key() {
|
||||
$license_key = defined( 'FACETWP_LICENSE_KEY' ) ? FACETWP_LICENSE_KEY : get_option( 'facetwp_license' );
|
||||
$license_key = apply_filters( 'facetwp_license_key', $license_key );
|
||||
return sanitize_key( trim( $license_key ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether the license is active
|
||||
* @since 3.3.0
|
||||
*/
|
||||
function is_license_active() {
|
||||
return ( 'success' == $this->get_license_meta( 'status' ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a license meta value
|
||||
* Possible keys: status, message, expiration, payment_id, price_id
|
||||
* @since 3.5.3
|
||||
*/
|
||||
function get_license_meta( $key = 'status' ) {
|
||||
$activation = get_option( 'facetwp_activation' );
|
||||
|
||||
if ( ! empty( $activation ) ) {
|
||||
$data = json_decode( $activation, true );
|
||||
|
||||
if ( isset( $data[ $key ] ) ) {
|
||||
return $data[ $key ];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
691
wp/wp-content/plugins/facetwp/includes/class-indexer.php
Normal file
@@ -0,0 +1,691 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Indexer
|
||||
{
|
||||
|
||||
/* (boolean) wp_insert_post running? */
|
||||
public $is_saving = false;
|
||||
|
||||
/* (boolean) Whether to index a single post */
|
||||
public $index_all = false;
|
||||
|
||||
/* (int) Number of posts to index before updating progress */
|
||||
public $chunk_size = 10;
|
||||
|
||||
/* (string) Whether a temporary table is active */
|
||||
public $table;
|
||||
|
||||
/* (array) Facet properties for the value being indexed */
|
||||
public $facet;
|
||||
|
||||
/* (array) Value modifiers set via the admin UI */
|
||||
public $modifiers;
|
||||
|
||||
|
||||
function __construct() {
|
||||
$this->set_table( 'auto' );
|
||||
$this->run_cron();
|
||||
|
||||
if ( apply_filters( 'facetwp_indexer_is_enabled', true ) ) {
|
||||
$this->run_hooks();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Event listeners
|
||||
* @since 2.8.4
|
||||
*/
|
||||
function run_hooks() {
|
||||
add_action( 'save_post', [ $this, 'save_post' ] );
|
||||
add_action( 'delete_post', [ $this, 'delete_post' ] );
|
||||
add_action( 'edited_term', [ $this, 'edit_term' ], 10, 3 );
|
||||
add_action( 'delete_term', [ $this, 'delete_term' ], 10, 3 );
|
||||
add_action( 'set_object_terms', [ $this, 'set_object_terms' ] );
|
||||
add_action( 'facetwp_indexer_cron', [ $this, 'get_progress' ] );
|
||||
add_filter( 'wp_insert_post_parent', [ $this, 'is_wp_insert_post' ] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cron task
|
||||
* @since 2.8.5
|
||||
*/
|
||||
function run_cron() {
|
||||
if ( ! wp_next_scheduled( 'facetwp_indexer_cron' ) ) {
|
||||
wp_schedule_single_event( time() + 300, 'facetwp_indexer_cron' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the index when posts get saved
|
||||
* @since 0.1.0
|
||||
*/
|
||||
function save_post( $post_id ) {
|
||||
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( false !== wp_is_post_revision( $post_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 'auto-draft' == get_post_status( $post_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->index( $post_id );
|
||||
$this->is_saving = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the index when posts get deleted
|
||||
* @since 0.6.0
|
||||
*/
|
||||
function delete_post( $post_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$wpdb->query( "DELETE FROM {$this->table} WHERE post_id = $post_id" );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the index when terms get saved
|
||||
* @since 0.6.0
|
||||
*/
|
||||
function edit_term( $term_id, $tt_id, $taxonomy ) {
|
||||
global $wpdb;
|
||||
|
||||
$term = get_term( $term_id, $taxonomy );
|
||||
$slug = FWP()->helper->safe_value( $term->slug );
|
||||
$matches = FWP()->helper->get_facets_by( 'source', "tax/$taxonomy" );
|
||||
|
||||
if ( ! empty( $matches ) ) {
|
||||
$facet_names = wp_list_pluck( $matches, 'name' );
|
||||
$facet_names = implode( "','", esc_sql( $facet_names ) );
|
||||
|
||||
$wpdb->query( $wpdb->prepare( "
|
||||
UPDATE {$this->table}
|
||||
SET facet_value = %s, facet_display_value = %s
|
||||
WHERE facet_name IN ('$facet_names') AND term_id = %d",
|
||||
$slug, $term->name, $term_id
|
||||
) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the index when terms get deleted
|
||||
* @since 0.6.0
|
||||
*/
|
||||
function delete_term( $term_id, $tt_id, $taxonomy ) {
|
||||
global $wpdb;
|
||||
|
||||
$matches = FWP()->helper->get_facets_by( 'source', "tax/$taxonomy" );
|
||||
|
||||
if ( ! empty( $matches ) ) {
|
||||
$facet_names = wp_list_pluck( $matches, 'name' );
|
||||
$facet_names = implode( "','", esc_sql( $facet_names ) );
|
||||
|
||||
$wpdb->query( "
|
||||
DELETE FROM {$this->table}
|
||||
WHERE facet_name IN ('$facet_names') AND term_id = $term_id"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* We're hijacking wp_insert_post_parent
|
||||
* Prevent our set_object_terms() hook from firing within wp_insert_post
|
||||
* @since 2.2.2
|
||||
*/
|
||||
function is_wp_insert_post( $post_parent ) {
|
||||
$this->is_saving = true;
|
||||
return $post_parent;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Support for manual taxonomy associations
|
||||
* @since 0.8.0
|
||||
*/
|
||||
function set_object_terms( $object_id ) {
|
||||
if ( ! $this->is_saving ) {
|
||||
$this->index( $object_id );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Rebuild the facet index
|
||||
* @param mixed $post_id The post ID (set to FALSE to re-index everything)
|
||||
*/
|
||||
function index( $post_id = false ) {
|
||||
global $wpdb;
|
||||
|
||||
$this->index_all = ( false === $post_id );
|
||||
|
||||
// Index everything
|
||||
if ( $this->index_all ) {
|
||||
|
||||
// Store the pre-index settings (see FacetWP_Diff)
|
||||
update_option( 'facetwp_settings_last_index', get_option( 'facetwp_settings' ), 'no' );
|
||||
|
||||
// PHP sessions are blocking, so close if active
|
||||
if ( PHP_SESSION_ACTIVE === session_status() ) {
|
||||
session_write_close();
|
||||
}
|
||||
|
||||
// Bypass the PHP timeout
|
||||
ini_set( 'max_execution_time', 0 );
|
||||
|
||||
// Prevent multiple indexing processes
|
||||
$touch = (int) $this->get_transient( 'touch' );
|
||||
|
||||
if ( 0 < $touch ) {
|
||||
// Run only if the indexer is inactive or stalled
|
||||
if ( ( time() - $touch ) < 60 ) {
|
||||
exit;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Create temp table
|
||||
$this->manage_temp_table( 'create' );
|
||||
}
|
||||
}
|
||||
// Index a single post
|
||||
elseif ( is_int( $post_id ) ) {
|
||||
|
||||
// Clear table values
|
||||
$wpdb->query( "DELETE FROM {$this->table} WHERE post_id = $post_id" );
|
||||
}
|
||||
// Exit
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Resume indexing?
|
||||
$offset = (int) ( $_POST['offset'] ?? 0 );
|
||||
$attempt = (int) ( $_POST['retries'] ?? 0 );
|
||||
|
||||
if ( 0 < $offset ) {
|
||||
$post_ids = json_decode( get_option( 'facetwp_indexing' ), true );
|
||||
}
|
||||
else {
|
||||
$post_ids = $this->get_post_ids_to_index( $post_id );
|
||||
|
||||
// Store post IDs
|
||||
if ( $this->index_all ) {
|
||||
update_option( 'facetwp_indexing', json_encode( $post_ids ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Count total posts
|
||||
$num_total = count( $post_ids );
|
||||
|
||||
// Get all facet sources
|
||||
$facets = FWP()->helper->get_facets();
|
||||
|
||||
// Populate an array of facet value modifiers
|
||||
$this->load_value_modifiers( $facets );
|
||||
|
||||
foreach ( $post_ids as $counter => $post_id ) {
|
||||
|
||||
// Advance until we reach the offset
|
||||
if ( $counter < $offset ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update the progress bar
|
||||
if ( $this->index_all ) {
|
||||
if ( 0 == ( $counter % $this->chunk_size ) ) {
|
||||
$num_retries = (int) $this->get_transient( 'retries' );
|
||||
|
||||
// Exit if newer retries exist
|
||||
if ( $attempt < $num_retries ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Exit if the indexer was cancelled
|
||||
wp_cache_delete( 'facetwp_indexing_cancelled', 'options' );
|
||||
|
||||
if ( 'yes' === get_option( 'facetwp_indexing_cancelled', 'no' ) ) {
|
||||
update_option( 'facetwp_transients', '' );
|
||||
update_option( 'facetwp_indexing', '' );
|
||||
$this->manage_temp_table( 'delete' );
|
||||
exit;
|
||||
}
|
||||
|
||||
$transients = [
|
||||
'num_indexed' => $counter,
|
||||
'num_total' => $num_total,
|
||||
'retries' => $attempt,
|
||||
'touch' => time(),
|
||||
];
|
||||
update_option( 'facetwp_transients', json_encode( $transients ) );
|
||||
}
|
||||
}
|
||||
|
||||
// If the indexer stalled, start from the last valid chunk
|
||||
if ( 0 < $offset && ( $counter - $offset < $this->chunk_size ) ) {
|
||||
$wpdb->query( "DELETE FROM {$this->table} WHERE post_id = $post_id" );
|
||||
}
|
||||
|
||||
$this->index_post( $post_id, $facets );
|
||||
}
|
||||
|
||||
// Indexing complete
|
||||
if ( $this->index_all ) {
|
||||
update_option( 'facetwp_last_indexed', time(), 'no' );
|
||||
update_option( 'facetwp_transients', '', 'no' );
|
||||
update_option( 'facetwp_indexing', '', 'no' );
|
||||
|
||||
$this->manage_temp_table( 'replace' );
|
||||
$this->manage_temp_table( 'delete' );
|
||||
}
|
||||
|
||||
do_action( 'facetwp_indexer_complete' );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of post IDs to index
|
||||
* @since 3.6.8
|
||||
*/
|
||||
function get_post_ids_to_index( $post_id = false ) {
|
||||
$args = [
|
||||
'post_type' => 'any',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => -1,
|
||||
'fields' => 'ids',
|
||||
'orderby' => 'ID',
|
||||
'cache_results' => false,
|
||||
'no_found_rows' => true,
|
||||
];
|
||||
|
||||
if ( is_int( $post_id ) ) {
|
||||
$args['p'] = $post_id;
|
||||
$args['posts_per_page'] = 1;
|
||||
}
|
||||
|
||||
$args = apply_filters( 'facetwp_indexer_query_args', $args );
|
||||
|
||||
$query = new WP_Query( $args );
|
||||
return (array) $query->posts;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Index an individual post
|
||||
* @since 3.6.8
|
||||
*/
|
||||
function index_post( $post_id, $facets ) {
|
||||
|
||||
// Force WPML to change the language
|
||||
do_action( 'facetwp_indexer_post', [ 'post_id' => $post_id ] );
|
||||
|
||||
// Loop through all facets
|
||||
foreach ( $facets as $facet ) {
|
||||
|
||||
// Do not index search facets
|
||||
if ( 'search' == $facet['type'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->facet = $facet;
|
||||
$source = $facet['source'] ?? '';
|
||||
|
||||
// Set default index_row() params
|
||||
$defaults = [
|
||||
'post_id' => $post_id,
|
||||
'facet_name' => $facet['name'],
|
||||
'facet_source' => $source,
|
||||
'facet_value' => '',
|
||||
'facet_display_value' => '',
|
||||
'term_id' => 0,
|
||||
'parent_id' => 0,
|
||||
'depth' => 0,
|
||||
'variation_id' => 0,
|
||||
];
|
||||
|
||||
$defaults = apply_filters( 'facetwp_indexer_post_facet_defaults', $defaults, [
|
||||
'facet' => $facet
|
||||
] );
|
||||
|
||||
// Set flag for custom handling
|
||||
$this->is_overridden = true;
|
||||
|
||||
// Bypass default indexing
|
||||
$bypass = apply_filters( 'facetwp_indexer_post_facet', false, [
|
||||
'defaults' => $defaults,
|
||||
'facet' => $facet
|
||||
] );
|
||||
|
||||
if ( $bypass ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->is_overridden = false;
|
||||
|
||||
// Get rows to insert
|
||||
$rows = $this->get_row_data( $defaults );
|
||||
|
||||
foreach ( $rows as $row ) {
|
||||
$this->index_row( $row );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get data for a table row
|
||||
* @since 2.1.1
|
||||
*/
|
||||
function get_row_data( $defaults ) {
|
||||
$output = [];
|
||||
|
||||
$facet = $this->facet;
|
||||
$post_id = $defaults['post_id'];
|
||||
$source = $defaults['facet_source'];
|
||||
|
||||
if ( 'tax/' == substr( $source, 0, 4 ) ) {
|
||||
$used_terms = [];
|
||||
$taxonomy = substr( $source, 4 );
|
||||
$term_objects = wp_get_object_terms( $post_id, $taxonomy );
|
||||
if ( is_wp_error( $term_objects ) ) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
// Store the term depths
|
||||
$hierarchy = FWP()->helper->get_term_depths( $taxonomy );
|
||||
|
||||
// Only index child terms
|
||||
$children = false;
|
||||
if ( ! empty( $facet['parent_term'] ) ) {
|
||||
$children = get_term_children( $facet['parent_term'], $taxonomy );
|
||||
}
|
||||
|
||||
foreach ( $term_objects as $term ) {
|
||||
|
||||
// If "parent_term" is set, only index children
|
||||
if ( false !== $children && ! in_array( $term->term_id, $children ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Prevent duplicate terms
|
||||
if ( isset( $used_terms[ $term->term_id ] ) ) {
|
||||
continue;
|
||||
}
|
||||
$used_terms[ $term->term_id ] = true;
|
||||
|
||||
// Handle hierarchical taxonomies
|
||||
$term_info = $hierarchy[ $term->term_id ];
|
||||
$depth = $term_info['depth'];
|
||||
|
||||
// Adjust depth if parent_term is set
|
||||
if ( ! empty( $facet['parent_term'] ) ) {
|
||||
if ( isset( $hierarchy[ $facet['parent_term'] ] ) ) {
|
||||
$anchor = (int) $hierarchy[ $facet['parent_term'] ]['depth'] + 1;
|
||||
$depth = ( $depth - $anchor );
|
||||
}
|
||||
}
|
||||
|
||||
$params = $defaults;
|
||||
$params['facet_value'] = $term->slug;
|
||||
$params['facet_display_value'] = $term->name;
|
||||
$params['term_id'] = $term->term_id;
|
||||
$params['parent_id'] = $term_info['parent_id'];
|
||||
$params['depth'] = $depth;
|
||||
$output[] = $params;
|
||||
|
||||
// Automatically index implicit parents
|
||||
if ( 'hierarchy' == $facet['type'] || FWP()->helper->facet_is( $facet, 'hierarchical', 'yes' ) ) {
|
||||
while ( $depth > 0 ) {
|
||||
$term_id = $term_info['parent_id'];
|
||||
$term_info = $hierarchy[ $term_id ];
|
||||
$depth = $depth - 1;
|
||||
|
||||
if ( ! isset( $used_terms[ $term_id ] ) ) {
|
||||
$used_terms[ $term_id ] = true;
|
||||
|
||||
$params = $defaults;
|
||||
$params['facet_value'] = $term_info['slug'];
|
||||
$params['facet_display_value'] = $term_info['name'];
|
||||
$params['term_id'] = $term_id;
|
||||
$params['parent_id'] = $term_info['parent_id'];
|
||||
$params['depth'] = $depth;
|
||||
$output[] = $params;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ( 'cf/' == substr( $source, 0, 3 ) ) {
|
||||
$source_noprefix = substr( $source, 3 );
|
||||
$values = get_post_meta( $post_id, $source_noprefix, false );
|
||||
foreach ( $values as $value ) {
|
||||
$params = $defaults;
|
||||
$params['facet_value'] = $value;
|
||||
$params['facet_display_value'] = $value;
|
||||
$output[] = $params;
|
||||
}
|
||||
}
|
||||
elseif ( 'post' == substr( $source, 0, 4 ) ) {
|
||||
$post = get_post( $post_id );
|
||||
$value = $post->{$source};
|
||||
$display_value = $value;
|
||||
if ( 'post_author' == $source ) {
|
||||
$user = get_user_by( 'id', $value );
|
||||
$display_value = ( $user instanceof WP_User ) ? $user->display_name : $value;
|
||||
}
|
||||
elseif ( 'post_type' == $source ) {
|
||||
$post_type = get_post_type_object( $value );
|
||||
if ( isset( $post_type->labels->name ) ) {
|
||||
$display_value = $post_type->labels->name;
|
||||
}
|
||||
}
|
||||
|
||||
$params = $defaults;
|
||||
$params['facet_value'] = $value;
|
||||
$params['facet_display_value'] = $display_value;
|
||||
$output[] = $params;
|
||||
}
|
||||
|
||||
return apply_filters( 'facetwp_indexer_row_data', $output, [
|
||||
'defaults' => $defaults,
|
||||
'facet' => $this->facet
|
||||
] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Index a facet value
|
||||
* @since 0.6.0
|
||||
*/
|
||||
function index_row( $params ) {
|
||||
|
||||
// Allow for custom indexing
|
||||
$params = apply_filters( 'facetwp_index_row', $params, $this );
|
||||
|
||||
// Allow hooks to bypass the row insertion
|
||||
if ( is_array( $params ) ) {
|
||||
$this->insert( $params );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save a facet value to DB
|
||||
* This can be trigged by "facetwp_index_row" to handle multiple values
|
||||
* @since 1.2.5
|
||||
*/
|
||||
function insert( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$value = $params['facet_value'];
|
||||
$display_value = $params['facet_display_value'];
|
||||
|
||||
// Only accept scalar values
|
||||
if ( '' === $value || ! is_scalar( $value ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply UI-based modifiers
|
||||
if ( isset( $this->modifiers[ $params['facet_name'] ] ) ) {
|
||||
$mod = $this->modifiers[ $params['facet_name' ] ];
|
||||
$is_match = in_array( $display_value, $mod['values'] );
|
||||
|
||||
if ( ( 'exclude' == $mod['type'] && $is_match ) || ( 'include' == $mod['type'] && ! $is_match ) ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$wpdb->query( $wpdb->prepare( "INSERT INTO {$this->table}
|
||||
(post_id, facet_name, facet_value, facet_display_value, term_id, parent_id, depth, variation_id) VALUES (%d, %s, %s, %s, %d, %d, %d, %d)",
|
||||
$params['post_id'],
|
||||
$params['facet_name'],
|
||||
FWP()->helper->safe_value( $value ),
|
||||
$display_value,
|
||||
$params['term_id'],
|
||||
$params['parent_id'],
|
||||
$params['depth'],
|
||||
$params['variation_id']
|
||||
) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the indexing completion percentage
|
||||
* @return mixed The decimal percentage, or -1
|
||||
* @since 0.1.0
|
||||
*/
|
||||
function get_progress() {
|
||||
$return = -1;
|
||||
$num_indexed = (int) $this->get_transient( 'num_indexed' );
|
||||
$num_total = (int) $this->get_transient( 'num_total' );
|
||||
$retries = (int) $this->get_transient( 'retries' );
|
||||
$touch = (int) $this->get_transient( 'touch' );
|
||||
|
||||
if ( 0 < $num_total ) {
|
||||
|
||||
// Resume a stalled indexer
|
||||
if ( 60 < ( time() - $touch ) ) {
|
||||
$post_data = [
|
||||
'blocking' => false,
|
||||
'timeout' => 0.02,
|
||||
'body' => [
|
||||
'action' => 'facetwp_resume_index',
|
||||
'offset' => $num_indexed,
|
||||
'retries' => $retries + 1,
|
||||
'touch' => $touch
|
||||
]
|
||||
];
|
||||
wp_remote_post( admin_url( 'admin-ajax.php' ), $post_data );
|
||||
}
|
||||
|
||||
// Calculate the percent completion
|
||||
if ( $num_indexed != $num_total ) {
|
||||
$return = round( 100 * ( $num_indexed / $num_total ), 2 );
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get indexer transient variables
|
||||
* @since 1.7.8
|
||||
*/
|
||||
function get_transient( $name = false ) {
|
||||
$transients = get_option( 'facetwp_transients' );
|
||||
|
||||
if ( ! empty( $transients ) ) {
|
||||
$transients = json_decode( $transients, true );
|
||||
if ( $name ) {
|
||||
return $transients[ $name ] ?? false;
|
||||
}
|
||||
|
||||
return $transients;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set either the index or temp table
|
||||
* @param string $table 'auto', 'index', or 'temp'
|
||||
* @since 4.1.4
|
||||
*/
|
||||
function set_table( $table = 'auto' ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( 'auto' == $table ) {
|
||||
$table = ( '' == get_option( 'facetwp_indexing', '' ) ) ? 'index' : 'temp';
|
||||
}
|
||||
|
||||
$this->table = $wpdb->prefix . 'facetwp_' . $table;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Index table management
|
||||
* @since 3.5
|
||||
*/
|
||||
function manage_temp_table( $action = 'create' ) {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->prefix . 'facetwp_index';
|
||||
$temp_table = $wpdb->prefix . 'facetwp_temp';
|
||||
|
||||
if ( 'create' == $action ) {
|
||||
$wpdb->query( "CREATE TABLE $temp_table LIKE $table" );
|
||||
$this->set_table( 'temp' );
|
||||
}
|
||||
elseif ( 'replace' == $action ) {
|
||||
$wpdb->query( "TRUNCATE TABLE $table" );
|
||||
$wpdb->query( "INSERT INTO $table SELECT * FROM $temp_table" );
|
||||
}
|
||||
elseif ( 'delete' == $action ) {
|
||||
$wpdb->query( "DROP TABLE IF EXISTS $temp_table" );
|
||||
$this->set_table( 'index' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Populate an array of facet value modifiers (defined in the admin UI)
|
||||
* @since 3.5.6
|
||||
*/
|
||||
function load_value_modifiers( $facets ) {
|
||||
$output = [];
|
||||
|
||||
foreach ( $facets as $facet ) {
|
||||
$name = $facet['name'];
|
||||
$type = empty( $facet['modifier_type'] ) ? 'off' : $facet['modifier_type'];
|
||||
|
||||
if ( 'include' == $type || 'exclude' == $type ) {
|
||||
$temp = preg_split( '/\r\n|\r|\n/', trim( $facet['modifier_values'] ) );
|
||||
$values = [];
|
||||
|
||||
// Compare using both original and encoded values
|
||||
foreach ( $temp as $val ) {
|
||||
$val = trim( $val );
|
||||
$val_encoded = htmlentities( $val );
|
||||
$val_decoded = html_entity_decode( $val );
|
||||
$values[ $val ] = true;
|
||||
$values[ $val_encoded ] = true;
|
||||
$values[ $val_decoded ] = true;
|
||||
}
|
||||
|
||||
$output[ $name ] = [ 'type' => $type, 'values' => array_keys( $values ) ];
|
||||
}
|
||||
}
|
||||
|
||||
$this->modifiers = $output;
|
||||
}
|
||||
}
|
||||
157
wp/wp-content/plugins/facetwp/includes/class-init.php
Normal file
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Init
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
add_action( 'init', [ $this, 'init' ], 20 );
|
||||
add_filter( 'woocommerce_is_rest_api_request', [ $this, 'is_rest_api_request' ] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize classes and WP hooks
|
||||
*/
|
||||
function init() {
|
||||
|
||||
// i18n
|
||||
$this->load_textdomain();
|
||||
|
||||
// is_plugin_active
|
||||
include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
|
||||
|
||||
$includes = [
|
||||
'api/fetch',
|
||||
'api/refresh',
|
||||
'class-helper',
|
||||
'class-ajax',
|
||||
'class-request',
|
||||
'class-renderer',
|
||||
'class-diff',
|
||||
'class-indexer',
|
||||
'class-display',
|
||||
'class-builder',
|
||||
'class-overrides',
|
||||
'class-settings',
|
||||
'class-upgrade',
|
||||
'functions'
|
||||
];
|
||||
|
||||
foreach ( $includes as $inc ) {
|
||||
include ( FACETWP_DIR . "/includes/$inc.php" );
|
||||
}
|
||||
|
||||
new FacetWP_Upgrade();
|
||||
new FacetWP_Overrides();
|
||||
|
||||
FWP()->api = new FacetWP_API_Fetch();
|
||||
FWP()->helper = new FacetWP_Helper();
|
||||
FWP()->facet = new FacetWP_Renderer();
|
||||
FWP()->settings = new FacetWP_Settings();
|
||||
FWP()->diff = new FacetWP_Diff();
|
||||
FWP()->indexer = new FacetWP_Indexer();
|
||||
FWP()->display = new FacetWP_Display();
|
||||
FWP()->builder = new FacetWP_Builder();
|
||||
FWP()->request = new FacetWP_Request();
|
||||
FWP()->ajax = new FacetWP_Ajax();
|
||||
|
||||
// integrations
|
||||
include( FACETWP_DIR . '/includes/integrations/searchwp/searchwp.php' );
|
||||
include( FACETWP_DIR . '/includes/integrations/woocommerce/woocommerce.php' );
|
||||
include( FACETWP_DIR . '/includes/integrations/edd/edd.php' );
|
||||
include( FACETWP_DIR . '/includes/integrations/acf/acf.php' );
|
||||
include( FACETWP_DIR . '/includes/integrations/wp-cli/wp-cli.php' );
|
||||
include( FACETWP_DIR . '/includes/integrations/wp-rocket/wp-rocket.php' );
|
||||
|
||||
// update checks
|
||||
include( FACETWP_DIR . '/includes/class-updater.php' );
|
||||
|
||||
// hooks
|
||||
add_action( 'admin_menu', [ $this, 'admin_menu' ] );
|
||||
add_action( 'wp_enqueue_scripts', [ $this, 'front_scripts' ] );
|
||||
add_filter( 'redirect_canonical', [ $this, 'redirect_canonical' ], 10, 2 );
|
||||
add_filter( 'plugin_action_links_facetwp/index.php', [ $this, 'plugin_action_links' ] );
|
||||
|
||||
do_action( 'facetwp_init' );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* i18n support
|
||||
*/
|
||||
function load_textdomain() {
|
||||
|
||||
// admin-facing
|
||||
load_plugin_textdomain( 'fwp' );
|
||||
|
||||
// front-facing
|
||||
load_plugin_textdomain( 'fwp-front', false, basename( FACETWP_DIR ) . '/languages' );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register the FacetWP settings page
|
||||
*/
|
||||
function admin_menu() {
|
||||
add_options_page( 'FacetWP', 'FacetWP', 'manage_options', 'facetwp', [ $this, 'settings_page' ] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enqueue jQuery
|
||||
*/
|
||||
function front_scripts() {
|
||||
if ( 'yes' == FWP()->helper->get_setting( 'load_jquery', 'yes' ) ) {
|
||||
wp_enqueue_script( 'jquery' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Route to the correct edit screen
|
||||
*/
|
||||
function settings_page() {
|
||||
include( FACETWP_DIR . '/templates/page-settings.php' );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prevent WP from redirecting FWP pager to /page/X
|
||||
*/
|
||||
function redirect_canonical( $redirect_url, $requested_url ) {
|
||||
if ( false !== strpos( $redirect_url, FWP()->helper->get_setting( 'prefix' ) . 'paged' ) ) {
|
||||
return false;
|
||||
}
|
||||
return $redirect_url;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add "Settings" link to plugin listing page
|
||||
*/
|
||||
function plugin_action_links( $links ) {
|
||||
$settings_link = admin_url( 'options-general.php?page=facetwp' );
|
||||
$settings_link = '<a href=" ' . $settings_link . '">' . __( 'Settings', 'fwp' ) . '</a>';
|
||||
array_unshift( $links, $settings_link );
|
||||
return $links;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* WooCommerce 3.6+ doesn't load its frontend includes for REST API requests
|
||||
* We need to force-load these includes for FacetWP refreshes
|
||||
* See includes() within class-woocommerce.php
|
||||
*
|
||||
* This code isn't within /integrations/woocommerce/ because it runs *before* init
|
||||
*
|
||||
* @since 3.3.10
|
||||
*/
|
||||
function is_rest_api_request( $request ) {
|
||||
if ( false !== strpos( $_SERVER['REQUEST_URI'], 'facetwp' ) ) {
|
||||
return false;
|
||||
}
|
||||
return $request;
|
||||
}
|
||||
}
|
||||
|
||||
$this->init = new FacetWP_Init();
|
||||
103
wp/wp-content/plugins/facetwp/includes/class-overrides.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Overrides
|
||||
{
|
||||
|
||||
public $raw;
|
||||
|
||||
|
||||
function __construct() {
|
||||
add_filter( 'facetwp_index_row', [ $this, 'index_row' ], 5, 2 );
|
||||
add_filter( 'facetwp_index_row', [ $this, 'format_numbers' ], 15, 2 );
|
||||
add_filter( 'facetwp_is_main_query', [ $this, 'ignore_post_types' ], 10, 2 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Indexer modifications
|
||||
*/
|
||||
function index_row( $params, $class ) {
|
||||
if ( $class->is_overridden ) {
|
||||
return $params;
|
||||
}
|
||||
|
||||
$facet = FWP()->helper->get_facet_by_name( $params['facet_name'] );
|
||||
|
||||
// Store raw numbers to format later
|
||||
if ( in_array( $facet['type'], [ 'number_range', 'slider' ] ) ) {
|
||||
$this->raw = [
|
||||
'value' => $params['facet_value'],
|
||||
'label' => $params['facet_display_value']
|
||||
];
|
||||
}
|
||||
|
||||
// Support "Other data source" values
|
||||
if ( ! empty( $facet['source_other'] ) ) {
|
||||
$other_params = $params;
|
||||
$other_params['facet_source'] = $facet['source_other'];
|
||||
$rows = $class->get_row_data( $other_params );
|
||||
$params['facet_display_value'] = $rows[0]['facet_display_value'];
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make sure that numbers are properly formatted
|
||||
*/
|
||||
function format_numbers( $params, $class ) {
|
||||
|
||||
if ( empty( $this->raw ) ) {
|
||||
return $params;
|
||||
}
|
||||
|
||||
$value = $params['facet_value'];
|
||||
$label = $params['facet_display_value'];
|
||||
|
||||
// Only format if un-altered
|
||||
if ( $this->raw['value'] === $value && $this->raw['label'] === $label ) {
|
||||
$params['facet_value'] = FWP()->helper->format_number( $this->raw['value'] );
|
||||
$params['facet_display_value'] = FWP()->helper->format_number( $this->raw['label'] );
|
||||
}
|
||||
|
||||
$this->raw = null;
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ignore certain post types
|
||||
*/
|
||||
function ignore_post_types( $is_main_query, $query ) {
|
||||
$blacklist = [
|
||||
'acf-field',
|
||||
'acf-field-group',
|
||||
'advanced_ads',
|
||||
'carts',
|
||||
'cookielawinfo',
|
||||
'edd_wish_list',
|
||||
'ms_relationship',
|
||||
'nav_menu_item',
|
||||
'wc_user_membership',
|
||||
'wp_block',
|
||||
'wp_global_styles',
|
||||
'wp_navigation',
|
||||
'wp_template',
|
||||
'wp_template_part'
|
||||
];
|
||||
$post_type = $query->get( 'post_type' );
|
||||
|
||||
if ( is_string( $post_type ) && in_array( $post_type, $blacklist ) ) {
|
||||
$is_main_query = false;
|
||||
}
|
||||
|
||||
// Ignore the "WP GDPR Compliance" plugin
|
||||
if ( '[wpgdprc_access_request_form]' == $query->get( 's' ) ) {
|
||||
$is_main_query = false;
|
||||
}
|
||||
|
||||
return $is_main_query;
|
||||
}
|
||||
}
|
||||
662
wp/wp-content/plugins/facetwp/includes/class-renderer.php
Normal file
@@ -0,0 +1,662 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Renderer
|
||||
{
|
||||
|
||||
/* (array) Data for the current facets */
|
||||
public $facets;
|
||||
|
||||
/* (array) Data for the current template */
|
||||
public $template;
|
||||
|
||||
/* (array) WP_Query arguments */
|
||||
public $query_args;
|
||||
|
||||
/* (array) Data used to build the pager */
|
||||
public $pager_args;
|
||||
|
||||
/* (string) MySQL WHERE clause passed to each facet */
|
||||
public $where_clause = '';
|
||||
|
||||
/* (array) AJAX parameters passed in */
|
||||
public $ajax_params;
|
||||
|
||||
/* (array) HTTP parameters from the original page (URI, GET) */
|
||||
public $http_params;
|
||||
|
||||
/* (boolean) Is search active? */
|
||||
public $is_search = false;
|
||||
|
||||
/* (boolean) Are we preloading? */
|
||||
public $is_preload = false;
|
||||
|
||||
/* (array) Cache preloaded facet values */
|
||||
public $preloaded_values;
|
||||
|
||||
/* (array) The final WP_Query object */
|
||||
public $query;
|
||||
|
||||
|
||||
function __construct() {
|
||||
$this->facet_types = FWP()->helper->facet_types;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet output
|
||||
* @param array $params An array of arrays (see the FacetWP->refresh() method)
|
||||
* @return array
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = [
|
||||
'facets' => [],
|
||||
'template' => '',
|
||||
'settings' => [],
|
||||
];
|
||||
|
||||
// Hook params
|
||||
$params = apply_filters( 'facetwp_render_params', $params );
|
||||
|
||||
// First ajax refresh?
|
||||
$first_load = (bool) $params['first_load'];
|
||||
$is_bfcache = (bool) $params['is_bfcache'];
|
||||
|
||||
// Initial pageload?
|
||||
$this->is_preload = isset( $params['is_preload'] );
|
||||
|
||||
// Set the AJAX and HTTP params
|
||||
$this->ajax_params = $params;
|
||||
$this->http_params = $params['http_params'];
|
||||
|
||||
// Validate facets
|
||||
$this->facets = [];
|
||||
foreach ( $params['facets'] as $f ) {
|
||||
$name = $f['facet_name'];
|
||||
$facet = FWP()->helper->get_facet_by_name( $name );
|
||||
if ( $facet ) {
|
||||
|
||||
// Default to "OR" mode
|
||||
$facet['operator'] = $facet['operator'] ?? 'or';
|
||||
|
||||
// Support the "facetwp_preload_url_vars" hook
|
||||
if ( $first_load && empty( $f['selected_values'] ) && ! empty( $this->http_params['url_vars'][ $name ] ) ) {
|
||||
$f['selected_values'] = $this->http_params['url_vars'][ $name ];
|
||||
}
|
||||
|
||||
// Support commas within search / autocomplete facets
|
||||
if ( 'search' == $facet['type'] || 'autocomplete' == $facet['type'] ) {
|
||||
$f['selected_values'] = implode( ',', (array) $f['selected_values'] );
|
||||
}
|
||||
|
||||
$facet['selected_values'] = FWP()->helper->sanitize( $f['selected_values'] );
|
||||
|
||||
$this->facets[ $name ] = $facet;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the template from $helper->settings
|
||||
if ( 'wp' == $params['template'] ) {
|
||||
$this->template = [ 'name' => 'wp' ];
|
||||
$query_args = FWP()->request->query_vars ?? [];
|
||||
}
|
||||
else {
|
||||
$this->template = FWP()->helper->get_template_by_name( $params['template'] );
|
||||
$query_args = $this->get_query_args();
|
||||
}
|
||||
|
||||
// Detect search string
|
||||
if ( ! empty( $query_args['s'] ) ) {
|
||||
$this->is_search = true;
|
||||
}
|
||||
|
||||
// Run the query once (prevent duplicate queries when preloading)
|
||||
if ( empty( $this->query_args ) ) {
|
||||
|
||||
// Support "post__in"
|
||||
if ( empty( $query_args['post__in'] ) ) {
|
||||
$query_args['post__in'] = [];
|
||||
}
|
||||
|
||||
// Get the template "query" field
|
||||
$query_args = apply_filters( 'facetwp_query_args', $query_args, $this );
|
||||
|
||||
// Pagination
|
||||
$query_args['paged'] = empty( $params['paged'] ) ? 1 : (int) $params['paged'];
|
||||
|
||||
// Preserve SQL_CALC_FOUND_ROWS
|
||||
unset( $query_args['no_found_rows'] );
|
||||
|
||||
// Narrow posts based on facet selections
|
||||
$post_ids = $this->get_filtered_post_ids( $query_args );
|
||||
|
||||
// Update the SQL query
|
||||
if ( ! empty( $post_ids ) ) {
|
||||
if ( FWP()->is_filtered ) {
|
||||
$query_args['post__in'] = $post_ids;
|
||||
}
|
||||
|
||||
$this->where_clause = ' AND post_id IN (' . implode( ',', $post_ids ) . ')';
|
||||
}
|
||||
|
||||
// Set the default limit
|
||||
if ( empty( $query_args['posts_per_page'] ) ) {
|
||||
$query_args['posts_per_page'] = (int) get_option( 'posts_per_page' );
|
||||
}
|
||||
|
||||
// Adhere to the "per page" box
|
||||
$per_page = isset( $params['extras']['per_page'] ) ? $params['extras']['per_page'] : '';
|
||||
if ( ! empty( $per_page ) && 'default' != $per_page ) {
|
||||
$query_args['posts_per_page'] = (int) $per_page;
|
||||
}
|
||||
|
||||
$this->query_args = apply_filters( 'facetwp_filtered_query_args', $query_args, $this );
|
||||
|
||||
// Run the WP_Query
|
||||
$this->query = new WP_Query( $this->query_args );
|
||||
}
|
||||
|
||||
// Debug
|
||||
if ( 'on' == FWP()->helper->get_setting( 'debug_mode', 'off' ) ) {
|
||||
$output['settings']['debug'] = $this->get_debug_info();
|
||||
}
|
||||
|
||||
// Generate the template HTML
|
||||
// For performance gains, skip the template on pageload
|
||||
if ( 'wp' != $this->template['name'] ) {
|
||||
if ( ! $first_load || $is_bfcache || apply_filters( 'facetwp_template_force_load', false ) ) {
|
||||
$output['template'] = $this->get_template_html();
|
||||
}
|
||||
}
|
||||
|
||||
// Don't render these facets
|
||||
$frozen_facets = $params['frozen_facets'];
|
||||
|
||||
// Calculate pager args
|
||||
$pager_args = [
|
||||
'page' => (int) $this->query_args['paged'],
|
||||
'per_page' => (int) $this->query_args['posts_per_page'],
|
||||
'total_rows' => (int) $this->query->found_posts,
|
||||
'total_pages' => 1,
|
||||
];
|
||||
|
||||
if ( 0 < $pager_args['per_page'] ) {
|
||||
$pager_args['total_pages'] = ceil( $pager_args['total_rows'] / $pager_args['per_page'] );
|
||||
}
|
||||
|
||||
$pager_args = apply_filters( 'facetwp_pager_args', $pager_args, $this );
|
||||
|
||||
$this->pager_args = $pager_args;
|
||||
|
||||
// Stick the pager args into the JSON response
|
||||
$output['settings']['pager'] = $pager_args;
|
||||
|
||||
// Display the pagination HTML
|
||||
if ( isset( $params['extras']['pager'] ) ) {
|
||||
$output['pager'] = $this->paginate( $pager_args );
|
||||
}
|
||||
|
||||
// Display the "per page" HTML
|
||||
if ( isset( $params['extras']['per_page'] ) ) {
|
||||
$output['per_page'] = $this->get_per_page_box();
|
||||
}
|
||||
|
||||
// Display the counts HTML
|
||||
if ( isset( $params['extras']['counts'] ) ) {
|
||||
$output['counts'] = $this->get_result_count( $pager_args );
|
||||
}
|
||||
|
||||
// Not paging
|
||||
if ( 0 == $params['soft_refresh'] ) {
|
||||
$output['settings']['num_choices'] = [];
|
||||
}
|
||||
|
||||
// Get facet data
|
||||
foreach ( $this->facets as $facet_name => $the_facet ) {
|
||||
$facet_type = $the_facet['type'];
|
||||
$ui_type = empty( $the_facet['ui_type'] ) ? $facet_type : $the_facet['ui_type'];
|
||||
|
||||
// Invalid facet type
|
||||
if ( ! isset( $this->facet_types[ $facet_type ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip facets when paging
|
||||
if ( 0 < $params['soft_refresh'] && 'pager' != $facet_type ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get facet labels
|
||||
if ( 0 == $params['soft_refresh'] ) {
|
||||
$output['settings']['labels'][ $facet_name ] = facetwp_i18n( $the_facet['label'] );
|
||||
}
|
||||
|
||||
// Load all facets on back / forward button press (first_load = true)
|
||||
if ( ! $first_load ) {
|
||||
|
||||
// Skip frozen facets
|
||||
if ( isset( $frozen_facets[ $facet_name ] ) ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$args = [
|
||||
'facet' => $the_facet,
|
||||
'where_clause' => $this->where_clause,
|
||||
'selected_values' => $the_facet['selected_values'],
|
||||
];
|
||||
|
||||
// Load facet values if needed
|
||||
if ( method_exists( $this->facet_types[ $facet_type ], 'load_values' ) ) {
|
||||
|
||||
// Grab preloaded values if available
|
||||
if ( isset( $this->preloaded_values[ $facet_name ] ) ) {
|
||||
$args['values'] = $this->preloaded_values[ $facet_name ];
|
||||
}
|
||||
else {
|
||||
$args['values'] = $this->facet_types[ $facet_type ]->load_values( $args );
|
||||
|
||||
if ( $this->is_preload ) {
|
||||
$this->preloaded_values[ $facet_name ] = $args['values'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filter the render args
|
||||
$args = apply_filters( 'facetwp_facet_render_args', $args );
|
||||
|
||||
// Return the number of available choices
|
||||
if ( isset( $args['values'] ) ) {
|
||||
$num_choices = 0;
|
||||
$is_ghost = FWP()->helper->facet_is( $the_facet, 'ghosts', 'yes' );
|
||||
|
||||
foreach ( $args['values'] as $choice ) {
|
||||
if ( isset( $choice['counter'] ) && ( 0 < $choice['counter'] || $is_ghost ) ) {
|
||||
$num_choices++;
|
||||
}
|
||||
}
|
||||
|
||||
$output['settings']['num_choices'][ $facet_name ] = $num_choices;
|
||||
}
|
||||
|
||||
// Generate the facet HTML
|
||||
$html = $this->facet_types[ $ui_type ]->render( $args );
|
||||
$output['facets'][ $facet_name ] = apply_filters( 'facetwp_facet_html', $html, $args );
|
||||
|
||||
// Return any JS settings
|
||||
if ( method_exists( $this->facet_types[ $ui_type ], 'settings_js' ) ) {
|
||||
$output['settings'][ $facet_name ] = $this->facet_types[ $ui_type ]->settings_js( $args );
|
||||
}
|
||||
|
||||
// Grab num_choices for slider facets
|
||||
if ( 'slider' == $the_facet['type'] ) {
|
||||
$min = $output['settings'][ $facet_name ]['range']['min'];
|
||||
$max = $output['settings'][ $facet_name ]['range']['max'];
|
||||
$output['settings']['num_choices'][ $facet_name ] = ( $min == $max ) ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
return apply_filters( 'facetwp_render_output', $output, $params );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get WP_Query arguments by executing the template "query" field
|
||||
* @return null
|
||||
*/
|
||||
function get_query_args() {
|
||||
|
||||
$defaults = [];
|
||||
|
||||
// Allow templates to piggyback archives
|
||||
if ( apply_filters( 'facetwp_template_use_archive', false ) ) {
|
||||
$main_query = $GLOBALS['wp_the_query'];
|
||||
|
||||
// Initial pageload
|
||||
if ( $main_query->is_archive || $main_query->is_search ) {
|
||||
if ( $main_query->is_category ) {
|
||||
$defaults['cat'] = $main_query->get( 'cat' );
|
||||
}
|
||||
elseif ( $main_query->is_tag ) {
|
||||
$defaults['tag_id'] = $main_query->get( 'tag_id' );
|
||||
}
|
||||
elseif ( $main_query->is_tax ) {
|
||||
$defaults['taxonomy'] = $main_query->get( 'taxonomy' );
|
||||
$defaults['term'] = $main_query->get( 'term' );
|
||||
}
|
||||
elseif ( $main_query->is_search ) {
|
||||
$defaults['s'] = $main_query->get( 's' );
|
||||
}
|
||||
|
||||
$this->archive_args = $defaults;
|
||||
}
|
||||
// Subsequent ajax requests
|
||||
elseif ( ! empty( $this->http_params['archive_args'] ) ) {
|
||||
foreach ( $this->http_params['archive_args'] as $key => $val ) {
|
||||
if ( in_array( $key, [ 'cat', 'tag_id', 'taxonomy', 'term', 's' ] ) ) {
|
||||
$defaults[ $key ] = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use the query builder
|
||||
if ( isset( $this->template['modes'] ) && 'visual' == $this->template['modes']['query'] ) {
|
||||
$query_args = FWP()->builder->parse_query_obj( $this->template['query_obj'] );
|
||||
}
|
||||
else {
|
||||
|
||||
// remove UTF-8 non-breaking spaces
|
||||
$query_args = preg_replace( "/\xC2\xA0/", ' ', $this->template['query'] );
|
||||
$query_args = (array) eval( '?>' . $query_args );
|
||||
}
|
||||
|
||||
// Merge the two arrays
|
||||
return array_merge( $defaults, $query_args );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get ALL post IDs for the matching query
|
||||
* @return array An array of post IDs
|
||||
*/
|
||||
function get_filtered_post_ids( $query_args = [] ) {
|
||||
|
||||
if ( empty( $query_args ) ) {
|
||||
$query_args = $this->query_args;
|
||||
}
|
||||
|
||||
// Only get relevant post IDs
|
||||
$args = array_merge( $query_args, [
|
||||
'paged' => 1,
|
||||
'posts_per_page' => -1,
|
||||
'update_post_meta_cache' => false,
|
||||
'update_post_term_cache' => false,
|
||||
'cache_results' => false,
|
||||
'no_found_rows' => true,
|
||||
'nopaging' => true, // prevent "offset" issues
|
||||
'facetwp' => false,
|
||||
'fields' => 'ids',
|
||||
] );
|
||||
|
||||
$query = new WP_Query( $args );
|
||||
|
||||
// Allow hooks to modify the default post IDs
|
||||
$post_ids = apply_filters( 'facetwp_pre_filtered_post_ids', $query->posts, $this );
|
||||
|
||||
// Store the unfiltered post IDs
|
||||
FWP()->unfiltered_post_ids = $post_ids;
|
||||
|
||||
foreach ( $this->facets as $facet_name => $the_facet ) {
|
||||
$facet_type = $the_facet['type'];
|
||||
|
||||
// Stop looping
|
||||
if ( empty( $post_ids ) ) {
|
||||
break;
|
||||
}
|
||||
|
||||
$matches = [];
|
||||
$selected_values = $the_facet['selected_values'];
|
||||
|
||||
if ( empty( $selected_values ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle each facet
|
||||
if ( isset( $this->facet_types[ $facet_type ] ) ) {
|
||||
|
||||
$hook_params = [
|
||||
'facet' => $the_facet,
|
||||
'selected_values' => $selected_values,
|
||||
];
|
||||
|
||||
// Hook to support custom filter_posts() handler
|
||||
$matches = apply_filters( 'facetwp_facet_filter_posts', false, $hook_params );
|
||||
|
||||
if ( false === $matches ) {
|
||||
$matches = $this->facet_types[ $facet_type ]->filter_posts( $hook_params );
|
||||
}
|
||||
}
|
||||
|
||||
// Skip this facet
|
||||
if ( 'continue' == $matches ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Force array
|
||||
$matches = (array) $matches;
|
||||
|
||||
// Store post IDs per facet (needed for "OR" mode)
|
||||
FWP()->or_values[ $facet_name ] = $matches;
|
||||
|
||||
if ( 'search' == $facet_type ) {
|
||||
$this->is_search = true;
|
||||
}
|
||||
|
||||
// For search facets, loop through $matches to set order
|
||||
// For other facets, loop through $post_ids to preserve the existing order
|
||||
$needles = ( 'search' == $facet_type ) ? $matches : $post_ids;
|
||||
$haystack = ( 'search' == $facet_type ) ? $post_ids : $matches;
|
||||
$haystack = array_flip( $haystack );
|
||||
$intersected_ids = [];
|
||||
|
||||
foreach ( $needles as $post_id ) {
|
||||
if ( isset( $haystack[ $post_id ] ) ) {
|
||||
$intersected_ids[] = $post_id;
|
||||
}
|
||||
}
|
||||
|
||||
$post_ids = $intersected_ids;
|
||||
}
|
||||
|
||||
$post_ids = apply_filters( 'facetwp_filtered_post_ids', array_values( $post_ids ), $this );
|
||||
|
||||
// Store the filtered post IDs
|
||||
FWP()->filtered_post_ids = $post_ids;
|
||||
|
||||
// Set a flag for whether filtering is applied
|
||||
FWP()->is_filtered = ( FWP()->filtered_post_ids !== FWP()->unfiltered_post_ids );
|
||||
|
||||
// Return a zero array if no matches
|
||||
return empty( $post_ids ) ? [ 0 ] : $post_ids;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run the template display code
|
||||
* @return string (HTML)
|
||||
*/
|
||||
function get_template_html() {
|
||||
global $post, $wp_query;
|
||||
|
||||
$output = apply_filters( 'facetwp_template_html', false, $this );
|
||||
|
||||
if ( false === $output ) {
|
||||
ob_start();
|
||||
|
||||
// Preserve globals
|
||||
$temp_post = is_object( $post ) ? clone $post : $post;
|
||||
$temp_wp_query = is_object( $wp_query ) ? clone $wp_query : $wp_query;
|
||||
|
||||
$query = $this->query;
|
||||
$wp_query = $query; // Make $query->blah() optional
|
||||
|
||||
if ( isset( $this->template['modes'] ) && 'visual' == $this->template['modes']['display'] ) {
|
||||
echo FWP()->builder->render_layout( $this->template['layout'] );
|
||||
}
|
||||
else {
|
||||
|
||||
// Remove UTF-8 non-breaking spaces
|
||||
$display_code = $this->template['template'];
|
||||
$display_code = preg_replace( "/\xC2\xA0/", ' ', $display_code );
|
||||
eval( '?>' . $display_code );
|
||||
}
|
||||
|
||||
// Reset globals
|
||||
$post = $temp_post;
|
||||
$wp_query = $temp_wp_query;
|
||||
|
||||
// Store buffered output
|
||||
$output = ob_get_clean();
|
||||
}
|
||||
|
||||
$output = preg_replace( "/\xC2\xA0/", ' ', $output );
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Result count (1-10 of 234)
|
||||
* @param array $params An array with "page", "per_page", and "total_rows"
|
||||
* @return string
|
||||
*/
|
||||
function get_result_count( $params = [] ) {
|
||||
$text_of = __( 'of', 'fwp-front' );
|
||||
|
||||
$page = (int) $params['page'];
|
||||
$per_page = (int) $params['per_page'];
|
||||
$total_rows = (int) $params['total_rows'];
|
||||
|
||||
if ( $per_page < $total_rows ) {
|
||||
$lower = ( 1 + ( ( $page - 1 ) * $per_page ) );
|
||||
$upper = ( $page * $per_page );
|
||||
$upper = ( $total_rows < $upper ) ? $total_rows : $upper;
|
||||
$output = "$lower-$upper $text_of $total_rows";
|
||||
}
|
||||
else {
|
||||
$lower = ( 0 < $total_rows ) ? 1 : 0;
|
||||
$upper = $total_rows;
|
||||
$output = $total_rows;
|
||||
}
|
||||
|
||||
return apply_filters( 'facetwp_result_count', $output, [
|
||||
'lower' => $lower,
|
||||
'upper' => $upper,
|
||||
'total' => $total_rows,
|
||||
] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pagination
|
||||
* @param array $params An array with "page", "per_page", and "total_rows"
|
||||
* @return string
|
||||
*/
|
||||
function paginate( $params = [] ) {
|
||||
$pager_class = FWP()->helper->facet_types['pager'];
|
||||
$pager_class->pager_args = $params;
|
||||
|
||||
$output = $pager_class->render_numbers([
|
||||
'inner_size' => 2,
|
||||
'dots_label' => '…',
|
||||
'prev_label' => '<<',
|
||||
'next_label' => '>>',
|
||||
]);
|
||||
|
||||
return apply_filters( 'facetwp_pager_html', $output, $params );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* "Per Page" dropdown box
|
||||
* @return string
|
||||
*/
|
||||
function get_per_page_box() {
|
||||
$pager_class = FWP()->helper->facet_types['pager'];
|
||||
$pager_class->pager_args = $this->pager_args;
|
||||
|
||||
$options = apply_filters( 'facetwp_per_page_options', [ 10, 25, 50, 100 ] );
|
||||
|
||||
$output = $pager_class->render_per_page([
|
||||
'default_label' => __( 'Per page', 'fwp-front' ),
|
||||
'per_page_options' => implode( ',', $options )
|
||||
]);
|
||||
|
||||
return apply_filters( 'facetwp_per_page_html', $output, [
|
||||
'options' => $options
|
||||
] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get debug info for the browser console
|
||||
* @since 3.5.7
|
||||
*/
|
||||
function get_debug_info() {
|
||||
$last_indexed = get_option( 'facetwp_last_indexed' );
|
||||
$last_indexed = $last_indexed ? human_time_diff( $last_indexed ) : 'never';
|
||||
|
||||
$debug = [
|
||||
'query_args' => $this->query_args,
|
||||
'sql' => $this->query->request,
|
||||
'facets' => $this->facets,
|
||||
'template' => $this->template,
|
||||
'settings' => FWP()->helper->settings['settings'],
|
||||
'last_indexed' => $last_indexed,
|
||||
'row_counts' => FWP()->helper->get_row_counts(),
|
||||
'hooks_used' => $this->get_hooks_used()
|
||||
];
|
||||
|
||||
// Reduce debug payload
|
||||
if ( ! empty( $this->query_args['post__in'] ) ) {
|
||||
$debug['query_args']['post__in_count'] = count( $this->query_args['post__in'] );
|
||||
$debug['query_args']['post__in'] = array_slice( $this->query_args['post__in'], 0, 10 );
|
||||
|
||||
$debug['sql'] = preg_replace_callback( '/posts.ID IN \((.*?)\)/s', function( $matches ) {
|
||||
$count = substr_count( $matches[1], ',' ) + 1;
|
||||
return ( $count <= 10 ) ? $matches[0] : "posts.ID IN (<$count IDs>)";
|
||||
}, $debug['sql'] );
|
||||
}
|
||||
|
||||
return $debug;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display the location of relevant hooks (for Debug Mode)
|
||||
* @since 3.5.7
|
||||
*/
|
||||
function get_hooks_used() {
|
||||
$relevant_hooks = [];
|
||||
|
||||
foreach ( $GLOBALS['wp_filter'] as $tag => $hook_data ) {
|
||||
if ( 0 === strpos( $tag, 'facetwp' ) || 'pre_get_posts' == $tag ) {
|
||||
foreach ( $hook_data->callbacks as $callbacks ) {
|
||||
foreach ( $callbacks as $cb ) {
|
||||
if ( is_string( $cb['function'] ) && false !== strpos( $cb['function'], '::' ) ) {
|
||||
$cb['function'] = explode( '::', $cb['function'] );
|
||||
}
|
||||
|
||||
if ( is_array( $cb['function'] ) ) {
|
||||
$class = is_object( $cb['function'][0] ) ? get_class( $cb['function'][0] ) : $cb['function'][0];
|
||||
$ref = new ReflectionMethod( $class, $cb['function'][1] );
|
||||
}
|
||||
elseif ( is_object( $cb['function'] ) ) {
|
||||
if ( is_a( $cb['function'], 'Closure' ) ) {
|
||||
$ref = new ReflectionFunction( $cb['function'] );
|
||||
}
|
||||
else {
|
||||
$class = get_class( $cb['function'] );
|
||||
$ref = new ReflectionMethod( $class, '__invoke' );
|
||||
}
|
||||
}
|
||||
else {
|
||||
$ref = new ReflectionFunction( $cb['function'] );
|
||||
}
|
||||
|
||||
$filename = str_replace( ABSPATH, '', $ref->getFileName() );
|
||||
|
||||
// ignore built-in hooks
|
||||
if ( false === strpos( $filename, 'plugins/facetwp' ) ) {
|
||||
if ( false !== strpos( $filename, 'wp-content' ) ) {
|
||||
$relevant_hooks[ $tag ][] = $filename . ':' . $ref->getStartLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $relevant_hooks;
|
||||
}
|
||||
}
|
||||
379
wp/wp-content/plugins/facetwp/includes/class-request.php
Normal file
@@ -0,0 +1,379 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Request
|
||||
{
|
||||
|
||||
/* (array) FacetWP-related GET variables */
|
||||
public $url_vars = [];
|
||||
|
||||
/* (mixed) The main query vars */
|
||||
public $query_vars = null;
|
||||
|
||||
/* (boolean) FWP template shortcode? */
|
||||
public $is_shortcode = false;
|
||||
|
||||
/* (boolean) Is a FacetWP refresh? */
|
||||
public $is_refresh = false;
|
||||
|
||||
/* (boolean) Initial load? */
|
||||
public $is_preload = false;
|
||||
|
||||
|
||||
function __construct() {
|
||||
$this->process_json();
|
||||
$this->intercept_request();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* application/json requires processing the raw PHP input stream
|
||||
*/
|
||||
function process_json() {
|
||||
$json = file_get_contents( 'php://input' );
|
||||
if ( 0 === strpos( $json, '{' ) ) {
|
||||
$post_data = json_decode( $json, true );
|
||||
$action = $post_data['action'] ?? '';
|
||||
|
||||
if ( is_string( $action ) && 0 === strpos( $action, 'facetwp' ) ) {
|
||||
$_POST = $post_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If AJAX and the template is "wp", return the buffered HTML
|
||||
* Otherwise, store the GET variables for later use
|
||||
*/
|
||||
function intercept_request() {
|
||||
$action = isset( $_POST['action'] ) ? sanitize_key( $_POST['action'] ) : '';
|
||||
|
||||
$valid_actions = [
|
||||
'facetwp_refresh',
|
||||
'facetwp_autocomplete_load'
|
||||
];
|
||||
|
||||
$this->is_refresh = ( 'facetwp_refresh' == $action );
|
||||
$this->is_preload = ! in_array( $action, $valid_actions );
|
||||
$prefix = FWP()->helper->get_setting( 'prefix' );
|
||||
$is_css_tpl = isset( $_POST['data']['template'] ) && 'wp' == $_POST['data']['template'];
|
||||
|
||||
// Disable the admin bar to prevent JSON issues
|
||||
if ( $this->is_refresh ) {
|
||||
add_filter( 'show_admin_bar', '__return_false' );
|
||||
}
|
||||
|
||||
// Pageload
|
||||
if ( $this->is_preload ) {
|
||||
$features = [ 'paged', 'per_page', 'sort' ];
|
||||
$valid_names = wp_list_pluck( FWP()->helper->get_facets(), 'name' );
|
||||
$valid_names = array_merge( $valid_names, $features );
|
||||
|
||||
// Store GET variables
|
||||
foreach ( $valid_names as $name ) {
|
||||
if ( isset( $_GET[ $prefix . $name ] ) && '' !== $_GET[ $prefix . $name ] ) {
|
||||
$new_val = stripslashes_deep( $_GET[ $prefix . $name ] );
|
||||
$new_val = in_array( $name, $features ) ? $new_val : explode( ',', $new_val );
|
||||
$this->url_vars[ $name ] = $new_val;
|
||||
}
|
||||
}
|
||||
|
||||
$this->url_vars = apply_filters( 'facetwp_preload_url_vars', $this->url_vars );
|
||||
}
|
||||
// Populate $_GET
|
||||
else {
|
||||
$data = stripslashes_deep( $_POST['data'] );
|
||||
|
||||
if ( ! empty( $data['http_params']['get'] ) ) {
|
||||
foreach ( $data['http_params']['get'] as $key => $val ) {
|
||||
if ( ! isset( $_GET[ $key ] ) ) {
|
||||
$_GET[ $key ] = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $this->is_preload || $is_css_tpl ) {
|
||||
add_filter( 'posts_pre_query', [ $this, 'maybe_abort_query' ], 10, 2 );
|
||||
add_action( 'pre_get_posts', [ $this, 'sacrificial_lamb' ], 998 );
|
||||
add_action( 'pre_get_posts', [ $this, 'update_query_vars' ], 999 );
|
||||
}
|
||||
|
||||
if ( ! $this->is_preload && $is_css_tpl && 'facetwp_autocomplete_load' != $action ) {
|
||||
add_action( 'shutdown', [ $this, 'inject_template' ], 0 );
|
||||
ob_start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* FacetWP runs the archive query before WP gets the chance.
|
||||
* This hook prevents the query from running twice, by letting us inject the
|
||||
* first query's posts (and counts) into the "main" query.
|
||||
*/
|
||||
function maybe_abort_query( $posts, $query ) {
|
||||
$do_abort = apply_filters( 'facetwp_archive_abort_query', true, $query );
|
||||
$has_query_run = ( ! empty( FWP()->facet->query ) );
|
||||
|
||||
if ( $do_abort && $has_query_run && isset( $this->query_vars ) ) {
|
||||
|
||||
// New var; any changes to $query will cause is_main_query() to return false
|
||||
$query_vars = $query->query_vars;
|
||||
|
||||
// If paged = 0, set to 1 or the compare will fail
|
||||
if ( empty( $query_vars['paged'] ) ) {
|
||||
$query_vars['paged'] = 1;
|
||||
}
|
||||
|
||||
// Only intercept the identical query
|
||||
if ( $query_vars === $this->query_vars ) {
|
||||
$posts = FWP()->facet->query->posts;
|
||||
$query->found_posts = FWP()->facet->query->found_posts;
|
||||
$query->max_num_pages = FWP()->facet->query->max_num_pages;
|
||||
}
|
||||
}
|
||||
|
||||
return $posts;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fixes https://core.trac.wordpress.org/ticket/40393
|
||||
*/
|
||||
function sacrificial_lamb( $query ) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Force FacetWP to use the default WP query
|
||||
*/
|
||||
function update_query_vars( $query ) {
|
||||
|
||||
if ( isset( $this->query_vars ) // Ran already
|
||||
|| $this->is_shortcode // Skip shortcode template
|
||||
|| ( is_admin() && ! wp_doing_ajax() ) // Skip admin
|
||||
|| ( wp_doing_ajax() && ! $this->is_refresh ) // Skip other ajax
|
||||
|| ! $this->is_main_query( $query ) // Not the main query
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the flag
|
||||
$query->set( 'facetwp', true );
|
||||
|
||||
// If "s" is an empty string and no post_type is set, WP sets
|
||||
// post_type = "any". We want to prevent this except on the search page.
|
||||
if ( '' == $query->get( 's' ) && ! isset( $_GET['s'] ) ) {
|
||||
$query->set( 's', null );
|
||||
}
|
||||
|
||||
// Set the initial query vars, needed for render()
|
||||
$this->query_vars = $query->query_vars;
|
||||
|
||||
// Notify
|
||||
do_action( 'facetwp_found_main_query' );
|
||||
|
||||
// Generate the FWP output
|
||||
$data = ( $this->is_preload ) ? $this->process_preload_data() : $this->process_post_data();
|
||||
$this->output = FWP()->facet->render( $data );
|
||||
|
||||
// Set the updated query vars, needed for maybe_abort_query()
|
||||
$this->query_vars = FWP()->facet->query->query_vars;
|
||||
|
||||
// Set the updated query vars
|
||||
$force_query = apply_filters( 'facetwp_preload_force_query', false, $query );
|
||||
|
||||
if ( ! $this->is_preload || ! empty( $this->url_vars ) || $force_query ) {
|
||||
$query->query_vars = FWP()->facet->query_args;
|
||||
}
|
||||
|
||||
if ( 'product_query' == $query->get( 'wc_query' ) ) {
|
||||
wc_set_loop_prop( 'total', FWP()->facet->pager_args['total_rows'] );
|
||||
wc_set_loop_prop( 'per_page', FWP()->facet->pager_args['per_page'] );
|
||||
wc_set_loop_prop( 'total_pages', FWP()->facet->pager_args['total_pages'] );
|
||||
wc_set_loop_prop( 'current_page', FWP()->facet->pager_args['page'] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is this the main query?
|
||||
*/
|
||||
function is_main_query( $query ) {
|
||||
if ( 'yes' == FWP()->helper->get_setting( 'strict_query_detection', 'no' ) ) {
|
||||
$is_main_query = ( $query->is_main_query() );
|
||||
}
|
||||
else {
|
||||
$is_main_query = ( $query->is_main_query() || $query->is_archive );
|
||||
}
|
||||
|
||||
$is_main_query = ( $query->is_singular || $query->is_feed ) ? false : $is_main_query;
|
||||
$is_main_query = ( $query->get( 'suppress_filters', false ) ) ? false : $is_main_query; // skip get_posts()
|
||||
$is_main_query = ( '' !== $query->get( 'facetwp' ) ) ? (bool) $query->get( 'facetwp' ) : $is_main_query; // flag
|
||||
return apply_filters( 'facetwp_is_main_query', $is_main_query, $query );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the AJAX $_POST data
|
||||
* This gets passed into FWP()->facet->render()
|
||||
*/
|
||||
function process_post_data() {
|
||||
$data = stripslashes_deep( $_POST['data'] );
|
||||
$facets = $data['facets'];
|
||||
$extras = $data['extras'] ?? [];
|
||||
$frozen_facets = $data['frozen_facets'] ?? [];
|
||||
|
||||
$params = [
|
||||
'facets' => [],
|
||||
'template' => $data['template'],
|
||||
'frozen_facets' => $frozen_facets,
|
||||
'http_params' => $data['http_params'],
|
||||
'extras' => $extras,
|
||||
'soft_refresh' => (int) $data['soft_refresh'],
|
||||
'is_bfcache' => (int) $data['is_bfcache'],
|
||||
'first_load' => (int) $data['first_load'], // skip the template?
|
||||
'paged' => (int) $data['paged'],
|
||||
];
|
||||
|
||||
foreach ( $facets as $facet_name => $selected_values ) {
|
||||
$params['facets'][] = [
|
||||
'facet_name' => $facet_name,
|
||||
'selected_values' => $selected_values,
|
||||
];
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* On initial pageload, preload the data
|
||||
*
|
||||
* This gets called twice; once in the template shortcode (to grab only the template)
|
||||
* and again in FWP()->display->front_scripts() to grab everything else.
|
||||
*
|
||||
* Two calls are needed for timing purposes; the template shortcode often renders
|
||||
* before some or all of the other FacetWP-related shortcodes.
|
||||
*/
|
||||
function process_preload_data( $template_name = false, $overrides = [] ) {
|
||||
|
||||
if ( false === $template_name ) {
|
||||
$template_name = $this->template_name ?? 'wp';
|
||||
}
|
||||
|
||||
$this->template_name = $template_name;
|
||||
|
||||
// Is this a template shortcode?
|
||||
$this->is_shortcode = ( 'wp' != $template_name );
|
||||
|
||||
$params = [
|
||||
'facets' => [],
|
||||
'template' => $template_name,
|
||||
'http_params' => [
|
||||
'get' => $_GET,
|
||||
'uri' => FWP()->helper->get_uri(),
|
||||
'url_vars' => $this->url_vars,
|
||||
],
|
||||
'frozen_facets' => [],
|
||||
'soft_refresh' => 1, // skip the facets
|
||||
'is_preload' => 1,
|
||||
'is_bfcache' => 0,
|
||||
'first_load' => 0, // load the template
|
||||
'extras' => [],
|
||||
'paged' => 1,
|
||||
];
|
||||
|
||||
// Support "/page/X/" on preload
|
||||
if ( ! empty( $this->query_vars['paged'] ) ) {
|
||||
$params['paged'] = (int) $this->query_vars['paged'];
|
||||
}
|
||||
|
||||
foreach ( $this->url_vars as $key => $val ) {
|
||||
if ( 'paged' == $key ) {
|
||||
$params['paged'] = $val;
|
||||
}
|
||||
elseif ( 'per_page' == $key || 'sort' == $key ) {
|
||||
$params['extras'][ $key ] = $val;
|
||||
}
|
||||
else {
|
||||
$params['facets'][] = [
|
||||
'facet_name' => $key,
|
||||
'selected_values' => $val,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Override the defaults
|
||||
$params = array_merge( $params, $overrides );
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This gets called from FWP()->display->front_scripts(), when we finally
|
||||
* know which shortcodes are on the page.
|
||||
*
|
||||
* Since we already got the template HTML on the first process_preload_data() call,
|
||||
* this time we're grabbing everything but the template.
|
||||
*
|
||||
* The return value of this method gets passed into the 2nd argument of
|
||||
* process_preload_data().
|
||||
*/
|
||||
function process_preload_overrides( $items ) {
|
||||
$overrides = [];
|
||||
$url_vars = FWP()->request->url_vars;
|
||||
|
||||
foreach ( $items['facets'] as $name ) {
|
||||
$overrides['facets'][] = [
|
||||
'facet_name' => $name,
|
||||
'selected_values' => $url_vars[ $name ] ?? [],
|
||||
];
|
||||
}
|
||||
|
||||
if ( isset( $items['extras']['counts'] ) ) {
|
||||
$overrides['extras']['counts'] = true;
|
||||
}
|
||||
if ( isset( $items['extras']['pager'] ) ) {
|
||||
$overrides['extras']['pager'] = true;
|
||||
}
|
||||
if ( isset( $items['extras']['per_page'] ) ) {
|
||||
$overrides['extras']['per_page'] = $url_vars['per_page'] ?? 'default';
|
||||
}
|
||||
if ( isset( $items['extras']['sort'] ) ) {
|
||||
$overrides['extras']['sort'] = $url_vars['sort'] ?? 'default';
|
||||
}
|
||||
|
||||
$overrides['soft_refresh'] = 0; // load the facets
|
||||
$overrides['first_load'] = 1; // skip the template
|
||||
|
||||
return $overrides;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inject the page HTML into the JSON response
|
||||
* We'll cherry-pick the content from the HTML using front.js
|
||||
*/
|
||||
function inject_template() {
|
||||
$html = ob_get_clean();
|
||||
|
||||
// Throw an error
|
||||
if ( empty( $this->output['settings'] ) ) {
|
||||
$html = __( 'FacetWP was unable to auto-detect the post listing', 'fwp' );
|
||||
}
|
||||
// Grab the <body> contents
|
||||
else {
|
||||
preg_match( "/<body(.*?)>(.*?)<\/body>/s", $html, $matches );
|
||||
|
||||
if ( ! empty( $matches ) ) {
|
||||
$html = trim( $matches[2] );
|
||||
}
|
||||
}
|
||||
|
||||
$this->output['template'] = $html;
|
||||
do_action( 'facetwp_inject_template', $this->output );
|
||||
wp_send_json( $this->output );
|
||||
}
|
||||
}
|
||||
646
wp/wp-content/plugins/facetwp/includes/class-settings.php
Normal file
@@ -0,0 +1,646 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Settings
|
||||
{
|
||||
|
||||
/**
|
||||
* Get the field settings array
|
||||
* @since 3.0.0
|
||||
*/
|
||||
function get_registered_settings() {
|
||||
$defaults = [
|
||||
'general' => [
|
||||
'label' => __( 'General', 'fwp' ),
|
||||
'fields' => [
|
||||
'license_key' => [
|
||||
'label' => __( 'License key', 'fwp' ),
|
||||
'html' => $this->get_setting_html( 'license_key' )
|
||||
],
|
||||
'gmaps_api_key' => [
|
||||
'label' => __( 'Google Maps API key', 'fwp' ),
|
||||
'html' => $this->get_setting_html( 'gmaps_api_key' )
|
||||
],
|
||||
'separators' => [
|
||||
'label' => __( 'Separators', 'fwp' ),
|
||||
'notes' => 'Enter the thousands and decimal separators, respectively',
|
||||
'html' => $this->get_setting_html( 'separators' )
|
||||
],
|
||||
'prefix' => [
|
||||
'label' => __( 'URL prefix', 'fwp' ),
|
||||
'html' => $this->get_setting_html( 'prefix', 'dropdown', [
|
||||
'choices' => [ 'fwp_' => 'fwp_', '_' => '_' ]
|
||||
])
|
||||
],
|
||||
'load_jquery' => [
|
||||
'label' => __( 'Load jQuery', 'fwp' ),
|
||||
'notes' => 'FacetWP no longer requires jQuery, but enable if needed',
|
||||
'html' => $this->get_setting_html( 'load_jquery', 'toggle', [
|
||||
'true_value' => 'yes',
|
||||
'false_value' => 'no'
|
||||
])
|
||||
],
|
||||
'load_a11y' => [
|
||||
'label' => __( 'Load a11y support', 'fwp' ),
|
||||
'notes' => 'Improved accessibility for users with disabilities',
|
||||
'html' => $this->get_setting_html( 'load_a11y', 'toggle', [
|
||||
'true_value' => 'yes',
|
||||
'false_value' => 'no'
|
||||
])
|
||||
],
|
||||
'strict_query_detection' => [
|
||||
'label' => __( 'Strict query detection', 'fwp' ),
|
||||
'notes' => 'Enable if FacetWP auto-detects the wrong archive query',
|
||||
'html' => $this->get_setting_html( 'strict_query_detection', 'toggle', [
|
||||
'true_value' => 'yes',
|
||||
'false_value' => 'no'
|
||||
])
|
||||
],
|
||||
'debug_mode' => [
|
||||
'label' => __( 'Debug mode', 'fwp' ),
|
||||
'notes' => 'After enabling, type "FWP.settings.debug" into the browser console on your front-end facet page',
|
||||
'html' => $this->get_setting_html( 'debug_mode', 'toggle', [
|
||||
'true_value' => 'on',
|
||||
'false_value' => 'off'
|
||||
])
|
||||
]
|
||||
]
|
||||
],
|
||||
'woocommerce' => [
|
||||
'label' => __( 'WooCommerce', 'fwp' ),
|
||||
'fields' => [
|
||||
'wc_enable_variations' => [
|
||||
'label' => __( 'Support product variations?', 'fwp' ),
|
||||
'notes' => __( 'Enable if your store uses variable products.', 'fwp' ),
|
||||
'html' => $this->get_setting_html( 'wc_enable_variations', 'toggle' )
|
||||
],
|
||||
'wc_index_all' => [
|
||||
'label' => __( 'Index out-of-stock products?', 'fwp' ),
|
||||
'notes' => __( 'Show facet choices for out-of-stock products?', 'fwp' ),
|
||||
'html' => $this->get_setting_html( 'wc_index_all', 'toggle' )
|
||||
]
|
||||
]
|
||||
],
|
||||
'backup' => [
|
||||
'label' => __( 'Import / Export', 'fwp' ),
|
||||
'fields' => [
|
||||
'export' => [
|
||||
'label' => __( 'Export', 'fwp' ),
|
||||
'html' => $this->get_setting_html( 'export' )
|
||||
],
|
||||
'import' => [
|
||||
'label' => __( 'Import', 'fwp' ),
|
||||
'html' => $this->get_setting_html( 'import' )
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
if ( ! is_plugin_active( 'woocommerce/woocommerce.php' ) ) {
|
||||
unset( $defaults['woocommerce'] );
|
||||
}
|
||||
|
||||
if ( '_' == FWP()->helper->settings['settings']['prefix'] ) {
|
||||
unset( $defaults['general']['fields']['prefix'] );
|
||||
}
|
||||
|
||||
return apply_filters( 'facetwp_settings_admin', $defaults, $this );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* All facet admin fields
|
||||
* @since 3.9
|
||||
*/
|
||||
function get_registered_facet_fields() {
|
||||
$settings = [
|
||||
'label_any' => [
|
||||
'label' => __( 'Default label', 'fwp' ),
|
||||
'notes' => 'Customize the "Any" label',
|
||||
'default' => __( 'Any', 'fwp' )
|
||||
],
|
||||
'placeholder' => [
|
||||
'label' => __( 'Placeholder text', 'fwp' )
|
||||
],
|
||||
'parent_term' => [
|
||||
'label' => __( 'Parent term', 'fwp' ),
|
||||
'notes' => 'To show only child terms, enter the parent <a href="https://facetwp.com/how-to-find-a-wordpress-terms-id/" target="_blank">term ID</a>. Otherwise, leave blank.',
|
||||
'show' => "facet.source.substr(0, 3) == 'tax'"
|
||||
],
|
||||
'hierarchical' => [
|
||||
'type' => 'toggle',
|
||||
'label' => __( 'Hierarchical', 'fwp' ),
|
||||
'notes' => 'Is this a hierarchical taxonomy?',
|
||||
'show' => "facet.source.substr(0, 3) == 'tax'"
|
||||
],
|
||||
'show_expanded' => [
|
||||
'type' => 'toggle',
|
||||
'label' => __( 'Show expanded', 'fwp' ),
|
||||
'notes' => 'Should child terms be visible by default?',
|
||||
'show' => "facet.hierarchical == 'yes'"
|
||||
],
|
||||
'multiple' => [
|
||||
'type' => 'toggle',
|
||||
'label' => __( 'Multi-select', 'fwp' ),
|
||||
'notes' => 'Allow multiple selections?'
|
||||
],
|
||||
'ghosts' => [
|
||||
'type' => 'alias',
|
||||
'items' => [
|
||||
'ghosts' => [
|
||||
'type' => 'toggle',
|
||||
'label' => __( 'Show ghosts', 'fwp' ),
|
||||
'notes' => 'Show choices that would return zero results?'
|
||||
],
|
||||
'preserve_ghosts' => [
|
||||
'type' => 'toggle',
|
||||
'label' => __( 'Preserve ghost order', 'fwp' ),
|
||||
'notes' => 'Keep ghost choices in the same order?',
|
||||
'show' => "facet.ghosts == 'yes'"
|
||||
]
|
||||
]
|
||||
],
|
||||
'modifiers' => [
|
||||
'type' => 'alias',
|
||||
'items' => [
|
||||
'modifier_type' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Value modifiers', 'fwp' ),
|
||||
'notes' => 'Include or exclude certain values?',
|
||||
'choices' => [
|
||||
'off' => __( 'Off', 'fwp' ),
|
||||
'exclude' => __( 'Exclude these values', 'fwp' ),
|
||||
'include' => __( 'Show only these values', 'fwp' )
|
||||
]
|
||||
],
|
||||
'modifier_values' => [
|
||||
'type' => 'textarea',
|
||||
'label' => '',
|
||||
'placeholder' => 'Add one value per line',
|
||||
'show' => "facet.modifier_type != 'off'"
|
||||
]
|
||||
]
|
||||
],
|
||||
'operator' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Facet logic', 'fwp' ),
|
||||
'notes' => 'How should multiple selections affect the results?',
|
||||
'choices' => [
|
||||
'and' => __( 'AND (match all)', 'fwp' ),
|
||||
'or' => __( 'OR (match any)', 'fwp' )
|
||||
]
|
||||
],
|
||||
'orderby' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Sort by', 'fwp' ),
|
||||
'choices' => [
|
||||
'count' => __( 'Highest count', 'fwp' ),
|
||||
'display_value' => __( 'Display value', 'fwp' ),
|
||||
'raw_value' => __( 'Raw value', 'fwp' ),
|
||||
'term_order' => __( 'Term order', 'fwp' )
|
||||
]
|
||||
],
|
||||
'count' => [
|
||||
'label' => __( 'Count', 'fwp' ),
|
||||
'notes' => 'The maximum number of choices to show (-1 for no limit)',
|
||||
'default' => 10
|
||||
],
|
||||
'soft_limit' => [
|
||||
'label' => __( 'Soft limit', 'fwp' ),
|
||||
'notes' => 'Show a toggle link after this many choices',
|
||||
'default' => 5,
|
||||
'show' => "facet.hierarchical != 'yes'"
|
||||
],
|
||||
'source_other' => [
|
||||
'label' => __( 'Other data source', 'fwp' ),
|
||||
'notes' => 'Use a separate value for the upper limit?',
|
||||
'html' => '<data-sources :facet="facet" setting-name="source_other"></data-sources>'
|
||||
],
|
||||
'compare_type' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Compare type', 'fwp' ),
|
||||
'notes' => "<strong>Basic</strong> - entered range surrounds the post's range<br /><strong>Enclose</strong> - entered range is fully inside the post's range<br /><strong>Intersect</strong> - entered range overlaps the post's range<br /><br />When in doubt, choose <strong>Basic</strong>",
|
||||
'choices' => [
|
||||
'' => __( 'Basic', 'fwp' ),
|
||||
'enclose' => __( 'Enclose', 'fwp' ),
|
||||
'intersect' => __( 'Intersect', 'fwp' )
|
||||
]
|
||||
],
|
||||
'ui_type' => [
|
||||
'label' => __( 'UI type', 'fwp' ),
|
||||
'html' => '<ui-type :facet="facet"></ui-type>'
|
||||
],
|
||||
'reset_text' => [
|
||||
'label' => __( 'Reset text', 'fwp' ),
|
||||
'default' => 'Reset'
|
||||
]
|
||||
];
|
||||
|
||||
foreach ( FWP()->helper->facet_types as $name => $obj ) {
|
||||
if ( method_exists( $obj, 'register_fields' ) ) {
|
||||
$settings = array_merge( $settings, $obj->register_fields() );
|
||||
}
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return HTML for a single facet field (supports aliases)
|
||||
* @since 3.9
|
||||
*/
|
||||
function get_facet_field_html( $name ) {
|
||||
ob_start();
|
||||
|
||||
$fields = FWP()->settings->get_registered_facet_fields();
|
||||
|
||||
if ( isset( $fields[ $name ] ) ) {
|
||||
$field = $fields[ $name ];
|
||||
|
||||
if ( isset( $field['type'] ) && 'alias' == $field['type'] ) {
|
||||
foreach ( $field['items'] as $k => $v ) {
|
||||
$v['name'] = $k;
|
||||
$this->render_facet_field( $v );
|
||||
}
|
||||
}
|
||||
else {
|
||||
$field['name'] = $name;
|
||||
$this->render_facet_field( $field );
|
||||
}
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render a facet field
|
||||
* @since 3.9
|
||||
*/
|
||||
function render_facet_field( $field ) {
|
||||
$name = str_replace( '_', '-', $field['name'] );
|
||||
$type = $field['type'] ?? 'text';
|
||||
$placeholder = $field['placeholder'] ?? '';
|
||||
$show = isset( $field['show'] ) ? ' v-show="' . $field['show'] . '"' : '';
|
||||
$default = isset( $field['default'] ) ? ' value="' . $field['default'] . '"' : '';
|
||||
$label = empty( $field['label'] ) ? '' : $field['label'];
|
||||
|
||||
if ( isset( $field['notes'] ) ) {
|
||||
$label = '<div class="facetwp-tooltip">' . $label . '<div class="facetwp-tooltip-content">' . $field['notes'] . '</div></div>';
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
if ( isset( $field['html'] ) ) {
|
||||
echo $field['html'];
|
||||
}
|
||||
elseif ( 'text' == $type ) {
|
||||
?>
|
||||
<input type="text" class="facet-<?php echo $name; ?>" placeholder="<?php echo $placeholder; ?>"<?php echo $default; ?> />
|
||||
<?php
|
||||
}
|
||||
elseif ( 'textarea' == $type ) {
|
||||
?>
|
||||
<textarea class="facet-<?php echo $name; ?>" placeholder="<?php echo $placeholder; ?>"></textarea>
|
||||
<?php
|
||||
}
|
||||
elseif ( 'toggle' == $type ) {
|
||||
?>
|
||||
<label class="facetwp-switch">
|
||||
<input type="checkbox" class="facet-<?php echo $name; ?>" true-value="yes" false-value="no" />
|
||||
<span class="facetwp-slider"></span>
|
||||
</label>
|
||||
<?php
|
||||
}
|
||||
elseif ( 'select' == $type ) {
|
||||
?>
|
||||
<select class="facet-<?php echo $name; ?>">
|
||||
<?php foreach ( $field['choices'] as $k => $v ) : ?>
|
||||
<option value="<?php echo $k; ?>"><?php echo $v; ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php
|
||||
}
|
||||
|
||||
$html = ob_get_clean();
|
||||
?>
|
||||
<div class="facetwp-row"<?php echo $show; ?>>
|
||||
<div><?php echo $label; ?></div>
|
||||
<div><?php echo $html; ?></div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return HTML for a setting field
|
||||
* @since 3.0.0
|
||||
*/
|
||||
function get_setting_html( $setting_name, $field_type = 'text', $atts = [] ) {
|
||||
ob_start();
|
||||
|
||||
if ( 'license_key' == $setting_name ) : ?>
|
||||
|
||||
<input type="text" class="facetwp-license" style="width:360px" value="<?php echo FWP()->helper->get_license_key(); ?>"<?php echo defined( 'FACETWP_LICENSE_KEY' ) ? ' disabled' : ''; ?> />
|
||||
<div @click="activate" class="btn-normal"><?php _e( 'Activate', 'fwp' ); ?></div>
|
||||
<div class="facetwp-activation-status field-notes"><?php echo $this->get_activation_status(); ?></div>
|
||||
|
||||
<?php elseif ( 'gmaps_api_key' == $setting_name ) : ?>
|
||||
|
||||
<input type="text" v-model="app.settings.gmaps_api_key" style="width:360px" />
|
||||
<a href="https://developers.google.com/maps/documentation/javascript/get-api-key" target="_blank"><?php _e( 'Get an API key', 'fwp' ); ?></a>
|
||||
|
||||
<?php elseif ( 'separators' == $setting_name ) : ?>
|
||||
|
||||
Thousands:
|
||||
<input type="text" v-model="app.settings.thousands_separator" style="width:30px" />
|
||||
Decimal:
|
||||
<input type="text" v-model="app.settings.decimal_separator" style="width:30px" />
|
||||
|
||||
<?php elseif ( 'export' == $setting_name ) : ?>
|
||||
|
||||
<select class="export-items" multiple="multiple">
|
||||
<?php foreach ( $this->get_export_choices() as $val => $label ) : ?>
|
||||
<option value="<?php echo $val; ?>"><?php echo $label; ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<div class="btn-normal export-submit">
|
||||
<?php _e( 'Export', 'fwp' ); ?>
|
||||
</div>
|
||||
|
||||
<?php elseif ( 'import' == $setting_name ) : ?>
|
||||
|
||||
<div><textarea class="import-code" placeholder="<?php _e( 'Paste the import code here', 'fwp' ); ?>"></textarea></div>
|
||||
<div><input type="checkbox" class="import-overwrite" /> <?php _e( 'Overwrite existing items?', 'fwp' ); ?></div>
|
||||
<div style="margin-top:5px">
|
||||
<div class="btn-normal import-submit"><?php _e( 'Import', 'fwp' ); ?></div>
|
||||
</div>
|
||||
|
||||
<?php elseif ( 'dropdown' == $field_type ) : ?>
|
||||
|
||||
<select class="facetwp-setting" v-model="app.settings.<?php echo $setting_name; ?>">
|
||||
<?php foreach ( $atts['choices'] as $val => $label ) : ?>
|
||||
<option value="<?php echo $val; ?>"><?php echo $label; ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
||||
<?php elseif ( 'toggle' == $field_type ) : ?>
|
||||
<?php
|
||||
|
||||
$true_value = $atts['true_value'] ?? 'yes';
|
||||
$false_value = $atts['false_value'] ?? 'no';
|
||||
|
||||
?>
|
||||
<label class="facetwp-switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="app.settings.<?php echo $setting_name; ?>"
|
||||
true-value="<?php echo $true_value; ?>"
|
||||
false-value="<?php echo $false_value; ?>"
|
||||
/>
|
||||
<span class="facetwp-slider"></span>
|
||||
</label>
|
||||
|
||||
<?php endif;
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of all facets and templates
|
||||
* @since 3.0.0
|
||||
*/
|
||||
function get_export_choices() {
|
||||
$export = [];
|
||||
|
||||
$settings = FWP()->helper->settings;
|
||||
|
||||
foreach ( $settings['facets'] as $facet ) {
|
||||
$export['facet-' . $facet['name']] = 'Facet - ' . $facet['label'];
|
||||
}
|
||||
|
||||
foreach ( $settings['templates'] as $template ) {
|
||||
$export['template-' . $template['name']] = 'Template - '. $template['label'];
|
||||
}
|
||||
|
||||
return $export;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the activation status
|
||||
* @since 3.0.0
|
||||
*/
|
||||
function get_activation_status() {
|
||||
$message = __( 'Not yet activated', 'fwp' );
|
||||
$status = FWP()->helper->get_license_meta( 'status' );
|
||||
|
||||
if ( false !== $status ) {
|
||||
if ( 'success' == $status ) {
|
||||
$expiration = FWP()->helper->get_license_meta( 'expiration' );
|
||||
$expiration = date( 'M j, Y', strtotime( $expiration ) );
|
||||
$message = __( 'Valid until', 'fwp' ) . ' ' . $expiration;
|
||||
}
|
||||
else {
|
||||
$message = FWP()->helper->get_license_meta( 'message' );
|
||||
}
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load i18n admin strings
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function get_i18n_strings() {
|
||||
return [
|
||||
'Number of grid columns' => __( 'Number of grid columns', 'fwp' ),
|
||||
'Spacing between results' => __( 'Spacing between results', 'fwp' ),
|
||||
'No results text' => __( 'No results text', 'fwp' ),
|
||||
'Text style' => __( 'Text style', 'fwp' ),
|
||||
'Text color' => __( 'Text color', 'fwp' ),
|
||||
'Font size' => __( 'Font size', 'fwp' ),
|
||||
'Background color' => __( 'Background color', 'fwp' ),
|
||||
'Border' => __( 'Border', 'fwp' ),
|
||||
'Border style' => __( 'Border style', 'fwp' ),
|
||||
'None' => __( 'None', 'fwp' ),
|
||||
'Solid' => __( 'Solid', 'fwp' ),
|
||||
'Dashed' => __( 'Dashed', 'fwp' ),
|
||||
'Dotted' => __( 'Dotted', 'fwp' ),
|
||||
'Double' => __( 'Double', 'fwp' ),
|
||||
'Border color' => __( 'Border color', 'fwp' ),
|
||||
'Border width' => __( 'Border width', 'fwp' ),
|
||||
'Button text' => __( 'Button text', 'fwp' ),
|
||||
'Button text color' => __( 'Button text color', 'fwp' ),
|
||||
'Button padding' => __( 'Button padding', 'fwp' ),
|
||||
'Separator' => __( 'Separator', 'fwp' ),
|
||||
'Custom CSS' => __( 'Custom CSS', 'fwp' ),
|
||||
'Column widths' => __( 'Column widths', 'fwp' ),
|
||||
'Content' => __( 'Content', 'fwp' ),
|
||||
'Image size' => __( 'Image size', 'fwp' ),
|
||||
'Author field' => __( 'Author field', 'fwp' ),
|
||||
'Display name' => __( 'Display name', 'fwp' ),
|
||||
'User login' => __( 'User login', 'fwp' ),
|
||||
'User ID' => __( 'User ID', 'fwp' ),
|
||||
'Field type' => __( 'Field type', 'fwp' ),
|
||||
'Text' => __( 'Text', 'fwp' ),
|
||||
'Date' => __( 'Date', 'fwp' ),
|
||||
'Number' => __( 'Number', 'fwp' ),
|
||||
'Date format' => __( 'Date format', 'fwp' ),
|
||||
'Input format' => __( 'Input format', 'fwp' ),
|
||||
'Number format' => __( 'Number format', 'fwp' ),
|
||||
'Link' => __( 'Link', 'fwp' ),
|
||||
'Link type' => __( 'Link type', 'fwp' ),
|
||||
'Post URL' => __( 'Post URL', 'fwp' ),
|
||||
'Custom URL' => __( 'Custom URL', 'fwp' ),
|
||||
'Open in new tab?' => __( 'Open in new tab?', 'fwp' ),
|
||||
'Prefix' => __( 'Prefix', 'fwp' ),
|
||||
'Suffix' => __( 'Suffix', 'fwp' ),
|
||||
'Hide item?' => __( 'Hide item?', 'fwp' ),
|
||||
'Padding' => __( 'Padding', 'fwp' ),
|
||||
'CSS class' => __( 'CSS class', 'fwp' ),
|
||||
'Button Border' => __( 'Button border', 'fwp' ),
|
||||
'Term URL' => __( 'Term URL', 'fwp' ),
|
||||
'Fetch' => __( 'Fetch', 'fwp' ),
|
||||
'All post types' => __( 'All post types', 'fwp' ),
|
||||
'and show' => __( 'and show', 'fwp' ),
|
||||
'per page' => __( 'per page', 'fwp' ),
|
||||
'Sort by' => __( 'Sort by', 'fwp' ),
|
||||
'Posts' => __( 'Posts', 'fwp' ),
|
||||
'Post Title' => __( 'Post Title', 'fwp' ),
|
||||
'Post Name' => __( 'Post Name', 'fwp' ),
|
||||
'Post Type' => __( 'Post Type', 'fwp' ),
|
||||
'Post Date' => __( 'Post Date', 'fwp' ),
|
||||
'Post Modified' => __( 'Post Modified', 'fwp' ),
|
||||
'Comment Count' => __( 'Comment Count', 'fwp' ),
|
||||
'Menu Order' => __( 'Menu Order', 'fwp' ),
|
||||
'Custom Fields' => __( 'Custom Fields', 'fwp' ),
|
||||
'Narrow results by' => __( 'Narrow results by', 'fwp' ),
|
||||
'Hit Enter' => __( 'Hit Enter', 'fwp' ),
|
||||
'Add query sort' => __( 'Add query sort', 'fwp' ),
|
||||
'Add query filter' => __( 'Add query filter', 'fwp' ),
|
||||
'Clear' => __( 'Clear', 'fwp' ),
|
||||
'Enter term slugs' => __( 'Enter term slugs', 'fwp' ),
|
||||
'Enter values' => __( 'Enter values', 'fwp' ),
|
||||
'Layout' => __( 'Layout', 'fwp' ),
|
||||
'Content' => __( 'Content', 'fwp' ),
|
||||
'Style' => __( 'Style', 'fwp' ),
|
||||
'Row' => __( 'Row', 'fwp' ),
|
||||
'Column' => __( 'Column', 'fwp' ),
|
||||
'Start typing' => __( 'Start typing', 'fwp' ),
|
||||
'Label' => __( 'Label', 'fwp' ),
|
||||
'Unique name' => __( 'Unique name', 'fwp' ),
|
||||
'Facet type' => __( 'Facet type', 'fwp' ),
|
||||
'Copy shortcode' => __( 'Copy shortcode', 'fwp' ),
|
||||
'Data source' => __( 'Data source', 'fwp' ),
|
||||
'Switch to advanced mode' => __( 'Switch to advanced mode', 'fwp' ),
|
||||
'Switch to visual mode' => __( 'Switch to visual mode', 'fwp' ),
|
||||
'Display' => __( 'Display', 'fwp' ),
|
||||
'Query' => __( 'Query', 'fwp' ),
|
||||
'Help' => __( 'Help', 'fwp' ),
|
||||
'Display Code' => __( 'Display Code', 'fwp' ),
|
||||
'Query Arguments' => __( 'Query Arguments', 'fwp' ),
|
||||
'Saving' => __( 'Saving', 'fwp' ),
|
||||
'Indexing' => __( 'Indexing', 'fwp' ),
|
||||
'The index table is empty' => __( 'The index table is empty', 'fwp' ),
|
||||
'Indexing complete' => __( 'Indexing complete', 'fwp' ),
|
||||
'Looking' => __( 'Looking', 'fwp' ),
|
||||
'Purging' => __( 'Purging', 'fwp' ),
|
||||
'Copied!' => __( 'Copied!', 'fwp' ),
|
||||
'Press CTRL+C to copy' => __( 'Press CTRL+C to copy', 'fwp' ),
|
||||
'Activating' => __( 'Activating', 'fwp' ),
|
||||
'Re-index' => __( 'Re-index', 'fwp' ),
|
||||
'Stop indexer' => __( 'Stop indexer', 'fwp' ),
|
||||
'Loading' => __( 'Loading', 'fwp' ),
|
||||
'Importing' => __( 'Importing', 'fwp' ),
|
||||
'Convert to query args' => __( 'Convert to query args', 'fwp' )
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get available image sizes
|
||||
* @since 3.2.7
|
||||
*/
|
||||
function get_image_sizes() {
|
||||
global $_wp_additional_image_sizes;
|
||||
|
||||
$sizes = [];
|
||||
|
||||
$default_sizes = [ 'thumbnail', 'medium', 'medium_large', 'large', 'full' ];
|
||||
|
||||
foreach ( get_intermediate_image_sizes() as $size ) {
|
||||
if ( in_array( $size, $default_sizes ) ) {
|
||||
$sizes[ $size ]['width'] = (int) get_option( "{$size}_size_w" );
|
||||
$sizes[ $size ]['height'] = (int) get_option( "{$size}_size_h" );
|
||||
}
|
||||
elseif ( isset( $_wp_additional_image_sizes[ $size ] ) ) {
|
||||
$sizes[ $size ] = $_wp_additional_image_sizes[ $size ];
|
||||
}
|
||||
}
|
||||
|
||||
return $sizes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return an array of formatted image sizes
|
||||
* @since 3.2.7
|
||||
*/
|
||||
function get_image_size_labels() {
|
||||
$labels = [];
|
||||
$sizes = $this->get_image_sizes();
|
||||
|
||||
foreach ( $sizes as $size => $data ) {
|
||||
$height = ( 0 === $data['height'] ) ? 'w' : 'x' . $data['height'];
|
||||
$label = $size . ' (' . $data['width'] . $height . ')';
|
||||
$labels[ $size ] = $label;
|
||||
}
|
||||
|
||||
$labels['full'] = __( 'full', 'fwp' );
|
||||
|
||||
return $labels;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create SVG images (based on Font Awesome)
|
||||
* @license https://fontawesome.com/license/free CC BY 4.0
|
||||
* @since 3.6.5
|
||||
*/
|
||||
function get_svg() {
|
||||
$output = [];
|
||||
|
||||
$icons = [
|
||||
// [viewBox width, viewBox height, icon width, svg data]
|
||||
"align-center" => [448, 512, 14, "M432 160H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0 256H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM108.1 96h231.81A12.09 12.09 0 0 0 352 83.9V44.09A12.09 12.09 0 0 0 339.91 32H108.1A12.09 12.09 0 0 0 96 44.09V83.9A12.1 12.1 0 0 0 108.1 96zm231.81 256A12.09 12.09 0 0 0 352 339.9v-39.81A12.09 12.09 0 0 0 339.91 288H108.1A12.09 12.09 0 0 0 96 300.09v39.81a12.1 12.1 0 0 0 12.1 12.1z"],
|
||||
"align-left" => [448, 512, 14, "M12.83 352h262.34A12.82 12.82 0 0 0 288 339.17v-38.34A12.82 12.82 0 0 0 275.17 288H12.83A12.82 12.82 0 0 0 0 300.83v38.34A12.82 12.82 0 0 0 12.83 352zm0-256h262.34A12.82 12.82 0 0 0 288 83.17V44.83A12.82 12.82 0 0 0 275.17 32H12.83A12.82 12.82 0 0 0 0 44.83v38.34A12.82 12.82 0 0 0 12.83 96zM432 160H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0 256H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16z"],
|
||||
"align-right" => [448, 512, 14, "M16 224h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16zm416 192H16a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm3.17-384H172.83A12.82 12.82 0 0 0 160 44.83v38.34A12.82 12.82 0 0 0 172.83 96h262.34A12.82 12.82 0 0 0 448 83.17V44.83A12.82 12.82 0 0 0 435.17 32zm0 256H172.83A12.82 12.82 0 0 0 160 300.83v38.34A12.82 12.82 0 0 0 172.83 352h262.34A12.82 12.82 0 0 0 448 339.17v-38.34A12.82 12.82 0 0 0 435.17 288z"],
|
||||
"arrow-circle-up" => [512, 512, 16, "M8 256C8 119 119 8 256 8s248 111 248 248-111 248-248 248S8 393 8 256zm143.6 28.9l72.4-75.5V392c0 13.3 10.7 24 24 24h16c13.3 0 24-10.7 24-24V209.4l72.4 75.5c9.3 9.7 24.8 9.9 34.3.4l10.9-11c9.4-9.4 9.4-24.6 0-33.9L273 107.7c-9.4-9.4-24.6-9.4-33.9 0L106.3 240.4c-9.4 9.4-9.4 24.6 0 33.9l10.9 11c9.6 9.5 25.1 9.3 34.4-.4z"],
|
||||
"bold" => [384, 512, 12, "M333.49 238a122 122 0 0 0 27-65.21C367.87 96.49 308 32 233.42 32H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h31.87v288H34a16 16 0 0 0-16 16v48a16 16 0 0 0 16 16h209.32c70.8 0 134.14-51.75 141-122.4 4.74-48.45-16.39-92.06-50.83-119.6zM145.66 112h87.76a48 48 0 0 1 0 96h-87.76zm87.76 288h-87.76V288h87.76a56 56 0 0 1 0 112z"],
|
||||
"caret-down" => [320, 512, 10, "M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z"],
|
||||
"cog" => [512, 512, 16, "M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z"],
|
||||
"columns" => [512, 512, 16, "M464 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V80c0-26.51-21.49-48-48-48zM224 416H64V160h160v256zm224 0H288V160h160v256z"],
|
||||
"eye-slash" => [640, 512, 20, "M320 400c-75.85 0-137.25-58.71-142.9-133.11L72.2 185.82c-13.79 17.3-26.48 35.59-36.72 55.59a32.35 32.35 0 0 0 0 29.19C89.71 376.41 197.07 448 320 448c26.91 0 52.87-4 77.89-10.46L346 397.39a144.13 144.13 0 0 1-26 2.61zm313.82 58.1l-110.55-85.44a331.25 331.25 0 0 0 81.25-102.07 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64a308.15 308.15 0 0 0-147.32 37.7L45.46 3.37A16 16 0 0 0 23 6.18L3.37 31.45A16 16 0 0 0 6.18 53.9l588.36 454.73a16 16 0 0 0 22.46-2.81l19.64-25.27a16 16 0 0 0-2.82-22.45zm-183.72-142l-39.3-30.38A94.75 94.75 0 0 0 416 256a94.76 94.76 0 0 0-121.31-92.21A47.65 47.65 0 0 1 304 192a46.64 46.64 0 0 1-1.54 10l-73.61-56.89A142.31 142.31 0 0 1 320 112a143.92 143.92 0 0 1 144 144c0 21.63-5.29 41.79-13.9 60.11z"],
|
||||
"italic" => [320, 512, 10, "M320 48v32a16 16 0 0 1-16 16h-62.76l-80 320H208a16 16 0 0 1 16 16v32a16 16 0 0 1-16 16H16a16 16 0 0 1-16-16v-32a16 16 0 0 1 16-16h62.76l80-320H112a16 16 0 0 1-16-16V48a16 16 0 0 1 16-16h192a16 16 0 0 1 16 16z"],
|
||||
"lock" => [448, 512, 14, "M400 224h-24v-72C376 68.2 307.8 0 224 0S72 68.2 72 152v72H48c-26.5 0-48 21.5-48 48v192c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V272c0-26.5-21.5-48-48-48zm-104 0H152v-72c0-39.7 32.3-72 72-72s72 32.3 72 72v72z"],
|
||||
"lock-open" => [576, 512, 18, "M423.5 0C339.5.3 272 69.5 272 153.5V224H48c-26.5 0-48 21.5-48 48v192c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V272c0-26.5-21.5-48-48-48h-48v-71.1c0-39.6 31.7-72.5 71.3-72.9 40-.4 72.7 32.1 72.7 72v80c0 13.3 10.7 24 24 24h32c13.3 0 24-10.7 24-24v-80C576 68 507.5-.3 423.5 0z"],
|
||||
"minus-circle" => [512, 512, 16, "M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zM124 296c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h264c6.6 0 12 5.4 12 12v56c0 6.6-5.4 12-12 12H124z"],
|
||||
"plus" => [448, 512, 14, "M416 208H272V64c0-17.67-14.33-32-32-32h-32c-17.67 0-32 14.33-32 32v144H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h144v144c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32V304h144c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z"],
|
||||
"plus-circle" => [512, 512, 16, "M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm144 276c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92h-92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"],
|
||||
"times" => [352, 512, 11, "M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"]
|
||||
];
|
||||
|
||||
foreach ( $icons as $name => $attr ) {
|
||||
$svg = '<svg aria-hidden="true" focusable="false" class="svg-inline--fa fa-{name} fa-w-{faw}" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {w} {h}"><path fill="currentColor" d="{d}"></path></svg>';
|
||||
$search = [ '{name}', '{w}', '{h}', '{faw}', '{d}' ];
|
||||
$replace = [ $name, $attr[0], $attr[1], $attr[2], $attr[3] ];
|
||||
$output[ $name ] = str_replace( $search, $replace, $svg );
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
183
wp/wp-content/plugins/facetwp/includes/class-updater.php
Normal file
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Updater
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
add_filter( 'plugins_api', [ $this, 'plugins_api' ], 10, 3 );
|
||||
add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'check_update' ] );
|
||||
add_action( 'in_plugin_update_message-' . FACETWP_BASENAME, [ $this, 'in_plugin_update_message' ], 10, 2 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get info for FacetWP and its add-ons
|
||||
*/
|
||||
function get_plugins_to_check() {
|
||||
$output = [];
|
||||
$plugins = get_plugins();
|
||||
|
||||
if ( is_multisite() ) {
|
||||
$active_plugins = get_site_option( 'active_sitewide_plugins', [] );
|
||||
$active_plugins = array_flip( $active_plugins ); // [plugin] => path
|
||||
}
|
||||
else {
|
||||
$active_plugins = get_option( 'active_plugins', [] );
|
||||
}
|
||||
|
||||
foreach ( $active_plugins as $plugin_path ) {
|
||||
if ( is_multisite() ) {
|
||||
$plugin_path = str_replace( WP_PLUGIN_DIR, '', $plugin_path );
|
||||
}
|
||||
|
||||
if ( isset( $plugins[ $plugin_path ] ) ) {
|
||||
$info = $plugins[ $plugin_path ];
|
||||
$slug = trim( dirname( $plugin_path ), '/' );
|
||||
|
||||
// only intercept FacetWP and its add-ons
|
||||
$is_valid = in_array( $slug, [ 'facetwp', 'user-post-type' ] );
|
||||
$is_valid = ( 0 === strpos( $slug, 'facetwp-' ) ) ? true : $is_valid;
|
||||
|
||||
if ( $is_valid ) {
|
||||
$output[ $slug ] = [
|
||||
'name' => $info['Name'],
|
||||
'version' => $info['Version'],
|
||||
'description' => $info['Description'],
|
||||
'plugin_path' => $plugin_path,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle the "View Details" popup
|
||||
*
|
||||
* $args->slug = "facetwp-flyout"
|
||||
* plugin_path = "facetwp-flyout/facetwp-flyout.php"
|
||||
*/
|
||||
function plugins_api( $result, $action, $args ) {
|
||||
if ( 'plugin_information' == $action ) {
|
||||
$slug = $args->slug;
|
||||
$to_check = $this->get_plugins_to_check();
|
||||
|
||||
$response = get_option( 'facetwp_updater_response', '' );
|
||||
$response = json_decode( $response, true );
|
||||
|
||||
if ( isset( $to_check[ $slug ] ) && isset( $response[ $slug ] ) ) {
|
||||
$local_data = $to_check[ $slug ];
|
||||
$remote_data = $response[ $slug ];
|
||||
|
||||
return (object) [
|
||||
'name' => $local_data['name'],
|
||||
'slug' => $local_data['plugin_path'],
|
||||
'version' => $remote_data['version'],
|
||||
'last_updated' => $remote_data['last_updated'],
|
||||
'download_link' => $remote_data['package'],
|
||||
'sections' => [
|
||||
'description' => $local_data['description'],
|
||||
'changelog' => $remote_data['sections']['changelog']
|
||||
],
|
||||
'homepage' => 'https://facetwp.com/',
|
||||
'rating' => 100,
|
||||
'num_ratings' => 1
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grab (and cache) plugin update data
|
||||
*/
|
||||
function check_update( $transient ) {
|
||||
if ( empty( $transient->checked ) ) {
|
||||
return $transient;
|
||||
}
|
||||
|
||||
$now = strtotime( 'now' );
|
||||
$response = get_option( 'facetwp_updater_response', '' );
|
||||
$ts = (int) get_option( 'facetwp_updater_last_checked' );
|
||||
$plugins = $this->get_plugins_to_check();
|
||||
|
||||
if ( empty( $response ) || $ts + 14400 < $now ) {
|
||||
|
||||
$request = wp_remote_post( 'https://api.facetwp.com', [
|
||||
'body' => [
|
||||
'action' => 'check_plugins',
|
||||
'slugs' => array_keys( $plugins ),
|
||||
'license' => FWP()->helper->get_license_key(),
|
||||
'host' => FWP()->helper->get_http_host(),
|
||||
'wp_v' => get_bloginfo( 'version' ),
|
||||
'fwp_v' => FACETWP_VERSION,
|
||||
'php_v' => phpversion(),
|
||||
]
|
||||
] );
|
||||
|
||||
if ( ! is_wp_error( $request ) || 200 == wp_remote_retrieve_response_code( $request ) ) {
|
||||
$body = json_decode( $request['body'], true );
|
||||
$activation = json_encode( $body['activation'] );
|
||||
$response = json_encode( $body['slugs'] );
|
||||
}
|
||||
|
||||
update_option( 'facetwp_activation', $activation );
|
||||
update_option( 'facetwp_updater_response', $response, 'no' );
|
||||
update_option( 'facetwp_updater_last_checked', $now, 'no' );
|
||||
}
|
||||
|
||||
if ( ! empty( $response ) ) {
|
||||
$response = json_decode( $response, true );
|
||||
|
||||
foreach ( $response as $slug => $info ) {
|
||||
if ( isset( $plugins[ $slug ] ) ) {
|
||||
$plugin_path = $plugins[ $slug ]['plugin_path'];
|
||||
$response_obj = (object) [
|
||||
'slug' => $slug,
|
||||
'plugin' => $plugin_path,
|
||||
'new_version' => $info['version'],
|
||||
'package' => $info['package'],
|
||||
'requires' => $info['requires'],
|
||||
'tested' => $info['tested']
|
||||
];
|
||||
|
||||
if ( version_compare( $plugins[ $slug ]['version'], $info['version'], '<' ) ) {
|
||||
$transient->response[ $plugin_path ] = $response_obj;
|
||||
}
|
||||
else {
|
||||
$transient->no_update[ $plugin_path ] = $response_obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $transient;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display a license reminder on the plugin list screen
|
||||
*/
|
||||
function in_plugin_update_message( $plugin_data, $response ) {
|
||||
if ( ! FWP()->helper->is_license_active() ) {
|
||||
$price_id = (int) FWP()->helper->get_license_meta( 'price_id' );
|
||||
$license = FWP()->helper->get_license_key();
|
||||
|
||||
if ( 0 < $price_id ) {
|
||||
echo '<br />' . sprintf(
|
||||
__( 'Please <a href="%s" target="_blank">renew your license</a> for automatic updates.', 'fwp' ),
|
||||
esc_url( "https://facetwp.com/checkout/?edd_action=add_to_cart&download_id=24&edd_options[price_id]=$price_id&discount=$license" )
|
||||
);
|
||||
}
|
||||
else {
|
||||
echo '<br />' . __( 'Please activate your FacetWP license for automatic updates.', 'fwp' );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new FacetWP_Updater();
|
||||
106
wp/wp-content/plugins/facetwp/includes/class-upgrade.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Upgrade
|
||||
{
|
||||
function __construct() {
|
||||
$this->version = FACETWP_VERSION;
|
||||
$this->last_version = get_option( 'facetwp_version' );
|
||||
|
||||
if ( version_compare( $this->last_version, $this->version, '<' ) ) {
|
||||
if ( version_compare( $this->last_version, '0.1.0', '<' ) ) {
|
||||
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
||||
$this->clean_install();
|
||||
}
|
||||
else {
|
||||
$this->run_upgrade();
|
||||
}
|
||||
|
||||
update_option( 'facetwp_version', $this->version );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function clean_install() {
|
||||
global $wpdb;
|
||||
|
||||
$int = apply_filters( 'facetwp_use_bigint', false ) ? 'BIGINT' : 'INT';
|
||||
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
|
||||
$sql = "
|
||||
CREATE TABLE IF NOT EXISTS {$wpdb->prefix}facetwp_index (
|
||||
id BIGINT unsigned not null auto_increment,
|
||||
post_id $int unsigned,
|
||||
facet_name VARCHAR(50),
|
||||
facet_value VARCHAR(50),
|
||||
facet_display_value VARCHAR(200),
|
||||
term_id $int unsigned default '0',
|
||||
parent_id $int unsigned default '0',
|
||||
depth INT unsigned default '0',
|
||||
variation_id $int unsigned default '0',
|
||||
PRIMARY KEY (id),
|
||||
INDEX post_id_idx (post_id),
|
||||
INDEX facet_name_idx (facet_name),
|
||||
INDEX facet_name_value_idx (facet_name, facet_value)
|
||||
) $charset_collate";
|
||||
dbDelta( $sql );
|
||||
|
||||
// Add default settings
|
||||
$settings = file_get_contents( FACETWP_DIR . '/assets/js/src/sample.json' );
|
||||
add_option( 'facetwp_settings', $settings );
|
||||
}
|
||||
|
||||
|
||||
private function run_upgrade() {
|
||||
global $wpdb;
|
||||
|
||||
$table = sanitize_key( $wpdb->prefix . 'facetwp_index' );
|
||||
|
||||
if ( version_compare( $this->last_version, '3.1.0', '<' ) ) {
|
||||
$wpdb->query( "ALTER TABLE $table MODIFY facet_name VARCHAR(50)" );
|
||||
$wpdb->query( "ALTER TABLE $table MODIFY facet_value VARCHAR(50)" );
|
||||
$wpdb->query( "ALTER TABLE $table MODIFY facet_display_value VARCHAR(200)" );
|
||||
$wpdb->query( "CREATE INDEX facet_name_value_idx ON $table (facet_name, facet_value)" );
|
||||
}
|
||||
|
||||
if ( version_compare( $this->last_version, '3.3.2', '<' ) ) {
|
||||
$wpdb->query( "CREATE INDEX post_id_idx ON $table (post_id)" );
|
||||
$wpdb->query( "DROP INDEX facet_source_idx ON $table" );
|
||||
$wpdb->query( "ALTER TABLE $table DROP COLUMN facet_source" );
|
||||
}
|
||||
|
||||
if ( version_compare( $this->last_version, '3.3.3', '<' ) ) {
|
||||
if ( function_exists( 'SWP' ) ) {
|
||||
$engines = array_keys( SWP()->settings['engines'] );
|
||||
$settings = get_option( 'facetwp_settings' );
|
||||
$settings = json_decode( $settings, true );
|
||||
|
||||
foreach ( $settings['facets'] as $key => $facet ) {
|
||||
if ( 'search' == $facet['type'] ) {
|
||||
if ( in_array( $facet['search_engine'], $engines ) ) {
|
||||
$settings['facets'][ $key ]['search_engine'] = 'swp_' . $facet['search_engine'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_option( 'facetwp_settings', json_encode( $settings ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( version_compare( $this->last_version, '3.5.3', '<' ) ) {
|
||||
update_option( 'facetwp_updater_response', '', 'no' );
|
||||
update_option( 'facetwp_updater_last_checked', '', 'no' );
|
||||
}
|
||||
|
||||
if ( version_compare( $this->last_version, '3.5.4', '<' ) ) {
|
||||
$settings = get_option( 'facetwp_settings' );
|
||||
$settings = json_decode( $settings, true );
|
||||
|
||||
if ( ! isset( $settings['settings']['prefix'] ) ) {
|
||||
$settings['settings']['prefix'] = 'fwp_';
|
||||
|
||||
update_option( 'facetwp_settings', json_encode( $settings ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
163
wp/wp-content/plugins/facetwp/includes/facets/autocomplete.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Autocomplete extends FacetWP_Facet
|
||||
{
|
||||
|
||||
public $is_buffering = false;
|
||||
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Autocomplete', 'fwp' );
|
||||
$this->fields = [ 'placeholder' ];
|
||||
|
||||
// ajax
|
||||
add_action( 'facetwp_autocomplete_load', [ $this, 'ajax_load' ] );
|
||||
|
||||
// css-based template
|
||||
add_action( 'facetwp_init', [ $this, 'maybe_buffer_output' ] );
|
||||
add_action( 'facetwp_found_main_query', [ $this, 'template_handler' ] );
|
||||
|
||||
// result limit
|
||||
$this->limit = (int) apply_filters( 'facetwp_facet_autocomplete_limit', 10 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* For page templates with a custom WP_Query, we need to prevent the
|
||||
* page header from being output with the autocomplete JSON
|
||||
*/
|
||||
function maybe_buffer_output() {
|
||||
if ( isset( $_POST['action'] ) && 'facetwp_autocomplete_load' == $_POST['action'] ) {
|
||||
$this->is_buffering = true;
|
||||
ob_start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* For CSS-based templates, the "facetwp_autocomplete_load" action isn't fired
|
||||
* so we need to manually check the action
|
||||
*/
|
||||
function template_handler() {
|
||||
if ( isset( $_POST['action'] ) && 'facetwp_autocomplete_load' == $_POST['action'] ) {
|
||||
if ( $this->is_buffering ) {
|
||||
while ( ob_get_level() ) {
|
||||
ob_end_clean();
|
||||
}
|
||||
}
|
||||
$this->ajax_load();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = '';
|
||||
$facet = $params['facet'];
|
||||
$value = (array) $params['selected_values'];
|
||||
$value = empty( $value ) ? '' : stripslashes( $value[0] );
|
||||
$placeholder = empty( $facet['placeholder'] ) ? __( 'Start typing', 'fwp-front' ) : $facet['placeholder'];
|
||||
$placeholder = facetwp_i18n( $placeholder );
|
||||
$output .= '<input type="text" class="facetwp-autocomplete" value="' . esc_attr( $value ) . '" placeholder="' . esc_attr( $placeholder ) . '" autocomplete="off" />';
|
||||
$output .= '<input type="button" class="facetwp-autocomplete-update" value="' . __( 'Go', 'fwp-front' ) . '" />';
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$selected_values = $params['selected_values'];
|
||||
$selected_values = is_array( $selected_values ) ? $selected_values[0] : $selected_values;
|
||||
$selected_values = stripslashes( $selected_values );
|
||||
|
||||
if ( empty( $selected_values ) ) {
|
||||
return 'continue';
|
||||
}
|
||||
|
||||
$sql = "
|
||||
SELECT DISTINCT post_id FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE facet_name = %s AND facet_display_value LIKE %s";
|
||||
|
||||
$sql = $wpdb->prepare( $sql, $facet['name'], '%' . $selected_values . '%' );
|
||||
return facetwp_sql( $sql, $facet );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Output any front-end scripts
|
||||
*/
|
||||
function front_scripts() {
|
||||
FWP()->display->json['no_results'] = __( 'No results', 'fwp-front' );
|
||||
FWP()->display->assets['fComplete.js'] = FACETWP_URL . '/assets/vendor/fComplete/fComplete.js';
|
||||
FWP()->display->assets['fComplete.css'] = FACETWP_URL . '/assets/vendor/fComplete/fComplete.css';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load facet values via AJAX
|
||||
*/
|
||||
function ajax_load() {
|
||||
global $wpdb;
|
||||
|
||||
// optimizations
|
||||
$_POST['data']['soft_refresh'] = 1;
|
||||
$_POST['data']['extras'] = [];
|
||||
|
||||
$query = stripslashes( $_POST['query'] );
|
||||
$query = FWP()->helper->sanitize( $wpdb->esc_like( $query ) );
|
||||
$facet_name = FWP()->helper->sanitize( $_POST['facet_name'] );
|
||||
$output = [];
|
||||
|
||||
// simulate a refresh
|
||||
FWP()->facet->render(
|
||||
FWP()->request->process_post_data()
|
||||
);
|
||||
|
||||
// then grab the matching post IDs
|
||||
$where_clause = $this->get_where_clause( [ 'name' => $facet_name ] );
|
||||
|
||||
if ( ! empty( $query ) && ! empty( $facet_name ) ) {
|
||||
$sql = "
|
||||
SELECT DISTINCT facet_display_value
|
||||
FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE
|
||||
facet_name = '$facet_name' AND
|
||||
facet_display_value LIKE '%$query%'
|
||||
$where_clause
|
||||
ORDER BY facet_display_value ASC
|
||||
LIMIT $this->limit";
|
||||
|
||||
$results = $wpdb->get_results( $sql );
|
||||
|
||||
foreach ( $results as $result ) {
|
||||
$output[] = [
|
||||
'value' => $result->facet_display_value,
|
||||
'label' => $result->facet_display_value,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
wp_send_json( $output );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Front-end) Attach settings to the AJAX response
|
||||
*/
|
||||
function settings_js( $params ) {
|
||||
return [
|
||||
'loadingText' => __( 'Loading', 'fwp-front' ) . '...',
|
||||
'minCharsText' => __( 'Enter {n} or more characters', 'fwp-front' ),
|
||||
'noResultsText' => __( 'No results', 'fwp-front' ),
|
||||
'maxResults' => $this->limit
|
||||
];
|
||||
}
|
||||
}
|
||||
106
wp/wp-content/plugins/facetwp/includes/facets/base.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet
|
||||
{
|
||||
|
||||
/**
|
||||
* Grab the orderby, as needed by several facet types
|
||||
* @since 3.0.4
|
||||
*/
|
||||
function get_orderby( $facet ) {
|
||||
$key = $facet['orderby'];
|
||||
|
||||
// Count (default)
|
||||
$orderby = 'counter DESC, f.facet_display_value ASC';
|
||||
|
||||
// Display value
|
||||
if ( 'display_value' == $key ) {
|
||||
$orderby = 'f.facet_display_value ASC';
|
||||
}
|
||||
// Raw value
|
||||
elseif ( 'raw_value' == $key ) {
|
||||
$orderby = 'f.facet_value ASC';
|
||||
}
|
||||
// Term order
|
||||
elseif ( 'term_order' == $key && 'tax' == substr( $facet['source'], 0, 3 ) ) {
|
||||
$term_ids = get_terms( [
|
||||
'taxonomy' => str_replace( 'tax/', '', $facet['source'] ),
|
||||
'term_order' => true, // Custom flag
|
||||
'fields' => 'ids',
|
||||
] );
|
||||
|
||||
if ( ! empty( $term_ids ) && ! is_wp_error( $term_ids ) ) {
|
||||
$term_ids = implode( ',', $term_ids );
|
||||
$orderby = "FIELD(f.term_id, $term_ids)";
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by depth
|
||||
if ( FWP()->helper->facet_is( $facet, 'hierarchical', 'yes' ) ) {
|
||||
$orderby = "f.depth, $orderby";
|
||||
}
|
||||
|
||||
return $orderby;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grab the limit, and support -1
|
||||
* @since 3.5.4
|
||||
*/
|
||||
function get_limit( $facet, $default = 10 ) {
|
||||
$count = $facet['count'];
|
||||
|
||||
if ( '-1' == $count ) {
|
||||
return 1000;
|
||||
}
|
||||
elseif ( ctype_digit( $count ) ) {
|
||||
return $count;
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adjust the $where_clause for facets in "OR" mode
|
||||
*
|
||||
* FWP()->or_values contains EVERY facet and their matching post IDs
|
||||
* FWP()->unfiltered_post_ids contains original post IDs
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
function get_where_clause( $facet ) {
|
||||
|
||||
// Ignore the current facet's selections
|
||||
if ( isset( FWP()->or_values ) && ( 1 < count( FWP()->or_values ) || ! isset( FWP()->or_values[ $facet['name'] ] ) ) ) {
|
||||
$post_ids = [];
|
||||
$or_values = FWP()->or_values; // Preserve original
|
||||
unset( $or_values[ $facet['name'] ] );
|
||||
|
||||
$counter = 0;
|
||||
foreach ( $or_values as $name => $vals ) {
|
||||
$post_ids = ( 0 == $counter ) ? $vals : array_intersect( $post_ids, $vals );
|
||||
$counter++;
|
||||
}
|
||||
|
||||
$post_ids = array_intersect( $post_ids, FWP()->unfiltered_post_ids );
|
||||
}
|
||||
else {
|
||||
$post_ids = FWP()->unfiltered_post_ids;
|
||||
}
|
||||
|
||||
$post_ids = empty( $post_ids ) ? [ 0 ] : $post_ids;
|
||||
return ' AND post_id IN (' . implode( ',', $post_ids ) . ')';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render some commonly used admin settings
|
||||
* @since 3.5.6
|
||||
* @deprecated 3.9
|
||||
*/
|
||||
function render_setting( $name ) {
|
||||
echo FWP()->settings->get_facet_field_html( $name );
|
||||
}
|
||||
}
|
||||
258
wp/wp-content/plugins/facetwp/includes/facets/checkboxes.php
Normal file
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Checkboxes extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Checkboxes', 'fwp' );
|
||||
$this->fields = [ 'parent_term', 'modifiers', 'hierarchical', 'show_expanded',
|
||||
'ghosts', 'operator', 'orderby', 'count', 'soft_limit' ];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the available choices
|
||||
*/
|
||||
function load_values( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$from_clause = $wpdb->prefix . 'facetwp_index f';
|
||||
$where_clause = $params['where_clause'];
|
||||
|
||||
// Orderby
|
||||
$orderby = $this->get_orderby( $facet );
|
||||
|
||||
// Limit
|
||||
$limit = $this->get_limit( $facet );
|
||||
|
||||
// Use "OR" mode when necessary
|
||||
$is_single = FWP()->helper->facet_is( $facet, 'multiple', 'no' );
|
||||
$using_or = FWP()->helper->facet_is( $facet, 'operator', 'or' );
|
||||
|
||||
// Facet in "OR" mode
|
||||
if ( $is_single || $using_or ) {
|
||||
$where_clause = $this->get_where_clause( $facet );
|
||||
}
|
||||
|
||||
$orderby = apply_filters( 'facetwp_facet_orderby', $orderby, $facet );
|
||||
$from_clause = apply_filters( 'facetwp_facet_from', $from_clause, $facet );
|
||||
$where_clause = apply_filters( 'facetwp_facet_where', $where_clause, $facet );
|
||||
|
||||
$sql = "
|
||||
SELECT f.facet_value, f.facet_display_value, f.term_id, f.parent_id, f.depth, COUNT(DISTINCT f.post_id) AS counter
|
||||
FROM $from_clause
|
||||
WHERE f.facet_name = '{$facet['name']}' $where_clause
|
||||
GROUP BY f.facet_value
|
||||
ORDER BY $orderby
|
||||
LIMIT $limit";
|
||||
|
||||
$output = $wpdb->get_results( $sql, ARRAY_A );
|
||||
|
||||
// Show "ghost" facet choices
|
||||
// For performance gains, only run if facets are in use
|
||||
$show_ghosts = FWP()->helper->facet_is( $facet, 'ghosts', 'yes' );
|
||||
|
||||
if ( $show_ghosts && FWP()->is_filtered ) {
|
||||
$raw_post_ids = implode( ',', FWP()->unfiltered_post_ids );
|
||||
|
||||
$sql = "
|
||||
SELECT f.facet_value, f.facet_display_value, f.term_id, f.parent_id, f.depth, 0 AS counter
|
||||
FROM $from_clause
|
||||
WHERE f.facet_name = '{$facet['name']}' AND post_id IN ($raw_post_ids)
|
||||
GROUP BY f.facet_value
|
||||
ORDER BY $orderby
|
||||
LIMIT $limit";
|
||||
|
||||
$ghost_output = $wpdb->get_results( $sql, ARRAY_A );
|
||||
$tmp = [];
|
||||
|
||||
$preserve_ghosts = FWP()->helper->facet_is( $facet, 'preserve_ghosts', 'yes' );
|
||||
$orderby_count = FWP()->helper->facet_is( $facet, 'orderby', 'count' );
|
||||
|
||||
// Keep the facet placement intact
|
||||
if ( $preserve_ghosts && ! $orderby_count ) {
|
||||
foreach ( $ghost_output as $row ) {
|
||||
$tmp[ $row['facet_value'] . ' ' ] = $row;
|
||||
}
|
||||
|
||||
foreach ( $output as $row ) {
|
||||
$tmp[ $row['facet_value'] . ' ' ] = $row;
|
||||
}
|
||||
|
||||
$output = $tmp;
|
||||
}
|
||||
else {
|
||||
// Make the array key equal to the facet_value (for easy lookup)
|
||||
foreach ( $output as $row ) {
|
||||
$tmp[ $row['facet_value'] . ' ' ] = $row; // Force a string array key
|
||||
}
|
||||
|
||||
$output = $tmp;
|
||||
|
||||
foreach ( $ghost_output as $row ) {
|
||||
$facet_value = $row['facet_value'];
|
||||
if ( ! isset( $output[ "$facet_value " ] ) ) {
|
||||
$output[ "$facet_value " ] = $row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$output = array_splice( $output, 0, $limit );
|
||||
$output = array_values( $output );
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
$facet = $params['facet'];
|
||||
|
||||
if ( FWP()->helper->facet_is( $facet, 'hierarchical', 'yes' ) ) {
|
||||
return $this->render_hierarchy( $params );
|
||||
}
|
||||
|
||||
$output = '';
|
||||
$values = (array) $params['values'];
|
||||
$soft_limit = empty( $facet['soft_limit'] ) ? 0 : (int) $facet['soft_limit'];
|
||||
|
||||
$key = 0;
|
||||
foreach ( $values as $key => $row ) {
|
||||
if ( 0 < $soft_limit && $key == $soft_limit ) {
|
||||
$output .= '<div class="facetwp-overflow facetwp-hidden">';
|
||||
}
|
||||
$output .= $this->render_choice( $row, $params );
|
||||
}
|
||||
|
||||
if ( 0 < $soft_limit && $soft_limit <= $key ) {
|
||||
$output .= '</div>';
|
||||
$output .= '<a class="facetwp-toggle">' . __( 'See {num} more', 'fwp-front' ) . '</a>';
|
||||
$output .= '<a class="facetwp-toggle facetwp-hidden">' . __( 'See less', 'fwp-front' ) . '</a>';
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML (hierarchical taxonomies)
|
||||
*/
|
||||
function render_hierarchy( $params ) {
|
||||
|
||||
$output = '';
|
||||
$facet = $params['facet'];
|
||||
$values = FWP()->helper->sort_taxonomy_values( $params['values'], $facet['orderby'] );
|
||||
|
||||
$init_depth = -1;
|
||||
$last_depth = -1;
|
||||
|
||||
foreach ( $values as $row ) {
|
||||
$depth = (int) $row['depth'];
|
||||
|
||||
if ( -1 == $last_depth ) {
|
||||
$init_depth = $depth;
|
||||
}
|
||||
elseif ( $depth > $last_depth ) {
|
||||
$output .= '<div class="facetwp-depth">';
|
||||
}
|
||||
elseif ( $depth < $last_depth ) {
|
||||
for ( $i = $last_depth; $i > $depth; $i-- ) {
|
||||
$output .= '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
$output .= $this->render_choice( $row, $params );
|
||||
|
||||
$last_depth = $depth;
|
||||
}
|
||||
|
||||
for ( $i = $last_depth; $i > $init_depth; $i-- ) {
|
||||
$output .= '</div>';
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render a single facet choice
|
||||
*/
|
||||
function render_choice( $row, $params ) {
|
||||
$label = esc_html( $row['facet_display_value'] );
|
||||
|
||||
$output = '';
|
||||
$selected_values = (array) $params['selected_values'];
|
||||
$selected = in_array( $row['facet_value'], $selected_values ) ? ' checked' : '';
|
||||
$selected .= ( '' != $row['counter'] && 0 == $row['counter'] && '' == $selected ) ? ' disabled' : '';
|
||||
$output .= '<div class="facetwp-checkbox' . $selected . '" data-value="' . esc_attr( $row['facet_value'] ) . '">';
|
||||
$output .= '<span class="facetwp-display-value">';
|
||||
$output .= apply_filters( 'facetwp_facet_display_value', $label, [
|
||||
'selected' => ( '' !== $selected ),
|
||||
'facet' => $params['facet'],
|
||||
'row' => $row
|
||||
]);
|
||||
$output .= '</span>';
|
||||
$output .= '<span class="facetwp-counter">(' . $row['counter'] . ')</span>';
|
||||
$output .= '</div>';
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$output = [];
|
||||
$facet = $params['facet'];
|
||||
$selected_values = $params['selected_values'];
|
||||
|
||||
$sql = $wpdb->prepare( "SELECT DISTINCT post_id
|
||||
FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE facet_name = %s",
|
||||
$facet['name']
|
||||
);
|
||||
|
||||
// Match ALL values
|
||||
if ( 'and' == $facet['operator'] ) {
|
||||
foreach ( $selected_values as $key => $value ) {
|
||||
$results = facetwp_sql( $sql . " AND facet_value IN ('$value')", $facet );
|
||||
$output = ( $key > 0 ) ? array_intersect( $output, $results ) : $results;
|
||||
|
||||
if ( empty( $output ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Match ANY value
|
||||
else {
|
||||
$selected_values = implode( "','", $selected_values );
|
||||
$output = facetwp_sql( $sql . " AND facet_value IN ('$selected_values')", $facet );
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Output any front-end scripts
|
||||
*/
|
||||
function front_scripts() {
|
||||
FWP()->display->json['expand'] = '[+]';
|
||||
FWP()->display->json['collapse'] = '[-]';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Front-end) Attach settings to the AJAX response
|
||||
*/
|
||||
function settings_js( $params ) {
|
||||
$expand = empty( $params['facet']['show_expanded'] ) ? 'no' : $params['facet']['show_expanded'];
|
||||
return [ 'show_expanded' => $expand ];
|
||||
}
|
||||
}
|
||||
292
wp/wp-content/plugins/facetwp/includes/facets/date_range.php
Normal file
@@ -0,0 +1,292 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Date_Range extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Date Range', 'fwp' );
|
||||
$this->fields = [ 'source_other', 'compare_type', 'date_fields', 'date_format' ];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = '';
|
||||
$value = $params['selected_values'];
|
||||
$value = empty( $value ) ? [ '', '' ] : $value;
|
||||
$fields = empty( $params['facet']['fields'] ) ? 'both' : $params['facet']['fields'];
|
||||
|
||||
$text_date = facetwp_i18n( __( 'Date', 'fwp-front' ) );
|
||||
$text_start_date = facetwp_i18n( __( 'Start Date', 'fwp-front' ) );
|
||||
$text_end_date = facetwp_i18n( __( 'End Date', 'fwp-front' ) );
|
||||
|
||||
if ( 'exact' == $fields ) {
|
||||
$output .= '<input type="text" class="facetwp-date facetwp-date-min" value="' . esc_attr( $value[0] ) . '" placeholder="' . esc_attr( $text_date ) . '" />';
|
||||
}
|
||||
if ( 'both' == $fields || 'start_date' == $fields ) {
|
||||
$output .= '<input type="text" class="facetwp-date facetwp-date-min" value="' . esc_attr( $value[0] ) . '" placeholder="' . esc_attr( $text_start_date ) . '" />';
|
||||
}
|
||||
if ( 'both' == $fields || 'end_date' == $fields ) {
|
||||
$output .= '<input type="text" class="facetwp-date facetwp-date-max" value="' . esc_attr( $value[1] ) . '" placeholder="' . esc_attr( $text_end_date ) . '" />';
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$values = $params['selected_values'];
|
||||
$where = '';
|
||||
|
||||
$min = empty( $values[0] ) ? false : $values[0];
|
||||
$max = empty( $values[1] ) ? false : $values[1];
|
||||
|
||||
$fields = $facet['fields'] ?? 'both';
|
||||
$compare_type = empty( $facet['compare_type'] ) ? 'basic' : $facet['compare_type'];
|
||||
$is_dual = ! empty( $facet['source_other'] );
|
||||
|
||||
if ( $is_dual && 'basic' != $compare_type ) {
|
||||
if ( 'exact' == $fields ) {
|
||||
$max = $min;
|
||||
}
|
||||
|
||||
$min = ( false !== $min ) ? $min : '0000-00-00';
|
||||
$max = ( false !== $max ) ? $max : '3000-12-31';
|
||||
|
||||
/**
|
||||
* Enclose compare
|
||||
* The post's range must surround the user-defined range
|
||||
*/
|
||||
if ( 'enclose' == $compare_type ) {
|
||||
$where .= " AND LEFT(facet_value, 10) <= '$min'";
|
||||
$where .= " AND LEFT(facet_display_value, 10) >= '$max'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersect compare
|
||||
* @link http://stackoverflow.com/a/325964
|
||||
*/
|
||||
if ( 'intersect' == $compare_type ) {
|
||||
$where .= " AND LEFT(facet_value, 10) <= '$max'";
|
||||
$where .= " AND LEFT(facet_display_value, 10) >= '$min'";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic compare
|
||||
* The user-defined range must surround the post's range
|
||||
*/
|
||||
else {
|
||||
if ( 'exact' == $fields ) {
|
||||
$max = $min;
|
||||
}
|
||||
if ( false !== $min ) {
|
||||
$where .= " AND LEFT(facet_value, 10) >= '$min'";
|
||||
}
|
||||
if ( false !== $max ) {
|
||||
$where .= " AND LEFT(facet_display_value, 10) <= '$max'";
|
||||
}
|
||||
}
|
||||
|
||||
$sql = "
|
||||
SELECT DISTINCT post_id FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE facet_name = '{$facet['name']}' $where";
|
||||
return facetwp_sql( $sql, $facet );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Output any front-end scripts
|
||||
*/
|
||||
function front_scripts() {
|
||||
FWP()->display->assets['fDate.css'] = FACETWP_URL . '/assets/vendor/fDate/fDate.css';
|
||||
FWP()->display->assets['fDate.js'] = FACETWP_URL . '/assets/vendor/fDate/fDate.min.js';
|
||||
}
|
||||
|
||||
|
||||
function register_fields() {
|
||||
return [
|
||||
'date_fields' => [
|
||||
'type' => 'alias',
|
||||
'items' => [
|
||||
'fields' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Fields to show', 'fwp' ),
|
||||
'choices' => [
|
||||
'both' => __( 'Start + End Dates', 'fwp' ),
|
||||
'exact' => __( 'Exact Date', 'fwp' ),
|
||||
'start_date' => __( 'Start Date', 'fwp' ),
|
||||
'end_date' => __( 'End Date', 'fwp' )
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
'date_format' => [
|
||||
'type' => 'alias',
|
||||
'items' => [
|
||||
'format' => [
|
||||
'label' => __( 'Display format', 'fwp' ),
|
||||
'notes' => 'See available <a href="https://facetwp.com/help-center/facets/facet-types/date-range/#tokens" target="_blank">formatting tokens</a>',
|
||||
'placeholder' => 'Y-m-d'
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Front-end) Attach settings to the AJAX response
|
||||
*/
|
||||
function settings_js( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$selected_values = $params['selected_values'];
|
||||
$fields = empty( $facet['fields'] ) ? 'both' : $facet['fields'];
|
||||
$format = empty( $facet['format'] ) ? '' : $facet['format'];
|
||||
|
||||
// Use "OR" mode by excluding the facet's own selection
|
||||
$where_clause = $this->get_where_clause( $facet );
|
||||
|
||||
$sql = "
|
||||
SELECT MIN(facet_value) AS `minDate`, MAX(facet_display_value) AS `maxDate` FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE facet_name = '{$facet['name']}' AND facet_display_value != '' $where_clause";
|
||||
$row = $wpdb->get_row( $sql );
|
||||
|
||||
$min = substr( $row->minDate, 0, 10 );
|
||||
$max = substr( $row->maxDate, 0, 10 );
|
||||
|
||||
if ( 'both' == $fields ) {
|
||||
$min_upper = ! empty( $selected_values[1] ) ? $selected_values[1] : $max;
|
||||
$max_lower = ! empty( $selected_values[0] ) ? $selected_values[0] : $min;
|
||||
|
||||
$range = [
|
||||
'min' => [
|
||||
'minDate' => $min,
|
||||
'maxDate' => $min_upper
|
||||
],
|
||||
'max' => [
|
||||
'minDate' => $max_lower,
|
||||
'maxDate' => $max
|
||||
]
|
||||
];
|
||||
}
|
||||
else {
|
||||
$range = [
|
||||
'minDate' => $min,
|
||||
'maxDate' => $max
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'locale' => $this->get_i18n_labels(),
|
||||
'format' => $format,
|
||||
'fields' => $fields,
|
||||
'range' => $range
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
function get_i18n_labels() {
|
||||
$locale = get_locale();
|
||||
$locale = empty( $locale ) ? 'en' : substr( $locale, 0, 2 );
|
||||
|
||||
$locales = [
|
||||
'ca' => [
|
||||
'weekdays_short' => ['Dg', 'Dl', 'Dt', 'Dc', 'Dj', 'Dv', 'Ds'],
|
||||
'months_short' => ['Gen', 'Febr', 'Març', 'Abr', 'Maig', 'Juny', 'Jul', 'Ag', 'Set', 'Oct', 'Nov', 'Des'],
|
||||
'months' => ['Gener', 'Febrer', 'Març', 'Abril', 'Maig', 'Juny', 'Juliol', 'Agost', 'Setembre', 'Octubre', 'Novembre', 'Desembre'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'da' => [
|
||||
'weekdays_short' => ['søn', 'man', 'tir', 'ons', 'tors', 'fre', 'lør'],
|
||||
'months_short' => ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'],
|
||||
'months' => ['januar', 'februar', 'marts', 'april', 'maj', 'juni', 'juli', 'august', 'september', 'oktober', 'november', 'december'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'de' => [
|
||||
'weekdays_short' => ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
|
||||
'months_short' => ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'],
|
||||
'months' => ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'es' => [
|
||||
'weekdays_short' => ['Dom', 'Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb'],
|
||||
'months_short' => ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'],
|
||||
'months' => ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'fr' => [
|
||||
'weekdays_short' => ['dim', 'lun', 'mar', 'mer', 'jeu', 'ven', 'sam'],
|
||||
'months_short' => ['janv', 'févr', 'mars', 'avr', 'mai', 'juin', 'juil', 'août', 'sept', 'oct', 'nov', 'déc'],
|
||||
'months' => ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'it' => [
|
||||
'weekdays_short' => ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'],
|
||||
'months_short' => ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'],
|
||||
'months' => ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'nb' => [
|
||||
'weekdays_short' => ['Søn', 'Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør'],
|
||||
'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Des'],
|
||||
'months' => ['Januar', 'Februar', 'Mars', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Desember'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'nl' => [
|
||||
'weekdays_short' => ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'],
|
||||
'months_short' => ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sept', 'okt', 'nov', 'dec'],
|
||||
'months' => ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'pl' => [
|
||||
'weekdays_short' => ['Nd', 'Pn', 'Wt', 'Śr', 'Cz', 'Pt', 'So'],
|
||||
'months_short' => ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru'],
|
||||
'months' => ['Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec', 'Lipiec', 'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'pt' => [
|
||||
'weekdays_short' => ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'],
|
||||
'months_short' => ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'],
|
||||
'months' => ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'],
|
||||
'firstDayOfWeek' => 0
|
||||
],
|
||||
'ro' => [
|
||||
'weekdays_short' => ['Dum', 'Lun', 'Mar', 'Mie', 'Joi', 'Vin', 'Sâm'],
|
||||
'months_short' => ['Ian', 'Feb', 'Mar', 'Apr', 'Mai', 'Iun', 'Iul', 'Aug', 'Sep', 'Oct', 'Noi', 'Dec'],
|
||||
'months' => ['Ianuarie', 'Februarie', 'Martie', 'Aprilie', 'Mai', 'Iunie', 'Iulie', 'August', 'Septembrie', 'Octombrie', 'Noiembrie', 'Decembrie'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'ru' => [
|
||||
'weekdays_short' => ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
|
||||
'months_short' => ['Янв', 'Фев', 'Март', 'Апр', 'Май', 'Июнь', 'Июль', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'],
|
||||
'months' => ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
|
||||
'firstDayOfWeek' => 1
|
||||
],
|
||||
'sv' => [
|
||||
'weekdays_short' => ['Sön', 'Mån', 'Tis', 'Ons', 'Tor', 'Fre', 'Lör'],
|
||||
'months_short' => ['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec'],
|
||||
'months' => ['Januari', 'Februari', 'Mars', 'April', 'Maj', 'Juni', 'Juli', 'Augusti', 'September', 'Oktober', 'November', 'December'],
|
||||
'firstDayOfWeek' => 1
|
||||
]
|
||||
];
|
||||
|
||||
if ( isset( $locales[ $locale ] ) ) {
|
||||
$locales[ $locale ]['clearText'] = __( 'Clear', 'fwp-front' );
|
||||
return $locales[ $locale ];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
73
wp/wp-content/plugins/facetwp/includes/facets/dropdown.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Dropdown extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Dropdown', 'fwp' );
|
||||
$this->fields = [ 'label_any', 'parent_term', 'modifiers', 'hierarchical', 'orderby', 'count' ];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the available choices
|
||||
*/
|
||||
function load_values( $params ) {
|
||||
return FWP()->helper->facet_types['checkboxes']->load_values( $params );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = '';
|
||||
$facet = $params['facet'];
|
||||
$values = (array) $params['values'];
|
||||
$selected_values = (array) $params['selected_values'];
|
||||
$is_hierarchical = FWP()->helper->facet_is( $facet, 'hierarchical', 'yes' );
|
||||
|
||||
if ( $is_hierarchical ) {
|
||||
$values = FWP()->helper->sort_taxonomy_values( $params['values'], $facet['orderby'] );
|
||||
}
|
||||
|
||||
$label_any = empty( $facet['label_any'] ) ? __( 'Any', 'fwp-front' ) : $facet['label_any'];
|
||||
$label_any = facetwp_i18n( $label_any );
|
||||
|
||||
$output .= '<select class="facetwp-dropdown">';
|
||||
$output .= '<option value="">' . esc_attr( $label_any ) . '</option>';
|
||||
|
||||
foreach ( $values as $row ) {
|
||||
$selected = in_array( $row['facet_value'], $selected_values ) ? ' selected' : '';
|
||||
$indent = $is_hierarchical ? str_repeat( ' ', (int) $row['depth'] ) : '';
|
||||
|
||||
// Determine whether to show counts
|
||||
$label = esc_attr( $row['facet_display_value'] );
|
||||
$label = apply_filters( 'facetwp_facet_display_value', $label, [
|
||||
'selected' => ( '' !== $selected ),
|
||||
'facet' => $facet,
|
||||
'row' => $row
|
||||
]);
|
||||
|
||||
$show_counts = apply_filters( 'facetwp_facet_dropdown_show_counts', true, [ 'facet' => $facet ] );
|
||||
|
||||
if ( $show_counts ) {
|
||||
$label .= ' (' . $row['counter'] . ')';
|
||||
}
|
||||
|
||||
$output .= '<option value="' . esc_attr( $row['facet_value'] ) . '"' . $selected . '>' . $indent . $label . '</option>';
|
||||
}
|
||||
|
||||
$output .= '</select>';
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
return FWP()->helper->facet_types['checkboxes']->filter_posts( $params );
|
||||
}
|
||||
}
|
||||
100
wp/wp-content/plugins/facetwp/includes/facets/fselect.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_fSelect extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'fSelect', 'fwp' );
|
||||
$this->fields = [ 'label_any', 'parent_term', 'modifiers', 'hierarchical', 'multiple',
|
||||
'ghosts', 'operator', 'orderby', 'count' ];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the available choices
|
||||
*/
|
||||
function load_values( $params ) {
|
||||
return FWP()->helper->facet_types['checkboxes']->load_values( $params );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = '';
|
||||
$facet = $params['facet'];
|
||||
$values = (array) $params['values'];
|
||||
$selected_values = (array) $params['selected_values'];
|
||||
$is_hierarchical = FWP()->helper->facet_is( $facet, 'hierarchical', 'yes' );
|
||||
|
||||
if ( $is_hierarchical ) {
|
||||
$values = FWP()->helper->sort_taxonomy_values( $params['values'], $facet['orderby'] );
|
||||
}
|
||||
|
||||
$multiple = FWP()->helper->facet_is( $facet, 'multiple', 'yes' ) ? ' multiple="multiple"' : '';
|
||||
$label_any = empty( $facet['label_any'] ) ? __( 'Any', 'fwp-front' ) : $facet['label_any'];
|
||||
$label_any = facetwp_i18n( $label_any );
|
||||
|
||||
$output .= '<select class="facetwp-dropdown"' . $multiple . '>';
|
||||
$output .= '<option value="">' . esc_html( $label_any ) . '</option>';
|
||||
|
||||
foreach ( $values as $row ) {
|
||||
$selected = in_array( $row['facet_value'], $selected_values, true ) ? ' selected' : '';
|
||||
$disabled = ( 0 == $row['counter'] && '' == $selected ) ? ' disabled' : '';
|
||||
|
||||
$label = esc_html( $row['facet_display_value'] );
|
||||
$label = apply_filters( 'facetwp_facet_display_value', $label, [
|
||||
'selected' => ( '' !== $selected ),
|
||||
'facet' => $facet,
|
||||
'row' => $row
|
||||
]);
|
||||
|
||||
$show_counts = apply_filters( 'facetwp_facet_dropdown_show_counts', true, [ 'facet' => $facet ] );
|
||||
$counter = $show_counts ? $row['counter'] : '';
|
||||
$depth = $is_hierarchical ? $row['depth'] : 0;
|
||||
|
||||
$output .= '<option value="' . esc_attr( $row['facet_value'] ) . '" data-counter="' . $counter . '" class="d' . $depth . '"' . $selected . $disabled . '>' . $label . '</option>';
|
||||
}
|
||||
|
||||
$output .= '</select>';
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
return FWP()->helper->facet_types['checkboxes']->filter_posts( $params );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Front-end) Attach settings to the AJAX response
|
||||
*/
|
||||
function settings_js( $params ) {
|
||||
$facet = $params['facet'];
|
||||
|
||||
$label_any = empty( $facet['label_any'] ) ? __( 'Any', 'fwp-front' ) : $facet['label_any'];
|
||||
$label_any = facetwp_i18n( $label_any );
|
||||
|
||||
return [
|
||||
'placeholder' => $label_any,
|
||||
'overflowText' => __( '{n} selected', 'fwp-front' ),
|
||||
'searchText' => __( 'Search', 'fwp-front' ),
|
||||
'noResultsText' => __( 'No results found', 'fwp-front' ),
|
||||
'operator' => $facet['operator']
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Output any front-end scripts
|
||||
*/
|
||||
function front_scripts() {
|
||||
FWP()->display->assets['fSelect.css'] = FACETWP_URL . '/assets/vendor/fSelect/fSelect.css';
|
||||
FWP()->display->assets['fSelect.js'] = FACETWP_URL . '/assets/vendor/fSelect/fSelect.js';
|
||||
}
|
||||
}
|
||||
179
wp/wp-content/plugins/facetwp/includes/facets/hierarchy.php
Normal file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Hierarchy extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Hierarchy', 'fwp' );
|
||||
$this->fields = [ 'label_any', 'modifiers', 'orderby', 'count' ];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the available choices
|
||||
*/
|
||||
function load_values( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$from_clause = $wpdb->prefix . 'facetwp_index f';
|
||||
$where_clause = $params['where_clause'];
|
||||
|
||||
$selected_values = (array) $params['selected_values'];
|
||||
$facet_parent_id = 0;
|
||||
$output = [];
|
||||
|
||||
$label_any = empty( $facet['label_any'] ) ? __( 'Any', 'fwp-front' ) : $facet['label_any'];
|
||||
$label_any = facetwp_i18n( $label_any );
|
||||
|
||||
// Orderby
|
||||
$orderby = $this->get_orderby( $facet );
|
||||
|
||||
// Determine the parent_id and depth
|
||||
if ( ! empty( $selected_values[0] ) ) {
|
||||
|
||||
// Get term ID from slug
|
||||
$sql = "
|
||||
SELECT t.term_id
|
||||
FROM {$wpdb->terms} t
|
||||
INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_id = t.term_id AND tt.taxonomy = %s
|
||||
WHERE t.slug = %s
|
||||
LIMIT 1";
|
||||
|
||||
$value = $selected_values[0];
|
||||
$taxonomy = str_replace( 'tax/', '', $facet['source'] );
|
||||
$facet_parent_id = (int) $wpdb->get_var( $wpdb->prepare( $sql, $taxonomy, $value ) );
|
||||
|
||||
// Invalid term
|
||||
if ( $facet_parent_id < 1 ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Create term lookup array
|
||||
$depths = FWP()->helper->get_term_depths( $taxonomy );
|
||||
$max_depth = (int) $depths[ $facet_parent_id ]['depth'];
|
||||
$last_parent_id = $facet_parent_id;
|
||||
|
||||
// Loop backwards
|
||||
for ( $i = 0; $i <= $max_depth; $i++ ) {
|
||||
$output[] = [
|
||||
'facet_value' => $depths[ $last_parent_id ]['slug'],
|
||||
'facet_display_value' => $depths[ $last_parent_id ]['name'],
|
||||
'depth' => $depths[ $last_parent_id ]['depth'] + 1,
|
||||
'counter' => 1, // FWP.settings.num_choices
|
||||
];
|
||||
|
||||
$last_parent_id = (int) $depths[ $last_parent_id ]['parent_id'];
|
||||
}
|
||||
|
||||
$output[] = [
|
||||
'facet_value' => '',
|
||||
'facet_display_value' => $label_any,
|
||||
'depth' => 0,
|
||||
'counter' => 1,
|
||||
];
|
||||
|
||||
// Reverse it
|
||||
$output = array_reverse( $output );
|
||||
}
|
||||
|
||||
// Update the WHERE clause
|
||||
$where_clause .= " AND parent_id = '$facet_parent_id'";
|
||||
|
||||
$orderby = apply_filters( 'facetwp_facet_orderby', $orderby, $facet );
|
||||
$from_clause = apply_filters( 'facetwp_facet_from', $from_clause, $facet );
|
||||
$where_clause = apply_filters( 'facetwp_facet_where', $where_clause, $facet );
|
||||
|
||||
$sql = "
|
||||
SELECT f.facet_value, f.facet_display_value, COUNT(DISTINCT f.post_id) AS counter
|
||||
FROM $from_clause
|
||||
WHERE f.facet_name = '{$facet['name']}' $where_clause
|
||||
GROUP BY f.facet_value
|
||||
ORDER BY $orderby";
|
||||
|
||||
$results = $wpdb->get_results( $sql, ARRAY_A );
|
||||
$new_depth = empty( $output ) ? 0 : $output[ count( $output ) - 1 ]['depth'] + 1;
|
||||
|
||||
foreach ( $results as $result ) {
|
||||
$result['depth'] = $new_depth;
|
||||
$result['is_choice'] = true;
|
||||
$output[] = $result;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
$facet = $params['facet'];
|
||||
$values = (array) $params['values'];
|
||||
$selected_values = (array) $params['selected_values'];
|
||||
|
||||
$output = '';
|
||||
$num_visible = $this->get_limit( $facet );
|
||||
$num = 0;
|
||||
|
||||
if ( ! empty( $values ) ) {
|
||||
foreach ( $values as $row ) {
|
||||
$last_depth = $last_depth ?? $row['depth'];
|
||||
$selected = ( ! empty( $selected_values ) && $row['facet_value'] == $selected_values[0] );
|
||||
|
||||
$label = esc_html( $row['facet_display_value'] );
|
||||
$label = apply_filters( 'facetwp_facet_display_value', $label, [
|
||||
'selected' => $selected,
|
||||
'facet' => $facet,
|
||||
'row' => $row
|
||||
]);
|
||||
|
||||
if ( $row['depth'] > $last_depth ) {
|
||||
$output .= '<div class="facetwp-depth">';
|
||||
}
|
||||
|
||||
if ( $num == $num_visible ) {
|
||||
$output .= '<div class="facetwp-overflow facetwp-hidden">';
|
||||
}
|
||||
|
||||
if ( ! $selected ) {
|
||||
if ( isset( $row['is_choice'] ) ) {
|
||||
$label .= ' <span class="facetwp-counter">(' . $row['counter'] . ')</span>';
|
||||
}
|
||||
else {
|
||||
$arrow = apply_filters( 'facetwp_facet_hierarchy_arrow', '‹ ' );
|
||||
$label = $arrow . $label;
|
||||
}
|
||||
}
|
||||
|
||||
$output .= '<div class="facetwp-link' . ( $selected ? ' checked' : '' ) . '" data-value="' . esc_attr( $row['facet_value'] ) . '">' . $label . '</div>';
|
||||
|
||||
if ( isset( $row['is_choice'] ) ) {
|
||||
$num++;
|
||||
}
|
||||
|
||||
$last_depth = $row['depth'];
|
||||
}
|
||||
|
||||
if ( $num_visible < $num ) {
|
||||
$output .= '</div>';
|
||||
$output .= '<a class="facetwp-toggle">' . __( 'See more', 'fwp-front' ) . '</a>';
|
||||
$output .= '<a class="facetwp-toggle facetwp-hidden">' . __( 'See less', 'fwp-front' ) . '</a>';
|
||||
}
|
||||
|
||||
for ( $i = 0; $i <= $last_depth; $i++ ) {
|
||||
$output .= '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
return FWP()->helper->facet_types['checkboxes']->filter_posts( $params );
|
||||
}
|
||||
}
|
||||
137
wp/wp-content/plugins/facetwp/includes/facets/number_range.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Number_Range extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Number Range', 'fwp' );
|
||||
$this->fields = [ 'source_other', 'compare_type', 'number_fields' ];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = '';
|
||||
$value = $params['selected_values'];
|
||||
$value = empty( $value ) ? [ '', '', ] : $value;
|
||||
$fields = empty( $params['facet']['fields'] ) ? 'both' : $params['facet']['fields'];
|
||||
|
||||
if ( 'exact' == $fields ) {
|
||||
$output .= '<input type="text" class="facetwp-number facetwp-number-min" value="' . esc_attr( $value[0] ) . '" placeholder="' . __( 'Number', 'fwp-front' ) . '" />';
|
||||
}
|
||||
if ( 'both' == $fields || 'min' == $fields ) {
|
||||
$output .= '<input type="text" class="facetwp-number facetwp-number-min" value="' . esc_attr( $value[0] ) . '" placeholder="' . __( 'Min', 'fwp-front' ) . '" />';
|
||||
}
|
||||
if ( 'both' == $fields || 'max' == $fields ) {
|
||||
$output .= '<input type="text" class="facetwp-number facetwp-number-max" value="' . esc_attr( $value[1] ) . '" placeholder="' . __( 'Max', 'fwp-front' ) . '" />';
|
||||
}
|
||||
|
||||
$output .= '<input type="button" class="facetwp-submit" value="' . __( 'Go', 'fwp-front' ) . '" />';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$values = $params['selected_values'];
|
||||
$where = '';
|
||||
|
||||
$min = ( '' == $values[0] ) ? false : $values[0];
|
||||
$max = ( '' == $values[1] ) ? false : $values[1];
|
||||
|
||||
$fields = $facet['fields'] ?? 'both';
|
||||
$compare_type = empty( $facet['compare_type'] ) ? 'basic' : $facet['compare_type'];
|
||||
$is_dual = ! empty( $facet['source_other'] );
|
||||
|
||||
if ( $is_dual && 'basic' != $compare_type ) {
|
||||
if ( 'exact' == $fields ) {
|
||||
$max = $min;
|
||||
}
|
||||
|
||||
$min = ( false !== $min ) ? $min : -999999999999;
|
||||
$max = ( false !== $max ) ? $max : 999999999999;
|
||||
|
||||
/**
|
||||
* Enclose compare
|
||||
* The post's range must surround the user-defined range
|
||||
*/
|
||||
if ( 'enclose' == $compare_type ) {
|
||||
$where .= " AND (facet_value + 0) <= '$min'";
|
||||
$where .= " AND (facet_display_value + 0) >= '$max'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersect compare
|
||||
* @link http://stackoverflow.com/a/325964
|
||||
*/
|
||||
if ( 'intersect' == $compare_type ) {
|
||||
$where .= " AND (facet_value + 0) <= '$max'";
|
||||
$where .= " AND (facet_display_value + 0) >= '$min'";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic compare
|
||||
* The user-defined range must surround the post's range
|
||||
*/
|
||||
else {
|
||||
if ( 'exact' == $fields ) {
|
||||
$max = $min;
|
||||
}
|
||||
if ( false !== $min ) {
|
||||
$where .= " AND (facet_value + 0) >= '$min'";
|
||||
}
|
||||
if ( false !== $max ) {
|
||||
$where .= " AND (facet_display_value + 0) <= '$max'";
|
||||
}
|
||||
}
|
||||
|
||||
$sql = "
|
||||
SELECT DISTINCT post_id FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE facet_name = '{$facet['name']}' $where";
|
||||
return facetwp_sql( $sql, $facet );
|
||||
}
|
||||
|
||||
|
||||
function register_fields() {
|
||||
return [
|
||||
'number_fields' => [
|
||||
'type' => 'alias',
|
||||
'items' => [
|
||||
'fields' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Fields to show', 'fwp' ),
|
||||
'choices' => [
|
||||
'both' => __( 'Min + Max', 'fwp' ),
|
||||
'exact' => __( 'Exact', 'fwp' ),
|
||||
'min' => __( 'Min', 'fwp' ),
|
||||
'max' => __( 'Max', 'fwp' )
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Front-end) Attach settings to the AJAX response
|
||||
*/
|
||||
function settings_js( $params ) {
|
||||
$facet = $params['facet'];
|
||||
$fields = empty( $facet['fields'] ) ? 'both' : $facet['fields'];
|
||||
|
||||
return [
|
||||
'fields' => $fields
|
||||
];
|
||||
}
|
||||
}
|
||||
277
wp/wp-content/plugins/facetwp/includes/facets/pager.php
Normal file
@@ -0,0 +1,277 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Pager extends FacetWP_Facet
|
||||
{
|
||||
|
||||
public $pager_args;
|
||||
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Pager', 'fwp' );
|
||||
$this->fields = [ 'pager_type', 'inner_size', 'dots_label', 'prev_label', 'next_label',
|
||||
'count_text_plural', 'count_text_singular', 'count_text_none', 'load_more_text',
|
||||
'loading_text', 'default_label', 'per_page_options' ];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
$facet = $params['facet'];
|
||||
$pager_type = $facet['pager_type'];
|
||||
$this->pager_args = FWP()->facet->pager_args;
|
||||
|
||||
$method = 'render_' . $pager_type;
|
||||
if ( method_exists( $this, $method ) ) {
|
||||
return $this->$method( $facet );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function render_numbers( $facet ) {
|
||||
$inner_size = (int) $facet['inner_size'];
|
||||
$dots_label = facetwp_i18n( $facet['dots_label'] );
|
||||
$prev_label = facetwp_i18n( $facet['prev_label'] );
|
||||
$next_label = facetwp_i18n( $facet['next_label'] );
|
||||
|
||||
$output = '';
|
||||
$page = (int) $this->pager_args['page'];
|
||||
$total_pages = (int) $this->pager_args['total_pages'];
|
||||
$inner_first = max( $page - $inner_size, 2 );
|
||||
$inner_last = min( $page + $inner_size, $total_pages - 1 );
|
||||
|
||||
if ( 1 < $total_pages ) {
|
||||
|
||||
// Prev button
|
||||
if ( 1 < $page && '' != $prev_label ) {
|
||||
$output .= $this->render_page( $page - 1, $prev_label, 'prev' );
|
||||
}
|
||||
|
||||
// First page
|
||||
$output .= $this->render_page( 1, false, 'first' );
|
||||
|
||||
// Dots
|
||||
if ( 2 < $inner_first && '' != $dots_label ) {
|
||||
$output .= $this->render_page( '', $dots_label, 'dots' );
|
||||
}
|
||||
|
||||
for ( $i = $inner_first; $i <= $inner_last; $i++ ) {
|
||||
$output .= $this->render_page( $i );
|
||||
}
|
||||
|
||||
// Dots
|
||||
if ( $inner_last < $total_pages - 1 && '' != $dots_label ) {
|
||||
$output .= $this->render_page( '', $dots_label, 'dots' );
|
||||
}
|
||||
|
||||
// Last page
|
||||
$output .= $this->render_page( $total_pages, false, 'last' );
|
||||
|
||||
// Next button
|
||||
if ( $page < $total_pages && '' != $next_label ) {
|
||||
$output .= $this->render_page( $page + 1, $next_label, 'next' );
|
||||
}
|
||||
}
|
||||
|
||||
return '<div class="facetwp-pager">' . $output . '</div>';
|
||||
}
|
||||
|
||||
|
||||
function render_page( $page, $label = false, $extra_class = false ) {
|
||||
$label = ( false === $label ) ? $page : $label;
|
||||
$class = 'facetwp-page';
|
||||
|
||||
if ( ! empty( $extra_class ) ) {
|
||||
$class .= ' ' . $extra_class;
|
||||
}
|
||||
|
||||
if ( $page == $this->pager_args['page'] ) {
|
||||
$class .= ' active';
|
||||
}
|
||||
|
||||
$data = empty( $page ) ? '' : ' data-page="' . $page . '"';
|
||||
$html = '<a class="' . $class . '"' . $data . '>' . $label . '</a>';
|
||||
|
||||
return apply_filters( 'facetwp_facet_pager_link', $html, [
|
||||
'page' => $page,
|
||||
'label' => $label,
|
||||
'extra_class' => $extra_class
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
function render_counts( $facet ) {
|
||||
$text_singular = facetwp_i18n( $facet['count_text_singular'] );
|
||||
$text_plural = facetwp_i18n( $facet['count_text_plural'] );
|
||||
$text_none = facetwp_i18n( $facet['count_text_none'] );
|
||||
|
||||
$page = $this->pager_args['page'];
|
||||
$per_page = $this->pager_args['per_page'];
|
||||
$total_rows = $this->pager_args['total_rows'];
|
||||
$total_pages = $this->pager_args['total_pages'];
|
||||
|
||||
if ( -1 == $per_page ) {
|
||||
$per_page = $total_rows;
|
||||
}
|
||||
|
||||
if ( 1 < $total_rows ) {
|
||||
$lower = ( 1 + ( ( $page - 1 ) * $per_page ) );
|
||||
$upper = ( $page * $per_page );
|
||||
$upper = ( $total_rows < $upper ) ? $total_rows : $upper;
|
||||
|
||||
// If a load_more pager is in use, force $lower = 1
|
||||
if ( FWP()->helper->facet_setting_exists( 'pager_type', 'load_more' ) ) {
|
||||
$lower = 1;
|
||||
}
|
||||
|
||||
$output = $text_plural;
|
||||
$output = str_replace( '[lower]', $lower, $output );
|
||||
$output = str_replace( '[upper]', $upper, $output );
|
||||
$output = str_replace( '[total]', $total_rows, $output );
|
||||
$output = str_replace( '[page]', $page, $output );
|
||||
$output = str_replace( '[per_page]', $per_page, $output );
|
||||
$output = str_replace( '[total_pages]', $total_pages, $output );
|
||||
}
|
||||
else {
|
||||
$output = ( 0 < $total_rows ) ? $text_singular : $text_none;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
function render_load_more( $facet ) {
|
||||
$text = facetwp_i18n( $facet['load_more_text'] );
|
||||
$loading_text = facetwp_i18n( $facet['loading_text'] );
|
||||
|
||||
return '<button class="facetwp-load-more" data-loading="' . esc_attr( $loading_text ) . '">' . esc_attr( $text ) . '</button>';
|
||||
}
|
||||
|
||||
|
||||
function render_per_page( $facet ) {
|
||||
$default = facetwp_i18n( $facet['default_label'] );
|
||||
$options = explode( ',', $facet['per_page_options'] );
|
||||
$options = array_map( 'trim', $options );
|
||||
$output = '';
|
||||
|
||||
if ( ! empty( $default ) ) {
|
||||
$output .= '<option value="">' . esc_attr( $default ) . '</option>';
|
||||
}
|
||||
|
||||
$per_page = $this->pager_args['per_page'];
|
||||
$var_exists = isset( FWP()->request->url_vars['per_page'] );
|
||||
|
||||
foreach ( $options as $option ) {
|
||||
$val = $label = $option;
|
||||
|
||||
// Support "All" option
|
||||
if ( ! ctype_digit( $val ) ) {
|
||||
$val = -1;
|
||||
$label = facetwp_i18n( $label );
|
||||
}
|
||||
|
||||
$selected = ( $var_exists && $val == $per_page ) ? ' selected' : '';
|
||||
$output .= '<option value="' . $val . '"' . $selected . '>' . esc_attr( $label ) . '</option>';
|
||||
}
|
||||
|
||||
return '<select class="facetwp-per-page-select">' . $output . '</select>';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
return 'continue';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Front-end) Attach settings to the AJAX response
|
||||
*/
|
||||
function settings_js( $params ) {
|
||||
$facet = $params['facet'];
|
||||
|
||||
return [
|
||||
'pager_type' => $facet['pager_type']
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
function register_fields() {
|
||||
return [
|
||||
'pager_type' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Pager type', 'fwp' ),
|
||||
'choices' => [
|
||||
'numbers' => __( 'Page numbers', 'fwp' ),
|
||||
'counts' => __( 'Result counts', 'fwp' ),
|
||||
'load_more' => __( 'Load more', 'fwp' ),
|
||||
'per_page' => __( 'Per page', 'fwp' )
|
||||
]
|
||||
],
|
||||
'inner_size' => [
|
||||
'label' => __( 'Inner size', 'fwp' ),
|
||||
'notes' => 'Number of pages to show on each side of the current page',
|
||||
'default' => 2,
|
||||
'show' => "facet.pager_type == 'numbers'"
|
||||
],
|
||||
'dots_label' => [
|
||||
'label' => __( 'Dots label', 'fwp' ),
|
||||
'notes' => 'The filler between the inner and outer pages',
|
||||
'default' => '…',
|
||||
'show' => "facet.pager_type == 'numbers'"
|
||||
],
|
||||
'prev_label' => [
|
||||
'label' => __( 'Prev button label', 'fwp' ),
|
||||
'notes' => 'Leave blank to hide',
|
||||
'default' => '« Prev',
|
||||
'show' => "facet.pager_type == 'numbers'"
|
||||
],
|
||||
'next_label' => [
|
||||
'label' => __( 'Next button label', 'fwp' ),
|
||||
'notes' => 'Leave blank to hide',
|
||||
'default' => 'Next »',
|
||||
'show' => "facet.pager_type == 'numbers'"
|
||||
],
|
||||
'count_text_plural' => [
|
||||
'label' => __( 'Count text (plural)', 'fwp' ),
|
||||
'notes' => 'Available tags: [lower], [upper], [total], [page], [per_page], [total_pages]',
|
||||
'default' => '[lower] - [upper] of [total] results',
|
||||
'show' => "facet.pager_type == 'counts'"
|
||||
],
|
||||
'count_text_singular' => [
|
||||
'label' => __( 'Count text (singular)', 'fwp' ),
|
||||
'default' => '1 result',
|
||||
'show' => "facet.pager_type == 'counts'"
|
||||
],
|
||||
'count_text_none' => [
|
||||
'label' => __( 'Count text (no results)', 'fwp' ),
|
||||
'default' => 'No results',
|
||||
'show' => "facet.pager_type == 'counts'"
|
||||
],
|
||||
'load_more_text' => [
|
||||
'label' => __( 'Load more text', 'fwp' ),
|
||||
'default' => 'Load more',
|
||||
'show' => "facet.pager_type == 'load_more'"
|
||||
],
|
||||
'loading_text' => [
|
||||
'label' => __( 'Loading text', 'fwp' ),
|
||||
'default' => 'Loading...',
|
||||
'show' => "facet.pager_type == 'load_more'"
|
||||
],
|
||||
'default_label' => [
|
||||
'label' => __( 'Default label', 'fwp' ),
|
||||
'default' => 'Per page',
|
||||
'show' => "facet.pager_type == 'per_page'"
|
||||
],
|
||||
'per_page_options' => [
|
||||
'label' => __( 'Per page options', 'fwp' ),
|
||||
'notes' => 'A comma-separated list of choices. Optionally add a non-numeric choice to be used as a "Show all" option.',
|
||||
'default' => '10, 25, 50, 100',
|
||||
'show' => "facet.pager_type == 'per_page'"
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
354
wp/wp-content/plugins/facetwp/includes/facets/proximity.php
Normal file
@@ -0,0 +1,354 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Proximity_Core extends FacetWP_Facet
|
||||
{
|
||||
|
||||
/* (array) Associative array containing post_id => distance */
|
||||
public $distance = [];
|
||||
|
||||
/* (array) Associative array containing post_id => [lat, lng] */
|
||||
public $post_latlng = [];
|
||||
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Proximity', 'fwp' );
|
||||
$this->fields = [ 'longitude', 'unit', 'radius_ui', 'radius_options', 'radius_min', 'radius_max', 'radius_default' ];
|
||||
|
||||
add_filter( 'facetwp_index_row', [ $this, 'index_latlng' ], 1, 2 );
|
||||
add_filter( 'facetwp_sort_options', [ $this, 'sort_options' ], 1, 2 );
|
||||
add_filter( 'facetwp_filtered_post_ids', [ $this, 'sort_by_distance' ], 10, 2 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = '';
|
||||
$facet = $params['facet'];
|
||||
$value = $params['selected_values'];
|
||||
$unit = empty( $facet['unit'] ) ? 'mi' : $facet['unit'];
|
||||
|
||||
$lat = empty( $value[0] ) ? '' : $value[0];
|
||||
$lng = empty( $value[1] ) ? '' : $value[1];
|
||||
$chosen_radius = empty( $value[2] ) ? '' : (float) $value[2];
|
||||
$location_name = empty( $value[3] ) ? '' : urldecode( $value[3] );
|
||||
|
||||
$radius_options = [ 10, 25, 50, 100, 250 ];
|
||||
|
||||
// Grab the radius UI
|
||||
$radius_ui = empty( $facet['radius_ui'] ) ? 'dropdown' : $facet['radius_ui'];
|
||||
|
||||
// Grab radius options from the UI
|
||||
if ( ! empty( $facet['radius_options'] ) ) {
|
||||
$radius_options = explode( ',', preg_replace( '/\s+/', '', $facet['radius_options'] ) );
|
||||
}
|
||||
|
||||
// Grab default radius from the UI
|
||||
if ( empty( $chosen_radius ) && ! empty( $facet['radius_default'] ) ) {
|
||||
$chosen_radius = (float) $facet['radius_default'];
|
||||
}
|
||||
|
||||
// Support dynamic radius
|
||||
if ( ! empty( $chosen_radius ) && 0 < $chosen_radius ) {
|
||||
if ( ! in_array( $chosen_radius, $radius_options ) ) {
|
||||
$radius_options[] = $chosen_radius;
|
||||
}
|
||||
}
|
||||
|
||||
$radius_options = apply_filters( 'facetwp_proximity_radius_options', $radius_options );
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
|
||||
<span class="facetwp-input-wrap">
|
||||
<i class="facetwp-icon locate-me"></i>
|
||||
<input type="text" class="facetwp-location" value="<?php echo esc_attr( $location_name ); ?>" placeholder="<?php _e( 'Enter location', 'fwp-front' ); ?>" autocomplete="off" />
|
||||
<div class="location-results facetwp-hidden"></div>
|
||||
</span>
|
||||
|
||||
<?php if ( 'dropdown' == $radius_ui ) : ?>
|
||||
|
||||
<select class="facetwp-radius facetwp-radius-dropdown">
|
||||
<?php foreach ( $radius_options as $radius ) : ?>
|
||||
<?php $selected = ( $chosen_radius == $radius ) ? ' selected' : ''; ?>
|
||||
<option value="<?php echo $radius; ?>"<?php echo $selected; ?>><?php echo "$radius $unit"; ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
||||
<?php elseif ( 'slider' == $radius_ui ) : ?>
|
||||
|
||||
<div class="facetwp-radius-wrap">
|
||||
<input class="facetwp-radius facetwp-radius-slider" type="range"
|
||||
min="<?php echo $facet['radius_min']; ?>"
|
||||
max="<?php echo $facet['radius_max']; ?>"
|
||||
value="<?php echo $chosen_radius; ?>"
|
||||
/>
|
||||
<div class="facetwp-radius-label">
|
||||
<span class="facetwp-radius-dist"><?php echo $chosen_radius; ?></span>
|
||||
<span class="facetwp-radius-unit"><?php echo $facet['unit']; ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php elseif ( 'none' == $radius_ui ) : ?>
|
||||
|
||||
<input class="facetwp-radius facetwp-hidden" value="<?php echo $chosen_radius; ?>" />
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
<input type="hidden" class="facetwp-lat" value="<?php echo esc_attr( $lat ); ?>" />
|
||||
<input type="hidden" class="facetwp-lng" value="<?php echo esc_attr( $lng ); ?>" />
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$selected_values = $params['selected_values'];
|
||||
$unit = empty( $facet['unit'] ) ? 'mi' : $facet['unit'];
|
||||
$earth_radius = ( 'mi' == $unit ) ? 3959 : 6371;
|
||||
|
||||
if ( empty( $selected_values ) || empty( $selected_values[0] ) ) {
|
||||
return 'continue';
|
||||
}
|
||||
|
||||
$lat1 = (float) $selected_values[0];
|
||||
$lng1 = (float) $selected_values[1];
|
||||
$radius = (float) $selected_values[2];
|
||||
$rad = M_PI / 180;
|
||||
|
||||
$sql = "
|
||||
SELECT DISTINCT post_id, facet_value AS `lat`, facet_display_value AS `lng`
|
||||
FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE facet_name = '{$facet['name']}'";
|
||||
|
||||
$results = $wpdb->get_results( $sql );
|
||||
|
||||
foreach ( $results as $row ) {
|
||||
$lat2 = (float) $row->lat;
|
||||
$lng2 = (float) $row->lng;
|
||||
|
||||
if ( ( $lat1 == $lat2 ) && ( $lng1 == $lng2 ) ) {
|
||||
$dist = 0;
|
||||
}
|
||||
else {
|
||||
$calc = sin( $lat1 * $rad ) * sin( $lat2 * $rad ) +
|
||||
cos( $lat1 * $rad ) * cos( $lat2 * $rad ) *
|
||||
cos( $lng2 * $rad - $lng1 * $rad );
|
||||
|
||||
// acos() must be between -1 and 1
|
||||
$dist = acos( max( -1, min( 1, $calc ) ) ) * $earth_radius;
|
||||
}
|
||||
|
||||
if ( $dist <= $radius ) {
|
||||
$existing = $this->distance[ $row->post_id ] ?? -1;
|
||||
|
||||
if ( -1 == $existing || $dist < $existing ) {
|
||||
$this->distance[ $row->post_id ] = $dist;
|
||||
|
||||
if ( apply_filters( 'facetwp_proximity_store_latlng', false ) ) {
|
||||
$this->post_latlng[ $row->post_id ] = [ $lat2, $lng2 ];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
asort( $this->distance, SORT_NUMERIC );
|
||||
|
||||
return array_keys( $this->distance );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Output front-end scripts
|
||||
*/
|
||||
function front_scripts() {
|
||||
if ( apply_filters( 'facetwp_proximity_load_js', true ) ) {
|
||||
|
||||
// hard-coded
|
||||
$api_key = defined( 'GMAPS_API_KEY' ) ? GMAPS_API_KEY : '';
|
||||
|
||||
// admin ui
|
||||
$tmp_key = FWP()->helper->get_setting( 'gmaps_api_key' );
|
||||
$api_key = empty( $tmp_key ) ? $api_key : $tmp_key;
|
||||
|
||||
// hook
|
||||
$api_key = apply_filters( 'facetwp_gmaps_api_key', $api_key );
|
||||
|
||||
FWP()->display->assets['gmaps'] = '//maps.googleapis.com/maps/api/js?libraries=places&key=' . trim( $api_key ) . '&callback=Function.prototype';
|
||||
}
|
||||
|
||||
// Pass extra options into Places Autocomplete
|
||||
$options = apply_filters( 'facetwp_proximity_autocomplete_options', [] );
|
||||
FWP()->display->json['proximity']['autocomplete_options'] = $options;
|
||||
FWP()->display->json['proximity']['clearText'] = __( 'Clear location', 'fwp-front' );
|
||||
FWP()->display->json['proximity']['queryDelay'] = 250;
|
||||
FWP()->display->json['proximity']['minLength'] = 3;
|
||||
}
|
||||
|
||||
|
||||
function register_fields() {
|
||||
return [
|
||||
'longitude' => [
|
||||
'type' => 'alias',
|
||||
'items' => [
|
||||
'source_other' => [
|
||||
'label' => __( 'Longitude', 'fwp' ),
|
||||
'notes' => '(Optional) use a separate longitude field',
|
||||
'html' => '<data-sources :facet="facet" setting-name="source_other"></data-sources>'
|
||||
]
|
||||
]
|
||||
],
|
||||
'unit' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Unit of measurement', 'fwp' ),
|
||||
'choices' => [
|
||||
'mi' => __( 'Miles', 'fwp' ),
|
||||
'km' => __( 'Kilometers', 'fwp' )
|
||||
]
|
||||
],
|
||||
'radius_ui' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Radius UI', 'fwp' ),
|
||||
'choices' => [
|
||||
'dropdown' => __( 'Dropdown', 'fwp' ),
|
||||
'slider' => __( 'Slider', 'fwp' ),
|
||||
'none' => __( 'None', 'fwp' )
|
||||
]
|
||||
],
|
||||
'radius_options' => [
|
||||
'label' => __( 'Radius options', 'fwp' ),
|
||||
'notes' => 'A comma-separated list of radius choices',
|
||||
'default' => '10, 25, 50, 100, 250',
|
||||
'show' => "facet.radius_ui == 'dropdown'"
|
||||
],
|
||||
'radius_min' => [
|
||||
'label' => __( 'Range (min)', 'fwp' ),
|
||||
'default' => 1,
|
||||
'show' => "facet.radius_ui == 'slider'"
|
||||
],
|
||||
'radius_max' => [
|
||||
'label' => __( 'Range (max)', 'fwp' ),
|
||||
'default' => 50,
|
||||
'show' => "facet.radius_ui == 'slider'"
|
||||
],
|
||||
'radius_default' => [
|
||||
'label' => __( 'Default radius', 'fwp' ),
|
||||
'default' => 25
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Index the coordinates
|
||||
* We expect a comma-separated "latitude, longitude"
|
||||
*/
|
||||
function index_latlng( $params, $class ) {
|
||||
|
||||
$facet = FWP()->helper->get_facet_by_name( $params['facet_name'] );
|
||||
|
||||
if ( false !== $facet && 'proximity' == $facet['type'] ) {
|
||||
$latlng = $params['facet_value'];
|
||||
|
||||
// Only handle "lat, lng" strings
|
||||
if ( ! empty( $latlng ) && is_string( $latlng ) ) {
|
||||
$latlng = preg_replace( '/[^0-9.,-]/', '', $latlng );
|
||||
|
||||
if ( ! empty( $facet['source_other'] ) ) {
|
||||
$other_params = $params;
|
||||
$other_params['facet_source'] = $facet['source_other'];
|
||||
$rows = $class->get_row_data( $other_params );
|
||||
|
||||
if ( ! empty( $rows ) && false === strpos( $latlng, ',' ) ) {
|
||||
$lng = $rows[0]['facet_display_value'];
|
||||
$lng = preg_replace( '/[^0-9.,-]/', '', $lng );
|
||||
$latlng .= ',' . $lng;
|
||||
}
|
||||
}
|
||||
|
||||
if ( preg_match( "/^([\d.-]+),([\d.-]+)$/", $latlng ) ) {
|
||||
$latlng = explode( ',', $latlng );
|
||||
$params['facet_value'] = $latlng[0];
|
||||
$params['facet_display_value'] = $latlng[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add "Distance" to the sort box
|
||||
*/
|
||||
function sort_options( $options, $params ) {
|
||||
|
||||
if ( FWP()->helper->facet_setting_exists( 'type', 'proximity' ) ) {
|
||||
$options['distance'] = [
|
||||
'label' => __( 'Distance', 'fwp-front' ),
|
||||
'query_args' => [
|
||||
'orderby' => 'post__in',
|
||||
'order' => 'ASC',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort the final (filtered) post IDs by distance
|
||||
*/
|
||||
function sort_by_distance( $post_ids, $class ) {
|
||||
|
||||
$distance = FWP()->helper->facet_types['proximity']->distance;
|
||||
|
||||
if ( ! empty( $distance ) ) {
|
||||
$ordered_posts = array_keys( $distance );
|
||||
$filtered_posts = array_flip( $post_ids );
|
||||
$intersected_ids = [];
|
||||
|
||||
foreach ( $ordered_posts as $p ) {
|
||||
if ( isset( $filtered_posts[ $p ] ) ) {
|
||||
$intersected_ids[] = $p;
|
||||
}
|
||||
}
|
||||
|
||||
$post_ids = $intersected_ids;
|
||||
}
|
||||
|
||||
return $post_ids;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a post's distance
|
||||
*/
|
||||
function facetwp_get_distance( $post_id = false ) {
|
||||
global $post;
|
||||
|
||||
// Get the post ID
|
||||
$post_id = ( false === $post_id ) ? $post->ID : $post_id;
|
||||
|
||||
// Get the proximity class
|
||||
$facet_type = FWP()->helper->facet_types['proximity'];
|
||||
|
||||
// Get the distance
|
||||
$distance = $facet_type->distance[ $post_id ] ?? -1;
|
||||
|
||||
if ( -1 < $distance ) {
|
||||
return apply_filters( 'facetwp_proximity_distance_output', $distance );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
64
wp/wp-content/plugins/facetwp/includes/facets/radio.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Radio_Core extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Radio', 'fwp' );
|
||||
$this->fields = [ 'label_any', 'parent_term', 'modifiers', 'ghosts', 'orderby', 'count' ];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the available choices
|
||||
*/
|
||||
function load_values( $params ) {
|
||||
$params['facet']['operator'] = 'or';
|
||||
return FWP()->helper->facet_types['checkboxes']->load_values( $params );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = '';
|
||||
$facet = $params['facet'];
|
||||
$values = (array) $params['values'];
|
||||
$selected_values = (array) $params['selected_values'];
|
||||
$label_any = empty( $facet['label_any'] ) ? false : facetwp_i18n( $facet['label_any'] );
|
||||
|
||||
if ( $label_any ) {
|
||||
$selected = empty( $selected_values ) ? ' checked' : '';
|
||||
$output .= '<div class="facetwp-radio' . $selected . '" data-value="">' . esc_attr( $label_any ) . '</div>';
|
||||
}
|
||||
|
||||
foreach ( $values as $row ) {
|
||||
$label = esc_html( $row['facet_display_value'] );
|
||||
$selected = in_array( $row['facet_value'], $selected_values ) ? ' checked' : '';
|
||||
$selected .= ( 0 == $row['counter'] && '' == $selected ) ? ' disabled' : '';
|
||||
$output .= '<div class="facetwp-radio' . $selected . '" data-value="' . esc_attr( $row['facet_value'] ) . '">';
|
||||
$output .= '<span class="facetwp-display-value">';
|
||||
$output .= apply_filters( 'facetwp_facet_display_value', $label, [
|
||||
'selected' => ( '' !== $selected ),
|
||||
'facet' => $facet,
|
||||
'row' => $row
|
||||
]);
|
||||
$output .= '</span>';
|
||||
$output .= '<span class="facetwp-counter">(' . $row['counter'] . ')</span>';
|
||||
$output .= '</div>';
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
$params['facet']['operator'] = 'or';
|
||||
return FWP()->helper->facet_types['checkboxes']->filter_posts( $params );
|
||||
}
|
||||
}
|
||||
114
wp/wp-content/plugins/facetwp/includes/facets/rating.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Rating extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Star Rating', 'fwp' );
|
||||
$this->fields = [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the available choices
|
||||
*/
|
||||
function load_values( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$from_clause = $wpdb->prefix . 'facetwp_index f';
|
||||
|
||||
// Facet in "OR" mode
|
||||
$where_clause = $this->get_where_clause( $facet );
|
||||
|
||||
$output = [
|
||||
1 => [ 'counter' => 0 ],
|
||||
2 => [ 'counter' => 0 ],
|
||||
3 => [ 'counter' => 0 ],
|
||||
4 => [ 'counter' => 0 ],
|
||||
5 => [ 'counter' => 0 ]
|
||||
];
|
||||
|
||||
$sql = "
|
||||
SELECT COUNT(*) AS `count`, FLOOR(f.facet_value) AS `rating`
|
||||
FROM $from_clause
|
||||
WHERE f.facet_name = '{$facet['name']}' AND FLOOR(f.facet_value) >= 1 $where_clause
|
||||
GROUP BY rating";
|
||||
|
||||
$results = $wpdb->get_results( $sql );
|
||||
|
||||
foreach ( $results as $result ) {
|
||||
$output[ $result->rating ]['counter'] = $result->count;
|
||||
}
|
||||
|
||||
$total = 0;
|
||||
|
||||
// The lower rating should include higher rating counts
|
||||
for ( $i = 5; $i > 0; $i-- ) {
|
||||
$output[ $i ]['counter'] += $total;
|
||||
$total = $output[ $i ]['counter'];
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = '';
|
||||
$facet = $params['facet'];
|
||||
$values = (array) $params['values'];
|
||||
$selected_values = (array) $params['selected_values'];
|
||||
|
||||
$num_stars = 0;
|
||||
foreach ( $values as $val ) {
|
||||
if ( 0 < $val['counter'] ) {
|
||||
$num_stars++;
|
||||
}
|
||||
}
|
||||
|
||||
if ( 0 < $num_stars ) {
|
||||
$output .= '<span class="facetwp-stars">';
|
||||
|
||||
for ( $i = $num_stars; $i >= 1; $i-- ) {
|
||||
$class = in_array( $i, $selected_values ) ? ' selected' : '';
|
||||
$output .= '<span class="facetwp-star' . $class . '" data-value="' . $i . '" data-counter="' . $values[ $i ]['counter'] . '">★</span>';
|
||||
}
|
||||
|
||||
$output .= '</span>';
|
||||
$output .= ' <span class="facetwp-star-label"></span>';
|
||||
$output .= ' <span class="facetwp-counter"></span>';
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$selected_values = $params['selected_values'];
|
||||
$selected_values = is_array( $selected_values ) ? $selected_values[0] : $selected_values;
|
||||
|
||||
$sql = "
|
||||
SELECT DISTINCT post_id FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE facet_name = '{$facet['name']}' AND facet_value >= '$selected_values'";
|
||||
return $wpdb->get_col( $sql );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Output front-end scripts
|
||||
*/
|
||||
function front_scripts() {
|
||||
FWP()->display->json['rating']['& up'] = __( '& up', 'fwp-front' );
|
||||
FWP()->display->json['rating']['Undo'] = __( 'Undo', 'fwp-front' );
|
||||
}
|
||||
}
|
||||
85
wp/wp-content/plugins/facetwp/includes/facets/reset.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Reset extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Reset', 'fwp' );
|
||||
$this->fields = [ 'reset_ui', 'reset_text', 'reset_mode', 'reset_facets', 'auto_hide' ];
|
||||
}
|
||||
|
||||
|
||||
function render( $params ) {
|
||||
$facet = $params['facet'];
|
||||
$reset_ui = $facet['reset_ui'];
|
||||
$reset_text = empty( $facet['reset_text'] ) ? __( 'Reset', 'fwp-front' ) : $facet['reset_text'];
|
||||
$reset_text = facetwp_i18n( $reset_text );
|
||||
|
||||
$classes = [ 'facetwp-reset' ];
|
||||
$attrs = '';
|
||||
|
||||
if ( ! FWP()->helper->facet_is( $facet, 'reset_mode', 'off' ) ) {
|
||||
if ( ! empty( $facet['reset_facets'] ) ) {
|
||||
$vals = implode( ',', $facet['reset_facets'] );
|
||||
$attrs = ' data-mode="{mode}" data-values="{vals}"';
|
||||
$attrs = str_replace( '{mode}', $facet['reset_mode'], $attrs );
|
||||
$attrs = str_replace( '{vals}', esc_attr( $vals ), $attrs );
|
||||
}
|
||||
}
|
||||
|
||||
if ( FWP()->helper->facet_is( $facet, 'auto_hide', 'yes' ) ) {
|
||||
$classes[] = 'facetwp-hide-empty';
|
||||
}
|
||||
|
||||
if ( 'button' == $reset_ui ) {
|
||||
$output = '<button class="{classes}"{attrs}>{label}</button>';
|
||||
}
|
||||
else {
|
||||
$output = '<a class="{classes}" href="javascript:;"{attrs}>{label}</a>';
|
||||
}
|
||||
|
||||
$output = str_replace( '{classes}', implode( ' ', $classes ), $output );
|
||||
$output = str_replace( '{label}', esc_attr( $reset_text ), $output );
|
||||
$output = str_replace( '{attrs}', $attrs, $output );
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
function filter_posts( $params ) {
|
||||
return 'continue';
|
||||
}
|
||||
|
||||
|
||||
function register_fields() {
|
||||
return [
|
||||
'reset_ui' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Reset UI', 'fwp' ),
|
||||
'choices' => [
|
||||
'button' => __( 'Button', 'fwp' ),
|
||||
'link' => __( 'Link', 'fwp' )
|
||||
]
|
||||
],
|
||||
'reset_mode' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Include / exclude', 'fwp' ),
|
||||
'notes' => 'Include or exclude certain facets?',
|
||||
'choices' => [
|
||||
'off' => __( 'Reset everything', 'fwp' ),
|
||||
'include' => __( 'Reset only these facets', 'fwp' ),
|
||||
'exclude' => __( 'Reset all except these facets', 'fwp' )
|
||||
]
|
||||
],
|
||||
'reset_facets' => [
|
||||
'label' => '',
|
||||
'html' => '<facet-names :facet="facet" setting="reset_facets"></facet-names>',
|
||||
'show' => "facet.reset_mode != 'off'"
|
||||
],
|
||||
'auto_hide' => [
|
||||
'type' => 'toggle',
|
||||
'label' => __( 'Auto-hide', 'fwp' ),
|
||||
'notes' => 'Hide when no facets have selected values'
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
89
wp/wp-content/plugins/facetwp/includes/facets/search.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Search extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Search', 'fwp' );
|
||||
$this->fields = [ 'search_engine', 'placeholder', 'auto_refresh' ];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
|
||||
$output = '';
|
||||
$facet = $params['facet'];
|
||||
$value = (array) $params['selected_values'];
|
||||
$value = empty( $value ) ? '' : stripslashes( $value[0] );
|
||||
$placeholder = empty( $facet['placeholder'] ) ? __( 'Enter keywords', 'fwp-front' ) : $facet['placeholder'];
|
||||
$placeholder = facetwp_i18n( $placeholder );
|
||||
$output .= '<span class="facetwp-input-wrap">';
|
||||
$output .= '<i class="facetwp-icon"></i>';
|
||||
$output .= '<input type="text" class="facetwp-search" value="' . esc_attr( $value ) . '" placeholder="' . esc_attr( $placeholder ) . '" autocomplete="off" />';
|
||||
$output .= '</span>';
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
|
||||
$facet = $params['facet'];
|
||||
$selected_values = $params['selected_values'];
|
||||
$selected_values = is_array( $selected_values ) ? $selected_values[0] : $selected_values;
|
||||
|
||||
if ( empty( $selected_values ) ) {
|
||||
return 'continue';
|
||||
}
|
||||
|
||||
// Default WP search
|
||||
$search_args = [
|
||||
's' => $selected_values,
|
||||
'posts_per_page' => 200,
|
||||
'fields' => 'ids',
|
||||
];
|
||||
|
||||
$search_args = apply_filters( 'facetwp_search_query_args', $search_args, $params );
|
||||
|
||||
$query = new WP_Query( $search_args );
|
||||
|
||||
return (array) $query->posts;
|
||||
}
|
||||
|
||||
|
||||
function register_fields() {
|
||||
$engines = apply_filters( 'facetwp_facet_search_engines', [] );
|
||||
$choices = [ '' => __( 'WP Default', 'fwp' ) ];
|
||||
|
||||
foreach ( $engines as $key => $label ) {
|
||||
$choices[ $key ] = $label;
|
||||
}
|
||||
|
||||
return [
|
||||
'search_engine' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Search engine', 'fwp' ),
|
||||
'choices' => $choices
|
||||
],
|
||||
'auto_refresh' => [
|
||||
'type' => 'toggle',
|
||||
'label' => __( 'Auto refresh', 'fwp' ),
|
||||
'notes' => 'Automatically refresh the results while typing?'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Front-end) Attach settings to the AJAX response
|
||||
*/
|
||||
function settings_js( $params ) {
|
||||
$auto_refresh = empty( $params['facet']['auto_refresh'] ) ? 'no' : $params['facet']['auto_refresh'];
|
||||
return [ 'auto_refresh' => $auto_refresh ];
|
||||
}
|
||||
}
|
||||
160
wp/wp-content/plugins/facetwp/includes/facets/slider.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Slider extends FacetWP_Facet
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Slider', 'fwp' );
|
||||
$this->fields = [ 'source_other', 'compare_type', 'prefix', 'suffix',
|
||||
'reset_text', 'slider_format', 'step' ];
|
||||
|
||||
add_filter( 'facetwp_render_output', [ $this, 'maybe_prevent_facet_html' ], 10, 2 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the facet HTML
|
||||
*/
|
||||
function render( $params ) {
|
||||
$facet = $params['facet'];
|
||||
$reset_text = __( 'Reset', 'fwp-front' );
|
||||
|
||||
if ( ! empty( $facet['reset_text'] ) ) {
|
||||
$reset_text = facetwp_i18n( $facet['reset_text'] );
|
||||
}
|
||||
|
||||
$output = '<div class="facetwp-slider-wrap">';
|
||||
$output .= '<div class="facetwp-slider"></div>';
|
||||
$output .= '</div>';
|
||||
$output .= '<span class="facetwp-slider-label"></span>';
|
||||
$output .= '<div><input type="button" class="facetwp-slider-reset" value="' . esc_attr( $reset_text ) . '" /></div>';
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the query based on selected values
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
return FWP()->helper->facet_types['number_range']->filter_posts( $params );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (Front-end) Attach settings to the AJAX response
|
||||
*/
|
||||
function settings_js( $params ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet = $params['facet'];
|
||||
$where_clause = $this->get_where_clause( $facet );
|
||||
$selected_values = $params['selected_values'];
|
||||
|
||||
// Set default slider values
|
||||
$defaults = [
|
||||
'format' => '',
|
||||
'prefix' => '',
|
||||
'suffix' => '',
|
||||
'step' => 1,
|
||||
];
|
||||
$facet = array_merge( $defaults, $facet );
|
||||
|
||||
$sql = "
|
||||
SELECT MIN(facet_value + 0) AS `min`, MAX(facet_display_value + 0) AS `max` FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE facet_name = '{$facet['name']}' AND facet_display_value != '' $where_clause";
|
||||
$row = $wpdb->get_row( $sql );
|
||||
|
||||
$range_min = (float) $row->min;
|
||||
$range_max = (float) $row->max;
|
||||
|
||||
$selected_min = (float) ( $selected_values[0] ?? $range_min );
|
||||
$selected_max = (float) ( $selected_values[1] ?? $range_max );
|
||||
|
||||
return [
|
||||
'range' => [ // outer (bar)
|
||||
'min' => min( $range_min, $selected_min ),
|
||||
'max' => max( $range_max, $selected_max )
|
||||
],
|
||||
'decimal_separator' => FWP()->helper->get_setting( 'decimal_separator' ),
|
||||
'thousands_separator' => FWP()->helper->get_setting( 'thousands_separator' ),
|
||||
'start' => [ $selected_min, $selected_max ], // inner (handles)
|
||||
'format' => $facet['format'],
|
||||
'prefix' => facetwp_i18n( $facet['prefix'] ),
|
||||
'suffix' => facetwp_i18n( $facet['suffix'] ),
|
||||
'step' => $facet['step']
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prevent the slider HTML from refreshing when active
|
||||
* @since 3.8.11
|
||||
*/
|
||||
function maybe_prevent_facet_html( $output, $params ) {
|
||||
if ( ! empty( $output['facets'] && 0 === $params['first_load' ] ) ) {
|
||||
foreach ( FWP()->facet->facets as $name => $facet ) {
|
||||
if ( 'slider' == $facet['type'] && ! empty( $facet['selected_values'] ) ) {
|
||||
unset( $output['facets'][ $name ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Output any front-end scripts
|
||||
*/
|
||||
function front_scripts() {
|
||||
FWP()->display->assets['nouislider.css'] = FACETWP_URL . '/assets/vendor/noUiSlider/nouislider.css';
|
||||
FWP()->display->assets['nouislider.js'] = FACETWP_URL . '/assets/vendor/noUiSlider/nouislider.min.js';
|
||||
FWP()->display->assets['nummy.js'] = FACETWP_URL . '/assets/vendor/nummy/nummy.min.js';
|
||||
}
|
||||
|
||||
|
||||
function register_fields() {
|
||||
$thousands = FWP()->helper->get_setting( 'thousands_separator' );
|
||||
$decimal = FWP()->helper->get_setting( 'decimal_separator' );
|
||||
$choices = [];
|
||||
|
||||
if ( '' != $thousands ) {
|
||||
$choices['0,0'] = "5{$thousands}280";
|
||||
$choices['0,0.0'] = "5{$thousands}280{$decimal}4";
|
||||
$choices['0,0.00'] = "5{$thousands}280{$decimal}42";
|
||||
}
|
||||
|
||||
$choices['0'] = '5280';
|
||||
$choices['0.0'] = "5280{$decimal}4";
|
||||
$choices['0.00'] = "5280{$decimal}42";
|
||||
$choices['0a'] = '5k';
|
||||
$choices['0.0a'] = "5{$decimal}3k";
|
||||
$choices['0.00a'] = "5{$decimal}28k";
|
||||
|
||||
return [
|
||||
'prefix' => [
|
||||
'label' => __( 'Prefix', 'fwp' ),
|
||||
'notes' => 'Text that appears before each slider value',
|
||||
],
|
||||
'suffix' => [
|
||||
'label' => __( 'Suffix', 'fwp' ),
|
||||
'notes' => 'Text that appears after each slider value',
|
||||
],
|
||||
'slider_format' => [
|
||||
'type' => 'alias',
|
||||
'items' => [
|
||||
'format' => [
|
||||
'type' => 'select',
|
||||
'label' => __( 'Format', 'fwp' ),
|
||||
'notes' => 'If the number separators are wrong, change the [Separators] setting in the Settings tab, then save and reload the page',
|
||||
'choices' => $choices
|
||||
]
|
||||
]
|
||||
],
|
||||
'step' => [
|
||||
'label' => __( 'Step', 'fwp' ),
|
||||
'notes' => 'The amount of increase between intervals',
|
||||
'default' => 1
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
226
wp/wp-content/plugins/facetwp/includes/facets/sort.php
Normal file
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Facet_Sort extends FacetWP_Facet
|
||||
{
|
||||
|
||||
public $sort_options = [];
|
||||
|
||||
|
||||
function __construct() {
|
||||
$this->label = __( 'Sort', 'fwp' );
|
||||
$this->fields = [ 'sort_default_label', 'sort_options' ];
|
||||
|
||||
add_filter( 'facetwp_filtered_query_args', [ $this, 'apply_sort' ], 1, 2 );
|
||||
add_filter( 'facetwp_render_output', [ $this, 'render_sort_feature' ], 1, 2 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render the sort facet
|
||||
*/
|
||||
function render( $params ) {
|
||||
$facet = $this->parse_sort_facet( $params['facet'] );
|
||||
$selected_values = (array) $params['selected_values'];
|
||||
|
||||
$label = facetwp_i18n( $facet['default_label'] );
|
||||
$output = '<option value="">' . esc_attr( $label ) . '</option>';
|
||||
|
||||
foreach ( $facet['sort_options'] as $key => $choice ) {
|
||||
$label = facetwp_i18n( $choice['label'] );
|
||||
$selected = in_array( $key, $selected_values ) ? ' selected' : '';
|
||||
$output .= '<option value="' . esc_attr( $key ) . '"' . $selected . '>' . esc_attr( $label ) . '</option>';
|
||||
}
|
||||
|
||||
return '<select>' . $output . '</select>';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort facets don't narrow results
|
||||
*/
|
||||
function filter_posts( $params ) {
|
||||
return 'continue';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register admin settings
|
||||
*/
|
||||
function register_fields() {
|
||||
return [
|
||||
'sort_default_label' => [
|
||||
'type' => 'alias',
|
||||
'items' => [
|
||||
'default_label' => [
|
||||
'label' => __( 'Default label', 'fwp' ),
|
||||
'notes' => 'The sort box placeholder text',
|
||||
'default' => __( 'Sort by', 'fwp' )
|
||||
]
|
||||
]
|
||||
],
|
||||
'sort_options' => [
|
||||
'label' => __( 'Sort options', 'fwp' ),
|
||||
'notes' => 'Define the choices that appear in the sort box',
|
||||
'html' => '<sort-options :facet="facet"></sort-options><input type="hidden" class="facet-sort-options" value="[]" />'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a sort facet's sort options into WP_Query arguments
|
||||
* @since 4.0.8
|
||||
*/
|
||||
function parse_sort_facet( $facet ) {
|
||||
$sort_options = [];
|
||||
|
||||
foreach ( $facet['sort_options'] as $row ) {
|
||||
$parsed = FWP()->builder->parse_query_obj([ 'orderby' => $row['orderby'] ]);
|
||||
|
||||
$sort_options[ $row['name'] ] = [
|
||||
'label' => $row['label'],
|
||||
'query_args' => array_intersect_key( $parsed, [
|
||||
'meta_query' => true,
|
||||
'orderby' => true
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
$sort_options = apply_filters( 'facetwp_facet_sort_options', $sort_options, [
|
||||
'facet' => $facet,
|
||||
'template_name' => FWP()->facet->template['name']
|
||||
]);
|
||||
|
||||
$facet['sort_options'] = $sort_options;
|
||||
|
||||
return $facet;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle both sort facets and the (old) sort feature
|
||||
* @since 4.0.6
|
||||
*/
|
||||
function apply_sort( $query_args, $class ) {
|
||||
|
||||
foreach ( $class->facets as $facet ) {
|
||||
if ( 'sort' == $facet['type'] ) {
|
||||
$sort_facet = $this->parse_sort_facet( $facet );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Support the (old) sort feature
|
||||
$sort_value = 'default';
|
||||
$this->sort_options = $this->get_sort_options();
|
||||
|
||||
if ( ! empty( $class->ajax_params['extras']['sort'] ) ) {
|
||||
$sort_value = $class->ajax_params['extras']['sort'];
|
||||
|
||||
if ( ! empty( $this->sort_options[ $sort_value ] ) ) {
|
||||
$args = $this->sort_options[ $sort_value ]['query_args'];
|
||||
$query_args = array_merge( $query_args, $args );
|
||||
}
|
||||
}
|
||||
|
||||
// Preserve relevancy sort
|
||||
$use_relevancy = apply_filters( 'facetwp_use_search_relevancy', true, $class );
|
||||
$is_default_sort = ( 'default' == $sort_value && empty( $class->http_params['get']['orderby'] ) );
|
||||
if ( $class->is_search && $use_relevancy && $is_default_sort && FWP()->is_filtered ) {
|
||||
$query_args['orderby'] = 'post__in';
|
||||
}
|
||||
|
||||
// Support the (new) sort facet
|
||||
if ( ! empty( $sort_facet['selected_values'] ) ) {
|
||||
$chosen = $sort_facet['selected_values'][0];
|
||||
$sort_options = $sort_facet['sort_options'];
|
||||
|
||||
if ( isset( $sort_options[ $chosen ] ) ) {
|
||||
$qa = $sort_options[ $chosen ]['query_args'];
|
||||
|
||||
if ( isset( $qa['meta_query'] ) ) {
|
||||
$meta_query = $query_args['meta_query'] ?? [];
|
||||
$query_args['meta_query'] = array_merge( $meta_query, $qa['meta_query'] );
|
||||
}
|
||||
|
||||
$query_args['orderby'] = $qa['orderby'];
|
||||
}
|
||||
}
|
||||
|
||||
return $query_args;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate choices for the (old) sort feature
|
||||
* @since 4.0.6
|
||||
*/
|
||||
function get_sort_options() {
|
||||
|
||||
$options = [
|
||||
'default' => [
|
||||
'label' => __( 'Sort by', 'fwp-front' ),
|
||||
'query_args' => []
|
||||
],
|
||||
'title_asc' => [
|
||||
'label' => __( 'Title (A-Z)', 'fwp-front' ),
|
||||
'query_args' => [
|
||||
'orderby' => 'title',
|
||||
'order' => 'ASC',
|
||||
]
|
||||
],
|
||||
'title_desc' => [
|
||||
'label' => __( 'Title (Z-A)', 'fwp-front' ),
|
||||
'query_args' => [
|
||||
'orderby' => 'title',
|
||||
'order' => 'DESC',
|
||||
]
|
||||
],
|
||||
'date_desc' => [
|
||||
'label' => __( 'Date (Newest)', 'fwp-front' ),
|
||||
'query_args' => [
|
||||
'orderby' => 'date',
|
||||
'order' => 'DESC',
|
||||
]
|
||||
],
|
||||
'date_asc' => [
|
||||
'label' => __( 'Date (Oldest)', 'fwp-front' ),
|
||||
'query_args' => [
|
||||
'orderby' => 'date',
|
||||
'order' => 'ASC',
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
return apply_filters( 'facetwp_sort_options', $options, [
|
||||
'template_name' => FWP()->facet->template['name'],
|
||||
] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Render the (old) sort feature
|
||||
* @since 4.0.6
|
||||
*/
|
||||
function render_sort_feature( $output, $params ) {
|
||||
$has_sort = isset( $params['extras']['sort'] );
|
||||
$has_choices = isset( $this->sort_options );
|
||||
|
||||
if ( 0 == $params['soft_refresh'] && $has_sort && $has_choices ) {
|
||||
$html = '';
|
||||
|
||||
foreach ( $this->sort_options as $key => $atts ) {
|
||||
$html .= '<option value="' . $key . '">' . $atts['label'] . '</option>';
|
||||
}
|
||||
|
||||
$html = '<select class="facetwp-sort-select">' . $html . '</select>';
|
||||
|
||||
$output['sort'] = apply_filters( 'facetwp_sort_html', $html, [
|
||||
'sort_options' => $this->sort_options,
|
||||
'template_name' => FWP()->facet->template['name'],
|
||||
]);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
43
wp/wp-content/plugins/facetwp/includes/functions.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
// DO NOT MODIFY THIS FILE!
|
||||
// Use your theme's functions.php instead
|
||||
|
||||
/**
|
||||
* An alternate to using do_shortcode()
|
||||
*
|
||||
* facetwp_display( 'pager' );
|
||||
* facetwp_display( 'template', 'cars' );
|
||||
* facetwp_display( 'template', 'cars', [ 'static' => true ] );
|
||||
*
|
||||
* @since 1.7.5
|
||||
*/
|
||||
function facetwp_display() {
|
||||
$args = array_replace( [ 'pager', true, [] ], func_get_args() );
|
||||
|
||||
$atts = (array) $args[2];
|
||||
$atts[ $args[0] ] = $args[1];
|
||||
|
||||
return FWP()->display->shortcode( $atts );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allow for translation of dynamic strings
|
||||
* @since 2.1
|
||||
*/
|
||||
function facetwp_i18n( $string ) {
|
||||
return apply_filters( 'facetwp_i18n', $string );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Support SQL modifications
|
||||
* @since 2.7
|
||||
*/
|
||||
function facetwp_sql( $sql, $facet ) {
|
||||
global $wpdb;
|
||||
|
||||
$sql = apply_filters( 'facetwp_wpdb_sql', $sql, $facet );
|
||||
return apply_filters( 'facetwp_wpdb_get_col', $wpdb->get_col( $sql ), $sql, $facet );
|
||||
}
|
||||
517
wp/wp-content/plugins/facetwp/includes/integrations/acf/acf.php
Normal file
@@ -0,0 +1,517 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Integration_ACF
|
||||
{
|
||||
|
||||
public $fields = [];
|
||||
public $parent_type_lookup = [];
|
||||
public $repeater_row;
|
||||
|
||||
|
||||
function __construct() {
|
||||
add_filter( 'facetwp_facet_sources', [ $this, 'facet_sources' ] );
|
||||
add_filter( 'facetwp_facet_orderby', [ $this, 'facet_orderby' ], 10, 2 );
|
||||
add_filter( 'facetwp_indexer_query_args', [ $this, 'lookup_acf_fields' ] );
|
||||
add_filter( 'facetwp_indexer_post_facet', [ $this, 'index_acf_values' ], 1, 2 );
|
||||
add_filter( 'facetwp_acf_display_value', [ $this, 'index_source_other' ], 1, 2 );
|
||||
add_filter( 'facetwp_builder_item_value', [ $this, 'layout_builder_values' ], 999, 2 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add ACF fields to the Data Sources dropdown
|
||||
*/
|
||||
function facet_sources( $sources ) {
|
||||
$fields = $this->get_fields();
|
||||
$choices = [];
|
||||
|
||||
foreach ( $fields as $field ) {
|
||||
$field_id = $field['hierarchy'];
|
||||
$field_name = $field['name'];
|
||||
$field_label = '[' . $field['group_title'] . '] ' . $field['parents'] . $field['label'];
|
||||
$choices[ "acf/$field_id" ] = $field_label;
|
||||
|
||||
// remove "hidden" ACF fields
|
||||
unset( $sources['custom_fields']['choices'][ "cf/_$field_name" ] );
|
||||
}
|
||||
|
||||
if ( ! empty( $choices ) ) {
|
||||
$sources['acf'] = [
|
||||
'label' => 'ACF',
|
||||
'choices' => $choices,
|
||||
'weight' => 5
|
||||
];
|
||||
}
|
||||
|
||||
return $sources;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the facet "Sort by" value is "Term order", then preserve
|
||||
* the custom order of certain ACF fields (checkboxes, radio, etc.)
|
||||
*/
|
||||
function facet_orderby( $orderby, $facet ) {
|
||||
if ( isset( $facet['source'] ) && isset( $facet['orderby'] ) ) {
|
||||
if ( 0 === strpos( $facet['source'], 'acf/' ) && 'term_order' == $facet['orderby'] ) {
|
||||
$source_parts = explode( '/', $facet['source'] );
|
||||
$field_id = array_pop( $source_parts );
|
||||
$field_object = get_field_object( $field_id );
|
||||
if ( ! empty( $field_object['choices'] ) ) {
|
||||
$choices = $field_object['choices'];
|
||||
$choices = implode( "','", esc_sql( $choices ) );
|
||||
$orderby = "FIELD(f.facet_display_value, '$choices')";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $orderby;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Index ACF field data
|
||||
*/
|
||||
function index_acf_values( $return, $params ) {
|
||||
$defaults = $params['defaults'];
|
||||
$facet = $params['facet'];
|
||||
|
||||
if ( isset( $facet['source'] ) && 'acf/' == substr( $facet['source'], 0, 4 ) ) {
|
||||
$hierarchy = explode( '/', substr( $facet['source'], 4 ) );
|
||||
|
||||
// support "User Post Type" plugin
|
||||
$object_id = apply_filters( 'facetwp_acf_object_id', $defaults['post_id'] );
|
||||
|
||||
// get values (for sub-fields, use the parent repeater)
|
||||
$value = get_field( $hierarchy[0], $object_id, false );
|
||||
|
||||
// handle repeater values
|
||||
if ( 1 < count( $hierarchy ) ) {
|
||||
|
||||
$parent_field_key = array_shift( $hierarchy );
|
||||
$value = $this->process_field_value( $value, $hierarchy, $parent_field_key );
|
||||
|
||||
// get the sub-field properties
|
||||
$sub_field = get_field_object( $hierarchy[0], $object_id, false, false );
|
||||
|
||||
foreach ( $value as $key => $val ) {
|
||||
$this->repeater_row = $key;
|
||||
$rows = $this->get_values_to_index( $val, $sub_field, $defaults );
|
||||
$this->index_field_values( $rows );
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// get the field properties
|
||||
$field = get_field_object( $hierarchy[0], $object_id, false, false );
|
||||
|
||||
// index values
|
||||
$rows = $this->get_values_to_index( $value, $field, $defaults );
|
||||
$this->index_field_values( $rows );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hijack the "facetwp_indexer_query_args" hook to lookup the fields once
|
||||
*/
|
||||
function lookup_acf_fields( $args ) {
|
||||
$this->get_fields();
|
||||
return $args;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Grab all ACF fields
|
||||
*/
|
||||
function get_fields() {
|
||||
|
||||
add_action( 'pre_get_posts', [ $this, 'disable_wpml' ] );
|
||||
$field_groups = acf_get_field_groups();
|
||||
remove_action( 'pre_get_posts', [ $this, 'disable_wpml' ] );
|
||||
|
||||
foreach ( $field_groups as $field_group ) {
|
||||
$fields = acf_get_fields( $field_group );
|
||||
|
||||
if ( ! empty( $fields ) ) {
|
||||
$this->flatten_fields( $fields, $field_group );
|
||||
}
|
||||
}
|
||||
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* We need to get field groups in ALL languages
|
||||
*/
|
||||
function disable_wpml( $query ) {
|
||||
$query->set( 'suppress_filters', true );
|
||||
$query->set( 'lang', '' );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract field values from the repeater array
|
||||
*/
|
||||
function process_field_value( $value, $hierarchy, $parent_field_key ) {
|
||||
$temp_val = [];
|
||||
|
||||
// prevent PHP8 fatal error on invalid lookup field
|
||||
$parent_field_type = $this->parent_type_lookup[ $parent_field_key ] ?? 'none';
|
||||
|
||||
if ( ! is_array( $value ) || 'none' == $parent_field_type ) {
|
||||
return $temp_val;
|
||||
}
|
||||
|
||||
// reduce the hierarchy array
|
||||
$field_key = array_shift( $hierarchy );
|
||||
|
||||
// group
|
||||
if ( 'group' == $parent_field_type ) {
|
||||
if ( 0 == count( $hierarchy ) ) {
|
||||
$temp_val[] = $value[ $field_key ];
|
||||
}
|
||||
else {
|
||||
return $this->process_field_value( $value[ $field_key ], $hierarchy, $field_key );
|
||||
}
|
||||
}
|
||||
// repeater
|
||||
else {
|
||||
if ( 0 == count( $hierarchy ) ) {
|
||||
foreach ( $value as $val ) {
|
||||
$temp_val[] = $val[ $field_key ];
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach ( $value as $outer ) {
|
||||
if ( isset( $outer[ $field_key ] ) ) {
|
||||
foreach ( $outer[ $field_key ] as $inner ) {
|
||||
$temp_val[] = $inner;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->process_field_value( $temp_val, $hierarchy, $field_key );
|
||||
}
|
||||
}
|
||||
|
||||
return $temp_val;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an array of $params arrays
|
||||
* Useful for indexing and grabbing values for the Layout Builder
|
||||
* @since 3.4.0
|
||||
*/
|
||||
function get_values_to_index( $value, $field, $params ) {
|
||||
$value = maybe_unserialize( $value );
|
||||
$type = $field['type'];
|
||||
$output = [];
|
||||
|
||||
// checkboxes
|
||||
if ( 'checkbox' == $type || 'select' == $type || 'radio' == $type ) {
|
||||
if ( false !== $value ) {
|
||||
foreach ( (array) $value as $val ) {
|
||||
$display_value = isset( $field['choices'][ $val ] ) ?
|
||||
$field['choices'][ $val ] :
|
||||
$val;
|
||||
|
||||
$params['facet_value'] = $val;
|
||||
$params['facet_display_value'] = $display_value;
|
||||
$output[] = $params;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// relationship
|
||||
elseif ( 'relationship' == $type || 'post_object' == $type || 'page_link' == $type ) {
|
||||
if ( false !== $value ) {
|
||||
foreach ( (array) $value as $val ) {
|
||||
|
||||
// does the post exist?
|
||||
if ( false !== get_post_type( $val ) ) {
|
||||
$params['facet_value'] = $val;
|
||||
$params['facet_display_value'] = get_the_title( $val );
|
||||
$output[] = $params;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// user
|
||||
elseif ( 'user' == $type ) {
|
||||
if ( false !== $value ) {
|
||||
foreach ( (array) $value as $val ) {
|
||||
$user = get_user_by( 'id', $val );
|
||||
|
||||
// does the user exist?
|
||||
if ( false !== $user ) {
|
||||
$params['facet_value'] = $val;
|
||||
$params['facet_display_value'] = $user->display_name;
|
||||
$output[] = $params;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// taxonomy
|
||||
elseif ( 'taxonomy' == $type ) {
|
||||
if ( ! empty( $value ) ) {
|
||||
foreach ( (array) $value as $val ) {
|
||||
global $wpdb;
|
||||
|
||||
$term_id = (int) $val;
|
||||
$term = $wpdb->get_row( "SELECT name, slug FROM {$wpdb->terms} WHERE term_id = '$term_id' LIMIT 1" );
|
||||
|
||||
// does the term exist?
|
||||
if ( null !== $term ) {
|
||||
$params['facet_value'] = $term->slug;
|
||||
$params['facet_display_value'] = $term->name;
|
||||
$params['term_id'] = $term_id;
|
||||
$output[] = $params;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// date_picker
|
||||
elseif ( 'date_picker' == $type ) {
|
||||
$formatted = $this->format_date( $value );
|
||||
$params['facet_value'] = $formatted;
|
||||
$params['facet_display_value'] = apply_filters( 'facetwp_acf_display_value', $formatted, $params );
|
||||
$output[] = $params;
|
||||
}
|
||||
|
||||
// true_false
|
||||
elseif ( 'true_false' == $type ) {
|
||||
$display_value = ( 0 < (int) $value ) ? __( 'Yes', 'fwp-front' ) : __( 'No', 'fwp-front' );
|
||||
$params['facet_value'] = $value;
|
||||
$params['facet_display_value'] = $display_value;
|
||||
$output[] = $params;
|
||||
}
|
||||
|
||||
// google_map
|
||||
elseif ( 'google_map' == $type ) {
|
||||
if ( isset( $value['lat'] ) && isset( $value['lng'] ) ) {
|
||||
$params['facet_value'] = $value['lat'];
|
||||
$params['facet_display_value'] = $value['lng'];
|
||||
$params['place_details'] = $value;
|
||||
$output[] = $params;
|
||||
}
|
||||
}
|
||||
|
||||
// text
|
||||
else {
|
||||
$params['facet_value'] = $value;
|
||||
$params['facet_display_value'] = apply_filters( 'facetwp_acf_display_value', $value, $params );
|
||||
$output[] = $params;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Index values
|
||||
*/
|
||||
function index_field_values( $rows ) {
|
||||
foreach ( $rows as $params ) {
|
||||
FWP()->indexer->index_row( $params );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle "source_other" setting
|
||||
*/
|
||||
function index_source_other( $value, $params ) {
|
||||
if ( ! empty( $params['facet_name'] ) ) {
|
||||
$facet = FWP()->helper->get_facet_by_name( $params['facet_name'] );
|
||||
|
||||
if ( ! empty( $facet['source_other'] ) ) {
|
||||
$hierarchy = explode( '/', substr( $facet['source_other'], 4 ) );
|
||||
|
||||
// support "User Post Type" plugin
|
||||
$object_id = apply_filters( 'facetwp_acf_object_id', $params['post_id'] );
|
||||
|
||||
// get the value
|
||||
$value = get_field( $hierarchy[0], $object_id, false );
|
||||
|
||||
// handle repeater values
|
||||
if ( 1 < count( $hierarchy ) ) {
|
||||
$parent_field_key = array_shift( $hierarchy );
|
||||
$value = $this->process_field_value( $value, $hierarchy, $parent_field_key );
|
||||
$value = $value[ $this->repeater_row ];
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'date_range' == $facet['type'] ) {
|
||||
$value = $this->format_date( $value );
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Format dates in YYYY-MM-DD
|
||||
*/
|
||||
function format_date( $str ) {
|
||||
if ( 8 == strlen( $str ) && ctype_digit( $str ) ) {
|
||||
$str = substr( $str, 0, 4 ) . '-' . substr( $str, 4, 2 ) . '-' . substr( $str, 6, 2 );
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a flat array of fields within a specific field group
|
||||
*/
|
||||
function flatten_fields( $fields, $field_group, $hierarchy = '', $parents = '' ) {
|
||||
foreach ( $fields as $field ) {
|
||||
|
||||
// append the hierarchy string
|
||||
$new_hierarchy = $hierarchy . '/' . $field['key'];
|
||||
|
||||
// loop again for repeater or group fields
|
||||
if ( 'repeater' == $field['type'] || 'group' == $field['type'] ) {
|
||||
$new_parents = $parents . $field['label'] . ' → ';
|
||||
|
||||
$this->parent_type_lookup[ $field['key'] ] = $field['type'];
|
||||
$this->flatten_fields( $field['sub_fields'], $field_group, $new_hierarchy, $new_parents );
|
||||
}
|
||||
else {
|
||||
$this->fields[] = [
|
||||
'key' => $field['key'],
|
||||
'name' => $field['name'],
|
||||
'label' => $field['label'],
|
||||
'hierarchy' => trim( $new_hierarchy, '/' ),
|
||||
'parents' => $parents,
|
||||
'group_title' => $field_group['title'],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the field value (support User Post Type)
|
||||
* @since 3.4.1
|
||||
*/
|
||||
function get_field( $source, $post_id ) {
|
||||
$hierarchy = explode( '/', substr( $source, 4 ) );
|
||||
$object_id = apply_filters( 'facetwp_acf_object_id', $post_id );
|
||||
return get_field( $hierarchy[0], $object_id );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fallback values for the layout builder
|
||||
* @since 3.4.0
|
||||
*
|
||||
* ACF return formats:
|
||||
* [image, file] = array, url, id
|
||||
* [select, checkbox, radio, button_group] = value, label, array (both)
|
||||
* [post_object, relationship, taxonomy] = object, id
|
||||
* [user] = array, object, id
|
||||
* [link] = array, url
|
||||
*/
|
||||
function layout_builder_values( $value, $item ) {
|
||||
global $post;
|
||||
|
||||
// exit if not an object or array
|
||||
if ( is_scalar( $value ) || is_null( $value ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$hierarchy = explode( '/', substr( $item['source'], 4 ) );
|
||||
|
||||
// support "User Post Type" plugin
|
||||
$object_id = apply_filters( 'facetwp_acf_object_id', $post->ID );
|
||||
|
||||
// get the field properties
|
||||
$field = get_field_object( $hierarchy[0], $object_id, false, false );
|
||||
|
||||
$type = $field['type'];
|
||||
$format = $field['return_format'] ?? '';
|
||||
$is_multiple = (bool) ( $field['multiple'] ?? false );
|
||||
|
||||
if ( ( 'post_object' == $type || 'relationship' == $type ) && 'object' == $format ) {
|
||||
$output = [];
|
||||
|
||||
$value = is_array( $value ) ? $value : [ $value ];
|
||||
|
||||
foreach ( $value as $val ) {
|
||||
$output[] = '<a href="' . get_permalink( $val->ID ) . '">' . esc_html( $val->post_title ) . '</a>';
|
||||
}
|
||||
|
||||
$value = $output;
|
||||
}
|
||||
|
||||
if ( 'taxonomy' == $type && 'object' == $format ) {
|
||||
$output = [];
|
||||
|
||||
foreach ( $value as $val ) {
|
||||
$output[] = $val->name;
|
||||
}
|
||||
|
||||
$value = $output;
|
||||
}
|
||||
|
||||
if ( ( 'select' == $type || 'checkbox' == $type || 'radio' == $type || 'button_group' == $type ) && 'array' == $format ) {
|
||||
$value = $value['label'] ?? wp_list_pluck( $value, 'label' );
|
||||
}
|
||||
|
||||
if ( ( 'image' == $type || 'gallery' == $type ) && 'array' == $format ) {
|
||||
$value = ( 'image' == $type ) ? [ $value ] : $value;
|
||||
|
||||
foreach ( $value as $val ) {
|
||||
$value = '<img src="' . esc_url( $val['url'] ) . '" title="' . esc_attr( $val['title'] ) . '" alt="' . esc_attr( $val['alt'] ) . '" />';
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'file' == $type && 'array' == $format ) {
|
||||
$value = '<a href="' . esc_url( $value['url'] ) . '">' . esc_html( $value['filename'] ) . '</a> (' . size_format( $value['filesize'], 1 ) . ')';
|
||||
}
|
||||
|
||||
if ( 'link' == $type && 'array' == $format ) {
|
||||
$value = '<a href="' . esc_url( $value['url'] ) . '" target="' . esc_attr( $value['target'] ) . '">' . esc_html( $value['title'] ) . '</a>';
|
||||
}
|
||||
|
||||
if ( 'google_map' == $type ) {
|
||||
$value = '<a href="https://www.google.com/maps/?q=' . $value['lat'] . ',' . $value['lng'] . '" target="_blank">' . esc_html( $value['address'] ) . '</a>';
|
||||
}
|
||||
|
||||
if ( 'user' == $type && ( 'object' == $format || 'array' == $format ) ) {
|
||||
$output = [];
|
||||
|
||||
$value = $is_multiple ? $value : [ $value ];
|
||||
|
||||
foreach ( $value as $val ) {
|
||||
if ( 'object' == $format ) {
|
||||
$output[] = $val->display_name;
|
||||
}
|
||||
elseif ( 'array' == $format ) {
|
||||
$output[] = $val['display_name'];
|
||||
}
|
||||
}
|
||||
$value = $output;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( function_exists( 'acf' ) && version_compare( acf()->settings['version'], '5.0', '>=' ) ) {
|
||||
FWP()->acf = new FacetWP_Integration_ACF();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
(function($) {
|
||||
$().on('facetwp-loaded', function() {
|
||||
$('.edd-no-js').addClass('facetwp-hidden');
|
||||
$('a.edd-add-to-cart').addClass('edd-has-js');
|
||||
});
|
||||
})(fUtil);
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Integration_EDD
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
add_filter( 'facetwp_facet_sources', [ $this, 'exclude_data_sources' ] );
|
||||
add_filter( 'edd_downloads_query', [ $this, 'edd_downloads_query' ] );
|
||||
add_action( 'facetwp_assets', [ $this, 'assets' ] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Trigger some EDD code on facetwp-loaded
|
||||
* @since 2.0.4
|
||||
*/
|
||||
function assets( $assets ) {
|
||||
$assets['edd.js'] = FACETWP_URL . '/includes/integrations/edd/edd.js';
|
||||
return $assets;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Help FacetWP auto-detect the [downloads] shortcode
|
||||
* @since 2.0.4
|
||||
*/
|
||||
function edd_downloads_query( $query ) {
|
||||
$query['facetwp'] = true;
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Exclude specific EDD custom fields
|
||||
* @since 2.4
|
||||
*/
|
||||
function exclude_data_sources( $sources ) {
|
||||
foreach ( $sources['custom_fields']['choices'] as $key => $val ) {
|
||||
if ( 0 === strpos( $val, '_edd_' ) ) {
|
||||
unset( $sources['custom_fields']['choices'][ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $sources;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( is_plugin_active( 'easy-digital-downloads/easy-digital-downloads.php' ) ) {
|
||||
new FacetWP_Integration_EDD();
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Integration_SearchWP
|
||||
{
|
||||
|
||||
public $keywords;
|
||||
public $swp_query;
|
||||
public $first_run = true;
|
||||
|
||||
|
||||
function __construct() {
|
||||
add_filter( 'facetwp_is_main_query', [ $this, 'is_main_query' ], 10, 2 );
|
||||
add_action( 'pre_get_posts', [ $this, 'pre_get_posts' ], 1000, 2 );
|
||||
add_filter( 'posts_pre_query', [ $this, 'posts_pre_query' ], 10, 2 );
|
||||
add_filter( 'posts_results', [ $this, 'posts_results' ], 10, 2 );
|
||||
add_filter( 'facetwp_facet_filter_posts', [ $this, 'search_facet' ], 10, 2 );
|
||||
add_filter( 'facetwp_facet_search_engines', [ $this, 'search_engines' ] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run and cache the \SWP_Query
|
||||
*/
|
||||
function is_main_query( $is_main_query, $query ) {
|
||||
if ( $is_main_query && $query->is_search() && ! empty( $query->get( 's' ) ) ) {
|
||||
$args = stripslashes_deep( $this->get_valid_args( $query ) );
|
||||
$this->keywords = $args['s'];
|
||||
$this->swp_query = $this->run_query( $args );
|
||||
$query->set( 'using_searchwp', true );
|
||||
$query->set( 'searchwp', false );
|
||||
}
|
||||
|
||||
return $is_main_query;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Whitelist supported SWP_Query arguments
|
||||
*
|
||||
* @link https://searchwp.com/documentation/classes/swp_query/#arguments
|
||||
*/
|
||||
function get_valid_args( $query ) {
|
||||
$output = [];
|
||||
|
||||
$valid = [
|
||||
's', 'engine', 'post__in', 'post__not_in', 'post_type', 'post_status',
|
||||
'tax_query', 'meta_query', 'date_query', 'order', 'orderby'
|
||||
];
|
||||
|
||||
foreach ( $valid as $arg ) {
|
||||
$val = $query->get( $arg );
|
||||
if ( ! empty( $val ) ) {
|
||||
$output[ $arg ] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Modify FacetWP's render() query to use SearchWP's results while bypassing
|
||||
* WP core search. We're using this additional query to support custom query
|
||||
* modifications, such as for FacetWP's sort box.
|
||||
*
|
||||
* The hook priority (1000) is important because this needs to run after
|
||||
* FacetWP_Request->update_query_vars()
|
||||
*/
|
||||
function pre_get_posts( $query ) {
|
||||
if ( true === $query->get( 'using_searchwp' ) ) {
|
||||
if ( true === $query->get( 'facetwp' ) && ! $this->first_run ) {
|
||||
$query->set( 's', '' );
|
||||
|
||||
$post_ids = FWP()->filtered_post_ids;
|
||||
$post_ids = empty( $post_ids ) ? [ 0 ] : $post_ids;
|
||||
$query->set( 'post__in', $post_ids );
|
||||
|
||||
if ( '' === $query->get( 'post_type' ) ) {
|
||||
$query->set( 'post_type', 'any' );
|
||||
$query->set( 'post_status', 'any' );
|
||||
}
|
||||
|
||||
if ( '' === $query->get( 'orderby' ) ) {
|
||||
$query->set( 'orderby', 'post__in' );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If [facetwp => false] then it's the get_filtered_post_ids() query. Return
|
||||
* the raw SearchWP results to prevent the additional query.
|
||||
*
|
||||
* If [facetwp => true] and [first_run => true] then it's the main WP query. Return
|
||||
* a non-null value to kill the query, since we don't use the results.
|
||||
*
|
||||
* If [facetwp => true] and [first_run => false] then it's the FacetWP render() query.
|
||||
*/
|
||||
function posts_pre_query( $posts, $query ) {
|
||||
if ( true === $query->get( 'using_searchwp' ) ) {
|
||||
if ( true === $query->get( 'facetwp' ) ) {
|
||||
$query->set( 's', $this->keywords );
|
||||
|
||||
// kill the main WP query
|
||||
if ( $this->first_run ) {
|
||||
$this->first_run = false;
|
||||
|
||||
$page = max( $query->get( 'paged' ), 1 );
|
||||
$per_page = (int) $query->get( 'posts_per_page', get_option( 'posts_per_page' ) );
|
||||
$query->found_posts = count( FWP()->filtered_post_ids );
|
||||
$query->max_num_pages = ( 0 < $per_page ) ? ceil( $query->found_posts / $per_page ) : 0;
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
else {
|
||||
return $this->swp_query->posts;
|
||||
}
|
||||
}
|
||||
|
||||
return $posts;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply highlighting if available
|
||||
*/
|
||||
function posts_results( $posts, $query ) {
|
||||
if ( true === $query->get( 'using_searchwp' ) ) {
|
||||
if ( true === $query->get( 'facetwp' ) && ! $this->first_run ) {
|
||||
|
||||
// SearchWP 4.1+
|
||||
if ( isset( $this->swp_query->query ) ) {
|
||||
foreach ( $posts as $index => $post ) {
|
||||
$source = \SearchWP\Utils::get_post_type_source_name( $post->post_type );
|
||||
$entry = new \SearchWP\Entry( $source, $post->ID, false );
|
||||
$posts[ $index ] = $entry->native( $this->swp_query->query );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $posts;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* For search facets, run \SWP_Query and return matching post IDs
|
||||
*/
|
||||
function search_facet( $return, $params ) {
|
||||
$facet = $params['facet'];
|
||||
$selected_values = $params['selected_values'];
|
||||
$selected_values = is_array( $selected_values ) ? $selected_values[0] : $selected_values;
|
||||
$engine = $facet['search_engine'] ?? '';
|
||||
|
||||
if ( 'search' == $facet['type'] && 0 === strpos( $engine, 'swp_' ) ) {
|
||||
$return = [];
|
||||
|
||||
if ( empty( $selected_values ) ) {
|
||||
$return = 'continue';
|
||||
}
|
||||
elseif ( ! empty( FWP()->unfiltered_post_ids ) ) {
|
||||
$swp_query = $this->run_query([
|
||||
's' => $selected_values,
|
||||
'engine' => substr( $engine, 4 ),
|
||||
'post__in' => FWP()->unfiltered_post_ids
|
||||
]);
|
||||
$return = $swp_query->posts;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run a search and return the \SWP_Query object
|
||||
*/
|
||||
function run_query( $args ) {
|
||||
$overrides = [ 'posts_per_page' => 200, 'fields' => 'ids', 'facetwp' => true ];
|
||||
$args = array_merge( $args, $overrides );
|
||||
return new \SWP_Query( $args );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add engines to the search facet
|
||||
*/
|
||||
function search_engines( $engines ) {
|
||||
|
||||
if ( version_compare( SEARCHWP_VERSION, '4.0', '>=' ) ) {
|
||||
$settings = get_option( SEARCHWP_PREFIX . 'engines' );
|
||||
|
||||
foreach ( $settings as $key => $info ) {
|
||||
$engines[ 'swp_' . $key ] = 'SearchWP - ' . $info['label'];
|
||||
}
|
||||
}
|
||||
else {
|
||||
$settings = get_option( SEARCHWP_PREFIX . 'settings' );
|
||||
|
||||
foreach ( $settings['engines'] as $key => $info ) {
|
||||
$label = $info['searchwp_engine_label'] ?? __( 'Default', 'fwp' );
|
||||
$engines[ 'swp_' . $key ] = 'SearchWP - ' . $label;
|
||||
}
|
||||
}
|
||||
|
||||
return $engines;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( defined( 'SEARCHWP_VERSION' ) ) {
|
||||
new FacetWP_Integration_SearchWP();
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Integration_WooCommerce_Taxonomy
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
add_action( 'woocommerce_after_template_part', [ $this, 'add_loop_tag'] );
|
||||
add_filter( 'get_terms', [ $this, 'adjust_term_counts' ], 10, 3 );
|
||||
add_filter( 'term_link', [ $this, 'append_url_vars' ], 10, 3 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Support category listings (Shop page display: Show categories)
|
||||
* @since 3.3.10
|
||||
*/
|
||||
function add_loop_tag( $template_name ) {
|
||||
if ( 'loop/loop-start.php' == $template_name ) {
|
||||
echo "<!--fwp-loop-->\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adjust the category listing counts when facets are selected
|
||||
* @since 3.3.10
|
||||
*/
|
||||
function adjust_term_counts( $terms, $taxonomy, $query_vars ) {
|
||||
if ( FWP()->request->is_refresh || ! empty( FWP()->request->url_vars ) ) {
|
||||
if ( 'product_cat' == reset( $taxonomy ) ) {
|
||||
global $wpdb, $wp_query;
|
||||
|
||||
$sql = $wp_query->request;
|
||||
if ( false !== ( $pos = strpos( $sql, ' ORDER BY' ) ) ) {
|
||||
$sql = substr( $sql, 0, $pos );
|
||||
}
|
||||
|
||||
$post_ids = $wpdb->get_col( $sql );
|
||||
|
||||
if ( ! empty( $post_ids ) ) {
|
||||
$term_counts = [];
|
||||
$post_ids_str = implode( ',', $post_ids );
|
||||
|
||||
$query = "
|
||||
SELECT term_id, COUNT(term_id) AS term_count
|
||||
FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE post_id IN ($post_ids_str)
|
||||
GROUP BY term_id";
|
||||
|
||||
$results = $wpdb->get_results( $query );
|
||||
|
||||
foreach ( $results as $row ) {
|
||||
$term_counts[ $row->term_id ] = (int) $row->term_count;
|
||||
}
|
||||
|
||||
foreach ( $terms as $term ) {
|
||||
$term->count = $term_counts[ $term->term_id ] ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $terms;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append facet URL variables to the category archive links
|
||||
* @since 3.3.10
|
||||
*/
|
||||
function append_url_vars( $term_link, $term, $taxonomy ) {
|
||||
if ( 'product_cat' == $taxonomy ) {
|
||||
$query_string = filter_var( $_SERVER['QUERY_STRING'], FILTER_SANITIZE_URL );
|
||||
|
||||
if ( ! empty( $query_string ) ) {
|
||||
$prefix = ( false !== strpos( $query_string, '?' ) ) ? '&' : '?';
|
||||
$term_link .= $prefix . $query_string;
|
||||
}
|
||||
}
|
||||
|
||||
return $term_link;
|
||||
}
|
||||
}
|
||||
|
||||
new FacetWP_Integration_WooCommerce_Taxonomy();
|
||||
@@ -0,0 +1,34 @@
|
||||
(function($) {
|
||||
|
||||
$().on('facetwp-refresh', function() {
|
||||
if (! FWP.loaded) {
|
||||
setup_woocommerce();
|
||||
}
|
||||
});
|
||||
|
||||
function setup_woocommerce() {
|
||||
|
||||
// Intercept WooCommerce pagination
|
||||
$().on('click', '.woocommerce-pagination a', function(e) {
|
||||
e.preventDefault();
|
||||
var matches = $(this).attr('href').match(/\/page\/(\d+)/);
|
||||
if (null !== matches) {
|
||||
FWP.paged = parseInt(matches[1]);
|
||||
FWP.soft_refresh = true;
|
||||
FWP.refresh();
|
||||
}
|
||||
});
|
||||
|
||||
// Disable sort handler
|
||||
$('.woocommerce-ordering').attr('onsubmit', 'event.preventDefault()');
|
||||
|
||||
// Intercept WooCommerce sorting
|
||||
$().on('change', '.woocommerce-ordering .orderby', function(e) {
|
||||
var qs = new URLSearchParams(window.location.search);
|
||||
qs.set('orderby', $(this).val());
|
||||
history.pushState(null, null, window.location.pathname + '?' + qs.toString());
|
||||
FWP.soft_refresh = true;
|
||||
FWP.refresh();
|
||||
});
|
||||
}
|
||||
})(fUtil);
|
||||
@@ -0,0 +1,570 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Integration_WooCommerce
|
||||
{
|
||||
|
||||
public $cache = [];
|
||||
public $lookup = [];
|
||||
public $storage = [];
|
||||
public $variations = [];
|
||||
public $post_clauses = false;
|
||||
|
||||
|
||||
function __construct() {
|
||||
add_action( 'facetwp_assets', [ $this, 'assets' ] );
|
||||
add_filter( 'facetwp_facet_sources', [ $this, 'facet_sources' ] );
|
||||
add_filter( 'facetwp_facet_display_value', [ $this, 'translate_hardcoded_choices' ], 10, 2 );
|
||||
add_filter( 'facetwp_indexer_post_facet', [ $this, 'index_woo_values' ], 10, 2 );
|
||||
|
||||
// Support WooCommerce product variations
|
||||
$is_enabled = ( 'yes' === FWP()->helper->get_setting( 'wc_enable_variations', 'no' ) );
|
||||
|
||||
if ( apply_filters( 'facetwp_enable_product_variations', $is_enabled ) ) {
|
||||
add_filter( 'facetwp_indexer_post_facet_defaults', [ $this, 'force_taxonomy' ], 10, 2 );
|
||||
add_filter( 'facetwp_indexer_query_args', [ $this, 'index_variations' ] );
|
||||
add_filter( 'facetwp_index_row', [ $this, 'attribute_variations' ], 1 );
|
||||
add_filter( 'facetwp_wpdb_sql', [ $this, 'wpdb_sql' ], 10, 2 );
|
||||
add_filter( 'facetwp_wpdb_get_col', [ $this, 'wpdb_get_col' ], 10, 3 );
|
||||
add_filter( 'facetwp_filtered_post_ids', [ $this, 'process_variations' ] );
|
||||
add_filter( 'facetwp_facet_where', [ $this, 'facet_where' ], 10, 2 );
|
||||
}
|
||||
|
||||
// Preserve the WooCommerce sort
|
||||
add_filter( 'posts_clauses', [ $this, 'preserve_sort' ], 20, 2 );
|
||||
|
||||
// Prevent WooCommerce from redirecting to a single result page
|
||||
add_filter( 'woocommerce_redirect_single_search_result', [ $this, 'redirect_single_search_result' ] );
|
||||
|
||||
// Prevent WooCommerce sort (posts_clauses) when doing FacetWP sort
|
||||
add_filter( 'woocommerce_default_catalog_orderby', [ $this, 'default_catalog_orderby' ] );
|
||||
|
||||
// Dynamic counts when Shop Page Display = "Categories" or "Both"
|
||||
if ( apply_filters( 'facetwp_woocommerce_support_categories_display', false ) ) {
|
||||
include( FACETWP_DIR . '/includes/integrations/woocommerce/taxonomy.php' );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run WooCommerce handlers on facetwp-refresh
|
||||
* @since 2.0.9
|
||||
*/
|
||||
function assets( $assets ) {
|
||||
$assets['woocommerce.js'] = FACETWP_URL . '/includes/integrations/woocommerce/woocommerce.js';
|
||||
return $assets;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add WooCommerce-specific data sources
|
||||
* @since 2.1.4
|
||||
*/
|
||||
function facet_sources( $sources ) {
|
||||
$sources['woocommerce'] = [
|
||||
'label' => __( 'WooCommerce', 'fwp' ),
|
||||
'choices' => [
|
||||
'woo/price' => __( 'Price' ),
|
||||
'woo/sale_price' => __( 'Sale Price' ),
|
||||
'woo/regular_price' => __( 'Regular Price' ),
|
||||
'woo/average_rating' => __( 'Average Rating' ),
|
||||
'woo/stock_status' => __( 'Stock Status' ),
|
||||
'woo/on_sale' => __( 'On Sale' ),
|
||||
'woo/featured' => __( 'Featured' ),
|
||||
'woo/product_type' => __( 'Product Type' ),
|
||||
],
|
||||
'weight' => 5
|
||||
];
|
||||
|
||||
// Move WC taxonomy choices
|
||||
foreach ( $sources['taxonomies']['choices'] as $key => $label ) {
|
||||
if ( 'tax/product_cat' == $key || 'tax/product_tag' == $key || 0 === strpos( $key, 'tax/pa_' ) ) {
|
||||
$sources['woocommerce']['choices'][ $key ] = $label;
|
||||
unset( $sources['taxonomies']['choices'][ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $sources;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attributes for WC product variations are stored in postmeta
|
||||
* @since 2.7.2
|
||||
*/
|
||||
function force_taxonomy( $defaults, $params ) {
|
||||
if ( 0 === strpos( $defaults['facet_source'], 'tax/pa_' ) ) {
|
||||
$post_id = (int) $defaults['post_id'];
|
||||
|
||||
if ( 'product_variation' == get_post_type( $post_id ) ) {
|
||||
$defaults['facet_source'] = str_replace( 'tax/', 'cf/attribute_', $defaults['facet_source'] );
|
||||
}
|
||||
}
|
||||
|
||||
return $defaults;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Index product variations
|
||||
* @since 2.7
|
||||
*/
|
||||
function index_variations( $args ) {
|
||||
|
||||
// Saving a single product
|
||||
if ( ! empty( $args['p'] ) ) {
|
||||
$post_id = (int) $args['p'];
|
||||
if ( 'product' == get_post_type( $post_id ) ) {
|
||||
if ( 'variable' == $this->get_product_type( $post_id ) ) {
|
||||
$product = wc_get_product( $post_id );
|
||||
|
||||
if ( false !== $product ) {
|
||||
$children = $product->get_children();
|
||||
$args['post_type'] = [ 'product', 'product_variation' ];
|
||||
$args['post__in'] = $children;
|
||||
$args['post__in'][] = $post_id;
|
||||
$args['posts_per_page'] = -1;
|
||||
unset( $args['p'] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Force product variations to piggyback products
|
||||
else {
|
||||
$pt = (array) $args['post_type'];
|
||||
|
||||
if ( in_array( 'any', $pt ) ) {
|
||||
$pt = get_post_types();
|
||||
}
|
||||
if ( in_array( 'product', $pt ) ) {
|
||||
$pt[] = 'product_variation';
|
||||
}
|
||||
|
||||
$args['post_type'] = $pt;
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When indexing product variations, attribute its parent product
|
||||
* @since 2.7
|
||||
*/
|
||||
function attribute_variations( $params ) {
|
||||
$post_id = (int) $params['post_id'];
|
||||
|
||||
// Set variation_id for all posts
|
||||
$params['variation_id'] = $post_id;
|
||||
|
||||
if ( 'product_variation' == get_post_type( $post_id ) ) {
|
||||
$params['post_id'] = wp_get_post_parent_id( $post_id );
|
||||
|
||||
// Lookup the term name for variation values
|
||||
if ( 0 === strpos( $params['facet_source'], 'cf/attribute_pa_' ) ) {
|
||||
$taxonomy = str_replace( 'cf/attribute_', '', $params['facet_source'] );
|
||||
$term = get_term_by( 'slug', $params['facet_value'], $taxonomy );
|
||||
|
||||
if ( false !== $term ) {
|
||||
$params['term_id'] = $term->term_id;
|
||||
$params['facet_display_value'] = $term->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hijack filter_posts() to grab variation IDs
|
||||
* @since 2.7
|
||||
*/
|
||||
function wpdb_sql( $sql, $facet ) {
|
||||
$sql = str_replace(
|
||||
'DISTINCT post_id',
|
||||
'DISTINCT post_id, GROUP_CONCAT(variation_id) AS variation_ids',
|
||||
$sql
|
||||
);
|
||||
|
||||
$sql .= ' GROUP BY post_id';
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Store a facet's variation IDs
|
||||
* @since 2.7
|
||||
*/
|
||||
function wpdb_get_col( $result, $sql, $facet ) {
|
||||
global $wpdb;
|
||||
|
||||
$facet_name = $facet['name'];
|
||||
$post_ids = $wpdb->get_col( $sql, 0 ); // arrays of product IDs
|
||||
$variations = $wpdb->get_col( $sql, 1 ); // variation IDs as arrays of comma-separated strings
|
||||
|
||||
foreach ( $post_ids as $index => $post_id ) {
|
||||
$variations_array = explode( ',', $variations[ $index ] );
|
||||
$type = in_array( $post_id, $variations_array ) ? 'products' : 'variations';
|
||||
|
||||
if ( isset( $this->cache[ $facet_name ][ $type ] ) ) {
|
||||
foreach ( $variations_array as $id ) {
|
||||
$this->cache[ $facet_name ][ $type ][] = $id;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->cache[ $facet_name ][ $type ] = $variations_array;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* We need lookup arrays for both products and variations
|
||||
* @since 2.7.1
|
||||
*/
|
||||
function generate_lookup_array( $post_ids ) {
|
||||
global $wpdb;
|
||||
|
||||
$output = [];
|
||||
|
||||
if ( ! empty( $post_ids ) ) {
|
||||
$sql = "
|
||||
SELECT DISTINCT post_id, variation_id
|
||||
FROM {$wpdb->prefix}facetwp_index
|
||||
WHERE post_id IN (" . implode( ',', $post_ids ) . ")";
|
||||
$results = $wpdb->get_results( $sql );
|
||||
|
||||
foreach ( $results as $result ) {
|
||||
$output['get_variations'][ $result->post_id ][] = $result->variation_id;
|
||||
$output['get_product'][ $result->variation_id ] = $result->post_id;
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine valid variation IDs
|
||||
* @since 2.7
|
||||
*/
|
||||
function process_variations( $post_ids ) {
|
||||
if ( empty( $this->cache ) ) {
|
||||
return $post_ids;
|
||||
}
|
||||
|
||||
$this->lookup = $this->generate_lookup_array( FWP()->unfiltered_post_ids );
|
||||
|
||||
// Loop through each facet's data
|
||||
foreach ( $this->cache as $facet_name => $groups ) {
|
||||
$this->storage[ $facet_name ] = [];
|
||||
|
||||
// Create an array of variation IDs
|
||||
foreach ( $groups as $type => $ids ) { // products or variations
|
||||
foreach ( $ids as $id ) {
|
||||
$this->storage[ $facet_name ][] = $id;
|
||||
|
||||
// Lookup variation IDs for each product
|
||||
if ( 'products' == $type ) {
|
||||
if ( ! empty( $this->lookup['get_variations'][ $id ] ) ) {
|
||||
foreach ( $this->lookup['get_variations'][ $id ] as $variation_id ) {
|
||||
$this->storage[ $facet_name ][] = $variation_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result = $this->calculate_variations();
|
||||
$this->variations = $result['variations'];
|
||||
$post_ids = array_intersect( $post_ids, $result['products'] );
|
||||
$post_ids = empty( $post_ids ) ? [ 0 ] : $post_ids;
|
||||
return $post_ids;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate variation IDs
|
||||
* @param mixed $facet_name Facet name to ignore, or FALSE
|
||||
* @return array Associative array of product IDs + variation IDs
|
||||
* @since 2.8
|
||||
*/
|
||||
function calculate_variations( $facet_name = false ) {
|
||||
|
||||
$new = true;
|
||||
$final_products = [];
|
||||
$final_variations = [];
|
||||
|
||||
// Intersect product + variation IDs across facets
|
||||
foreach ( $this->storage as $name => $variation_ids ) {
|
||||
|
||||
// Skip facets in "OR" mode
|
||||
if ( $facet_name === $name ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$final_variations = ( $new ) ? $variation_ids : array_intersect( $final_variations, $variation_ids );
|
||||
$new = false;
|
||||
}
|
||||
|
||||
// Lookup each variation's product ID
|
||||
foreach ( $final_variations as $variation_id ) {
|
||||
if ( isset( $this->lookup['get_product'][ $variation_id ] ) ) {
|
||||
$final_products[ $this->lookup['get_product'][ $variation_id ] ] = true; // prevent duplicates
|
||||
}
|
||||
}
|
||||
|
||||
// Append product IDs to the variations array
|
||||
$final_products = array_keys( $final_products );
|
||||
|
||||
foreach ( $final_products as $id ) {
|
||||
$final_variations[] = $id;
|
||||
}
|
||||
|
||||
return [
|
||||
'products' => $final_products,
|
||||
'variations' => array_unique( $final_variations )
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply variation IDs to load_values() method
|
||||
* @since 2.7
|
||||
*/
|
||||
function facet_where( $where_clause, $facet ) {
|
||||
|
||||
// Support facets in "OR" mode
|
||||
if ( FWP()->helper->facet_is( $facet, 'operator', 'or' ) ) {
|
||||
$result = $this->calculate_variations( $facet['name'] );
|
||||
$variations = $result['variations'];
|
||||
}
|
||||
else {
|
||||
$variations = $this->variations;
|
||||
}
|
||||
|
||||
if ( ! empty( $variations ) ) {
|
||||
$where_clause .= ' AND variation_id IN (' . implode( ',', $variations ) . ')';
|
||||
}
|
||||
|
||||
return $where_clause;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Efficiently grab the product type without wc_get_product()
|
||||
* @since 3.3.8
|
||||
*/
|
||||
function get_product_type( $post_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$sql = "
|
||||
SELECT t.name
|
||||
FROM $wpdb->terms t
|
||||
INNER JOIN $wpdb->term_taxonomy tt ON tt.term_id = t.term_id AND tt.taxonomy = 'product_type'
|
||||
INNER JOIN $wpdb->term_relationships tr ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tr.object_id = %d";
|
||||
|
||||
$type = $wpdb->get_var(
|
||||
$wpdb->prepare( $sql, $post_id )
|
||||
);
|
||||
|
||||
return ( null !== $type ) ? $type : 'simple';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Index WooCommerce-specific values
|
||||
* @since 2.1.4
|
||||
*/
|
||||
function index_woo_values( $return, $params ) {
|
||||
$facet = $params['facet'];
|
||||
$defaults = $params['defaults'];
|
||||
$post_id = (int) $defaults['post_id'];
|
||||
$post_type = get_post_type( $post_id );
|
||||
|
||||
// Index out of stock products?
|
||||
$index_all = ( 'yes' === FWP()->helper->get_setting( 'wc_index_all', 'no' ) );
|
||||
$index_all = apply_filters( 'facetwp_index_all_products', $index_all );
|
||||
|
||||
if ( 'product' == $post_type || 'product_variation' == $post_type ) {
|
||||
$product = wc_get_product( $post_id );
|
||||
|
||||
if ( ! $product || ( ! $index_all && ! $product->is_in_stock() ) ) {
|
||||
return true; // skip
|
||||
}
|
||||
}
|
||||
|
||||
// Default handling
|
||||
if ( 'product' != $post_type || empty( $facet['source'] ) ) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
// Ignore product attributes with "Used for variations" ticked
|
||||
if ( 0 === strpos( $facet['source'], 'tax/pa_' ) ) {
|
||||
if ( 'variable' == $this->get_product_type( $post_id ) ) {
|
||||
$attrs = $product->get_attributes();
|
||||
$attr_name = str_replace( 'tax/', '', $facet['source'] );
|
||||
if ( isset( $attrs[ $attr_name ] ) && 1 === $attrs[ $attr_name ]['is_variation'] ) {
|
||||
return true; // skip
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Custom woo fields
|
||||
if ( 0 === strpos( $facet['source'], 'woo/' ) ) {
|
||||
$source = substr( $facet['source'], 4 );
|
||||
|
||||
// Price
|
||||
if ( 'price' == $source || 'sale_price' == $source || 'regular_price' == $source ) {
|
||||
if ( $product->is_type( 'variable' ) ) {
|
||||
$method_name = "get_variation_$source";
|
||||
$price_min = $product->$method_name( 'min' ); // get_variation_price()
|
||||
$price_max = $product->$method_name( 'max' );
|
||||
}
|
||||
else {
|
||||
$method_name = "get_$source";
|
||||
$price_min = $price_max = $product->$method_name(); // get_price()
|
||||
}
|
||||
|
||||
$defaults['facet_value'] = $price_min;
|
||||
$defaults['facet_display_value'] = $price_max;
|
||||
FWP()->indexer->index_row( $defaults );
|
||||
}
|
||||
|
||||
// Average Rating
|
||||
elseif ( 'average_rating' == $source ) {
|
||||
$rating = $product->get_average_rating();
|
||||
$defaults['facet_value'] = $rating;
|
||||
$defaults['facet_display_value'] = $rating;
|
||||
FWP()->indexer->index_row( $defaults );
|
||||
}
|
||||
|
||||
// Stock Status
|
||||
elseif ( 'stock_status' == $source ) {
|
||||
$in_stock = $product->is_in_stock();
|
||||
$defaults['facet_value'] = (int) $in_stock;
|
||||
$defaults['facet_display_value'] = $in_stock ? 'In Stock' : 'Out of Stock';
|
||||
FWP()->indexer->index_row( $defaults );
|
||||
}
|
||||
|
||||
// On Sale
|
||||
elseif ( 'on_sale' == $source ) {
|
||||
if ( $product->is_on_sale() ) {
|
||||
$defaults['facet_value'] = 1;
|
||||
$defaults['facet_display_value'] = 'On Sale';
|
||||
FWP()->indexer->index_row( $defaults );
|
||||
}
|
||||
}
|
||||
|
||||
// Featured
|
||||
elseif ( 'featured' == $source ) {
|
||||
if ( $product->is_featured() ) {
|
||||
$defaults['facet_value'] = 1;
|
||||
$defaults['facet_display_value'] = 'Featured';
|
||||
FWP()->indexer->index_row( $defaults );
|
||||
}
|
||||
}
|
||||
|
||||
// Product Type
|
||||
elseif ( 'product_type' == $source ) {
|
||||
$type = $product->get_type();
|
||||
$defaults['facet_value'] = $type;
|
||||
$defaults['facet_display_value'] = $type;
|
||||
FWP()->indexer->index_row( $defaults );
|
||||
}
|
||||
|
||||
return true; // skip
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allow certain hard-coded choices to be translated dynamically
|
||||
* instead of stored as translated in the index table
|
||||
* @since 3.9.6
|
||||
*/
|
||||
function translate_hardcoded_choices( $label, $params ) {
|
||||
$source = $params['facet']['source'];
|
||||
|
||||
if ( 'woo/stock_status' == $source ) {
|
||||
$label = ( 'In Stock' == $label ) ? __( 'In Stock', 'fwp-front' ) : __( 'Out of Stock', 'fwp-front' );
|
||||
}
|
||||
elseif ( 'woo/on_sale' == $source ) {
|
||||
$label = __( 'On Sale', 'fwp-front' );
|
||||
}
|
||||
elseif ( 'woo/featured' == $source ) {
|
||||
$label = __( 'Featured', 'fwp-front' );
|
||||
}
|
||||
|
||||
return $label;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* WooCommerce removes its sort hooks after the main product_query runs
|
||||
* We need to preserve the sort for FacetWP to work
|
||||
*
|
||||
* @since 3.2.8
|
||||
*/
|
||||
function preserve_sort( $clauses, $query ) {
|
||||
|
||||
if ( ! apply_filters( 'facetwp_woocommerce_preserve_sort', true ) ) {
|
||||
return $clauses;
|
||||
}
|
||||
|
||||
$prefix = FWP()->helper->get_setting( 'prefix' );
|
||||
$using_sort = isset( FWP()->facet->http_params['get'][ $prefix . 'sort' ] );
|
||||
|
||||
if ( 'product_query' == $query->get( 'wc_query' ) && true === $query->get( 'facetwp' ) && ! $using_sort ) {
|
||||
if ( false === $this->post_clauses ) {
|
||||
$this->post_clauses = $clauses;
|
||||
}
|
||||
else {
|
||||
$clauses['join'] = $this->post_clauses['join'];
|
||||
$clauses['where'] = $this->post_clauses['where'];
|
||||
$clauses['orderby'] = $this->post_clauses['orderby'];
|
||||
|
||||
// Narrow the main query results
|
||||
$where_clause = FWP()->facet->where_clause;
|
||||
|
||||
if ( ! empty( $where_clause ) ) {
|
||||
$column = $GLOBALS['wpdb']->posts;
|
||||
$clauses['where'] .= str_replace( 'post_id', "$column.ID", $where_clause );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $clauses;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prevent WooCommerce from redirecting to single result page
|
||||
* @since 3.3.7
|
||||
*/
|
||||
function redirect_single_search_result( $bool ) {
|
||||
$using_facetwp = ( FWP()->request->is_refresh || ! empty( FWP()->request->url_vars ) );
|
||||
return $using_facetwp ? false : $bool;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prevent WooCommerce sort when a FacetWP sort is active
|
||||
* @since 3.6.8
|
||||
*/
|
||||
function default_catalog_orderby( $orderby ) {
|
||||
$sort = FWP()->helper->get_setting( 'prefix' ) . 'sort';
|
||||
return isset( $_GET[ $sort ] ) ? 'menu_order' : $orderby;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( is_plugin_active( 'woocommerce/woocommerce.php' ) ) {
|
||||
new FacetWP_Integration_WooCommerce();
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Builds or purges the FacetWP index.
|
||||
*/
|
||||
class FacetWP_Integration_WP_CLI
|
||||
{
|
||||
|
||||
/**
|
||||
* Index facet data.
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* [--ids=<ids>]
|
||||
* : Index specific post IDs (comma-separated).
|
||||
*
|
||||
* [--facets=<facets>]
|
||||
* : Index specific facet names (comma-separated).
|
||||
*/
|
||||
function index( $args, $assoc_args ) {
|
||||
$index_all = true;
|
||||
|
||||
if ( isset( $assoc_args['ids'] ) ) {
|
||||
if ( empty( $assoc_args['ids'] ) ) {
|
||||
WP_CLI::error( 'IDs empty.' );
|
||||
}
|
||||
|
||||
$ids = preg_replace( '/\s+/', '', $assoc_args['ids'] );
|
||||
$ids = explode( ',', $ids );
|
||||
$post_ids = array_filter( $ids, 'ctype_digit' );
|
||||
$index_all = false;
|
||||
}
|
||||
else {
|
||||
$post_ids = FWP()->indexer->get_post_ids_to_index();
|
||||
}
|
||||
|
||||
if ( isset( $assoc_args['facets'] ) ) {
|
||||
if ( empty( $assoc_args['facets'] ) ) {
|
||||
WP_CLI::error( 'Facets empty.' );
|
||||
}
|
||||
|
||||
$facets = [];
|
||||
$facet_names = preg_replace( '/\s+/', '', $assoc_args['facets'] );
|
||||
$facet_names = explode( ',', $facet_names );
|
||||
foreach ( $facet_names as $name ) {
|
||||
$facet = FWP()->helper->get_facet_by_name( $name );
|
||||
if ( false !== $facet ) {
|
||||
$facets[] = $facet;
|
||||
}
|
||||
}
|
||||
|
||||
$index_all = false;
|
||||
}
|
||||
else {
|
||||
$facets = FWP()->helper->get_facets();
|
||||
}
|
||||
|
||||
$progress = WP_CLI\Utils\make_progress_bar( 'Indexing:', count( $post_ids ) );
|
||||
|
||||
// prep
|
||||
if ( $index_all ) {
|
||||
FWP()->indexer->manage_temp_table( 'create' );
|
||||
}
|
||||
else {
|
||||
$assoc_args['pre_index'] = true;
|
||||
$this->purge( $args, $assoc_args );
|
||||
}
|
||||
|
||||
// manually load value modifiers
|
||||
FWP()->indexer->load_value_modifiers( $facets );
|
||||
|
||||
// index
|
||||
foreach ( $post_ids as $post_id ) {
|
||||
FWP()->indexer->index_post( $post_id, $facets );
|
||||
$progress->tick();
|
||||
}
|
||||
|
||||
// cleanup
|
||||
if ( $index_all ) {
|
||||
update_option( 'facetwp_last_indexed', time(), 'no' );
|
||||
FWP()->indexer->manage_temp_table( 'replace' );
|
||||
FWP()->indexer->manage_temp_table( 'delete' );
|
||||
}
|
||||
|
||||
$progress->finish();
|
||||
|
||||
WP_CLI::success( 'Indexing complete.' );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Purge facet data.
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* [--ids=<ids>]
|
||||
* : Purge specific post IDs (comma-separated).
|
||||
*
|
||||
* [--facets=<facets>]
|
||||
* : Purge specific facet names (comma-separated).
|
||||
*/
|
||||
function purge( $args, $assoc_args ) {
|
||||
global $wpdb;
|
||||
|
||||
$table = FWP()->indexer->table;
|
||||
|
||||
if ( ! isset( $assoc_args['ids'] ) && ! isset( $assoc_args['facets'] ) ) {
|
||||
$sql = "TRUNCATE TABLE $table";
|
||||
}
|
||||
else {
|
||||
$where = [];
|
||||
|
||||
if ( isset( $assoc_args['ids'] ) ) {
|
||||
if ( empty( $assoc_args['ids'] ) ) {
|
||||
WP_CLI::error( 'IDs empty.' );
|
||||
}
|
||||
|
||||
$ids = preg_replace( '/\s+/', '', ',' . $assoc_args['ids'] );
|
||||
$ids = explode( ',', $ids );
|
||||
$post_ids = array_filter( $ids, 'ctype_digit' );
|
||||
$post_ids = implode( "','", $post_ids );
|
||||
$where[] = "post_id IN ('$post_ids')";
|
||||
}
|
||||
|
||||
if ( isset( $assoc_args['facets'] ) ) {
|
||||
if ( empty( $assoc_args['facets'] ) ) {
|
||||
WP_CLI::error( 'Facets empty.' );
|
||||
}
|
||||
|
||||
$facet_names = preg_replace( '/\s+/', '', $assoc_args['facets'] );
|
||||
$facet_names = explode( ',', $facet_names );
|
||||
$facet_names = array_map( 'esc_sql', $facet_names );
|
||||
$facet_names = implode( "','", $facet_names );
|
||||
$where[] = "facet_name IN ('$facet_names')";
|
||||
}
|
||||
|
||||
$sql = "DELETE FROM $table WHERE " . implode( ' AND ', $where );
|
||||
}
|
||||
|
||||
$wpdb->query( $sql );
|
||||
|
||||
if ( ! isset( $assoc_args['pre_index'] ) ) {
|
||||
WP_CLI::success( 'Purge complete.' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
||||
WP_CLI::add_command( 'facetwp', 'FacetWP_Integration_WP_CLI' );
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
class FacetWP_Integration_WP_Rocket
|
||||
{
|
||||
|
||||
function __construct() {
|
||||
add_filter( 'rocket_exclude_defer_js', [ $this, 'get_exclusions' ] );
|
||||
add_filter( 'rocket_delay_js_exclusions', [ $this, 'get_exclusions' ] );
|
||||
add_filter( 'rocket_cdn_reject_files', [ $this, 'get_exclusions' ] );
|
||||
}
|
||||
|
||||
|
||||
function get_exclusions( $excluded ) {
|
||||
$excluded[] = '(.*)facetwp(.*)';
|
||||
$excluded[] = '(.*)maps.googleapis(.*)';
|
||||
$excluded[] = '/jquery-?[0-9.]*(.min|.slim|.slim.min)?.js';
|
||||
return $excluded;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( defined( 'WP_ROCKET_VERSION' ) ) {
|
||||
new FacetWP_Integration_WP_Rocket();
|
||||
}
|
||||
78
wp/wp-content/plugins/facetwp/index.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: FacetWP
|
||||
Description: Advanced Filtering for WordPress
|
||||
Version: 4.1.5
|
||||
Author: FacetWP, LLC
|
||||
Author URI: https://facetwp.com/
|
||||
|
||||
Copyright 2023 FacetWP, LLC
|
||||
|
||||
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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) or exit;
|
||||
|
||||
class FacetWP
|
||||
{
|
||||
|
||||
private static $instance;
|
||||
|
||||
|
||||
function __construct() {
|
||||
|
||||
// php check
|
||||
if ( version_compare( phpversion(), '7.0', '<' ) ) {
|
||||
add_action( 'admin_notices', array( $this, 'upgrade_notice' ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// setup variables
|
||||
define( 'FACETWP_VERSION', '4.1.5' );
|
||||
define( 'FACETWP_DIR', dirname( __FILE__ ) );
|
||||
define( 'FACETWP_URL', plugins_url( '', __FILE__ ) );
|
||||
define( 'FACETWP_BASENAME', plugin_basename( __FILE__ ) );
|
||||
|
||||
// get the gears turning
|
||||
include( FACETWP_DIR . '/includes/class-init.php' );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Singleton
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( ! isset( self::$instance ) ) {
|
||||
self::$instance = new self;
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Require PHP 7.0+
|
||||
*/
|
||||
function upgrade_notice() {
|
||||
$message = __( 'FacetWP requires PHP %s or above. Please contact your host and request a PHP upgrade.', 'fwp' );
|
||||
echo '<div class="error"><p>' . sprintf( $message, '7.0' ) . '</p></div>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function FWP() {
|
||||
return FacetWP::instance();
|
||||
}
|
||||
|
||||
|
||||
FWP();
|
||||
BIN
wp/wp-content/plugins/facetwp/languages/fwp-front-ca.mo
Normal file
185
wp/wp-content/plugins/facetwp/languages/fwp-front-ca.po
Normal file
@@ -0,0 +1,185 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: FacetWP (front)\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-02-21 14:01+0000\n"
|
||||
"PO-Revision-Date: 2022-08-15 12:41+0000\n"
|
||||
"Last-Translator: Matt Gibbs <hello@facetwp.com>\n"
|
||||
"Language-Team: Catalan\n"
|
||||
"Language: ca\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Loco https://localise.biz/\n"
|
||||
"X-Loco-Version: 2.5.0; wp-5.6\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
|
||||
#: includes/facets/rating.php:111
|
||||
msgid "& up"
|
||||
msgstr "& up"
|
||||
|
||||
#: includes/facets/fselect.php:37 includes/facets/fselect.php:80
|
||||
#: includes/facets/dropdown.php:35 includes/facets/hierarchy.php:26
|
||||
msgid "Any"
|
||||
msgstr "Qualsevol"
|
||||
|
||||
#: includes/facets/date_range.php:282
|
||||
msgid "Clear"
|
||||
msgstr "Esborrar"
|
||||
|
||||
#: includes/facets/proximity.php:180
|
||||
msgid "Clear location"
|
||||
msgstr "Localització de clara"
|
||||
|
||||
#: includes/facets/date_range.php:23
|
||||
msgid "Date"
|
||||
msgstr "Data"
|
||||
|
||||
#: includes/facets/sort.php:154
|
||||
msgid "Date (Newest)"
|
||||
msgstr "Data (més recent)"
|
||||
|
||||
#: includes/facets/sort.php:161
|
||||
msgid "Date (Oldest)"
|
||||
msgstr "Data (més antic)"
|
||||
|
||||
#: includes/facets/proximity.php:285
|
||||
msgid "Distance"
|
||||
msgstr "Distància"
|
||||
|
||||
#: includes/facets/date_range.php:29
|
||||
msgid "End Date"
|
||||
msgstr "Data final"
|
||||
|
||||
#: includes/facets/search.php:20
|
||||
msgid "Enter keywords"
|
||||
msgstr "Introduïu les paraules clau"
|
||||
|
||||
#: includes/facets/proximity.php:64
|
||||
msgid "Enter location"
|
||||
msgstr "Introduïu la ubicació"
|
||||
|
||||
#: includes/facets/autocomplete.php:157
|
||||
msgid "Enter {n} or more characters"
|
||||
msgstr ""
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:502
|
||||
msgid "Featured"
|
||||
msgstr ""
|
||||
|
||||
#: includes/facets/number_range.php:32 includes/facets/autocomplete.php:64
|
||||
msgid "Go"
|
||||
msgstr "Cercar"
|
||||
|
||||
#: includes/class-display.php:175
|
||||
msgid "Go to next page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-display.php:174
|
||||
msgid "Go to page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-display.php:176
|
||||
msgid "Go to previous page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:496
|
||||
msgid "In Stock"
|
||||
msgstr "En Stock"
|
||||
|
||||
#: includes/facets/autocomplete.php:156
|
||||
msgid "Loading"
|
||||
msgstr ""
|
||||
|
||||
#: includes/facets/number_range.php:29
|
||||
msgid "Max"
|
||||
msgstr "Màx"
|
||||
|
||||
#: includes/facets/number_range.php:26
|
||||
msgid "Min"
|
||||
msgstr "Min"
|
||||
|
||||
#: includes/integrations/acf/acf.php:293
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
|
||||
#: includes/facets/autocomplete.php:97 includes/facets/autocomplete.php:158
|
||||
msgid "No results"
|
||||
msgstr "No hi ha resultats"
|
||||
|
||||
#: includes/class-display.php:194 includes/facets/fselect.php:87
|
||||
msgid "No results found"
|
||||
msgstr "No s'han trobat resultats"
|
||||
|
||||
#: includes/facets/number_range.php:23
|
||||
msgid "Number"
|
||||
msgstr "Número"
|
||||
|
||||
#: includes/class-renderer.php:512
|
||||
msgid "of"
|
||||
msgstr "de"
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:499
|
||||
msgid "On Sale"
|
||||
msgstr "Oferta"
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:496
|
||||
msgid "Out of Stock"
|
||||
msgstr "Sense Stock"
|
||||
|
||||
#: includes/class-renderer.php:569
|
||||
msgid "Per page"
|
||||
msgstr "Per pàgina"
|
||||
|
||||
#: includes/facets/slider.php:20 includes/facets/reset.php:15
|
||||
msgid "Reset"
|
||||
msgstr "Restablir"
|
||||
|
||||
#: includes/facets/fselect.php:86
|
||||
msgid "Search"
|
||||
msgstr "Cercar"
|
||||
|
||||
#: includes/facets/checkboxes.php:131 includes/facets/hierarchy.php:161
|
||||
msgid "See less"
|
||||
msgstr "Veure menys"
|
||||
|
||||
#: includes/facets/hierarchy.php:160
|
||||
msgid "See more"
|
||||
msgstr "Veure més"
|
||||
|
||||
#: includes/facets/checkboxes.php:130
|
||||
msgid "See {num} more"
|
||||
msgstr "Veure més {num}"
|
||||
|
||||
#: includes/facets/sort.php:136
|
||||
msgid "Sort by"
|
||||
msgstr "Ordena per"
|
||||
|
||||
#: includes/facets/date_range.php:26
|
||||
msgid "Start Date"
|
||||
msgstr "Data inici"
|
||||
|
||||
#: includes/facets/autocomplete.php:61
|
||||
msgid "Start typing"
|
||||
msgstr "Comenceu a escriure"
|
||||
|
||||
#: includes/facets/sort.php:140
|
||||
msgid "Title (A-Z)"
|
||||
msgstr "Títol (A-Z)"
|
||||
|
||||
#: includes/facets/sort.php:147
|
||||
msgid "Title (Z-A)"
|
||||
msgstr "Títol (Z-A)"
|
||||
|
||||
#: includes/facets/rating.php:112
|
||||
msgid "Undo"
|
||||
msgstr "Desfer"
|
||||
|
||||
#: includes/integrations/acf/acf.php:293
|
||||
msgid "Yes"
|
||||
msgstr "Sí"
|
||||
|
||||
#: includes/facets/fselect.php:85
|
||||
msgid "{n} selected"
|
||||
msgstr "{n} seleccionats"
|
||||
BIN
wp/wp-content/plugins/facetwp/languages/fwp-front-da_DK.mo
Normal file
185
wp/wp-content/plugins/facetwp/languages/fwp-front-da_DK.po
Normal file
@@ -0,0 +1,185 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: FacetWP (front)\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-02-21 14:01+0000\n"
|
||||
"PO-Revision-Date: 2022-08-15 12:41+0000\n"
|
||||
"Last-Translator: Matt Gibbs <hello@facetwp.com>\n"
|
||||
"Language-Team: Danish\n"
|
||||
"Language: da_DK\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Loco https://localise.biz/\n"
|
||||
"X-Loco-Version: 2.5.0; wp-5.6\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
|
||||
#: includes/facets/rating.php:111
|
||||
msgid "& up"
|
||||
msgstr "& op"
|
||||
|
||||
#: includes/facets/fselect.php:37 includes/facets/fselect.php:80
|
||||
#: includes/facets/dropdown.php:35 includes/facets/hierarchy.php:26
|
||||
msgid "Any"
|
||||
msgstr "Enhver"
|
||||
|
||||
#: includes/facets/date_range.php:282
|
||||
msgid "Clear"
|
||||
msgstr "Ryd"
|
||||
|
||||
#: includes/facets/proximity.php:180
|
||||
msgid "Clear location"
|
||||
msgstr "Rud beliggenhed"
|
||||
|
||||
#: includes/facets/date_range.php:23
|
||||
msgid "Date"
|
||||
msgstr "Dato"
|
||||
|
||||
#: includes/facets/sort.php:154
|
||||
msgid "Date (Newest)"
|
||||
msgstr "Dato (nyeste)"
|
||||
|
||||
#: includes/facets/sort.php:161
|
||||
msgid "Date (Oldest)"
|
||||
msgstr "Dato (ældste)"
|
||||
|
||||
#: includes/facets/proximity.php:285
|
||||
msgid "Distance"
|
||||
msgstr "Afstand"
|
||||
|
||||
#: includes/facets/date_range.php:29
|
||||
msgid "End Date"
|
||||
msgstr "Slut dato"
|
||||
|
||||
#: includes/facets/search.php:20
|
||||
msgid "Enter keywords"
|
||||
msgstr "Indtast søgeord"
|
||||
|
||||
#: includes/facets/proximity.php:64
|
||||
msgid "Enter location"
|
||||
msgstr "Indtast lokation"
|
||||
|
||||
#: includes/facets/autocomplete.php:157
|
||||
msgid "Enter {n} or more characters"
|
||||
msgstr ""
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:502
|
||||
msgid "Featured"
|
||||
msgstr ""
|
||||
|
||||
#: includes/facets/number_range.php:32 includes/facets/autocomplete.php:64
|
||||
msgid "Go"
|
||||
msgstr "Gå"
|
||||
|
||||
#: includes/class-display.php:175
|
||||
msgid "Go to next page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-display.php:174
|
||||
msgid "Go to page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-display.php:176
|
||||
msgid "Go to previous page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:496
|
||||
msgid "In Stock"
|
||||
msgstr "På lager"
|
||||
|
||||
#: includes/facets/autocomplete.php:156
|
||||
msgid "Loading"
|
||||
msgstr ""
|
||||
|
||||
#: includes/facets/number_range.php:29
|
||||
msgid "Max"
|
||||
msgstr "Maks"
|
||||
|
||||
#: includes/facets/number_range.php:26
|
||||
msgid "Min"
|
||||
msgstr "Min"
|
||||
|
||||
#: includes/integrations/acf/acf.php:293
|
||||
msgid "No"
|
||||
msgstr "Nej"
|
||||
|
||||
#: includes/facets/autocomplete.php:97 includes/facets/autocomplete.php:158
|
||||
msgid "No results"
|
||||
msgstr "Ingen resultater"
|
||||
|
||||
#: includes/class-display.php:194 includes/facets/fselect.php:87
|
||||
msgid "No results found"
|
||||
msgstr "Ingen resultater fundet"
|
||||
|
||||
#: includes/facets/number_range.php:23
|
||||
msgid "Number"
|
||||
msgstr "Nummer"
|
||||
|
||||
#: includes/class-renderer.php:512
|
||||
msgid "of"
|
||||
msgstr "af"
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:499
|
||||
msgid "On Sale"
|
||||
msgstr "Udsalg"
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:496
|
||||
msgid "Out of Stock"
|
||||
msgstr "Ikke på lager"
|
||||
|
||||
#: includes/class-renderer.php:569
|
||||
msgid "Per page"
|
||||
msgstr "Per side"
|
||||
|
||||
#: includes/facets/slider.php:20 includes/facets/reset.php:15
|
||||
msgid "Reset"
|
||||
msgstr "Nulstil"
|
||||
|
||||
#: includes/facets/fselect.php:86
|
||||
msgid "Search"
|
||||
msgstr "Søg"
|
||||
|
||||
#: includes/facets/checkboxes.php:131 includes/facets/hierarchy.php:161
|
||||
msgid "See less"
|
||||
msgstr "Se mindre"
|
||||
|
||||
#: includes/facets/hierarchy.php:160
|
||||
msgid "See more"
|
||||
msgstr "Læs mere"
|
||||
|
||||
#: includes/facets/checkboxes.php:130
|
||||
msgid "See {num} more"
|
||||
msgstr "Se {num} mere"
|
||||
|
||||
#: includes/facets/sort.php:136
|
||||
msgid "Sort by"
|
||||
msgstr "Sorter efter"
|
||||
|
||||
#: includes/facets/date_range.php:26
|
||||
msgid "Start Date"
|
||||
msgstr "Start dato"
|
||||
|
||||
#: includes/facets/autocomplete.php:61
|
||||
msgid "Start typing"
|
||||
msgstr "Begynd at skrive"
|
||||
|
||||
#: includes/facets/sort.php:140
|
||||
msgid "Title (A-Z)"
|
||||
msgstr "Titel (A - Å)"
|
||||
|
||||
#: includes/facets/sort.php:147
|
||||
msgid "Title (Z-A)"
|
||||
msgstr "Titel (Å-A)"
|
||||
|
||||
#: includes/facets/rating.php:112
|
||||
msgid "Undo"
|
||||
msgstr "Fortryd"
|
||||
|
||||
#: includes/integrations/acf/acf.php:293
|
||||
msgid "Yes"
|
||||
msgstr "Ja"
|
||||
|
||||
#: includes/facets/fselect.php:85
|
||||
msgid "{n} selected"
|
||||
msgstr "{n} valgt"
|
||||
BIN
wp/wp-content/plugins/facetwp/languages/fwp-front-de_DE.mo
Normal file
185
wp/wp-content/plugins/facetwp/languages/fwp-front-de_DE.po
Normal file
@@ -0,0 +1,185 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: FacetWP (front)\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-02-21 14:01+0000\n"
|
||||
"PO-Revision-Date: 2022-08-15 12:41+0000\n"
|
||||
"Last-Translator: Matt Gibbs <hello@facetwp.com>\n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de_DE\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Loco https://localise.biz/\n"
|
||||
"X-Loco-Version: 2.5.0; wp-5.6\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
|
||||
#: includes/facets/rating.php:111
|
||||
msgid "& up"
|
||||
msgstr "& up"
|
||||
|
||||
#: includes/facets/fselect.php:37 includes/facets/fselect.php:80
|
||||
#: includes/facets/dropdown.php:35 includes/facets/hierarchy.php:26
|
||||
msgid "Any"
|
||||
msgstr "Alle"
|
||||
|
||||
#: includes/facets/date_range.php:282
|
||||
msgid "Clear"
|
||||
msgstr "Zurücksetzen"
|
||||
|
||||
#: includes/facets/proximity.php:180
|
||||
msgid "Clear location"
|
||||
msgstr "Location löschen"
|
||||
|
||||
#: includes/facets/date_range.php:23
|
||||
msgid "Date"
|
||||
msgstr "Datum"
|
||||
|
||||
#: includes/facets/sort.php:154
|
||||
msgid "Date (Newest)"
|
||||
msgstr "Datum (Neueste)"
|
||||
|
||||
#: includes/facets/sort.php:161
|
||||
msgid "Date (Oldest)"
|
||||
msgstr "Datum (älteste)"
|
||||
|
||||
#: includes/facets/proximity.php:285
|
||||
msgid "Distance"
|
||||
msgstr "Entfernung"
|
||||
|
||||
#: includes/facets/date_range.php:29
|
||||
msgid "End Date"
|
||||
msgstr "Enddatum"
|
||||
|
||||
#: includes/facets/search.php:20
|
||||
msgid "Enter keywords"
|
||||
msgstr "Suchbegriff eingeben"
|
||||
|
||||
#: includes/facets/proximity.php:64
|
||||
msgid "Enter location"
|
||||
msgstr "Geben Sie den Ort an"
|
||||
|
||||
#: includes/facets/autocomplete.php:157
|
||||
msgid "Enter {n} or more characters"
|
||||
msgstr ""
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:502
|
||||
msgid "Featured"
|
||||
msgstr ""
|
||||
|
||||
#: includes/facets/number_range.php:32 includes/facets/autocomplete.php:64
|
||||
msgid "Go"
|
||||
msgstr "Los"
|
||||
|
||||
#: includes/class-display.php:175
|
||||
msgid "Go to next page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-display.php:174
|
||||
msgid "Go to page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-display.php:176
|
||||
msgid "Go to previous page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:496
|
||||
msgid "In Stock"
|
||||
msgstr "Auf Lager"
|
||||
|
||||
#: includes/facets/autocomplete.php:156
|
||||
msgid "Loading"
|
||||
msgstr ""
|
||||
|
||||
#: includes/facets/number_range.php:29
|
||||
msgid "Max"
|
||||
msgstr "Max"
|
||||
|
||||
#: includes/facets/number_range.php:26
|
||||
msgid "Min"
|
||||
msgstr "Min"
|
||||
|
||||
#: includes/integrations/acf/acf.php:293
|
||||
msgid "No"
|
||||
msgstr "Nein"
|
||||
|
||||
#: includes/facets/autocomplete.php:97 includes/facets/autocomplete.php:158
|
||||
msgid "No results"
|
||||
msgstr "Keine Ergebnisse"
|
||||
|
||||
#: includes/class-display.php:194 includes/facets/fselect.php:87
|
||||
msgid "No results found"
|
||||
msgstr "Keine Ergebnisse gefunden"
|
||||
|
||||
#: includes/facets/number_range.php:23
|
||||
msgid "Number"
|
||||
msgstr "Nummer"
|
||||
|
||||
#: includes/class-renderer.php:512
|
||||
msgid "of"
|
||||
msgstr "von"
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:499
|
||||
msgid "On Sale"
|
||||
msgstr "Im Angebot"
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:496
|
||||
msgid "Out of Stock"
|
||||
msgstr "Ausverkauft"
|
||||
|
||||
#: includes/class-renderer.php:569
|
||||
msgid "Per page"
|
||||
msgstr "Pro Seite"
|
||||
|
||||
#: includes/facets/slider.php:20 includes/facets/reset.php:15
|
||||
msgid "Reset"
|
||||
msgstr "Zurücksetzen"
|
||||
|
||||
#: includes/facets/fselect.php:86
|
||||
msgid "Search"
|
||||
msgstr "Suche"
|
||||
|
||||
#: includes/facets/checkboxes.php:131 includes/facets/hierarchy.php:161
|
||||
msgid "See less"
|
||||
msgstr "Weniger sehen"
|
||||
|
||||
#: includes/facets/hierarchy.php:160
|
||||
msgid "See more"
|
||||
msgstr "Mehr anzeigen"
|
||||
|
||||
#: includes/facets/checkboxes.php:130
|
||||
msgid "See {num} more"
|
||||
msgstr "{num} weitere anzeigen"
|
||||
|
||||
#: includes/facets/sort.php:136
|
||||
msgid "Sort by"
|
||||
msgstr "Sortieren nach"
|
||||
|
||||
#: includes/facets/date_range.php:26
|
||||
msgid "Start Date"
|
||||
msgstr "Startdatum"
|
||||
|
||||
#: includes/facets/autocomplete.php:61
|
||||
msgid "Start typing"
|
||||
msgstr "Adresse eingeben"
|
||||
|
||||
#: includes/facets/sort.php:140
|
||||
msgid "Title (A-Z)"
|
||||
msgstr "Titel (A-Z)"
|
||||
|
||||
#: includes/facets/sort.php:147
|
||||
msgid "Title (Z-A)"
|
||||
msgstr "Titel (Z-A)"
|
||||
|
||||
#: includes/facets/rating.php:112
|
||||
msgid "Undo"
|
||||
msgstr "Rückgängig"
|
||||
|
||||
#: includes/integrations/acf/acf.php:293
|
||||
msgid "Yes"
|
||||
msgstr "Ja"
|
||||
|
||||
#: includes/facets/fselect.php:85
|
||||
msgid "{n} selected"
|
||||
msgstr "{n} ausgewählt"
|
||||
BIN
wp/wp-content/plugins/facetwp/languages/fwp-front-es_AR.mo
Normal file
185
wp/wp-content/plugins/facetwp/languages/fwp-front-es_AR.po
Normal file
@@ -0,0 +1,185 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: FacetWP (front)\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-02-21 14:01+0000\n"
|
||||
"PO-Revision-Date: 2022-08-15 12:41+0000\n"
|
||||
"Last-Translator: Matt Gibbs <hello@facetwp.com>\n"
|
||||
"Language-Team: Spanish (Argentina)\n"
|
||||
"Language: es_AR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Loco https://localise.biz/\n"
|
||||
"X-Loco-Version: 2.5.0; wp-5.6\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
|
||||
#: includes/facets/rating.php:111
|
||||
msgid "& up"
|
||||
msgstr "& up"
|
||||
|
||||
#: includes/facets/fselect.php:37 includes/facets/fselect.php:80
|
||||
#: includes/facets/dropdown.php:35 includes/facets/hierarchy.php:26
|
||||
msgid "Any"
|
||||
msgstr "Cualquier"
|
||||
|
||||
#: includes/facets/date_range.php:282
|
||||
msgid "Clear"
|
||||
msgstr "Limpiar"
|
||||
|
||||
#: includes/facets/proximity.php:180
|
||||
msgid "Clear location"
|
||||
msgstr "Borrar Ubicación"
|
||||
|
||||
#: includes/facets/date_range.php:23
|
||||
msgid "Date"
|
||||
msgstr "Fecha"
|
||||
|
||||
#: includes/facets/sort.php:154
|
||||
msgid "Date (Newest)"
|
||||
msgstr "Fecha (más reciente)"
|
||||
|
||||
#: includes/facets/sort.php:161
|
||||
msgid "Date (Oldest)"
|
||||
msgstr "Fecha (más antigua)"
|
||||
|
||||
#: includes/facets/proximity.php:285
|
||||
msgid "Distance"
|
||||
msgstr "Distancia"
|
||||
|
||||
#: includes/facets/date_range.php:29
|
||||
msgid "End Date"
|
||||
msgstr "Último día"
|
||||
|
||||
#: includes/facets/search.php:20
|
||||
msgid "Enter keywords"
|
||||
msgstr "Ingrese palabras claves"
|
||||
|
||||
#: includes/facets/proximity.php:64
|
||||
msgid "Enter location"
|
||||
msgstr "Introducir ubicación"
|
||||
|
||||
#: includes/facets/autocomplete.php:157
|
||||
msgid "Enter {n} or more characters"
|
||||
msgstr ""
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:502
|
||||
msgid "Featured"
|
||||
msgstr ""
|
||||
|
||||
#: includes/facets/number_range.php:32 includes/facets/autocomplete.php:64
|
||||
msgid "Go"
|
||||
msgstr "Ir"
|
||||
|
||||
#: includes/class-display.php:175
|
||||
msgid "Go to next page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-display.php:174
|
||||
msgid "Go to page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-display.php:176
|
||||
msgid "Go to previous page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:496
|
||||
msgid "In Stock"
|
||||
msgstr "En Stock"
|
||||
|
||||
#: includes/facets/autocomplete.php:156
|
||||
msgid "Loading"
|
||||
msgstr ""
|
||||
|
||||
#: includes/facets/number_range.php:29
|
||||
msgid "Max"
|
||||
msgstr "Max"
|
||||
|
||||
#: includes/facets/number_range.php:26
|
||||
msgid "Min"
|
||||
msgstr "Minutos"
|
||||
|
||||
#: includes/integrations/acf/acf.php:293
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
|
||||
#: includes/facets/autocomplete.php:97 includes/facets/autocomplete.php:158
|
||||
msgid "No results"
|
||||
msgstr "No hay resultados"
|
||||
|
||||
#: includes/class-display.php:194 includes/facets/fselect.php:87
|
||||
msgid "No results found"
|
||||
msgstr "No se encontraron resultados"
|
||||
|
||||
#: includes/facets/number_range.php:23
|
||||
msgid "Number"
|
||||
msgstr "Número"
|
||||
|
||||
#: includes/class-renderer.php:512
|
||||
msgid "of"
|
||||
msgstr "de"
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:499
|
||||
msgid "On Sale"
|
||||
msgstr "A la venta"
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:496
|
||||
msgid "Out of Stock"
|
||||
msgstr "Fuera de Stock"
|
||||
|
||||
#: includes/class-renderer.php:569
|
||||
msgid "Per page"
|
||||
msgstr "Por página"
|
||||
|
||||
#: includes/facets/slider.php:20 includes/facets/reset.php:15
|
||||
msgid "Reset"
|
||||
msgstr "Reiniciar"
|
||||
|
||||
#: includes/facets/fselect.php:86
|
||||
msgid "Search"
|
||||
msgstr "Buscar"
|
||||
|
||||
#: includes/facets/checkboxes.php:131 includes/facets/hierarchy.php:161
|
||||
msgid "See less"
|
||||
msgstr "Ver menos"
|
||||
|
||||
#: includes/facets/hierarchy.php:160
|
||||
msgid "See more"
|
||||
msgstr "Ver más"
|
||||
|
||||
#: includes/facets/checkboxes.php:130
|
||||
msgid "See {num} more"
|
||||
msgstr "Ver {num} más"
|
||||
|
||||
#: includes/facets/sort.php:136
|
||||
msgid "Sort by"
|
||||
msgstr "Ordenar por"
|
||||
|
||||
#: includes/facets/date_range.php:26
|
||||
msgid "Start Date"
|
||||
msgstr "Fecha de Comienzo"
|
||||
|
||||
#: includes/facets/autocomplete.php:61
|
||||
msgid "Start typing"
|
||||
msgstr "Comience a escribir"
|
||||
|
||||
#: includes/facets/sort.php:140
|
||||
msgid "Title (A-Z)"
|
||||
msgstr "Título (A-Z)"
|
||||
|
||||
#: includes/facets/sort.php:147
|
||||
msgid "Title (Z-A)"
|
||||
msgstr "Título (Z-A)"
|
||||
|
||||
#: includes/facets/rating.php:112
|
||||
msgid "Undo"
|
||||
msgstr "Deshacer"
|
||||
|
||||
#: includes/integrations/acf/acf.php:293
|
||||
msgid "Yes"
|
||||
msgstr "Sí"
|
||||
|
||||
#: includes/facets/fselect.php:85
|
||||
msgid "{n} selected"
|
||||
msgstr "{n} selecciones"
|
||||
BIN
wp/wp-content/plugins/facetwp/languages/fwp-front-es_ES.mo
Normal file
185
wp/wp-content/plugins/facetwp/languages/fwp-front-es_ES.po
Normal file
@@ -0,0 +1,185 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: FacetWP (front)\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-02-21 14:01+0000\n"
|
||||
"PO-Revision-Date: 2022-08-15 12:41+0000\n"
|
||||
"Last-Translator: Matt Gibbs <hello@facetwp.com>\n"
|
||||
"Language-Team: Spanish (Spain)\n"
|
||||
"Language: es_ES\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Loco https://localise.biz/\n"
|
||||
"X-Loco-Version: 2.5.0; wp-5.6\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
|
||||
#: includes/facets/rating.php:111
|
||||
msgid "& up"
|
||||
msgstr "& up"
|
||||
|
||||
#: includes/facets/fselect.php:37 includes/facets/fselect.php:80
|
||||
#: includes/facets/dropdown.php:35 includes/facets/hierarchy.php:26
|
||||
msgid "Any"
|
||||
msgstr "Cualquiera"
|
||||
|
||||
#: includes/facets/date_range.php:282
|
||||
msgid "Clear"
|
||||
msgstr "Limpiar"
|
||||
|
||||
#: includes/facets/proximity.php:180
|
||||
msgid "Clear location"
|
||||
msgstr "Borrar ubicación"
|
||||
|
||||
#: includes/facets/date_range.php:23
|
||||
msgid "Date"
|
||||
msgstr "Fecha"
|
||||
|
||||
#: includes/facets/sort.php:154
|
||||
msgid "Date (Newest)"
|
||||
msgstr "Fecha (más reciente)"
|
||||
|
||||
#: includes/facets/sort.php:161
|
||||
msgid "Date (Oldest)"
|
||||
msgstr "Fecha (más antigua)"
|
||||
|
||||
#: includes/facets/proximity.php:285
|
||||
msgid "Distance"
|
||||
msgstr "Distancia"
|
||||
|
||||
#: includes/facets/date_range.php:29
|
||||
msgid "End Date"
|
||||
msgstr "Fecha de finalización"
|
||||
|
||||
#: includes/facets/search.php:20
|
||||
msgid "Enter keywords"
|
||||
msgstr "¿Qué estás buscando?"
|
||||
|
||||
#: includes/facets/proximity.php:64
|
||||
msgid "Enter location"
|
||||
msgstr "Introducir ubicación"
|
||||
|
||||
#: includes/facets/autocomplete.php:157
|
||||
msgid "Enter {n} or more characters"
|
||||
msgstr ""
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:502
|
||||
msgid "Featured"
|
||||
msgstr ""
|
||||
|
||||
#: includes/facets/number_range.php:32 includes/facets/autocomplete.php:64
|
||||
msgid "Go"
|
||||
msgstr "Ir"
|
||||
|
||||
#: includes/class-display.php:175
|
||||
msgid "Go to next page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-display.php:174
|
||||
msgid "Go to page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-display.php:176
|
||||
msgid "Go to previous page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:496
|
||||
msgid "In Stock"
|
||||
msgstr "En Stock"
|
||||
|
||||
#: includes/facets/autocomplete.php:156
|
||||
msgid "Loading"
|
||||
msgstr ""
|
||||
|
||||
#: includes/facets/number_range.php:29
|
||||
msgid "Max"
|
||||
msgstr "Máx"
|
||||
|
||||
#: includes/facets/number_range.php:26
|
||||
msgid "Min"
|
||||
msgstr "Mín"
|
||||
|
||||
#: includes/integrations/acf/acf.php:293
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
|
||||
#: includes/facets/autocomplete.php:97 includes/facets/autocomplete.php:158
|
||||
msgid "No results"
|
||||
msgstr "No hay resultados"
|
||||
|
||||
#: includes/class-display.php:194 includes/facets/fselect.php:87
|
||||
msgid "No results found"
|
||||
msgstr "No se han encontrado resultados"
|
||||
|
||||
#: includes/facets/number_range.php:23
|
||||
msgid "Number"
|
||||
msgstr "Número"
|
||||
|
||||
#: includes/class-renderer.php:512
|
||||
msgid "of"
|
||||
msgstr "de"
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:499
|
||||
msgid "On Sale"
|
||||
msgstr "En oferta"
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:496
|
||||
msgid "Out of Stock"
|
||||
msgstr "Agotado"
|
||||
|
||||
#: includes/class-renderer.php:569
|
||||
msgid "Per page"
|
||||
msgstr "Por página"
|
||||
|
||||
#: includes/facets/slider.php:20 includes/facets/reset.php:15
|
||||
msgid "Reset"
|
||||
msgstr "Reiniciar"
|
||||
|
||||
#: includes/facets/fselect.php:86
|
||||
msgid "Search"
|
||||
msgstr "Buscar"
|
||||
|
||||
#: includes/facets/checkboxes.php:131 includes/facets/hierarchy.php:161
|
||||
msgid "See less"
|
||||
msgstr "Ver menos"
|
||||
|
||||
#: includes/facets/hierarchy.php:160
|
||||
msgid "See more"
|
||||
msgstr "Ver más"
|
||||
|
||||
#: includes/facets/checkboxes.php:130
|
||||
msgid "See {num} more"
|
||||
msgstr "Ver {num} más"
|
||||
|
||||
#: includes/facets/sort.php:136
|
||||
msgid "Sort by"
|
||||
msgstr "Ordenar por"
|
||||
|
||||
#: includes/facets/date_range.php:26
|
||||
msgid "Start Date"
|
||||
msgstr "Fecha de inicio"
|
||||
|
||||
#: includes/facets/autocomplete.php:61
|
||||
msgid "Start typing"
|
||||
msgstr "Escribe algo"
|
||||
|
||||
#: includes/facets/sort.php:140
|
||||
msgid "Title (A-Z)"
|
||||
msgstr "Título (A-Z)"
|
||||
|
||||
#: includes/facets/sort.php:147
|
||||
msgid "Title (Z-A)"
|
||||
msgstr "Título (Z-A)"
|
||||
|
||||
#: includes/facets/rating.php:112
|
||||
msgid "Undo"
|
||||
msgstr "Deshacer"
|
||||
|
||||
#: includes/integrations/acf/acf.php:293
|
||||
msgid "Yes"
|
||||
msgstr "Sí"
|
||||
|
||||
#: includes/facets/fselect.php:85
|
||||
msgid "{n} selected"
|
||||
msgstr "{n} selecciones"
|
||||
BIN
wp/wp-content/plugins/facetwp/languages/fwp-front-fr_FR.mo
Normal file
185
wp/wp-content/plugins/facetwp/languages/fwp-front-fr_FR.po
Normal file
@@ -0,0 +1,185 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: FacetWP (front)\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-02-21 14:01+0000\n"
|
||||
"PO-Revision-Date: 2022-08-15 12:42+0000\n"
|
||||
"Last-Translator: Matt Gibbs <hello@facetwp.com>\n"
|
||||
"Language-Team: French (France)\n"
|
||||
"Language: fr_FR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"X-Generator: Loco https://localise.biz/\n"
|
||||
"X-Loco-Version: 2.5.0; wp-5.6\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
|
||||
#: includes/facets/rating.php:111
|
||||
msgid "& up"
|
||||
msgstr "& up"
|
||||
|
||||
#: includes/facets/fselect.php:37 includes/facets/fselect.php:80
|
||||
#: includes/facets/dropdown.php:35 includes/facets/hierarchy.php:26
|
||||
msgid "Any"
|
||||
msgstr "Tous"
|
||||
|
||||
#: includes/facets/date_range.php:282
|
||||
msgid "Clear"
|
||||
msgstr "Effacer"
|
||||
|
||||
#: includes/facets/proximity.php:180
|
||||
msgid "Clear location"
|
||||
msgstr "Effacer l’emplacement"
|
||||
|
||||
#: includes/facets/date_range.php:23
|
||||
msgid "Date"
|
||||
msgstr "Date"
|
||||
|
||||
#: includes/facets/sort.php:154
|
||||
msgid "Date (Newest)"
|
||||
msgstr "Date (plus récent)"
|
||||
|
||||
#: includes/facets/sort.php:161
|
||||
msgid "Date (Oldest)"
|
||||
msgstr "Date (la plus ancienne)"
|
||||
|
||||
#: includes/facets/proximity.php:285
|
||||
msgid "Distance"
|
||||
msgstr "Distance"
|
||||
|
||||
#: includes/facets/date_range.php:29
|
||||
msgid "End Date"
|
||||
msgstr "Date de fin"
|
||||
|
||||
#: includes/facets/search.php:20
|
||||
msgid "Enter keywords"
|
||||
msgstr "Rechercher par nom"
|
||||
|
||||
#: includes/facets/proximity.php:64
|
||||
msgid "Enter location"
|
||||
msgstr "Entrez la localisation"
|
||||
|
||||
#: includes/facets/autocomplete.php:157
|
||||
msgid "Enter {n} or more characters"
|
||||
msgstr ""
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:502
|
||||
msgid "Featured"
|
||||
msgstr ""
|
||||
|
||||
#: includes/facets/number_range.php:32 includes/facets/autocomplete.php:64
|
||||
msgid "Go"
|
||||
msgstr "Aller"
|
||||
|
||||
#: includes/class-display.php:175
|
||||
msgid "Go to next page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-display.php:174
|
||||
msgid "Go to page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-display.php:176
|
||||
msgid "Go to previous page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:496
|
||||
msgid "In Stock"
|
||||
msgstr "En stock"
|
||||
|
||||
#: includes/facets/autocomplete.php:156
|
||||
msgid "Loading"
|
||||
msgstr ""
|
||||
|
||||
#: includes/facets/number_range.php:29
|
||||
msgid "Max"
|
||||
msgstr "Max"
|
||||
|
||||
#: includes/facets/number_range.php:26
|
||||
msgid "Min"
|
||||
msgstr "Min"
|
||||
|
||||
#: includes/integrations/acf/acf.php:293
|
||||
msgid "No"
|
||||
msgstr "Non"
|
||||
|
||||
#: includes/facets/autocomplete.php:97 includes/facets/autocomplete.php:158
|
||||
msgid "No results"
|
||||
msgstr "Aucun résultat"
|
||||
|
||||
#: includes/class-display.php:194 includes/facets/fselect.php:87
|
||||
msgid "No results found"
|
||||
msgstr "Aucun résultat trouvé"
|
||||
|
||||
#: includes/facets/number_range.php:23
|
||||
msgid "Number"
|
||||
msgstr "Nombre"
|
||||
|
||||
#: includes/class-renderer.php:512
|
||||
msgid "of"
|
||||
msgstr "de"
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:499
|
||||
msgid "On Sale"
|
||||
msgstr "Promo"
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:496
|
||||
msgid "Out of Stock"
|
||||
msgstr "Rupture de stock"
|
||||
|
||||
#: includes/class-renderer.php:569
|
||||
msgid "Per page"
|
||||
msgstr "Par page"
|
||||
|
||||
#: includes/facets/slider.php:20 includes/facets/reset.php:15
|
||||
msgid "Reset"
|
||||
msgstr "Réinitialiser"
|
||||
|
||||
#: includes/facets/fselect.php:86
|
||||
msgid "Search"
|
||||
msgstr "Rechercher"
|
||||
|
||||
#: includes/facets/checkboxes.php:131 includes/facets/hierarchy.php:161
|
||||
msgid "See less"
|
||||
msgstr "Voir moins"
|
||||
|
||||
#: includes/facets/hierarchy.php:160
|
||||
msgid "See more"
|
||||
msgstr "Voir plus"
|
||||
|
||||
#: includes/facets/checkboxes.php:130
|
||||
msgid "See {num} more"
|
||||
msgstr "Voir {num} plus"
|
||||
|
||||
#: includes/facets/sort.php:136
|
||||
msgid "Sort by"
|
||||
msgstr "Trier par"
|
||||
|
||||
#: includes/facets/date_range.php:26
|
||||
msgid "Start Date"
|
||||
msgstr "Date de début"
|
||||
|
||||
#: includes/facets/autocomplete.php:61
|
||||
msgid "Start typing"
|
||||
msgstr "Commencer à taper"
|
||||
|
||||
#: includes/facets/sort.php:140
|
||||
msgid "Title (A-Z)"
|
||||
msgstr "Titre (A-Z)"
|
||||
|
||||
#: includes/facets/sort.php:147
|
||||
msgid "Title (Z-A)"
|
||||
msgstr "Titre (Z-A)"
|
||||
|
||||
#: includes/facets/rating.php:112
|
||||
msgid "Undo"
|
||||
msgstr "Annuler"
|
||||
|
||||
#: includes/integrations/acf/acf.php:293
|
||||
msgid "Yes"
|
||||
msgstr "Oui"
|
||||
|
||||
#: includes/facets/fselect.php:85
|
||||
msgid "{n} selected"
|
||||
msgstr "{n} sélectionné"
|
||||
BIN
wp/wp-content/plugins/facetwp/languages/fwp-front-it_IT.mo
Normal file
185
wp/wp-content/plugins/facetwp/languages/fwp-front-it_IT.po
Normal file
@@ -0,0 +1,185 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: FacetWP (front)\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-02-21 14:01+0000\n"
|
||||
"PO-Revision-Date: 2022-08-15 12:41+0000\n"
|
||||
"Last-Translator: Matt Gibbs <hello@facetwp.com>\n"
|
||||
"Language-Team: Italian\n"
|
||||
"Language: it_IT\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Loco https://localise.biz/\n"
|
||||
"X-Loco-Version: 2.5.0; wp-5.6\n"
|
||||
"X-Poedit-SourceCharset: UTF-8\n"
|
||||
|
||||
#: includes/facets/rating.php:111
|
||||
msgid "& up"
|
||||
msgstr "& up"
|
||||
|
||||
#: includes/facets/fselect.php:37 includes/facets/fselect.php:80
|
||||
#: includes/facets/dropdown.php:35 includes/facets/hierarchy.php:26
|
||||
msgid "Any"
|
||||
msgstr "Qualsiasi"
|
||||
|
||||
#: includes/facets/date_range.php:282
|
||||
msgid "Clear"
|
||||
msgstr "Cancella"
|
||||
|
||||
#: includes/facets/proximity.php:180
|
||||
msgid "Clear location"
|
||||
msgstr "Cancella località"
|
||||
|
||||
#: includes/facets/date_range.php:23
|
||||
msgid "Date"
|
||||
msgstr "Data"
|
||||
|
||||
#: includes/facets/sort.php:154
|
||||
msgid "Date (Newest)"
|
||||
msgstr "Data (più recente)"
|
||||
|
||||
#: includes/facets/sort.php:161
|
||||
msgid "Date (Oldest)"
|
||||
msgstr "Data (più vecchia)"
|
||||
|
||||
#: includes/facets/proximity.php:285
|
||||
msgid "Distance"
|
||||
msgstr "Distanza"
|
||||
|
||||
#: includes/facets/date_range.php:29
|
||||
msgid "End Date"
|
||||
msgstr "Data Fine"
|
||||
|
||||
#: includes/facets/search.php:20
|
||||
msgid "Enter keywords"
|
||||
msgstr "Inserisci parole chiave"
|
||||
|
||||
#: includes/facets/proximity.php:64
|
||||
msgid "Enter location"
|
||||
msgstr "Inserisci posizione"
|
||||
|
||||
#: includes/facets/autocomplete.php:157
|
||||
msgid "Enter {n} or more characters"
|
||||
msgstr ""
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:502
|
||||
msgid "Featured"
|
||||
msgstr ""
|
||||
|
||||
#: includes/facets/number_range.php:32 includes/facets/autocomplete.php:64
|
||||
msgid "Go"
|
||||
msgstr "Vai"
|
||||
|
||||
#: includes/class-display.php:175
|
||||
msgid "Go to next page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-display.php:174
|
||||
msgid "Go to page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/class-display.php:176
|
||||
msgid "Go to previous page"
|
||||
msgstr ""
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:496
|
||||
msgid "In Stock"
|
||||
msgstr "Disponibile"
|
||||
|
||||
#: includes/facets/autocomplete.php:156
|
||||
msgid "Loading"
|
||||
msgstr ""
|
||||
|
||||
#: includes/facets/number_range.php:29
|
||||
msgid "Max"
|
||||
msgstr "Max"
|
||||
|
||||
#: includes/facets/number_range.php:26
|
||||
msgid "Min"
|
||||
msgstr "Min"
|
||||
|
||||
#: includes/integrations/acf/acf.php:293
|
||||
msgid "No"
|
||||
msgstr "No"
|
||||
|
||||
#: includes/facets/autocomplete.php:97 includes/facets/autocomplete.php:158
|
||||
msgid "No results"
|
||||
msgstr "Nessun risultato"
|
||||
|
||||
#: includes/class-display.php:194 includes/facets/fselect.php:87
|
||||
msgid "No results found"
|
||||
msgstr "Nessun risultato trovato"
|
||||
|
||||
#: includes/facets/number_range.php:23
|
||||
msgid "Number"
|
||||
msgstr "Numero"
|
||||
|
||||
#: includes/class-renderer.php:512
|
||||
msgid "of"
|
||||
msgstr "di"
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:499
|
||||
msgid "On Sale"
|
||||
msgstr "Offerta"
|
||||
|
||||
#: includes/integrations/woocommerce/woocommerce.php:496
|
||||
msgid "Out of Stock"
|
||||
msgstr "Esaurito"
|
||||
|
||||
#: includes/class-renderer.php:569
|
||||
msgid "Per page"
|
||||
msgstr "Per pagina"
|
||||
|
||||
#: includes/facets/slider.php:20 includes/facets/reset.php:15
|
||||
msgid "Reset"
|
||||
msgstr "Reset"
|
||||
|
||||
#: includes/facets/fselect.php:86
|
||||
msgid "Search"
|
||||
msgstr "Cerca"
|
||||
|
||||
#: includes/facets/checkboxes.php:131 includes/facets/hierarchy.php:161
|
||||
msgid "See less"
|
||||
msgstr "Vedi di meno"
|
||||
|
||||
#: includes/facets/hierarchy.php:160
|
||||
msgid "See more"
|
||||
msgstr "Vedi di più"
|
||||
|
||||
#: includes/facets/checkboxes.php:130
|
||||
msgid "See {num} more"
|
||||
msgstr "Vedi altri {num}"
|
||||
|
||||
#: includes/facets/sort.php:136
|
||||
msgid "Sort by"
|
||||
msgstr "Ordina per"
|
||||
|
||||
#: includes/facets/date_range.php:26
|
||||
msgid "Start Date"
|
||||
msgstr "Data Inizio"
|
||||
|
||||
#: includes/facets/autocomplete.php:61
|
||||
msgid "Start typing"
|
||||
msgstr "Scrivi l'indirizzo o la città qui"
|
||||
|
||||
#: includes/facets/sort.php:140
|
||||
msgid "Title (A-Z)"
|
||||
msgstr "Titoli (A-Z)"
|
||||
|
||||
#: includes/facets/sort.php:147
|
||||
msgid "Title (Z-A)"
|
||||
msgstr "Titoli (Z-A)"
|
||||
|
||||
#: includes/facets/rating.php:112
|
||||
msgid "Undo"
|
||||
msgstr "Annulla"
|
||||
|
||||
#: includes/integrations/acf/acf.php:293
|
||||
msgid "Yes"
|
||||
msgstr "Sì"
|
||||
|
||||
#: includes/facets/fselect.php:85
|
||||
msgid "{n} selected"
|
||||
msgstr "{n} selezionato"
|
||||