@@ -50,6 +50,18 @@ class Rop_Posts_Selector_Model extends Rop_Model_Abstract {
5050 */
5151 private $ settings = array ();
5252
53+ /**
54+ * Per-account keyword filter override taken from the account post format.
55+ *
56+ * Null means "no per-account override" — the global keyword filter applies.
57+ * Set per call to select() so it never leaks between accounts.
58+ *
59+ * @since 9.3.7
60+ * @access private
61+ * @var array|null
62+ */
63+ private $ post_format_keyword_override = null ;
64+
5365 /**
5466 * Rop_Posts_Selector_Model constructor.
5567 *
@@ -344,6 +356,9 @@ public function select( $account_id = false ) {
344356 $ post_types = $ this ->build_post_types ();
345357 $ global_settings = new Rop_Global_Settings ();
346358
359+ // Reset per-account overrides so they never leak between accounts.
360+ $ this ->post_format_keyword_override = null ;
361+
347362 // Taxonomy: Post Format new option
348363 if ( $ global_settings ->license_type () > 0 && $ global_settings ->license_type () !== 7 && ! empty ( $ account_id ) ) {
349364 $ parts = explode ( '_ ' , $ account_id );
@@ -369,6 +384,17 @@ public function select( $account_id = false ) {
369384 }
370385
371386 $ tax_queries = $ this ->build_tax_query ( $ custom_data );
387+
388+ // Per-account keyword filter (overrides the global one when set).
389+ if ( isset ( $ post_format ['keyword_filter ' ] ) && '' !== trim ( (string ) $ post_format ['keyword_filter ' ] ) ) {
390+ $ keywords = Rop_Settings_Model::parse_keyword_string ( $ post_format ['keyword_filter ' ] );
391+ if ( ! empty ( $ keywords ) ) {
392+ $ this ->post_format_keyword_override = array (
393+ 'keywords ' => $ keywords ,
394+ 'exclude ' => isset ( $ post_format ['exclude_keywords ' ] ) ? filter_var ( $ post_format ['exclude_keywords ' ], FILTER_VALIDATE_BOOLEAN ) : true ,
395+ );
396+ }
397+ }
372398 } else {
373399 $ tax_queries = $ this ->build_tax_query ();
374400 }
@@ -459,8 +485,35 @@ private function query_results( $account_id, $post_types, $tax_queries, $exclude
459485 $ exclude = array ();
460486 }
461487
462- $ args = $ this ->build_query_args ( $ post_types , $ tax_queries , $ exclude );
488+ $ args = $ this ->build_query_args ( $ post_types , $ tax_queries , $ exclude );
489+
490+ /**
491+ * Optional keyword filtering on post title/content. Only active when the
492+ * user has entered keywords; otherwise the query is untouched. A per-account
493+ * (post format) filter takes precedence over the global one. The filter is
494+ * added only around this query and guarded by a custom query var so no other
495+ * query is ever affected.
496+ */
497+ if ( is_array ( $ this ->post_format_keyword_override ) ) {
498+ $ keywords = $ this ->post_format_keyword_override ['keywords ' ];
499+ $ exclude_keyword = ! empty ( $ this ->post_format_keyword_override ['exclude ' ] );
500+ } else {
501+ $ keywords = $ this ->settings ->get_keyword_filter ();
502+ $ exclude_keyword = $ this ->settings ->get_exclude_keywords ();
503+ }
504+ if ( ! empty ( $ keywords ) ) {
505+ $ args ['rop_keyword_filter ' ] = array (
506+ 'keywords ' => $ keywords ,
507+ 'exclude ' => $ exclude_keyword ,
508+ );
509+ add_filter ( 'posts_where ' , array ( $ this , 'filter_keyword_where ' ), 10 , 2 );
510+ }
511+
463512 $ query = new WP_Query ( $ args );
513+
514+ if ( ! empty ( $ keywords ) ) {
515+ remove_filter ( 'posts_where ' , array ( $ this , 'filter_keyword_where ' ), 10 );
516+ }
464517 // echo $query->request;
465518 $ posts = $ query ->posts ;
466519
@@ -594,6 +647,46 @@ private function build_query_args( $post_types, $tax_queries, $exclude ) {
594647 return $ args ;
595648 }
596649
650+ /**
651+ * Append a keyword condition to the posts query WHERE clause.
652+ *
653+ * Hooked on `posts_where` only while the selector query runs. It acts solely
654+ * on queries carrying the `rop_keyword_filter` query var, matching the
655+ * keywords against the post title or content. In exclude mode posts matching
656+ * any keyword are removed; in include mode only matching posts are kept.
657+ *
658+ * @since 9.3.7
659+ * @access public
660+ *
661+ * @param string $where The WHERE clause of the query.
662+ * @param WP_Query $query The current WP_Query instance.
663+ *
664+ * @return string
665+ */
666+ public function filter_keyword_where ( $ where , $ query ) {
667+ global $ wpdb ;
668+
669+ $ config = $ query ->get ( 'rop_keyword_filter ' );
670+ if ( empty ( $ config ) || empty ( $ config ['keywords ' ] ) || ! is_array ( $ config ['keywords ' ] ) ) {
671+ return $ where ;
672+ }
673+
674+ $ clauses = array ();
675+ foreach ( $ config ['keywords ' ] as $ keyword ) {
676+ $ like = '% ' . $ wpdb ->esc_like ( $ keyword ) . '% ' ;
677+ $ clauses [] = $ wpdb ->prepare ( "( {$ wpdb ->posts }.post_title LIKE %s OR {$ wpdb ->posts }.post_content LIKE %s) " , $ like , $ like );
678+ }
679+
680+ if ( empty ( $ clauses ) ) {
681+ return $ where ;
682+ }
683+
684+ $ group = '( ' . implode ( ' OR ' , $ clauses ) . ' ) ' ;
685+ $ where .= ! empty ( $ config ['exclude ' ] ) ? " AND NOT {$ group }" : " AND {$ group }" ;
686+
687+ return $ where ;
688+ }
689+
597690 /**
598691 * Method to determine if the buffer is empty or not.
599692 *
0 commit comments