<?php
namespace FindStr;
/**
* Class Fields
*
* This class is responsible for managing the fields in the FindStr application.
* It provides methods for getting, updating, and deleting fields.
* It also provides methods for getting field types and data sources.
*/
class Fields {
public array $field_types = array();
public string $field_save_file = 'findstr/fields.json';
public array $data_sources = array();
public function __construct() {
//default fields type
$this->field_types = array(
'search' => array(
'label' => __( 'Search', 'findstr' ),
'type' => 'search',
),
'checkbox' => array(
'label' => _x( 'Checkbox', 'field type', 'findstr' ),
'type' => 'checkbox',
),
'dropdown' => array(
'label' => _x( 'Dropdown', 'field type', 'findstr' ),
'type' => 'select',
),
'toggle' => array(
'label' => _x( 'Toggle', 'field type', 'findstr' ),
'type' => 'toggle',
),
'datepicker' => array(
'label' => _x( 'Date Picker', 'field type', 'findstr' ),
'type' => 'datepicker',
),
'results-count' => array(
'label' => _x( 'Results Count', 'field type', 'findstr' ),
'type' => 'results-count',
),
'sort' => array(
'label' => _x( 'Sort', 'field type', 'findstr' ),
'type' => 'sort',
),
'pagination' => array(
'label' => _x( 'Pagination', 'field type', 'findstr' ),
'type' => 'pagination',
),
'loadMore' => array(
'label' => _x( 'Load More', 'field type', 'findstr' ),
'type' => 'loadMore',
),
'selectedFilters' => array(
'label' => _x( 'Selected Filters', 'field type', 'findstr' ),
'type' => 'selectedFilters',
),
'reset' => array(
'label' => _x( 'Reset', 'field type', 'findstr' ),
'type' => 'reset',
),
);
/**
* Filter the path where the fields are saved
* This filter allows you to change the path where the fields are saved.
*
* @hook findstr_fields_save_path
*
* @param {string} $path
*
* @return {string} $path
*/
$this->field_save_file = apply_filters( 'findstr_fields_save_path', trailingslashit( get_stylesheet_directory() ) . $this->field_save_file );
}
/**
* Get all fields
*
* @return array
*/
public function get_fields(): array {
if ( is_file( $this->field_save_file ) ) {
$fields_json = file_get_contents( $this->field_save_file, true );
if ( ! empty( $fields_json ) ) {
$fields = json_decode( $fields_json, true );
if ( ! empty( $fields ) ) {
/**
* This filter allows you to modify the fields before they are returned.
*
* @hook findstr_get_fields
*
* @param {array} $fields
*
* @return {array} $fields
*
*/
return (array) apply_filters( 'findstr_get_fields', $fields );
}
}
}
$fields = (array) get_option( 'findstr_fields', array() );
/**
* @see findstr_get_fields hook
*/
return (array) apply_filters( 'findstr_get_fields', $fields );
}
public function update_fields( $fields ) {
/**
* This filter allows you to modify the fields before they are saved.
*
* @hook findstr_update_fields
*
* @param {array} $fields
*
* @return {array} $fields
*/
$fields = apply_filters( 'findstr_update_fields', $fields );
$fields_json = wp_json_encode( $fields, JSON_PRETTY_PRINT );
$file_stored = ( new Settings() )->is_file_stored();
if ( $file_stored ) {
Helpers::file_put_content( $this->field_save_file, $fields_json );
}
update_option( 'findstr_fields', $fields );
return $fields;
}
public function update_field( $field ) {
if ( empty( $field['id'] ) ) {
return false;
}
$fields = $this->get_fields();
//remove fields with the fieldSlug property starting with and underscore,
//because they are reserved for internal use
$fields = array_filter(
$fields,
function ( $f ) {
return 0 !== strpos( $f['fieldSlug'], '_' );
}
);
$fields[ $field['id'] ] = $field;
$this->update_fields( $fields );
return $fields;
}
public function get_field( string $slug ) {
$fields = $this->get_fields();
if ( empty( $fields ) ) {
return false;
}
$key = array_search( $slug, array_column( $fields, 'fieldSlug', 'id' ), true );
if ( ! empty( $key ) ) {
$field = $fields[ $key ];
return new Field( $field );
}
return false;
}
public function delete_field( $field_id ) {
if ( empty( $field_id ) ) {
return false;
}
$fields = $this->get_fields();
unset( $fields[ $field_id ] );
$this->update_fields( $fields );
return $fields;
}
/**
* Get field types
*
* @return array
*/
public function get_field_types(): array {
/**
* FindStr field types
*
* This filter allows you to add or remove field types from the FindStr plugin.
*
* @hook findstr_field_types
*
* @param {array} $field_types
*
* @returns {array} $field_types
*
*/
return apply_filters( 'findstr_field_types', $this->field_types );
}
public function get_data_sources( $post_types = array() ): array {
global $wpdb;
if ( empty( $post_types ) ) {
$settings = new Settings();
$post_types = $settings->get( 'postTypes' );
}
// At this point, $post_types is an array of post types.
// It's empty if no post types are selected in the settings.
if ( empty( $post_types ) ) {
return array();
}
if ( ! is_array( $post_types ) ) {
$post_types = array( $post_types );
}
if ( ! empty( $this->data_sources ) ) {
$sources = $this->data_sources;
} else {
$sources = array(
'posts' => array(
'label' => __( 'Posts', 'findstr' ),
'options' => array(
array(
'value' => 'post_title',
'label' => __( 'Post Title', 'findstr' ),
),
array(
'value' => 'post_content',
'label' => __( 'Post Content', 'findstr' ),
),
array(
'value' => 'post_type',
'label' => __( 'Post Type', 'findstr' ),
),
array(
'value' => 'post_date',
'label' => __( 'Post Date', 'findstr' ),
),
array(
'value' => 'post_modified',
'label' => __( 'Post Modified', 'findstr' ),
),
array(
'value' => 'post_author',
'label' => __( 'Post Author', 'findstr' ),
),
array(
'value' => 'post_parent',
'label' => __( 'Post Parent', 'findstr' ),
),
),
'weight' => 10,
),
'taxonomies' => array(
'label' => __( 'Taxonomies', 'findstr' ),
'options' => array(),
'weight' => 20,
),
'custom_fields' => array(
'label' => __( 'Custom Fields', 'findstr' ),
'options' => array(),
'weight' => 30,
),
);
if ( function_exists( 'acf_get_field_groups' ) ) {
$sources['acf_fields'] = array(
'label' => __( 'Advanced Custom Fields', 'findstr' ),
'options' => array(),
'weight' => 40,
);
}
// Get taxonomies
foreach ( $post_types as $post_type ) {
$taxonomies = get_object_taxonomies( $post_type, 'object' );
foreach ( $taxonomies as $tax ) {
if ( true === $tax->publicly_queryable ) {
$sources['taxonomies']['options'][] = array(
'value' => 'tax/' . $tax->name,
'label' => $tax->labels->name . ' (' . $tax->name . ')',
);
}
}
}
//ninja trick to remove duplicates
$sources['taxonomies']['options'] = array_values( array_unique( $sources['taxonomies']['options'], SORT_REGULAR ) );
//Get custom fields
$meta_keys = get_transient( 'findstr_meta_keys' );
if ( empty( $meta_keys ) ) {
$meta_keys = $wpdb->get_col(
$wpdb->prepare(
"SELECT DISTINCT pm.meta_key
FROM {$wpdb->postmeta} pm
LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id
WHERE p.post_type IN ( " . implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ) . ' )
AND pm.meta_key NOT LIKE %s
ORDER BY pm.meta_key',
array_merge( $post_types, array( '\_%' ) )
)
);
set_transient( 'findstr_meta_keys', $meta_keys, 4 * HOUR_IN_SECONDS );
}
/**
* Filter to exclude custom fields.
* This filter allows you to exclude specified data sources from UI.
*
* @hook findstr_excluded_custom_fields
*
* @param {array} $excluded_fields
*
* @return {array} $excluded_fields
*
*/
$excluded_fields = apply_filters(
'findstr_excluded_custom_fields',
array(
'_edit_last',
'_edit_lock',
)
);
$custom_fields = array_diff( $meta_keys, $excluded_fields );
foreach ( $custom_fields as $cf ) {
if ( 0 !== strpos( $cf, '_oembed_' ) ) {
$sources['custom_fields']['options'][] = array(
'value' => 'cf/' . $cf,
'label' => $cf,
);
}
}
//get acf fields
if ( function_exists( 'acf_get_field_groups' ) ) {
$acf_groups = acf_get_field_groups();
$acf_fields = array();
foreach ( $acf_groups as $group ) {
$fields = acf_get_fields( $group['key'] );
foreach ( $fields as $field ) {
$sources['acf_fields']['options'][] = array(
'value' => 'acf/' . $field['name'],
'label' => $group['title'] . ' - ' . $field['label'] . ' (' . $field['name'] . ')',
);
}
}
}
$this->data_sources = $sources;
}
/**
* This filter allows you to modify the data sources before they are returned.
*
* @hook findstr_fields_sources
*
* @param {array} $sources
* @param {array} $post_types
*
* @return {array} $sources
*/
$sources = apply_filters( 'findstr_fields_sources', $sources, $post_types );
uasort( $sources, array( $this, 'sort_by_weight' ) );
return $sources;
}
public function sort_by_weight( $a, $b ): int {
$a['weight'] = $a['weight'] ?? 10;
$b['weight'] = $b['weight'] ?? 10;
if ( $a['weight'] === $b['weight'] ) {
return 0;
}
return ( $a['weight'] < $b['weight'] ) ? - 1 : 1;
}
public static function get_field_source_name( $field ) {
if ( ! empty( $field['options']['sourceName'] ) ) {
$field_source = explode( '/', $field['options']['sourceName'] );
return end( $field_source );
}
return false;
}
}