function log($message = '', $type = 'notice') { global $wp_filesystem; WP_Filesystem(); // if we're not active, don't do anything if (!$this->active || !file_exists($this->logfile)) { return false; } // get the existing log $existing = $wp_filesystem->get_contents($this->logfile); // format our entry $entry = '[' . date('Y-d-m G:i:s', current_time('timestamp')) . '][' . sanitize_text_field($type) . ']'; // flag it with the process ID $entry .= '[' . SearchWP::instance()->getPid() . ']'; // sanitize the message $message = sanitize_text_field(esc_html($message)); $message = str_replace('=>', '=>', $message); // put back array identifiers $message = str_replace('->', '->', $message); // put back property identifiers $message = str_replace(''', "'", $message); // put back apostrophe's // finally append the message $entry .= ' ' . $message; // append the entry $log = $existing . "\n" . $entry; // write log $wp_filesystem->put_contents($this->logfile, $log); error_log($log); }
/** * Perform a search via SearchWP * * @since 1.0 * * @param string $query The search query * * @uses sanitize_text_field() to sanitize input * @uses add_filter() to hook into SearchWP (if applicable) to allow custom pagination and prevent full Post object loading * @uses SearchWP::instance() to retrieve the SearchWP singleton (if applicable) * @uses SearchWP::search() to perform a SearchWP-powered search (if applicable) * * @return array Search results comprised of Post IDs */ function searchwp($query = '') { $posts = array(0); if (class_exists('SearchWP')) { $searchwp = SearchWP::instance(); // set up custom posts per page add_filter('searchwp_posts_per_page', array($this, 'get_posts_per_page')); // prevent loading Post objects, we only want IDs add_filter('searchwp_load_posts', '__return_false'); $engine = isset($_REQUEST['swpengine']) ? sanitize_text_field($_REQUEST['swpengine']) : 'default'; // grab our post IDs $results = $searchwp->search($engine, $query); // if no results were found we need to force our impossible array if (!empty($results)) { $posts = $results; } } return $posts; }
function plugin_row() { if (!class_exists('SearchWP')) { ?> <tr class="plugin-update-tr searchwp"> <td colspan="3" class="plugin-update"> <div class="update-message"> <?php _e('SearchWP must be active for Term Highlight to work', 'searchwp'); ?> </div> </td> </tr> <?php } else { $searchwp = SearchWP::instance(); if (version_compare($searchwp->version, '1.9.5', '<')) { ?> <tr class="plugin-update-tr searchwp"> <td colspan="3" class="plugin-update"> <div class="update-message"> <?php _e('SearchWP Term Highlight requires SearchWP 1.9.5 or greater', 'searchwp'); ?> </div> </td> </tr> <?php } } }
/** * Back-end widget form. * * @see WP_Widget::form() * * @param array $instance Previously saved values from database. * * @return string|void */ public function form($instance) { $widget_title = isset($instance['title']) ? $instance['title'] : __('Search', 'swplas'); $widget_placeholder = isset($instance['placeholder']) ? $instance['placeholder'] : __('Search for...', 'swplas'); $widget_destination = isset($instance['destination']) ? $instance['destination'] : ''; // we'll piggyback SearchWP itself to pull a list of search engines $widget_engine = isset($instance['engine']) ? $instance['engine'] : 'default'; $engines = array(); if (class_exists('SearchWP')) { $engines['default'] = 'Default'; $searchwp = SearchWP::instance(); $searchwp_engines = $searchwp->settings['engines']; foreach ($searchwp_engines as $engine => $engine_settings) { if (isset($engine_settings['searchwp_engine_label'])) { $engines[$engine] = $engine_settings['searchwp_engine_label']; } } } // we're going to utilize SearchWP_Live_Search_Form to populate the config dropdown $widget_config = isset($instance['config']) ? $instance['config'] : 'default'; if (!class_exists('SearchWP_Live_Search_Form')) { include_once dirname(__FILE__) . '/class-form.php'; } $form = new SearchWP_Live_Search_Form(); $form->setup(); ?> <p> <label for="<?php echo $this->get_field_id('title'); ?> "><?php _e('Title:'); ?> </label> <input class="widefat" id="<?php echo $this->get_field_id('title'); ?> " name="<?php echo $this->get_field_name('title'); ?> " type="text" value="<?php echo esc_attr($widget_title); ?> "> </p> <?php if (!empty($engines)) { ?> <p> <label for="<?php echo $this->get_field_id('engine'); ?> "><?php _e('SearchWP Engine:'); ?> </label> <select name="<?php echo $this->get_field_name('engine'); ?> " id="<?php echo $this->get_field_id('engine'); ?> "> <?php foreach ($engines as $engine_name => $engine_label) { ?> <option value="<?php echo esc_attr($engine_name); ?> " <?php selected($widget_engine, $engine_name); ?> ><?php echo esc_html($engine_label); ?> </option> <?php } ?> </select> </p> <?php } ?> <p> <label for="<?php echo $this->get_field_id('config'); ?> "><?php _e('Configuration:'); ?> </label> <select name="<?php echo $this->get_field_name('config'); ?> " id="<?php echo $this->get_field_id('config'); ?> "> <?php foreach ($form->configs as $config => $val) { ?> <option value="<?php echo esc_attr($config); ?> " <?php selected($widget_config, $config); ?> ><?php echo esc_html($config); ?> </option> <?php } ?> </select> </p> <?php $swpuniqid = uniqid('swp'); ?> <p><a href="#" class="button searchwp-widget-<?php echo $swpuniqid; ?> "><?php _e('Advanced', 'searchwp'); ?> </a></p> <div class="searchwp-live-search-widget-advanced" style="display:none;"> <p> <label for="<?php echo $this->get_field_id('placeholder'); ?> "><?php _e('Placholder:'); ?> </label> <input class="widefat" id="<?php echo $this->get_field_id('placeholder'); ?> " name="<?php echo $this->get_field_name('placeholder'); ?> " type="placeholder" value="<?php echo esc_attr($widget_placeholder); ?> "> </p> <p> <label for="<?php echo $this->get_field_id('destination'); ?> "><?php _e('Destination fallback URL (optional):'); ?> </label> <input class="widefat" id="<?php echo $this->get_field_id('destination'); ?> " name="<?php echo $this->get_field_name('destination'); ?> " type="text" value="<?php echo esc_attr($widget_destination); ?> "> </p> </div> <script type="text/javascript"> jQuery(document).ready(function($){ $('.searchwp-widget-<?php echo $swpuniqid; ?> ').click(function(){ var $advanced = $(this).parents().find('.searchwp-live-search-widget-advanced'); if($advanced.is(':visible')){ $advanced.hide(); }else{ $advanced.show(); } return false; }); }); </script> <?php }
/** * Constructor * * @param array $args * @since 1.0 */ function __construct($args = array()) { global $wpdb, $searchwp; do_action('searchwp_log', 'SearchWPSearch __construct()'); $defaults = array('terms' => '', 'engine' => 'default', 'page' => 1, 'posts_per_page' => intval(get_option('posts_per_page')), 'order' => $this->order, 'load_posts' => true); $this->db_prefix = $wpdb->prefix . SEARCHWP_DBPREFIX; // process our arguments $args = wp_parse_args($args, $defaults); $this->searchwp = SearchWP::instance(); // instantiate our stemmer for later $this->stemmer = new SearchWPStemmer(); do_action('searchwp_log', '$args = ' . var_export($args, true)); // if we have a valid engine, perform the query if ($this->searchwp->is_valid_engine($args['engine'])) { // this filter is also applied in the SearchWP class search methods // TODO: should this be applied in both places? which? $sanitizeTerms = apply_filters('searchwp_sanitize_terms', true, $args['engine']); if (!is_bool($sanitizeTerms)) { $sanitizeTerms = true; } // whitelist search terms $pre_whitelist_terms = is_array($args['terms']) ? implode(' ', $args['terms']) : $args['terms']; $whitelisted_terms = $this->searchwp->extract_terms_using_pattern_whitelist($pre_whitelist_terms, false); // TODO: if $whitelisted_terms has matches with spaces, there will be dupes: do we need to loop through and remove? // store the original search query (e.g. logging) $pre_search_original_terms = ''; if (!empty($searchwp->original_query)) { $pre_search_original_terms = trim($searchwp->original_query); } elseif (!empty($args['terms'])) { // might have been instantiated directly, use the terms from the args $pre_search_original_terms = is_array($args['terms']) ? implode(' ', $args['terms']) : $args['terms']; $pre_search_original_terms = trim($pre_search_original_terms); } if ($sanitizeTerms) { $terms = $this->searchwp->sanitize_terms($args['terms']); } else { $terms = $args['terms']; do_action('searchwp_log', 'Opted out of internal sanitization'); } if (is_array($whitelisted_terms)) { $whitelisted_terms = array_filter(array_map('trim', $whitelisted_terms), 'strlen'); } if (is_array($terms)) { $terms = array_filter(array_map('trim', $terms), 'strlen'); $terms = array_merge($terms, $whitelisted_terms); } else { $terms .= ' ' . implode(' ', $whitelisted_terms); } // make sure the terms are unique, especially after whitelist matching if (is_array($terms)) { $terms = array_unique($terms); $terms = array_filter($terms, 'strlen'); } $engine = $args['engine']; // allow dev to customize post statuses are included $this->post_statuses = (array) apply_filters('searchwp_post_statuses', $this->post_statuses, $engine); foreach ($this->post_statuses as $post_status_key => $post_status_value) { $this->post_statuses[$post_status_key] = sanitize_key($post_status_value); } do_action('searchwp_log', '$terms = ' . var_export($terms, true)); if ('DESC' != strtoupper(apply_filters('searchwp_search_query_order', $args['order'])) && 'ASC' != strtoupper($args['order'])) { $args['order'] = 'DESC'; } // filter the terms just before querying $terms = apply_filters('searchwp_pre_search_terms', $terms, $engine); do_action('searchwp_log', 'searchwp_pre_search_terms $terms = ' . var_export($terms, true)); $this->terms = $terms; $this->engine = $engine; $this->settings = empty($searchwp) ? get_option(SEARCHWP_PREFIX . 'settings') : $searchwp->settings; $this->page = absint($args['page']); $this->postsPer = intval($args['posts_per_page']); $this->order = $args['order']; $this->load_posts = is_bool($args['load_posts']) ? $args['load_posts'] : true; $this->offset = isset($args['offset']) && !empty($args['offset']) ? absint($args['offset']) : 0; // if it's a native search we can piggyback default includes and excludes if (is_search()) { $this->set_default_include_and_exclude(); } // perform our query $this->posts = $this->query(); // log this if (!empty($pre_search_original_terms) && apply_filters('searchwp_log_search', true, $engine, $pre_search_original_terms, count($this->posts))) { $pre_search_original_terms = sanitize_text_field($pre_search_original_terms); $pre_search_original_terms = trim($pre_search_original_terms); // respect database schema if (200 < strlen($pre_search_original_terms)) { $pre_search_original_terms = substr($pre_search_original_terms, 0, 199); } if (!empty($pre_search_original_terms)) { /** @noinspection PhpUnusedLocalVariableInspection */ $log_result = $wpdb->insert($this->db_prefix . 'log', array('event' => 'search', 'query' => $pre_search_original_terms, 'hits' => count($this->posts), 'engine' => $engine, 'wpsearch' => 0), array('%s', '%s', '%d', '%s', '%d')); } } } }
function plugin_row() { if (!class_exists('SearchWP')) { return; } $searchwp = SearchWP::instance(); if (version_compare($searchwp->version, '2.0.3', '<')) { ?> <tr class="plugin-update-tr searchwp"> <td colspan="3" class="plugin-update"> <div class="update-message"> <?php _e('SearchWP Fuzzy Matches requires SearchWP 2.0.3 or greater', $searchwp->textDomain); ?> </div> </td> </tr> <?php } }
/** * Singleton * * @return SearchWP * @since 1.0 */ public static function instance() { if (!isset(self::$instance) && !self::$instance instanceof SearchWP) { // store background indexer request if (isset($_REQUEST['swpnonce'])) { searchwp_delete_option('indexnonce'); searchwp_add_option('indexnonce', sanitize_text_field($_REQUEST['swpnonce'])); } self::$instance = new SearchWP(); self::$instance->init(); // we want to purge a post from the index when comments are manipulated add_action('comment_post', array(self::$instance, 'purge_post_via_comment')); add_action('edit_comment', array(self::$instance, 'purge_post_via_comment')); add_action('trash_comment', array(self::$instance, 'purge_post_via_comment')); add_action('delete_comment', array(self::$instance, 'purge_post_via_comment')); add_action('delete_attachment', array(self::$instance, 'purge_post_via_edit'), 999); // purge a post from the index when a related term is deleted add_action('set_object_terms', array(self::$instance, 'purge_post_via_term'), 10, 6); // process the purge queue once everything is said and done add_action('shutdown', array(self::$instance, 'setup_purge_queue')); add_action('after_setup_theme', array(self::$instance, 'set_settings_cap')); } return self::$instance; }
function do_searchwp_search($query_args) { // limit the pool WP Job Manager works from by providing our own post__in if (!empty($this->query) && class_exists('SearchWP')) { // we'll do our own keyword search (added in 1.21.4) remove_filter('posts_clauses', 'get_job_listings_keyword_search'); remove_filter('posts_clauses', 'get_resumes_keyword_search'); // instantiate SearchWP $engine = SearchWP::instance(); // prevent pagination $query_args['posts_per_page'] = $this->posts_per_page; add_filter('searchwp_posts_per_page', array($this, 'posts_per_page')); // we only want post IDs add_filter('searchwp_load_posts', '__return_false'); // perform the search $results = array(0); // fallback default if ($this->page) { // this is a resume search $results = $engine->search($this->search_engine_name, $this->query, $this->page); } else { $results = $engine->search($this->search_engine_name, $this->query); } // there is a chance this hook is being used elsewhere so before we blatantly // override post__in let's make sure we intersect it // make sure if it's defined it's an array if (!empty($query_args['post__in'])) { // make sure it's an array $source = $query_args['post__in']; if (is_string($source)) { $source = explode(',', $source); } $source = array_map('trim', $source); $source = array_map('absint', $source); $source = array_unique($source); $query_args['post__in'] = $source; } // check to see if it's already being limited if (isset($query_args['post__in']) && is_array($query_args['post__in']) && count($query_args['post__in'])) { $query_args['post__in'] = array_intersect($query_args['post__in'], $results); // we may have (correctly) just zeroed out the results set // the radius limiter sets post__in to an array of results within that radius // but the keyword match may net zero results here, which is accurate if (empty($query_args['post__in'])) { $query_args['post__in'] = array(0); } } else { // post__in wasn't set so let's just set it $query_args['post__in'] = $results; // if it was empty, we want to be sure it's empty if (empty($query_args['post__in'])) { // no results, so force that $query_args['post__in'] = array(0); } } // sort the results by SearchWP relevance if (!empty($query_args['post__in'])) { $query_args['orderby'] = 'post__in'; } if (0 == count($results)) { // a search was submitted, but no results were found // so we need to force that by limiting post__in to // an impossible results set $query_args['post__in'] = array(0); } // if the developer really wants to, they can override the WP Job Manager restricted post_type if ($this->override_post_types) { // SearchWP's engine configuration will restrict this $query_args['post_type'] = 'any'; $query_args['post_status'] = 'any'; } } return $query_args; }
/** * Search through the Docs for a given term. * * @param string $original_term term to search for. * * @return array Array with post ID's */ public function search($original_term) { do_action('wpkb_search', $original_term); // use SearchWP if possible if (class_exists('SearchWP')) { $engine = \SearchWP::instance(); $posts = $engine->search('wpkb_search', $original_term); if (is_array($posts)) { return $posts; } return array(); } global $wpdb; // go for an easy escape if the original term is very short if (strlen($original_term) < 3) { return array(); } // start building SQL query string $params = array(); $string = ''; $string .= "SELECT wpp.id"; $string .= " FROM {$wpdb->posts} wpp"; $string .= " LEFT JOIN {$wpdb->term_relationships} wptr ON wpp.id = wptr.object_id"; $string .= " LEFT JOIN {$wpdb->term_taxonomy} wptt ON wptr.term_taxonomy_id = wptt.term_taxonomy_id"; $string .= " LEFT JOIN {$wpdb->terms} wpt ON wpt.term_id = wptt.term_id"; // only query post type doc $string .= " WHERE wpp.post_type = 'wpkb-article'"; // only query published docs $string .= " AND wpp.post_status = 'publish'"; // query each search word in post title, post content, docs keyword and docs category $string .= " AND ("; // query title & content $string .= " wpp.post_title LIKE %s OR wpp.post_content LIKE '%s'"; $params[] = '%%' . $original_term . '%%'; $params[] = '%%' . $original_term . '%%'; // query keywords $string .= " OR ( wptt.taxonomy = 'wpkb-keyword' AND wpt.name LIKE '%s' )"; $params[] = '%%' . $original_term . '%%'; // query category $string .= " OR ( wptt.taxonomy = 'wpkb-category' AND wpt.name LIKE '%s' )"; $params[] = '%%' . $original_term . '%%'; // close opened AND parenthesis $string .= " )"; // group by post id $string .= " GROUP BY wpp.id"; // prepare sql query string $query = $wpdb->prepare($string, $params); // execute query $results = $wpdb->get_results($query); if (!is_array($results) || count($results) === 0) { return array(); } $ids = array_map(function ($p) { return $p->id; }, $results); $posts = get_posts(array('post_type' => Plugin::POST_TYPE_NAME, 'post__in' => $ids)); return $posts; }
/** * Constructor * * @param array $args * @since 1.0 */ function __construct($args = array()) { global $wpdb, $searchwp; do_action('searchwp_log', 'SearchWPSearch __construct()'); $defaults = array('terms' => '', 'engine' => 'default', 'page' => 1, 'posts_per_page' => intval(get_option('posts_per_page')), 'order' => $this->order, 'load_posts' => true); $this->db_prefix = $wpdb->prefix . SEARCHWP_DBPREFIX; // process our arguments $args = wp_parse_args($args, $defaults); $this->searchwp = SearchWP::instance(); // instantiate our stemmer for later $this->stemmer = new SearchWPStemmer(); do_action('searchwp_log', '$args = ' . var_export($args, true)); // if we have a valid engine, perform the query if ($this->searchwp->isValidEngine($args['engine'])) { // this filter is also applied in the SearchWP class search methods // TODO: should this be applied in both places? which? $sanitizeTerms = apply_filters('searchwp_sanitize_terms', true, $args['engine']); if (!is_bool($sanitizeTerms)) { $sanitizeTerms = true; } // whitelist search terms $pre_whitelist_terms = is_array($args['terms']) ? implode(' ', $args['terms']) : ' ' . $args['terms'] . ' '; $whitelisted_terms = $this->searchwp->extract_terms_using_pattern_whitelist($pre_whitelist_terms, false); // TODO: if $whitelisted_terms has matches with spaces, there will be dupes: do we need to loop through and remove? if ($sanitizeTerms) { $terms = $this->searchwp->sanitizeTerms($args['terms']); } else { $terms = $args['terms']; do_action('searchwp_log', 'Opted out of internal sanitization'); } if (is_array($whitelisted_terms)) { $whitelisted_terms = array_filter(array_map('trim', $whitelisted_terms), 'strlen'); } if (is_array($terms)) { $terms = array_filter(array_map('trim', $terms), 'strlen'); $terms = array_merge($terms, $whitelisted_terms); } else { $terms .= ' ' . implode(' ', $whitelisted_terms); } // make sure the terms are unique, especially after whitelist matching if (is_array($terms)) { $terms = array_unique($terms); $terms = array_filter($terms, 'strlen'); } $engine = $args['engine']; // allow dev to customize post statuses are included $this->post_statuses = (array) apply_filters('searchwp_post_statuses', $this->post_statuses, $engine); foreach ($this->post_statuses as $post_status_key => $post_status_value) { $this->post_statuses[$post_status_key] = sanitize_key($post_status_value); } do_action('searchwp_log', '$terms = ' . var_export($terms, true)); if (strtoupper(apply_filters('searchwp_search_query_order', $args['order'])) != 'DESC' && strtoupper($args['order']) != 'ASC') { $args['order'] = 'DESC'; } // filter the terms just before querying $terms = apply_filters('searchwp_pre_search_terms', $terms, $engine); do_action('searchwp_log', 'searchwp_pre_search_terms $terms = ' . var_export($terms, true)); $this->terms = $terms; $this->engine = $engine; $this->settings = empty($searchwp) ? get_option(SEARCHWP_PREFIX . 'settings') : $searchwp->settings; $this->page = absint($args['page']); $this->postsPer = intval($args['posts_per_page']); $this->order = $args['order']; $this->load_posts = is_bool($args['load_posts']) ? $args['load_posts'] : true; // perform our query $this->posts = $this->query(); } }
/** * Callback for plugin row display */ function plugin_row() { $searchwp = SearchWP::instance(); if (version_compare($searchwp->version, '1.0.8', '<')) { ?> <tr class="plugin-update-tr searchwp"> <td colspan="3" class="plugin-update"> <div class="update-message"> <?php esc_html_e('SearchWP Term Archive Priority requires SearchWP 1.0.8 or greater', 'searchwp'); ?> </div> </td> </tr> <?php } }
function plugin_row() { if (!class_exists('SearchWP')) { ?> <tr class="plugin-update-tr searchwp"> <td colspan="3" class="plugin-update"> <div class="update-message"> <?php _e('SearchWP must be active to use this Extension'); ?> </div> </td> </tr> <?php } else { ?> <?php $searchwp = SearchWP::instance(); ?> <?php if (version_compare($searchwp->version, '1.3.3', '<')) { ?> <tr class="plugin-update-tr searchwp"> <td colspan="3" class="plugin-update"> <div class="update-message"> <?php _e('SearchWP Xpdf Integration requires SearchWP 1.3.3 or greater', 'searchwp'); ?> </div> </td> </tr> <?php } ?> <?php } }