480 lines
15 KiB
PHP
480 lines
15 KiB
PHP
<?php
|
|
/**
|
|
* Extension: Base fields.
|
|
*
|
|
* @since 5.1.0
|
|
*
|
|
* @package wsal
|
|
* @subpackage entities
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace WSAL\Entities;
|
|
|
|
use WSAL\MainWP\MainWP_Addon;
|
|
use WSAL\MainWP\MainWP_Helper;
|
|
use WSAL\Entities\Metadata_Entity;
|
|
use WSAL\Entities\Occurrences_Entity;
|
|
use WSAL\Helpers\DateTime_Formatter_Helper;
|
|
|
|
// Exit if accessed directly.
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
if ( ! class_exists( '\WSAL\Entities\Base_Fields' ) ) {
|
|
/**
|
|
* Provides fields searching functionality.
|
|
*
|
|
* @since 5.0.0
|
|
*/
|
|
class Base_Fields {
|
|
/**
|
|
* Filed must have:
|
|
* - unique name of field (no spaces)
|
|
* - DB relation (which DB field represents the field)
|
|
* - type
|
|
* - validation
|
|
*/
|
|
|
|
/**
|
|
* Mapper method - maps all the fields for searching to the corresponding fields (and how to extract value if they are not directly callable from the occurrence table)
|
|
*
|
|
* @return array
|
|
*
|
|
* @since 5.0.0
|
|
*/
|
|
public static function prepare_all_fields(): array {
|
|
|
|
// Collects the default table fields.
|
|
$fields = Occurrences_Entity::get_fields();
|
|
|
|
$table_meta = Metadata_Entity::get_table_name();
|
|
|
|
/**
|
|
* Logic for searching in the meta table for some values.
|
|
*
|
|
* @param sql_prefix - The SQL prefix to search for given values
|
|
* @param sub_sql_string - The SQL part of the query used to search for specific values (how)
|
|
* @param sql_suffix - The SQL suffix for the search query
|
|
* @param column_name - Which column from the occurrences table to map the results to
|
|
*/
|
|
$fields_subs = array(
|
|
'post_title' => array(
|
|
'sql_prefix' => 'SELECT occurrence_id FROM ' . $table_meta . ' as meta WHERE meta.name=\'PostTitle\' AND ( ',
|
|
'sub_sql_string' => "( (meta.value LIKE '%s') > 0 )",
|
|
'sql_suffix' => ' )',
|
|
'column_name' => 'occurrence_id',
|
|
),
|
|
);
|
|
|
|
/**
|
|
* All the user fields logic for collecting
|
|
* That differs because the users can be searched using mails, names, roles etc...
|
|
*
|
|
* With this we are mapping the logic for extraction the user IDs using different user searc criteria
|
|
*
|
|
* @param call - Which method (and class) to call with the parameters.
|
|
* @param field_name - The name of the field to extract data from.
|
|
* @param in_table - Marks what to use for searching in the occurrences table (they are currently the same)
|
|
*/
|
|
$user_fields = array(
|
|
'user_first_name' => array(
|
|
'call' => array( self::class, 'users_search' ),
|
|
'field_name' => 'first_name',
|
|
'in_table' => array(
|
|
'user_id' => 'ID',
|
|
'username' => 'user_login',
|
|
),
|
|
),
|
|
'user_last_name' => array(
|
|
'call' => array( self::class, 'users_search' ),
|
|
'field_name' => 'last_name',
|
|
'in_table' => array(
|
|
'user_id' => 'ID',
|
|
'username' => 'user_login',
|
|
),
|
|
),
|
|
'user_email' => array(
|
|
'call' => array( self::class, 'users_search' ),
|
|
'field_name' => 'user_email',
|
|
'in_table' => array(
|
|
'user_id' => 'ID',
|
|
'username' => 'user_login',
|
|
),
|
|
),
|
|
'user_role' => array(
|
|
'call' => array( self::class, 'users_search' ),
|
|
'extract' => 'role',
|
|
'in_table' => array(
|
|
'user_id' => 'ID',
|
|
'username' => 'user_login',
|
|
),
|
|
),
|
|
'user_id' => array(
|
|
'call' => array( self::class, 'users_search' ),
|
|
'field_name' => 'ID',
|
|
'in_table' => array(
|
|
'user_id' => 'ID',
|
|
'username' => 'user_login',
|
|
),
|
|
),
|
|
);
|
|
|
|
$fields_aliases = array(
|
|
'post_title' => array( 'post_name' ),
|
|
);
|
|
|
|
foreach ( array_keys( $fields_subs ) as $key ) {
|
|
if ( isset( $fields_aliases[ $key ] ) ) {
|
|
foreach ( $fields_aliases[ $key ] as $alias ) {
|
|
$fields_subs[ $alias ] = $fields_subs[ $key ];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Dates.
|
|
$dates = array(
|
|
'start_date' => 'date',
|
|
'end_date' => 'date',
|
|
);
|
|
|
|
return \array_merge( $fields_subs, $fields, $dates, $user_fields );
|
|
}
|
|
|
|
/**
|
|
* Parses the given search string (human readable) and returns the WHERE SQL statement based on the given string logic. To construct this use "field_name" followed by ":" and then the value (no spaces are supported) if there is a space in the string use '"'. For exclusions use "-" before the field name.
|
|
*
|
|
* @param string $search_string - Example: alert_id:1000 post_name:"something" user_id:1 .
|
|
*
|
|
* @return string
|
|
*
|
|
* @since 5.0.0
|
|
*/
|
|
public static function string_to_search( string $search_string ): string {
|
|
/*
|
|
* alert_id:1000 post_name:"something" user_id:1
|
|
*/
|
|
|
|
// There is nothing to parse - bounce.
|
|
if ( ! \strpos( $search_string, ':' ) ) {
|
|
return $search_string;
|
|
}
|
|
|
|
$fields_values = array();
|
|
|
|
$where_sql = '';
|
|
|
|
$occurrences_ids_to_search = array();
|
|
$occurrences_ids_to_exclude = array();
|
|
|
|
// Remove trailing and leading spaces.
|
|
$string = trim( $search_string );
|
|
|
|
$raw_fields = \explode( ' ', $string );
|
|
|
|
foreach ( $raw_fields as $field ) {
|
|
if ( empty( $field ) || ! \strpos( $field, ':' ) ) {
|
|
continue;
|
|
}
|
|
list($field_key, $field_value) = \explode( ':', trim( $field ) );
|
|
|
|
$exc_flag = false;
|
|
|
|
if ( false !== \strpos( $field_key, '-' ) ) {
|
|
$exc_flag = true;
|
|
$field_key = ltrim( $field_key, $field_key[0] );
|
|
}
|
|
|
|
if ( '' !== trim( $field_value ) ) {
|
|
if ( isset( $fields_values[ $field_key ] ) ) {
|
|
if ( $exc_flag ) {
|
|
$fields_values[ $field_key ]['exc'][] = $field_value;
|
|
} else {
|
|
$fields_values[ $field_key ][] = $field_value;
|
|
}
|
|
} else {
|
|
$fields_values[ $field_key ] = array();
|
|
if ( $exc_flag ) {
|
|
$fields_values[ $field_key ]['exc'][] = $field_value;
|
|
} else {
|
|
$fields_values[ $field_key ][] = $field_value;
|
|
}
|
|
}
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
$get_sub_fields = self::prepare_all_fields();
|
|
|
|
foreach ( $fields_values as $filed_name => $filed_values ) {
|
|
if ( isset( $get_sub_fields[ $filed_name ] ) && isset( $get_sub_fields[ $filed_name ]['sql_prefix'] ) ) {
|
|
self::meta_search( $get_sub_fields[ $filed_name ], $filed_values, $occurrences_ids_to_search, $occurrences_ids_to_exclude );
|
|
}
|
|
|
|
if ( isset( $get_sub_fields[ $filed_name ] ) && isset( $get_sub_fields[ $filed_name ]['call'] ) ) {
|
|
$where_sql .= \call_user_func( $get_sub_fields[ $filed_name ]['call'], $get_sub_fields[ $filed_name ], $filed_values ) . ' AND ';
|
|
|
|
}
|
|
|
|
if ( isset( $get_sub_fields[ $filed_name ] ) && ( ! isset( $get_sub_fields[ $filed_name ]['call'] ) && ! isset( $get_sub_fields[ $filed_name ]['sql_prefix'] ) ) ) {
|
|
$where_sql .= self::direct_field_call( (string) $filed_name, $filed_values ) . ' AND ';
|
|
}
|
|
}
|
|
|
|
$where_sql = \rtrim( $where_sql, ' AND ' );
|
|
|
|
return $where_sql;
|
|
}
|
|
|
|
/**
|
|
* Special class method for searching for users based on different criteria - user first name, user roles, etc...
|
|
*
|
|
* @param array $search_keys - Keys (from field mapping logic @see prepare_all_fields method) giving the logic of how to search for user based on specific field.
|
|
* @param array $search_values - Values collected from the search string to search for.
|
|
*
|
|
* @return string
|
|
*
|
|
* @since 5.0.0
|
|
*/
|
|
public static function users_search( array $search_keys, array $search_values ) {
|
|
$exclude = false;
|
|
|
|
if ( isset( $search_values['exc'] ) ) {
|
|
$exclude = true;
|
|
$search_user_values = $search_values['exc'];
|
|
} else {
|
|
$search_user_values = $search_values;
|
|
}
|
|
|
|
if ( isset( $search_keys['field_name'] ) ) {
|
|
$users = array();
|
|
|
|
$exclude = false;
|
|
|
|
if ( isset( $search_values['exc'] ) ) {
|
|
$exclude = true;
|
|
$search_user_values = $search_values['exc'];
|
|
} else {
|
|
$search_user_values = $search_values;
|
|
}
|
|
|
|
$args = array(
|
|
'blog_id' => 0,
|
|
'meta_query' => array(
|
|
array(
|
|
'key' => $search_keys['field_name'],
|
|
'values' => $search_user_values,
|
|
'compare' => 'REGEXP',
|
|
),
|
|
),
|
|
);
|
|
|
|
if ( 'ID' === $search_keys['field_name'] ) {
|
|
unset( $args['meta_query'] );
|
|
$args['include'] = $search_user_values;
|
|
}
|
|
|
|
if ( 'user_email' === $search_keys['field_name'] ) {
|
|
global $wpdb;
|
|
$search_str = '';
|
|
$search_vals = array();
|
|
foreach ( $search_user_values as $value ) {
|
|
$search_str .= ' ' . $search_keys['field_name'] . ' LIKE %s OR ';
|
|
$search_vals[] = $value;
|
|
}
|
|
$search_str = \rtrim( $search_str, ' OR ' );
|
|
$users_array = $wpdb->get_results(
|
|
$wpdb->prepare( 'SELECT * FROM ' . $wpdb->users . ' WHERE ' . $search_str, $search_vals ) // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
|
); // This will return list of users with matching email domain.
|
|
} else {
|
|
$users_array = \get_users( $args );
|
|
}
|
|
|
|
if ( MainWP_Addon::check_mainwp_plugin_active() ) {
|
|
|
|
$mainwp_users = MainWP_Helper::find_users_by( array( $search_keys['field_name'] ), $search_user_values, false );
|
|
|
|
$users_array = array_merge( $users_array, $mainwp_users );
|
|
}
|
|
|
|
foreach ( $users_array as $user ) {
|
|
if ( isset( $search_keys['in_table'] ) ) {
|
|
$arr = array();
|
|
foreach ( $search_keys['in_table'] as $field_name => $object_name ) {
|
|
$arr[ $field_name ] = "'" . $user->$object_name . "'";
|
|
}
|
|
|
|
$users[] = $arr;
|
|
} else {
|
|
$users[] = array( 'user_id' => $user->ID );
|
|
}
|
|
}
|
|
|
|
$from_string_array = array();
|
|
|
|
if ( ! empty( $users ) ) {
|
|
foreach ( $users as $user ) {
|
|
foreach ( $user as $field_name => $field_value ) {
|
|
if ( ! empty( $field_value ) ) {
|
|
$from_string_array[ $field_name ][] = $field_value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$users_sql = '';
|
|
foreach ( $from_string_array as $field_name => $field_values ) {
|
|
$users_sql .= ( ( true === $exclude ) ? ' ( ' . $field_name . ' IS NULL OR ' : '' ) . $field_name . ( ( true === $exclude ) ? ' NOT' : '' ) . ' IN ( ' . \implode( ',', $field_values ) . ( ( true === $exclude ) ? ' ) ) AND ' : ' ) OR ' );
|
|
}
|
|
|
|
if ( true === $exclude ) {
|
|
$users_sql = ' ( ' . \rtrim( $users_sql, ' AND ' ) . ' ) ';
|
|
} else {
|
|
$users_sql = ' ( ' . \rtrim( $users_sql, ' OR ' ) . ' ) ';
|
|
}
|
|
|
|
if ( true === $exclude ) {
|
|
unset( $search_values['exc'] );
|
|
if ( ! empty( $search_values ) ) {
|
|
$users_sql .= ' AND ' . self::users_search( $search_keys, $search_values );
|
|
}
|
|
}
|
|
|
|
return $users_sql;
|
|
} elseif ( isset( $search_keys['extract'] ) ) {
|
|
if ( 'role' === $search_keys['extract'] ) {
|
|
|
|
$users_sql = '';
|
|
foreach ( $search_user_values as $field_value ) {
|
|
$users_sql .= ( ( true === $exclude ) ? '!' : '' ) . " FIND_IN_SET('" . $field_value . "',`user_roles`) " . ( ( true === $exclude ) ? ' AND ' : ' OR ' );
|
|
}
|
|
|
|
if ( true === $exclude ) {
|
|
$users_sql = ' ( ' . \rtrim( $users_sql, ' AND ' ) . ' ) ';
|
|
} else {
|
|
$users_sql = ' ( ' . \rtrim( $users_sql, ' OR ' ) . ' ) ';
|
|
}
|
|
|
|
if ( true === $exclude ) {
|
|
unset( $search_values['exc'] );
|
|
if ( ! empty( $search_values ) ) {
|
|
$users_sql .= ' AND ' . self::users_search( $search_keys, $search_values );
|
|
}
|
|
}
|
|
|
|
return $users_sql;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Special class method used for searching in the meta table for the given filed/value based on the mapping provided in the @see prepare_all_fields
|
|
*
|
|
* @param array $search_keys - Keys (from field mapping logic @see prepare_all_fields method) giving the logic of how to search for user based on specific field.
|
|
* @param array $search_values - Values collected from the search string to search for.
|
|
* @param array $occurrences_ids_to_search - Array with the collected IDs to search for so far.
|
|
* @param array $occurrences_ids_to_exclude - Array with the collected IDs to exclude from search so far.
|
|
*
|
|
* @return void
|
|
*
|
|
* @since 5.0.0
|
|
*/
|
|
public static function meta_search( array $search_keys, array $search_values, array &$occurrences_ids_to_search, array &$occurrences_ids_to_exclude ) {
|
|
$sub_sql = $search_keys['sql_prefix'];
|
|
|
|
$exclude = false;
|
|
|
|
if ( isset( $search_values['exc'] ) ) {
|
|
$exclude = true;
|
|
$search_user_values = $search_values['exc'];
|
|
} else {
|
|
$search_user_values = $search_values;
|
|
}
|
|
|
|
foreach ( $search_user_values as $value ) {
|
|
$sub_sql .= $search_keys['sub_sql_string'] . ' OR ';
|
|
}
|
|
|
|
$sub_sql = \rtrim( $sub_sql, ' OR ' );
|
|
|
|
$sub_sql .= $search_keys['sql_suffix'];
|
|
|
|
$_wpdb = Metadata_Entity::get_connection();
|
|
|
|
$sql = $_wpdb->prepare( $sub_sql, $search_user_values );
|
|
$results = $_wpdb->get_results( $sql, ARRAY_A );
|
|
|
|
if ( ! empty( $results ) ) {
|
|
if ( true === $exclude ) {
|
|
$occurrences_ids_to_exclude = \array_merge( $occurrences_ids_to_exclude, array_column( $results, $search_keys['column_name'] ) );
|
|
} else {
|
|
$occurrences_ids_to_search = \array_merge( $occurrences_ids_to_search, array_column( $results, $search_keys['column_name'] ) );
|
|
}
|
|
}
|
|
|
|
if ( true === $exclude ) {
|
|
unset( $search_values['exc'] );
|
|
if ( ! empty( $search_values ) ) {
|
|
self::meta_search( $search_keys, $search_values, $occurrences_ids_to_search, $occurrences_ids_to_exclude );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Falls here if the filed / value pair to search for is directly present in the occurrences table - meaning that there is no need to map, or extract additional data.
|
|
*
|
|
* @param string $field_name - The name of the field to search for.
|
|
* @param array $search_values - The search values provided.
|
|
*
|
|
* @return string
|
|
*
|
|
* @since 5.0.0
|
|
*/
|
|
public static function direct_field_call( string $field_name, array $search_values ) {
|
|
$exclude = false;
|
|
|
|
$field_sql = '';
|
|
|
|
if ( \in_array( $field_name, array( 'start_date', 'end_date' ), true ) ) {
|
|
if ( 'start_date' === $field_name ) {
|
|
$start_datetime = \DateTime::createFromFormat( 'Y-m-d H:i:s', $search_values[0] . ' 00:00:00' );
|
|
$_start_timestamp = $start_datetime->format( 'U' ) + ( DateTime_Formatter_Helper::get_time_zone_offset() ) * -1;
|
|
$field_sql .= " created_on >= {$_start_timestamp} ";
|
|
} else {
|
|
$end_datetime = \DateTime::createFromFormat( 'Y-m-d H:i:s', $search_values[0] . ' 23:59:59' );
|
|
$_end_timestamp = $end_datetime->format( 'U' ) + ( DateTime_Formatter_Helper::get_time_zone_offset() ) * -1;
|
|
$field_sql .= " created_on <= {$_end_timestamp} ";
|
|
}
|
|
} else {
|
|
|
|
if ( isset( $search_values['exc'] ) ) {
|
|
$exclude = true;
|
|
$search_field_values = $search_values['exc'];
|
|
} else {
|
|
$search_field_values = $search_values;
|
|
}
|
|
foreach ( $search_field_values as $field_values ) {
|
|
$field_sql .= ( ( true === $exclude ) ? ' ( ' . $field_name . ' IS NULL OR ' : '' ) . $field_name . ( ( true === $exclude ) ? ' NOT' : '' ) . ' IN ( ' . "'" . \implode( ",'", (array) $field_values ) . "'" . ( ( true === $exclude ) ? ' ) ) AND ' : ' ) OR ' );
|
|
}
|
|
|
|
if ( true === $exclude ) {
|
|
$field_sql = ' ( ' . \rtrim( $field_sql, ' AND ' ) . ' ) ';
|
|
} else {
|
|
$field_sql = ' ( ' . \rtrim( $field_sql, ' OR ' ) . ' ) ';
|
|
}
|
|
|
|
if ( true === $exclude ) {
|
|
unset( $search_values['exc'] );
|
|
if ( ! empty( $search_values ) ) {
|
|
$field_sql .= ' AND ' . self::direct_field_call( $field_name, $search_values );
|
|
}
|
|
}
|
|
}
|
|
|
|
return $field_sql;
|
|
}
|
|
}
|
|
}
|