@@ -77,6 +77,9 @@ public function __construct() {
7777 Hook_Registry::add_filter ( self ::$ prefix . '_settings_sanitize ' , array ( $ this , 'change_settings_on_save ' ), 99 );
7878 Hook_Registry::add_filter ( self ::$ prefix . '_after_setting_output ' , array ( $ this , 'after_setting_output ' ), 10 , 2 );
7979 Hook_Registry::add_action ( self ::$ prefix . '_settings_form_buttons ' , array ( $ this , 'add_wizard_button ' ) );
80+
81+ Hook_Registry::add_action ( 'wp_ajax_nopriv_ ' . self ::$ prefix . '_taxonomy_search_tom_select ' , array ( __CLASS__ , 'taxonomy_search_tom_select ' ) );
82+ Hook_Registry::add_action ( 'wp_ajax_ ' . self ::$ prefix . '_taxonomy_search_tom_select ' , array ( __CLASS__ , 'taxonomy_search_tom_select ' ) );
8083 }
8184
8285 /**
@@ -603,14 +606,12 @@ public static function settings_search() {
603606 'exclude_cat_slugs ' => array (
604607 'id ' => 'exclude_cat_slugs ' ,
605608 'name ' => esc_html__ ( 'Exclude Categories ' , 'better-search ' ),
606- 'desc ' => esc_html__ ( 'Comma separated list of category slugs. The field above has an autocomplete so simply start typing in the starting letters and it will prompt you with options. Does not support custom taxonomies . ' , 'better-search ' ),
609+ 'desc ' => esc_html__ ( 'The field above has an autocomplete. Start typing in the starting letters, and it will prompt you with options. This field requires a specific format as displayed by the autocomplete . ' , 'better-search ' ),
607610 'type ' => 'csv ' ,
608611 'default ' => '' ,
609612 'size ' => 'large ' ,
610- 'field_class ' => 'category_autocomplete ' ,
611- 'field_attributes ' => array (
612- 'data-wp-taxonomy ' => 'category ' ,
613- ),
613+ 'field_class ' => 'ts_autocomplete ' ,
614+ 'field_attributes ' => self ::get_taxonomy_search_field_attributes ( 'category ' ),
614615 ),
615616 'exclude_categories ' => array (
616617 'id ' => 'exclude_categories ' ,
@@ -1227,4 +1228,164 @@ public function add_wizard_button() {
12271228 esc_html__ ( 'Start Settings Wizard ' , 'better-search ' )
12281229 );
12291230 }
1231+
1232+ /**
1233+ * AJAX handler for Tom Select taxonomy search.
1234+ *
1235+ * @since 4.2.0
1236+ *
1237+ * @return void
1238+ */
1239+ public static function taxonomy_search_tom_select () {
1240+ // Verify nonce.
1241+ if ( ! isset ( $ _REQUEST ['nonce ' ] ) ) {
1242+ wp_send_json_error ( array ( 'message ' => 'Missing nonce ' ) );
1243+ }
1244+
1245+ $ nonce_valid = wp_verify_nonce ( sanitize_key ( $ _REQUEST ['nonce ' ] ), self ::$ prefix . '_taxonomy_search_tom_select ' );
1246+
1247+ if ( ! $ nonce_valid ) {
1248+ wp_send_json_error (
1249+ array (
1250+ 'message ' => 'Invalid nonce ' ,
1251+ 'received_nonce ' => sanitize_key ( $ _REQUEST ['nonce ' ] ),
1252+ 'expected_action ' => self ::$ prefix . '_taxonomy_search_tom_select ' ,
1253+ )
1254+ );
1255+ }
1256+
1257+ if ( ! isset ( $ _REQUEST ['endpoint ' ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1258+ wp_send_json_error ( 'Missing endpoint ' );
1259+ }
1260+
1261+ $ endpoint = sanitize_key ( $ _REQUEST ['endpoint ' ] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1262+
1263+ $ search_term = isset ( $ _REQUEST ['q ' ] ) ? sanitize_text_field ( wp_unslash ( $ _REQUEST ['q ' ] ) ) : '' ; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1264+
1265+ $ comma = _x ( ', ' , 'tag delimiter ' , 'better-search ' );
1266+ if ( ', ' !== $ comma ) {
1267+ $ search_term = str_replace ( $ comma , ', ' , $ search_term );
1268+ }
1269+ if ( false !== strpos ( $ search_term , ', ' ) ) {
1270+ $ search_term = explode ( ', ' , $ search_term );
1271+ $ search_term = $ search_term [ count ( $ search_term ) - 1 ];
1272+ }
1273+ $ search_term = trim ( $ search_term );
1274+
1275+ if ( 'public_taxonomies ' === $ endpoint ) {
1276+ $ taxonomies = (array ) get_taxonomies ( array ( 'public ' => true ), 'objects ' );
1277+ $ taxonomy = array ();
1278+ $ tax = null ;
1279+
1280+ foreach ( $ taxonomies as $ taxonomy_name => $ taxonomy_object ) {
1281+ if ( ! is_string ( $ taxonomy_name ) || '' === $ taxonomy_name ) {
1282+ continue ;
1283+ }
1284+
1285+ if ( empty ( $ taxonomy_object ->cap ->assign_terms ) ) {
1286+ continue ;
1287+ }
1288+
1289+ if ( ! current_user_can ( $ taxonomy_object ->cap ->assign_terms ) ) {
1290+ continue ;
1291+ }
1292+
1293+ $ taxonomy [] = $ taxonomy_name ;
1294+ }
1295+
1296+ if ( empty ( $ taxonomy ) ) {
1297+ wp_send_json_success ( array () );
1298+ }
1299+
1300+ $ tax = get_taxonomy ( $ taxonomy [0 ] );
1301+ } else {
1302+ $ taxonomy = $ endpoint ;
1303+ $ tax = get_taxonomy ( $ taxonomy );
1304+
1305+ if ( ! $ tax ) {
1306+ wp_send_json_error ( 'Invalid taxonomy ' );
1307+ }
1308+
1309+ if ( ! current_user_can ( $ tax ->cap ->assign_terms ) ) {
1310+ wp_send_json_error ( 'Insufficient permissions ' );
1311+ }
1312+ }
1313+
1314+ /** This filter has been defined in /wp-admin/includes/ajax-actions.php */
1315+ $ term_search_min_chars = (int ) apply_filters ( 'term_search_min_chars ' , 2 , $ tax , $ search_term );
1316+
1317+ /*
1318+ * Require $term_search_min_chars chars for matching (default: 2)
1319+ * ensure it's a non-negative, non-zero integer.
1320+ */
1321+ if ( ( 0 === $ term_search_min_chars ) || ( strlen ( $ search_term ) < $ term_search_min_chars ) ) {
1322+ wp_send_json_success ( array () );
1323+ }
1324+
1325+ $ terms = get_terms (
1326+ array (
1327+ 'taxonomy ' => $ taxonomy ,
1328+ 'name__like ' => $ search_term ,
1329+ 'hide_empty ' => false ,
1330+ )
1331+ );
1332+
1333+ $ results = array ();
1334+ foreach ( (array ) $ terms as $ term ) {
1335+ $ formatted_string = "{$ term ->name } ( {$ term ->taxonomy }: {$ term ->term_taxonomy_id }) " ;
1336+ $ results [] = array (
1337+ 'value ' => $ formatted_string ,
1338+ 'text ' => $ term ->name ,
1339+ );
1340+ }
1341+
1342+ wp_send_json_success ( $ results );
1343+ }
1344+
1345+ /**
1346+ * Get field attributes for Tom Select taxonomy search fields.
1347+ *
1348+ * @since 4.2.0
1349+ *
1350+ * @param string $taxonomy The taxonomy to search.
1351+ * @param array $ts_config Optional Tom Select configuration.
1352+ * @return array Field attributes array.
1353+ */
1354+ private static function get_taxonomy_search_field_attributes ( $ taxonomy , $ ts_config = array () ) {
1355+ $ attributes = array (
1356+ 'data-wp-prefix ' => strtoupper ( (string ) self ::$ prefix ),
1357+ 'data-wp-action ' => self ::$ prefix . '_taxonomy_search_tom_select ' ,
1358+ 'data-wp-nonce ' => wp_create_nonce ( self ::$ prefix . '_taxonomy_search_tom_select ' ),
1359+ 'data-wp-endpoint ' => $ taxonomy ,
1360+ );
1361+
1362+ if ( ! empty ( $ ts_config ) ) {
1363+ $ attributes ['data-wp-ts-config ' ] = wp_json_encode ( $ ts_config );
1364+ }
1365+
1366+ return $ attributes ;
1367+ }
1368+
1369+ /**
1370+ * Get field attributes for Tom Select meta key search fields.
1371+ *
1372+ * @since 4.2.0
1373+ *
1374+ * @param array $ts_config Optional Tom Select configuration.
1375+ * @return array Field attributes array.
1376+ */
1377+ private static function get_meta_keys_search_field_attributes ( $ ts_config = array () ) {
1378+ $ attributes = array (
1379+ 'data-wp-prefix ' => strtoupper ( (string ) self ::$ prefix ),
1380+ 'data-wp-action ' => self ::$ prefix . '_taxonomy_search_tom_select ' ,
1381+ 'data-wp-nonce ' => wp_create_nonce ( self ::$ prefix . '_taxonomy_search_tom_select ' ),
1382+ 'data-wp-endpoint ' => 'meta_keys ' ,
1383+ );
1384+
1385+ if ( ! empty ( $ ts_config ) ) {
1386+ $ attributes ['data-ts-config ' ] = wp_json_encode ( $ ts_config );
1387+ }
1388+
1389+ return $ attributes ;
1390+ }
12301391}
0 commit comments