Commit inicial - WordPress Análisis de Precios Unitarios

- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
<?php
namespace WPDRMS\ASP\Api\Rest0;
use WP_Error, WP_REST_Request, WPDRMS\ASP\Patterns\SingletonTrait;
class Rest {
use SingletonTrait;
/**
* The Rest Namespace with version code
*
* @var string
*/
private $namespace = 'ajax-search-pro/v0';
/**
* Route information
*
* @var string[][]
*/
private $routes = array(
'/woo_search' => array(
'methods' => 'GET',
'handler' => 'RouteWooSearch'
),
'/search' => array(
'methods' => 'GET',
'handler' => 'RouteSearch'
)
);
/**
* Init process
*
* @return bool
*/
function init(): bool {
if ( defined('ASP_DISABLE_REST') && ASP_DISABLE_REST ) {
return false;
} else {
add_action('rest_api_init', array($this, 'registerRoutes'));
}
return true;
}
/**
* Registering the routes
*/
function registerRoutes() {
foreach ( $this->routes as $route => $data ) {
register_rest_route($this->namespace, $route, array(
'methods' => $data['methods'],
'callback' => array( $this, 'route'),
'permission_callback' => '__return_true'
));
}
}
/**
* Handles the request route
*
* @param WP_REST_Request $request
* @return array|WP_Error
*/
function route( WP_REST_Request $request ) {
$route = str_replace($this->namespace . '/', '', $request->get_route());
if ( isset($this->routes[$route]) ) {
if ( !$this->securityCheck( $request ) ) {
return new WP_Error(
'401',
esc_html__( 'Not Authorized', 'ajax-search-pro' ),
array( 'status' => 401 )
);
} else {
// PHP 7.4 support, the $class name has to be put into a variable first
$class = __NAMESPACE__ . '\\' . $this->routes[$route]['handler'];
return (new $class)->handle($request);
}
} else {
return new WP_Error(
'400',
esc_html__( 'Route error', 'ajax-search-pro' ),
array( 'status' => 400 )
);
}
}
/** @noinspection PhpUnusedParameterInspection */
private function securityCheck($request ): bool {
return true;
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace WPDRMS\ASP\API\REST0;
use WP_REST_Request;
interface RouteInterface {
public function handle( WP_REST_Request $request );
}

View File

@@ -0,0 +1,75 @@
<?php
namespace WPDRMS\ASP\Api\Rest0;
use ASP_Post;
use WP_Post;
use WP_REST_Request;
use WPDRMS\ASP\Query\SearchQuery;
class RouteSearch implements RouteInterface {
private $args, $params;
/**
* Handles the Search Route
*
* @param WP_REST_Request $request
* @return WP_Post[]|ASP_Post[]
*/
function handle( WP_REST_Request $request ): array {
$defaults = $this->args = array(
's' => '',
'id' => -1,
'is_wp_json' => true,
'posts_per_page' => 9999,
);
$this->params = $request->get_params();
foreach ( $defaults as $k => $v ) {
if ( isset($this->params[ $k ]) && $this->params[ $k ] !== null ) {
$this->args[ $k ] = $this->params[ $k ];
}
}
$search_id = $this->args['id'];
// id does not exist in SearchQueryArgs constructor
unset($this->args['id']);
$this->getPostTypeArgs();
$this->getTaxonomyArgs();
$this->getPostMetaArgs();
$this->getExclusionArgs();
$this->getInclusionArgs();
$this->args = apply_filters('asp_rest_search_query_args', $this->args, $search_id, $request);
$asp_query = new SearchQuery($this->args, $search_id);
return $asp_query->posts;
}
private function getTaxonomyArgs(): void {
// Post taxonomy filter
if ( isset($this->params['post_tax_filter']) ) {
$this->args['post_tax_filter'] = array();
foreach ( $this->params['post_tax_filter'] as $taxonomy => $terms ) {
if ( taxonomy_exists($taxonomy) && is_array($terms) && count($terms) ) {
$this->args['post_tax_filter'][] = array(
'taxonomy' => $taxonomy,
'include' => array_map('intval', $terms),
'allow_empty' => false,
);
}
}
}
}
private function getPostTypeArgs(): void {
}
private function getPostMetaArgs(): void {
}
private function getExclusionArgs(): void {
}
private function getInclusionArgs(): void {
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace WPDRMS\ASP\Api\Rest0;
use WP_REST_Request;
use WPDRMS\ASP\Query\SearchQuery;
class RouteWooSearch implements RouteInterface {
function handle( WP_REST_Request $request): array {
$defaults = $args = array(
's' => '',
'id' => -1,
'is_wp_json' => true,
'posts_per_page' => 9999
);
foreach ($defaults as $k => $v) {
$param = $request->get_param($k);
if ( $param !== null ) {
$args[$k] = $param;
}
}
if ( $args['id'] == -1 ) {
// Fetch the search ID, which is probably the WooCommerce search
foreach (wd_asp()->instances->get() as $instance) {
if ( in_array('product', $instance['data']['customtypes']) ) {
$args['id'] = $instance['id'];
break;
}
}
}
// No search was found with products enabled, set it explicitly
if ( $args['id'] == -1 ) {
$args['post_type'] = array('product');
}
$search_id = $args['id'];
// id does not exist in SearchQueryArgs constructor
unset( $args['id']);
$args = apply_filters('asp_rest_woo_search_query_args', $args, $search_id, $request);
$asp_query = new SearchQuery($args, $search_id);
return $asp_query->posts;
}
}

View File

@@ -0,0 +1,84 @@
<?php /** @noinspection PhpLanguageLevelInspection */
namespace WPDRMS\ASP\Api\Swagger;
use OpenApi\Attributes as OA;
#[OA\Schema]
class ASP_Data {
#[OA\Property]
/**
* Title including advanced title field data
*/
public string $title;
#[OA\Property]
/**
* Result ID
*/
public int $id;
#[OA\Property]
/**
* Result blog ID
*/
public ?int $blogid;
#[OA\Property]
/**
* Result blog ID
*/
public string $date;
#[OA\Property]
/**
* Content including advanced title field data
*/
public string $content;
#[OA\Property]
/**
* Excerpt data
*/
public ?string $excerpt;
#[OA\Property]
/**
* Post type
*/
public ?string $post_type;
#[OA\Property]
/**
* Content type
*
* @var "pagepost"|"user"|"term"|"blog"|"bp_group"|"bp_activity"|"comment"
*/
public string $content_type;
#[OA\Property]
/**
* Author
*/
public ?string $author;
#[OA\Property]
/**
* URL of the search result when search ID is not set
*/
public ?string $asp_guid;
#[OA\Property]
/**
* URL of the search result when search ID is set
*/
public ?string $url;
#[OA\Property]
/**
* Image URL when search ID is set
*
* @var string
*/
public ?string $image;
}

View File

@@ -0,0 +1,54 @@
<?php /** @noinspection PhpLanguageLevelInspection */
/**
* Swagger related stuff, to make it easy all is in this directory.
* This code is not used anywhere, it's only for swagger generator.
*/
namespace WPDRMS\ASP\Api\Swagger;
use OpenApi\Attributes as OA;
#[OA\Info(
version: "1.0",
title: "Ajax Search Pro for WordPress"
)]
final class Routes {
#[OA\Get(
path: '/wp-json/ajax-search-pro/v0/search',
description: 'Generic search',
tags: ['search'],
parameters: [
new OA\Parameter(name: 's', in: 'query', required: true, schema: new OA\Schema(type: 'string')),
new OA\Parameter(name: 'id', in: 'query', required: false, schema: new OA\Schema(type: 'integer')),
],
responses: [
new OA\Response(
response: 200,
description: 'Results array.',
content: new OA\JsonContent(type: 'array', items: new OA\Items(type: 'object', ref: WP_Post_ASP::class))
),
]
)]
public function searchRoute() {}
#[OA\Get(
path: '/wp-json/ajax-search-pro/v0/woo_search',
description: 'WooCommerce Specific Search',
tags: ['woocommerce'],
parameters: [
new OA\Parameter(name: 's', in: 'query', required: true, schema: new OA\Schema(type: 'string')),
new OA\Parameter(name: 'id', in: 'query', required: false, schema: new OA\Schema(type: 'integer')),
],
responses: [
new OA\Response(
response: 200,
description: 'Product results array.',
content: new OA\JsonContent(type: 'array', items: new OA\Items(type: 'object', ref: WP_Post_ASP::class))
),
]
)]
public function wooSearchRoute() {}
}

View File

@@ -0,0 +1,239 @@
<?php
namespace WPDRMS\ASP\Api\Swagger;
use OpenApi\Attributes as OA;
#[OA\Schema]
final class WP_Post_ASP {
#[OA\Property]
/**
* Post ID.
*
* @since 3.5.0
* @var int
*/
public $ID;
#[OA\Property]
/**
* ID of post author.
*
* A numeric string, for compatibility reasons.
*
* @since 3.5.0
* @var string
*/
public $post_author = 0;
#[OA\Property]
/**
* The post's local publication time.
*
* @since 3.5.0
* @var string
*/
public $post_date = '0000-00-00 00:00:00';
#[OA\Property]
/**
* The post's GMT publication time.
*
* @since 3.5.0
* @var string
*/
public $post_date_gmt = '0000-00-00 00:00:00';
#[OA\Property]
/**
* The post's content.
*
* @since 3.5.0
* @var string
*/
public $post_content = '';
#[OA\Property]
/**
* The post's title.
*
* @since 3.5.0
* @var string
*/
public $post_title = '';
#[OA\Property]
/**
* The post's excerpt.
*
* @since 3.5.0
* @var string
*/
public $post_excerpt = '';
#[OA\Property]
/**
* The post's status.
*
* @since 3.5.0
* @var string
*/
public $post_status = 'publish';
#[OA\Property]
/**
* Whether comments are allowed.
*
* @since 3.5.0
* @var string
*/
public $comment_status = 'open';
#[OA\Property]
/**
* Whether pings are allowed.
*
* @since 3.5.0
* @var string
*/
public $ping_status = 'open';
#[OA\Property]
/**
* The post's password in plain text.
*
* @since 3.5.0
* @var string
*/
public $post_password = '';
#[OA\Property]
/**
* The post's slug.
*
* @since 3.5.0
* @var string
*/
public $post_name = '';
#[OA\Property]
/**
* URLs queued to be pinged.
*
* @since 3.5.0
* @var string
*/
public $to_ping = '';
#[OA\Property]
/**
* URLs that have been pinged.
*
* @since 3.5.0
* @var string
*/
public $pinged = '';
#[OA\Property]
/**
* The post's local modified time.
*
* @since 3.5.0
* @var string
*/
public $post_modified = '0000-00-00 00:00:00';
#[OA\Property]
/**
* The post's GMT modified time.
*
* @since 3.5.0
* @var string
*/
public $post_modified_gmt = '0000-00-00 00:00:00';
#[OA\Property]
/**
* A utility DB field for post content.
*
* @since 3.5.0
* @var string
*/
public $post_content_filtered = '';
#[OA\Property]
/**
* ID of a post's parent post.
*
* @since 3.5.0
* @var int
*/
public $post_parent = 0;
#[OA\Property]
/**
* The unique identifier for a post, not necessarily a URL, used as the feed GUID.
*
* @since 3.5.0
* @var string
*/
public $guid = '';
#[OA\Property]
/**
* A field used for ordering posts.
*
* @since 3.5.0
* @var int
*/
public $menu_order = 0;
#[OA\Property]
/**
* The post's type, like post or page.
*
* @since 3.5.0
* @var string
*/
public $post_type = 'post';
#[OA\Property]
/**
* An attachment's mime type.
*
* @since 3.5.0
* @var string
*/
public $post_mime_type = '';
#[OA\Property]
/**
* Cached comment count.
*
* A numeric string, for compatibility reasons.
*
* @since 3.5.0
* @var string
*/
public $comment_count = 0;
#[OA\Property]
/**
* Stores the post object's sanitization level.
*
* Does not correspond to a DB field.
*
* @since 3.5.0
* @var string
*/
public $filter;
#[OA\Property]
/**
* Additional Search Data added by Ajax Search Pro
*
* @var ASP_Data
*/
public ASP_Data $asp_data;
}

View File

@@ -0,0 +1,9 @@
<?php
namespace WPDRMS\ASP\Asset;
interface AssetInterface {
public function register(): void;
public function deregister(): void;
}

View File

@@ -0,0 +1,33 @@
<?php
namespace WPDRMS\ASP\Asset;
if ( !defined('ABSPATH') ) {
die('-1');
}
/**
* Common functions for CSS/Script/Font asset managers
*/
class AssetManager {
/**
* Checks if any compatibility issues should be considered in the current request
*
* @return bool
*/
protected function conflict(): bool {
// phpcs:disable
// Widgets screen
return (
wp_is_json_request() || // Widgets screen
wp_doing_ajax() || // Ajax Resuests in General
isset($_GET['et_fb']) || // Divi frontend editor
isset($_GET['vcv-ajax']) || // Visual Composer Frontend editor
isset($_GET['fl_builder']) || // Beaver Builder Frontend editor
isset($_GET['elementor-preview']) || // Elementor Frontend
isset($_GET['breakdance']) || isset($_GET['_breakdance_doing_ajax']) || // Breakdance editor
isset($_GET['wc-ajax']) || // WooCommerce Custom Ajax Request
( defined('BRICKS_VERSION') && isset($_GET['bricks']) ) || // Bricks Builder
( isset($_GET['action']) && $_GET['action'] == 'elementor' ) // Elementor Parts editor
);
}
}

View File

@@ -0,0 +1,136 @@
<?php
namespace WPDRMS\ASP\Asset\Css;
use WPDRMS\ASP\Asset\GeneratorInterface;
use WPDRMS\ASP\Asset\Script\Requirements;
use WPDRMS\ASP\Utils\FileManager;
use WPDRMS\ASP\Utils\Str;
use WPDRMS\ASP\Utils\Css;
defined('ABSPATH') or die("You can't access this file directly.");
if ( !class_exists(__NAMESPACE__ . '\Generator') ) {
class Generator implements GeneratorInterface {
private
$basic_flags_string = '',
$minify;
function __construct( $minify = false ) {
$this->minify = $minify;
}
function generate(): string {
if ( wd_asp()->instances->exists() ) {
$basic_css = $this->generateGlobal();
$instance_css_arr = $this->generateInstances();
return $this->saveFiles($basic_css, $instance_css_arr);
}
return '';
}
function verifyFiles(): bool {
if (
!file_exists(wd_asp()->cache_path . $this->filename('basic')) ||
!file_exists(wd_asp()->cache_path . $this->filename('instances')) ||
@filesize(wd_asp()->cache_path . $this->filename('instances')) < 1025
) {
return false;
} else {
return true;
}
}
function filename( $handle ) {
$media_flags = get_site_option('asp_css_flags', array(
'basic' => ''
));
$flag = Str::anyToString($media_flags['basic']);
$files = array(
'basic' => 'style.basic'.$flag.'.css',
'wpdreams-asp-basics' => 'style.basic'.$flag.'.css',
'instances' => 'style.instances'.$flag.'.css',
'wpdreams-ajaxsearchpro-instances' => 'style.instances'.$flag.'.css'
);
return $files[$handle] ?? 'search' . $handle . '.css';
}
private function generateGlobal() {
// Basic CSS
ob_start();
echo file_get_contents(ASP_CSS_PATH . "/global/woocommerce.css");
echo file_get_contents(ASP_CSS_PATH . "/global/advanced-result-fields.css");
include(ASP_PATH . "/css/style.basic.css.php");
$basic_css = ob_get_clean();
$unused_assets = Requirements::getUnusedAssets(false);
foreach ( $unused_assets['internal'] as $flag ) {
// Remove unneccessary CSS
$basic_css = asp_get_outer_substring($basic_css, '/*[' . $flag . ']*/');
$this->basic_flags_string .= '-' . substr($flag, 0, 2);
}
foreach ( $unused_assets['external'] as $flag ) {
// Remove unneccessary CSS
$basic_css = asp_get_outer_substring($basic_css, '/*[' . $flag . ']*/');
$this->basic_flags_string .= '-' . substr($flag, 0, 2);
}
return $basic_css;
}
private function generateInstances(): array {
// Instances CSS
$css_arr = array();
foreach (wd_asp()->instances->get() as $s) {
// $style and $id needed in the include
$style = &$s['data'];
$id = $s['id'];
ob_start();
include(ASP_PATH . "/css/style.css.php");
$out = ob_get_contents();
$css_arr[$id] = $out;
ob_end_clean();
}
return $css_arr;
}
private function saveFiles($basic_css, $instance_css_arr): string {
// Too big, disabled...
$css = implode(" ", $instance_css_arr);
// Individual CSS rules by search ID
foreach ($instance_css_arr as $sid => &$c) {
if ( $this->minify ) {
$c = Css::Minify($c);
}
FileManager::instance()->write(wd_asp()->cache_path . "search" . $sid . ".css", $c);
}
// Save the style instances file nevertheless, even if async enabled
if ( $this->minify ) {
$css = Css::Minify($css);
$basic_css = Css::Minify($basic_css);
}
FileManager::instance()->write(wd_asp()->cache_path . "style.instances.css", $basic_css . $css);
FileManager::instance()->write(wd_asp()->cache_path . "style.basic.css", $basic_css);
if ( $this->basic_flags_string != '' ) {
FileManager::instance()->write(wd_asp()->cache_path . "style.basic" . $this->basic_flags_string . ".css", $basic_css);
FileManager::instance()->write(wd_asp()->cache_path . "style.instances" . $this->basic_flags_string . ".css", $basic_css . $css);
}
update_site_option('asp_css_flags', array(
'basic' => $this->basic_flags_string
));
update_site_option("asp_media_query", asp_gen_rnd_str());
update_site_option('asp_css', array(
'basic' => $basic_css,
'instances' => $instance_css_arr
));
return $basic_css . $css;
}
}
}

View File

@@ -0,0 +1,141 @@
<?php
namespace WPDRMS\ASP\Asset\Css;
use WPDRMS\ASP\Asset\AssetManager;
use WPDRMS\ASP\Asset\ManagerInterface;
use WPDRMS\ASP\Patterns\SingletonTrait;
use WPDRMS\ASP\Utils\Html;
if ( !defined('ABSPATH') ) {
die('-1');
}
class Manager extends AssetManager implements ManagerInterface {
use SingletonTrait;
private
$method, // file, optimized, inline
$force_inline = false, // When "file" is the $method, but the files can't be created
$media_query,
$minify;
public
$generator;
function __construct() {
$comp_settings = wd_asp()->o['asp_compatibility'];
$this->method = $comp_settings['css_loading_method']; // optimized, inline, file
$this->minify = $comp_settings['css_minify'];
$this->media_query = get_site_option("asp_media_query", "defncss");
$this->generator = new Generator( $this->minify );
$this->adjustOptionsForCompatibility();
if ( $this->method == 'optimized' || $this->method == 'file' ) {
if ( !$this->generator->verifyFiles() ) {
$this->generator->generate();
if ( !$this->generator->verifyFiles() ) {
// Swap to inline if the files were not generated
$this->force_inline = true;
}
}
}
/**
* Call order:
* wp_enqueue_scripts -> enqueue()
* wp_head -> headerStartBuffer() -> start buffer
* shutdown -> print() -> end buffer trigger
*/
}
/**
* Called at wp_enqueue_scripts
*/
function enqueue( $force = false ): void {
if ( $force || $this->method == 'file' ) {
if ( !$this->generator->verifyFiles() ) {
$this->generator->generate();
if ( !$this->generator->verifyFiles() ) {
$this->force_inline = true;
}
}
// Still enqueue to the head, but the file was not possible to create.
if ( $this->force_inline ) {
add_action('wp_head', function(){
echo $this->getBasic();
echo $this->getInstances();
}, 999);
} else {
wp_enqueue_style('asp-instances', $this->url('instances'), array(), $this->media_query);
}
}
}
// asp_ob_end
function injectToBuffer($buffer, $instances): string {
if ( $this->method != 'file' ) {
$output = $this->getBasic();
$output .= $this->getInstances( $instances );
Html::inject($output, $buffer);
}
return $buffer;
}
/**
* Called at shutdown, after asp_ob_end, checks if the items were printed
*/
function printInline( $instances = array() ): void {
if ( $this->method != 'file' ) {
echo $this->getBasic();
echo $this->getInstances($instances);
}
}
private function getBasic(): string {
$output = '';
if ( $this->method == 'inline' || $this->force_inline ) {
$css = get_site_option('asp_css', array('basic' => '', 'instances' => array()));
if ( $css['basic'] != '' ) {
$output .= "<style id='asp-basic'>" . $css['basic'] . "</style>";
}
} else if ( $this->method == 'optimized' ) {
$output = '<link rel="stylesheet" id="asp-basic" href="' . $this->url('basic') . '?mq='.$this->media_query.'" media="all" />';
}
return $output;
}
private function adjustOptionsForCompatibility() {
if ( defined('SiteGround_Optimizer\VERSION') ) {
// SiteGround Optimized CSS combine does not pick up the CSS files when injected
if ( $this->method == 'optimized' ) {
$this->method = 'inline';
}
}
if ( $this->conflict() ) {
$this->method = 'file';
}
}
private function getInstances( $instances = false ): string {
$css = get_site_option('asp_css', array('basic' => '', 'instances' => array()));
$output = '';
$instances = $instances === false ? array_keys($css['instances']) : $instances;
foreach ($instances as $search_id) {
if ( isset($css['instances'][$search_id]) && $css['instances'][$search_id] != '' ) {
$output .= "<style id='asp-instance-$search_id'>" . $css['instances'][$search_id] . "</style>";
}
}
return $output;
}
private function url( $handle ): string {
if ( '' != $file = $this->generator->filename($handle) ) {
return wd_asp()->cache_url . $file;
} else {
return '';
}
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace WPDRMS\ASP\Asset\Font;
use WPDRMS\ASP\Asset\GeneratorInterface;
defined('ABSPATH') or die("You can't access this file directly.");
if ( !class_exists(__NAMESPACE__ . '\Generator') ) {
class Generator implements GeneratorInterface {
function generate(): array {
$imports = array();
$font_sources = array("inputfont", "descfont", "titlefont", 'fe_sb_font',
"authorfont", "datefont", "showmorefont", "groupfont",
"exsearchincategoriestextfont", "groupbytextfont", "settingsdropfont",
"prestitlefont", "presdescfont", "pressubtitlefont", "search_text_font");
foreach (wd_asp()->instances->get() as $instance) {
foreach($font_sources as $fs) {
if (
isset($instance['data']["import-".$fs]) &&
!in_array(trim($instance['data']["import-".$fs]), $imports)
) {
$imports[] = trim($instance['data']["import-" . $fs]);
}
}
}
foreach ( $imports as $ik => $im ) {
if ( $im == '' ) {
unset($imports[$ik]);
}
}
$imports = apply_filters('asp_custom_fonts', $imports);
$fonts = array();
foreach ($imports as $import) {
$import = trim(str_replace(array("@import url(", ");", "https:", "http:"), "", $import));
$import = trim(str_replace("//fonts.googleapis.com/css?family=", "", $import));
if ( $import != '' ) {
$fonts[] = $import;
}
}
return $fonts;
}
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace WPDRMS\ASP\Asset\Font;
/* Prevent direct access */
use WPDRMS\ASP\Asset\AssetManager;
use WPDRMS\ASP\Asset\ManagerInterface;
use WPDRMS\ASP\Patterns\SingletonTrait;
use WPDRMS\ASP\Utils\Html;
if ( !defined('ABSPATH') ) {
die('-1');
}
/**
* Manager for the Font assets
*/
class Manager extends AssetManager implements ManagerInterface {
use SingletonTrait;
public function enqueue( $force = false ): void {}
public function injectToBuffer( string $buffer, $instances = false ): string {
if ( !$this->conflict() ) {
$output = $this->get();
if ( $output !== '' ) {
Html::inject($output, $buffer);
}
}
return $buffer;
}
public function printInline( $instances = array() ): void {
if ( !$this->conflict() ) {
echo $this->get();
}
}
private function get(): string {
$comp_options = wd_asp()->o['asp_compatibility'];
$out = '';
if ( $comp_options['load_google_fonts'] == 1 ) {
$generator = new Generator();
$fonts = $generator->generate();
if ( count($fonts) > 0 ) {
$stored_fonts = get_site_option('asp_fonts', array());
$key = md5(implode('|', $fonts));
$fonts_css = '';
if ( isset($stored_fonts[ $key ]) ) {
$fonts_css = $stored_fonts[ $key ];
} else {
$fonts_request = wp_safe_remote_get('https://fonts.googleapis.com/css?family=' . implode('|', $fonts) . '&display=swap');
if ( !is_wp_error($fonts_request) ) {
$fonts_css = wp_remote_retrieve_body($fonts_request);
if ( $fonts_css != '' ) {
$stored_fonts[ $key ] = $fonts_css;
update_site_option('asp_fonts', $stored_fonts);
}
}
}
if ( !is_wp_error($fonts_css) && $fonts_css != '' ) {
// Do NOT preload the fonts - it will give worst PagesPeed score. Preconnect is sufficient.
$out = '
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<style>
' . $fonts_css . '
</style>';
} else {
$out = '
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="preload" as="style" href="//fonts.googleapis.com/css?family=' . implode('|', $fonts) . '&display=swap" />
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=' . implode('|', $fonts) . '&display=swap" media="all" />
';
}
}
}
return $out;
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace WPDRMS\ASP\Asset;
/* Prevent direct access */
defined('ABSPATH') or die("You can't access this file directly.");
interface GeneratorInterface {
function generate();
}

View File

@@ -0,0 +1,135 @@
<?php
namespace WPDRMS\ASP\Asset;
use WPDRMS\ASP\Misc\OutputBuffer;
use WPDRMS\ASP\Patterns\SingletonTrait;
/* Prevent direct access */
defined('ABSPATH') or die("You can't access this file directly.");
class Manager {
use SingletonTrait;
private
$instances = array();
/**
* hook: wp_enqueue_scripts
*/
function enqueue() {
if ( $this->shouldLoadCss() ) {
Css\Manager::instance()->enqueue();
}
if ( $this->shouldLoadJs() ) {
Script\Manager::instance()->enqueue();
}
}
/**
* hook: wp_print_footer_scripts, priority 6
* hook: admin_print_footer_scripts, priority 6
*/
function onPluginFooter() {
// Needed to enqueue the internal (jquery, UI) requirements
$this->getVisibleSearchIds();
if ( count($this->instances) ) {
if ( $this->shouldLoadJs() ) {
Script\Manager::instance()->earlyFooterEnqueue($this->instances);
}
}
}
/**
* Classic script enqueue for the plugin backend
*
* hook: admin_print_footer_scripts, priority 7
*/
function onPluginBackendFooter() {
Script\Manager::instance()->enqueue( true );
}
// asp_ob_end
function injectToBuffer($buffer) {
$this->getVisibleSearchIds($buffer);
if ( count($this->instances) ) {
if ( $this->shouldLoadCss() ) {
$buffer = Css\Manager::instance()->injectToBuffer($buffer, $this->instances);
$buffer = Font\Manager::instance()->injectToBuffer($buffer);
}
if ( $this->shouldLoadJs() ) {
$buffer = Script\Manager::instance()->injectToBuffer($buffer, $this->instances);
}
}
return $buffer;
}
/**
* Called at shutdown, after asp_ob_end, checks if the items were printed
*/
function printBackup() {
$this->getVisibleSearchIds();
if ( count($this->instances) && !OutputBuffer::getInstance()->obFound() ) {
if ( $this->shouldLoadCss() ) {
Css\Manager::instance()->printInline($this->instances);
Font\Manager::instance()->printInline();
}
if ( $this->shouldLoadJs() ) {
Script\Manager::instance()->printInline($this->instances);
}
}
}
public function shouldLoadCss(): bool {
return
wd_asp()->instances->exists() &&
!apply_filters('asp_load_css_js', false) &&
!apply_filters('asp_load_css', false);
}
public function shouldLoadJs(): bool {
return
wd_asp()->instances->exists() &&
!apply_filters('asp_load_css_js', false) &&
!apply_filters('asp_load_js', false);
}
function getVisibleSearchIds( $html = '' ): array {
$this->instances = $this->getInstancesFromHtml($html);
$this->instances = array_merge(
$this->instances,
wd_asp()->instances->getInstancesPrinted()
);
// Search results page && keyword highlighter
if (
isset($_GET['p_asid']) && intval($_GET['p_asid']) > 0 &&
wd_asp()->instances->exists($_GET['p_asid'])
) {
$instance = wd_asp()->instances->get(intval($_GET['p_asid']));
if (
( $instance['data']['single_highlight'] && isset($_GET['asp_highlight']) ) ||
( $instance['data']['result_page_highlight'] && isset($_GET['s']) )
) {
$this->instances[] = $instance['id'];
}
}
$this->instances = array_unique($this->instances);
sort($this->instances);
return $this->instances;
}
private function getInstancesFromHtml($out): array {
if ( $out !== false && $out !== '' ) {
if ( preg_match_all('/data-asp-id=["\'](\d+)[\'"]\s/', $out, $matches) > 0 ) {
foreach ( $matches[1] as $search_id ) {
$search_id = (int) $search_id;
if ( $search_id !== 0 ) {
$this->instances[] = $search_id;
}
}
}
}
return $this->instances;
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace WPDRMS\ASP\Asset;
if ( !defined('ABSPATH') ) {
die('-1');
}
interface ManagerInterface {
/**
* To be called before (or within) wp_print_footer_scripts|admin_print_footer_scripts
*
* @param bool $force
* @return void
*/
public function enqueue( bool $force = false ): void;
/**
* To be called on shutdown - backup print scripts for panic mode
*
* @param array<string, mixed> $instances
* @return void
*/
public function printInline( array $instances = array() ): void;
/**
* Injection handler for the output buffer
*
* @param string $buffer
* @param bool|array<string, mixed> $instances
* @return string
*/
public function injectToBuffer( string $buffer, $instances ): string;
}

View File

@@ -0,0 +1,82 @@
<?php
namespace WPDRMS\ASP\Asset\Script;
use WPDRMS\ASP\Asset\GeneratorInterface;
use WPDRMS\ASP\Utils\FileManager;
defined('ABSPATH') or die("You can't access this file directly.");
if ( !class_exists(__NAMESPACE__ . '\Generator') ) {
class Generator implements GeneratorInterface {
private
$scripts;
function __construct($scripts) {
$this->scripts = $scripts;
}
function get() {
if ( $this->verifyFiles() ) {
return $this->filename();
} else {
$this->generate();
if ( $this->verifyFiles() ) {
return $this->filename();
}
}
return '';
}
function generate(): bool {
if ( !$this->verifyFiles() ) {
$final_content = '';
foreach ($this->scripts as $script) {
if ( !isset($script['path']) ) {
return false;
} else {
$content = file_get_contents($script['path']);
if ( $content == '' ) {
return false;
} else {
$final_content .= $content;
}
}
}
FileManager::instance()->write(wd_asp()->cache_path . $this->fileName(), $final_content);
return $this->verifyFiles();
}
return true;
}
function verifyFiles(): bool {
if (
!file_exists(wd_asp()->cache_path . $this->filename()) ||
@filesize(wd_asp()->cache_path . $this->filename()) < 1025
) {
return false;
} else {
return true;
}
}
public static function deleteFiles(): int {
if ( !empty(wd_asp()->cache_path) && wd_asp()->cache_path !== '' ) {
return FileManager::instance()->deleteByPattern(wd_asp()->cache_path, '*.js');
}
return 0;
}
function fileName(): string {
return $this->fileHandle() . '.min.js';
}
function fileHandle(): string {
$concat = ASP_CURR_VER_STRING . ASP_CURR_VER;
foreach ( $this->scripts as $script ) {
$concat .= $script['handle'];
}
return 'asp-' . substr(md5($concat), 0, 8);
}
}
}

View File

@@ -0,0 +1,585 @@
<?php
namespace WPDRMS\ASP\Asset\Script;
use stdClass;
use WPDRMS\ASP\Asset\AssetManager;
use WPDRMS\ASP\Asset\ManagerInterface;
use WPDRMS\ASP\Patterns\SingletonTrait;
use WPDRMS\ASP\Utils\Html;
use WPDRMS\ASP\Utils\Script;
if ( !defined('ABSPATH') ) {
die('-1');
}
class Manager extends AssetManager implements ManagerInterface {
use SingletonTrait;
private $prepared = array();
private $media_query = '';
private $inline = '';
private $inline_instance = '';
private $instances = false;
private $args = array();
private $scripts = array(
'wd-asp-photostack' => array(
'src' => 'js/{js_source}/external/photostack.js',
'prereq' => false,
),
'wd-asp-select2' => array(
'src' => 'js/{js_source}/external/jquery.select2.js',
'prereq' => array( 'jquery' ),
),
'wd-asp-nouislider' => array(
'src' => 'js/{js_source}/external/nouislider.all.js',
'prereq' => false,
),
'wd-asp-rpp-isotope' => array(
'src' => 'js/{js_source}/external/isotope.js',
'prereq' => false,
),
'wd-asp-ajaxsearchpro' => array(
'src' => 'js/{js_source}/plugin/merged/asp{js_min}.js',
'prereq' => false,
),
'wd-asp-prereq-and-wrapper' => array(
'src' => 'js/{js_source}/plugin/merged/asp-prereq-and-wrapper{js_min}.js',
'prereq' => false,
),
);
private $optimized_scripts = array(
'wd-asp-ajaxsearchpro' => array(
'wd-asp-ajaxsearchpro-prereq' => array(
'handle' => 'wd-asp-ajaxsearchpro', // Handle alias, for the enqueue
'src' => 'js/{js_source}/plugin/optimized/asp-prereq.js',
),
'wd-asp-ajaxsearchpro-core' => array(
'src' => 'js/{js_source}/plugin/optimized/asp-core.js',
),
'wd-asp-ajaxsearchpro-settings' => array(
'src' => 'js/{js_source}/plugin/optimized/asp-settings.js',
'prereq' => array( 'wd-asp-ajaxsearchpro' ),
),
'wd-asp-ajaxsearchpro-compact' => array(
'src' => 'js/{js_source}/plugin/optimized/asp-compact.js',
'prereq' => array( 'wd-asp-ajaxsearchpro' ),
),
'wd-asp-ajaxsearchpro-vertical' => array(
'src' => 'js/{js_source}/plugin/optimized/asp-results-vertical.js',
'prereq' => array( 'wd-asp-ajaxsearchpro' ),
),
'wd-asp-ajaxsearchpro-horizontal' => array(
'src' => 'js/{js_source}/plugin/optimized/asp-results-horizontal.js',
'prereq' => array( 'wd-asp-ajaxsearchpro' ),
),
'wd-asp-ajaxsearchpro-polaroid' => array(
'src' => 'js/{js_source}/plugin/optimized/asp-results-polaroid.js',
'prereq' => array( 'wd-asp-ajaxsearchpro' ),
),
'wd-asp-ajaxsearchpro-isotopic' => array(
'src' => 'js/{js_source}/plugin/optimized/asp-results-isotopic.js',
'prereq' => array( 'wd-asp-ajaxsearchpro' ),
),
'wd-asp-ajaxsearchpro-ga' => array(
'src' => 'js/{js_source}/plugin/optimized/asp-ga.js',
'prereq' => array( 'wd-asp-ajaxsearchpro' ),
),
'wd-asp-ajaxsearchpro-live' => array(
'src' => 'js/{js_source}/plugin/optimized/asp-live.js',
'prereq' => array( 'wd-asp-ajaxsearchpro' ),
),
'wd-asp-ajaxsearchpro-autocomplete' => array(
'src' => 'js/{js_source}/plugin/optimized/asp-autocomplete.js',
'prereq' => array( 'wd-asp-ajaxsearchpro' ),
),
'wd-asp-ajaxsearchpro-wrapper' => array(
'src' => 'js/{js_source}/plugin/optimized/asp-wrapper.js',
'prereq' => true, // TRUE => previously loaded script
),
'wd-asp-ajaxsearchpro-addon-elementor' => array(
'src' => 'js/{js_source}/plugin/optimized/asp-addons-elementor.js',
'prereq' => true, // TRUE => previously loaded script
),
'wd-asp-ajaxsearchpro-addon-bricks' => array(
'src' => 'js/{js_source}/plugin/optimized/asp-addons-bricks.js',
'prereq' => true, // TRUE => previously loaded script
),
'wd-asp-ajaxsearchpro-addon-divi' => array(
'src' => 'js/{js_source}/plugin/optimized/asp-addons-divi.js',
'prereq' => true, // TRUE => previously loaded script
),
'wd-asp-ajaxsearchpro-addon-blocksy' => array(
'src' => 'js/{js_source}/plugin/optimized/asp-addons-blocksy.js',
'prereq' => true, // TRUE => previously loaded script
),
'wd-asp-ajaxsearchpro-addon-woocommerce' => array(
'src' => 'js/{js_source}/plugin/optimized/asp-addons-woocommerce.js',
'prereq' => true, // TRUE => previously loaded script
),
),
);
public function enqueue( $force = false ): void {
if ( $force || $this->args['method'] === 'classic' ) {
// Do not allow async method in footer enqueue
$this->args['method'] = $this->args['method'] === 'optimized_async' ? 'optimized' :$this->args['method'];
$this->earlyFooterEnqueue();
$this->initialize();
echo $this->inline; // @phpcs:ignore
foreach ( $this->prepared as $script ) {
wp_enqueue_script($script['handle']);
}
}
}
public function printInline( $instances = array() ): void {
if ( $this->args['method'] !== 'classic' ) {
$this->initialize();
$output = $this->inline_instance . $this->inline . $this->preparedToInline();
if ( $output !== '' ) {
echo $output; // @phpcs:ignore
}
}
}
public function injectToBuffer( $buffer, $instances ): string {
if ( $this->args['method'] !== 'classic' ) {
$this->initialize($instances);
$output = $this->inline_instance . $this->inline . $this->preparedToInline();
if ( $output !== '' ) {
Html::inject($output, $buffer, array( '</body>', '</html>', '<script' ), false);
}
}
return $buffer;
}
public function cleanup() {
Generator::deleteFiles();
}
private function preparedToInline() {
$out = '';
foreach ( $this->prepared as $script ) {
$async = isset($script['async']) ? 'async ' : '';
$out .= '<script ' . $async . "type='text/javascript' src='" . $script['src'] . "' id='" . $script['handle'] . "-js'></script>";
}
return $out;
}
private function __construct() {
$this->args = array(
'method' => wd_asp()->o['asp_compatibility']['script_loading_method'],
'source' => wd_asp()->o['asp_compatibility']['js_source'],
'detect_ajax' => wd_asp()->o['asp_compatibility']['detect_ajax'],
'init_only_in_viewport' => wd_asp()->o['asp_compatibility']['init_instances_inviewport_only'],
'custom_ajax_handler' => wd_asp()->o['asp_compatibility']['usecustomajaxhandler'],
);
$this->adjustOptionsForCompatibility();
}
private function adjustOptionsForCompatibility() {
if ( $this->conflict() ) {
$this->args['method'] = 'classic';
}
}
private function get( $handles = array(), $minified = true, $optimized = false, $except = array() ): array {
$handles = is_string($handles) ? array( $handles ) : $handles;
$handles = count($handles) === 0 ? array_keys($this->scripts) : $handles;
$js_source = $minified ? 'min' : 'nomin';
$js_min = $minified ? '.min' : '';
$return = array();
foreach ( $handles as $handle ) {
if ( in_array($handle, $except, true) || !Requirements::isRequired($handle, $this->instances) ) {
continue;
}
if ( isset($this->scripts[ $handle ]) ) {
if ( $optimized && isset($this->optimized_scripts[ $handle ]) ) {
$prev_handle = '';
foreach ( $this->optimized_scripts[ $handle ] as $optimized_script_handle => $optimized_script ) {
if ( in_array($optimized_script_handle, $except, true) || !Requirements::isRequired($optimized_script_handle, $this->instances) ) {
continue;
}
$prereq = !isset($optimized_script['prereq']) || $optimized_script['prereq'] === false ? array() : $optimized_script['prereq'];
if ( $prereq === true ) {
$prereq = array( $prev_handle );
}
$return[] = array(
'handle' => $optimized_script['handle'] ?? $optimized_script_handle,
'path' => ASP_PATH . str_replace(
array( '{js_source}' ),
array( $js_source ),
$js_source === 'min' ? str_replace('.js', '.min.js', $optimized_script['src']) : $optimized_script['src']
),
'src' => ASP_URL . str_replace(
array( '{js_source}' ),
array( $js_source ),
$js_source === 'min' ? str_replace('.js', '.min.js', $optimized_script['src']) : $optimized_script['src']
),
'prereq' => $prereq,
);
$prev_handle = $optimized_script_handle;
}
continue;
}
$return[] = array(
'handle' => $handle,
'path' => ASP_PATH . str_replace(
array( '{js_source}' ),
array( $js_source ),
str_replace('{js_min}', $js_min, $this->scripts[ $handle ]['src'])
),
'src' => ASP_URL . str_replace(
array( '{js_source}' ),
array( $js_source ),
str_replace('{js_min}', $js_min, $this->scripts[ $handle ]['src'])
),
'prereq' => $this->scripts[ $handle ]['prereq'],
);
} elseif ( $optimized && wd_in_array_r($handle, $this->optimized_scripts) ) {
foreach ( $this->optimized_scripts as $scripts ) {
if ( isset($scripts[ $handle ]) ) {
$return[] = array(
'handle' => $handle,
'path' => ASP_PATH . str_replace(
array( '{js_source}' ),
array( $js_source ),
$scripts[ $handle ]['src']
),
'src' => ASP_URL . str_replace(
array( '{js_source}' ),
array( $js_source ),
$scripts[ $handle ]['src']
),
'prereq' => $scripts[ $handle ]['prereq'],
);
}
}
}
}
return $return;
}
public function earlyFooterEnqueue( $instances = false ) {
/**
* Internal WP Scripts have to be enqueued at this point
*/
if ( Requirements::isRequired('jquery', $instances) ) {
wp_enqueue_script('jquery');
}
if ( Requirements::isRequired('jquery-ui-datepicker', $instances) ) {
wp_enqueue_script('jquery-ui-datepicker');
}
}
public function initialize( $instances = false ): bool {
$this->instances = $instances;
$analytics = wd_asp()->o['asp_analytics'];
$load_in_footer = true;
$media_query = ASP_DEBUG ? asp_gen_rnd_str() : get_site_option('asp_media_query', 'defn');
if ( wd_asp()->manager->getContext() === 'backend' ) {
$js_minified = false;
$js_optimized = true;
$js_async_load = false;
$js_aggregate = false;
} else {
$js_minified = $this->args['source'] === 'jqueryless-min';
$js_optimized = $this->args['method'] !== 'classic';
$js_async_load = $this->args['method'] === 'optimized_async';
$js_aggregate = !$js_async_load && !ASP_DEBUG && !( defined('WP_ASP_TEST_ENV') && is_user_logged_in() );
}
$single_highlight = false;
$single_highlight_arr = array();
// Search results page && keyword highlighter
if (
isset($_GET['p_asid']) && intval($_GET['p_asid']) > 0 &&
wd_asp()->instances->exists($_GET['p_asid']) // @phpcs:ignore
) {
$phrase = $_GET['s'] ?? $_GET['asp_highlight'] ?? ''; // @phpcs:ignore
if ( $phrase !== '' ) {
$search = wd_asp()->instances->get(intval($_GET['p_asid']));
if ( $search['data']['single_highlight'] || $search['data']['result_page_highlight'] ) {
$single_highlight = true;
$single_highlight_arr = array(
'id' => $search['id'],
'selector' => $search['data']['single_highlight_selector'],
'scroll' => boolval($search['data']['single_highlight_scroll']),
'scroll_offset' => intval($search['data']['single_highlight_offset']),
'whole' => boolval($search['data']['single_highlightwholewords']),
'minWordLength' => intval($search['data']['min_word_length']),
);
}
}
}
$ajax_url = admin_url('admin-ajax.php');
if ( !is_admin() ) {
if ( $this->args['custom_ajax_handler'] ) {
$ajax_url = ASP_URL . 'ajax_search.php';
}
if ( wd_asp()->o['asp_caching']['caching'] && wd_asp()->o['asp_caching']['caching_method'] === 'sc_file' ) {
$ajax_url = ASP_URL . 'sc-ajax.php';
}
}
$handle = 'wd-asp-ajaxsearchpro';
if ( !$js_async_load ) {
$handle = $this->prepare(
$this->get(
array(),
$js_minified,
$js_optimized,
array(
'wd-asp-prereq-and-wrapper',
)
),
array(
'media_query' => $media_query,
'in_footer' => $load_in_footer,
'aggregate' => $js_aggregate,
'handle' => $handle,
)
);
$additional_scripts = $this->get(
array(),
$js_minified,
$js_optimized,
array( 'wd-asp-prereq-and-wrapper', 'wd-asp-ajaxsearchpro-wrapper' )
);
} else {
$handle = 'wd-asp-prereq-and-wrapper';
$this->prepare(
$this->get($handle, $js_minified, $js_optimized),
array(
'media_query' => $media_query,
'in_footer' => $load_in_footer,
)
);
$additional_scripts = $this->get(
array(),
$js_minified,
$js_optimized,
array(
'wd-asp-prereq-and-wrapper',
'wd-asp-ajaxsearchpro-wrapper',
'wd-asp-ajaxsearchpro-prereq',
)
);
}
// Safety against Path Traversal attacks
$additional_scripts = array_map(
function ( $script ) {
unset($script['path']);
return $script;
},
$additional_scripts
);
// The new variable is ASP
$this->inline = Script::objectToInlineScript(
$handle,
'ASP',
array(
'wp_rocket_exception' => 'DOMContentLoaded', // WP Rocket hack to prevent the wrapping of the inline script: https://docs.wp-rocket.me/article/1265-load-javascript-deferred
'ajaxurl' => $ajax_url,
'backend_ajaxurl' => admin_url('admin-ajax.php'),
'asp_url' => ASP_URL,
'upload_url' => wd_asp()->upload_url,
'detect_ajax' => $this->args['detect_ajax'],
'media_query' => get_site_option('asp_media_query', 'defn'),
'version' => ASP_CURR_VER_STRING,
'build' => ASP_CURR_VER,
'pageHTML' => '',
'additional_scripts' => $additional_scripts,
'script_async_load' => $js_async_load,
'font_url' => str_replace('http:', '', plugins_url()) . '/ajax-search-pro/css/fonts/icons/icons2.woff2',
'init_only_in_viewport' => boolval( $this->args['init_only_in_viewport']),
'highlight' => array(
'enabled' => $single_highlight,
'data' => $single_highlight_arr,
),
'debug' => ASP_DEBUG || defined('WP_ASP_TEST_ENV'),
'instances' => new stdClass(),
'analytics' => array(
'method' => $analytics['analytics'],
'tracking_id' => $analytics['analytics_tracking_id'],
'event' => array(
'focus' => array(
'active' => boolval($analytics['gtag_focus']),
'action' => $analytics['gtag_focus_action'],
'category' => $analytics['gtag_focus_ec'],
'label' => $analytics['gtag_focus_el'],
'value' => $analytics['gtag_focus_value'],
),
'search_start' => array(
'active' => boolval($analytics['gtag_search_start']),
'action' => $analytics['gtag_search_start_action'],
'category' => $analytics['gtag_search_start_ec'],
'label' => $analytics['gtag_search_start_el'],
'value' => $analytics['gtag_search_start_value'],
),
'search_end' => array(
'active' => boolval($analytics['gtag_search_end']),
'action' => $analytics['gtag_search_end_action'],
'category' => $analytics['gtag_search_end_ec'],
'label' => $analytics['gtag_search_end_el'],
'value' => $analytics['gtag_search_end_value'],
),
'magnifier' => array(
'active' => boolval($analytics['gtag_magnifier']),
'action' => $analytics['gtag_magnifier_action'],
'category' => $analytics['gtag_magnifier_ec'],
'label' => $analytics['gtag_magnifier_el'],
'value' => $analytics['gtag_magnifier_value'],
),
'return' => array(
'active' => boolval($analytics['gtag_return']),
'action' => $analytics['gtag_return_action'],
'category' => $analytics['gtag_return_ec'],
'label' => $analytics['gtag_return_el'],
'value' => $analytics['gtag_return_value'],
),
'try_this' => array(
'active' => boolval($analytics['gtag_try_this']),
'action' => $analytics['gtag_try_this_action'],
'category' => $analytics['gtag_try_this_ec'],
'label' => $analytics['gtag_try_this_el'],
'value' => $analytics['gtag_try_this_value'],
),
'facet_change' => array(
'active' => boolval($analytics['gtag_facet_change']),
'action' => $analytics['gtag_facet_change_action'],
'category' => $analytics['gtag_facet_change_ec'],
'label' => $analytics['gtag_facet_change_el'],
'value' => $analytics['gtag_facet_change_value'],
),
'result_click' => array(
'active' => boolval($analytics['gtag_result_click']),
'action' => $analytics['gtag_result_click_action'],
'category' => $analytics['gtag_result_click_ec'],
'label' => $analytics['gtag_result_click_el'],
'value' => $analytics['gtag_result_click_value'],
),
),
),
),
'before',
true,
false
);
// Instance data
$script_data = wd_asp()->instances->get_script_data();
if ( count($script_data) > 0 ) {
if ( $instances === false ) {
$script = 'window.ASP_INSTANCES = [];';
foreach ( $script_data as $id => $data ) {
$script .= "window.ASP_INSTANCES[$id] = $data;";
}
$script_id = 'wd-asp-instances-' . substr(md5($script), 0, 8);
$this->inline_instance .= "<script id='$script_id'>$script</script>";
} else {
$script = '';
foreach ( $instances as $id ) {
if ( isset($script_data[ $id ]) ) {
$script .= "window.ASP_INSTANCES[$id] = $script_data[$id];";
}
}
if ( $script !== '' ) {
$script = 'window.ASP_INSTANCES = [];' . $script;
}
}
/**
* Why not wp_add_inline_script(), why this way?
*
* Because the script ID needs to be different for each different output, to signify difference
* for cache plugin. Otherwise caches like wp-optimize will cache the same output for the same
* script ID - and then the search instances will be missing.
*
* WordPress prints scripts at priority: 10 in this hook
*/
if ( $script !== '' ) {
$script_id = 'wd-asp-instances-' . substr(md5($script), 0, 8);
$this->inline_instance .= "<script id='$script_id'>$script</script>";
}
}
return true;
}
private function prepare( $scripts = array(), $args = array() ) {
$defaults = array(
'media_query' => '',
'in_footer' => true,
'prereq' => array(),
'aggregate' => false,
'handle' => '',
);
$args = wp_parse_args($args, $defaults);
$register_scripts = $scripts;
if ( $args['aggregate'] ) {
$generator = new Generator($scripts);
$filename = $generator->get();
if ( $filename !== '' ) {
$handle = $generator->fileHandle();
$prereq = $args['prereq'];
foreach ( $scripts as $script ) {
if ( is_array($script['prereq']) ) {
$prereq = array_merge($prereq, $script['prereq']);
}
}
$prereq = array_unique($prereq);
$prereq = array_filter(
$prereq,
function ( $p ) {
return strpos($p, 'asp-') === false;
}
);
$register_scripts = array(
array(
'handle' => $handle,
'src' => wd_asp()->cache_url . $filename,
'prereq' => $prereq,
'async' => true,
),
);
}
}
foreach ( $register_scripts as $script ) {
if ( isset($script['prereq']) ) {
if ( $script['prereq'] === false ) {
$script['prereq'] = array();
}
} else {
$script['prereq'] = $args['prereq'];
}
wp_register_script(
$script['handle'],
$script['src'],
$script['prereq'],
$args['media_query'],
$args['in_footer']
);
$this->prepared[] = $script;
}
return $handle ?? $args['handle'];
}
}

View File

@@ -0,0 +1,236 @@
<?php
namespace WPDRMS\ASP\Asset\Script;
defined('ABSPATH') or die("You can't access this file directly.");
class Requirements {
public static function isRequired( $handle, $instances = false ): bool {
if ( wd_asp()->manager->getContext() == "backend" ) {
return true;
}
$unused = self::getUnusedAssets(false, $instances);
$wp_scripts = wp_scripts();
$required = false;
switch ( strtolower($handle) ) {
case 'jquery':
if ( !wd_in_array_r('select2', $unused) && !isset($wp_scripts->done['jquery']) ) {
$required = true;
}
break;
case 'jquery-ui-datepicker':
case 'datepicker':
if ( !wd_in_array_r('datepicker', $unused) && !isset($wp_scripts->done['jquery-ui-datepicker']) ) {
$required = true;
}
break;
case 'wd-asp-photostack':
case 'wd-asp-ajaxsearchpro-polaroid':
if ( !wd_in_array_r('polaroid', $unused) ) {
$required = true;
}
break;
case 'wd-asp-select2':
if ( !wd_in_array_r('select2', $unused) ) {
$required = true;
}
break;
case 'wd-asp-nouislider':
if ( !wd_in_array_r('noui', $unused) ) {
$required = true;
}
break;
case 'wd-asp-rpp-isotope':
if ( !wd_in_array_r('isotope', $unused) ) {
$required = true;
}
break;
case 'wd-asp-ajaxsearchpro-settings':
if ( !wd_in_array_r('settings', $unused) ) {
$required = true;
}
break;
case 'wd-asp-ajaxsearchpro-compact':
if ( !wd_in_array_r('compact', $unused) ) {
$required = true;
}
break;
case 'wd-asp-ajaxsearchpro-vertical':
if ( !wd_in_array_r('vertical', $unused) ) {
$required = true;
}
break;
case 'wd-asp-ajaxsearchpro-horizontal':
if ( !wd_in_array_r('horizontal', $unused) ) {
$required = true;
}
break;
case 'wd-asp-ajaxsearchpro-isotopic':
if ( !wd_in_array_r('isotopic', $unused) ) {
$required = true;
}
break;
case 'wd-asp-ajaxsearchpro-autopopulate':
if ( !wd_in_array_r('autopopulate', $unused) ) {
$required = true;
}
break;
case 'wd-asp-ajaxsearchpro-ga':
if ( !wd_in_array_r('ga', $unused) ) {
$required = true;
}
break;
case 'wd-asp-ajaxsearchpro-autocomplete':
if ( !wd_in_array_r('autocomplete', $unused) ) {
$required = true;
}
break;
case 'wd-asp-ajaxsearchpro-addon-bricks':
if ( defined('BRICKS_VERSION') ) {
$required = true;
}
break;
case 'wd-asp-ajaxsearchpro-addon-elementor':
if ( defined('ELEMENTOR_PRO_VERSION') ) {
$required = true;
}
break;
case 'wd-asp-ajaxsearchpro-addon-divi':
if ( function_exists('et_setup_theme') ) {
$required = true;
}
break;
case 'wd-asp-ajaxsearchpro-addon-blocksy':
if ( defined('BLOCKSY_PATH') ) {
$required = true;
}
break;
case 'wd-asp-ajaxsearchpro-live':
default:
$required = true;
break;
}
return $required;
}
public static function getUnusedAssets( $return_stored = false, $instances = false ) {
$dependencies = array(
'vertical', 'horizontal', 'isotopic', 'polaroid', 'noui', 'datepicker', 'autocomplete',
'settings', 'compact', 'autopopulate', 'ga'
);
$external_dependencies = array(
'select2', 'isotope'
);
if ( $return_stored !== false && $instances === false ) {
return get_site_option('asp_unused_assets', array(
'internal' => $dependencies,
'external' => $external_dependencies
));
}
// --- Analytics
// php 7.4 "string" != 0 -> false, 7.4+ "string" != 0 -> true
if ( wd_asp()->o['asp_analytics']['analytics'] !== "0" ) {
$dependencies = array_diff($dependencies, array('ga'));
}
if ( $instances === false ) {
$search = wd_asp()->instances->get();
} else {
$search = array();
foreach ( $instances as $instance ) {
$search[] = wd_asp()->instances->get($instance);
}
}
if (is_array($search) && count($search)>0) {
foreach ($search as $s) {
// $style and $id needed in the include
$style = $s['data'];
$id = $s['id'];
// Calculate flags for the generated basic CSS
// --- Results type
$dependencies = array_diff($dependencies, array($s['data']['resultstype']));
// --- Compact box
if ( $s['data']['box_compact_layout'] ) {
$dependencies = array_diff($dependencies, array('compact'));
}
// --- Auto populate
if ( $s['data']['auto_populate'] != 'disabled' ) {
$dependencies = array_diff($dependencies, array('autopopulate'));
}
// --- Autocomplete
if ( $s['data']['autocomplete'] ) {
$dependencies = array_diff($dependencies, array('autocomplete'));
}
// --- NOUI
asp_parse_filters($id, $style, true, true);
// --- Settings visibility
/**
* DO NOT check the switch or if the settings are visible, because the user
* can still use the settings shortcode, and that is not possible to check
*/
if ( count(wd_asp()->front_filters->get()) > 0 ) {
$dependencies = array_diff($dependencies, array('settings'));
}
foreach (wd_asp()->front_filters->get() as $filter) {
if ($filter->display_mode == 'slider' || $filter->display_mode == 'range') {
$dependencies = array_diff($dependencies, array('noui'));
break;
}
}
// --- Datepicker
foreach (wd_asp()->front_filters->get() as $filter) {
if ($filter->display_mode == 'date' || $filter->display_mode == 'datepicker') {
$dependencies = array_diff($dependencies, array('datepicker'));
break;
}
}
// --- Scrollable filters
foreach (wd_asp()->front_filters->get() as $filter) {
if ( isset($filter->data['visible']) && $filter->data['visible'] == 0 ) {
continue;
}
if ($filter->display_mode == 'checkboxes' || $filter->display_mode == 'radio') {
break;
}
}
// --- Autocomplete (not used yet)
// --- Select2
foreach (wd_asp()->front_filters->get() as $filter) {
if ($filter->display_mode == 'dropdownsearch' || $filter->display_mode == 'multisearch') {
$external_dependencies = array_diff($external_dependencies, array('select2'));
break;
}
}
// --- Isotope
if ( $s['data']['resultstype'] == 'isotopic' || $s['data']['fss_column_layout'] == 'masonry' ) {
$external_dependencies = array_diff($external_dependencies, array('isotope'));
}
}
}
// Store for the init script
if ( $instances === false ) {
update_site_option('asp_unused_assets', array(
'internal' => $dependencies,
'external' => $external_dependencies
));
}
return array(
'internal' => $dependencies,
'external' => $external_dependencies
);
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace WPDRMS\ASP;
class Autoloader {
protected static $_instance;
protected $aliases = array(
'asp_indexTable' => 'WPDRMS\\ASP\\Index\\Manager',
'ASP_Query' => 'WPDRMS\\ASP\\Query\\SearchQuery',
//'ASP_Helpers' => 'WPDRMS\\ASP\\Utils\\Str'
);
private function __construct() {
defined('ABSPATH') or die();
spl_autoload_register(array(
$this, 'loader'
));
}
function loader( $class ) {
// project-specific namespace prefix
$prefix = 'WPDRMS\\ASP\\';
// base directory for the namespace prefix
$base_dir = ASP_CLASSES_PATH;
// does the class use the namespace prefix?
$len = strlen($prefix);
if ( strncmp($prefix, $class, $len) !== 0 ) {
// is this an alias?
if ( isset($this->aliases[$class]) ) {
if ( !class_exists($this->aliases[$class]) ) {
$this->loader($this->aliases[$class]);
}
if ( class_exists($this->aliases[$class]) ) {
/**
* Create class alias for old class names
*/
class_alias($this->aliases[$class], $class);
}
}
} else {
// get the relative class name
$relative_class = substr($class, $len);
// replace the namespace prefix with the base directory, replace namespace
// separators with directory separators in the relative class name, append
// with .php
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
// if the file exists, require it
if ( file_exists($file) ) {
require $file;
}
}
}
// ------------------------------------------------------------
// ---------------- SINGLETON SPECIFIC --------------------
// ------------------------------------------------------------
public static function getInstance() {
if ( ! ( self::$_instance instanceof self ) ) {
self::$_instance = new self();
}
return self::$_instance;
}
}
Autoloader::getInstance();

View File

@@ -0,0 +1,92 @@
<?php
namespace WPDRMS\ASP\BlockEditor;
use WPDRMS\ASP\Utils\Script;
if ( !defined('ABSPATH') ) {
die("You can't access this file directly.");
}
/**
* Full Site Editor and Gutenberg blocks controller
*/
class ASPBlock implements BlockInterface {
/**
* Server side registration of the blocks
*
* @hook init
* @return void
*/
public function register(): void {
if ( !function_exists('register_block_type') ) {
return;
}
$instances = wd_asp()->instances->getWithoutData();
if ( count($instances) === 0 ) {
return;
}
$metadata = require_once ASP_PATH . '/build/js/block-editor.asset.php'; // @phpstan-ignore-line
wp_register_script(
'wdo-asp-block-editor',
ASP_URL_NP . 'build/js/block-editor.js',
$metadata['dependencies'],
$metadata['version'],
array(
'in_footer' => true,
)
);
$metadata = require_once ASP_PATH . '/build/css/block-editor.asset.php'; // @phpstan-ignore-line
wp_register_style(
'wdo-asp-block-editor-style',
ASP_URL_NP . 'build/css/block-editor.css',
$metadata['dependencies'],
$metadata['version'],
);
register_block_type(
'ajax-search-pro/block-asp-main',
array(
'editor_script' => 'wdo-asp-block-editor',
'editor_style' => 'wdo-asp-block-editor-style',
'render_callback' => array( $this, 'render' ),
'attributes' => array(
'instance' => array(
'default' => 1,
'type' => 'integer',
),
'scType' => array(
'default' => 1,
'type' => 'integer',
),
),
)
);
}
/**
* How to render the ajax-search-pro/block-asp-main block via ServerSideRender JSX component
*
* @param array{scType: integer, instance: integer} $atts
* @return string
*/
public function render( array $atts ): string {
// Editor render
if ( isset($_GET['context']) && $_GET['context'] === 'edit' ) {
if ( $atts['scType'] === 1 ) {
return do_shortcode('[wd_asp id="' . $atts['instance'] . '" include_styles=1]');
} else {
return '';
}
} elseif ( $atts['scType'] === 1 ) {
return do_shortcode("[wd_asp id={$atts['instance']}]");
} elseif ( $atts['scType'] === 2 ) {
return do_shortcode("[wpdreams_asp_settings id={$atts['instance']}]");
} elseif ( $atts['scType'] === 3 ) {
return do_shortcode("[wpdreams_ajaxsearchpro_results id={$atts['instance']}]");
}
return '';
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace WPDRMS\ASP\BlockEditor;
interface BlockInterface {
/**
* Block registration handler
*
* @return void
*/
public function register(): void;
}

View File

@@ -0,0 +1,665 @@
<?php
namespace WPDRMS\ASP\Cache;
/*
* bfi_thumb - WP Image Resizer v1.3
*
* (c) 2013 Benjamin F. Intal / Gambit
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use Exception;
use Imagick;
use WP_Error;
use WP_Image_Editor_GD;
use WP_Image_Editor_Imagick;
/** Uses WP's Image Editor Class to resize and filter images
*
* @param $url string the local image URL to manipulate
* @param $params array the options to perform on the image. Keys and values supported:
* 'width' int pixels
* 'height' int pixels
* 'opacity' int 0-100
* 'color' string hex-color #000000-#ffffff
* 'grayscale' bool
* 'negate' bool
* 'crop' bool
* 'crop_only' bool
* 'crop_x' bool string
* 'crop_y' bool string
* 'crop_width' bool string
* 'crop_height' bool string
* 'quality' int 1-100
* @param $single boolean, if false then an array of data will be returned
* @return string|array containing the url of the resized modified image
*/
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
/*
* Include the WP Image classes
*/
require_once ABSPATH . WPINC . '/class-wp-image-editor.php';
require_once ABSPATH . WPINC . '/class-wp-image-editor-imagick.php';
require_once ABSPATH . WPINC . '/class-wp-image-editor-gd.php';
/*
* Enhanced Imagemagick Image Editor
*/
class BFI_Image_Editor_Imagick_1_3 extends WP_Image_Editor_Imagick {
/** Changes the opacity of the image
*
* @supports 3.5.1
* @access public
*
* @param float $opacity (0.0-1.0)
* @return boolean|WP_Error
*/
public function opacity( $opacity ) {
$opacity /= 100;
try {
// From: http://stackoverflow.com/questions/3538851/php-imagick-setimageopacity-destroys-transparency-and-does-nothing
// preserves transparency
//$this->image->setImageOpacity($opacity);
return $this->image->evaluateImage( Imagick::EVALUATE_MULTIPLY, $opacity, Imagick::CHANNEL_ALPHA );
} catch ( Exception $e ) {
return new WP_Error( 'image_opacity_error', $e->getMessage() );
}
}
/** Tints the image a different color
*
* @supports 3.5.1
* @access public
*
* @param string hex color e.g. #ff00ff
* @return boolean|WP_Error
*/
public function colorize( $hexColor ) {
try {
return $this->image->colorizeImage( $hexColor, 1.0 );
} catch ( Exception $e ) {
return new WP_Error( 'image_colorize_error', $e->getMessage() );
}
}
/** Makes the image grayscale
*
* @supports 3.5.1
* @access public
*
* @return boolean|WP_Error
*/
public function grayscale() {
try {
return $this->image->modulateImage( 100, 0, 100 );
} catch ( Exception $e ) {
return new WP_Error( 'image_grayscale_error', $e->getMessage() );
}
}
/** Negates the image
*
* @supports 3.5.1
* @access public
*
* @return boolean|WP_Error
*/
public function negate() {
try {
return $this->image->negateImage( false );
} catch ( Exception $e ) {
return new WP_Error( 'image_negate_error', $e->getMessage() );
}
}
}
/*
* Enhanced GD Image Editor
*/
class BFI_Image_Editor_GD_1_3 extends WP_Image_Editor_GD {
/** Rotates current image counter-clockwise by $angle.
* Ported from image-edit.php
* Added presevation of alpha channels
*
* @since 3.5.0
* @access public
*
* @param float $angle
* @return boolean|WP_Error
*/
public function rotate( $angle ) {
if ( function_exists('imagerotate') ) {
$rotated = imagerotate( $this->image, $angle, 0 );
// Add alpha blending
imagealphablending( $rotated, true );
imagesavealpha( $rotated, true );
if ( is_resource( $rotated ) ) {
imagedestroy( $this->image );
$this->image = $rotated;
$this->update_size();
return true;
}
}
return new WP_Error( 'image_rotate_error', __( 'Image rotate failed.', 'default' ), $this->file );
}
/** Changes the opacity of the image
*
* @supports 3.5.1
* @access public
*
* @param float $opacity (0.0-1.0)
* @return boolean|WP_Error
*/
public function opacity( $opacity ) {
$opacity /= 100;
$filtered = $this->_opacity( $this->image, $opacity );
if ( is_resource( $filtered ) ) {
// imagedestroy($this->image);
$this->image = $filtered;
return true;
}
return new WP_Error( 'image_opacity_error', __('Image opacity change failed.', 'default' ), $this->file );
}
// from: http://php.net/manual/en/function.imagefilter.php
// params: image resource id, opacity (eg. 0.0-1.0)
protected function _opacity( $image, $opacity ) {
if ( ! function_exists( 'imagealphablending' ) ||
! function_exists( 'imagecolorat' ) ||
! function_exists( 'imagecolorallocatealpha' ) ||
! function_exists( 'imagesetpixel' ) ) {
return false;
}
// get image width and height
$w = imagesx( $image );
$h = imagesy( $image );
// turn alpha blending off
imagealphablending( $image, false );
// find the most opaque pixel in the image (the one with the smallest alpha value)
$minalpha = 127;
for ( $x = 0; $x < $w; $x++ ) {
for ( $y = 0; $y < $h; $y++ ) {
$alpha = ( imagecolorat( $image, $x, $y ) >> 24 ) & 0xFF;
if ( $alpha < $minalpha ) {
$minalpha = $alpha;
}
}
}
// loop through image pixels and modify alpha for each
for ( $x = 0; $x < $w; $x++ ) {
for ( $y = 0; $y < $h; $y++ ) {
// get current alpha value (represents the TANSPARENCY!)
$colorxy = imagecolorat( $image, $x, $y );
$alpha = ( $colorxy >> 24 ) & 0xFF;
// calculate new alpha
if ( $minalpha !== 127 ) {
$alpha = 127 + 127 * $opacity * ( $alpha - 127 ) / ( 127 - $minalpha );
} else {
$alpha += 127 * $opacity;
}
// get the color index with new alpha
$alphacolorxy = imagecolorallocatealpha( $image, ( $colorxy >> 16 ) & 0xFF, ( $colorxy >> 8 ) & 0xFF, $colorxy & 0xFF, $alpha );
// set pixel with the new color + opacity
if( ! imagesetpixel( $image, $x, $y, $alphacolorxy ) ) {
return false;
}
}
}
imagesavealpha( $image, true );
return $image;
}
/** Tints the image a different color
*
* @supports 3.5.1
* @access public
*
* @param string hex color e.g. #ff00ff
* @return boolean|WP_Error
*/
public function colorize( $hexColor ) {
if ( function_exists( 'imagefilter' ) &&
function_exists( 'imagesavealpha' ) &&
function_exists( 'imagealphablending' ) ) {
$hexColor = preg_replace( '#^\##', '', $hexColor );
$r = hexdec( substr( $hexColor, 0, 2 ) );
$g = hexdec( substr( $hexColor, 2, 2 ) );
$b = hexdec( substr( $hexColor, 2, 2 ) );
imagealphablending( $this->image, false );
if ( imagefilter( $this->image, IMG_FILTER_COLORIZE, $r, $g, $b, 0 ) ) {
imagesavealpha( $this->image, true );
return true;
}
}
return new WP_Error( 'image_colorize_error', __( 'Image color change failed.', 'default' ), $this->file );
}
/** Makes the image grayscale
*
* @supports 3.5.1
* @access public
*
* @return boolean|WP_Error
*/
public function grayscale() {
if ( function_exists( 'imagefilter' ) ) {
if ( imagefilter( $this->image, IMG_FILTER_GRAYSCALE ) ) {
return true;
}
}
return new WP_Error( 'image_grayscale_error', __( 'Image grayscale failed.', 'default' ), $this->file );
}
/** Negates the image
*
* @supports 3.5.1
* @access public
*
* @return boolean|WP_Error
*/
public function negate() {
if ( function_exists( 'imagefilter' ) ) {
if ( imagefilter( $this->image, IMG_FILTER_NEGATE ) ) {
return true;
}
}
return new WP_Error( 'image_negate_error', __( 'Image negate failed.', 'default' ), $this->file );
}
}
/*
* Main Class
*/
class BFI_Thumb_1_3 {
/** Uses WP's Image Editor Class to resize and filter images
* Inspired by: https://github.com/sy4mil/Aqua-Resizer/blob/master/aq_resizer.php
*
* @param $url string the local image URL to manipulate
* @param $params array the options to perform on the image. Keys and values supported:
* 'width' int pixels
* 'height' int pixels
* 'opacity' int 0-100
* 'color' string hex-color #000000-#ffffff
* 'grayscale' bool
* 'crop' bool
* 'negate' bool
* 'crop_only' bool
* 'crop_x' bool string
* 'crop_y' bool string
* 'crop_width' bool string
* 'crop_height' bool string
* 'quality' int 1-100
* @param $single boolean, if false then an array of data will be returned
* @return string|array
*/
public static function thumb( $url, $params = array(), $single = true ) {
extract( $params );
//validate inputs
if ( ! $url ) {
return false;
}
$crop_only = isset( $crop_only ) ? $crop_only : false;
//define upload path & dir
$upload_info = wp_upload_dir();
$upload_dir = $upload_info['basedir'];
$upload_url = $upload_info['baseurl'];
$theme_url = get_template_directory_uri();
$theme_dir = get_template_directory();
// find the path of the image. Perform 2 checks:
// #1 check if the image is in the uploads folder
if ( strpos( $url, $upload_url ) !== false ) {
$rel_path = str_replace( $upload_url, '', $url );
$img_path = $upload_dir . $rel_path;
// #2 check if the image is in the current theme folder
} else if ( strpos( $url, $theme_url ) !== false ) {
$rel_path = str_replace( $theme_url, '', $url );
$img_path = $theme_dir . $rel_path;
}
// Fail if we can't find the image in our WP local directory
if ( empty( $img_path ) ) {
return $url;
}
// check if img path exists, and is an image indeed
if( ! @file_exists( $img_path ) || ! getimagesize( $img_path ) ) {
return $url;
}
// This is the filename
$basename = basename( $img_path );
//get image info
$info = pathinfo( $img_path );
$ext = $info['extension'];
list( $orig_w, $orig_h ) = getimagesize( $img_path );
// support percentage dimensions. compute percentage based on
// the original dimensions
if ( isset( $width ) ) {
if ( stripos( $width, '%' ) !== false ) {
$width = (int) ( (float) str_replace( '%', '', $width ) / 100 * $orig_w );
}
}
if ( isset( $height ) ) {
if ( stripos( $height, '%' ) !== false ) {
$height = (int) ( (float) str_replace( '%', '', $height ) / 100 * $orig_h );
}
}
// The only purpose of this is to determine the final width and height
// without performing any actual image manipulation, which will be used
// to check whether a resize was previously done.
if ( isset( $width ) && $crop_only === false ) {
//get image size after cropping
$dims = image_resize_dimensions( $orig_w, $orig_h, $width, isset( $height ) ? $height : null, isset( $crop ) ? $crop : false );
if ( is_array($dims) ) {
$dst_w = $dims[4];
$dst_h = $dims[5];
}
} else if ( $crop_only === true ) {
// we don't want a resize,
// but only a crop in the image
// get x position to start croping
$src_x = ( isset( $crop_x ) ) ? $crop_x : 0;
// get y position to start croping
$src_y = ( isset( $crop_y ) ) ? $crop_y : 0;
// width of the crop
if ( isset( $crop_width ) ) {
$src_w = $crop_width;
} else if ( isset( $width ) ) {
$src_w = $width;
} else {
$src_w = $orig_w;
}
// height of the crop
if ( isset( $crop_height ) ) {
$src_h = $crop_height;
} else if ( isset( $height ) ) {
$src_h = $height;
} else {
$src_h = $orig_h;
}
// set the width resize with the crop
if ( isset( $crop_width ) && isset( $width ) ) {
$dst_w = $width;
} else {
$dst_w = null;
}
// set the height resize with the crop
if ( isset( $crop_height ) && isset( $height ) ) {
$dst_h = $height;
} else {
$dst_h = null;
}
// allow percentages
if ( isset( $dst_w ) ) {
if ( stripos( $dst_w, '%' ) !== false ) {
$dst_w = (int) ( (float) str_replace( '%', '', $dst_w ) / 100 * $orig_w );
}
}
if ( isset( $dst_h ) ) {
if ( stripos( $dst_h, '%' ) !== false ) {
$dst_h = (int) ( (float) str_replace( '%', '', $dst_h ) / 100 * $orig_h );
}
}
$dims = image_resize_dimensions( $src_w, $src_h, $dst_w, $dst_h, false );
if ( is_array($dims) ) {
$dst_w = $dims[4];
$dst_h = $dims[5];
}
// Make the pos x and pos y work with percentages
if ( stripos( $src_x, '%' ) !== false ) {
$src_x = (int) ( (float) str_replace( '%', '', $width ) / 100 * $orig_w );
}
if ( stripos( $src_y, '%' ) !== false ) {
$src_y = (int) ( (float) str_replace( '%', '', $height ) / 100 * $orig_h );
}
// allow center to position crop start
if ( $src_x === 'center' ) {
$src_x = ( $orig_w - $src_w ) / 2;
}
if ( $src_y === 'center' ) {
$src_y = ( $orig_h - $src_h ) / 2;
}
}
// create the suffix for the saved file
// we can use this to check whether we need to create a new file or just use an existing one.
$suffix = (string) filemtime( $img_path ) .
( isset( $width ) ? str_pad( (string) $width, 5, '0', STR_PAD_LEFT ) : '00000' ) .
( isset( $height ) ? str_pad( (string) $height, 5, '0', STR_PAD_LEFT ) : '00000' ) .
( isset( $opacity ) ? str_pad( (string) $opacity, 3, '0', STR_PAD_LEFT ) : '100' ) .
( isset( $color ) ? str_pad( preg_replace( '#^\##', '', $color ), 8, '0', STR_PAD_LEFT ) : '00000000' ) .
( isset( $grayscale ) ? ( $grayscale ? '1' : '0' ) : '0' ) .
( isset( $crop ) ? ( $crop ? '1' : '0' ) : '0' ) .
( isset( $negate ) ? ( $negate ? '1' : '0' ) : '0' ) .
( isset( $crop_only ) ? ( $crop_only ? '1' : '0' ) : '0' ) .
( isset( $src_x ) ? str_pad( (string) $src_x, 5, '0', STR_PAD_LEFT ) : '00000' ) .
( isset( $src_y ) ? str_pad( (string) $src_y, 5, '0', STR_PAD_LEFT ) : '00000' ) .
( isset( $src_w ) ? str_pad( (string) $src_w, 5, '0', STR_PAD_LEFT ) : '00000' ) .
( isset( $src_h ) ? str_pad( (string) $src_h, 5, '0', STR_PAD_LEFT ) : '00000' ) .
( isset( $dst_w ) ? str_pad( (string) $dst_w, 5, '0', STR_PAD_LEFT ) : '00000' ) .
( isset( $dst_h ) ? str_pad( (string) $dst_h, 5, '0', STR_PAD_LEFT ) : '00000' ) .
( ( isset ( $quality ) && $quality > 0 && $quality <= 100 ) ? ( $quality ? (string) $quality : '0' ) : '0' );
$suffix = self::base_convert_arbitrary( $suffix, 10, 36 );
// use this to check if cropped image already exists, so we can return that instead
$dst_rel_path = str_replace( '.' . $ext, '', basename( $img_path ) );
// If opacity is set, change the image type to png
if ( isset( $opacity ) ) {
$ext = 'png';
}
// Create the upload subdirectory, this is where
// we store all our generated images
if ( defined( 'BFITHUMB_UPLOAD_DIR' ) ) {
$upload_dir .= "/" . BFITHUMB_UPLOAD_DIR;
$upload_url .= "/" . BFITHUMB_UPLOAD_DIR;
} else {
$upload_dir .= "/bfi_thumb";
$upload_url .= "/bfi_thumb";
}
if ( ! is_dir( $upload_dir ) ) {
wp_mkdir_p( $upload_dir );
}
// desination paths and urls
$destfilename = "{$upload_dir}/{$dst_rel_path}-{$suffix}.{$ext}";
// The urls generated have lower case extensions regardless of the original case
$ext = strtolower( $ext );
$img_url = "{$upload_url}/{$dst_rel_path}-{$suffix}.{$ext}";
// if file exists, just return it
if ( @file_exists( $destfilename ) && getimagesize( $destfilename ) ) {
} else {
// perform resizing and other filters
$editor = wp_get_image_editor( $img_path );
if ( is_wp_error( $editor ) ) return false;
/*
* Perform image manipulations
*/
if ( $crop_only === false ) {
if ( ( isset( $width ) && $width ) || ( isset( $height ) && $height ) ) {
if ( is_wp_error( $editor->resize( isset( $width ) ? $width : null, isset( $height ) ? $height : null, isset( $crop ) ? $crop : false ) ) ) {
// Instead of returning false, return the original yo?
return $url;
}
}
} else {
if ( is_wp_error( $editor->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h ) ) ) {
return $url;
}
}
if ( isset( $negate ) ) {
if ( $negate ) {
if ( is_wp_error( $editor->negate() ) ) {
return $url;
}
}
}
if ( isset( $opacity ) ) {
if ( is_wp_error( $editor->opacity( $opacity ) ) ) {
return $url;
}
}
if ( isset( $grayscale ) ) {
if ( $grayscale ) {
if ( is_wp_error( $editor->grayscale() ) ) {
return $url;
}
}
}
if ( isset( $color ) ) {
if ( is_wp_error( $editor->colorize( $color ) ) ) {
return $url;
}
}
// set the image quality (1-100) to save this image at
if ( isset( $quality ) && $quality > 0 && $quality <= 100 && $ext != 'png' ) {
$editor->set_quality( $quality );
}
// save our new image
$mime_type = isset( $opacity ) ? 'image/png' : null;
$resized_file = $editor->save( $destfilename, $mime_type );
}
//return the output
if ( $single ) {
$image = $img_url;
} else {
//array return
$image = array (
0 => $img_url,
1 => isset( $dst_w ) ? $dst_w : $orig_w,
2 => isset( $dst_h ) ? $dst_h : $orig_h,
);
}
return $image;
}
/** Shortens a number into a base 36 string
*
* @param $number - string a string of numbers to convert
* @param $fromBase - starting base
* @param $toBase - base to convert the number to
* @return string base converted characters
*/
protected static function base_convert_arbitrary( $number, $fromBase, $toBase ) {
$digits = '0123456789abcdefghijklmnopqrstuvwxyz';
$length = strlen( $number );
$result = '';
$nibbles = array();
for ( $i = 0; $i < $length; ++$i ) {
$nibbles[ $i ] = strpos( $digits, $number[ $i ] );
}
do {
$value = 0;
$newlen = 0;
for ( $i = 0; $i < $length; ++$i ) {
$value = $value * $fromBase + $nibbles[ $i ];
if ( $value >= $toBase ) {
$nibbles[ $newlen++ ] = (int) ( $value / $toBase );
$value %= $toBase;
} else if ( $newlen > 0 ) {
$nibbles[ $newlen++ ] = 0;
}
}
$length = $newlen;
$result = $digits[ $value ] . $result;
}
while ( $newlen != 0 );
return $result;
}
}

View File

@@ -0,0 +1,123 @@
<?php
namespace WPDRMS\ASP\Cache;
/* Prevent direct access */
use WPDRMS\ASP\Utils\FileManager;
defined('ABSPATH') or die("You can't access this file directly.");
class TextCache {
private $interval;
private $cache_name;
private $cache_path;
private $last_file_mtime = 0;
private $last_file_path = "";
private static $unique_db_prefix = '_aspdbcache_';
public function __construct($cache_path, $cache_name = "txt", $interval = 36000) {
$this->cache_name = $cache_name;
$this->cache_path = $cache_path;
$this->interval = $interval;
}
public function getCache($file = "") {
$file = $this->filePath($file);
$this->last_file_path = $file;
if ( FileManager::instance()->isFile($file) ) {
$filetime = FileManager::instance()->mtime($file);
} else {
return false;
}
if ( $filetime === false || (time() - $filetime) > $this->interval )
return false;
$this->last_file_mtime = $filetime;
return FileManager::instance()->read($file);
}
public function getDBCache($handle) {
$this->last_file_path = $handle;
$data = get_option(self::$unique_db_prefix . $handle, '');
if ( isset($data['time'], $data['content']) && (time() - $data['time']) <= $this->interval ) {
$this->last_file_mtime = $data['time'];
return $data['content'];
} else {
return false;
}
}
public function getLastFileMtime(): int {
return $this->last_file_mtime;
}
public function setCache($content, $file = "") {
if ( $file === '' ) {
$file = $this->last_file_path;
if ( $file === '' )
return;
} else {
$file = $this->filePath($file);
}
if ( FileManager::instance()->isFile($file) ) {
$filetime = FileManager::instance()->mtime($file);
} else {
$filetime = 0;
}
if ( (time() - $filetime) > $this->interval ) {
FileManager::instance()->write($file, $content);
}
}
public function setDBCache($content, $handle = '') {
if ( $handle === '' ) {
$handle = $this->last_file_path;
if ( $handle === '' )
return;
}
$data = get_option(self::$unique_db_prefix . $handle, '');
if ( isset($data['time']) ) {
if ( (time() - $data['time']) > $this->interval ) {
update_option(self::$unique_db_prefix . $handle, array(
'time' => time(),
'content' => $content
));
}
} else {
update_option(self::$unique_db_prefix . $handle, array(
'time' => time(),
'content' => $content
));
}
}
public static function generateSCFiles() {
$content = FileManager::instance()->read(ASP_PATH . "sc-config.php");
if ( $content !== '' ) {
$content = preg_replace('/ASP_SC_CACHE_INTERVAL = (.*?);/', 'ASP_SC_CACHE_INTERVAL = ' . wd_asp()->o['asp_caching']['cachinginterval'] . ';', $content);
$content = preg_replace('/ASP_SC_CACHE_PATH = (.*?);/', "ASP_SC_CACHE_PATH = '" . wd_asp()->cache_path . "';", $content);
$content = preg_replace('/ASP_SC_ADMIN_AJAX_PATH = (.*?);/', "ASP_SC_ADMIN_AJAX_PATH = '" . ABSPATH . 'wp-admin/admin-ajax.php' . "';", $content);
FileManager::instance()->write(ASP_PATH . "sc-config.php", $content);
}
}
public static function clearDBCache(): int {
global $wpdb;
$query = $wpdb->prepare("DELETE FROM $wpdb->options WHERE option_name LIKE '%s'", self::$unique_db_prefix . '%');
$res = $wpdb->query($query);
if ( !is_wp_error($res) ) {
return intval($res);
} else {
return 0;
}
}
private function filePath($file): string {
return trailingslashit($this->cache_path) . $this->cache_name . "_" . $file . ".wpd";
}
}

View File

@@ -0,0 +1,75 @@
<?php
namespace WPDRMS\ASP\Core;
use WPDRMS\ASP\Asset\AssetInterface;
use WPDRMS\ASP\BlockEditor\ASPBlock;
use WPDRMS\ASP\BlockEditor\BlockEditorAssets;
use WPDRMS\ASP\BlockEditor\BlockInterface;
use WPDRMS\ASP\Integration\Imagely\NextGenGallery;
use WPDRMS\ASP\Options\OptionAssets;
use WPDRMS\ASP\Options\Routes\DirectoriesRoute;
use WPDRMS\ASP\Options\Routes\IndexTableOptionsRoute;
use WPDRMS\ASP\Options\Routes\SearchOptionsRoute;
use WPDRMS\ASP\Options\Routes\TaxonomyTermsRoute;
use WPDRMS\ASP\Patterns\SingletonTrait;
use WPDRMS\ASP\Rest\RestInterface;
use WPDRMS\ASP\Rest\TimedModalRoutes;
/**
* Returns all class instances for a given interface name
*
* @see .phpstorm.meta.php for corrected type hints
*/
class Factory {
use SingletonTrait;
const SUPPORTED_INTERFACES = array(
'Rest' => array(
TimedModalRoutes::class,
DirectoriesRoute::class,
TaxonomyTermsRoute::class,
SearchOptionsRoute::class,
IndexTableOptionsRoute::class,
),
'Asset' => array(
OptionAssets::class,
),
'Block' => array(
ASPBlock::class,
),
'Integration' => array(
NextGenGallery::class,
),
);
/**
* Get all the objects array for a given interface
*
* @param key-of<self::SUPPORTED_INTERFACES> $interface_name
* @param mixed[] $args
*/
public function get( string $interface_name, ?array $args = null ): array {
if ( !isset(self::SUPPORTED_INTERFACES[ $interface_name ]) ) {
return array();
}
$classes = self::SUPPORTED_INTERFACES[ $interface_name ];
return array_map(
function ( $class_name ) use ( $args ) {
if ( method_exists($class_name, 'instance') ) {
if ( is_array($args) ) {
return $class_name::instance(...$args);
} else {
return $class_name::instance();
}
}
if ( is_array($args) ) {
return new $class_name(...$args); // @phpstan-ignore-line
} else {
return new $class_name(); // @phpstan-ignore-line
}
},
$classes
);
}
}

View File

@@ -0,0 +1,176 @@
<?php
namespace WPDRMS\ASP\Core;
if (!defined('ABSPATH')) die('-1');
class Globals {
/**
* The plugin options and defaults
*
* @var array
*/
public $options;
/**
* The plugin options and defaults (shorthand)
*
* @var array
*/
public $o;
/**
* Instance of the init class
*
* @var Init
*/
public $init;
/**
* Instance of the instances class
*
* @var Instances
*/
public $instances;
/**
* Instance of the instances class
*
* @var \WPDRMS\ASP\Misc\PriorityGroups
*/
public $priority_groups;
/**
* Instance of the updates manager
*
* @var \asp_updates
*/
public $updates;
/**
* Instance of the database manager
*
* @var \WPDRMS\ASP\Database\Manager
*/
public $db;
/**
* Instance of the REST API manager
*
* @var \WPDRMS\ASP\API\REST0\Rest
*/
public $rest_api;
/**
* Instance of the manager
*
* @var Manager
*/
public $manager;
/**
* Instance of the manager
*
* @var WPDRMS\ASP\Frontend\FiltersManager
*/
public $front_filters;
/**
* Instance of the manager
*
* @var \WD_ASP_Instant
*/
public $instant;
/**
* Instance of the scripts manager
*
* @var \WPDRMS\ASP\Asset\Script\Manager
*/
public $script_manager;
/**
* Instance of the scripts manager
*
* @var \WPDRMS\ASP\Asset\Css\Manager
*/
public $css_manager;
/**
* Instance of the legacy scripts manager
*
* @var ScriptsLegacy
*/
public $scripts_legacy;
/**
* Array of ASP tables
*
* @var array
*/
public $tables;
/**
* Holds the correct table prefix for ASP tables
*
* @var string
*/
public $_prefix;
/**
* The upload directory for the plugin
*
* @var string
*/
public $upload_dir = "asp_upload";
/**
* The upload directory for the BFI thumb library
*
* @var string
*/
public $bfi_dir = "bfi_thumb";
/**
* The upload path
*
* @var string
*/
public $upload_path;
/**
* The BFI lib upload path
*
* @var string
*/
public $bfi_path;
/**
* The upload URL
*
* @var string
*/
public $upload_url;
/**
* Cache subdirectory name for CSS/JS assets
*
* @var string
*/
public $global_cache_path;
/**
* Cache path for CSS/JS assets
*
* @var string
*/
public $cache_path;
/**
* Cache url for CSS/JS assets
*
* @var string
*/
public $cache_url;
}

View File

@@ -0,0 +1,570 @@
<?php
namespace WPDRMS\ASP\Core;
use WPDRMS\ASP\Asset\Script;
use WPDRMS\ASP\Cache\TextCache;
use WPDRMS\ASP\Database;
use WPDRMS\ASP\Hooks\Ajax\DeleteCache;
use WPDRMS\ASP\Patterns\SingletonTrait;
use WPDRMS\ASP\Utils\FileManager;
use WPDRMS\ASP\Utils\Plugin;
defined('ABSPATH') or die("You can't access this file directly.");
/**
* Class aspInit
*
* AJAX SEARCH PRO initializator Class
*/
class Init {
use SingletonTrait;
private static $new_install = false;
private function __construct() {
wd_asp()->db = Database\Manager::getInstance();
// Before the ASP_Helpers::previousVersion is ever executed, _asp_version option does not exist
if ( get_option('_asp_version', false) === false ) {
self::$new_install = true;
}
}
/**
* Runs on activation OR if this->safety_check() detects a silent change
*/
public function activate() {
// Includes the index table creation as well
Database\Manager::getInstance()->create();
$this->activation_only_backwards_compatibility_fixes();
FileManager::instance()->createRequiredDirectories();
DeleteCache::getInstance()->handle(false);
// Was the plugin previously installed, and updated?
if ( Plugin::previousVersion(ASP_CURR_VER_STRING, '<') ) {
update_option('asp_recently_updated', 1);
}
set_transient('asp_just_activated', 1);
// Add functions to asp_scheduled_activation_events to schedule background process events to the activation hook
wp_schedule_single_event( time() + 15, 'asp_scheduled_activation_events' );
}
/**
* Checks if the user correctly updated the plugin and fixes if not
*/
public function safety_check() {
// Run the re-activation actions if this is actually a newer version
if ( Plugin::previousVersion(ASP_CURR_VER_STRING, '<', true) ) {
$this->activate();
// Run a backwards compatibility check
$this->backwards_compatibility_fixes();
wd_asp()->css_manager->generator->generate();
// Take a note on the recent update
update_option('asp_recently_updated', 1);
} else {
// Was the plugin just activated, without version change?
if ( get_transient('asp_just_activated') !== false ) {
// Check the folders, they might have been deleted by accident
FileManager::instance()->createRequiredDirectories();
// Run a backwards compatibility check
$this->backwards_compatibility_fixes();
wd_asp()->css_manager->generator->generate();
delete_transient('asp_just_activated');
}
}
}
/**
* Fix known backwards incompatibilities, only running at plugin activation
*/
public function activation_only_backwards_compatibility_fixes() {
// Turn off the jquery script versions
$comp = wd_asp()->o['asp_compatibility'];
if ( !self::$new_install ) {
if ( $comp['js_source'] == 'min' || $comp['js_source'] == 'min-scoped' ) {
wd_asp()->o['asp_compatibility']['js_source'] = 'jqueryless-min';
} elseif ( $comp['js_source'] == 'nomin' || $comp['js_source'] == 'nomin-scoped' ) {
wd_asp()->o['asp_compatibility']['js_source'] = 'jqueryless-nomin';
}
asp_save_option('asp_compatibility');
}
// Old way of reatining the browser back button to new one
if ( isset($comp['js_retain_popstate']) && $comp['js_retain_popstate'] == 1 ) {
foreach ( wd_asp()->instances->get() as $si ) {
$id = $si['id'];
$sd = $si['data'];
$sd['trigger_update_href'] = 1;
wd_asp()->instances->update($id, $sd);
}
unset($comp['js_retain_popstate']);
wd_asp()->o['asp_compatibility'] = $comp;
asp_save_option('asp_compatibility');
}
}
/**
* Fix known backwards incompatibilities
*/
public function backwards_compatibility_fixes() {
// Index table option fixes
$ito = wd_asp()->o['asp_it_options'];
if ( isset($ito['it_post_types']) && !is_array($ito['it_post_types']) ) {
$ito['it_post_types'] = explode('|', $ito['it_post_types']);
foreach ( $ito['it_post_types'] as $ck => $ct ) {
if ( $ct == '' ) {
unset($ito['it_post_types'][ $ck ]);
}
}
wd_asp()->o['asp_it_options']['it_post_types'] = $ito['it_post_types'];
asp_save_option('asp_it_options');
}
$ana = wd_asp()->o['asp_analytics'];
// Analytics Options fixes 4.18
if ( isset($ana['analytics']) && ( $ana['analytics'] == 1 || $ana['analytics'] == 'pageview' ) ) {
wd_asp()->o['asp_analytics']['analytics'] = 'event';
asp_save_option('asp_analytics');
}
// 4.18.6 Pool sizes no longer slow down page load on index table options
delete_option('_asp_it_pool_sizes');
// 4.22.6 - These were removed at the same time
$comp = wd_asp()->o['asp_compatibility'];
if ( isset($comp['forceinlinestyles']) ) {
if ( $comp['forceinlinestyles'] ) {
$comp['css_loading_method'] = 'inline';
}
unset($comp['forceinlinestyles']);
unset($comp['css_async_load']);
unset($comp['load_in_footer']);
unset($comp['load_mcustom_js']);
wd_asp()->o['asp_compatibility'] = $comp;
asp_save_option('asp_compatibility');
}
/*
* Search instance option fixes
*
* - Get instances
* - Check options
* - Transition to new options based on old ones
* - Save instances
*/
foreach ( wd_asp()->instances->get() as $si ) {
$id = $si['id'];
$sd = $si['data'];
// -------------------------- 4.10 ------------------------------
// Primary and secondary fields
$values = array( '-1', '0', '1', '2', 'c__f' );
$adv_fields = array(
'primary_titlefield',
'secondary_titlefield',
'primary_descriptionfield',
'secondary_descriptionfield',
);
foreach ( $adv_fields as $field ) {
// Force string conversion for proper comparision
if ( !in_array($sd[ $field ] . '', $values) ) {
// Custom field value is selected
$sd[ $field . '_cf' ] = $sd[ $field ];
$sd[ $field ] = 'c__f';
}
}
// -------------------------- 4.10 ------------------------------
// ------------------------- 4.10.4 -----------------------------
// Autocomplete aggreagated to one option only.
if ( isset($sd['autocomplete_mobile']) ) {
if ( $sd['autocomplete_mobile'] == 1 && $sd['autocomplete'] == 1 ) {
$sd['autocomplete'] = 1;
} elseif ( $sd['autocomplete_mobile'] == 1 ) {
$sd['autocomplete'] = 3;
} elseif ( $sd['autocomplete'] == 1 ) {
$sd['autocomplete'] = 2;
} else {
$sd['autocomplete'] = 0;
}
unset($sd['autocomplete_mobile']);
}
// ------------------------- 4.10.4 -----------------------------
// ------------------------- 4.11 -------------------------------
// Autocomplete aggreagated to one option only.
if ( !isset($sd['frontend_fields']['unselected']) ) {
$sd['frontend_fields']['unselected'] = array();
}
if ( isset($sd['showexactmatches'], $sd['exactmatchestext']) ) {
$sd['frontend_fields']['labels']['exact'] = $sd['exactmatchestext'];
if ( $sd['showexactmatches'] == 0 ) {
$sd['frontend_fields']['unselected'][] = 'exact';
$sd['frontend_fields']['selected'] =
array_diff( $sd['frontend_fields']['selected'], array( 'exact' ) );
}
}
if ( isset($sd['showsearchintitle'], $sd['searchintitletext']) ) {
$sd['frontend_fields']['labels']['title'] = $sd['searchintitletext'];
if ( $sd['showsearchintitle'] == 0 ) {
$sd['frontend_fields']['unselected'][] = 'title';
$sd['frontend_fields']['selected'] =
array_diff( $sd['frontend_fields']['selected'], array( 'title' ) );
}
}
if ( isset($sd['showsearchincontent'], $sd['searchincontenttext']) ) {
$sd['frontend_fields']['labels']['content'] = $sd['searchincontenttext'];
if ( $sd['showsearchincontent'] == 0 ) {
$sd['frontend_fields']['unselected'][] = 'content';
$sd['frontend_fields']['selected'] =
array_diff( $sd['frontend_fields']['selected'], array( 'content' ) );
}
}
if ( isset($sd['showsearchinexcerpt'], $sd['searchinexcerpttext']) ) {
$sd['frontend_fields']['labels']['excerpt'] = $sd['searchinexcerpttext'];
if ( $sd['showsearchinexcerpt'] == 0 ) {
$sd['frontend_fields']['unselected'][] = 'excerpt';
$sd['frontend_fields']['selected'] =
array_diff( $sd['frontend_fields']['selected'], array( 'excerpt' ) );
}
}
// ------------------------- 4.11 -------------------------------
// ------------------------- 4.11.6 -----------------------------
// User meta fields to array
if ( isset($sd['user_search_meta_fields']) && !is_array($sd['user_search_meta_fields']) ) {
$sd['user_search_meta_fields'] = explode(',', $sd['user_search_meta_fields']);
foreach ( $sd['user_search_meta_fields'] as $umk =>$umv ) {
$sd['user_search_meta_fields'][ $umk ] = trim($umv);
if ( $sd['user_search_meta_fields'][ $umk ] == '' ) {
unset($sd['user_search_meta_fields'][ $umk ]);
}
}
}
// ------------------------- 4.11.6 -----------------------------
// ------------------------- 4.11.10 ----------------------------
// Before, this was a string
if ( isset($sd['customtypes']) && !is_array($sd['customtypes']) ) {
$sd['customtypes'] = explode('|', $sd['customtypes']);
foreach ( $sd['customtypes'] as $ck => $ct ) {
if ( $ct == '' ) {
unset($sd['customtypes'][ $ck ]);
}
}
}
// No longer exists
if ( isset($sd['selected-customtypes']) ) {
unset($sd['selected-customtypes']);
}
// No longer exists
if ( isset($sd['searchinpages']) ) {
if ( $sd['searchinpages'] == 1 && !in_array('page', $sd['customtypes']) ) {
array_unshift($sd['customtypes'], 'page');
}
unset($sd['searchinpages']);
}
// No longer exists
if ( isset($sd['searchinposts']) ) {
if ( $sd['searchinposts'] == 1 && !in_array('post', $sd['customtypes']) ) {
array_unshift($sd['customtypes'], 'post');
}
unset($sd['searchinposts']);
}
// ------------------------- 4.11.10 ----------------------------
// ------------------------- 4.12 -------------------------------
if ( is_numeric($sd['i_item_width']) ) {
$sd['i_item_width'] = $sd['i_item_width'] . 'px';
}
// ------------------------- 4.12 -------------------------------
// ------------------------- 4.13.1 -----------------------------
$font_sources = array(
'inputfont',
'descfont',
'titlefont',
'authorfont',
'datefont',
'showmorefont',
'groupfont',
'exsearchincategoriestextfont',
'groupbytextfont',
'settingsdropfont',
'prestitlefont',
'presdescfont',
'pressubtitlefont',
'search_text_font',
);
if ( isset($sd['inputfont']) && strpos($sd['inputfont'], '--g--') !== false ) {
/**
* Remove the unneccessary --g-- tags and quotes
*/
foreach ( $font_sources as $fk ) {
if ( isset($sd[ $fk ]) ) {
$sd[ $fk ] = str_replace(array( '--g--', '"', "'" ), '', $sd[ $fk ]);
}
}
}
if ( isset($sd['results_order']) && strpos($sd['results_order'], 'peepso') === false ) {
$sd['results_order'] .= '|peepso_groups|peepso_activities';
}
if ( isset($sd['groupby_content_type']) && !isset($sd['groupby_content_type']['peepso_groups']) ) {
$sd['groupby_content_type']['peepso_groups'] = 'Peepso Groups';
$sd['groupby_content_type']['peepso_activities'] = 'Peepso Activities';
}
// ------------------------- 4.13.1 -----------------------------
// ------------------------- 4.14.4 -----------------------------
if ( isset($sd['frontend_fields']['labels']['comments']) ) {
unset($sd['frontend_fields']['labels']['comments']);
}
$sd['frontend_fields']['selected'] = array_diff( $sd['frontend_fields']['selected'], array( 'comments' ) );
$sd['frontend_fields']['unselected'] = array_diff( $sd['frontend_fields']['unselected'], array( 'comments' ) );
$sd['frontend_fields']['checked'] = array_diff( $sd['frontend_fields']['checked'], array( 'comments' ) );
// ------------------------- 4.14.4 -----------------------------
// ------------------------- 4.14.5 -----------------------------
// For non-existence checks use the raw_data array
if ( !isset($si['raw_data']['i_item_width_tablet']) ) {
$sd['i_item_width_tablet'] = $sd['i_item_width'];
$sd['i_item_width_phone'] = $sd['i_item_width'];
}
// For non-existence checks use the raw_data array
if ( !isset($si['raw_data']['i_item_height_tablet']) ) {
$sd['i_item_height_tablet'] = $sd['i_item_height'];
$sd['i_item_height_phone'] = $sd['i_item_height'];
}
// For non-existence checks use the raw_data array
if ( !isset($si['raw_data']['box_width_tablet']) ) {
$sd['box_width_tablet'] = $sd['box_width'];
$sd['box_width_phone'] = $sd['box_width'];
}
// ------------------------- 4.14.5 -----------------------------
// ------------------------- 4.15 -------------------------------
if ( is_numeric($sd['i_item_height']) ) {
$sd['i_item_height'] = $sd['i_item_height'] . 'px';
}
if ( is_numeric($sd['i_item_height_tablet']) ) {
$sd['i_item_height_tablet'] = $sd['i_item_height_tablet'] . 'px';
}
if ( is_numeric($sd['i_item_height_phone']) ) {
$sd['i_item_height_phone'] = $sd['i_item_height_phone'] . 'px';
}
// ------------------------- 4.15 -------------------------------
// ------------------------- 4.17 -------------------------------
if ( !empty($sd['image_default'])
&& !isset($si['raw_data']['tax_image_default'], $si['raw_data']['tax_image_default']) ) {
$sd['tax_image_default'] = $sd['image_default'];
$sd['user_image_default'] = $sd['image_default'];
}
// ------------------------- 4.17 -------------------------------
// ------------------------- 4.18.2 -----------------------------
if ( isset($sd['jquery_chosen_nores']) ) {
$sd['jquery_select2_nores'] = $sd['jquery_chosen_nores'];
}
// ------------------------- 4.18.2 -----------------------------
// ------------------------- 4.18.8 -----------------------------
if ( !isset($si['raw_data']['tax_res_showdescription']) ) {
$sd['tax_res_showdescription'] = $sd['showdescription'];
$sd['user_res_showdescription'] = $sd['showdescription'];
$sd['tax_res_descriptionlength'] = $sd['descriptionlength'];
$sd['user_res_descriptionlength'] = $sd['descriptionlength'];
}
// ------------------------- 4.18.8 -----------------------------
// ------------------------- 4.20.5 -----------------------------
// The mobile settings state can be forced without forcing the hover
if ( Plugin::previousVersion('4.20.4') ) {
if ( $sd['mob_force_sett_hover'] == 0 ) {
$sd['mob_force_sett_state'] = 'none';
}
}
// ------------------------- 4.20.5 -----------------------------
// ------------------------- 4.26.16 ----------------------------
if ( isset($sd['orderby']) ) {
$sd['orderby_secondary'] = $sd['orderby'];
unset($sd['orderby']);
}
if ( isset($sd['horizontal_res_height']) ) {
$sd['h_item_height'] = $sd['horizontal_res_height'];
$sd['h_item_height_tablet'] = $sd['horizontal_res_height'];
$sd['h_item_height_phone'] = $sd['horizontal_res_height'];
unset($sd['horizontal_res_height']);
}
if ( isset($sd['hreswidth']) ) {
$sd['h_item_width'] = $sd['hreswidth'];
$sd['h_item_width_tablet'] = $sd['hreswidth'];
$sd['h_item_width_phone'] = $sd['hreswidth'];
unset($sd['hreswidth']);
}
if ( isset($sd['hor_img_height']) ) {
$sd['h_image_height'] = $sd['hor_img_height'];
$sd['h_image_height_tablet'] = $sd['hor_img_height'];
$sd['h_image_height_phone'] = $sd['hor_img_height'];
unset($sd['hor_img_height']);
}
// ---------------------------- 4.27 ----------------------------
if ( Plugin::previousVersion('4.26.16') ) {
foreach ( array(
'advtitlefield',
'advdescriptionfield',
'user_search_advanced_title_field',
'user_search_advanced_description_field',
) as $fk
) {
$sd[ $fk ] = str_replace(
array( '{titlefield}', '{descriptionfield}' ),
array( "{result_field field_name='title' hash='x'}", "{result_field field_name='content' hash='y'}" ),
$sd[ $fk ]
);
}
}
// ----------------- Unset some unused search data --------------
// Leave this here, so it is executed as last
$values = array(
// from 4.10
'magnifierimage_selects',
'settingsimage_selects',
'loadingimage_selects',
'i_res_magnifierimage_selects',
'i_pagination_arrow_selects',
'keyword_logic_def',
'user_search_title_field_def',
'frontend_search_settings_position_def',
'term_logic_def',
'cf_logic_def',
'resultstype_def',
'resultsposition_def',
'box_compact_float_def',
'box_compact_position_def',
'keyword_suggestion_source_def',
'bpgroupstitle_def',
'bpgroupstitle',
'settingsimagepos_def',
'blogtitleorderby_def',
'i_ifnoimage_def',
'i_pagination_position_def',
'weight_def',
'user_search_description_field_def',
'triggeronclick',
'triggeronreturn',
'redirectonclick',
'redirect_click_to',
'redirect_on_enter',
'redirect_enter_to',
'mob_trigger_on_click',
// from 4.11
'showexactmatches',
'exactmatchestext',
'showsearchintitle',
'searchintitletext',
'showsearchincontent',
'searchincontenttext',
'showsearchincomments',
'searchincommentstext',
'showsearchinexcerpt',
'searchinexcerpttext',
// from 4.18.2
'jquery_chosen_nores',
);
foreach ( $values as $v ) {
if ( isset($sd[ $v ]) ) {
unset($sd[ $v ]);
}
}
// At the end, update
wd_asp()->instances->update($id, $sd);
}
}
public function pluginReset( $triggerActivate = true ) {
$options = array(
'asp_version',
'_asp_version',
'asp_glob_d',
'asp_performance_def',
'asp_performance',
'asp_it_def',
'asp_it',
'asp_it_options',
'asp_analytics_def',
'asp_analytics',
'asp_caching_def',
'asp_caching',
'asp_compatibility_def',
'asp_compatibility',
'asp_defaults',
'asp_st_override',
'asp_woo_override',
'asp_stat',
'asp_updates',
'asp_updates_lc',
'asp_media_query',
'asp_performance_stats',
'asp_recently_updated',
'asp_fonts',
'_asp_tables',
'_asp_priority_groups',
'_asp_it_pool_sizes',
'asp_license_data',
);
foreach ( $options as $o ) {
delete_option($o);
delete_site_option($o);
}
wp_clear_scheduled_hook('asp_cron_it_extend');
Script\Manager::getInstance()->cleanup();
if ( $triggerActivate ) {
$this->activate();
}
}
public function pluginWipe() {
// Options
$this->pluginReset( false );
// Meta
if ( is_multisite() ) {
global $switched;
$sites = get_sites(array( 'fields' => 'ids' ));
foreach ( $sites as $site ) {
switch_to_blog($site);
delete_metadata('post', 1, '_asp_additional_tags', '', true);
delete_metadata('post', 1, '_asp_metadata', '', true);
TextCache::clearDBCache(); // Delete options cache
restore_current_blog();
}
} else {
delete_metadata('post', 1, '_asp_additional_tags', '', true);
delete_metadata('post', 1, '_asp_metadata', '', true);
TextCache::clearDBCache(); // Delete options cache
}
// Database
wd_asp()->db->delete();
FileManager::instance()->removeRequiredDirectories();
// Deactivate
deactivate_plugins(ASP_FILE);
}
}

View File

@@ -0,0 +1,766 @@
<?php
namespace WPDRMS\ASP\Core;
use WP_Error;
use WPDRMS\ASP\Core\Models\SearchInstance;
use WPDRMS\ASP\Misc\Themes;
use WPDRMS\ASP\Options\Data\SearchOptions;
use WPDRMS\ASP\Patterns\SingletonTrait;
defined('ABSPATH') or die("You can't access this file directly.");
/**
* Class WD_ASP_Instances
*
* This class handles the data transfer between code and instance data
*
* @class WD_ASP_Instances
* @version 1.0
* @package AjaxSearchPro/Classes/Core
* @category Class
* @author Ernest Marcinko
*/
class Instances {
use SingletonTrait;
public const ALL_INSTANCES = -1;
/**
* This holds the search instances
*
* @var SearchInstance[]
*/
private array $search_instances;
/**
* This holds the search instances without data
*
* @var array{
* id: int,
* name: string
* }
*/
private array $instances_no_data;
/**
* When updating or first demand, this variable sets to true, telling that instances need re-parsing
*
* @var bool
*/
private bool $refresh = true;
/**
* The search instances init script JSON data
*
* @var string[]
*/
private array $script_data = array();
/**
* Gets the search instance if exists
*
* @param int $id
* @param bool $force_refresh
* @param bool|int $check_ownership
* @return ( $id is positive-int ? SearchInstance : SearchInstance[] )
*/
public function get(int $id = self::ALL_INSTANCES, bool $force_refresh = false, $check_ownership = false ) {
if ( $this->refresh || $force_refresh ) {
$this->init();
$this->refresh = false;
}
if ( $check_ownership !== false && !is_super_admin() ) {
if ( is_int($check_ownership) ) {
$user_id = $check_ownership;
} else {
$user_id = intval( get_current_user_id() );
}
foreach ( $this->search_instances as $key => $inst ) {
if ( $inst['data']['owner'] != 0 && $inst['data']['owner'] != $user_id ) {
unset($this->search_instances[ $key ]);
}
}
}
if ( $id > -1 ) {
return isset($this->search_instances[ $id ]) ? $this->search_instances[ $id ] : array();
}
return $this->search_instances;
}
/**
* Get an option value for a search instance ID
*
* @param $instance
* @param $option_name
* @return false|mixed
*/
public function getOption( $instance, $option_name ) {
if ( $this->exists($instance) ) {
$instance = $this->get($instance);
return $instance['data'][ $option_name ] ?? false;
}
return false;
}
/**
* Temporary changes the search instance data within the cache variable (not permanent)
*
* @param int $id
* @param array $data
* @return bool|array
*/
public function set( $id = 0, $data = array() ) {
if ( $this->refresh ) {
$this->init();
$this->refresh = false;
}
if ( isset($this->search_instances[ $id ]) ) {
$this->search_instances[ $id ]['data'] = array_merge($this->search_instances[ $id ]['data'], $data);
return true;
}
return false;
}
/**
* Gets the search instance if exists, without data
*
* @param int $id
* @param bool $force_refresh
* @return array
*/
public function getWithoutData( $id = self::ALL_INSTANCES, $force_refresh = false ) {
if ( $this->refresh || $force_refresh ) {
$this->init();
$this->refresh = false;
}
if ( $id > -1 ) {
return isset($this->instances_no_data[ $id ]) ? $this->instances_no_data[ $id ] : array();
}
return $this->instances_no_data;
}
/**
* Checks if the given search instance exists
*
* @param $id
* @return bool
*/
public function exists( $id = false ) {
if ( $this->refresh ) {
$this->init();
$this->refresh = false;
}
if ( $id === false ) {
return count($this->search_instances) > 0;
} else {
return isset($this->search_instances[ $id ]);
}
}
/**
* Create a new search instance with the default options set
*
* @param $name
* @param int $owner User ID of the owner
* @return bool|int
*/
public function add( $name, $owner = 0 ) {
global $wpdb;
$this->refresh = true;
$data = wd_asp()->options['asp_defaults'];
$data['owner'] = intval($owner);
if (
$wpdb->query(
'INSERT INTO ' . wd_asp()->db->table('main') . "
(name, data) VALUES
('" . esc_sql($name) . "', '" . wd_mysql_escape_mimic(json_encode($data)) . "')"
) !== false
) {
return $wpdb->insert_id;
}
return false;
}
/**
* Import the search from the Lite version, as a new search instance
*
* @param $name
* @param int $owner User ID of the owner
* @return bool|int
*/
public function importFromLite( $name, $owner = 0 ) {
global $wpdb;
$this->refresh = true;
$data = wd_asp()->options['asp_defaults'];
$data['owner'] = intval($owner);
$lite = get_option('asl_options', array());
if ( count($lite) > 0 && get_option('asl_version', 0) > 4732 ) {
// --- Resolve the options from the lite version ---
// 1. Resolve these as-is, no change required
$as_is = array(
'keyword_logic',
'triggerontype',
'customtypes',
'searchintitle',
'searchincontent',
'searchinexcerpt',
'search_in_permalinks',
'search_in_ids',
'search_all_cf',
'customfields',
'post_status',
'override_default_results',
'override_method',
'exactonly',
'exact_match_location',
'searchinterms',
'charcount',
'itemscount',
'orderby_primary',
'orderby_secondary',
'show_images',
'image_width',
'image_height',
'image_source1',
'image_source2',
'image_source3',
'image_source4',
'image_source5',
'image_source_featured',
'image_custom_field',
'show_frontend_search_settings',
'box_width',
'resultsposition',
'defaultsearchtext',
'showmoreresults',
'showmoreresultstext',
'v_res_max_height',
'results_click_blank',
'scroll_to_results',
'resultareaclickable',
'close_on_document_click',
'show_close_icon',
'showauthor',
'showdate',
'showdescription',
'descriptionlength',
'description_context',
'noresultstext',
'didyoumeantext',
'autocomplete',
'shortcode_op',
'striptagsexclude',
'excludeposts',
'wpml_compatibility',
'polylang_compatibility',
'click_action_location',
'return_action_location',
'showcustomtypes',
// 4.9
'primary_titlefield',
'primary_descriptionfield',
'primary_titlefield_cf',
'primary_descriptionfield_cf',
'advtitlefield',
'advdescriptionfield',
'kw_exceptions',
'kw_exceptions_e',
'single_highlight',
'single_highlightwholewords',
'single_highlightcolor',
'single_highlightbgcolor',
'single_highlight_scroll',
'single_highlight_offset',
'single_highlight_selector',
'image_display_mode',
);
foreach ( $as_is as $key ) {
if ( isset($lite[ $key ]) ) {
$data[ $key ] = $lite[ $key ];
}
}
// 2. Resolve by difference in keys only
$resolve_keys = array(
'trigger_on_facet_change' => 'trigger_on_facet',
'titlefield' => 'primary_titlefield',
'descriptionfield' => 'primary_descriptionfield',
'titlefield_cf' => 'primary_titlefield_cf',
'descriptionfield_cf' => 'primary_descriptionfield_cf',
'kw_suggestions' => 'keywordsuggestions',
'kw_length' => 'keyword_suggestion_length',
'kw_count' => 'keyword_suggestion_count',
'kw_google_lang' => 'keywordsuggestionslang',
'kw_exceptions' => 'autocompleteexceptions',
'kw_highlight' => 'highlight',
'kw_highlight_whole_words' => 'highlightwholewords',
'highlight_color' => 'highlightcolor',
'highlight_bg_color' => 'highlightbgcolor',
'redirect_click_to' => 'click_action',
'redirect_enter_to' => 'return_action',
'custom_redirect_url' => 'redirect_url',
'maxresults' => 'posts_limit',
);
foreach ( $resolve_keys as $lkey => $pkey ) {
if ( isset($lite[ $lkey ]) ) {
$data[ $pkey ] = $lite[ $lkey ];
}
}
// 3. Manually Resolve
// -- Default image
if ( !empty($lite['image_default']) && strpos($lite['image_default'], 'img/default.jpg') === false ) {
$data['image_default'] = $lite['image_default'];
}
// -- Generic filters
$data['frontend_fields']['selected'] = array();
$data['frontend_fields']['unselected'][] = 'exact';
if ( $lite['showexactmatches'] == 1 ) {
$data['frontend_fields']['selected'][] = 'exact';
$data['frontend_fields']['labels']['exact'] = $lite['exactmatchestext'];
}
if ( $lite['showsearchintitle'] == 1 ) {
$data['frontend_fields']['selected'][] = 'title';
$data['frontend_fields']['labels']['title'] = $lite['searchintitletext'];
}
if ( $lite['showsearchincontent'] == 1 ) {
$data['frontend_fields']['selected'][] = 'content';
$data['frontend_fields']['labels']['content'] = $lite['searchincontenttext'];
}
if ( $lite['showsearchinexcerpt'] == 1 ) {
$data['frontend_fields']['selected'][] = 'excerpt';
$data['frontend_fields']['labels']['excerpt'] = $lite['searchinexcerpttext'];
}
if ( count($data['frontend_fields']['selected']) > 0 ) {
$data['frontend_fields']['unselected'] = array_diff(
$data['frontend_fields']['unselected'],
$data['frontend_fields']['selected']
);
}
// -- Old version Post type filters
if ( isset($lite['showsearchinposts']) && $lite['showsearchinposts'] == 1 ) {
if ( $data['showcustomtypes'] == '' ) {
$data['showcustomtypes'] = 'post;' . $lite['searchinpoststext'];
} else {
$data['showcustomtypes'] = 'post;' . $lite['searchinpoststext'] . '|' . $data['showcustomtypes'];
}
}
if ( isset($lite['showsearchinpages']) && $lite['showsearchinpages'] == 1 ) {
if ( $data['showcustomtypes'] == '' ) {
$data['showcustomtypes'] = 'page;' . $lite['searchinpagestext'];
} else {
$data['showcustomtypes'] = 'page;' . $lite['searchinpagestext'] . '|' . $data['showcustomtypes'];
}
}
// -- Category filters
if ( $lite['showsearchincategories'] == 1 && $lite['exsearchincategories'] != '' ) {
$categories = explode('|', $lite['exsearchincategories']);
foreach ( $categories as $ck => $cc ) {
if ( $cc == '' ) {
unset($categories[ $ck ]);
}
}
if ( count($categories) > 0 ) {
// Set the display mode
$data['show_terms']['display_mode']['category'] = array(
'type' => 'checkboxes',
'select_all' => false,
'box_header_text' => $lite['exsearchincategoriestext'],
'select_all_text' => '',
'default' => 'checked',
);
$categories = get_terms(
array(
'taxonomy' => 'category',
'include' => $categories,
'orderby' => 'include',
)
);
if ( !is_wp_error($categories) && count($categories) > 0 ) {
$categories_sorted = array();
wd_sort_terms_hierarchicaly($categories, $categories_sorted);
wd_flatten_hierarchical_terms($categories_sorted, $categories);
foreach ( $categories as $category ) {
$data['show_terms']['terms'][] = array(
'taxonomy' => 'category',
'id' => $category->term_id,
'level' => $category->level,
'ex_ids' => array(),
);
}
}
}
}
// -- Themes & layout
$themes = array(
'simple-red' => 'Simple Red vertical (default)',
'simple-blue' => 'Simple Blue vertical',
'simple-grey' => 'Simple Grey vertical',
'classic-blue' => 'Classic Blue vertical',
'curvy-black' => 'Curvy Black vertical',
'curvy-red' => 'Curvy Red vertical',
'curvy-blue' => 'Curvy Blue vertical',
'underline' => 'Underline Grey vertical',
);
if ( isset($themes[ $lite['theme'] ]) ) {
$data['themes'] = $themes[ $lite['theme'] ];
if ( $lite['theme'] != 'simple-red' ) {
$theme = Themes::get('search', $themes[ $lite['theme'] ]);
foreach ( $theme as $tkey => $tval ) {
$data[ $tkey ] = $tval;
}
}
}
if ( $lite['override_bg'] == 1 ) {
$data['boxbackground'] = $lite['override_bg_color'];
// Simple themes, input background
if ( strpos($lite['theme'], 'simple-') === 0 ) {
$data['inputbackground'] = $lite['override_bg_color'];
}
}
if ( $lite['override_icon'] == 1 ) {
$data['settingsbackground'] = $lite['override_icon_bg_color'];
$data['magnifierbackground'] = $lite['override_icon_bg_color'];
$data['settingsimage_color'] = $lite['override_icon_color'];
$data['magnifierimage_color'] = $lite['override_icon_color'];
}
if ( $lite['override_border'] == 1 ) {
$data['boxborder'] = $lite['override_border_style'];
}
if ( $lite['results_bg_override'] == 1 ) {
$data['resultsbackground'] = $lite['results_bg_override_color'];
}
if ( $lite['results_item_bg_override'] == 1 ) {
$data['resultscontainerbackground'] = $lite['results_item_bg_override_color'];
}
if ( $lite['results_override_border'] == 1 ) {
$data['resultsborder'] = $lite['results_override_border_style'];
}
if ( $lite['settings_bg_override'] == 1 ) {
$data['settingsdropbackground'] = $lite['settings_bg_override_color'];
}
// -- Category based exclusions
if ( $lite['excludecategories'] != '' ) {
$terms = explode('|', $lite['excludecategories']);
foreach ( $terms as $tk => $tt ) {
if ( $tt == '' ) {
unset($terms[ $tk ]);
}
}
if ( count($terms) > 0 ) {
foreach ( $terms as $term ) {
$data['exclude_by_terms']['terms'][] = array(
'taxonomy' => 'category',
'id' => $term,
'level' => 0,
'ex_ids' => array(),
);
}
}
}
} else {
return false;
}
// Resume the import
if (
$wpdb->query(
'INSERT INTO ' . wd_asp()->db->table('main') . "
(name, data) VALUES
('" . esc_sql($name) . "', '" . wd_mysql_escape_mimic(json_encode($data)) . "')"
) !== false
) {
// Final import options, the form override
if ( $lite['override_search_form'] == 1 ) {
update_option('asp_st_override', $wpdb->insert_id);
}
if ( $lite['override_woo_search_form'] == 1 ) {
update_option('asp_woo_override', $wpdb->insert_id);
}
return $wpdb->insert_id;
}
return false;
}
/**
* Update the search data
*
* @param $id
* @param $data
* @param $update_owner bool|int
* @return false|int
*/
public function update( $id, $data = array(), $update_owner = false ) {
global $wpdb;
$this->refresh = true;
if ( isset($this->search_instances[ $id ], $this->search_instances[ $id ]['data']) ) {
$data = array_merge($this->search_instances[ $id ]['data'], $data);
}
$data = $this->clearData($data);
if ( $update_owner === true ) {
$data['owner'] = intval( get_current_user_id() );
} elseif ( $update_owner !== false ) {
$update_owner = intval($update_owner);
if ( $update_owner >= 0 ) {
$data['owner'] = $update_owner;
}
}
return $wpdb->query(
'
UPDATE ' . wd_asp()->db->table('main') . "
SET data = '" . wd_mysql_escape_mimic(json_encode($data)) . "'
WHERE id = " . intval($id) . '
'
);
}
/**
* Renames a search instance
*
* @param $new_name string
* @param $id int
* @return bool|int
*/
public function rename( $new_name, $id ) {
global $wpdb;
$this->refresh = true;
return $wpdb->query(
$wpdb->prepare('UPDATE ' . wd_asp()->db->table('main') . " SET name = '%s' WHERE id = %d", $new_name, $id)
);
}
/**
* Resets the search instance to the default options.
*
* @param int $id Search instance ID
*/
public function reset( $id ) {
global $wpdb;
$this->refresh = true;
$query = 'UPDATE ' . wd_asp()->db->table('main') . "
SET
data='" . wd_mysql_escape_mimic(json_encode(wd_asp()->options['asp_defaults'])) . "'
WHERE id=" . intval($id);
$wpdb->query($query);
}
/**
* Duplicates a search instance
*
* @param $id int
* @return bool|int
*/
public function duplicate( $id ) {
global $wpdb;
$this->refresh = true;
return $wpdb->query(
$wpdb->prepare(
'
INSERT INTO ' . wd_asp()->db->table('main') . "( name, data )
SELECT CONCAT(name, ' duplicate'), data FROM " . wd_asp()->db->table('main') . '
WHERE id=%d;',
$id
)
);
}
/**
* Deletes a search instance
*
* @param $id int
* @return bool|int
*/
public function delete( $id ) {
global $wpdb;
$this->refresh = true;
return $wpdb->query(
$wpdb->prepare('DELETE FROM ' . wd_asp()->db->table('main') . ' WHERE id=%d', $id)
);
}
/**
* @param int $id
* @return string
*/
public function export( $id = 0 ) {
if ( $this->exists($id) ) {
return base64_encode( json_encode( $this->get($id) ) );
}
return '';
}
/**
* @param array|string $data json encoded array of base 64 encoded instance objects (or only one base 64 encoded instance)
* @return int|string|WP_Error number of instances (int) or an error message from the Exception
*/
public function import( $data ) {
$imported = 0;
$instances = array();
if ( is_array($data) ) {
foreach ( $data as $d ) {
$instances[] = json_decode(base64_decode($d), true);
if ( json_last_error() != 0 ) {
break;
}
}
} elseif ( is_string($data) ) {
$instances[] = json_decode(base64_decode($data), true);
} else {
return new WP_Error('data', __('Invalid data', 'ajax-search-pro'));
}
if ( json_last_error() != 0 ) {
return new WP_Error( 'json', __('Invalid JSON data', 'ajax-search-pro'));
}
foreach ( $instances as $instance ) {
if ( isset($instance['name']) ) {
$id = $this->add($instance['name'] . ' imported');
$this->update($id, $instance['data']);
++$imported;
}
}
return $imported;
}
/**
* This method is intended to use on params AFTER parsed from the database
*
* @param $params
* @return mixed
*/
public function decode_params( $params ) {
/**
* New method for future use.
* Detects if there is a _decode_ prefixed input for the current field.
* If so, then decodes and overrides the posted value.
*/
foreach ( $params as $k =>$v ) {
if ( gettype($v) === 'string' && substr($v, 0, strlen('_decode_')) == '_decode_' ) {
$real_v = substr($v, strlen('_decode_'));
$params[ $k ] = json_decode(base64_decode($real_v), true);
}
}
return $params;
}
public function add_script_data( $id, $data ) {
$this->script_data[ $id ] = $data;
update_site_option('_asp_script_data', $this->script_data);
}
public function get_script_data(): array {
/**
* To avoid any data corruption, the datatype should be checked explicitly
* https://github.com/WPDreams/ajax-search-pro-development/issues/101
*/
$script_data = get_option('_asp_script_data', array());
return is_array($script_data) ? $script_data : array();
}
public function getInstancesPrinted(): array {
return array_keys($this->script_data);
}
public function areInstancesOnCurrentPage(): bool {
return count($this->script_data) > 0;
}
// ------------------------------------------------------------
// ---------------- PRIVATE --------------------
// ------------------------------------------------------------
/**
* Clears unwanted keys from the search instance data array
*
* @param $data array
* @return array
*/
private function clearData( $data ) {
$remove_keys = array( 'asp_options_serialized' );
if ( is_array($data) ) {
foreach ( $remove_keys as $key ) {
if ( isset($data[ $key ]) ) {
unset($data[ $key ]);
}
}
}
return $data;
}
/**
* Fetches the search instances from the DB and stores them internally for future use
*/
private function init() {
global $wpdb;
// Reset both variables, so in case of deleting no remains are left
$this->search_instances = array();
$this->instances_no_data = array();
if ( !wd_asp()->db->exists('main') ) {
return;
}
$instances = $wpdb->get_results('SELECT * FROM ' . wd_asp()->db->table('main') . ' ORDER BY id ASC', ARRAY_A);
foreach ( $instances as $k => $inst ) {
$instance = new SearchInstance(
array(
'name' => $inst['name'],
'id' => $inst['id'],
)
);
$this->instances_no_data[ $instance->id ] = array(
'name' => $instance->name,
'id' => $instance->id,
);
/**
* Explanation:
* 1. json_decode(..) -> converts the params from the database to PHP format
* 2. $this->decode_params(..) -> decodes params that are stored in base64 and prefixed to be decoded
*
* This is not equivalent with wd_parse_params(..) as that runs before inserting to the DB as well,
* and $this->decode_params(..) runs after getting the data from the database, so it stays redundant.
*
* NOTE:
* raw_data is used in backwards compatibility checks, to see if a certain option exists before merging
* with the defaults
*/
$instance->raw_data = $this->decode_params(json_decode($inst['data'], true));
$instance->data = array_merge(
wd_asp()->options['asp_defaults'],
$instance->raw_data
);
$instance->options = new SearchOptions(json_decode($inst['data'], true));
$instance->data = apply_filters('asp_instance_options', $instance->data , $instance->id);
$this->search_instances[ $instance->id ] = $instance;
}
}
}

View File

@@ -0,0 +1,404 @@
<?php
namespace WPDRMS\ASP\Core;
use WPDRMS\ASP\Api\Rest0\Rest;
use WPDRMS\ASP\Asset;
use WPDRMS\ASP\BlockEditor\BlockInterface;
use WPDRMS\ASP\Hooks\ActionsManager;
use WPDRMS\ASP\Hooks\AjaxManager;
use WPDRMS\ASP\Hooks\FiltersManager;
use WPDRMS\ASP\Modal\Factories\ModalFactory;
use WPDRMS\ASP\Patterns\SingletonTrait;
use WPDRMS\ASP\Rest\RestInterface;
use WPDRMS\ASP\Modal\Services\TimedModalService;
use WPDRMS\ASP\Updates\Remote as UpdatesRemote;
if ( !defined('ABSPATH') ) {
die('-1');
}
class Manager {
use SingletonTrait;
/**
* Context of the current WP environment
*
* Is used to include the correct and only necessary files for each context to save performance
*
* Possible values:
* ajax - an ajax call triggered by the search
* frontend - simple front-end call, or an ajax request not triggered by ASP
* backend - on any of the plugin back-end pages
* global_backend - on any other back-end page
* special - special cases
*
* @since 1.0
* @var string
*/
private $context = 'frontend';
/**
* Initialize and run the plugin-in
*/
private function __construct() {
do_action('wd_asp_before_load');
$this->preLoad();
$this->loadInstances();
$this->initUploadGlobals();
$this->initCacheGlobals();
/**
* Available after this point:
* (WD_ASP_Init) wd_asp()->instances, (global) $wd_asp->instances
*/
register_activation_hook(ASP_FILE, array( $this, 'activationHook' ));
/**
* Available after this point:
* (array) wd_asp()->options, (global) $wd_asp->options
* (WD_ASP_Init) wd_asp()->init, (global) $wd_asp->init
* (WD_ASP_DBMan) wd_asp()->db, (global) $wd_asp->db
*/
add_action( 'init', array( $this, 'init' ), 0 );
}
public function init() {
// Check if the plugin needs to be stopped on certain conditions
// ..this needs to be here, otherwise filter not accesible from functions.php
if ( $this->stopLoading() ) {
return;
}
do_action('wd_asp_loading_start');
// After 6.7 this must be executed in the "init" hook
load_plugin_textdomain( 'ajax-search-pro', false, ASP_DIR . '/languages' );
$this->getContext();
/**
* Available after this point:
* $this->context
*/
$this->loadIncludes();
$this->loadShortcodes();
$this->loadMenu();
$this->loadHooks();
\WPDRMS\ASP\NavMenu\Controller::getInstance();
wd_asp()->init->safety_check();
add_action('admin_notices', array( $this, 'loadNotices' ));
do_action('wd_asp_loaded');
}
private function stopLoading() {
$ret = false;
if ( isset($_GET, $_GET['action']) ) {
if ( $_GET['action'] == 'ere_property_search_ajax' ) {
$ret = true;
}
}
// Plugin uploading screen
if (
is_admin() &&
isset($_GET['action']) &&
$_GET['action'] === 'upload-plugin' &&
str_contains(wd_current_page_url(), 'update.php')
) {
$ret = true;
}
// Allow filtering this condition
return apply_filters('asp_stop_loading', $ret);
}
/**
* Preloading: for functions and other stuff needed
*/
private function preLoad() {
require_once ASP_PATH . '/backend/settings/default_options.php';
require_once ASP_FUNCTIONS_PATH . 'functions.php';
// We need to initialize the init here to get the init->table() function
wd_asp()->init = Init::getInstance();
// This needs to be registered here, the 'init' is too late to register
add_filter( 'cron_schedules', array( $this, 'cronExtraIntervals' ) );
}
/**
* Adds additional intervals to cron jobs
*
* @param $schedules
* @return mixed
*/
function cronExtraIntervals( $schedules ) {
$schedules['asp_cr_two_minutes'] = array(
'interval' => 120,
'display' => __( 'Every 2 Minutes', 'ajax-search-pro' ),
);
$schedules['asp_cr_three_minutes'] = array(
'interval' => 180,
'display' => __( 'Every 3 Minutes', 'ajax-search-pro' ),
);
$schedules['asp_cr_five_minutes'] = array(
'interval' => 300,
'display' => __( 'Every 5 Minutes', 'ajax-search-pro' ),
);
$schedules['asp_cr_fifteen_minutes'] = array(
'interval' => 900,
'display' => __( 'Every 15 Minutes', 'ajax-search-pro' ),
);
$schedules['asp_cr_thirty_minutes'] = array(
'interval' => 1800,
'display' => __( 'Every 30 Minutes', 'ajax-search-pro' ),
);
return $schedules;
}
/**
* Gets the upload path with back-slash
*/
public function initUploadGlobals() {
if ( is_multisite() ) {
/**
* On multisite, the wp_upload_dir() returns the current blog URLs,
* even if the switch_to_blog(1) is initiated (WordPress core bug?)
*
* Bypass solution: Save the upload dir option as a site option, when the main blog is visited,
* then get this value on each site, with a fallback.
* WARNING: DO NOT USE get_site_option() and update_site_option() - as those slow down the multisite loading by a LOT
* use get_blog_option() and update_blog_option() instead
*/
$upload_dir = wp_upload_dir();
if ( get_current_blog_id() == get_network()->site_id ) {
update_blog_option(get_network()->site_id, '_asp_upload_dir', $upload_dir);
} else {
$upload_dir = get_blog_option(get_network()->site_id, '_asp_upload_dir', $upload_dir);
}
} else {
$upload_dir = wp_upload_dir();
}
$upload_dir = apply_filters('asp_glob_upload_dir', $upload_dir);
wd_asp()->upload_path = $upload_dir['basedir'] . '/' . wd_asp()->upload_dir . '/';
wd_asp()->upload_url = $upload_dir['baseurl'] . '/' . wd_asp()->upload_dir . '/';
// Let us make sure, that the URL is using the correct protocol
if ( defined('ASP_URL') ) {
// Site is https, but URL is http
if ( strpos(ASP_URL, 'https://') === 0 && strpos(wd_asp()->upload_url, 'https://') === false ) {
wd_asp()->upload_url = str_replace('http://', 'https://', wd_asp()->upload_url);
// Site is http, but URL is https
} elseif ( strpos(ASP_URL, 'http://') === 0 && strpos(wd_asp()->upload_url, 'http://') === false ) {
wd_asp()->upload_url = str_replace('https://', 'http://', wd_asp()->upload_url);
}
}
if ( defined( 'BFITHUMB_UPLOAD_DIR' ) ) {
wd_asp()->bfi_path = $upload_dir['basedir'] . '/' . BFITHUMB_UPLOAD_DIR . '/';
} else {
wd_asp()->bfi_path = $upload_dir['basedir'] . '/' . wd_asp()->bfi_dir . '/';
}
// Allow globals modification for developers
wd_asp()->upload_path = apply_filters('asp_glob_upload_path', wd_asp()->upload_path);
wd_asp()->upload_url = apply_filters('asp_glob_upload_url', wd_asp()->upload_url);
wd_asp()->bfi_path = apply_filters('asp_glob_bfi_path', wd_asp()->bfi_path);
}
public function initCacheGlobals() {
$base_url = WP_CONTENT_URL;
if ( defined('ASP_URL') ) {
if ( strpos(ASP_URL, 'https://') === 0 && strpos($base_url, 'https://') === false ) {
$base_url = str_replace('http://', 'https://', $base_url);
}
}
wd_asp()->global_cache_path = apply_filters('asp_glob_global_cache_path', WP_CONTENT_DIR . '/cache/');
wd_asp()->cache_path = apply_filters('asp_glob_cache_path', WP_CONTENT_DIR . '/cache/asp/');
wd_asp()->cache_url = apply_filters('asp_glob_cache_url', $base_url . '/cache/asp/');
}
/**
* Gets the call context for further use
*/
public function getContext() {
$backend_pages = Menu::getMenuPages();
if ( !empty($_POST['action']) ) {
if ( in_array($_POST['action'], AjaxManager::getAll()) ) {
$this->context = 'ajax';
}
if ( isset($_POST['wd_required']) ) {
$this->context = 'special';
}
// If it is not part of the plugin ajax actions, the context stays "frontend"
} elseif ( !empty($_GET['page']) && in_array($_GET['page'], $backend_pages) ) {
$this->context = 'backend';
} elseif ( is_admin() ) {
$this->context = 'global_backend';
} else {
$this->context = 'frontend';
}
return $this->context;
}
/**
* Loads the instance data into the global scope
*/
private function loadInstances() {
wd_asp()->instances = Instances::getInstance();
}
/**
* Loads the required files based on the context
*/
private function loadIncludes() {
// This must be here!! If it's in a conditional statement, it will fail..
require_once ASP_PATH . '/backend/vc/vc.extend.php';
switch ( $this->context ) {
case 'special':
require_once ASP_PATH . '/backend/settings/types.inc.php';
break;
case 'ajax':
break;
case 'frontend':
break;
case 'backend':
require_once ASP_PATH . '/backend/settings/types.inc.php';
break;
case 'global_backend':
break;
default:
break;
}
// Special case
if ( wpdreams_on_backend_post_editor() ) {
require_once ASP_PATH . '/backend/tinymce/buttons.php';
require_once ASP_PATH . '/backend/metaboxes/default.php';
}
wd_asp()->css_manager = Asset\Css\Manager::getInstance();
// Lifting some weight off from ajax requests
if ( $this->context != 'ajax' ) {
if ( wd_asp()->o['asp_compatibility']['rest_api_enabled'] ) {
wd_asp()->rest_api = Rest::getInstance();
}
wd_asp()->updates = UpdatesRemote::getInstance();
}
}
/**
* Use the Shorcodes loader to assign the shortcodes to handler classes
*/
private function loadShortcodes() {
Shortcodes::registerAll();
}
/**
* This is hooked to the admin_notices action
*/
public function loadNotices() {
// -------------------- Handle requests here ----------------------
// Update related notes
if ( isset($_GET['asp_notice_clear_ru']) ) {
update_option('asp_recently_updated', 0);
}
if ( isset($_GET['asp_notice_clear_ri']) ) {
update_option('asp_recreate_index', 0);
}
// -------------------- Handle notices here ----------------------
// Index table re-creation note
if ( $this->context == 'backend' && get_option('asp_recreate_index', 0) == 1 ) {
?>
<div class="notice notice-error asp-notice-nohide asp-notice-ri">
<p>
<?php echo __('<b>Ajax Search Pro notice: </b> The Index Table options have been modified, please re-create the index table!', 'ajax-search-pro'); ?>
<a class="button button-primary" href="<?php echo get_admin_url() . 'admin.php?page=asp_index_table'; ?>"><?php echo __('Let\'s do it!', 'ajax-search-pro'); ?></a>
<a class="button button-secondary" href="<?php echo esc_url(add_query_arg(array( 'asp_notice_clear_ri' => '1' ))); ?>"><?php echo __('Hide this message', 'ajax-search-pro'); ?></a>
</p>
</div>
<?php
}
}
/**
* Generates the menu
*/
private function loadMenu() {
add_action('admin_menu', array( '\\WPDRMS\\ASP\\Core\\Menu', 'register' ));
}
/**
* Fires up the plugin hooks
*/
private function loadHooks(): void {
$factory = Factory::instance();
// Register handlers only if the context is ajax indeed
if ( $this->context === 'ajax' ) {
AjaxManager::registerAll();
}
if ( $this->context !== 'ajax' ) {
foreach ( $factory->get('Asset') as $asset ) {
$asset->register();
}
// Editor blocks
foreach ( $factory->get( 'Block' ) as $block ) {
$block->register();
}
if ( $this->context === 'backend' ) {
ActionsManager::register('admin_init', 'Compatibility');
TimedModalService::instance(new ModalFactory())->init();
}
ActionsManager::registerAll();
if ( wd_asp()->o['asp_compatibility']['rest_api_enabled'] ) {
wd_asp()->rest_api->init();
}
add_action(
'rest_api_init',
function () {
foreach ( Factory::instance()->get('Rest') as $rest ) {
$rest->registerRoutes();
}
}
);
}
foreach ( $factory->get('Integration') as $integration ) {
$integration->load();
}
FiltersManager::registerAll();
}
/**
* Run at the plugin activation
*/
public function activationHook(): void {
// Run the activation tasks
wd_asp()->init->activate();
}
}

View File

@@ -0,0 +1,166 @@
<?php
namespace WPDRMS\ASP\Core;
if ( !defined('ABSPATH') ) {
die('-1');
}
class Menu {
/**
* Holds the main menu item
*
* @var array the main menu
*/
private static $main_menu = array();
/**
* @var array
*/
private static $hooks = array();
/**
* Submenu titles and slugs
*
* @var array
*/
private static $submenu_items = array();
/**
* Bypass method to support translations, because static array varialbes cannot have a value defined as a result
* of a function, like 'key' => __('text', ..)
*/
private static function preInit() {
if ( count(self::$submenu_items) == 0 ) {
$main_menu = array(
'title' => __('Ajax Search Pro', 'ajax-search-pro'),
'slug' => 'asp_main_settings',
'file' => '/backend/settings.php',
'position' => '207.9',
'icon_url' => 'icon.png',
);
$submenu_items = array(
array(
'title' => __('Index Table', 'ajax-search-pro'),
'file' => '/backend/index_table.php',
'slug' => 'asp_index_table',
),
array(
'title' => __('Priorities', 'ajax-search-pro'),
'file' => '/backend/priorities.php',
'slug' => 'asp_priorities',
),
array(
'title' => __('Search Statistics', 'ajax-search-pro'),
'file' => '/backend/statistics.php',
'slug' => 'asp_statistics',
),
array(
'title' => __('Analytics Integration', 'ajax-search-pro'),
'file' => '/backend/analytics.php',
'slug' => 'asp_analytics',
),
array(
'title' => __('Cache Settings', 'ajax-search-pro'),
'file' => '/backend/cache_settings.php',
'slug' => 'asp_cache_settings',
),
array(
'title' => __('Performance tracking', 'ajax-search-pro'),
'file' => '/backend/performance.php',
'slug' => 'asp_performance',
),
array(
'title' => __('Compatibility & Other Settings', 'ajax-search-pro'),
'file' => '/backend/compatibility_settings.php',
'slug' => 'asp_compatibility_settings',
),
array(
'title' => __('Export/Import', 'ajax-search-pro'),
'file' => '/backend/export_import.php',
'slug' => 'asp_export_import',
),
array(
'title' => __('Maintenance', 'ajax-search-pro'),
'file' => '/backend/maintenance.php',
'slug' => 'asp_maintenance',
),
array(
'title' => __('Help & Updates', 'ajax-search-pro'),
'file' => '/backend/updates_help.php',
'slug' => 'asp_updates_help',
),
array(
'title' => 'Dev',
'file' => '/backend/dev.php',
'slug' => 'asp_dev',
),
);
self::$main_menu = $main_menu;
self::$submenu_items = $submenu_items;
}
}
/**
* Runs the menu registration process
*/
public static function register() {
$capability = ASP_DEMO == 1 ? 'read' : 'manage_options';
self::preInit();
$h = add_menu_page(
self::$main_menu['title'],
self::$main_menu['title'],
$capability,
self::$main_menu['slug'],
array( '\\WPDRMS\\ASP\\Core\\Menu', 'route' ),
ASP_URL . self::$main_menu['icon_url'],
self::$main_menu['position']
);
self::$hooks[ $h ] = self::$main_menu['slug'];
foreach ( self::$submenu_items as $submenu ) {
if ( $submenu['slug'] === 'asp_dev' && !defined('WP_ASP_TEST_ENV') ) {
continue;
}
$h = add_submenu_page(
self::$main_menu['slug'],
self::$main_menu['title'],
$submenu['title'],
$capability,
$submenu['slug'],
array( '\\WPDRMS\\ASP\\Core\\Menu', 'route' )
);
self::$hooks[ $h ] = $submenu['slug'];
}
}
/**
* Includes the correct back-end file based on the page string
*/
public static function route() {
$current_view = self::$hooks[ current_filter() ];
include ASP_PATH . 'backend/' . str_replace('asp_', '', $current_view) . '.php';
}
/**
* Method to obtain the menu pages for context checking
*
* @return array
*/
public static function getMenuPages(): array {
self::preInit();
$ret = array();
$ret[] = self::$main_menu['slug'];
foreach ( self::$submenu_items as $menu ) {
$ret[] = $menu['slug'];
}
return $ret;
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace WPDRMS\ASP\Core\Models;
use ArrayAccess;
use WPDRMS\ASP\Options\Data\SearchOptions;
use WPDRMS\ASP\Patterns\ObjectAsArrayTrait;
/**
* @implements ArrayAccess<string, mixed>
*/
class SearchInstance implements ArrayAccess {
use ObjectAsArrayTrait;
public string $name;
/**
* @var Array<string, mixed>
*/
public array $raw_data;
/**
* @var Array<string, mixed>
*/
public array $data;
public int $id;
public SearchOptions $options;
/**
* @param Array<string, mixed> $args
*/
public function __construct( array $args = array() ) {
$this->name = $args['name'] ?? '';
$this->id = $args['id'] ?? 0;
$this->data = $args['data'] ?? array();
if ( isset($args['options']) ) {
$this->options = new SearchOptions($args['options']);
}
}
}

View File

@@ -0,0 +1,106 @@
<?php
namespace WPDRMS\ASP\Core;
use WPDRMS\ASP\Patterns\SingletonTrait;
if ( !defined('ABSPATH') ) {
die('-1');
}
/**
* Allows Ajax Search Pro as a menu element
*/
class NavMenuMetaBox {
use SingletonTrait;
/**
* Default menu custom metadata
*
* @var array<string, mixed>
*/
public static array $defaults = array(
'prevent_events' => false,
);
/**
* Registers the meta boxes
*
* @hook admin_init
* @return void
*/
public function addMetaBoxes(): void {
add_meta_box(
'asp_nav_menu_link',
__('Ajax Search Pro', 'ajax-search-pro'),
array( $this, 'navMenuLink' ),
'nav-menus',
'side',
'low'
);
}
public function navMenuLink(): void {
?>
<div id="posttype-wl-login" class="posttypediv">
<div id="tabs-panel-wishlist-login" class="tabs-panel tabs-panel-active">
<ul id ="wishlist-login-checklist" class="categorychecklist form-no-clear">
<li>
<label class="menu-item-title">
<input type="checkbox" class="menu-item-checkbox" name="menu-item[-1][menu-item-object-id]" value="1"> Search 1
</label>
<input type="hidden" class="menu-item-type" name="menu-item[-1][menu-item-type]" value="custom">
<input type="hidden" class="menu-item-title" name="menu-item[-1][menu-item-title]" value="Search 1">
<input type="hidden" class="menu-item-url" name="menu-item[-1][menu-item-url]" value="<?php bloginfo('wpurl'); ?>/wp-login.php">
<input type="hidden" class="menu-item-classes" name="menu-item[-1][menu-item-classes]" value="wl-login-pop">
</li>
<li>
<label class="menu-item-title">
<input type="checkbox" class="menu-item-checkbox" name="menu-item[-1][menu-item-object-id]" value="2"> Search 2
</label>
<input type="hidden" class="menu-item-type" name="menu-item[-1][menu-item-type]" value="custom">
<input type="hidden" class="menu-item-title" name="menu-item[-1][menu-item-title]" value="Search 2">
<input type="hidden" class="menu-item-url" name="menu-item[-1][menu-item-url]" value="<?php bloginfo('wpurl'); ?>/wp-login.php">
<input type="hidden" class="menu-item-classes" name="menu-item[-1][menu-item-classes]" value="aspm_container">
</li>
</ul>
</div>
<p class="button-controls">
<span class="list-controls">
<a href="/wordpress/wp-admin/nav-menus.php?page-tab=all&amp;selectall=1#posttype-page" class="select-all">Select All</a>
</span>
<span class="add-to-menu">
<input type="submit" class="button-secondary submit-add-to-menu right" value="Add to Menu" name="add-post-type-menu-item" id="submit-posttype-wl-login">
<span class="spinner"></span>
</span>
</p>
</div>
<?php
}
public function printNavMenuCustomFields( $item_id ): void {
$data = get_post_meta($item_id, '_ajax_search_pro_menu_data', true);
$data = wp_parse_args($data, self::$defaults);
?>
<p class="aspm-show-as-button description description-wide">
<label for="aspm-menu-item-button-<?php echo esc_attr($item_id); ?>" >
<input type="checkbox"
id="aspm-prevent_events-<?php echo esc_attr($item_id); ?>"
name="aspm-prevent_events[<?php echo esc_attr($item_id); ?>]"
<?php checked($data['prevent_events']); ?>
/><?php esc_html_e('Prevent custom script events', 'ajax-search-pro'); ?>
</label>
</p>
<?php
}
public function updateNavMenuCustomFields( $menu_id, $menu_item_db_id ) {
$prevent_events = isset($_POST['aspm-prevent_events'][ $menu_item_db_id ]) && $_POST['aspm-prevent_events'][ $menu_item_db_id ] === 'on'; // phpcs:ignore
update_post_meta(
$menu_item_db_id,
'_ajax_search_pro_menu_data',
array(
'prevent_events' => $prevent_events,
)
);
}
}

View File

@@ -0,0 +1,116 @@
<?php
namespace WPDRMS\ASP\Core;
if ( !defined('ABSPATH') ) {
die('-1');
}
class Shortcodes {
const NAMESPACE = '\\WPDRMS\\ASP\\Shortcodes\\';
/**
* Array of internal known shortcodes
*
* @var array
*/
private static $shortcodes = array(
'wpdreams_ajaxsearchpro' => 'Search',
'wd_asp' => 'SearchBox',
'wpdreams_ajaxsearchpro_results' => 'Results',
'wd_asp_results' => 'Results',
'asp_results' => 'Results',
'wpdreams_asp_settings' => 'Settings',
'wd_asp_settings' => 'Settings',
'asp_settings' => 'Settings',
'wpdreams_ajaxsearchpro_two_column' => 'TwoColumn',
'wd_asp_two_column' => 'TwoColumn',
'asp_two_column' => 'TwoColumn',
'wpdreams_ajaxsearchlite' => 'AjaxSearchLite',
'wd_asl' => 'AjaxSearchLite',
);
/**
* Array of already registered shortcodes
*
* @var array
*/
private static $registered = array();
/**
* Registers all the handlers from the $actions variable
*/
public static function registerAll() {
foreach ( self::$shortcodes as $shortcode => $handler ) {
self::register($shortcode, $handler);
}
}
/**
* Get all the queued handlers
*
* @return array
*/
public static function getAll() {
return array_keys(self::$shortcodes);
}
/**
* Get all the already registered handlers
*
* @return array
*/
public static function getRegistered() {
return self::$registered;
}
/**
* Registers a filter with the handler class name.
*
* @param $shortcode
* @param $handler string|array
* @return bool
*/
public static function register( $shortcode, $handler ) {
if ( is_array($handler) ) {
$class = self::NAMESPACE . $handler[0];
$handle = $handler[1];
} else {
$class = self::NAMESPACE . $handler;
$handle = 'handle';
}
if ( !class_exists($class) ) {
return false;
}
if ( !shortcode_exists($shortcode) ) {
add_shortcode($shortcode, array( call_user_func(array( $class, 'getInstance' )), $handle ));
}
self::$registered[] = $shortcode;
return true;
}
/**
* Deregisters a shortcode
*
* @param $shortcode string
* @return bool
*/
public static function deregister( $shortcode ) {
// Check if it is already registered
if ( isset(self::$registered[ $shortcode ]) ) {
remove_shortcode( $shortcode );
} elseif ( isset(self::$shortcodes[ $shortcode ]) ) {
unset(self::$shortcodes[ $shortcode ]);
}
return true;
}
}

View File

@@ -0,0 +1,212 @@
<?php
namespace WPDRMS\ASP\Database;
use WPDRMS\ASP\Index;
use WPDRMS\ASP\Patterns\SingletonTrait;
use WPDRMS\ASP\Utils\Plugin;
/* Prevent direct access */
defined('ABSPATH') or die("You can't access this file directly.");
if ( !class_exists(__NAMESPACE__ . '\Manager') ) {
class Manager {
use SingletonTrait;
/**
* All the table slug => name combinations used
*
* @since 1.0
* @var array
*/
private $tables = array(
"main" => "ajaxsearchpro",
"stats" => "ajaxsearchpro_statistics",
"priorities" => "ajaxsearchpro_priorities",
"synonyms" => "asp_synonyms",
"index" => "asp_index",
'instant' => 'asp_instant'
);
function __construct() {
global $wpdb;
if (isset($wpdb->base_prefix)) {
wd_asp()->_prefix = $wpdb->base_prefix;
} else {
wd_asp()->_prefix = $wpdb->prefix;
}
foreach ($this->tables as $slug => $table)
$this->tables[$slug] = wd_asp()->_prefix . $table;
// Push the correct table names to the globals back
$this->tables = (object) $this->tables;
wd_asp()->tables = $this->tables;
}
function create(): string {
global $wpdb;
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
$return = array();
$table_name = $this->table("main");
$query = "
CREATE TABLE IF NOT EXISTS `$table_name` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text NOT NULL,
`data` mediumtext NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci AUTO_INCREMENT=1 ;
";
dbDelta($query);
$wpdb->query($query);
$return[] = $query;
// 4.6.1+ change the data row to medium text
if ( Plugin::previousVersion('4.6.1') ) {
$query = "ALTER TABLE `$table_name` MODIFY `data` mediumtext";
$wpdb->query($query);
$return[] = $query;
}
// 4.18.3+ change charsets to utfmb4 and collate
if ( Plugin::previousVersion('4.18.3') ) {
$query = "ALTER TABLE `$table_name` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;";
$wpdb->query($query);
$return[] = $query;
}
$table_name = $this->table("stats");
$query = "
CREATE TABLE IF NOT EXISTS `$table_name` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`search_id` int(11) NOT NULL,
`keyword` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`num` int(11) NOT NULL,
`last_date` int(11) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci AUTO_INCREMENT=1 ;
";
dbDelta($query);
$wpdb->query($query);
$return[] = $query;
// 4.18.3+ change charsets to utfmb4 and collate
if ( Plugin::previousVersion('4.18.3') ) {
$query = "ALTER TABLE `$table_name` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;";
dbDelta($query);
$wpdb->query($query);
$return[] = $query;
}
// ------------- SYNONYMS DB ---------------------------
$table_name = $this->table("synonyms");
$syn = \WPDRMS\ASP\Synonyms\Manager::getInstance();
$return = array_merge($return, $syn->createTable($table_name));
// -----------------------------------------------------
$table_name = $this->table("priorities");
$query = "
CREATE TABLE IF NOT EXISTS `$table_name` (
`post_id` int(11) NOT NULL,
`blog_id` int(11) NOT NULL,
`priority` int(11) NOT NULL,
PRIMARY KEY (`post_id`, `blog_id`)
) DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci AUTO_INCREMENT=1 ;
";
dbDelta($query);
$wpdb->query($query);
$return[] = $query;
$query = "SHOW INDEX FROM `$table_name` WHERE KEY_NAME = 'post_blog_id'";
$index_exists = $wpdb->query($query);
if ($index_exists == 0) {
$query = "ALTER TABLE `$table_name` ADD INDEX `post_blog_id` (`post_id`, `blog_id`);";
$wpdb->query($query);
$return[] = $query;
}
// 4.18.3+ change charsets to utfmb4 and collate
if ( Plugin::previousVersion('4.18.3') ) {
$query = "ALTER TABLE `$table_name` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;";
dbDelta($query);
$wpdb->query($query);
$return[] = $query;
}
// ------------- Index TABLE DB ------------------------
$indexDB = new Index\Database();
$return = array_merge($return, $indexDB->create());
// -----------------------------------------------------
$ret = '';
foreach ( $return as &$r ){
$r = trim($r);
if ( $r != '' ) {
$r = substr($r, -1) != ';' ? $r . ';' : $r;
$ret .= $r . PHP_EOL;
}
}
return $ret;
}
public function delete($table_slug = '') {
global $wpdb;
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
if ( empty($table_slug) ) {
foreach ($this->tables as $table_name) {
$q = "DROP TABLE IF EXISTS `$table_name`;";
dbDelta($q);
$wpdb->query($q);
}
delete_option('_asp_tables');
} else {
$q = "DROP TABLE IF EXISTS `$table_slug`;";
dbDelta($q);
$wpdb->query($q);
}
}
public function exists($table_slug = '', $force_check = false): bool {
global $wpdb;
// Store the data in the options cache as SHOW TABLES .. query is expensive
$table_opt = get_option('_asp_db_tables', array());
if ( !$force_check ) {
if ( isset($table_opt[$table_slug]) && $table_opt[$table_slug] == 1 )
return true;
}
$table_name = $this->table($table_slug);
$db_table_name = $wpdb->get_var("SHOW TABLES LIKE '$table_name'");
// SHOW command probably prohibited
if ( is_wp_error($db_table_name) )
return true;
$db_table_name = is_string($db_table_name) ? $db_table_name : '';
if (
$table_name === false ||
strtolower($db_table_name) != strtolower($table_name) // Some databases return lowercase, ignore that
) {
$table_opt[$table_slug] = 0;
update_option('_asp_db_tables', $table_opt);
return false;
} else {
$table_opt[$table_slug] = 1;
update_option('_asp_db_tables', $table_opt);
return true;
}
}
/**
* Return the table name by table slug
*
* @param $table_slug
* @return string|boolean
*/
function table($table_slug) {
return $this->tables->{$table_slug} ?? false;
}
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace WPDRMS\ASP\Frontend\Filters;
if (!defined('ABSPATH')) die('-1');
class Button extends Filter {
protected $default = array(
'label' => '',
'type' => '',
'container_class' => '',
'button_class' => ''
);
protected $key = 'type';
protected $type = 'button';
}

View File

@@ -0,0 +1,22 @@
<?php
namespace WPDRMS\ASP\Frontend\Filters;
if (!defined('ABSPATH')) die('-1');
class ContentType extends Filter {
public $data = array(
"field" => "",
"required" => false,
"invalid_input_text" => "Please select one!"
);
protected $default = array(
'label' => '',
'selected' => false,
'value' => '',
'level' => 0,
'default' => false
);
protected $key = 'value';
protected $type = 'content_type';
}

View File

@@ -0,0 +1,116 @@
<?php
namespace WPDRMS\ASP\Frontend\Filters;
if (!defined('ABSPATH')) die('-1');
class CustomField extends Filter {
public $data = array(
"field" => "",
"source" => "postmeta",
"type" => "checkboxes",
"required" => false,
"invalid_input_text" => "Please select one!",
"logic" => 'and',
/*
* Apply the AND logic as if fields are separately stored with the same name ex.:
* field_name => value1,
* field_name => value2
* .. instead of field_name => 'value1, value2 etc..'
*/
"logic_and_separate_custom_fields" => false,
"operator" => '=',
"acf_type" => false
);
protected $default = array(
'label' => '',
'selected' => false,
'value' => '',
'level' => 0,
'default' => false,
'parent' => 0,
'option_group' => false
/**
* Other possible keys here, depending on the type:
* 'slider_from', 'slider_to', 'placeholder'
*/
);
protected $special_args = array(
"slider" => array(
'slider_prefix' => '-,',
'slider_suffix' => '.',
'slider_step' => 1,
'slider_from' => 1,
'slider_to' => 1000,
'slider_decimals' => 0,
'slider_t_separator' => ' ',
'operator' => 'let'
),
"number_range" => array(
'range_step' => 1,
'range_from' => 1,
'placeholder1' => '',
'placeholder2' => '',
'range_to' => 1000,
'range_t_separator' => ' '
),
"range" => array(
'range_prefix' => '-,',
'range_suffix' => '.',
'range_step' => 1,
'range_from' => 1,
'range_to' => 1000,
'range_decimals' => 0,
'range_t_separator' => ' '
),
"datepicker" => array(
'placeholder' => '',
'date_format' => 'dd/mm/yy',
'date_store_format' => 'datetime' // datetime, acf, timestamp
),
"dropdown" => array(
'placeholder' => '',
'multiple' => false
),
"dropdownsearch" => array(
'placeholder' => ''
),
"multisearch" => array(
'placeholder' => ''
)
);
protected $key = 'value';
protected $type = 'custom_field';
public function __construct($label = '', $display_mode = 'checkboxes', $data = array(), $position = -1) {
parent::__construct($label, $display_mode, $data, $position);
if ( isset($this->special_args[$this->display_mode]) ) {
$this->data = array_merge($this->data, $this->special_args[$this->display_mode], $data);
} else {
$this->data = array_merge($this->data, $data);
}
if ( function_exists('get_field') ) {
$this->data['acf_type'] = asp_acf_get_field_type($this->data['field']);
}
}
public function field():string {
return $this->data['field'];
}
public function getUniqueFieldName( $to_display = false ) {
if ( isset($this->data['field']) ) {
$field = $this->data['field'];
if ( $to_display ) {
$field = str_replace(
array('[', ']'),
array('!_brktl_!', '!_brktr_!'),
$field
);
}
return $field . '__' . $this->id;
} else {
return $this->id;
}
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace WPDRMS\ASP\Frontend\Filters;
if (!defined('ABSPATH')) die('-1');
class Date extends Filter {
public $data = array();
protected $default = array(
'label' => '',
'value' => '',
'name' => '',
'format' => 0,
'default' => false,
'placeholder' => ''
);
protected $key = 'value';
protected $type = 'date';
}

View File

@@ -0,0 +1,238 @@
<?php
namespace WPDRMS\ASP\Frontend\Filters;
if (!defined('ABSPATH')) die('-1');
class Filter {
private static $last_position = 0;
private static $last_id = 0;
public $label = '';
public $display_mode = 'checkboxes';
public $data = array();
public $position = 0;
public $id = 0;
public $is_api = true; // Filter added via API, set to false for parsers
protected $values = array();
// Default value for the $values array
protected $default = array(
'label' => '',
'selected' => false,
'id' => 0, // Can be numeric, as well as a field name etc..
'level' => 0,
'default' => false
);
protected $key = 'id'; // The main distinctive field
protected $type = '';
protected $option_path = array(
'taxonomy' => 'termset',
'custom_field' => 'aspf'
);
function __construct($label = '', $display_mode = 'checkboxes', $data = array(), $position = -1) {
$this->label = $label;
$this->display_mode = $display_mode;
$data = is_array($data) ? $data : (array)$data;
$this->data = array_merge($this->data, $data);
$this->id = ++self::$last_id;
if ( isset($data['is_api']) )
$this->is_api = $data['is_api'];
if ( $position > -1 ) {
$this->position = $position;
if ( $position > self::$last_position ) {
self::$last_position = $position;
}
} else {
$this->position = self::$last_position;
++self::$last_position;
}
}
public function isEmpty(): bool {
return empty($this->values);
}
public function add($filter, $position = false) {
$new = (object)array_merge($this->default, $filter);
if ( $position === false ) {
$this->values[] = $new;
} else {
$position = intval($position);
array_splice( $this->values, $position, 0, array($new) );
}
return $new;
}
public function get($ids = array()) {
$key = $this->key;
if ( is_array($ids) ) {
if (empty($ids)) {
return $this->values;
} else {
$ret = array();
foreach ($this->values as $v) {
if (in_array($v->{$key}, $ids)) {
$ret[] = $v;
}
}
return $ret;
}
} else {
foreach ($this->values as $v) {
if ($v->{$key} == $ids) {
return $v;
}
}
}
return array();
}
public function remove($ids = array(), $by_id = false) {
if ( $by_id ) {
$i = is_array($ids) ? $ids : array($ids);
foreach ( $i as $ii ) {
if (isset($this->values[$ii])) {
unset($this->values[$ii]);
}
}
} else {
$key = $this->key;
if ( is_array($ids) ) {
if (empty($ids)) {
$this->values = array();
} else {
foreach ($this->values as $k => $v) {
if (in_array($v->{$key}, $ids)) {
unset($this->values[$k]);
}
}
}
} else {
foreach ($this->values as $k => $v) {
if ($v->{$key} == $ids) {
unset($this->values[$k]);
}
}
}
}
}
public function attr($ids = array(), $att = '', $val = '', $by_id = false) {
if ( $by_id ) {
$i = is_array($ids) ? $ids : array($ids);
foreach ( $i as $ii ) {
if ( isset($this->values[$ii]) ) {
$this->values[$ii]->{$att} = $val;
}
}
} else {
$key = $this->key;
if ( is_array($ids) ) {
if (empty($ids)) {
foreach ($this->values as $k => $v) {
$this->values[$k]->{$att} = $val;
}
} else {
foreach ($this->values as $k => $v) {
if (in_array($v->{$key}, $ids)) {
$this->values[$k]->{$att} = $val;
}
}
}
} else {
foreach ($this->values as $k => $v) {
if ($v->{$key} == $ids) {
$this->values[$k]->{$att} = $val;
}
}
}
}
}
public function select($ids = array(), $unselect = false) {
if ($unselect) {
$this->unselect();
}
$this->attr($ids, 'selected', true);
}
public function unselect($ids = array(), $select = false) {
if ($select) {
$this->select();
}
$this->attr($ids, 'selected', false);
}
/** @noinspection PhpUnused */
public function selectByOptions($options ) {
if ( $this->is_api && $this->type != '' && isset($this->option_path[$this->type], $options['_fo']) ) {
$path = $this->option_path[$this->type];
$key = $this->key;
$o = $options['_fo'];
foreach( $this->values as &$value ) {
if ( $this->type == 'taxonomy' ) {
if ( isset($o[$path], $o[$path][$value->taxonomy]) ) {
$posted = $o[$path][$value->taxonomy];
if ( is_array($posted) ) {
$value->selected = in_array($value->{$key}, $posted);
} else {
$value->selected = $value->{$key} == $posted;
}
} else {
$value->selected = false;
}
} else if ( $this->type == 'custom_field' ) {
if ( method_exists($this, 'getUniqueFieldName') ) {
$unique_field_name = $this->getUniqueFieldName();
if ( isset($o[$path], $o[$path][$unique_field_name]) ) {
$posted = $o[$path][$unique_field_name];
if ( is_array($posted) ) {
if ( !is_array($value->{$key}) ) {
$value->selected = in_array($value->{$key} . '', $posted);
} else {
$value->value = array_values($posted);
}
} else {
$value->selected = $value->{$key} == $posted;
if ( $this->display_mode == 'datepicker' && isset($o[$path][$unique_field_name]) ) {
$value->value = $o[$path][$unique_field_name];
} else if ( in_array($this->display_mode, array('hidden', 'text', 'slider')) ) {
$value->value = $posted;
}
}
} else {
$value->selected = false;
}
}
}
}
}
}
public function type(): string {
return $this->type;
}
public function field(): string {
return $this->type();
}
public static function getLastId(): int {
return self::$last_id;
}
/** @noinspection PhpUnused */
public static function getLastPosition() {
return self::$last_position;
}
public static function reset() {
self::$last_id = 0;
self::$last_position = 0;
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace WPDRMS\ASP\Frontend\Filters;
if (!defined('ABSPATH')) die('-1');
class Generic extends Filter {
public $data = array(
"field" => ""
);
protected $default = array(
'label' => '',
'selected' => false,
'value' => '',
'level' => 0,
'default' => false
);
protected $key = 'value';
protected $type = 'generic';
}

View File

@@ -0,0 +1,12 @@
<?php
namespace WPDRMS\ASP\Frontend\Filters;
if (!defined('ABSPATH')) die('-1');
class PostTags extends TaxonomyTerm {
protected $type = 'post_tags';
public function field(): string {
return 'post_tag';
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace WPDRMS\ASP\Frontend\Filters;
if (!defined('ABSPATH')) die('-1');
class PostType extends Filter {
public $data = array(
'required' => false,
'invalid_input_text' => 'This is required!'
);
protected $default = array(
'label' => '',
'selected' => false,
'value' => '',
'level' => 0,
'default' => false
);
protected $key = 'value';
protected $type = 'post_type';
}

View File

@@ -0,0 +1,47 @@
<?php
namespace WPDRMS\ASP\Frontend\Filters;
if (!defined('ABSPATH')) die('-1');
class TaxonomyTerm extends Filter {
public $data = array(
"type" => "checkboxes",
"required" => false,
"invalid_input_text" => "Please select one!",
"default" => "checked",
"placeholder" => "",
"taxonomy" => "category",
"allow_empty" => '', // true or false, or '' for inherit
"logic" => '' // and, or, andex or '' for inherit
);
protected $default = array(
'label' => '',
'selected' => false,
'id' => 0,
'level' => 0,
'default' => false,
'parent' => 0,
'taxonomy' => 'category'
);
protected $type = 'taxonomy';
public function field(): string {
return $this->data['taxonomy'];
}
public function isMixed(): bool {
$taxonomies = array();
foreach ( $this->values as $value ) {
if ( $value->id != 0 && isset($value->taxonomy) ) {
$taxonomies[] = $value->taxonomy;
$taxonomies = array_unique($taxonomies);
if (count($taxonomies) > 1)
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,208 @@
<?php /** @noinspection PhpMissingParamTypeInspection */
/** @noinspection PhpMissingReturnTypeInspection */
namespace WPDRMS\ASP\Frontend;
use WPDRMS\ASP\Frontend\Filters\Filter;
use WPDRMS\ASP\Patterns\SingletonTrait;
if ( !defined('ABSPATH') ) die('-1');
class FiltersManager {
use SingletonTrait;
const NAMESPACE = "WPDRMS\\ASP\\Frontend\\Filters\\";
/**
* Filters array
*
* @var array
*/
private $filters = array();
/**
* The current search ID
*
* @var int
*/
private $search_id = 0;
/**
* Types to class names array
*
* @var array
*/
private $classes = array(
'generic' => 'Generic',
'content_type' => 'ContentType',
'post_type' => 'PostType',
'taxonomy' => 'TaxonomyTerm',
'post_tags' => 'PostTags',
'custom_field' => 'CustomField',
'date' => 'Date',
'button' => 'Button'
);
/**
* Sets the currenty search ID
*
* @param $id - search ID
*/
public function setSearchId($id) {
$this->search_id = $id + 0;
}
/**
* Creates and returns a new filter object, depending on the type
*
* @param $type - Type of the filter (generic, content_type, taxonomy, custom_field, date, search, reset)
* @param $label
* @param $display_mode - checkboxes, input, slider, range, dropdown, radio, dropdownsearch, multisearch
* @param $data - Other related information for the given filter type
* @return Filter
*/
public function create($type, $label = '', $display_mode = '', $data = array()) {
$class = !empty($type) && isset($this->classes[$type]) ? $this->classes[$type] : $this->classes['taxonomy'];
$class = self::NAMESPACE . $class;
return new $class($label, $display_mode, $data);
}
/**
* Adds an existing filter to the end of the filters list
*
* @param Filter $filter
* @return Filter mixed
*/
public function add( $filter ) {
if ( !isset($this->filters[$this->search_id]) )
$this->filters[$this->search_id] = array();
$this->filters[$this->search_id][] = $filter;
$this->organize();
return $filter;
}
/**
* Removes a filter by name or array key (id)
*
* @param int|string $key
* @return bool
*/
public function remove( $key ) {
$k = $this->find( $key, true );
if ( $k !== false ) {
unset($this->filters[$this->search_id][$k]);
$this->organize();
return true;
}
return false;
}
/**
* Clears the filters array, removing every filter, and resetting every position etc...
*
* @param int $id the search ID
*/
public function clear($id ) {
if ( isset($this->filters[$id]) )
$this->filters[$id] = array();
Filter::reset();
}
/**
* Finds a filter by array key (id) or label
*
* @param $key
* @param bool $key_only
* @return bool|int|Filter
*/
public function find($key, $key_only = false ) {
if ( is_numeric($key) ) {
if ( isset($this->filters[$this->search_id][$key]) ) {
return $key_only ? $key : $this->filters[$this->search_id][$key];
}
} else {
foreach ( $this->filters[$this->search_id] as $k => $v ) {
if ( isset($v->label) && $v->label == $key ) {
return $key_only ? $k : $this->filters[$this->search_id][$k];
}
}
}
return false;
}
/**
* Gets the filters
*
* @param string $order 'position' or 'added'
* @param bool|string $type false, or the filter type
* @return Filter[]
*/
public function get($order = 'position', $type = false ) {
if ( !isset($this->filters[$this->search_id]) )
return array();
if ( $order == 'position' ) {
$return = array();
$added = array();
while ( count($return) != count($this->filters[$this->search_id]) ) {
$key = -1;
$lowest_position = 999999999;
foreach ( $this->filters[$this->search_id] as $k => $v ) {
if ( !in_array($k, $added) ) {
if ( $v->position <= $lowest_position ) {
$lowest_position = $v->position;
$key = $k;
}
}
}
if ( $key > -1 && !in_array($key, $added) ) {
$return[] = $this->filters[$this->search_id][$key];
$added[] = $key;
}
}
} else {
$return = $this->filters[$this->search_id];
}
if ( $type !== false ) {
foreach ( $return as $k => $filter ) {
if ( $filter->type() != $type ) {
unset($return[$k]);
}
}
}
return apply_filters('asp_pre_get_front_filters', $return, $type);
}
/**
* Sets a filter attribute by array key (id) or by label
*
* @param int|string $key array key (id)
* @param $attribute
* @param $value
* @return bool
*/
public function set($key, $attribute, $value ) {
$k = $this->find( $key, true );
if ( $k !== false ) {
$this->filters[$this->search_id][$k]->{$attribute} = $value;
return true;
}
return false;
}
/**
* Reorganizes the filter IDs
*/
private function organize() {
foreach ( $this->filters as $k => $v ) {
if ( is_array($v) ) {
foreach ( $this->filters[$k] as $kk => $vv ) {
$this->filters[$k][$kk]->id = $kk + 1;
}
}
}
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace WPDRMS\ASP\Hooks\Actions;
use WPDRMS\ASP\Patterns\SingletonTrait;
if (!defined('ABSPATH')) die('-1');
abstract class AbstractAction {
use SingletonTrait;
/**
* The handler
*
* This function is called by the appropriate handler
*/
abstract public function handle();
}

View File

@@ -0,0 +1,50 @@
<?php
namespace WPDRMS\ASP\Hooks\Actions;
if (!defined('ABSPATH')) die('-1');
class Cookies extends AbstractAction {
public function handle() {
// Forcefully unset the cookies, if requested
if ( isset($_GET['asp_unset_cookies']) ) {
self::forceUnsetCookies();
}
/**
* NOTES
* ---------------------------------------
* DO NOT DELETE THE COOKIES HERE, UNLESS A CERTAIN CRITERIA MET!!
* This filter is executed on ajax requests and redirects and during all
* kinds of background tasks. Unset only if the criteria is deterministic,
* and applies on a very specific case. (like the example above)
*/
// Set cookie if this is search, and asp is active
if ( isset($_POST['asp_active']) && isset($_GET['s']) ) {
if (isset($_POST['p_asp_data']) || isset($_POST['np_asp_data'])) {
$parse = parse_url(get_bloginfo('url'));
$host = $parse['host'];
$_p_data = $_POST['p_asp_data'] ?? $_POST['np_asp_data'];
$_p_id = $_POST['p_asid'] ?? $_POST['np_asid'];
setcookie('asp_data', $_p_data, time() + (30 * DAY_IN_SECONDS), '/', $host, is_ssl() );
setcookie('asp_id', $_p_id, time() + (30 * DAY_IN_SECONDS), '/', $host, is_ssl() );
setcookie('asp_phrase', $_GET['s'], time() + (30 * DAY_IN_SECONDS), '/', $host, is_ssl() );
$_COOKIE['asp_data'] = $_p_data;
$_COOKIE['asp_id'] = $_p_id;
$_COOKIE['asp_phrase'] = $_GET['s'];
}
}
}
public static function forceUnsetCookies() {
$parse = parse_url(get_bloginfo('url'));
$host = $parse['host'];
setcookie('asp_data', '', time() - 7200, '/', $host, is_ssl() );
setcookie('asp_id', '', time() - 7200, '/', $host, is_ssl() );
setcookie('asp_phrase', '', time() - 7200, '/', $host, is_ssl() );
unset($_COOKIE['asp_data']);
unset($_COOKIE['asp_id']);
unset($_COOKIE['asp_phrase']);
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace WPDRMS\ASP\Hooks\Actions;
if (!defined('ABSPATH')) die('-1');
class Footer extends AbstractAction {
public function handle() {
$exit1 = apply_filters('asp_load_css_js', false);
$exit2 = apply_filters('asp_load_css', false);
if ($exit1 || $exit2)
return;
// Blur for isotopic
?>
<div class='asp_hidden_data' id="asp_hidden_data" style="display: none !important;">
<svg style="position:absolute" height="0" width="0">
<filter id="aspblur">
<feGaussianBlur in="SourceGraphic" stdDeviation="4"/>
</filter>
</svg>
<svg style="position:absolute" height="0" width="0">
<filter id="no_aspblur"></filter>
</svg>
</div>
<?php
}
}

View File

@@ -0,0 +1,236 @@
<?php
namespace WPDRMS\ASP\Hooks\Actions;
use Closure;
use WPDRMS\ASP\Index\Manager;
use WPDRMS\ASP\Options\Data\IndexTableOptions;
if ( !defined('ABSPATH') ) {
die('-1');
}
class IndexTable extends AbstractAction {
/**
* @var Array<string, Closure>
*/
private static array $queue = array();
public function handle(): void {}
/**
* Executes the queued tasks
*
* @return void
*/
public function executeQueue(): void {
foreach ( self::$queue as $callable ) {
$callable();
}
}
/**
* Triggers on post metadata update
*
* @param int $mid The meta ID after successful update.
* @param int $object_id ID of the object metadata is for.
* @return void
*/
public function update_post_meta( int $mid, int $object_id ) {
$it_options = wd_asp()->o['asp_it_options'];
if ( $it_options['it_index_on_update_post_meta'] ) {
self::update($object_id, null, true);
}
}
/**
* Queues an index table update action
*
* @param int|null $post_id
* @param mixed $_post
* @param bool $update
* @return bool
*/
public function update( ?int $post_id = null, $_post = null, bool $update = false ): bool {
/**
* Argument default values are set to NULL, as some developers like to call
* this action without arguments, which causes an error.
*/
if ( !isset($post_id) || wp_is_post_revision( $post_id ) ) {
return false;
}
$stop = apply_filters('asp_index_on_save_stop', false, $post_id, $_post, $update);
if ( $stop ) {
return false;
}
// It's already queued :)
if ( isset(self::$queue[ 'post_' . $post_id ]) ) {
return false;
}
self::$queue[ 'post_' . $post_id ] = function () use ( $post_id, $update ): void {
$it_options = wd_asp()->o['asp_it_options'];
if ( $it_options === false ) {
return;
}
/**
* In some cases custom fields are not created in time of saving the post.
* To solve that, the user has an option to turn off automatic indexing
* when the post is created - but not when updated, or when a CRON job is executed.
*/
if ( !$it_options['it_index_on_save'] && !$update ) {
return;
}
$args = array();
foreach ( $it_options as $k => $o ) {
$args[ str_replace('it_', '', $k) ] = $o;
}
/**
* @var IndexTableOptions $new_options
*/
$new_options = $it_options['options'];
$args['attachment_exclude_directories'] = $new_options->get('attachment_exclude_directories')->directories;
$args['attachment_include_directories'] = $new_options->get('attachment_include_directories')->directories;
$it_o = new Manager( $args );
$post_status = get_post_status( $post_id );
$allowed_statuses = array_filter(explode(',', $args['post_statuses']), fn( $v )=>trim($v) !=='');
if ( count($allowed_statuses) === 0 ) {
return;
}
foreach ( $allowed_statuses as &$v ) {
$v = trim($v);
}
$allowed_statuses = apply_filters('asp/index/hooks/post_update/allowed_statuses', $allowed_statuses, $post_id);
if ( $post_status === 'trash' || !in_array($post_status, $allowed_statuses, true) ) {
$this->delete( $post_id );
return;
}
$post_type = get_post_type( $post_id );
$allowed_types = $args['post_types'];
// If this is a product, and product variations should be indexed, index them as well
if ( class_exists('WooCommerce') &&
in_array($post_type, array( 'product', 'product_variation' ), true)
) { // Woo products and variations
if ( $post_type === 'product' ) { // Product saving
// Save the variations, if selected
if ( in_array('product_variation', $allowed_types, true) ) {
$args = array(
'post_type' => 'product_variation',
'post_status' => $allowed_statuses,
'numberposts' => -1,
'fields' => 'ids',
'post_parent' => $post_id, // $post->ID
);
$variations = get_posts($args);
foreach ( $variations as $variation ) {
if ( is_numeric($variation) ) {
$it_o->indexDocument($variation, true, true);
}
}
}
// Save the product, if selected
if ( in_array('product', $allowed_types, true) ) {
$it_o->indexDocument( $post_id, true, true );
}
} elseif ( in_array('product_variation', $allowed_types, true) ) { // variation saving
// Check if post parent status before indexing
$parent = wp_get_post_parent_id( $post_id );
if ( $parent !== false ) {
$parent_post_status = get_post_status( $parent );
if ( in_array($parent_post_status, $allowed_statuses, true) ) {
$it_o->indexDocument( $post_id, true, true );
}
}
}
} else { // Any other post type
$it_o->indexDocument( $post_id, true, true );
}
};
return true;
}
public function delete( int $post_id ): void {
$it_o = new Manager();
$post_type = get_post_type( $post_id );
if ( class_exists('WooCommerce') &&
$post_type === 'product'
) {
$args = array(
'post_type' => 'product_variation',
'post_status' => 'any',
'numberposts' => -1,
'fields' => 'ids',
'post_parent' => $post_id, // $post->ID
);
$variations = get_posts( $args );
$variations[] = $post_id;
$it_o->removeDocument( $variations );
} else {
$it_o->removeDocument( $post_id );
}
}
public function extend(): void {
$asp_it_options = wd_asp()->o['asp_it_options'];
if ( $asp_it_options !== false ) {
$args = array();
foreach ( $asp_it_options as $k => $o ) {
$args[ str_replace('it_', '', $k) ] = $o;
}
/**
* @var IndexTableOptions $new_options
*/
$new_options = $asp_it_options['options'];
$args['attachment_exclude_directories'] = $new_options->get('attachment_exclude_directories')->directories;
$args['attachment_include_directories'] = $new_options->get('attachment_include_directories')->directories;
$it_obj = new Manager( $args );
$res = $it_obj->extendIndex( );
update_option(
'asp_it_cron',
array(
'last_run' => time(),
'result' => $res,
)
);
}
}
public function cron_extend(): void {
if ( wp_next_scheduled( 'asp_cron_it_extend' ) !== false ) {
return;
}
$asp_it_options = wd_asp()->o['asp_it_options'];
if ( $asp_it_options === false ) {
return;
}
$enabled = $asp_it_options['it_cron_enable'];
$period = $asp_it_options['it_cron_period'];
$do_media = is_array($asp_it_options['it_post_types']) && in_array( 'attachment', $asp_it_options['it_post_types'], true );
// If enabled, or attachments are queued for index
if ( $enabled || $do_media ) {
if ( $do_media ) {
// Index media at least every 5 minutes
if ( $enabled && in_array($period, array( 'asp_cr_two_minutes', 'asp_cr_three_minutes' ), true) ) {
wp_schedule_event(time(), $period, 'asp_cron_it_extend');
} else {
wp_schedule_event(time(), 'asp_cr_five_minutes', 'asp_cron_it_extend');
}
} else {
wp_schedule_event(time(), $period, 'asp_cron_it_extend');
}
}
}
}

View File

@@ -0,0 +1,52 @@
<?php /** @noinspection PhpUnused */
namespace WPDRMS\ASP\Hooks\Actions;
use WPDRMS\ASP\Hooks\Ajax\DeleteCache;
use WPDRMS\ASP\Index\Database;
use WPDRMS\ASP\Utils\Polylang\StringTranslations as PolylangStringTranslations;
if (!defined('ABSPATH')) die('-1');
class Other extends AbstractAction {
public function handle() {}
public function on_save_post() {
// Clear all the cache just in case
DeleteCache::getInstance()->handle(false);
}
/**
* Fix for 'WP External Links' plugin
* https://wordpress.org/plugins/wp-external-links/
*
* @param $link
*/
public function plug_WPExternalLinks_fix( $link ) {
// ignore links with class "asp_showmore"
if ( $link->has_attr_value( 'class', 'asp_showmore' ) ) {
$link->set_ignore();
}
}
public function pll_init_string_translations() {
PolylangStringTranslations::init();
}
public function pll_save_string_translations() {
// Save any possible PLL translation strings stack
PolylangStringTranslations::save();
}
public function pll_register_string_translations() {
PolylangStringTranslations::register();
}
/**
* Triggers when asp_scheduled_activation_events is triggered (during activation only)
*/
public function scheduledActivationEvents() {
$index = new Database();
$index->scheduled();
}
}

View File

@@ -0,0 +1,18 @@
<?php /** @noinspection PhpUnused */
namespace WPDRMS\ASP\Hooks\Actions;
if (!defined('ABSPATH')) die('-1');
class OutputBuffer extends AbstractAction {
// template_redirect
function obStart() {
\WPDRMS\ASP\Misc\OutputBuffer::getInstance()->obStart();
}
// shutdown
function obClose() {
\WPDRMS\ASP\Misc\OutputBuffer::getInstance()->obClose();
}
function handle(){}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace WPDRMS\ASP\Hooks\Actions;
use WPDRMS\ASP\Updates\Manager;
if (!defined('ABSPATH')) die('-1');
class Updates extends AbstractAction {
function handle() {
new Manager(ASP_PLUGIN_NAME, ASP_PLUGIN_BASE, wd_asp()->updates);
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace WPDRMS\ASP\Hooks\Actions;
if (!defined('ABSPATH')) die('-1');
class Widgets extends AbstractAction {
public function handle() {
register_widget("\\WPDRMS\\ASP\\Widgets\\Search");
register_widget("\\WPDRMS\\ASP\\Widgets\\LastSearches");
register_widget("\\WPDRMS\\ASP\\Widgets\\TopSearches");
}
}

View File

@@ -0,0 +1,277 @@
<?php
namespace WPDRMS\ASP\Hooks;
if ( !defined('ABSPATH') ) {
die('-1');
}
class ActionsManager {
const NAMESPACE = 'WPDRMS\\ASP\\Hooks\\Actions\\';
/**
* Array of internal known actions
*
* @var array
*/
private static $actions = array(
array(
'action' => 'init',
'handler' => 'Cookies',
'priority' => 10,
'args' => 0,
),
array(
'action' => 'template_redirect',
'handler' => array( 'OutputBuffer', 'obStart' ),
'priority' => 9999,
'args' => 0,
),
array(
'action' => 'shutdown',
'handler' => array( 'OutputBuffer', 'obClose' ),
'priority' => -101,
'args' => 0,
),
array(
'action' => 'wp_footer',
'handler' => 'Footer',
'priority' => 10,
'args' => 0,
),
array(
'action' => 'init',
'handler' => 'Updates',
'priority' => 10,
'args' => 0,
),
array(
'action' => 'widgets_init',
'handler' => 'Widgets',
'priority' => 10,
'args' => 0,
),
array(
'action' => 'edit_attachment',
'handler' => array( 'IndexTable', 'update' ),
'priority' => 999999998,
'args' => 3,
),
array(
'action' => 'add_attachment',
'handler' => array( 'IndexTable', 'update' ),
'priority' => 999999998,
'args' => 3,
),
array(
'action' => 'save_post',
'handler' => array( 'IndexTable', 'update' ),
'priority' => 999999998,
'args' => 3,
),
array(
'action' => 'added_post_meta',
'handler' => array( 'IndexTable', 'update_post_meta' ),
'priority' => 999999999,
'args' => 2,
),
array(
'action' => 'updated_post_meta',
'handler' => array( 'IndexTable', 'update_post_meta' ),
'priority' => 999999999,
'args' => 2,
),
array(
'action' => 'wp_insert_post',
'handler' => array( 'IndexTable', 'update' ),
'priority' => 999999999,
'args' => 3,
),
array(
'action' => 'new_to_publish',
'handler' => array( 'Other', 'on_save_post' ),
'priority' => 999999999,
'args' => 0,
),
array(
'action' => 'draft_to_publish',
'handler' => array( 'Other', 'on_save_post' ),
'priority' => 999999999,
'args' => 0,
),
array(
'action' => 'pending_to_publish',
'handler' => array( 'Other', 'on_save_post' ),
'priority' => 999999999,
'args' => 0,
),
array(
'action' => 'delete_post',
'handler' => array( 'IndexTable', 'delete' ),
'priority' => 10,
'args' => 1,
),
array(
'action' => 'asp_cron_it_extend',
'handler' => array( 'IndexTable', 'extend' ),
'priority' => 10,
'args' => 0,
'cron' => true,
),
array(
'action' => 'wp_footer',
'handler' => array( 'IndexTable', 'executeQueue' ),
'priority' => 10,
'args' => 0,
),
array(
'action' => 'admin_bar_menu',
'handler' => array( 'IndexTable', 'executeQueue' ),
'priority' => 10,
'args' => 0,
),
array(
'action' => 'shutdown',
'handler' => array( 'IndexTable', 'executeQueue' ),
'priority' => 10,
'args' => 0,
),
array(
'action' => 'wpel_before_apply_link',
'handler' => array( 'Other', 'plug_WPExternalLinks_fix' ),
'priority' => 9999,
'args' => 1,
),
array(
'action' => 'wp_footer',
'handler' => array( 'Other', 'pll_save_string_translations' ),
'priority' => 9999,
'args' => 0,
),
array(
'action' => 'init',
'handler' => array( 'Other', 'pll_init_string_translations' ),
'priority' => 9998,
'args' => 0,
),
array(
'action' => 'init',
'handler' => array( 'Other', 'pll_register_string_translations' ),
'priority' => 9999,
'args' => 0,
),
array(
'action' => 'asp_scheduled_activation_events',
'handler' => array( 'Other', 'scheduledActivationEvents' ),
'priority' => 9999,
'args' => 0,
),
);
/**
* Array of already registered handlers
*
* @var array
*/
private static $registered = array();
/**
* Registers all the handlers from the $actions variable
*/
public static function registerAll() {
foreach ( self::$actions as $data ) {
self::register($data['action'], $data['handler'], $data['priority'], $data['args']);
if ( !empty($data['cron']) ) {
self::registerCron( $data['handler'] );
}
}
}
/**
* Get all the queued handlers
*
* @return array
*/
public static function getAll(): array {
return array_keys(self::$actions);
}
/**
* Get all the already registered handlers
*
* @return array
*/
public static function getRegistered(): array {
return self::$registered;
}
/**
* Registers an action with the handler class name.
*
* @param $action
* @param $handler
* @param int $priority
* @param int $accepted_args
* @return bool
*/
public static function register( $action, $handler, int $priority = 10, int $accepted_args = 0 ): bool {
if ( is_array($handler) ) {
$class = self::NAMESPACE . $handler[0];
$handle = $handler[1];
} else {
$class = self::NAMESPACE . $handler;
$handle = 'handle';
}
if ( !class_exists($class) ) {
return false;
}
add_action($action, array( call_user_func(array( $class, 'getInstance' )), $handle ), $priority, $accepted_args);
self::$registered[] = $action;
return true;
}
public static function registerCron( $handler ) {
if ( is_array($handler) ) {
$class = self::NAMESPACE . $handler[0];
$handle = 'cron_' . $handler[1];
} else {
$class = self::NAMESPACE . $handler;
$handle = 'cron_handle';
}
$o = call_user_func(array( $class, 'getInstance' ));
// Run the handler, that will register the event
$o->$handle();
}
/**
* Deregisters an action handler.
*
* @param $action
* @param $handler
* @noinspection PhpMissingParamTypeInspection
*/
public static function deregister( $action, $handler = false ) {
if ( $handler === false ) {
foreach ( self::$actions as $a ) {
if ( $a['action'] == $action ) {
if ( is_array($a['handler']) ) {
remove_action($action, array( call_user_func(array( self::NAMESPACE . $a['handler'][0], 'getInstance' )), $a['handler'][1] ));
} else {
remove_action($action, array( call_user_func(array( self::NAMESPACE . $a['handler'], 'getInstance' )), 'handle' ));
}
}
}
} elseif ( is_array($handler) ) {
remove_action($action, array( call_user_func(array( self::NAMESPACE . $handler[0], 'getInstance' )), $handler[1] ));
} elseif ( is_string($handler) ) {
remove_action($action, array( call_user_func(array( self::NAMESPACE . $handler, 'getInstance' )), 'handle' ));
}
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace WPDRMS\ASP\Hooks\Ajax;
use WPDRMS\ASP\Patterns\SingletonTrait;
if (!defined('ABSPATH')) die('-1');
abstract class AbstractAjax {
use SingletonTrait;
/**
* The handler
*
* This function is called by the appropriate handler
*/
abstract public function handle();
}

View File

@@ -0,0 +1,20 @@
<?php
namespace WPDRMS\ASP\Hooks\Ajax;
use WPDRMS\ASP\Misc\Statistics;
use WPDRMS\ASP\Utils\Ajax;
if (!defined('ABSPATH')) die('-1');
class AddKeyword extends AbstractAjax {
function handle() {
if ( isset($_POST['id'], $_POST['keyword']) ) {
echo (Statistics::addKeyword($_POST['id'] + 0, $_POST['keyword']) === true) ? 1 : 0;
exit;
}
Ajax::prepareHeaders();
echo 0;
die();
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace WPDRMS\ASP\Hooks\Ajax;
use WPDRMS\ASP\Suggest\KeywordSuggest;
use WPDRMS\ASP\Utils\Ajax;
if (!defined('ABSPATH')) die('-1');
class Autocomplete extends AbstractAjax {
public function handle() {
// DO NOT TRIM! It will give incorrect results :)
$s = preg_replace('/\s+/', ' ', $_POST['sauto']);
do_action('asp_before_autocomplete', $s);
if ( empty($_POST['asid']) ) return "";
$search = wd_asp()->instances->get( $_POST['asid'] + 0 );
if ( empty($search['data']) )
return false;
$sd = &$search['data'];
$options = array();
if ( isset($_POST['options']) ) {
if (is_array($_POST['options']))
$options = $_POST['options'];
else
parse_str($_POST['options'], $options);
}
$keyword = '';
$types = array();
if (isset($sd['customtypes']) && count($sd['customtypes']) > 0)
$types = array_merge($types, $sd['customtypes']);
foreach (w_isset_def($sd['selected-autocomplete_source'], array('google')) as $source) {
if ( empty($source) )
continue;
$taxonomy = "";
// Check if this is a taxonomy
if (strpos($source, 'xtax_') !== false) {
$taxonomy = str_replace('xtax_', '', $source);
$source = "terms";
}
if ( function_exists( 'qtranxf_use' ) && !empty($options['qtranslate_lang']) ) {
$lang = $options['qtranslate_lang'];
} else if ( !empty($options['wpml_lang']) ) {
$lang = $options['wpml_lang'];
} else if ( !empty($options['polylang_lang']) ) {
$lang = $options['polylang_lang'];
} else {
$lang = $sd['keywordsuggestionslang'];
}
$t = new KeywordSuggest($source, array(
'maxCount' => 1,
'maxCharsPerWord' => $sd['autocomplete_length'],
'postTypes' => $types,
'lang' => $lang,
'overrideUrl' => '',
'taxonomy' => $taxonomy,
'match_start' => true,
'api_key' => $sd['autoc_google_places_api'],
'search_id' => $_POST['asid'] + 0,
'options' => $options
));
$res = $t->getKeywords($s);
if (isset($res[0]) && $keyword = $res[0])
break;
}
do_action('asp_after_autocomplete', $s, $keyword);
Ajax::prepareHeaders();
print $keyword;
die();
}
}

View File

@@ -0,0 +1,47 @@
<?php
/** @noinspection PhpMissingParamTypeInspection */
/** @noinspection PhpMissingReturnTypeInspection */
namespace WPDRMS\ASP\Hooks\Ajax;
use WPDRMS\ASP\Cache\TextCache;
use WPDRMS\ASP\Utils\Ajax;
use WPDRMS\ASP\Utils\FileManager;
if (!defined('ABSPATH')) die('-1');
class DeleteCache extends AbstractAjax {
/**
* Deletes the Ajax Search Pro directory
*/
public function handle( $exit = true ) {
// $exit can be an empty string "", so force boolean
$exit = $exit !== false ? true : false;
if (
current_user_can( 'manage_options' ) &&
( !$exit || (
isset($_POST['asp_delete_cache_request_nonce']) &&
wp_verify_nonce( $_POST['asp_delete_cache_request_nonce'], 'asp_delete_cache_request_nonce' )
) )
) {
if ( !empty(wd_asp()->cache_path) && wd_asp()->cache_path !== '' )
$count = FileManager::instance()->deleteByPattern(wd_asp()->cache_path, '*.wpd');
if ( !empty(wd_asp()->bfi_path) && wd_asp()->bfi_path !== '' ) {
$count = $count + FileManager::instance()->deleteByPattern(wd_asp()->bfi_path, '*.jpg');
$count = $count + FileManager::instance()->deleteByPattern(wd_asp()->bfi_path, '*.jpeg');
$count = $count + FileManager::instance()->deleteByPattern(wd_asp()->bfi_path, '*.png');
}
// Clear database cache
$count = $count + TextCache::clearDBCache();
}
if ( $exit !== false ) {
Ajax::prepareHeaders();
print $count;
die();
}
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace WPDRMS\ASP\Hooks\Ajax;
use WPDRMS\ASP\Misc\Statistics;
use WPDRMS\ASP\Utils\Ajax;
if (!defined('ABSPATH')) die('-1');
class DeleteKeyword extends AbstractAjax {
function handle() {
if (
isset($_POST['asp_statistics_request_nonce']) &&
wp_verify_nonce( $_POST['asp_statistics_request_nonce'], 'asp_statistics_request_nonce' ) &&
current_user_can( 'manage_options' )
) {
if (isset($_POST['keywordid'])) {
echo Statistics::deleteKw($_POST['keywordid'] + 0);
exit;
}
Ajax::prepareHeaders();
echo 0;
}
die();
}
}

View File

@@ -0,0 +1,147 @@
<?php
namespace WPDRMS\ASP\Hooks\Ajax;
use WPDRMS\ASP\Index\Database;
use WPDRMS\ASP\Index\Manager;
use WPDRMS\ASP\Utils\Ajax;
if ( !defined('ABSPATH') ) {
die('-1');
}
class IndexTable extends AbstractAjax {
public function handle() {
if ( isset($_POST['asp_it_request_nonce']) &&
wp_verify_nonce( $_POST['asp_it_request_nonce'], 'asp_it_request_nonce' ) &&
current_user_can( 'manage_options' )
) {
if ( $_POST['action'] == 'asp_indextable_optimize' ) {
$it = new Database();
print 'OPTIMIZE: ' . $it->optimize();
die();
}
Ajax::prepareHeaders();
if ( isset($_POST['data']) ) {
if ( is_array($_POST['data']) ) {
$options = $_POST['data'];
} else {
parse_str($_POST['data'], $options);
}
$options = wd_asp()->instances->decode_params($options);
} else {
print 'No post data detected, function terminated.';
die();
}
update_option('asp_recreate_index', 0);
$limit = $options['it_limit'];
// Adjust the limit based on the previous and longest request duration
if ( isset($_POST['last_request_duration'], $_POST['longest_request_duration']) ) {
$dur = ( intval( $_POST['last_request_duration'] ) + intval( $_POST['longest_request_duration'] ) ) / 2;
if ( $dur > 25 ) {
$limit = intval($limit / 5);
} elseif ( $dur > 20 ) {
$limit = intval($limit / 4);
} elseif ( $dur > 15 ) {
$limit = intval($limit / 3);
} elseif ( $dur > 10 ) {
$limit = intval($limit / 2);
}
$limit = $limit < 1 ? 1 : $limit;
}
$it_obj = new Manager(
array(
'index_title' => $options['it_index_title'],
'index_content' => $options['it_index_content'],
'index_excerpt' => $options['it_index_excerpt'],
'index_tags' => $options['it_index_tags'],
'index_categories' => $options['it_index_categories'],
'post_types' => $options['it_post_types'],
'attachment_mime_types' => $options['it_attachment_mime_types'],
'index_pdf_content' => $options['it_index_pdf_content'],
'index_pdf_method' => $options['it_index_pdf_method'],
'index_text_content' => $options['it_index_text_content'],
'index_richtext_content' => $options['it_index_richtext_content'],
'index_msword_content' => $options['it_index_msword_content'],
'index_msexcel_content' => $options['it_index_msexcel_content'],
'index_msppt_content' => $options['it_index_msppt_content'],
'media_service_send_file' => $options['it_media_service_send_file'],
'attachment_exclude_directories' =>
$options['attachment_exclude_directories']['directories'] ??
wd_asp()->options['asp_it_options']['options']->get('attachment_exclude_directories')->directories,
'attachment_include_directories' =>
$options['attachment_include_directories']['directories'] ??
wd_asp()->options['asp_it_options']['options']->get('attachment_include_directories')->directories,
'post_statuses' => $options['it_post_statuses'],
'post_password_protected' => $options['it_post_password_protected'],
'index_taxonomies' =>$options['it_index_taxonomies'],
'index_permalinks' =>$options['it_index_permalinks'],
'index_customfields' => $options['it_index_customfields'],
'index_author_name' => $options['it_index_author_name'],
'index_author_bio' => $options['it_index_author_bio'],
'blog_id' => !empty($_POST['blog_id']) ? $_POST['blog_id'] : get_current_blog_id(),
'inflections' => $options['it_inflections'],
'language' => $options['it_language'],
'extend' => ( w_isset_def($_POST['asp_index_action'], 'new') == 'extend' ? 1 : 0 ),
'limit' => $limit,
'use_stopwords' => $options['it_use_stopwords'],
'stopwords' => $options['it_stopwords'],
'min_word_length' => $options['it_min_word_length'],
'extract_gutenberg_blocks' => $options['it_extract_gutenberg_blocks'],
'extract_shortcodes' => $options['it_extract_shortcodes'],
'exclude_shortcodes' => $options['it_exclude_shortcodes'],
'synonyms_as_keywords' => $options['it_synonyms_as_keywords'],
)
);
if ( $_POST['action'] == 'asp_indextable_get_stats' ) {
$stats = array(
'postsIndexed' => $it_obj->getPostsIndexed(),
'postsToIndex' => $it_obj->getPostIdsToIndexCount(),
'totalKeywords' => $it_obj->getTotalKeywords(),
);
print '!!!ASP_INDEX_STAT_START!!!';
print_r(json_encode($stats));
print '!!!ASP_INDEX_STAT_STOP!!!';
die();
}
if ( isset($_POST['asp_index_action']) ) {
switch ( $_POST['asp_index_action'] ) {
case 'new':
$ret = $it_obj->newIndex();
print 'New index !!!ASP_INDEX_START!!!';
print_r(json_encode($ret));
print '!!!ASP_INDEX_STOP!!!';
die();
case 'extend':
$ret = $it_obj->extendIndex();
print 'Extend index !!!ASP_INDEX_START!!!';
print_r(json_encode($ret));
print '!!!ASP_INDEX_STOP!!!';
die();
case 'switching_blog':
$ret = $it_obj->extendIndex(true);
print 'Extend index (blog_switch) !!!ASP_INDEX_START!!!';
print_r(json_encode($ret));
print '!!!ASP_INDEX_STOP!!!';
die();
case 'delete':
$ret = $it_obj->emptyIndex();
print 'Delete index !!!ASP_INDEX_START!!!';
print_r(json_encode($ret));
print '!!!ASP_INDEX_STOP!!!';
die();
}
}
// no action set, or other failure
print 'No action !!!ASP_INDEX_START!!!0!!!ASP_INDEX_STOP!!!';
}
die();
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace WPDRMS\ASP\Hooks\Ajax;
use WPDRMS\ASP\Misc\PluginLicense;
if (!defined('ABSPATH')) die('-1');
class License extends AbstractAjax {
function handle() {
if (
isset($_POST['asp_license_request_nonce']) &&
wp_verify_nonce( $_POST['asp_license_request_nonce'], 'asp_license_request_nonce' ) &&
current_user_can( 'manage_options' )
) {
if ( !isset($_POST['op']) ) die(-2);
if ( ASP_DEMO ) {
print_r(json_encode(array("status"=>0, "message"=>"This functions is disabled on this demo.")));
die();
}
if ( $this->excessiveUsage() ) {
print_r(json_encode(array("status"=>0, "message"=>"WP Excessive usage Warning: Please wait a few seconds before the next request.")));
die();
}
if ( $_POST['op'] == "activate" && !empty($_POST['asp_key']) ) {
$key = $this->preValidateKey( $_POST['asp_key'] );
if ( $key === false ) {
print_r(json_encode(array("status"=>0, "message"=>"WP: Invalid key specified.")));
die();
}
$res = PluginLicense::activate( $key );
if ($res === false)
print_r(json_encode(array("status"=>0, "message"=>"WP: Connection error, please try again later.")));
else
print_r(json_encode($res));
die();
} else if ($_POST['op'] == "deactivate") {
$res = PluginLicense::deactivate();
if ($res === false)
print_r(json_encode(array("status"=>0, "message"=>"WP: Connection error, please try again later.")));
else
print_r(json_encode($res));
die();
}
// We reached here, something is missing..
print_r(json_encode(array("status"=>0, "message"=>"WP: Missing information, please check the input fields.")));
}
die();
}
function preValidateKey( $key ) {
$key = trim($key);
if ( strlen($key)!=36 )
return false;
return $key;
}
function excessiveUsage(): bool {
$usage = get_option("_asp_update_usage", array());
$n_usage = array();
// Leave only recent usages
foreach ($usage as $u) {
if ($u > (time() - 60))
$n_usage[] = $u;
}
if ( count($n_usage) <= 10 ) {
$n_usage[] = time();
update_option("_asp_update_usage", $n_usage);
return false;
} else {
return true;
}
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace WPDRMS\ASP\Hooks\Ajax;
use WPDRMS\ASP\Index\Database;
use WPDRMS\ASP\Utils\Ajax;
if (!defined('ABSPATH')) die('-1');
class Maintenance extends AbstractAjax {
public function handle() {
if (
current_user_can( 'manage_options' )
) {
if (ASP_DEMO) {
print "Maintenance !!!ASP_MAINT_START!!!";
print_r(json_encode(array(
'status' => 0,
'action' => '',
'msg' => 'Not allowed in demo mode!'
)));
Ajax::prepareHeaders();
print "!!!ASP_MAINT_STOP!!!";
die();
}
$status = 0;
$msg = 'Missing POST information, please try again!';
$action = 'none';
$nonce = false;
if ( isset($_POST, $_POST['data']) ) {
if (is_array($_POST['data']))
$data = $_POST['data'];
else
parse_str($_POST['data'], $data);
if ( isset($data['asp_reset_nonce']) ) {
$nonce = 'asp_reset_nonce';
} else if ( isset($data['asp_wipe_nonce']) ) {
$nonce = 'asp_wipe_nonce';
} else if ( isset($data['asp_index_defrag_nonce']) ) {
$nonce = 'asp_index_defrag_nonce';
}
if (
$nonce !== false &&
isset($data[$nonce]) &&
wp_verify_nonce( $data[$nonce], $nonce )
) {
if ( $nonce == 'asp_reset_nonce' ) { // Reset
wd_asp()->init->pluginReset();
$status = 1;
$action = 'refresh';
$msg = 'The plugin data was successfully reset!';
} else if ( $nonce == 'asp_wipe_nonce' ) { // Wipe
wd_asp()->init->pluginWipe();
$status = 1;
$action = 'redirect';
$msg = 'All plugin data was successfully wiped, you will be redirected in 5 seconds!';
} else {
$it = new Database();
$it->optimize();
$status = 1;
$action = 'nothing';
$msg = 'Index Table Optimized and Defragmented!';
}
} else {
$msg = 'Missing or invalid NONCE, please <strong>reload this page</strong> and try again!';
}
}
$ret = array(
'status' => $status,
'action' => $action,
'msg' => $msg
);
Ajax::prepareHeaders();
print "Maintenance !!!ASP_MAINT_START!!!";
print_r(json_encode($ret));
print "!!!ASP_MAINT_STOP!!!";
}
die();
}
}

View File

@@ -0,0 +1,42 @@
<?php
/** @noinspection PhpFullyQualifiedNameUsageInspection */
/** @noinspection PhpIncludeInspection */
namespace WPDRMS\ASP\Hooks\Ajax;
use WPDRMS\ASP\Utils\Ajax;
use WPDRMS\ASP\Media\RemoteService;
if (!defined('ABSPATH')) die('-1');
class MediaService extends AbstractAjax {
function handle() {
if (
isset($_POST['asp_mediaservice_request_nonce']) &&
wp_verify_nonce( $_POST['asp_mediaservice_request_nonce'], 'asp_mediaservice_request_nonce' ) &&
current_user_can( 'manage_options' )
) {
if ( isset($_POST['ms_deactivate']) ) {
RemoteService\License::getInstance()->delete();
Ajax::prepareHeaders();
print 0;
} else {
$success = 0;
if ( isset($_POST['ms_license_key']) ) {
$r = RemoteService\License::getInstance()->activate($_POST['ms_license_key']);
$success = $r['success'];
$text = $r['text'];
} else {
$text = "License key is missing or invalid.";
}
Ajax::prepareHeaders();
print_r(json_encode(array(
'success' => $success,
'text' => $text
)));
}
}
exit;
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace WPDRMS\ASP\Hooks\Ajax;
use WPDRMS\ASP\Utils\Ajax;
if (!defined('ABSPATH')) die('-1');
class Preview extends AbstractAjax {
function handle() {
if (
isset($_POST['asp_backend_preview_nonce']) &&
wp_verify_nonce( $_POST['asp_backend_preview_nonce'], 'asp_backend_preview_nonce' ) &&
( current_user_can( 'manage_options' ) || apply_filters('wpdrms/backend/options/ajax/user_role_override', false) )
) {
$o = \WPDRMS\ASP\Shortcodes\Search::getInstance();
// Needs to be here, as the $o->handle(..) also prints out things :)
Ajax::prepareHeaders();
parse_str($_POST['formdata'], $style);
$out = $o->handle(array(
"id" => $_POST['asid'],
"style" => $style,
"preview" => true,
));
print $out;
}
die();
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace WPDRMS\ASP\Hooks\Ajax;
use WPDRMS\ASP\Utils\Ajax;
if (!defined('ABSPATH')) die('-1');
class Priorities extends AbstractAjax {
function handle() {
if (
isset($_POST['asp_priorities_request_nonce']) &&
wp_verify_nonce( $_POST['asp_priorities_request_nonce'], 'asp_priorities_request_nonce' ) &&
current_user_can( 'manage_options' )
) {
if ( !empty($_POST['ptask']) ) {
Ajax::prepareHeaders();
if ( ASP_DEMO && $_POST['ptask'] == "set" ) {
echo "!!PSASPSTART!!0!!PSASPEND!!";
die();
}
if ( $_POST['ptask'] == "get" )
\WPDRMS\ASP\Misc\Priorities::ajax_get_posts();
else if ( $_POST['ptask'] == "set" )
\WPDRMS\ASP\Misc\Priorities::ajax_set_priorities();
}
}
die();
}
}

View File

@@ -0,0 +1,164 @@
<?php /** @noinspection PhpMissingParamTypeInspection */
/** @noinspection PhpComposerExtensionStubsInspection */
namespace WPDRMS\ASP\Hooks\Ajax;
use WPDRMS\ASP\Cache\TextCache;
use WPDRMS\ASP\Misc\Performance;
use WPDRMS\ASP\Query\SearchQuery;
use WPDRMS\ASP\Utils\Ajax;
use WPDRMS\ASP\Utils\Search as SearchUtils;
if (!defined('ABSPATH')) die('-1');
class Search extends AbstractAjax {
private $cache;
/**
* Oversees and handles the search request
*
* @param bool $dontGroup
* @return array|mixed|void
* @noinspection PhpIncludeInspection
*/
public function handle($dontGroup = false) {
$perf_options = wd_asp()->o['asp_performance'];
if (w_isset_def($perf_options['enabled'], 1)) {
$performance = new Performance('asp_performance_stats');
$performance->start_measuring();
}
$s = $_POST['aspp'];
if (is_array($_POST['options']))
$options = $_POST['options'];
else
parse_str($_POST['options'], $options);
$id = (int)$_POST['asid'];
$instance = wd_asp()->instances->get($id);
$sd = &$instance['data'];
if (
wd_asp()->o['asp_caching']['caching'] == 1
) {
$this->printCache($options, $s, $id);
}
// If previewed, we need the details
if ( isset($_POST['asp_preview_options']) && (current_user_can("activate_plugins") || ASP_DEMO) ) {
require_once(ASP_PATH . "backend" . DIRECTORY_SEPARATOR . "settings" . DIRECTORY_SEPARATOR . "types.inc.php");
parse_str( $_POST['asp_preview_options'], $preview_options );
$_POST['asp_preview_options'] = wpdreams_parse_params($preview_options);
$_POST['asp_preview_options'] = wd_asp()->instances->decode_params($_POST['asp_preview_options']);
$sd = $_POST['asp_preview_options'];
}
$asp_query = new SearchQuery(array(
"s" => $s,
"_id" => $id,
"_ajax_search" => true,
"_call_num" => $_POST['asp_call_num'] ?? 0
), $id, $options);
$results = $asp_query->posts;
if ( count($results) > 0 ) {
$results = apply_filters('asp_only_non_keyword_results', $results, $id, $s, $asp_query->getArgs());
} else {
if ( $sd['result_suggestions'] ) {
$results = $asp_query->resultsSuggestions( $sd['keywordsuggestions'] );
} else if ( $sd['keywordsuggestions'] ) {
$results = $asp_query->kwSuggestions();
}
}
if (
count($results) <= 0 &&
$sd['keywordsuggestions'] &&
(!isset($_GET['asp_call_num']) || $_GET['asp_call_num'] < 2)
) {
$results = $asp_query->resultsSuggestions();
} else if (count($results) > 0) {
$results = apply_filters('asp_only_non_keyword_results', $results, $id, $s, $asp_query->getArgs());
}
$results = apply_filters('asp_ajax_results', $results, $id, $s, $sd);
do_action('asp_after_search', $s, $results, $id);
if ( isset($performance) ) {
$performance->stop_measuring();
}
$html_results = SearchUtils::generateHTMLResults($results, $sd, $id, $s);
// Override from hooks
if (isset($_POST['asp_get_as_array'])) {
return $results;
}
$html_results = apply_filters('asp_before_ajax_output', $html_results, $id, $results, $asp_query->getArgs());
$final_output = "";
/* Clear output buffer, possible warnings */
$final_output .= "___ASPSTART_HTML___" . $html_results . "___ASPEND_HTML___";
$final_output .= "___ASPSTART_DATA___";
if ( defined('JSON_INVALID_UTF8_IGNORE') ) {
$final_output .= json_encode(array(
'results_count' => isset($results["keywords"]) ? 0 : $asp_query->returned_posts,
'full_results_count' => $asp_query->found_posts,
'results' => $results
), JSON_INVALID_UTF8_IGNORE);
} else {
$final_output .= json_encode(array(
'results_count' => isset($results["keywords"]) ? 0 : $asp_query->returned_posts,
'full_results_count' => $asp_query->found_posts,
'results' => $results
));
}
$final_output .= "___ASPEND_DATA___";
$this->setCache($final_output);
Ajax::prepareHeaders();
print_r($final_output);
die();
}
private function printCache($options, $s, $id) {
$o = $options;
$this->cache = new TextCache(wd_asp()->cache_path, "z_asp", wd_asp()->o['asp_caching']['cachinginterval'] * 60);
$call_num = $_POST['asp_call_num'] ?? 0;
unset($o['filters_initial'], $o['filters_changed']);
$file_name = md5(json_encode($o) . $call_num . $s . $id);
if ( wd_asp()->o['asp_caching']['caching_method'] == 'file' || wd_asp()->o['asp_caching']['caching_method'] == 'sc_file' ) {
$cache_content = $this->cache->getCache($file_name);
} else {
$cache_content = $this->cache->getDBCache($file_name);
}
if ( $cache_content !== false ) {
$cache_content = apply_filters('asp_cached_content', $cache_content, $s, $id);
do_action('asp_after_search', $cache_content, $s, $id);
print "cached(" . date("F d Y H:i:s.", $this->cache->getLastFileMtime()) . ")";
print_r($cache_content);
die;
}
}
private function setCache($content) {
if ( isset($this->cache) ) {
if (
wd_asp()->o['asp_caching']['caching_method'] == 'file' ||
wd_asp()->o['asp_caching']['caching_method'] == 'sc_file'
) {
$this->cache->setCache('!!ASPSTART!!' . $content . "!!ASPEND!!");
} else {
$this->cache->setDBCache('!!ASPSTART!!' . $content . "!!ASPEND!!");
}
}
}
}

View File

@@ -0,0 +1,108 @@
<?php /** @noinspection PhpComposerExtensionStubsInspection */
namespace WPDRMS\ASP\Hooks\Ajax;
use WPDRMS\ASP\Synonyms\Manager as SynonymsManager;
use WPDRMS\ASP\Utils\Ajax;
if (!defined('ABSPATH')) die('-1');
class Synonyms extends AbstractAjax {
function handle() {
if (
isset($_POST['asp_synonyms_request_nonce']) &&
wp_verify_nonce( $_POST['asp_synonyms_request_nonce'], 'asp_synonyms_request_nonce' ) &&
current_user_can( 'manage_options' )
) {
if ( !isset($_POST['op']) ) {
print -1;
die();
} else if ($_POST['op'] == 'find' || $_POST['op'] == 'findexact') {
$this->find();
} else if ($_POST['op'] == 'update') {
$this->update();
} else if ($_POST['op'] == 'delete') {
$this->delete();
} else if ($_POST['op'] == 'wipe') {
$this->wipe();
} else if ($_POST['op'] == 'export') {
$this->export();
} else if ($_POST['op'] == 'import') {
$this->import();
}
}
}
private function find() {
$syn = SynonymsManager::getInstance();
$exact = $_POST['op'] == 'findexact';
$limit = $exact == true ? 1 : 30;
if ( isset($_POST['keyword'], $_POST['lang']) )
$ret = $syn->find($_POST['keyword'], $_POST['lang'], $limit, $exact);
else
$ret = -1;
print '!!!ASP_SYN_START!!!';
Ajax::prepareHeaders();
print_r(json_encode($ret));
print '!!!ASP_SYN_END!!!';
die();
}
private function update() {
$syn = SynonymsManager::getInstance();
if ( isset($_POST['keyword'], $_POST['synonyms'], $_POST['lang'], $_POST['overwrite_existing']) )
$ret = $syn->update($_POST['keyword'], $_POST['synonyms'], $_POST['lang'], $_POST['overwrite_existing']);
else
$ret = -1;
Ajax::prepareHeaders();
print
'!!!ASP_SYN_START!!!'.
$ret.
'!!!ASP_SYN_END!!!';
die();
}
private function delete() {
$syn = SynonymsManager::getInstance();
if ( isset($_POST['id']) )
$ret = $syn->deleteByID($_POST['id']);
else
$ret = -1;
Ajax::prepareHeaders();
print
'!!!ASP_SYN_START!!!'.
$ret.
'!!!ASP_SYN_END!!!';
die();
}
private function wipe() {
$syn = SynonymsManager::getInstance();
$syn->wipe();
Ajax::prepareHeaders();
print
'!!!ASP_SYN_START!!!0!!!ASP_SYN_END!!!';
die();
}
private function export() {
$syn = SynonymsManager::getInstance();
Ajax::prepareHeaders();
print
'!!!ASP_SYN_START!!!'.$syn->export().'!!!ASP_SYN_END!!!';
die();
}
private function import() {
$syn = SynonymsManager::getInstance();
if ( !isset($_POST['path']) )
$ret = -1;
else
$ret = $syn->import($_POST['path']);
Ajax::prepareHeaders();
print
'!!!ASP_SYN_START!!!'.$ret.'!!!ASP_SYN_END!!!';
die();
}
}

View File

@@ -0,0 +1,222 @@
<?php /** @noinspection PhpUnused */
namespace WPDRMS\ASP\Hooks;
if (!defined('ABSPATH')) die('-1');
class AjaxManager {
const NAMESPACE = "WPDRMS\\ASP\\Hooks\\Ajax\\";
/**
* Array of internal ajax actions
*
* @var array
*/
private static $actions = array(
"ajaxsearchpro_search" => array(
"handler" => "Search",
"priv" => true,
"nopriv" => true
),
"ajaxsearchpro_autocomplete" => array(
"handler" => "Autocomplete",
"priv" => true,
"nopriv" => true
),
"ajaxsearchpro_addkeyword" => array(
"handler" => "AddKeyword",
"priv" => true,
"nopriv" => true
),
"ajaxsearchpro_preview" => array(
"handler" => "Preview",
"priv" => true,
"nopriv" => false
),
"ajaxsearchpro_precache" => array(
"handler" => "Precache",
"priv" => true,
"nopriv" => false
),
"ajaxsearchpro_priorities" => array(
"handler" => "Priorities",
"priv" => true,
"nopriv" => false
),
"ajaxsearchpro_deletecache" => array(
"handler" => "DeleteCache",
"priv" => true,
"nopriv" => false
),
"ajaxsearchpro_deletekeyword" => array(
"handler" => "DeleteKeyword",
"priv" => true,
"nopriv" => false
),
"asp_indextable_admin_ajax" => array(
"handler" => "IndexTable",
"priv" => true,
"nopriv" => false
),
"asp_indextable_optimize" => array(
"handler" => "IndexTable",
"priv" => true,
"nopriv" => false
),
"asp_indextable_get_stats" => array(
"handler" => "IndexTable",
"priv" => true,
"nopriv" => false
),
"asp_media_service" => array(
"handler" => "MediaService",
"priv" => true,
"nopriv" => false
),
'asp_maintenance_admin_ajax' => array(
"handler" => 'Maintenance',
"priv" => true,
"nopriv" => false
),
"asp_license_ajax" => array(
"handler" => "License",
"priv" => true,
"nopriv" => false
),
"asp_syn_admin_ajax" => array(
"handler" => "Synonyms",
"priv" => true,
"nopriv" => false
)
);
/**
* Array of already registered handlers
*
* @var array
*/
private static $registered = array();
/**
* Registers all the handlers from the $actions variable
*/
public static function registerAll( $custom_ajax = false) {
foreach (self::$actions as $action => $data)
self::register($action, $data['handler'], $data['priv'], $data['nopriv'], $custom_ajax);
}
/**
* Get all the queued handlers
*
* @return array
*/
public static function getAll( ): array {
return array_keys(self::$actions);
}
/**
* Get all the already registered handlers
*
* @return array
*/
public static function getRegistered(): array {
return self::$registered;
}
/**
* Checks if currently a Search Plugin ajax is in progress
*
* @param string $handle
* @return bool
*/
public static function doingAjax(string $handle = "" ): bool {
if (!empty($_POST['action'])) {
if ($handle != "")
return $_POST['action'] == $handle;
return in_array($_POST['action'], self::getAll());
}
return false;
}
/**
* Registers an action with the handler class name.
*
* @param $action
* @param $handler
* @param bool $priv
* @param bool $nopriv
* @param bool $custom_ajax
* @return bool
*/
public static function register($action, $handler, bool $priv = true, bool $nopriv = true, bool $custom_ajax = false): bool {
if ( !$priv && !$nopriv) return false;
$class = self::NAMESPACE . $handler;
$prefix = $custom_ajax == true ? "ASP_" : "wp_ajax_";
if ( !class_exists($class) ) return false;
if ( $priv )
add_action($prefix . $action, array(call_user_func(array($class, 'getInstance')), 'handle'));
if ( $nopriv )
add_action($prefix . 'nopriv_' . $action, array(call_user_func(array($class, 'getInstance')), 'handle'));
self::$registered[] = $action;
return true;
}
/**
* Deregisters an action handler.
*
* @param $action
* @param bool $handler
*/
public static function deregister($action, bool $handler = false ) {
if ( $handler === false ) {
foreach ( self::$actions as $k => $a ) {
if ( $k == $action ) {
remove_action($action, array(call_user_func(array(self::NAMESPACE . $a['handler'], 'getInstance')), 'handle'));
}
}
} else if ( is_string($handler) ) {
remove_action($action, array(call_user_func(array(self::NAMESPACE . $handler, 'getInstance')), 'handle'));
}
}
/**
* Adds an action to the register queue
*
* @param $action
* @param $handler
* @param bool $priv
* @param bool $nopriv
*/
public static function queue($action, $handler, bool $priv = true, bool $nopriv = true) {
self::$actions[$action] = array(
"handler" => $handler,
"priv" => $priv,
"nopriv" => $nopriv
);
}
/**
* Dequeues an action from the register queue.
*
* @param $action
*/
public static function dequeue($action) {
if ( isset(self::$actions[$action]) )
unset(self::$actions[$action]);
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace WPDRMS\ASP\Hooks\Filters;
use WP_Post;
if ( !defined('ABSPATH') ) {
die('-1');
}
/**
* Advanced Custom Fields related Hooks
*/
class ACF extends AbstractFilter {
/**
* @return void
*/
public function handle() {}
/**
* @param array<string, mixed> $values
* @param WP_Post $the_post
* @param string $field
* @return array<string|int, mixed>
*/
public function indexRepeaterAndFlexFields( array $values, WP_Post $the_post, string $field ): array {
if ( function_exists('have_rows') && function_exists( 'the_row') ) {
if ( have_rows($field, $the_post->ID) ) {
while ( have_rows($field, $the_post->ID ) ) {
$row = the_row();
if ( !is_array($row) ) {
continue;
}
foreach ( $row as $sub_field ) {
$values[] = $sub_field;
}
}
}
}
return $values;
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace WPDRMS\ASP\Hooks\Filters;
use WPDRMS\ASP\Patterns\SingletonTrait;
if ( !defined('ABSPATH') ) {
die('-1');
}
abstract class AbstractFilter {
use SingletonTrait;
/**
* The handler
*
* This function is called by the appropriate handler
*/
public function handle() {}
}

View File

@@ -0,0 +1,100 @@
<?php /** @noinspection PhpUnused */
namespace WPDRMS\ASP\Hooks\Filters;
use WPDRMS\ASP\Asset\Manager;
if ( !defined('ABSPATH') ) {
die('-1');
}
class Asset extends AbstractFilter {
/**
* hook: wp_enqueue_scripts
*/
function onPluginFrontendHead() {
Manager::instance()->enqueue();
}
/**
* hook: wp_print_footer_scripts, priority 6
* hook: admin_print_footer_scripts, priority 6
*/
function onPluginFooter() {
Manager::instance()->onPluginFooter();
}
/**
* Classic script enqueue for the plugin backend
*
* hook: admin_print_footer_scripts, priority 7
*/
function onPluginBackendFooter() {
if ( wd_asp()->manager->getContext() == 'backend' ) {
Manager::instance()->onPluginBackendFooter();
}
}
/**
* hook: asp_ob_end
*
* @param $buffer
* @return mixed
*/
function injectToOutputBuffer( $buffer ) {
return Manager::instance()->injectToBuffer($buffer);
}
/**
* Safety check, if the injections were not successful
*
* hook: shutdown (executed after asp_ob_end)
*/
function onShutdown() {
if (
wd_asp()->manager->getContext() == 'frontend' && !wp_is_json_request()
&& !isset($_POST['ags_wc_filters_ajax_shop']) // divi shop live pagination
&& !(
isset($_SERVER['REQUEST_URI']) &&
substr_compare($_SERVER['REQUEST_URI'], '.xml', -strlen('.xml')) === 0
) // Skip for XML document requests
) {
Manager::instance()->printBackup();
}
}
public function applySelectiveAssetLoader( $exit ) {
$comp_settings = wd_asp()->o['asp_compatibility'];
if ( $comp_settings['selective_enabled'] ) {
if ( is_front_page() ) {
if ( $comp_settings['selective_front'] == 0 ) {
$exit = true;
}
} elseif ( is_archive() ) {
if ( $comp_settings['selective_archive'] == 0 ) {
$exit = true;
}
} elseif ( is_singular() ) {
if ( $comp_settings['selective_exin'] != '' ) {
global $post;
if ( isset($post, $post->ID) ) {
$_ids = wpd_comma_separated_to_array($comp_settings['selective_exin']);
if ( !empty($_ids) ) {
if ( $comp_settings['selective_exin_logic'] == 'exclude' && in_array($post->ID, $_ids) ) {
$exit = true;
} elseif ( $comp_settings['selective_exin_logic'] == 'include' && !in_array($post->ID, $_ids) ) {
$exit = true;
}
}
}
}
}
}
return $exit;
}
function handle() {}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace WPDRMS\ASP\Hooks\Filters;
use WPDRMS\ASP\Query\SearchQuery;
use WPDRMS\ASP\Utils\Search;
/**
* GenerateBlocks plugin Query Loop live search and filter compatibility
*/
class BlocksyAdvancedPostsBlock extends AbstractFilter {
/**
* Hooks into: blocksy:general:blocks:query:args
*
* @param array $query
* @param array $attributes
* @return array
*/
public function queryVars( array $query, array $attributes ) {
if ( !isset($_GET['p_asid'], $_GET['unique_id']) ) {
return $query;
}
$query_id = strval($attributes['uniqueId']) ?? '';
if ( $query_id !== '' && strval($_GET['unique_id']) !== $query_id ) {
return $query;
}
$query['asp_override'] = true;
/**
* The $query_args['page'] should not be set as we want the full set
* and the loop will take care of the page.
*/
if ( isset($_GET['asp_force_reset_pagination']) ) {
// For the correct pagination highlight
$query['offset'] = 0;
}
return $query;
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace WPDRMS\ASP\Hooks\Filters;
use WP_Query;
use WPDRMS\ASP\Models\SearchQueryArgs;
use WPDRMS\ASP\Utils\Search;
use WPDRMS\ASP\Utils\WPQueryUtils;
if ( !defined('ABSPATH') ) {
die('-1');
}
/**
* Bricks Query Loop Integration
*/
class BricksQueryLoop extends AbstractFilter {
/**
* Hooks into bricks/posts/query_vars
*/
public function bricksPostsQueryVars( $query_vars, $settings, $element_id, $element_name ) {
$id = Search::overrideSearchId();
if (
$id > 0 &&
isset($settings['_cssClasses']) &&
strpos($settings['_cssClasses'], 'asp_es_' . $id) !== false
) {
if ( isset($_GET['asp_force_reset_pagination']) ) {
// For the correct pagination highlight
$query_vars['paged'] = 1;
}
$query_vars['asp_override'] = true;
}
return $query_vars;
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace WPDRMS\ASP\Hooks\Filters;
use WP_Query;
use WPDRMS\ASP\Models\SearchQueryArgs;
use WPDRMS\ASP\Utils\Search;
if ( !defined('ABSPATH') ) {
die('-1');
}
class Divi extends AbstractFilter {
public function handle() {}
/**
* Handles override for Divi built-in blogs module
*
* @param WP_Query $wp_query
* @param array<string, mixed> $atts
* @return WP_Query
*/
public function blog( WP_Query $wp_query, array $atts = array() ): WP_Query {
$id = Search::overrideSearchId();
if ( !isset($atts['module_class']) || !str_contains('asp_es_' . $id, $atts['module_class']) ) {
return $wp_query;
}
add_filter(
'asp_query_args',
function ( SearchQueryArgs $args ) use ( $wp_query ) {
$args->search_type = array( 'cpt' );
$args->_sd['shortcode_op'] = 'remove';
$args->posts_per_page = $wp_query->query_vars['posts_per_page'] ?? $args->posts_per_page;
return $args;
},
100,
1
);
$wp_query->query_vars['asp_override'] = true;
$wp_query->query_vars['asp_not_archive'] = true;
$wp_query->is_home = false; // This will prevent archive detection
$new_query = SearchOverride::instance()->override(array(), $wp_query, 'wp_query');
return $new_query;
}
/**
* Divi extras blog module
*
* @param array<string, mixed> $args
* @param array<string, mixed> $atts
* @return array<string, mixed>
*/
public function blogExtras( array $args, array $atts = array() ): array {
$id = Search::overrideSearchId();
if ( !isset($atts['module_class']) || !str_contains('asp_es_' . $id, $atts['module_class']) ) {
return $args;
}
$args['asp_override'] = true;
return $args;
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace WPDRMS\ASP\Hooks\Filters;
use WP_Query;
use WPDRMS\ASP\Models\SearchQueryArgs;
use WPDRMS\ASP\Utils\WPQueryUtils;
if ( !defined('ABSPATH') ) {
die('-1');
}
/**
* Advanced Custom Fields related Hooks
*/
class DiviFilterGrid {
private ?WP_Query $wp_query;
public function __construct( ?WP_Query $wp_query ) {
$this->wp_query = $wp_query;
}
/**
* Fixes ordering and other query arguments on Divi Filter Grid
*
* Hook: asp_query_args
*
* @param SearchQueryArgs $args
* @return SearchQueryArgs
*/
public function filterGridQueryArgs( SearchQueryArgs $args ): SearchQueryArgs {
$wp_query = $this->wp_query;
if ( !isset($wp_query, $wp_query->query_vars['dfg_context'], $wp_query->query_vars['asp_override']) ) {
return $args;
}
if ( isset($_POST['module_data']['query_var']['s']) ) {
$args->s = $_POST['module_data']['query_var']['s']; // @phpcs:ignore
}
$args = WPQueryUtils::toASPQueryOrdering($wp_query, $args);
return WPQueryUtils::toASPQueryTaxFilters($wp_query, $args);
}
}

View File

@@ -0,0 +1,145 @@
<?php
namespace WPDRMS\ASP\Hooks\Filters;
use WPDRMS\ASP\Query\SearchQuery;
use WPDRMS\ASP\Utils\Search;
if ( !defined('ABSPATH') ) {
die('-1');
}
class Elementor extends AbstractFilter {
function handle() {}
public function jetListingGridQuery( $args, $obj, $settings ) {
$id = Search::overrideSearchId();
if (
$id > 0 &&
isset($settings['_css_classes']) &&
strpos($settings['_css_classes'], 'asp_es_' . $id) !== false
) {
if ( isset($_GET['asp_force_reset_pagination']) ) {
// For the correct pagination highlight
$args['paged'] = 1;
}
$args['post_type'] = 'asp_override';
}
return $args;
}
/**
* Converts the "query" query type (query builder) to post so the override can apply
*
* @param $source
* @param $settings
* @param $widget
* @return string
*/
public function jetListingGridQueryBuilderToPost( $source, $settings, $widget ) {
$id = Search::overrideSearchId();
if (
$id > 0 &&
isset($settings['_css_classes']) &&
strpos($settings['_css_classes'], 'asp_es_' . $id) !== false
) {
return 'posts';
}
return $source;
}
/**
* Posts and Loop Grid widget Support
*
* @param $args
* @param $widget
* @return array|mixed
*/
public function posts( $args = array(), $widget = array() ) {
$id = Search::overrideSearchId();
$data = $widget->get_data();
if (
$id > 0 &&
isset($data['settings']['_css_classes']) &&
strpos($data['settings']['_css_classes'], 'asp_es_' . $id) !== false
) {
if ( isset($_GET['asp_force_reset_pagination']) ) {
// For the correct pagination highlight
$args['paged'] = 1;
}
$args['post_type'] = 'asp_override';
$args['is_elementor'] = true;
}
return $args;
}
public function posts_archive( $args = array() ) {
$id = Search::overrideSearchId();
if ( $id > 0 ) {
if ( isset($_GET['asp_force_reset_pagination']) ) {
// For the correct pagination highlight
$args['paged'] = 1;
}
$args['post_type'] = is_array($args['post_type']) ? $args['post_type'] : array($args['post_type']);
$args['post_type'][] = 'asp_override';
}
return $args;
}
/** @noinspection PhpUnusedParameterInspection */
public function products( $args = array(), $atts = array(), $type = '' ) {
$id = Search::overrideSearchId();
if ( $id > 0 ) {
$instance = wd_asp()->instances->get($id);
$sd = $instance['data'];
if ( isset($sd['customtypes']) ) {
$post_type = in_array('product_variation', $sd['customtypes']) ? array( 'product', 'product_variation' ) : array( 'product' );
} else {
$post_type = array( 'product' );
}
$ids = array();
$phrase = $_GET['asp_ls'] ?? $_GET['s'];
$search_args = array(
's' => $phrase,
'_ajax_search' => false,
'post_type' => $post_type,
'search_type' => array( 'cpt' ),
// Do not recommend going over that, as the post__in argument will generate a
// too long query to complete, as well as Elementor processes all of these
// results, yielding a terrible loading time.
'posts_per_page' =>500,
);
add_filter('asp_query_args', array( SearchOverride::getInstance(), 'getAdditionalArgs' ));
if ( isset($_GET['asp_force_reset_pagination']) ) {
// For the correct pagination highlight
$search_args['page'] = 1;
}
$options = Search::getOptions();
if ( $options === false || count($options) == 0 ) {
$asp_query = new SearchQuery($search_args, $id);
} else {
$asp_query = new SearchQuery($search_args, $id, $options);
}
foreach ( $asp_query->posts as $r ) {
$ids[] = $r->ID;
}
if ( count($ids) > 0 ) {
$args['post__in'] = $ids;
} else {
$args['post__in'] = array( -1 );
echo Search::generateHTMLResults(array(), false, $id, $phrase, 'elementor');
}
$args['orderby'] = 'post__in';
}
return $args;
}
}

View File

@@ -0,0 +1,219 @@
<?php
/** @noinspection PhpUnusedParameterInspection */
/** @noinspection PhpMissingReturnTypeInspection */
/** @noinspection PhpUnused */
namespace WPDRMS\ASP\Hooks\Filters;
use WPDRMS\ASP\NavMenu\Controller;
use WPDRMS\ASP\Utils\Html;
if ( !defined('ABSPATH') ) {
die('-1');
}
/**
* @phpstan-import-type WP_Menu_Item from Controller
*/
class EtcFixes extends AbstractFilter {
protected static $diviElementsLoaded = false;
function handle() {}
/**
* Fix for the Download Monitor plugin download urls
*
* @param $results
* @return mixed
*/
function plug_DownloadMonitorLink( $results ) {
if ( class_exists('\\DLM_Download') ) {
foreach ( $results as $r ) {
if ( $r->post_type == 'dlm_download' ) {
$dl = new \DLM_Download($r->id);
if ( $dl->exists() ) {
$r->link = $dl->get_the_download_link();
}
}
}
}
return $results;
}
/**
* Executes search shortcodes when placed as menu titles
*
* @deprecated 4.27 This handler will be moved to \WPDRMS\ASP\NavMenu\Controller::handleMenuOutput()
* @see \WPDRMS\ASP\NavMenu\Controller::handleMenuOutput()
* @param WP_Menu_Item[] $menu_items
* @return WP_Menu_Item[]
*/
public function allowShortcodeInMenus( array $menu_items ): array {
foreach ( $menu_items as $menu_item ) {
if (
strpos($menu_item->title, '[wd_asp') !== false ||
strpos($menu_item->title, '[wd_asl') !== false ||
strpos($menu_item->title, '[wpdreams_') !== false
) {
$menu_item->title = do_shortcode($menu_item->title);
$menu_item->url = '';
}
}
return $menu_items;
}
/**
* Adds the 'Standard' post format virtual term to the filter list
*/
function fixPostFormatStandard( $terms, $taxonomy, $args, $needed_all ) {
if (
$taxonomy == 'post_format' && !is_wp_error($terms) &&
( $needed_all || in_array(-200, $args['include_ids']) )
) {
$std_term = new \stdClass();
$std_term->term_id = -200;
$std_term->taxonomy = 'post_format';
$std_term->children = array();
$std_term->name = asp_icl_t('Post format: Standard', 'Standard');
$std_term->label = asp_icl_t('Post format: Standard', 'Standard');
$std_term->parent = 0;
$std_term = apply_filters('asp_post_format_standard', $std_term);
array_unshift($terms, $std_term);
}
return $terms;
}
/**
* Fixes the 'Standard' post format filter back-end
*/
function fixPostFormatStandardArgs( $args ) {
if ( isset($args['post_tax_filter']) && is_array($args['post_tax_filter']) ) {
foreach ( $args['post_tax_filter'] as $v ) {
if ( $v['taxonomy'] == 'post_format' ) {
if ( isset($v['_termset']) && in_array(-200, $v['_termset']) && !in_array(-200, $v['exclude']) ) {
// Case 1: Checkbox, not unselected, but displayed
$v['allow_empty'] = 1;
} elseif ( in_array(-200, $v['exclude']) ) {
// Case 2: 'Standard' unchecked
$v['allow_empty'] = 0;
} elseif ( in_array(-200, $v['include']) && count($v['include']) == 1 ) {
// Case 3: Non-checkbox, and 'Standard' selected.
$v['allow_empty'] = 1;
} elseif ( isset($v['_is_checkbox']) && !$v['_is_checkbox'] && !in_array(-200, $v['include']) ) {
$v['allow_empty'] = 0;
}
}
}
}
return $args;
}
/**
* Fix for the Oxygen builder plugin editor error console
*/
function fixOxygenEditorJS( $exit ) {
if ( isset($_GET['ct_builder']) ) {
return true;
}
return false;
}
/**
* Remove extra line breaks from within HTML tags
*/
function optimizeHTML( $output ) {
return Html::optimize($output);
}
/**
* Fix images on multisite installation results pages with search override
*
* @param $image
* @param $attachment_id
* @param $size
* @param $icon
* @return array
*/
function multisiteImageFix( $image, $attachment_id, $size, $icon ) {
if (
defined('ASP_MULTISITE_IMAGE_FIX') &&
is_multisite() &&
!empty( get_asp_result_field('image') )
) {
return array( get_asp_result_field('image'), 300, 300, true );
} else {
return $image;
}
}
function allow_json_mime_type( $mimes ) {
$mimes['json'] = 'application/json';
return $mimes;
}
function http_request_host_is_external_filter( $allow, $host, $url ) {
return ( false !== strpos( $host, 'wp-dreams' ) ) ? true : $allow;
}
function http_request_args( $args, $url ) {
// If it is a https request, and we are performing a package download, disable ssl verification.
if ( strpos( $url, 'update.wp-dreams.com' ) ) {
$args['sslverify'] = false;
$args['reject_unsafe_urls'] = false;
}
return $args;
}
public function breakdanceFixShortcodeInEditor( $include_styles ) {
return isset($_GET['_breakdance_doing_ajax']) ? 1 : $include_styles;
}
public function diviBuilderReady() {
self::$diviElementsLoaded = true;
}
/**
* Init DIVI builder modules for index table save process on the front-end divi editor screen
*/
function diviInitModules( $content ) {
if ( !self::$diviElementsLoaded && function_exists('et_builder_add_main_elements') ) {
if (
( isset($_POST['action']) && $_POST['action'] == 'et_fb_ajax_save' )
) {
self::$diviElementsLoaded = true;
et_builder_add_main_elements();
}
}
return $content;
}
/**
* Init DIVI builder modules for index table ajax process
*/
function diviInitModulesOnAjax( $actions ) {
$actions[] = 'asp_indextable_admin_ajax';
return $actions;
}
/**
* gTranslate plugin auto-translation trigger by changing the ajax headers conten-type
*
* @param string $content_type
* @return string
*/
public function gTranslateAjaxHeaders( string $content_type ): string {
// GTranslate +
if (
isset($_SERVER['HTTP_X_GT_LANG']) &&
!empty(isset($_SERVER['HTTP_X_GT_LANG'])) &&
wd_asp()->manager->getContext() === 'ajax'
) {
return 'text/html';
}
return $content_type;
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace WPDRMS\ASP\Hooks\Filters;
if (!defined('ABSPATH')) die('-1');
class FormOverride extends AbstractFilter {
public function handle( $form = "" ) {
$asp_st_override = get_option("asp_st_override", -1);
if ( $asp_st_override > -1 && wd_asp()->instances->exists( $asp_st_override ) ) {
$new_form = do_shortcode("[wpdreams_ajaxsearchpro id=".$asp_st_override."]");
if (strlen($new_form) > 100)
return $new_form;
else
return $form;
}
return $form;
}
}

View File

@@ -0,0 +1,128 @@
<?php
namespace WPDRMS\ASP\Hooks\Filters;
use WPDRMS\ASP\Query\SearchQuery;
use WPDRMS\ASP\Utils\Search;
/**
* GenerateBlocks plugin Query Loop live search and filter compatibility
*/
class GenerateBlocksQueryBlock extends AbstractFilter {
/**
* Cache for POST ids for each query block
*
* This is needed because in case of pagination the search query should not execute twice
*
* @var array<int, int[]>
*/
private array $cached_results = array();
/**
* Adds a custom 'isDecorative' attribute to all Image blocks.
*
* @param array $args The block arguments for the registered block type.
* @param string $block_type The block type name, including namespace.
* @return array The modified block arguments.
*/
public function addAttributes( $args, $block_type ) {
if ( $block_type === 'generateblocks/query' ) {
if ( !isset( $args['attributes'] ) ) {
$args['attributes'] = array();
}
if ( !isset( $args['provides_context'] ) ) {
$args['provides_context'] = array();
}
$args['attributes']['asp_override_id'] = array(
'type' => 'integer',
'default' => 0,
);
// To pass the attribute down as context variable
$args['provides_context']['asp_override_id'] = 'asp_override_id';
}
/**
* This will make sure that the $block->context['asp_override_id']
* exists on child blocks within the Query Loop and all pagination blocks.
*/
if ( $block_type === 'generateblocks/looper'
|| str_starts_with($block_type, 'generateblocks/query-page-numbers')
|| str_starts_with($block_type, 'generateblocks/query-no-results')
) {
if ( !isset( $args['uses_context'] ) ) {
$args['uses_context'] = array();
}
$args['uses_context'][] = 'asp_override_id';
}
return $args;
}
/**
* Hooks into: generateblocks_query_wp_query_args
*
* @param array $query
* @param array $attributes
* @return array
*/
public function queryVars( array $query, array $attributes ) {
if ( !isset($_GET['p_asid'], $attributes['asp_override_id']) ) {
return $query;
}
if ( intval($_GET['p_asid']) === intval($attributes['asp_override_id']) ) {
$query_id = intval($attributes['uniqueId']) ?? 0;
if ( $query_id > 0 && isset($this->cached_results[ $query_id ]) ) {
$query['orderby'] = 'post__in';
$query['post__in'] = $this->cached_results[ $query_id ];
return $query;
}
$id = intval($_GET['p_asid']);
$query['asp_override'] = false;
$page = isset($_GET[ "query-$query_id-page" ]) ? intval($_GET[ "query-$query_id-page" ]) : 1;
$phrase = $_GET['asp_ls'] ?? $_GET['s'] ?? $wp_query->query_vars['s'] ?? '';
$ids = array();
$search_args = array(
's' => $phrase,
'_ajax_search' => false,
'search_type' => array( 'cpt' ),
// Do not recommend going over that, as the post__in argument will generate a
// too long query to complete, as well as Elementor processes all of these
// results, yielding a terrible loading time.
'posts_per_page' => 500,
);
add_filter('asp_query_args', array( SearchOverride::getInstance(), 'getAdditionalArgs' ));
/**
* The $query_args['page'] should not be set as we want the full set
* and the loop will take care of the page.
*/
if ( isset($_GET['asp_force_reset_pagination']) ) {
// For the correct pagination highlight
$query['offset'] = 0;
}
$options = Search::getOptions();
if ( $options === false || count($options) === 0 ) {
$asp_query = new SearchQuery($search_args, $id);
} else {
$asp_query = new SearchQuery($search_args, $id, $options);
}
foreach ( $asp_query->posts as $r ) {
$ids[] = $r->ID;
}
if ( count($ids) > 0 ) {
$query['post__in'] = $ids;
$query['orderby'] = 'post__in';
$this->cached_results[ $query_id ] = $query['post__in'];
} else {
$query['post_type'] = '____non_existent';
}
}
return $query;
}
}

View File

@@ -0,0 +1,27 @@
<?php /** @noinspection PhpMissingReturnTypeInspection */
namespace WPDRMS\ASP\Hooks\Filters;
use WPDRMS\ASP\Utils\Polylang\StringTranslations as PolylangStringTranslations;
if (!defined('ABSPATH')) die('-1');
class IclTranslations extends AbstractFilter {
function handle() {}
/**
* Registered to: apply_filters("asp_query_args", $args, $search_id, $options);
* @noinspection PhpUnused
*/
public function aspQueryArgsTranslations($args, $search_id) {
PolylangStringTranslations::init();
if ( $args['_ajax_search'] && isset($args["_sd"]['advtitlefield'] ) ) {
$args["_sd"]['advtitlefield'] = asp_icl_t("Advanced Title Field for Post Type ($search_id)", $args["_sd"]['advtitlefield']);
$args["_sd"]['user_search_advanced_title_field'] = asp_icl_t("Advanced Title Field for Users ($search_id)", $args["_sd"]['user_search_advanced_title_field']);
$args["_sd"]['advdescriptionfield'] = asp_icl_t("Advanced Content Field for Post Type ($search_id)", $args["_sd"]['advdescriptionfield']);
$args["_sd"]['user_search_advanced_description_field'] = asp_icl_t("Advanced Content Field for Users ($search_id)", $args["_sd"]['user_search_advanced_description_field']);
}
PolylangStringTranslations::save();
return $args;
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace WPDRMS\ASP\Hooks\Filters;
class JsCompatibility extends AbstractFilter {
public function handle() {
// TODO: Implement handle() method.
}
/**
* Rocket WP js exclude
*
* @param mixed $handles
* @return mixed
*/
public function rocket_exclude_js( $handles ) {
$rules = array(
'/asp/asp-(.*).js',
'/ajax-search-pro/(.*)/asp-(.*).js',
);
if ( is_null($handles) ) {
return $rules;
}
if ( is_array($handles) ) {
return array_merge($handles, $rules);
}
return $handles;
}
/**
* Rocket WP inline js exclude by content match
*
* @param mixed $handles
* @return mixed
*/
public function rocket_excluded_inline_js_content( $handles ) {
$rules = array(
'ajax-search-pro',
);
if ( is_null($handles) ) {
return $rules;
}
if ( is_array($handles) ) {
return array_merge($handles, $rules);
}
return $handles;
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace WPDRMS\ASP\Hooks\Filters;
if (!defined('ABSPATH')) die('-1');
class MediaScreen extends AbstractFilter {
public function handle( $form_fields = array(), $post = null ) {
$field_value = get_post_meta( $post->ID, '_asp_attachment_text', true );
if ( $field_value !== '' ) {
$form_fields['asp_attachment_text'] = array(
'value' => $field_value,
'label' => __( 'Content (not editable)' ),
'helps' => __( 'Parsed content by Ajax Search Pro Media Parser service' ),
'input' => 'textarea'
);
}
return $form_fields;
}
}

View File

@@ -0,0 +1,117 @@
<?php
namespace WPDRMS\ASP\Hooks\Filters;
use WP_Block;
use WPDRMS\ASP\Query\SearchQuery;
use WPDRMS\ASP\Utils\Search;
class QueryLoopBlock extends AbstractFilter {
/**
* Cache for POST ids for each query block
*
* This is needed because in case of pagination the search query should not execute twice
*
* @var array<int, int[]>
*/
private array $cached_results = array();
/**
* Adds a custom 'isDecorative' attribute to all Image blocks.
*
* @param array $args The block arguments for the registered block type.
* @param string $block_type The block type name, including namespace.
* @return array The modified block arguments.
*/
public function addAttributes( $args, $block_type ) {
if ( $block_type === 'core/query' ) {
if ( !isset( $args['attributes'] ) ) {
$args['attributes'] = array();
}
if ( !isset( $args['provides_context'] ) ) {
$args['provides_context'] = array();
}
$args['attributes']['asp_override_id'] = array(
'type' => 'integer',
'default' => 0,
);
// To pass the attribute down as context variable
$args['provides_context']['asp_override_id'] = 'asp_override_id';
}
/**
* This will make sure that the $block->context['asp_override_id']
* exists on child blocks within the Query Loop and all pagination blocks.
*/
if ( $block_type === 'core/post-template'
|| str_starts_with($block_type, 'core/query-pagination')
|| str_starts_with($block_type, 'core/query-no-results')
) {
if ( !isset( $args['uses_context'] ) ) {
$args['uses_context'] = array();
}
$args['uses_context'][] = 'asp_override_id';
}
return $args;
}
public function queryVars( array $query, WP_Block $block, int $page ) {
if ( !isset($_GET['p_asid'], $block->context['asp_override_id']) ) {
return $query;
}
if ( intval($_GET['p_asid']) === intval($block->context['asp_override_id']) ) {
$query_id = intval($block->context['queryId']) ?? 0;
if ( $query_id > 0 && isset($this->cached_results[ $query_id ]) ) {
$query['orderby'] = 'post__in';
$query['post__in'] = $this->cached_results[ $query_id ];
return $query;
}
$id = intval($_GET['p_asid']);
$query['asp_override'] = false;
$page = isset($_GET[ "query-$query_id-page" ]) ? intval($_GET[ "query-$query_id-page" ]) : 1;
$phrase = $_GET['asp_ls'] ?? $_GET['s'] ?? $wp_query->query_vars['s'] ?? '';
$ids = array();
$search_args = array(
's' => $phrase,
'_ajax_search' => false,
'search_type' => array( 'cpt' ),
// Do not recommend going over that, as the post__in argument will generate a
// too long query to complete, as well as Elementor processes all of these
// results, yielding a terrible loading time.
'posts_per_page' => 1000,
);
add_filter('asp_query_args', array( SearchOverride::getInstance(), 'getAdditionalArgs' ));
/**
* The $query_args['page'] should not be set as we want the full set
* and the loop will take care of the page.
*/
if ( isset($_GET['asp_force_reset_pagination']) ) {
// For the correct pagination highlight
$query['offset'] = 0;
}
$options = Search::getOptions();
if ( $options === false || count($options) == 0 ) {
$asp_query = new SearchQuery($search_args, $id);
} else {
$asp_query = new SearchQuery($search_args, $id, $options);
}
foreach ( $asp_query->posts as $r ) {
$ids[] = $r->ID;
}
if ( count($ids) > 0 ) {
$query['post__in'] = $ids;
$query['orderby'] = 'post__in';
$this->cached_results[ $query_id ] = $query['post__in'];
} else {
$query['post_type'] = '____non_existent';
}
}
return $query;
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace WPDRMS\ASP\Hooks\Filters;
use WPDRMS\ASP\Models\SearchQueryArgs;
if ( !defined('ABSPATH') ) {
die('-1');
}
/**
* Makes Search Exclude plugin exclusions compatible with Ajax Search Lite
*
* @see https://wordpress.org/plugins/search-exclude/
*/
class SearchExclude extends AbstractFilter {
/**
* @param SearchQueryArgs $args
* @return SearchQueryArgs
*/
public function handleExclusions( SearchQueryArgs $args ): SearchQueryArgs {
if ( class_exists('\QuadLayers\QLSE\Models\Settings') ) {
/** @noinspection All */
$excluded = \QuadLayers\QLSE\Models\Settings::instance()->get();
if ( ! isset( $excluded ) ) {
return $args;
}
/**
* Exclude posts by post type
*/
if ( isset( $excluded->entries ) ) {
$post__not_in = array();
foreach ( $excluded->entries as $post_type => $excluded_post_type ) {
$post_type_ids = ! empty( $excluded_post_type['all'] ) ? $this->getAllPostTypeIds( $post_type ) : $excluded_post_type['ids'];
$post__not_in = array_merge( $post__not_in, $post_type_ids );
}
$args->post_not_in = array_unique( array_merge( $args->post_not_in, $post__not_in ) );
}
}
return $args;
}
/**
* @param string $post_type
* @return int[]
*/
private function getAllPostTypeIds( string $post_type ): array {
// @phpstan-ignore-next-line
return get_posts(
array(
'post_type' => $post_type,
'posts_per_page' => -1,
'fields' => 'ids',
)
);
}
}

View File

@@ -0,0 +1,576 @@
<?php
/** @noinspection PhpMissingReturnTypeInspection */
/** @noinspection RegExpSingleCharAlternation */
namespace WPDRMS\ASP\Hooks\Filters;
use WP_Post;
use WP_Query;
use WPDRMS\ASP\Query\SearchQuery;
use WPDRMS\ASP\Utils\Archive;
use WPDRMS\ASP\Utils\Search;
use WPDRMS\ASP\Utils\WooCommerce;
if ( !defined('ABSPATH') ) {
die('-1');
}
class SearchOverride extends AbstractFilter {
public static $overrideCount = 0;
public function handle() {}
/**
* Checks and cancels the original search query made by WordPress, if required
*
* @param string $request - The SQL query
* @param WP_Query $wp_query - The instance of WP_Query() for this query
* @return string
* @noinspection PhpUnused
*/
public function maybeCancelWPQuery( string $request, WP_Query $wp_query ) {
// is_home check is needed for the blog/home page filtering, query reset will break it
if ( $this->checkSearchOverride(true, $wp_query) === true && !$wp_query->is_home ) {
$request = '';
}
return $request;
}
/**
* Overrides the $posts object array with the results from Ajax Search Pro
*
* @param mixed $posts array of posts
* @param null|WP_Query $wp_query The instance of WP_Query() for this query
* @param 'posts'|'wp_query' $return_as to return the $posts or the modified $wp_query
* @return ($return_as is 'posts' ? WP_Post[] : WP_Query)
*/
public function override( $posts, ?WP_Query $wp_query, string $return_as = 'posts' ) {
$check_override = $this->checkSearchOverride(false, $wp_query);
if ( $check_override === false ) {
return $posts;
} else {
$_p_id = $check_override[0];
$s_data = $check_override[1];
}
// The get_query_var() is malfunctioning in some cases!!! use $_GET['paged']
// $paged = (get_query_var('paged') != 0) ? get_query_var('paged') : 1;
if ( isset($_GET['asp_force_reset_pagination']) ) {
// For the correct pagination highlight
$_GET['paged'] = 1;
$paged = 1;
set_query_var('paged', 1);
set_query_var('page', 1);
} else {
$paged = $_GET['paged'] ?? $wp_query->query_vars['paged'] ?? 1;
}
if ( !wd_asp()->instances->exists($_p_id) ) {
return $posts;
}
$instance = wd_asp()->instances->get($_p_id);
$sd = $instance['data'];
// First check the asp_ls, as s might be set already
$phrase = $_GET['asp_ls'] ?? $_GET['s'] ?? $wp_query->query_vars['s'] ?? '';
$paged = $paged <= 0 ? 1 : $paged;
// Elementor related
if (
isset($wp_query->query_vars, $wp_query->query_vars['posts_per_page']) &&
$this->queryVarsHasAspOverridePostType($wp_query)
) {
$posts_per_page = $wp_query->query_vars['posts_per_page'];
} else {
$posts_per_page = $sd['results_per_page'];
}
if ( $posts_per_page === 'auto' ) {
if ( isset($wp_query->query_vars, $wp_query->query_vars['posts_per_page']) ) {
$posts_per_page = $wp_query->query_vars['posts_per_page'];
} else {
$posts_per_page = get_option( 'posts_per_page' );
}
}
$posts_per_page = intval($posts_per_page);
$posts_per_page = $posts_per_page === 0 ? 1 : $posts_per_page;
$s_data = apply_filters('asp_search_override_data', $s_data, $posts, $wp_query, $_p_id, $phrase);
// A possible exit point for the user, if he sets the _abort argument
if ( isset($s_data['_abort']) ) {
return $posts;
}
$args = array(
's' => $phrase,
'_ajax_search' => false,
'posts_per_page' => $posts_per_page,
'page' => $paged,
);
// $args = self::getAdditionalArgs($args);
add_filter('asp_query_args', array( $this, 'getAdditionalArgs' ));
add_filter('asp_query_args', array( new DiviFilterGrid($wp_query), 'filterGridQueryArgs' ), 99, 1);
if ( count($s_data) === 0 ) {
$asp_query = new SearchQuery($args, $_p_id);
} else {
$asp_query = new SearchQuery($args, $_p_id, $s_data);
}
$res = $asp_query->posts;
if ( $sd['result_suggestions_results_page'] && count($res) === 0 ) {
$asp_query->resultsSuggestions();
$res = $asp_query->posts;
}
do_action('asp_after_search', $phrase, $res, $_p_id);
// Elementor Posts widget no results text
if (
count($res) === 0 &&
isset($wp_query->query_vars, $wp_query->query_vars['is_elementor'])
) {
echo Search::generateHTMLResults(array(), false, $_p_id, $phrase, 'elementor');
}
$wp_query->query_vars['post__in'] = $wp_query->query_vars['post__in'] ?? array();
if ( is_array($wp_query->query_vars['post__in']) ) {
$wp_query->query_vars['post__in'] = array_unique(
array_merge(
$wp_query->query_vars['post__in'],
array_map(
function ( $aspr ) {
return $aspr->id;
},
$asp_query->all_results
)
)
);
$wp_query->query_vars['orderby'] = 'post__in';
}
if ( isset($wp_query->posts) ) {
$wp_query->posts = $res;
}
$wp_query->post_count = count($res);
/**
* Reset post type
* Very important. Some archive posts modules fail because asp_override is non-existent.
*/
if ( empty($wp_query->query_vars['post_type']) || $this->queryVarsHasAspOverridePostType( $wp_query ) ) {
$wp_query->query_vars['post_type'] = $asp_query->args->post_type;
}
$wp_query->found_posts = $asp_query->found_posts;
if ( ( $wp_query->found_posts / $posts_per_page ) > 1 ) {
$wp_query->max_num_pages = intval(ceil($wp_query->found_posts / $posts_per_page));
} else {
$wp_query->max_num_pages = 1;
}
$res = apply_filters('asp/search/override/results', $res, $posts, $wp_query, $asp_query);
$wp_query = apply_filters('asp/search/override/wp_query', $wp_query, $res, $posts, $asp_query);
++self::$overrideCount;
if ( $return_as === 'posts' ) {
return $res;
} else {
return $wp_query;
}
}
/**
* Checks and gets additional arguments for the override query
*
* @param $args - query arguments for the SearchQuery()
*/
public static function getAdditionalArgs( $args ) {
global $wpdb, $wp_query;
// WooCommerce price filter
if ( isset($_GET['min_price'], $_GET['max_price']) ) {
$args['post_meta_filter'][] = array(
'key' => '_price', // meta key
'value' => array( floatval($_GET['min_price']), floatval($_GET['max_price']) ),
'operator' => 'BETWEEN',
);
}
// WooCommerce or other custom Ordering
if ( isset($_GET['orderby']) || isset($_GET['product_orderby']) ) {
$o_by = $_GET['orderby'] ?? $_GET['product_orderby'];
$o_by = str_replace(' ', '', ( strtolower($o_by) ));
if ( isset($_GET['order']) || isset($_GET['product_order']) ) {
$o_way = $_GET['order'] ?? $_GET['product_order'];
} elseif ( $o_by == 'price' || $o_by == 'product_price' ) {
$o_way = 'ASC';
} elseif ( $o_by == 'alphabetical' ) {
$o_way = 'ASC';
} else {
$o_way = 'DESC';
}
$o_way = strtoupper($o_way);
if ( $o_way != 'DESC' && $o_way != 'ASC' ) {
$o_way = 'DESC';
}
switch ( $o_by ) {
case 'id':
case 'post_id':
case 'product_id':
$args['post_primary_order'] = "id $o_way";
break;
case 'popularity':
case 'post_popularity':
case 'product_popularity':
$args['post_primary_order'] = "customfp $o_way";
$args['post_primary_order_metatype'] = 'numeric';
$args['_post_primary_order_metakey'] = 'total_sales';
break;
case 'rating':
case 'post_rating':
case 'product_rating':
// Custom query args here
$args['cpt_query']['fields'] = "(
SELECT
IF(AVG( $wpdb->commentmeta.meta_value ) IS NULL, 0, AVG( $wpdb->commentmeta.meta_value ))
FROM
$wpdb->comments
LEFT JOIN $wpdb->commentmeta ON($wpdb->comments.comment_ID = $wpdb->commentmeta.comment_id)
WHERE
$wpdb->posts.ID = $wpdb->comments.comment_post_ID
AND ( $wpdb->commentmeta.meta_key = 'rating' OR $wpdb->commentmeta.meta_key IS null )
) as average_rating, ";
$args['cpt_query']['orderby'] = "average_rating $o_way, ";
// Force different field order for index table
$args['post_primary_order'] = "average_rating $o_way";
break;
case 'date':
case 'post_date':
case 'product_date':
$args['post_primary_order'] = "post_date $o_way";
break;
case 'name':
case 'post_name':
case 'product_name':
case 'alphabetical':
case 'reverse_alpha':
case 'reverse_alphabetical':
$args['post_primary_order'] = "post_title $o_way";
break;
case 'price':
case 'product_price':
case 'price-desc':
$args['post_primary_order'] = "customfp $o_way";
$args['post_primary_order_metatype'] = 'numeric';
$args['_post_primary_order_metakey'] = '_price';
break;
case 'relevance':
$args['post_primary_order'] = "relevance $o_way";
break;
}
}
if ( isset($_GET['post_type']) && $_GET['post_type'] == 'product' ) {
$args['search_type'] = array( 'cpt' );
$old_ptype = $args['post_type'];
$args['post_type'] = array();
if ( in_array('product', $old_ptype) ) {
$args['post_type'][] = 'product';
}
if ( in_array('product_variation', $old_ptype) ) {
$args['post_type'][] = 'product_variation';
}
// Exclude from search products
$visibility_term = get_term_by('slug', 'exclude-from-search', 'product_visibility');
if ( $visibility_term !== false ) {
$found = false;
if ( isset($args['post_tax_filter']) ) {
foreach ( $args['post_tax_filter'] as &$filter ) {
if ( $filter['taxonomy'] == 'product_visibility' ) {
$filter['exclude'] = $filter['exclude'] ?? array();
$filter['exclude'] = array_merge($filter['exclude'], array( $visibility_term->term_id ));
$found = true;
break;
}
}
}
if ( !$found ) {
$args['post_tax_filter'][] = array(
'taxonomy' => 'product_visibility',
'include' => array(),
'exclude' => array( $visibility_term->term_id ),
'allow_empty' => true,
);
}
}
}
// Archive pages
if ( isset($_GET['asp_ls']) && !isset($wp_query->query_vars['asp_not_archive']) ) {
if ( WooCommerce::isShop() ) {
$args['search_type'] = array( 'cpt' );
// Only allow product or product variations if selected
$args['post_type'] = in_array('product_variation', $args['post_type']) ? array( 'product', 'product_variation' ) : array( 'product' );
// Exclude from catalog products
$visibility_term = get_term_by('slug', 'exclude-from-search', 'product_visibility');
$catalog_term = get_term_by('slug', 'exclude-from-catalog', 'product_visibility');
if ( $visibility_term !== false && $catalog_term !== false ) {
$found = false;
if ( isset($args['post_tax_filter']) ) {
foreach ( $args['post_tax_filter'] as &$filter ) {
if ( $filter['taxonomy'] == 'product_visibility' ) {
$filter['exclude'] = $filter['exclude'] ?? array();
$filter['exclude'] = array_merge(
$filter['exclude'],
array(
$visibility_term->term_id,
$catalog_term->term_id,
)
);
$found = true;
break;
}
}
}
if ( !$found ) {
$args['post_tax_filter'][] = array(
'taxonomy' => 'product_visibility',
'include' => array(),
'exclude' => array(
$visibility_term->term_id,
$catalog_term->term_id,
),
'allow_empty' => true,
);
}
}
} elseif ( Archive::isTaxonomyArchive() ) {
$args['search_type'] = array( 'cpt' );
$found = false;
$children = get_term_children(get_queried_object()->term_id, get_queried_object()->taxonomy);
$include = !is_wp_error($children) ? array_merge(array( get_queried_object()->term_id ), $children) : array( get_queried_object()->term_id );
if ( isset($args['post_tax_filter']) ) {
foreach ( $args['post_tax_filter'] as &$filter ) {
if ( $filter['taxonomy'] == get_queried_object()->taxonomy ) {
$filter['include'] = array_diff($include, $filter['exclude']);
$found = true;
break;
}
}
}
if ( !$found ) {
$args['post_tax_filter'][] = array(
'taxonomy' => get_queried_object()->taxonomy,
'include' => $include,
'exclude' => array(),
'allow_empty' => true,
);
}
} elseif ( Archive::isPostTypeArchive() ) {
$args['search_type'] = array( 'cpt' );
$post_type = $wp_query->get( 'post_type' );
$args['post_type'] = $post_type == '' ? array( 'post' ) : array( $post_type );
}
}
return $args;
}
/**
* Checks if the default WordPress search query is executed right now, and if it needs an override.
* Also sets some cookie and request variables, if needed.
*
* @param bool $check_only - when true, only checks if the override should be initiated, no variable changes
* @param null|WP_Query $wp_query - The instance of WP_Query() for this query
* @return array|bool
*/
public function checkSearchOverride( bool $check_only, ?WP_Query $wp_query ) {
// Check the search query
if ( !$this->isSearch($wp_query) ) {
return false;
}
// If you get method is used, then the cookies are not present or not used
if ( isset($_GET['p_asp_data']) ) {
if ( $check_only ) {
return true;
}
$_p_id = $_GET['p_asid'] ?? $_GET['np_asid'];
if ( $_GET['p_asp_data'] == 1 ) {
$s_data = $_GET;
} else {
// Legacy support
parse_str(base64_decode($_GET['p_asp_data']), $s_data);
}
} elseif (
isset($_GET['s'], $_COOKIE['asp_data']) && (
( $_GET['s'] != '' && isset($_COOKIE['asp_phrase']) && $_COOKIE['asp_phrase'] == $_GET['s'] ) ||
( $_GET['s'] == '' )
)
) {
if ( $check_only ) {
return true;
}
parse_str($_COOKIE['asp_data'], $s_data);
$_POST['np_asp_data'] = $_COOKIE['asp_data'];
$_POST['np_asid'] = $_COOKIE['asp_id'];
$_p_id = $_COOKIE['asp_id'];
} elseif (
isset($wp_query, $wp_query->query_vars['asp_id'])
) {
if ( $check_only ) {
return true;
}
$_p_id = $wp_query->query_vars['asp_id'];
$s_data = array();
} else {
// Probably the search results page visited via URL, not triggered via search bar
if ( isset($_GET['post_type']) && $_GET['post_type'] == 'product' ) {
$override_id = get_option('asp_woo_override', -1);
} else {
$override_id = get_option('asp_st_override', -1);
}
if ( $override_id > -1 && wd_asp()->instances->exists( $override_id ) ) {
$inst = wd_asp()->instances->get( $override_id );
if ( $inst['data']['override_default_results'] == 1 ) {
return array( $override_id, array() );
}
}
// Something is not right
return false;
}
return array( $_p_id, $s_data );
}
public function isSearch( ?WP_Query $wp_query ): bool {
$is_search = true;
$soft_check =
defined('ELEMENTOR_VERSION') || // Elementor
defined( 'ET_CORE' ) || // Divi
wd_asp()->o['asp_compatibility']['query_soft_check'];
// This can't be a search query if none of this is set
if ( !isset($wp_query, $wp_query->query_vars, $_GET['s']) ) {
$is_search = false;
} else {
// Possible candidates for search below
if ( $soft_check ) {
// In soft check mode, it does not have to be the main query
if ( !$wp_query->is_search() ) {
$is_search = false;
}
} elseif ( !$wp_query->is_search() || !$wp_query->is_main_query() ) {
$is_search = false;
}
if ( !$is_search && isset($wp_query->query_vars['aps_title']) ) {
$is_search = true;
}
}
// GEO directory search, do not override
if ( $is_search && isset($_GET['geodir_search']) ) {
$is_search = false;
}
// Elementor or other forced override
// Only allow override once in this case, otherwise duplicates will appear
// @ticket: https://wp-dreams.com/forums/topic/filtered-results-show-duplicate-posts/
if ( $this->queryVarsHasAspOverridePostType($wp_query) && self::$overrideCount === 0 ) {
$is_search = true;
}
// Archive pages
if ( isset($_GET['asp_ls'], $_GET['p_asid']) && $wp_query->is_main_query() ) {
if (
( wd_asp()->instances->getOption($_GET['p_asid'], 'woo_shop_live_search') && WooCommerce::isShop() ) ||
( wd_asp()->instances->getOption($_GET['p_asid'], 'cpt_archive_live_search') && Archive::isPostTypeArchive() ) ||
( wd_asp()->instances->getOption($_GET['p_asid'], 'taxonomy_archive_live_search') && Archive::isTaxonomyArchive() ) ) {
$is_search = true;
}
}
// Is this the admin area?
if ( $is_search && is_admin() ) {
$is_search = false;
}
// Forced non-override
if ( isset($wp_query->query_vars['asp_override']) ) {
$is_search = (bool) $wp_query->query_vars['asp_override'];
}
// Possibility to add exceptions
return (bool) apply_filters('asp_query_is_search', $is_search, $wp_query);
}
/**
* Fixes the non-live result URLs for generic themes
*
* @param $url
* @param $post
* @return mixed
* @noinspection PhpUnused
*/
public function fixUrls( $url, $post ) {
if ( isset($post->asp_data, $post->asp_data->link) ) {
return $post->asp_data->link;
} elseif ( isset($post->asp_guid) ) {
return $post->asp_guid;
}
return $url;
}
/**
* Fixes the URLs of the non-live search results, when using the Genesis Framework
*
* @param $output
* @param $wrap
* @param $title
* @return mixed
* @noinspection PhpUnused
*/
public function fixUrlsGenesis( $output, $wrap, $title ) {
global $post;
if ( isset($post, $post->asp_guid) && is_object($post) && function_exists('genesis_markup') ) {
$pattern = "/(?<=href=(\"|'))[^\"']+(?=(\"|'))/";
$title = preg_replace($pattern, $post->asp_guid, $title);
$output = genesis_markup(
array(
'open' => "<{$wrap} %s>",
'close' => "</{$wrap}>",
'content' => $title,
'context' => 'entry-title',
'params' => array(
'wrap' => $wrap,
),
'echo' => false,
)
);
}
return $output;
}
private function queryVarsHasAspOverridePostType( ?WP_Query $wp_query ): bool {
if ( $wp_query === null || !isset($wp_query->query_vars['post_type']) ) {
return false;
}
if ( is_array($wp_query->query_vars['post_type']) ) {
return in_array('asp_override', $wp_query->query_vars['post_type']);
} else {
return $wp_query->query_vars['post_type'] === 'asp_override';
}
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace WPDRMS\ASP\Hooks\Filters;
if (!defined('ABSPATH')) die('-1');
class WooFormOverride extends AbstractFilter {
public function handle( $form = "" ) {
$asp_woo_override = get_option("asp_woo_override", -1);
if ( $asp_woo_override > -1 && wd_asp()->instances->exists( $asp_woo_override ) ) {
$new_form = do_shortcode("[wpdreams_ajaxsearchpro id=".$asp_woo_override."]");
if (strlen($new_form) > 100)
return $new_form;
else
return $form;
}
return $form;
}
}

View File

@@ -0,0 +1,434 @@
<?php
namespace WPDRMS\ASP\Hooks;
if ( !defined('ABSPATH') ) {
die('-1');
}
class FiltersManager {
const NAMESPACE = 'WPDRMS\\ASP\\Hooks\\Filters\\';
/**
* Array of internal known filters
*
* @var array
*/
private static $filters = array(
array(
'filter' => 'posts_request',
'handler' => array( 'SearchOverride', 'maybeCancelWPQuery' ),
'priority' => 999999999,
'args' => 2,
),
array(
'filter' => 'posts_results',
'handler' => array( 'SearchOverride', 'override' ),
'priority' => 999999999,
'args' => 2,
),
array(
'filter' => 'page_link',
'handler' => array( 'SearchOverride', 'fixUrls' ),
'priority' => 999999999,
'args' => 2,
),
array(
'filter' => 'post_link',
'handler' => array( 'SearchOverride', 'fixUrls' ),
'priority' => 999999999,
'args' => 2,
),
array(
'filter' => 'post_type_link',
'handler' => array( 'SearchOverride', 'fixUrls' ),
'priority' => 999999999,
'args' => 2,
),
array(
'filter' => 'elementor/query/query_args',
'handler' => array( 'Elementor', 'posts' ),
'priority' => 999,
'args' => 2,
),
array(
'filter' => 'elementor/theme/posts_archive/query_posts/query_vars',
'handler' => array( 'Elementor', 'posts_archive' ),
'priority' => 999,
'args' => 1,
),
array(
'filter' => 'woocommerce_shortcode_products_query',
'handler' => array( 'Elementor', 'products' ),
'priority' => 999,
'args' => 3,
),
array(
'filter' => 'jet-engine/listing/grid/posts-query-args',
'handler' => array( 'Elementor', 'jetListingGridQuery' ),
'priority' => 0,
'args' => 3,
),
array(
'filter' => 'jet-engine/listing/grid/source',
'handler' => array( 'Elementor', 'jetListingGridQueryBuilderToPost' ),
'priority' => 99999,
'args' => 3,
),
array(
'filter' => 'register_block_type_args',
'handler' => array( 'QueryLoopBlock', 'addAttributes' ),
'priority' => 10,
'args' => 2,
),
array(
'filter' => 'query_loop_block_query_vars',
'handler' => array( 'QueryLoopBlock', 'queryVars' ),
'priority' => 99,
'args' => 3,
),
array(
'filter' => 'register_block_type_args',
'handler' => array( 'GenerateBlocksQueryBlock', 'addAttributes' ),
'priority' => 10,
'args' => 2,
),
array(
'filter' => 'generateblocks_query_wp_query_args',
'handler' => array( 'GenerateBlocksQueryBlock', 'queryVars' ),
'priority' => 10,
'args' => 2,
),
array(
'filter' => 'blocksy:general:blocks:query:args',
'handler' => array( 'BlocksyAdvancedPostsBlock', 'queryVars' ),
'priority' => 10,
'args' => 2,
),
array(
'filter' => 'bricks/posts/query_vars',
'handler' => array( 'BricksQueryLoop', 'bricksPostsQueryVars' ),
'priority' => 10,
'args' => 4,
),
array(
'filter' => 'wp_enqueue_scripts',
'handler' => array( 'Asset', 'onPluginFrontendHead' ),
'priority' => 10,
'args' => 0,
),
array(
'filter' => 'wp_print_footer_scripts',
'handler' => array( 'Asset', 'onPluginFooter' ),
'priority' => 6,
'args' => 0,
),
array(
'filter' => 'admin_print_footer_scripts',
'handler' => array( 'Asset', 'onPluginFooter' ),
'priority' => 6,
'args' => 0,
),
array(
'filter' => 'admin_print_footer_scripts',
'handler' => array( 'Asset', 'onPluginBackendFooter' ),
'priority' => 7,
'args' => 0,
),
array(
'filter' => 'asp_ob_end',
'handler' => array( 'Asset', 'injectToOutputBuffer' ),
'priority' => -101,
'args' => 1,
),
array(
'filter' => 'shutdown',
'handler' => array( 'Asset', 'onShutdown' ),
'priority' => -100,
'args' => 0,
),
array(
'filter' => 'asp_load_css_js',
'handler' => array( 'Asset', 'applySelectiveAssetLoader' ),
'priority' => 10,
'args' => 1,
),
/* GENESIS REPLACEMENT FOR MULTISITE */
array(
'filter' => 'genesis_post_title_output',
'handler' => array( 'SearchOverride', 'fixUrlsGenesis' ),
'priority' => 999999999,
'args' => 3,
),
/* ALLOW SHORTCODE AS MENU TITLE */
array(
'filter' => 'wp_nav_menu_objects',
'handler' => array( 'EtcFixes', 'allowShortcodeInMenus' ),
'priority' => 10,
'args' => 1,
),
array(
'filter' => 'asp_theme_search_form',
'handler' => 'FormOverride',
'priority' => 999999999,
'args' => 1,
),
array(
'filter' => 'get_search_form',
'handler' => 'FormOverride',
'priority' => 999999999,
'args' => 1,
),
array(
'filter' => 'get_product_search_form',
'handler' => 'WooFormOverride',
'priority' => 999999999,
'args' => 1,
),
array(
'filter' => 'asp_query_args',
'handler' => array( 'SearchExclude', 'handleExclusions' ),
'priority' => 20,
'args' => 1,
),
array(
'filter' => 'asp_results',
'handler' => array( 'EtcFixes', 'plug_DownloadMonitorLink' ),
'priority' => 999999999,
'args' => 1,
),
array(
'filter' => 'asp_fontend_get_taxonomy_terms',
'handler' => array( 'EtcFixes', 'fixPostFormatStandard' ),
'priority' => 999,
'args' => 4,
),
array(
'filter' => 'asp_query_args',
'handler' => array( 'EtcFixes', 'fixPostFormatStandardArgs' ),
'priority' => 999,
'args' => 1,
),
array(
'filter' => 'asp_query_args',
'handler' => array( 'IclTranslations', 'aspQueryArgsTranslations' ),
'priority' => 999,
'args' => 2,
),
array(
'filter' => 'asp_load_js',
'handler' => array( 'EtcFixes', 'fixOxygenEditorJS' ),
'priority' => 999,
'args' => 1,
),
array(
'filter' => 'wp_get_attachment_image_src',
'handler' => array( 'EtcFixes', 'multisiteImageFix' ),
'priority' => 999,
'args' => 4,
),
array(
'filter' => 'upload_mimes',
'handler' => array( 'EtcFixes', 'allow_json_mime_type' ),
'priority' => 999,
'args' => 1,
),
array(
'filter' => 'http_request_host_is_external',
'handler' => array( 'EtcFixes', 'http_request_host_is_external_filter' ),
'priority' => 9999,
'args' => 3,
),
array(
'filter' => 'http_request_args',
'handler' => array( 'EtcFixes', 'http_request_args' ),
'priority' => 9999,
'args' => 2,
),
array(
'filter' => 'asp/shortcode/include_styles',
'handler' => array( 'EtcFixes', 'breakdanceFixShortcodeInEditor' ),
'priority' => 10,
'args' => 1,
),
array(
'filter' => 'asp_post_content_before_tokenize_clear',
'handler' => array( 'EtcFixes', 'diviInitModules' ),
'priority' => 9999,
'args' => 1,
),
array(
'filter' => 'asp/ajax/headers/content_type',
'handler' => array( 'EtcFixes', 'gTranslateAjaxHeaders' ),
'priority' => 10,
'args' => 1,
),
array(
'filter' => 'attachment_fields_to_edit',
'handler' => 'MediaScreen',
'priority' => 9999,
'args' => 2,
),
array(
'filter' => 'et_builder_blog_query',
'handler' => array( 'Divi', 'blog' ),
'priority' => 9999,
'args' => 2,
),
array(
'filter' => 'et_builder_load_actions',
'handler' => array( 'EtcFixes', 'diviInitModulesOnAjax' ),
'priority' => 9999,
'args' => 1,
),
array(
'filter' => 'divi_blog_extras_query_args',
'handler' => array( 'Divi', 'blogExtras' ),
'priority' => 9999,
'args' => 2,
),
array(
'filter' => 'asp_post_custom_field_before_tokenize',
'handler' => array( 'ACF', 'indexRepeaterAndFlexFields' ),
'priority' => 10,
'args' => 3,
),
array(
'filter' => 'et_builder_ready',
'handler' => array( 'EtcFixes', 'diviBuilderReady' ),
'priority' => 9999,
'args' => 0,
),
/**
* Optimizes the HTML output by removing line breaks.
* This prevents some wpautop applied paragraphs and line breaks from appearing
* when the shortcode is used in a shortcode block.
*/
array(
'filter' => 'asp_shortcode_output',
'handler' => array( 'EtcFixes', 'optimizeHTML' ),
'priority' => 9999,
'args' => 1,
),
array(
'filter' => 'rocket_exclude_js',
'handler' => array( 'JsCompatibility', 'rocket_exclude_js' ),
'priority' => 9999,
'args' => 1,
),
array(
'filter' => 'rocket_excluded_inline_js_content',
'handler' => array( 'JsCompatibility', 'rocket_excluded_inline_js_content' ),
'priority' => 9999,
'args' => 1,
),
);
/**
* Array of already registered objects
*
* @var array
*/
private static $registered = array();
/**
* Registers all the handlers from the $actions variable
*/
public static function registerAll() {
foreach ( self::$filters as $data ) {
self::register($data['filter'], $data['handler'], $data['priority'], $data['args']);
}
}
/**
* Get all the queued handlers
*
* @return array
*/
public static function getAll(): array {
return array_keys(self::$filters);
}
/**
* Get all the already registered handlers (singleton instance storage)
*
* @return array
*/
public static function getRegistered(): array {
return self::$registered;
}
/**
* Registers a filter with the handler class name.
*
* @param $filter
* @param $handler string|array
* @param int $priority
* @param int $accepted_args
* @return bool
*/
public static function register( $filter, $handler, int $priority = 10, int $accepted_args = 0 ): bool {
if ( is_array($handler) ) {
$class = self::NAMESPACE . $handler[0];
$handle = $handler[1];
} else {
$class = self::NAMESPACE . $handler;
$handle = 'handle';
}
if ( !class_exists($class) ) {
return false;
}
if ( !isset(self::$registered[ $class ]) ) {
self::$registered[ $class ] = call_user_func(array( $class, 'getInstance' ));
}
if ( !has_filter($filter, array( self::$registered[ $class ], $handle )) ) {
add_filter($filter, array( self::$registered[ $class ], $handle ), $priority, $accepted_args);
}
return true;
}
/**
* Deregisters an action handler.
*
* @param $filter
* @param $handler
*/
public static function deregister( $filter, $handler ) {
if ( is_array($handler) ) {
$class = self::NAMESPACE . $handler[0];
$handle = $handler[1];
} else {
$class = self::NAMESPACE . $handler;
$handle = 'handle';
}
if ( isset(self::$registered[ $class ]) ) {
// Deregister via custom method, as WordPress sometimes does not recognize object->method filters
self::remove_object_filter($filter, $class, $handle);
}
}
private static function remove_object_filter( $filter_name, $class_name, $function_name ) {
global $wp_filter;
foreach ( $wp_filter[ $filter_name ]->callbacks as $priority => $pri_data ) {
foreach ( $pri_data as $cb => $cb_data ) {
if (
is_array($cb_data['function']) &&
isset($cb_data['function'][0], $cb_data['function'][1])
&& get_class($cb_data['function'][0]) == $class_name &&
$cb_data['function'][1] == $function_name
) {
unset($wp_filter[ $filter_name ]->callbacks[ $priority ][ $cb ]);
}
}
}
}
}

View File

@@ -0,0 +1,96 @@
<?php
namespace WPDRMS\ASP\Index;
defined('ABSPATH') or die("You can't access this file directly.");
if ( !class_exists(__NAMESPACE__ . '\Content') ) {
class Content {
public static function mySQLFixes( $str ) {
return str_replace(array(
// SELECT œ LIKE oe => FALSE but SELECT œ = oe => TRUE
// When doing INSERT with "oe" or "œ" in keyword, it is ignored as it is considered already existing
'œ'
),array(
'oe'
), $str);
}
public static function arabicRemoveDiacritics( $str ) {
if ( is_array($str) ) {
foreach ($str as &$v) {
$v = self::arabicRemoveDiacritics($v);
}
return $str;
}
$characters = array(
"~[\x{0600}-\x{061F}]~u",
"~[\x{063B}-\x{063F}]~u",
"~[\x{064B}-\x{065E}]~u",
"~[\x{066A}-\x{06FF}]~u",
);
return preg_replace($characters, "", $str);
}
public static function hebrewUnvocalize( $str ) {
if ( is_array($str) ) {
foreach ($str as &$v) {
$v = self::hebrewUnvocalize($v);
}
return $str;
}
if ( preg_match("/[\x{0591}-\x{05F4}]/u", $str) ) {
$hebrew_common_ligatures = array(
'ײַ' => 'ײ',
'ﬠ' => 'ע',
'ﬡ' => 'א',
'ﬢ' => 'ד',
'ﬣ' => 'ה',
'ﬤ' => 'כ',
'ﬥ' => 'ל',
'ﬦ' => 'ם',
'ﬧ' => 'ר',
'ﬨ' => 'ת',
'שׁ' => 'ש',
'שׂ' => 'ש',
'שּׁ' => 'ש',
'שּׂ' => 'ש',
'אַ' => 'א',
'אָ' => 'א',
'אּ' => 'א',
'בּ' => 'ב',
'גּ' => 'ג',
'דּ' => 'ד',
'הּ' => 'ה',
'וּ' => 'ו',
'זּ' => 'ז',
'טּ' => 'ט',
'יּ' => 'י',
'ךּ' => 'ך',
'כּ' => 'כ',
'לּ' => 'ל',
'מּ' => 'מ',
'נּ' => 'נ',
'סּ' => 'ס',
'ףּ' => 'ף',
'פּ' => 'פ',
'צּ' => 'צ',
'קּ' => 'ק',
'רּ' => 'ר',
'שּ' => 'ש',
'תּ' => 'ת',
'וֹ' => 'ו',
'בֿ' => 'ב',
'כֿ' => 'כ',
'פֿ' => 'פ',
'ﭏ' => 'אל'
);
$str = trim(preg_replace('/\p{Mn}/u', '', $str));
foreach ($hebrew_common_ligatures as $word1 => $word2) {
$str = trim(str_replace($word1, $word2, $str));
}
}
return $str;
}
}
}

View File

@@ -0,0 +1,563 @@
<?php
namespace WPDRMS\ASP\Index;
use WPDRMS\ASP\Utils\Plugin;
use WPDRMS\ASP\Utils\Str;
defined('ABSPATH') or die("You can't access this file directly.");
if ( !class_exists(__NAMESPACE__ . '\Database') ) {
class Database {
private $table_name;
function __construct() {
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
$this->table_name = wd_asp()->db->table('index');
}
function create(): array {
global $wpdb;
$return = array();
$charset_collate = '';
if ( ! empty( $wpdb->charset ) ) {
$charset_collate_bin_column = "CHARACTER SET $wpdb->charset";
$charset_collate = "DEFAULT $charset_collate_bin_column";
}
if ( strpos( $wpdb->collate, '_' ) > 0 ) {
$charset_collate .= " COLLATE $wpdb->collate";
}
$query = '
CREATE TABLE IF NOT EXISTS ' . $this->table_name . " (
doc bigint(20) UNSIGNED NOT NULL DEFAULT '0',
term varchar(150) NOT NULL DEFAULT '0',
term_reverse varchar(150) NOT NULL DEFAULT '0',
blogid mediumint(9) UNSIGNED NOT NULL DEFAULT '0',
content smallint(9) UNSIGNED NOT NULL DEFAULT '0',
title tinyint(3) UNSIGNED NOT NULL DEFAULT '0',
comment tinyint(3) UNSIGNED NOT NULL DEFAULT '0',
tag tinyint(3) UNSIGNED NOT NULL DEFAULT '0',
link tinyint(3) UNSIGNED NOT NULL DEFAULT '0',
author tinyint(3) UNSIGNED NOT NULL DEFAULT '0',
excerpt tinyint(3) UNSIGNED NOT NULL DEFAULT '0',
customfield smallint(9) UNSIGNED NOT NULL DEFAULT '0',
post_type varchar(50) NOT NULL DEFAULT 'post',
lang varchar(20) NOT NULL DEFAULT '0',
PRIMARY KEY doctermitem (doc, term, blogid)) $charset_collate";
dbDelta( $query );
$return[] = $query;
$query = "SHOW INDEX FROM $this->table_name";
$indices = $wpdb->get_results( $query );
$existing_indices = array();
foreach ( $indices as $index ) {
if ( isset( $index->Key_name ) ) {
$existing_indices[] = $index->Key_name;
}
}
// Worst case scenario optimal indexes
if ( ! in_array( 'term_ptype_bid_lang', $existing_indices ) ) {
$sql = "CREATE INDEX term_ptype_bid_lang ON $this->table_name (term(20), post_type(20), blogid, lang(10))";
$wpdb->query( $sql );
$return[] = $sql;
}
if ( ! in_array( 'rterm_ptype_bid_lang', $existing_indices ) ) {
$sql = "CREATE INDEX rterm_ptype_bid_lang ON $this->table_name (term_reverse(20), post_type(20), blogid, lang(10))";
$wpdb->query( $sql );
$return[] = $sql;
}
if ( !in_array( 'doc', $existing_indices ) ) {
$sql = "CREATE INDEX `doc` ON $this->table_name (`doc`)";
$wpdb->query( $sql );
$return[] = $sql;
}
return $return;
}
public function scheduled() {
global $wpdb;
// 4.20.3
if ( Plugin::previousVersion('4.20.2') ) {
if ( $wpdb->get_var( "SHOW COLUMNS FROM `$this->table_name` LIKE 'taxonomy';" ) ) {
$query = "ALTER TABLE `$this->table_name`
DROP COLUMN `taxonomy`,
DROP COLUMN `category`,
DROP COLUMN `item`";
$wpdb->query($query);
$query = "ALTER TABLE `$this->table_name`
MODIFY COLUMN `content` smallint(9) UNSIGNED,
MODIFY COLUMN `title` tinyint(3) UNSIGNED,
MODIFY COLUMN `comment` tinyint(3) UNSIGNED,
MODIFY COLUMN `tag` tinyint(3) UNSIGNED,
MODIFY COLUMN `link` tinyint(3) UNSIGNED,
MODIFY COLUMN `author` tinyint(3) UNSIGNED,
MODIFY COLUMN `excerpt` tinyint(3) UNSIGNED,
MODIFY COLUMN `customfield` smallint(9) UNSIGNED";
$wpdb->query( $query );
$query = "OPTIMIZE TABLE `$this->table_name`";
$wpdb->query( $query );
}
}
}
/**
* Runs a table optimize query on the index table
*
* @return bool|false|int
*/
public function optimize() {
global $wpdb;
// In innoDB this is mapped to "ALTER TABLE .. FORCE", aka. defragmenting
// OPTIMIZE only needs SELECT and INSERT privileges
return $wpdb->query( 'OPTIMIZE TABLE ' . $this->table_name );
}
public function truncate() {
global $wpdb;
$wpdb->query( 'TRUNCATE TABLE ' . $this->table_name );
}
function removeDocument( $post_id ) {
global $wpdb;
if ( is_array($post_id) ) {
foreach ( $post_id as &$v ) {
$v = $v + 0;
}
$post_ids = implode(', ', $post_id);
$wpdb->query( "DELETE FROM $this->table_name WHERE doc IN ($post_ids)" );
} else {
$wpdb->query(
$wpdb->prepare(
"DELETE FROM $this->table_name WHERE doc = %d",
$post_id
)
);
}
/*
DO NOT call finishOperation() here, it would switch back the blog too early.
Calling this function from an action hooks does not require switching the blog,
as the correct one is in use there.
*/
}
/**
* Generates the query based on the post and the token array and inserts into DB
*
* @return int
*/
function insertTokensToDB( $the_post, $tokens, $blog_id, $lang ) {
global $wpdb;
$values = array();
if ( count( $tokens ) <= 0 ) {
return false;
}
foreach ( $tokens as $d ) {
// If it's numeric, delete the leading space
$term = trim( $d['_keyword'] );
if ( isset($d['_no_reverse']) && $d['_no_reverse'] === true ) {
$value = $wpdb->prepare(
'(%d, %s, %s, %d, %d, %d, %d, %d, %d, %d, %d, %d, %s, %s)',
$the_post->ID,
$term,
'',
$blog_id,
$d['content'],
$d['title'],
$d['comment'],
$d['tag'],
$d['link'],
$d['author'],
$d['excerpt'],
$d['customfield'],
$the_post->post_type,
$lang
);
} else {
$value = $wpdb->prepare(
'(%d, %s, REVERSE(%s), %d, %d, %d, %d, %d, %d, %d, %d, %d, %s, %s)',
$the_post->ID,
$term,
$term,
$blog_id,
$d['content'],
$d['title'],
$d['comment'],
$d['tag'],
$d['link'],
$d['author'],
$d['excerpt'],
$d['customfield'],
$the_post->post_type,
$lang
);
}
$values[] = $value;
// Split INSERT at every 200 records
if ( count( $values ) > 199 ) {
$values = implode( ', ', $values );
$query = "INSERT IGNORE INTO $this->table_name
(`doc`, `term`, `term_reverse`, `blogid`, `content`, `title`, `comment`, `tag`, `link`, `author`,
`excerpt`, `customfield`, `post_type`, `lang`)
VALUES $values";
$wpdb->query( $query );
$values = array();
}
}
// Add the remaining as well
if ( count( $values ) > 0 ) {
$values = implode( ', ', $values );
$query = "INSERT IGNORE INTO $this->table_name
(`doc`, `term`, `term_reverse`, `blogid`, `content`, `title`, `comment`, `tag`, `link`, `author`,
`excerpt`, `customfield`, `post_type`, `lang`)
VALUES $values";
$wpdb->query( $query );
}
return count( $tokens );
}
/**
* Gets the post IDs to index
*
* @return array of post IDs
*/
function getPostIdsToIndex( $args, $posts_to_ignore ): array {
global $wpdb;
$parent_join = '';
$_statuses = explode(',', $args['post_statuses']);
foreach ( $_statuses as &$sv ) {
$sv = trim($sv);
}
$valid_status = "'" . implode("', '", $_statuses ) . "'";
if ( count($args['post_types']) > 0 ) {
$post_types = $args['post_types'];
if ( class_exists('WooCommerce') && in_array('product_variation', $post_types) ) { // Special case for Woo variations
$post_types = array_diff($post_types, array( 'product_variation' ));
$rest = '';
if ( count($post_types) > 0 ) {
$rest = " OR post.post_type IN('" . implode("', '", $post_types) . "') ";
}
// In case of product variation the parent post status must also match, otherwise it is not relevant
$parent_join = "LEFT JOIN $wpdb->posts parent ON (post.post_parent = parent.ID)";
$restriction = " AND ( (post.post_type = 'product_variation' AND parent.post_status IN($valid_status) ) $rest )";
} else {
$restriction = " AND post.post_type IN ('" . implode("', '", $post_types) . "')";
}
} else {
return array();
}
$mimes_restrict = '';
if ( in_array('attachment', $args['post_types'], true) ) {
$restriction .= $this->getAttachmentDirRestrictionQueryPart( $args );
if ( $args['attachment_mime_types'] != '' ) {
$mimes_arr = wpd_comma_separated_to_array($args['attachment_mime_types']);
if ( count($mimes_arr) > 0 ) {
$mimes_restrict = "OR ( post.post_status = 'inherit' AND post.post_mime_type IN ('" . implode("','", $mimes_arr) . "') )";
}
}
}
$post_password = '';
if ( $args['post_password_protected'] == 0 ) {
$post_password = " AND (post.post_password = '') ";
}
$ignore_posts = '';
if ( !empty($posts_to_ignore[ $args['blog_id'] ]) ) {
$ignore_posts = ' AND post.ID NOT IN( ' . implode(',', $posts_to_ignore[ $args['blog_id'] ]) . ' )';
}
$limit = $args['limit'] > 1000 ? 1000 : ( $args['limit'] + 0 );
$add_where = apply_filters('asp/index/database/get_posts_to_index/query/add_where', '', $args);
$add_where_post_status = apply_filters('asp/index/database/get_posts_to_index/query/add_where_post_status', '', $args);
if ( $args['extend'] ) {
// We are extending, so keep the existing
$q = "SELECT post.ID
FROM $wpdb->posts post
$parent_join
LEFT JOIN $this->table_name r ON (post.ID = r.doc AND r.blogid = " . $args['blog_id'] . ")
WHERE
r.doc is null
AND
(
post.post_status IN ($valid_status)
$mimes_restrict
$add_where_post_status
)
$restriction
$ignore_posts
$post_password
$add_where
ORDER BY post.ID ASC
LIMIT $limit";
} else {
$q = "SELECT post.ID
FROM $wpdb->posts post
$parent_join
WHERE
(
post.post_status IN ($valid_status)
$mimes_restrict
$add_where_post_status
)
$restriction
$ignore_posts
$post_password
$add_where
ORDER BY post.ID ASC
LIMIT $limit";
}
return $wpdb->get_results( $q );
}
/**
* Gets the number documents to index
*/
public function getPostIdsToIndexCount( $args, $posts_to_ignore, $check_only = false ): int {
if ( defined('ASP_INDEX_BYPASS_COUNT') ) {
return 9999;
}
global $wpdb;
$parent_join = '';
$_statuses = explode(',', $args['post_statuses']);
foreach ( $_statuses as &$sv ) {
$sv = trim($sv);
}
$valid_status = "'" . implode("', '", $_statuses ) . "'";
$mimes_restrict = '';
if ( count($args['post_types']) > 0 ) {
$post_types = $args['post_types'];
if ( class_exists('WooCommerce') && in_array('product_variation', $post_types) ) { // Special case for Woo variations
$post_types = array_diff($post_types, array( 'product_variation' ));
$rest = '';
if ( count($post_types) > 0 ) { // are there any left?
$rest = " OR post.post_type IN('" . implode("', '", $post_types) . "') ";
}
// In case of product variation the parent post status must also match, otherwise it is not relevant
$parent_join = "LEFT JOIN $wpdb->posts parent ON (post.post_parent = parent.ID)";
$restriction = " AND ( (post.post_type = 'product_variation' AND parent.post_status IN($valid_status) ) $rest )";
} else {
$restriction = " AND post.post_type IN ('" . implode("', '", $post_types) . "')";
}
} else {
return 0;
}
if ( in_array('attachment', $args['post_types'], true) ) {
$restriction .= $this->getAttachmentDirRestrictionQueryPart( $args );
if ( $args['attachment_mime_types'] != '' ) {
$mimes_arr = wpd_comma_separated_to_array($args['attachment_mime_types']);
if ( count($mimes_arr) > 0 ) {
$mimes_restrict = "OR ( post.post_status = 'inherit' AND post.post_mime_type IN ('" . implode("','", $mimes_arr) . "') )";
}
}
}
$post_password = '';
if ( $args['post_password_protected'] == 0 ) {
$post_password = " AND (post.post_password = '') ";
}
$ignore_posts = '';
if ( !empty($posts_to_ignore[ $args['blog_id'] ]) ) {
$ignore_posts = ' AND post.ID NOT IN( ' . implode(',', $posts_to_ignore[ $args['blog_id'] ]) . ' )';
}
$add_where = apply_filters('asp/index/database/get_posts_to_index/query/add_where', '', $args);
$add_where_post_status = apply_filters('asp/index/database/get_posts_to_index/query/add_where_post_status', '', $args);
if ( $check_only ) {
$q = "SELECT 1
FROM $wpdb->posts post
$parent_join
LEFT JOIN $this->table_name r ON (post.ID = r.doc AND r.blogid = " . $args['blog_id'] . ")
WHERE
r.doc is null
AND
(
post.post_status IN ($valid_status)
$mimes_restrict
$add_where_post_status
)
$restriction
$ignore_posts
$post_password
$add_where
LIMIT 1";
} else {
$q = "SELECT COUNT(DISTINCT post.ID)
FROM $wpdb->posts post
$parent_join
LEFT JOIN $this->table_name r ON (post.ID = r.doc AND r.blogid = " . $args['blog_id'] . ")
WHERE
r.doc is null
AND
(
post.post_status IN ($valid_status)
$mimes_restrict
$add_where_post_status
)
$restriction
$ignore_posts
$post_password
$add_where";
}
return intval( $wpdb->get_var( $q ) );
}
private function getAttachmentDirRestrictionQueryPart( array $args ): string {
global $wpdb;
$attachment_dir_query = '';
$uploads = wp_get_upload_dir();
if ( false !== $uploads['error'] ) {
return '';
}
/**
* This parts makes corrections to "trim" the absolute path to the upload directory
* as _wp_attached_file stores the directory part only after ../wp-content/uploads/
*/
$uploads_relative_dir =
trailingslashit( str_replace(ABSPATH, '', $uploads['basedir']) );
$exclude_directories = array_filter(
array_map(
function ( $dir ) use ( $uploads_relative_dir ) {
return str_replace($uploads_relative_dir, '', $dir);
},
$args['attachment_exclude_directories']
)
);
$include_directories = array_filter(
array_map(
function ( $dir ) use ( $uploads_relative_dir ) {
return str_replace($uploads_relative_dir, '', $dir);
},
$args['attachment_include_directories']
)
);
if ( count($exclude_directories) > 0 ) {
$not_like_values = implode(
'AND',
array_map(
function ( $directory ) use ( $wpdb ) {
return " $wpdb->postmeta.meta_value NOT LIKE '" . Str::escape($directory) . "%'";
},
$exclude_directories
)
);
$attachment_dir_query .= " AND (
(
(
SELECT IF((meta_key IS NULL OR meta_value = ''), -1, COUNT(meta_id))
FROM $wpdb->postmeta
WHERE $wpdb->postmeta.post_id = post.ID AND $wpdb->postmeta.meta_key='_wp_attached_file'
LIMIT 1
) = -1
OR
(
SELECT COUNT(meta_id) as mtc
FROM $wpdb->postmeta
WHERE $wpdb->postmeta.post_id = post.ID AND $wpdb->postmeta.meta_key='_wp_attached_file' AND
($not_like_values)
ORDER BY mtc
LIMIT 1
) >= 1
) )";
}
if ( count($include_directories) > 0 ) {
$not_like_values = implode(
'OR',
array_map(
function ( $directory ) use ( $wpdb ) {
return " $wpdb->postmeta.meta_value LIKE '" . Str::escape($directory) . "%'";
},
$include_directories
)
);
$attachment_dir_query .= " AND (
(
(
SELECT IF((meta_key IS NULL OR meta_value = ''), -1, COUNT(meta_id))
FROM $wpdb->postmeta
WHERE $wpdb->postmeta.post_id = post.ID AND $wpdb->postmeta.meta_key='_wp_attached_file'
LIMIT 1
) = -1
OR
(
SELECT COUNT(meta_id) as mtc
FROM $wpdb->postmeta
WHERE $wpdb->postmeta.post_id = post.ID AND $wpdb->postmeta.meta_key='_wp_attached_file' AND
($not_like_values)
ORDER BY mtc
LIMIT 1
) >= 1
) )";
}
return $attachment_dir_query;
}
public function getPostsIndexed() {
if ( defined('ASP_INDEX_BYPASS_COUNT') ) {
return 9999;
}
global $wpdb;
// Tested faster as a regular single query count
$sql = 'SELECT COUNT(count) FROM (SELECT 1 as count FROM ' . wd_asp()->db->table('index') . ' GROUP BY doc) as A';
return $wpdb->get_var($sql);
}
public function getTotalKeywords() {
if ( defined('ASP_INDEX_BYPASS_COUNT') ) {
return 9999;
}
global $wpdb;
if ( is_multisite() ) {
$sql = 'SELECT COUNT(doc) FROM ' . wd_asp()->db->table('index');
} else {
$sql = 'SELECT COUNT(doc) FROM ' . wd_asp()->db->table('index') . ' WHERE blogid = ' . get_current_blog_id();
}
return $wpdb->get_var($sql);
}
public function isEmpty(): bool {
global $wpdb;
return $wpdb->query('SELECT 1 FROM ' . wd_asp()->db->table('index') . ' LIMIT 1') == 0;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,166 @@
<?php /** @noinspection PhpComposerExtensionStubsInspection */
/** @noinspection RegExpRedundantEscape */
namespace WPDRMS\ASP\Index;
use WPDRMS\ASP\Utils\Str;
defined('ABSPATH') or die("You can't access this file directly.");
if ( !class_exists(__NAMESPACE__ . '\Shortcode') ) {
class Shortcode {
private $temporary_shortcode_tags = array();
/**
* Executes the shortcodes within the given string
*
* @param $content
* @param $post
* @param $exclude
* @return string
* @noinspection PhpUndefinedClassInspection
*/
function execute($content, $post, $exclude): string {
$content = apply_filters( 'asp_index_before_shortcode_execution', $content, $post );
// WP Table Reloaded support
if ( defined( 'WP_TABLE_RELOADED_ABSPATH' ) ) {
/** @noinspection PhpIncludeInspection */
include_once( WP_TABLE_RELOADED_ABSPATH . 'controllers/controller-frontend.php' );
if ( class_exists('\\WP_Table_Reloaded_Controller_Frontend') ) {
/** @noinspection PhpFullyQualifiedNameUsageInspection */
$wpt_reloaded = new \WP_Table_Reloaded_Controller_Frontend();
}
}
// TablePress support
if ( defined( 'TABLEPRESS_ABSPATH' ) && class_exists('\\TablePress') ) {
$content .= ' ' . $this->parseTablePressShortcodes($content);
}
// Remove user defined shortcodes
$shortcodes = explode( ',',$exclude );
$try_getting_sc_content = apply_filters('asp_it_try_getting_sc_content', true);
foreach ( $shortcodes as $shortcode ) {
$shortcode = trim($shortcode);
if ( $shortcode == '' )
continue;
// First let us try to get any contents from the shortcode itself
if ( $try_getting_sc_content ) {
$content = preg_replace(
'/(?:\[' . $shortcode . '[ ]+.*?\]|\[' . $shortcode . '[ ]*\])(.*?)\[\/' . $shortcode . '[ ]*]/su',
' $1 ',
$content
);
}
// Then remove the shortcode completely
$this->temporaryDisableShortcode($shortcode);
}
// Try extracting the content of these shortcodes, but do not execute
$more_shortcodes = array(
'cws-widget', 'cws-row', 'cws-column', 'col', 'row', 'item'
);
foreach ( $more_shortcodes as $shortcode ) {
// First let us try to get any contents from the shortcode itself
$content = preg_replace(
'/(?:\[' . $shortcode . '[ ]+.*?\]|\[' . $shortcode . '[ ]*\])(.*?)\[\/' . $shortcode . '[ ]*]/su',
' $1 ',
$content
);
/*remove_shortcode( $shortcode );
add_shortcode( $shortcode, array( $this, 'return_empty_string' ) );*/
$this->temporaryDisableShortcode($shortcode);
}
// These shortcodes are completely ignored, and removed with content
$ignore_shortcodes = array(
'vc_asp_search',
'ts_products_in_category_tabs',
'wd_asp',
'wpdreams_ajaxsearchpro',
'wpdreams_ajaxsearchpro_results',
'wpdreams_asp_settings',
'contact-form',
'starrater',
'responsive-flipbook',
'avatar_upload',
'product_categories',
'recent_products',
'templatera',
'bsf-info-box', 'logo-slider',
'ourteam', 'embedyt', 'gallery', 'bsf-info-box', 'tweet', 'blog', 'portfolio',
'peepso_activity', 'peepso_profile', 'peepso_group'
);
if ( defined( 'TABLEPRESS_ABSPATH' ) && class_exists('\\TablePress') ) {
$ignore_shortcodes[] = 'table';
}
foreach ( $ignore_shortcodes as $shortcode ) {
$this->temporaryDisableShortcode($shortcode);
}
$content = do_shortcode( $content );
// WP 4.2 emoji strip
if ( function_exists( 'wp_encode_emoji' ) ) {
$content = wp_encode_emoji( $content );
}
if ( defined( 'TABLEPRESS_ABSPATH' ) ) {
unset( $tp_controller );
}
if ( defined( 'WP_TABLE_RELOADED_ABSPATH' ) ) {
unset( $wpt_reloaded );
}
$this->enableDisabledShortcodes();
return apply_filters( 'asp_index_after_shortcode_execution', $content, $post );
}
private function temporaryDisableShortcode($tag) {
global $shortcode_tags;
if ( array_key_exists( $tag, $shortcode_tags ) ) {
$this->temporary_shortcode_tags[$tag] = $shortcode_tags[$tag];
$shortcode_tags[ $tag ] = array( $this, 'return_empty_string' );
}
}
private function enableDisabledShortcodes() {
global $shortcode_tags;
foreach ($this->temporary_shortcode_tags as $tag => $callback) {
$shortcode_tags[$tag] = $callback;
}
}
private function parseTablePressShortcodes( $content ): string {
$regex = '/\[table[^\]]*id\=[\'"]{0,1}([0-9]+)[\'"]{0,1}?[^\]]*\]/';
$tables = json_decode(get_option('tablepress_tables'), true);
if ( !is_null($tables) && isset($tables['table_post']) && preg_match_all($regex, $content, $matches) > 0) {
$return = array();
foreach ( $matches[1] as $table_id ) {
$data = json_decode( get_post_field('post_content', $tables['table_post'][$table_id]), true );
if ( $data !== null ) {
$return[] = Str::anyToString($data);
}
}
return implode(' ', $return);
}
return '';
}
/**
* An empty function to override individual shortcodes. This must be a public method.
*
* @return string
* @noinspection PhpUnused
*/
function return_empty_string(): string {
return "";
}
}
}

View File

@@ -0,0 +1,590 @@
<?php
namespace WPDRMS\ASP\Index;
use WPDRMS\ASP\Utils\Html;
use WPDRMS\ASP\Utils\Inflect\InflectController;
use WPDRMS\ASP\Utils\MB;
use WPDRMS\ASP\Utils\Str;
/**
* @phpstan-type TokenizerArgs array{
* min_word_length: int,
* use_stopwords: bool,
* stopwords: string[],
* synonyms_as_keywords: bool,
* inflect: bool,
* language: string,
* }
*/
class Tokenizer {
/**
* @var string unique random string for special replacements
*/
private string $randstr = 'wpivdny3htnydqd6mlyg';
/**
* @var TokenizerArgs
*/
private array $args;
/**
* @var string[]
*/
public static array $additional_keywords_pattern = array(
'"',
"'",
'`',
'',
'',
'”',
'“',
'«',
'»',
'+',
'.',
',',
':',
'-',
'_',
'=',
'%',
'(',
')',
'{',
'}',
'*',
'[',
']',
'|',
'&',
'/',
);
/**
* @param TokenizerArgs $args
*/
public function __construct( array $args ) {
$defaults = array(
'min_word_length' => 2,
'use_stopwords' => false,
'stopwords' => array(),
'synonyms_as_keywords' => false,
'inflect' => false,
'language' => 'english',
);
$this->args = wp_parse_args( $args, $defaults ); // @phpstan-ignore-line
}
public function setLanguage(string $language): void {
$this->args['language'] = $language;
}
/**
* Performs a simple trimming, character removal on a string
*
* @param $str
* @param $post
* @return string
*/
function tokenizeSimple( $str, $post ): string {
if ( function_exists( 'mb_internal_encoding' ) ) {
mb_internal_encoding( 'UTF-8' );
}
$str = Str::anyToString($str);
$str = Html::toTxt( $str );
$str = strip_tags( $str );
$str = stripslashes( $str );
// Non breakable spaces to regular spaces
$str = preg_replace('/\xc2\xa0/', ' ', $str);
$str = preg_replace( '/[[:space:]]+/', ' ', $str );
$str = str_replace( array( "\n", "\r", ' ' ), ' ', $str );
// Turkish uppercase I does not lowercase correctly
$str = str_replace( 'İ', 'i', $str );
$str = MB::strtolower( $str );
$str = trim($str);
$str = Content::hebrewUnvocalize($str);
$str = Content::arabicRemoveDiacritics($str);
$negative_keywords = $this->getNegativeWords($post);
foreach ( $negative_keywords as $negative_keyword ) {
// If there is a negative keyword within, this case is over
if ( strpos($str, $negative_keyword) !== false ) {
return '';
}
}
$stop_words = $this->getStopWords();
foreach ( $stop_words as $stop_word ) {
// whole word matches only
if ( preg_match('/\b' . $stop_word . '\b/', $str, $match, PREG_OFFSET_CAPTURE) ) {
return '';
}
}
return $str;
}
/**
* Performs a simple trimming, character removal on a string, but returns array of keywords
* by the separator character
*
* @param $str
* @param $post
* @return array
*/
function tokenizePhrases( $str, $post, $word_separator = ',' ): array {
if ( function_exists( 'mb_internal_encoding' ) ) {
mb_internal_encoding( 'UTF-8' );
}
$args = $this->args;
$str = Str::anyToString($str);
$str = Html::toTxt( $str );
$str = strip_tags( $str );
$str = stripslashes( $str );
// Non breakable spaces to regular spaces
$str = preg_replace('/\xc2\xa0/', ' ', $str);
$str = preg_replace( '/[[:space:]]+/', ' ', $str );
$str = str_replace( array( "\n", "\r", ' ' ), ' ', $str );
// Turkish uppercase I does not lowercase correctly
$str = str_replace( 'İ', 'i', $str );
$str = MB::strtolower( $str );
$str = trim($str);
$str = Content::hebrewUnvocalize($str);
$str = Content::arabicRemoveDiacritics($str);
$words = explode($word_separator, $str);
$words = array_map('trim', $words);
$words = array_filter(
$words,
function ( $word ) {
return \WPDRMS\ASP\Utils\MB::strlen($word);
}
);
$keywords = array();
while ( ( $c_word = array_shift($words) ) !== null ) {
$c_word = trim($c_word);
if ( $c_word == '' || MB::strlen($c_word) < $args['min_word_length'] ) {
continue;
}
// Numerics won't work otherwise, need to trim that later
if ( is_numeric($c_word) ) {
$c_word = ' ' . $c_word;
}
if ( array_key_exists($c_word, $keywords) ) {
++$keywords[ $c_word ][1];
} else {
$keywords[ $c_word ] = array( $c_word, 1 );
}
}
unset($c_word);
return $keywords;
}
/**
* Performs a keyword extraction on the given content string.
*
* @return array of keywords $keyword = array( 'keyword', {count} )
*/
function tokenize( $str, $post = false, $lang = '' ): array {
if ( is_array( $str ) ) {
$str = Str::anyToString( $str );
}
if ( function_exists('mb_strlen') ) {
$fn_strlen = 'mb_strlen';
} else {
$fn_strlen = 'strlen';
}
$args = $this->args;
if ( function_exists( 'mb_internal_encoding' ) ) {
mb_internal_encoding( 'UTF-8' );
}
$str = apply_filters( 'asp_indexing_string_pre_process', $str );
$str = Html::toTxt( $str );
$str = wp_specialchars_decode( $str );
$str = strip_tags( $str );
$str = stripslashes( $str );
// Replace non-word boundary dots with a unique string + 'd'
/** @noinspection RegExpRedundantEscape */
$str = preg_replace('/([0-9])[\.]([0-9])/', '$1' . $this->randstr . 'd$2', $str);
// Remove potentially dangerous or unusable characters
$str = str_replace(
array(
'·',
'…',
'€',
'&shy;',
'·',
'…',
'®',
'©',
'™',
"\xC2\xAD",
),
'',
$str
);
$str = str_replace(
array(
'. ', // dot followed by space as boundary, otherwise it might be a part of the word
', ', // comma followed by space only, otherwise it might be a word part
'<',
'>',
'†',
'‡',
'‰',
'',
'™',
'¡',
'¢',
'¤',
'¥',
'¦',
'§',
'¨',
'©',
'ª',
'«',
'¬',
'®',
'¯',
'°',
'±',
'¹',
'²',
'³',
'¶',
'·',
'º',
'»',
'¼',
'½',
'¾',
'¿',
'÷',
'•',
'…',
'←',
'←',
'↑',
'→',
'↓',
'↔',
'↵',
'⇐',
'⇑',
'⇒',
'⇓',
'⇔',
'√',
'∝',
'∞',
'∠',
'∧',
'',
'∂',
'∃',
'∅',
'',
'∩',
'',
'∫',
'∴',
'',
'≅',
'≈',
'≠',
'≡',
'≤',
'≥',
'⊂',
'⊃',
'⊄',
'⊆',
'⊇',
'⊕',
'⊗',
'⊥',
'◊',
'♠',
'♣',
'♥',
'♦',
'🔴',
'',
'◊',
'〈',
'〉',
'⌊',
'⌋',
'⌈',
'⌉',
'⋅',
'ˇ',
'°',
'~',
'Ë›',
'Ëť',
'¸',
'§',
'¨',
'’',
'‘',
'”',
'“',
'„',
'´',
'—',
'–',
'Ă—',
'&#8217;',
'&#128308;',
'&nbsp;',
"\n",
"\r",
'& ',
'\\',
'^',
'?',
'!',
';',
chr( 194 ) . chr( 160 ),
),
' ',
$str
);
$str = str_replace( 'Ăź', 'ss', $str );
// Turkish uppercase I does not lowercase correctly
$special_replace = array(
'İ' => 'i',
'—' => '-',
);
$str = str_replace( array_keys($special_replace), array_values($special_replace), $str );
// Any yet undefined punctuation
// $str = preg_replace( '/[[:punct:]]+/u', ' ', $str );
// Non breakable spaces to regular spaces
$str = preg_replace('/\xc2\xa0/', ' ', $str);
// Any remaining multiple space characters
$str = preg_replace( '/[[:space:]]+/', ' ', $str );
$str = MB::strtolower($str);
$str = Content::hebrewUnvocalize($str);
$str = Content::arabicRemoveDiacritics($str);
// $str = preg_replace('/[^\p{L}0-9 ]/', ' ', $str);
$str = str_replace( "\xEF\xBB\xBF", '', $str );
$str = trim( preg_replace( '/\s+/', ' ', $str ) );
// Set back the non-word boundary dots
$str = str_replace( $this->randstr . 'd', '.', $str );
$str = apply_filters( 'asp_indexing_string_post_process', $str );
$words = explode( ' ', $str );
// Remove punctuation marks + some extra from the start and the end of words
// Characters, which should not be standalone (but can be in start on end)
$non_standalone_strings = array( '$', '€', '£', '%' );
// Additional keywords, should not be standalone
$additional_keywords_string = implode('', array_diff(self::$additional_keywords_pattern, $non_standalone_strings));
foreach ( $words as $wk => &$ww ) {
$ww = MB::trim($ww, $additional_keywords_string);
if ( $ww == '' || in_array($ww, $non_standalone_strings ) ) {
unset($words[ $wk ]);
}
}
unset($wk);
unset($ww);
// Get additional words if available
$additional_words = array();
$started = microtime(true);
foreach ( $words as $ww ) {
// This operation can be costly, so limit to 3 seconds just to be sure
if ( ( microtime(true) - $started ) > 3 ) {
break;
}
// ex.: 123-45-678 to 123, 45, 678
$ww1 = str_replace(self::$additional_keywords_pattern, ' ', $ww);
$wa = explode(' ', $ww1);
if ( count($wa) > 1 ) {
foreach ( $wa as $wak => $wav ) {
$wav = trim(preg_replace( '/[[:space:]]+/', ' ', $wav ));
if ( $wav != '' && !in_array($wav, $words) ) {
$wa[ $wak ] = $wav;
} else {
unset($wa[ $wak ]);
}
}
$additional_words = array_merge($additional_words, $wa);
}
// ex.: 123-45-678 to 12345678
$ww2 = str_replace(self::$additional_keywords_pattern, '', $ww);
if ( $ww2 !== '' && $ww2 != $ww && !in_array($ww2, $words) && !in_array($ww2, $additional_words) ) {
$additional_words[] = $ww2;
}
// Accent removal and transliteration
$transliterated = str_replace(array( 'ʾ', "'", '"' ), '', Str::removeAccents($ww));
if (
$transliterated !== '' &&
$transliterated !== $ww &&
!in_array($transliterated, $words, true) &&
!in_array($transliterated, $additional_words, true)
) {
$additional_words[] = $transliterated;
}
}
// Append them after the words array
$words = array_merge($words, $additional_words);
// Inflections
if ( $this->args['inflect'] && !empty($words) ) {
$words = array_merge(
$words,
InflectController::instance()->get($words, $this->args['language'])
);
}
/**
* Apply synonyms for the whole string instead of the words, because
* synonyms can be multi-keyword phrases too
*/
$syn_inst = \WPDRMS\ASP\Synonyms\Manager::getInstance();
if ( $syn_inst->exists() ) {
if ( $this->args['synonyms_as_keywords'] == 1 ) {
$syn_inst->synonymsAsKeywords();
}
$additional_words_by_synonyms = array();
$synonyms = $syn_inst->get();
// If the langauge is set
if ( $lang != '' && isset($synonyms[ $lang ]) ) {
foreach ( $synonyms[ $lang ] as $keyword => $synonyms_arr ) {
if ( preg_match('/\b' . preg_quote($keyword) . '\b/u', $str) ) {
$additional_words_by_synonyms = array_merge($additional_words_by_synonyms, $synonyms_arr);
}
}
}
unset($keyword, $synonyms_arr);
// Also for the "default" aka "any"
if ( isset($synonyms['default']) ) {
foreach ( $synonyms['default'] as $keyword => $synonyms_arr ) {
if ( preg_match('/\b' . preg_quote($keyword) . '\b/u', $str) ) {
$additional_words_by_synonyms = array_merge($additional_words_by_synonyms, $synonyms_arr);
}
}
}
if ( count($additional_words_by_synonyms) > 0 ) {
$words = array_merge($words, $additional_words_by_synonyms);
}
}
$stop_words = $this->getStopWords();
$negative_keywords = $this->getNegativeWords($post);
$keywords = array();
foreach ( $words as $c_word ) {
$c_word = trim($c_word);
if ( $c_word == '' || $fn_strlen($c_word) < $args['min_word_length'] ) {
continue;
}
// Only whole word matches for stop-words
if ( !empty($stop_words) && in_array($c_word, $stop_words) ) {
continue;
}
// Partial matches for negative keywords
foreach ( $negative_keywords as $negative_keyword ) {
if ( strpos($c_word, $negative_keyword) !== false ) {
continue 2;
}
}
// Numerics won't work otherwise, need to trim that later
if ( is_numeric($c_word) ) {
$c_word = ' ' . $c_word;
}
if ( array_key_exists($c_word, $keywords) ) {
++$keywords[ $c_word ][1];
} else {
$keywords[ $c_word ] = array( $c_word, 1 );
}
}
unset($c_word);
return apply_filters( 'asp_indexing_keywords', $keywords );
}
/**
* Returns the stop words
*/
private function getStopWords(): array {
$stop_words = array();
// Only compare to common words if $restrict is set to false
if ( $this->args['use_stopwords'] == 1 && $this->args['stopwords'] != '' ) {
$this->args['stopwords'] = str_replace(' ', '', $this->args['stopwords']);
$stop_words = explode( ',', $this->args['stopwords'] );
}
$stop_words = array_unique( $stop_words );
foreach ( $stop_words as $sk => &$sv ) {
$sv = trim($sv);
if ( $sv == '' || MB::strlen($sv) < $this->args['min_word_length'] ) {
unset($stop_words[ $sk ]);
}
}
return $stop_words;
}
/**
* Returns negative keywords for the current post object
*/
private function getNegativeWords( $post ): array {
// Post level stop-words, negative keywords
if ( $post !== false ) {
$negative_keywords = get_post_meta($post->ID, '_asp_negative_keywords', true);
if ( !empty($negative_keywords) ) {
$negative_keywords = trim( preg_replace('/\s+/', ' ', $negative_keywords) );
$negative_keywords = explode(' ', $negative_keywords);
$negative_keywords = array_filter($negative_keywords, fn( $keyword )=>$keyword !=='');
return array_unique($negative_keywords);
}
}
return array();
}
}

View File

@@ -0,0 +1,225 @@
<?php
namespace WPDRMS\ASP\Integration\Imagely;
use WP_Error;
use WP_Post;
use WPDRMS\ASP\Integration\IntegrationInterface;
use WPDRMS\ASP\Patterns\SingletonTrait;
use WPDRMS\ASP\Utils\Imagely\NggImage;
class NextGenGallery implements IntegrationInterface {
use SingletonTrait;
public function load(): void {
if ( !class_exists('\Imagely\NGG\DataMappers\Image') ) {
return;
}
add_filter('asp_results', array( $this, 'searchResults' ), 10, 4);
add_filter('asp/utils/advanced-field/raw_value', array( $this, 'resultAdvancedFields' ), 10, 4);
add_filter('asp/utils/advanced-field/field-types/taxonomy/args', array( $this, 'resultAdvancedFieldsTaxonomy' ), 10, 4);
add_filter(
'asp/index/database/get_posts_to_index/query/add_where_post_status',
array( $this, 'indexTablePostStatusFix' ),
10,
2
);
add_filter(
'asp/index/hooks/post_update/allowed_statuses',
array( $this, 'indexTablePostUpdateFix' ),
10,
2
);
add_filter('asp_index_post', array( $this, 'indexTablePost' ), 10, 2);
add_filter('asp_index_terms', array( $this, 'indexTableTerms' ), 10, 3);
}
public function unload(): void {
remove_filter('asp_results', array( $this, 'searchResults' ));
remove_filter('asp/utils/advanced-field/raw_value', array( $this, 'resultAdvancedFields' ));
remove_filter('asp/utils/advanced-field/field-types/taxonomy/args', array( $this, 'resultAdvancedFieldsTaxonomy' ));
remove_filter('asp/index/database/get_posts_to_index/query/add_where_post_status', array( $this, 'indexTablePostStatusFix' ));
remove_filter('asp_index_post', array( $this, 'indexTablePost' ) );
remove_filter('asp_index_terms', array( $this, 'indexTableTerms' ) );
}
/**
* Fixes live search results
*
* @hook asp_results
* @param array $results
* @return array
*/
public function searchResults( array $results, $search_id, $is_ajax, $args ): array {
foreach ( $results as $k => $r ) {
if ( !isset($r->post_type) ) {
continue;
}
if ( $r->post_type === 'ngg_pictures' ) {
$r->image = NggImage::getImageUrl($r->id);
if ( count($args['attachment_include_directories']) > 0 ) {
$found = false;
foreach ( $args['attachment_include_directories'] as $include_dir ) {
if ( str_contains( $r->image, $include_dir) ) {
$found = true;
break;
}
}
if ( !$found ) {
unset($results[ $k ]);
continue;
}
}
foreach ( $args['attachment_exclude_directories'] as $exclude_dir ) {
if ( str_contains( $r->image, $exclude_dir) ) {
unset($results[ $k ]);
continue 2;
}
}
$r->link = $r->image;
}
}
return $results;
}
/**
* Fixes advanced title and content fields
*
* @hook asp/utils/advanced-field/raw_value
* @param string $value
* @param string $field
* @param object $result
* @param array $args
* @return string
*/
public function resultAdvancedFields( string $value, string $field, $result, array $args ) {
if (
$field !== 'result_field' ||
$result->post_type !== 'ngg_pictures'
) {
return $value;
}
if ( $args['field_name'] === 'title' ) {
return NggImage::getImageTitle($result->id);
}
if ( $args['field_name'] === 'content' ) {
return NggImage::getImageDescription($result->id);
}
return $value;
}
/**
* Fixes taxonomy term advanced title field
*
* @hook asp/utils/advanced-field/field-types/taxonomy/args
* @param Array<string, mixed> $args
* @param object $result
* @return Array<string, mixed>
*/
public function resultAdvancedFieldsTaxonomy( array $args, $result ) {
if ( $result->post_type !== 'ngg_pictures' ) {
return array();
}
$image = NggImage::getImageFromPostId( $result->id );
if ( $image === null ) {
return array();
}
$args['object_ids'] = $image->pid;
return $args;
}
/**
* Allows indexing "draft" ngg_pictures post type for indexed search
*
* @hook asp/index/database/get_posts_to_index/query/add_where_post_status
* @param string $add_where
* @param array $args
* @return string
*/
public function indexTablePostStatusFix( string $add_where, array $args ) {
if ( !in_array('ngg_pictures', $args['post_types'], true) ) {
return $add_where;
}
return $add_where . " OR ( post.post_status = 'draft' AND post.post_type = 'ngg_pictures') ";
}
/**
* Allows indexing "draft" ngg_pictures post type when they are saved
*
* @hook asp/index/hooks/post_update/allowed_statuses
* @param string[] $allowed_statuses
* @param int $post_id
* @return array
*/
public function indexTablePostUpdateFix( array $allowed_statuses, int $post_id ) {
$post_type = get_post_type($post_id);
if ( $post_type === 'ngg_pictures' ) {
$allowed_statuses[] = 'draft';
$allowed_statuses = array_unique($allowed_statuses);
}
return $allowed_statuses;
}
/**
* Fixes post fields on index table process
*
* @hook asp_index_post
* @param WP_Post $post
* @param array $args
* @return WP_Post|null
*/
public function indexTablePost( WP_Post $post, array $args ): ?WP_Post {
if ( $post->post_type !== 'ngg_pictures' ) {
return $post;
}
$file_url = NggImage::getImageUrl( $post->ID );
if ( count($args['attachment_include_directories']) > 0 ) {
$found = false;
foreach ( $args['attachment_include_directories'] as $include_dir ) {
if ( str_contains( $file_url, $include_dir) ) {
$found = true;
break;
}
}
if ( !$found ) {
return null;
}
}
foreach ( $args['attachment_exclude_directories'] as $exclude_dir ) {
if ( str_contains( $file_url, $exclude_dir) ) {
return null;
}
}
$post->post_title = NggImage::getImageTitle( $post->ID );
$post->post_content =
NggImage::getImageDescription( $post->ID ) . ' ' .
NggImage::getImageFileName( $post->ID );
return $post;
}
/**
* Fixes taxonomy term indexing for ngg_tag
*
* @param array|WP_Error $terms
* @param string $taxonomy
* @param WP_Post $post
* @return string[]
*/
public function indexTableTerms( $terms, string $taxonomy, WP_Post $post ) {
if ( $post->post_type !== 'ngg_pictures' || $taxonomy !== 'ngg_tag' ) {
return $terms;
}
return NggImage::getImageTags( $post->ID );
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace WPDRMS\ASP\Integration;
interface IntegrationInterface {
public function load(): void;
public function unload(): void;
}

View File

@@ -0,0 +1,421 @@
<?php
namespace WPDRMS\ASP\Media;
/* Prevent direct access */
use Exception;
use WPDRMS\ASP\Media\RemoteService\License;
use WPDRMS\ASP\Utils\FileManager;
use WPDRMS\ASP\Utils\Str;
defined( 'ABSPATH' ) or die( "You can't access this file directly." );
class Parser {
/**
* Mime groups array
*
* @var array
*/
private static $mimes = array(
'image' => array(
'image/jpeg',
'image/gif',
'image/png',
'image/bmp',
'image/tiff',
'image/x-icon',
),
'pdf' => array(
'application/pdf',
),
'text' => array(
'text/plain',
'text/csv',
'text/tab-separated-values',
'text/calendar',
'text/css',
'text/vtt',
'text/html',
),
'richtext' => array(
'text/richtext',
'application/rtf',
),
'mso_word' => array(
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.ms-word.document.macroEnabled.12',
'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'application/vnd.ms-word.template.macroEnabled.12',
'application/vnd.oasis.opendocument.text',
),
'mso_excel' => array(
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.ms-excel.sheet.macroEnabled.12',
'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'application/vnd.ms-excel.template.macroEnabled.12',
'application/vnd.ms-excel.addin.macroEnabled.12',
'application/vnd.oasis.opendocument.spreadsheet',
'application/vnd.oasis.opendocument.chart',
'application/vnd.oasis.opendocument.database',
'application/vnd.oasis.opendocument.formula',
),
'mso_powerpoint' => array(
'application/vnd.ms-powerpoint',
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
'application/vnd.openxmlformats-officedocument.presentationml.template',
'application/vnd.ms-powerpoint.template.macroEnabled.12',
'application/vnd.ms-powerpoint.addin.macroEnabled.12',
'application/vnd.openxmlformats-officedocument.presentationml.slide',
'application/vnd.ms-powerpoint.slide.macroEnabled.12',
'application/vnd.oasis.opendocument.presentation',
'application/vnd.oasis.opendocument.graphics',
),
);
private $args, $post, $filepath;
/**
* Constructor.
*
* @param \WP_Post $post
* @param array $args
*/
function __construct( $post, $args ) {
$defaults = array(
'media_service_send_file' => false,
'pdf_parser' => 'auto', // auto, smalot, pdf2txt
'text_content' => true,
'richtext_content' => true,
'pdf_content' => true,
'msword_content' => true,
'msexcel_content' => true,
'msppt_content' => true,
);
$this->post = $post;
$this->args = wp_parse_args($args, $defaults);
$this->args = apply_filters('asp_media_parser_args', $this->args, $defaults);
// To allow adding mimes programmatically
self::$mimes = apply_filters('asp/media/parser/mimes', self::$mimes);
}
/**
* Parses the file contents
*
* @return string|\WP_Error
*/
function parse( $remote = true ) {
$this->filepath = get_attached_file( $this->post->ID );
if ( is_wp_error($this->filepath) || empty($this->filepath) || !file_exists($this->filepath) ) {
return '';
}
$text = '';
if ( $this->isThisParseEnabled() ) {
if ( $remote ) {
$text = $this->parseRemote();
} else {
$text = $this->parseLocal();
}
/**
* In case of PDF there are many cases of gibberish in text when using OCR, such as
* "my text a<bc my other text" -> where "<bc" is treated as an opening HTML tag.
* Then running strip_tags("my text a<bc my other text") = "my text a" -> will remove everything
* after the tag starting bracket, because the "my other text" is considered as tag attributes.
* The best course of action here is to simply remove these symbols.
*/
if ( !is_wp_error($text) && $this->isThis('pdf') ) {
$text = str_replace(array( '<', '>' ), ' ', $text);
}
}
return $text;
}
function parseRemote() {
$text = '';
$license = License::getInstance();
if ( $license->active() && $license->valid() ) {
if ( !in_array('_asp_attachment_text', get_post_custom_keys($this->post->ID)) ) {
$license_data = $license->getData();
$license_max_filesize =
isset($license_data['stats'], $license_data['stats']['max_filesize']) ?
$license_data['stats']['max_filesize'] : 0;
$parser = new RemoteService\Parser(
$this->filepath,
wp_get_attachment_url($this->post->ID),
$license->get(),
$this->args['media_service_send_file'],
$license_max_filesize
);
$text = $parser->request();
$license->setStats($parser->getStats());
if ( !is_wp_error($text) ) {
update_post_meta($this->post->ID, '_asp_attachment_text', $text);
} else {
return $text;
}
} else {
$text = get_post_meta($this->post->ID, '_asp_attachment_text', true);
}
}
return $text;
}
function parseLocal() {
if ( $this->isThis('text') && $this->args['text_content'] ) {
$text = $this->parseTXT();
} elseif ( $this->isThis('richtext') && $this->args['richtext_content'] ) {
$text = $this->parseRTF();
} elseif ( $this->isThis('pdf') && $this->args['pdf_content'] ) {
$text = $this->parsePDF();
} elseif ( $this->isThis('mso_word') && $this->args['msword_content'] ) {
$text = $this->parseMSOWord();
} elseif ( $this->isThis('mso_excel') && $this->args['msexcel_content'] ) {
$text = $this->parseMSOExcel();
} elseif ( $this->isThis('mso_powerpoint') && $this->args['msppt_content'] ) {
$text = $this->parseMSOPpt();
} else {
$text = '';
}
return $text;
}
/**
* Checks if a mime type belongs to a certain mime group (text, richtext etc..)
*
* @param string $type
* @return bool
*/
public function isThis( string $type = 'text' ): bool {
return ( isset(self::$mimes[ $type ]) && in_array($this->post->post_mime_type, self::$mimes[ $type ]) );
}
public function isThisParseEnabled(): bool {
return ( $this->isThis('text') && $this->args['text_content'] ) ||
( $this->isThis('richtext') && $this->args['richtext_content'] ) ||
( $this->isThis('pdf') && $this->args['pdf_content'] ) ||
( $this->isThis('mso_word') && $this->args['msword_content'] ) ||
( $this->isThis('mso_excel') && $this->args['msexcel_content'] ) ||
( $this->isThis('mso_powerpoint') && $this->args['msppt_content'] ) ||
( $this->isThis('image') && License::instance()->active() && !License::instance()->isFree() );
}
/**
* Gets contents from a text based file
*
* @return string
*/
function parseTXT() {
$contents = FileManager::instance()->read($this->filepath);
// CSV files often store the values in quotes. We don't need those in this case.
if ( $this->post->post_mime_type == 'text/csv' ) {
$contents = str_replace(array( '"', "'" ), ' ', $contents);
}
return $contents;
}
/**
* Gets contents from a richtext file
*
* @return string
*/
function parseRTF() {
$rtf = FileManager::instance()->read($this->filepath);
if ( $rtf != '' ) {
include_once ASP_EXTERNALS_PATH . 'class.rtf-html-php.php';
$reader = new \ASP_RtfReader();
$reader->Parse($rtf);
$formatter = new \ASP_RtfHtml();
return html_entity_decode(strip_tags($formatter->Format($reader->root)));
}
return '';
}
/**
* Gets contents from a PDF file
*
* @return string
*/
function parsePDF() {
$args = $this->args;
$contents = '';
// PDF Parser for php 5.3 and above
if ( $args['pdf_parser'] == 'auto' || $args['pdf_parser'] == 'smalot' ) {
if ( version_compare(PHP_VERSION, '5.3', '>=') ) {
include_once ASP_EXTERNALS_PATH . 'class.pdfsmalot.php';
$parser = new \ASP_PDFSmalot();
$parser = $parser->getObj();
try {
$pdf = $parser->parseFile($this->filepath);
$contents = $pdf->getText();
} catch ( Exception $e ) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
}
}
// Different method maybe?
if ( $args['pdf_parser'] == 'auto' || $args['pdf_parser'] == 'pdf2txt' ) {
if ( $contents == '' ) {
include_once ASP_EXTERNALS_PATH . 'class.pdf2txt.php';
$pdfParser = new \ASP_PDF2Text();
$pdfParser->setFilename($this->filepath);
$pdfParser->decodePDF();
$contents = $pdfParser->output();
}
}
return $contents;
}
/**
* Gets contents from an Office Word file
*
* @return string
*/
function parseMSOWord() {
if ( false !== strpos( $this->post->post_mime_type, 'opendocument' ) ) {
$o = $this->getFileFromArchive('content.xml', $this->filepath);
} else {
$o = $this->getFileFromArchive('word/document.xml', $this->filepath);
}
return $o;
}
/**
* Gets contents from an Office Excel file
*
* @return string
*/
function parseMSOExcel() {
if ( false !== strpos($this->post->post_mime_type, 'opendocument') ) {
$o = $this->getFileFromArchive('content.xml', $this->filepath);
} elseif ( substr_compare($this->filepath, '.xls', -strlen('.xls')) === 0 ) {
$o = '';
include_once ASP_EXTERNALS_PATH . 'php-excel/autoload.php';
$reader = \Asan\PHPExcel\Excel::load(
$this->filepath,
function ( \Asan\PHPExcel\Reader\Xls $reader ) use ( $o ) {
$reader->ignoreEmptyRow(true);
}
);
foreach ( $reader->sheets() as $shk => $sheet ) {
$reader->setSheetIndex($shk);
foreach ( $reader as $row ) {
$o .= ' ' . Str::anyToString($row);
}
}
} else {
$o = $this->getFileFromArchive('xl/sharedStrings.xml', $this->filepath);
}
return $o;
}
/**
* Gets contents from an Office Powerpoint file
*
* @return string
*/
function parseMSOPpt() {
$out = '';
if ( class_exists( '\\ZipArchive' ) ) {
if ( false !== strpos($this->post->post_mime_type, 'opendocument') ) {
$out = $this->getFileFromArchive('content.xml', $this->filepath);
} else {
$zip = new \ZipArchive();
if ( true === $zip->open($this->filepath) ) {
$slide_num = 1;
while ( false !== ( $xml_index = $zip->locateName('ppt/slides/slide' . absint($slide_num) . '.xml') ) ) {
$xml = $zip->getFromIndex($xml_index);
$out .= ' ' . $this->getXMLContent($xml);
++$slide_num;
}
$zip->close();
} elseif ( substr_compare($this->filepath, '.ppt', -strlen('.ppt')) === 0 ) {
// This approach uses detection of the string "chr(0f).Hex_value.chr(0x00).chr(0x00).chr(0x00)" to find text strings, which are then terminated by another NUL chr(0x00). [1] Get text between delimiters [2]
$fileHandle = fopen($this->filepath, 'r');
$line = @fread($fileHandle, filesize($this->filepath));
$lines = explode(chr(0x0f), $line);
$outtext = '';
foreach ( $lines as $thisline ) {
if ( strpos($thisline, chr(0x00) . chr(0x00) . chr(0x00)) == 1 ) {
$text_line = substr($thisline, 4);
$end_pos = strpos($text_line, chr(0x00));
$text_line = substr($text_line, 0, $end_pos);
$text_line = preg_replace("/[^a-zA-Z0-9\s\,\.\-\n\r\t@\/\_\(\)]/", '', $text_line);
if ( strlen($text_line) > 1 ) {
$outtext .= substr($text_line, 0, $end_pos) . "\n";
}
}
}
$out = $outtext;
}
}
}
return $out;
}
/**
* Gets the content from an XML string
*
* @param $xml
* @return string
*/
private function getXMLContent( $xml ) {
if ( class_exists('\\DOMDocument') ) {
$dom = new \DOMDocument();
$dom->loadXML($xml, LIBXML_NOENT | LIBXML_XINCLUDE | LIBXML_NOERROR | LIBXML_NOWARNING);
return $dom->saveXML();
}
return '';
}
/**
* Gets a file from an archive, based on the xml file name
*
* @param $xml
* @param $filename
* @return string
*/
private function getFileFromArchive( $xml, $filename ) {
if ( class_exists('\\ZipArchive') && class_exists('\\DOMDocument') ) {
$output_text = '';
$zip = new \ZipArchive();
if ( true === $zip->open($filename) ) {
if ( false !== ( $xml_index = $zip->locateName($xml) ) ) {
$xml_data = $zip->getFromIndex($xml_index);
$dom = new \DOMDocument();
$dom->loadXML( $xml_data, LIBXML_NOENT | LIBXML_XINCLUDE | LIBXML_NOERROR | LIBXML_NOWARNING );
$output_text = $dom->saveXML();
}
$zip->close();
} else {
// File open error
return '';
}
return $output_text;
}
// The ZipArchive class is missing
return '';
}
}

View File

@@ -0,0 +1,186 @@
<?php
namespace WPDRMS\ASP\Media\RemoteService;
use WPDRMS\ASP\Patterns\SingletonTrait;
if ( !defined('ABSPATH') ) {
die('-1');
}
/**
* @phpstan-type LicenseStatsArr array{
* free: bool,
* max_files_usage: int,
* max_files: int|'unlimited',
* max_filesize: int
* }
* @phpstan-type LicenseDataArr array{
* license: string,
* active: bool,
* last_check: int,
* stats: LicenseStatsArr
* }
*/
class License {
use SingletonTrait;
/**
* @var LicenseDataArr
*/
private array $data;
private string $url = 'https://media1.ajaxsearchpro.com/';
private function __construct() {
$this->data = get_option(
'_asp_media_service_data',
array(
'license' => '',
'active' => false,
'stats' => array(
'free' => true,
'max_files_usage' => 1,
'max_files' => 0,
'max_filesize' => 0,
),
)
);
$this->refresh();
}
public function active(): bool {
return $this->data['active'] && $this->get() !== '';
}
public function isFree(): bool {
return $this->data['stats']['free'];
}
public function valid(): bool {
if ( $this->data['stats']['max_files'] === 'unlimited' ) {
return true;
}
if (
(int) $this->data['stats']['max_files_usage'] >= (int) $this->data['stats']['max_files']
) {
/**
* The "stats" are updated ONLY during indexing. If the max_file threshold was met during a recent
* index, then max_files < max_files_usage forever, and this function would return "false" all the time.
* If the last check was performed over 5 minutes ago, the report "true" even if the files
* threshold was met, so a request will be made to the media server to verify that.
*/
if ( ( time() - (int) $this->data['last_check'] ) > 300 ) {
return true;
} else {
return false;
}
} else {
return true;
}
}
public function refresh(): void {
if ( $this->active() ) {
if ( ( time() - (int) $this->data['last_check'] ) > 300 ) {
$this->activate($this->data['license']);
}
}
}
/**
* @param string $license
* @return array{
* success: 1|0,
* text: string
* }
*/
public function activate( string $license ): array {
$success = 0;
if (
strlen($license) === 36 ||
preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/', $license) === 1
) {
$response = wp_safe_remote_post(
$this->url,
array(
'body' => array(
'license' => $license,
),
)
);
if ( !is_wp_error($response) ) {
$data = json_decode($response['body'], true); // @phpstan-ignore-line
if ( !$data['success'] ) {
$text = $data['text'];
} else {
$this->set($license, true, $data['stats']);
$success = 1;
$text = 'License successfully activated!';
}
} else {
$text = $response->get_error_message(); // @phpstan-ignore-line
}
} else {
$text = __('Invalid license key length or missing characters. Please make sure to copy the 36 character license key here.', 'ajax-search-pro');
}
return array(
'success' => $success,
'text' => $text,
);
}
public function deactivate(): void {
$this->data['active'] = false;
update_option('_asp_media_service_data', $this->data);
}
public function delete(): void {
delete_option('_asp_media_service_data');
}
public function get(): string {
return !empty($this->data['license']) ? $this->data['license'] : '';
}
/**
* @return LicenseDataArr
*/
public function getData(): array {
return $this->data;
}
/**
* @param string $license
* @param bool $active
* @param LicenseStatsArr $stats
* @return void
*/
public function set( string $license, bool $active, array $stats ) {
$this->data = array(
'license' => $license,
'active' => $active,
'last_check' => time(),
'stats' => $stats,
);
update_option('_asp_media_service_data', $this->data);
}
/**
* @param LicenseStatsArr|false|array{} $stats
* @return void
*/
public function setStats( $stats = false ) {
if ( $stats !== false && count($stats) > 0 && $this->data['license'] !== false ) {
$this->data['stats'] = $stats;
update_option(
'_asp_media_service_data',
array(
'license' => $this->data['license'],
'active' => $this->data['active'],
'last_check' => time(),
'stats' => $stats,
)
);
}
}
}

View File

@@ -0,0 +1,126 @@
<?php
namespace WPDRMS\ASP\Media\RemoteService;
use WP_Error;
defined('ABSPATH') or die("You can't access this file directly.");
class Parser {
private $server = "https://mediaservices.ajaxsearchpro.com/";
private $filepath, $license, $file_url, $send_file, $stats = false, $max_filesize = 0;
function __construct( $filepath, $file_url, $license, $send_file = false, $max_filesize = 0 ) {
$this->filepath = $filepath;
$this->file_url = $file_url;
$this->license = $license;
$this->send_file = $send_file;
$this->max_filesize = $max_filesize;
}
function request() {
$url = $this->getServer();
if ( $this->getServer() !== false ) {
$response = $this->post( $url );
if ( is_wp_error($response) ) {
return $response;
}
$data = json_decode($response['body'], true);
$this->stats = $data['stats'];
if ( $data['success'] == 0 ) {
return new WP_Error($data['code'], $data['text']);
} else {
return $data['text'];
}
}
}
function getStats() {
return $this->stats;
}
private function post( $url ) {
if ( $this->send_file ) {
$file = fopen( $this->filepath, 'r' );
if ( false === $file ) {
$response = new WP_Error( 'fopen', 'Could not open the file for reading.' );
} else {
$file_size = filesize( $this->filepath );
if ( $file_size === false ) {
$response = new WP_Error( 'fsize_error', 'Local server could not determine the file size.' );
} else if ( $this->max_filesize > 0 && ($file_size / 1048576 ) > $this->max_filesize ) {
$response = new WP_Error( 'fsize_too_big', 'Local file size check: File too big ('.number_format($file_size / 1048576, 2) . ' MB'.')' );
} else {
$file_data = fread( $file, $file_size );
$response = wp_safe_remote_post(
$url,
array_merge(
$this->multipartPostData($file_data, $this->filepath, array(
'license' => $this->license
)), array(
'timeout' => 60
)
)
);
}
}
} else {
$response = wp_safe_remote_post(
$url,
array(
'body' => array(
'license' => $this->license,
'url' => $this->file_url
)
)
);
}
return $response;
}
private function getServer() {
$address = get_transient('_asp_media_server_address');
if ( $address === false ) {
$server = wp_safe_remote_get($this->server);
if ( !is_wp_error($server) ) {
$address = trim($server['body']);
set_transient('_asp_media_server_address', $address);
} else {
$address = false;
}
}
return $address;
}
private function multipartPostData($file_contents, $file_name, $post_fields = array()) {
$boundary = wp_generate_password( 24 );
$headers = array(
'content-type' => 'multipart/form-data; boundary=' . $boundary,
);
$payload = '';
foreach ( $post_fields as $name => $value ) {
$payload .= '--' . $boundary;
$payload .= "\r\n";
$payload .= 'Content-Disposition: form-data; name="' . $name .
'"' . "\r\n\r\n";
$payload .= $value;
$payload .= "\r\n";
}
if ( strlen($file_contents) > 0 ) {
$payload .= '--' . $boundary;
$payload .= "\r\n";
$payload .= 'Content-Disposition: form-data; name="' . 'file' .
'"; filename="' . basename( $file_name ) . '"' . "\r\n";
$payload .= "\r\n";
$payload .= $file_contents;
$payload .= "\r\n";
}
$payload .= '--' . $boundary . '--';
return array(
'headers' => $headers,
'body' => $payload,
);
}
}

View File

@@ -0,0 +1,46 @@
<?php /** @noinspection HttpUrlsUsage */
namespace WPDRMS\ASP\Misc;
use WPDRMS\ASP\Patterns\SingletonTrait;
class OutputBuffer {
use SingletonTrait;
private $found = false;
function obStart() {
ob_start(array($this, 'obCallback'));
}
function obCallback($buffer, $phase) {
if ($phase & PHP_OUTPUT_HANDLER_FINAL || $phase & PHP_OUTPUT_HANDLER_END) {
// Hook into this to change the buffer
return apply_filters('asp_ob_end', $buffer);
}
return $buffer;
}
function obClose(): bool {
$handlers = ob_list_handlers();
$callback = self::class . '::obCallback';
$found = in_array($callback, $handlers);
if ( $found ) {
for ($i = count($handlers) - 1; $i >= 0; $i--) {
ob_end_flush();
if ($handlers[$i] === $callback) {
break;
}
}
}
// $this->found = found is not good. If this function is triggered multiple times, then may override "true" to "false"
$this->found = $found == true ? true : $this->found;
return $found;
}
function obFound(): bool {
return $this->found;
}
}

View File

@@ -0,0 +1,100 @@
<?php
namespace WPDRMS\ASP\Misc;
defined('ABSPATH') or die("You can't access this file directly.");
class Performance {
/**
* @var array of performance values
*/
private $records;
/**
* @var array default values for the records array
*/
private $default = array(
'run_count' => 0,
'average_runtime' => 0,
'average_memory' => 0,
'last_runtime' => 0,
'last_memory' => 0
);
/**
* @var int current runtime
*/
private $runtime;
/**
* @var int actual memory usage
*/
private $memory;
/**
* @var string the name of the storage
*/
private $key;
/**
* Setup Class
*
* @param string $key
*/
function __construct(string $key = "plugin_performance") {
$this->key = $key;
$this->records = get_option($key, $this->default);
}
/**
* Deletes the storage
*/
function reset() {
delete_option($this->key);
}
/**
* Gets the storage
*
* @return array
*/
function get_data(): array {
return $this->records;
}
/**
* Starts the measurement
*/
function start_measuring() {
$this->runtime = microtime(true);
$this->memory = memory_get_usage(true);
}
/**
* Stops the measurement
*/
function stop_measuring() {
$this->runtime = microtime(true) - $this->runtime;
$this->memory = memory_get_peak_usage(true) - $this->memory;
$this->save();
}
/**
* Saves the values
*/
private function save() {
$this->count_averages();
$this->records['last_runtime'] = $this->runtime > 15 ? 15 : $this->runtime;
$this->records['last_memory'] = $this->memory;
++$this->records['run_count'];
update_option($this->key, $this->records);
}
/**
* Calculates the final averages before writing to database
*/
private function count_averages() {
$this->records['average_runtime'] =
($this->records['average_runtime'] * $this->records['run_count'] + $this->runtime) / ($this->records['run_count'] + 1);
$this->records['average_memory'] =
($this->records['average_memory'] * $this->records['run_count'] + $this->memory) / ($this->records['run_count'] + 1);
}
}

View File

@@ -0,0 +1,95 @@
<?php /** @noinspection HttpUrlsUsage */
namespace WPDRMS\ASP\Misc;
use WP_Error;
if ( !defined('ABSPATH') ) {
die('-1');
}
class PluginLicense {
static $url = 'https://update.wp-dreams.com/';
private const OPTION_NAME = 'asp_license_data';
public static function activate( $license_key ) {
$url = isset($_SERVER['HTTP_HOST']) ? rawurlencode($_SERVER['HTTP_HOST']) : 'https://unkwown.domain';
$key = rawurlencode( $license_key );
$url = self::$url . "license/activate/$key/?url=" . $url;
$response = wp_remote_post( $url );
if ( $response instanceof WP_Error ) {
return false;
}
$data = json_decode( $response['body'], true );
// something went wrong
if ( empty($data) ) {
return false;
}
if ( isset($data['status']) && $data['status'] == 1 ) {
update_site_option(
self::OPTION_NAME,
array(
'key' => $license_key,
'host' => $_SERVER['HTTP_HOST'] ?? 'unkwown.domain',
)
);
}
return $data;
}
public static function deactivate( $remote_check = true ) {
$data = false;
if ( $remote_check ) {
$key = self::isActivated();
if ( false !== $key ) {
$url = isset($_SERVER['HTTP_HOST']) ? rawurlencode($_SERVER['HTTP_HOST']) : 'unkwown.domain';
$key = rawurlencode($key);
$url = self::$url . "license/deactivate/$key?url=" . $url;
$response = wp_remote_request($url, array( 'method' =>'PATCH' ));
if ( $response instanceof WP_Error ) {
return false;
}
$data = json_decode($response['body'], true);
}
}
delete_site_option(self::OPTION_NAME);
return $data;
}
public static function isActivated( bool $remote_check = false ) {
self::convertToSiteOption();
$data = get_site_option(self::OPTION_NAME);
if ( $data === false || !isset($data['host']) || !isset($data['key']) ) {
return false;
}
if ( $remote_check ) {
$url = isset($_SERVER['HTTP_HOST']) ? rawurlencode($_SERVER['HTTP_HOST']) : 'unknown.domain';
$key = rawurlencode( $data['key'] );
$url = self::$url . "license/is_active/$key/?url=" . $url;
$response = wp_remote_get( $url );
if ( $response instanceof WP_Error ) {
return false;
}
$rdata = json_decode( $response['body'], true );
return $rdata['status'] ? $data['key'] : false;
}
return $data['key'];
}
private static function convertToSiteOption() {
// Old option with the old key
$data = get_option('asp_update_data');
if ( $data !== false ) {
update_site_option(self::OPTION_NAME, $data);
delete_option('asp_update_data');
}
}
}

View File

@@ -0,0 +1,129 @@
<?php
namespace WPDRMS\ASP\Misc;
use WPDRMS\ASP\Utils\Str;
if (!defined('ABSPATH')) die('-1');
class Priorities {
public static function count() {
$count = get_site_option('_asp_priority_count');
if ( $count === false ) {
global $wpdb;
$wpdb->get_var( "SELECT 1 FROM " . wd_asp()->db->table('priorities') . " LIMIT 1" );
$count = $wpdb->num_rows;
update_site_option('_asp_priority_count', $count);
}
return $count;
}
static function ajax_get_posts() {
global $wpdb;
parse_str($_POST['options'], $o);
$w_post_type = '';
$w_filter = '';
$w_limit = (int)$o['p_asp_limit'] + 0;
$w_limit = $w_limit < 1 ? 20 : $w_limit;
$w_limit = $w_limit > 200 ? 200 : $w_limit;
$pt = wd_asp()->db->table("priorities");
if (isset($o['blog_id']) && $o['blog_id'] != 0 && is_multisite())
switch_to_blog($o['p_asp_blog']);
if (isset($o['p_asp_filter']) && $o['p_asp_filter'] != '') {
$w_filter = "AND $wpdb->posts.post_title LIKE '%" . Str::escape($o['p_asp_filter']) . "%'";
}
if (isset($o['p_asp_post_type']) && $o['p_asp_post_type'] != 'all') {
$w_post_type = "AND $wpdb->posts.post_type = '" . Str::escape($o['p_asp_post_type']) . "'";
}
if ( $o['p_asp_post_type'] == 'attachment' ) {
$post_status = "$wpdb->posts.post_status IN ('inherit')";
} else {
$post_status = "$wpdb->posts.post_status IN ('publish', 'pending')";
}
$allowed_orderings = array(
'id DESC', 'id ASC', 'title DESC', 'title ASC', 'priority DESC', 'priority ASC'
);
if ( !isset($o['p_asp_ordering']) || !in_array($o['p_asp_ordering'], $allowed_orderings) ) {
$o['p_asp_ordering'] = 'id DESC';
}
$querystr = "
SELECT
$wpdb->posts.post_title as `title`,
$wpdb->posts.ID as id,
$wpdb->posts.post_date as `date`,
$wpdb->users.user_nicename as `author`,
$wpdb->posts.post_type as `post_type`,
CASE WHEN $pt.priority IS NULL
THEN 100
ELSE $pt.priority
END as `priority`
FROM $wpdb->posts
LEFT JOIN $wpdb->users ON $wpdb->users.ID = $wpdb->posts.post_author
LEFT JOIN $pt ON ($pt.post_id = $wpdb->posts.ID AND $pt.blog_id = " . get_current_blog_id() . ")
WHERE
$wpdb->posts.ID>0 AND
$post_status AND
$wpdb->posts.post_type NOT IN ('revision')
$w_post_type
$w_filter
GROUP BY
$wpdb->posts.ID
ORDER BY " . $o['p_asp_ordering'] . "
LIMIT $w_limit";
echo "!!PASPSTART!!" . json_encode($wpdb->get_results($querystr, OBJECT)) . '!!PASPEND!!';
if (is_multisite())
restore_current_blog();
die();
}
/**
*
*/
static function ajax_set_priorities() {
global $wpdb;
$i = 0;
parse_str($_POST['options'], $o);
if ($o['p_blogid'] == 0)
$o['p_blogid'] = get_current_blog_id();
foreach ($o['priority'] as $k => $v) {
// See if the value changed, count them
if ($v != $o['old_priority'][$k]) {
$i++;
$query = "INSERT INTO ".wd_asp()->db->table("priorities")."
(post_id, blog_id, priority)
VALUES($k, " . $o['p_blogid'] . ", $v)
ON DUPLICATE KEY UPDATE priority=" . $v;
$wpdb->query($query);
}
}
echo "!!PSASPSTART!!" . $i . "!!PSASPEND!!";
if (is_multisite())
restore_current_blog();
// Cleanup
$wpdb->query("DELETE FROM " . wd_asp()->db->table("priorities") . " WHERE priority=100");
$wpdb->get_var( "SELECT 1 FROM ".wd_asp()->db->table("priorities")." LIMIT 1" );
$count = $wpdb->num_rows;
update_site_option('_asp_priority_count', $count);
die();
}
}

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