360 lines
9.8 KiB
PHP
360 lines
9.8 KiB
PHP
<?php
|
|
/**
|
|
* Simply Schedule Appointments Db Model.
|
|
*
|
|
* @since 0.0.3
|
|
* @package Simply_Schedule_Appointments
|
|
*/
|
|
|
|
/**
|
|
* Simply Schedule Appointments Db Model.
|
|
*
|
|
* @since 0.0.3
|
|
*/
|
|
abstract class SSA_Db_Model extends TD_DB_Model {
|
|
protected $hook_namespace = 'ssa';
|
|
protected $db_namespace = 'ssa';
|
|
protected $api_namespace = 'ssa';
|
|
protected $api_version = '1';
|
|
|
|
/**
|
|
* Parent plugin class.
|
|
*
|
|
* @since 0.0.3
|
|
*
|
|
* @var Simply_Schedule_Appointments
|
|
*/
|
|
protected $plugin = null;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @since 0.0.3
|
|
*
|
|
* @param Simply_Schedule_Appointments $plugin Main plugin object.
|
|
*/
|
|
public function __construct( $plugin ) {
|
|
parent::__construct( $plugin );
|
|
$this->ssa_model_hooks();
|
|
|
|
add_filter( 'rest_authentication_errors', array( $this, 'whitelist_ssa_rest_api' ), 1000 );
|
|
}
|
|
|
|
public function whitelist_ssa_rest_api( $result ) {
|
|
if ( isset( $GLOBALS['wp']->query_vars['rest_route'] ) ) {
|
|
$route = untrailingslashit( $GLOBALS['wp']->query_vars['rest_route'] );
|
|
if ( 0 === strpos( $route, '/ssa/' ) ) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Use to retrieve the primary key for the model
|
|
* @return string
|
|
*/
|
|
public function primary_key() {
|
|
return $this->primary_key;
|
|
}
|
|
|
|
/**
|
|
* Initiate our hooks.
|
|
*
|
|
* @since 0.0.3
|
|
*/
|
|
public function ssa_model_hooks() {
|
|
add_filter( 'query_'.$this->slug.'_db_where_conditions', array( $this, 'ssa_filter_where_conditions' ), 10, 2 );
|
|
}
|
|
|
|
public function ssa_filter_where_conditions( $where, $args ) {
|
|
global $wpdb;
|
|
|
|
if( ! empty( $args['id'] ) ) {
|
|
|
|
if( is_array( $args['id'] ) ) {
|
|
$ids = implode( ',', array_map('intval', $args['id'] ) );
|
|
$where .= " AND `".$this->primary_key."` IN( $ids ) ";
|
|
} else {
|
|
$ids = intval( $args['id'] );
|
|
$where .= $wpdb->prepare( " AND `".$this->primary_key."` = %d ", $ids );
|
|
}
|
|
|
|
|
|
}
|
|
|
|
if ( !empty( $this->schema['user_id'] ) ) {
|
|
// rows for specific user actions
|
|
if( ! empty( $args['user_id'] ) ) {
|
|
|
|
if( is_array( $args['user_id'] ) ) {
|
|
$user_ids = implode( ',', array_map('intval', $args['user_id'] ) );
|
|
$where .= " AND `user_id` IN( $user_ids ) ";
|
|
} else {
|
|
$user_ids = intval( $args['user_id'] );
|
|
$where .= $wpdb->prepare( " AND `user_id` = %d", $user_ids );
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
if ( !empty( $this->post_id_field ) && !empty( $this->schema[$this->post_id_field] ) ) {
|
|
if ( is_user_logged_in()
|
|
&& !current_user_can( 'ssa_manage_appointments' ) ) {
|
|
$args['author_id'] = get_current_user_id();
|
|
}
|
|
|
|
// rows for specific user accounts
|
|
if( ! empty( $args['author_id'] ) ) {
|
|
if( is_array( $args['author_id'] ) ) {
|
|
$author_ids = implode( ',', array_map('intval', $args['author_id'] ) );
|
|
$where .= $wpdb->prepare( " AND `%i` IN( SELECT ID FROM $wpdb->posts WHERE post_author IN ( $author_ids ) ) ", $this->post_id_field );
|
|
} else {
|
|
$author_ids = intval( $args['author_id'] );
|
|
$where .= $wpdb->prepare( " AND `%i` IN( SELECT ID FROM $wpdb->posts WHERE post_author = $author_ids ) ", $this->post_id_field);
|
|
}
|
|
|
|
}
|
|
|
|
// specific rows by name
|
|
if( ! empty( $args[$this->post_id_field] ) ) {
|
|
if ( is_array( $args[$this->post_id_field] ) ) {
|
|
$post_ids = implode( ',', array_map('intval', $args[$this->post_id_field] ) );
|
|
$where .= $wpdb->prepare( " AND `%i` IN( $post_ids ) ", $this->post_id_field );
|
|
} else {
|
|
$where .= $wpdb->prepare( " AND `%i` = %d ", $this->post_id_field, $args[$this->post_id_field] );
|
|
}
|
|
}
|
|
}
|
|
|
|
// specific rows by type
|
|
if ( !empty( $this->schema['type'] ) ) {
|
|
if( ! empty( $args['type'] ) ) {
|
|
$where .= $wpdb->prepare( " AND `type` = '" . '%s' . "' ", $args['type'] );
|
|
}
|
|
}
|
|
|
|
|
|
// specific rows by name
|
|
if ( !empty( $this->schema['name'] ) ) {
|
|
if( ! empty( $args['name'] ) ) {
|
|
$where .= $wpdb->prepare( " AND `name` = '" . '%s' . "' ", $args['name'] );
|
|
}
|
|
}
|
|
|
|
if ( !empty( $this->schema['start_date'] ) ) {
|
|
// Customers created for a specific date or in a date range
|
|
if( ! empty( $args['start_date'] ) && false !== strtotime( $args['start_date'] ) ) {
|
|
$where .= $wpdb->prepare( " AND `start_date` = '" . '%s' . "' ", $args['start_date'] );
|
|
} else {
|
|
|
|
if( ! empty( $args['start_date_min'] ) && false !== strtotime( $args['start_date_min'] )) {
|
|
$where .= $wpdb->prepare( " AND `start_date` >= '" . '%s' . "' ", $args['start_date_min'] );
|
|
}
|
|
|
|
if( ! empty( $args['start_date_max'] ) && false !== strtotime( $args['start_date_max'] ) ) {
|
|
$where .= $wpdb->prepare( " AND `start_date` <= '" . '%s' . "' ", $args["start_date_max"] );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if ( !empty( $this->schema['end_date'] ) ) {
|
|
// Customers created for a specific date or in a date range
|
|
if( ! empty( $args['end_date'] ) ) {
|
|
$where .= $wpdb->prepare( " AND `end_date` = '" . '%s' . "' ", $args['end_date'] );
|
|
} else {
|
|
|
|
if( ! empty( $args['end_date_min'] ) && false !== strtotime( $args['end_date_min'] ) ) {
|
|
$where .= $wpdb->prepare( " AND `end_date` >= '" . '%s' . "' ",$args["end_date_min"] );
|
|
}
|
|
|
|
if( ! empty( $args['end_date_max'] ) && false !== strtotime( $args['end_date_max'] ) ) {
|
|
$where .= $wpdb->prepare( " AND `end_date` <= '" . '%s' . "' ", $args["end_date_max"] );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if ( !empty( $this->schema['date_created'] ) ) {
|
|
// Customers created for a specific date or in a date range
|
|
if( ! empty( $args['date_created'] ) ) {
|
|
$where .= $wpdb->prepare( " AND `date_created` = '" . '%s' . "' ", $args['date_created'] );
|
|
} else {
|
|
|
|
if( ! empty( $args['date_created_min'] ) ) {
|
|
$where .= $wpdb->prepare( " AND `date_created` >= '" . '%s' . "' ",$args["date_created_min"] );
|
|
}
|
|
|
|
if( ! empty( $args['date_created_max'] ) ) {
|
|
$where .= $wpdb->prepare( " AND `date_created` <= '" . '%s' . "' ", $args["date_created_max"] );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if ( !empty( $this->schema['date_modified'] ) ) {
|
|
// Customers created for a specific date or in a date range
|
|
if( ! empty( $args['date_modified'] ) ) {
|
|
|
|
if( !is_array( $args['date_modified'] ) ) {
|
|
|
|
$year = date( 'Y', strtotime( $args['date_modified'] ) );
|
|
$month = date( 'm', strtotime( $args['date_modified'] ) );
|
|
$day = date( 'd', strtotime( $args['date_modified'] ) );
|
|
|
|
$where .= " AND $year = YEAR ( date_modified ) AND $month = MONTH ( date_modified ) AND $day = DAY ( date_modified )";
|
|
}
|
|
|
|
} else {
|
|
|
|
if( ! empty( $args['date_modified_min'] ) ) {
|
|
$where .= $wpdb->prepare( " AND `date_modified` >= '" . '%s' . "' ", $args["date_modified_min"] );
|
|
}
|
|
|
|
if( ! empty( $args['date_modified_max'] ) ) {
|
|
$where .= $wpdb->prepare( " AND `date_modified` <= '" . '%s' . "' " , $args["date_modified_max"] );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return $where;
|
|
}
|
|
|
|
private function extract_id_from_input( $input ) {
|
|
if ( is_numeric( $input ) ) {
|
|
return $input;
|
|
} elseif ( is_array( $input ) && isset( $input['id'] ) ) {
|
|
return $input['id'];
|
|
} elseif ( $input instanceof WP_REST_Request ) {
|
|
return $input->get_param('id');
|
|
} elseif ( is_object( $input ) && property_exists( $input, 'id' ) ) {
|
|
return $input->id;
|
|
}
|
|
return false;
|
|
}
|
|
public function get_string_to_tokenize( $input ) {
|
|
$id = $this->extract_id_from_input( $input );
|
|
if ( empty( $id ) ) {
|
|
return false;
|
|
}
|
|
|
|
$string_to_tokenize = $id;
|
|
|
|
$model_entity = $this->get( $id, -1 );
|
|
|
|
if ( ! empty( $model_entity['date_created'] ) ) {
|
|
$string_to_tokenize .= $model_entity['date_created'];
|
|
}
|
|
|
|
return $string_to_tokenize;
|
|
}
|
|
|
|
/**
|
|
* New get_id_token that uses the id alongside the date_created field
|
|
*/
|
|
public function get_id_token( $input ) {
|
|
$string_to_tokenize = $this->get_string_to_tokenize( $input );
|
|
if ( empty( $string_to_tokenize ) ) {
|
|
return false;
|
|
}
|
|
return SSA_Utils::site_unique_hash( $string_to_tokenize );
|
|
}
|
|
|
|
/**
|
|
* Consider removing this code after 2026-10-10
|
|
*/
|
|
public function deprecated_get_id_token( $input ) {
|
|
$string_to_tokenize = $this->get_string_to_tokenize( $input );
|
|
if ( empty( $string_to_tokenize ) ) {
|
|
return false;
|
|
}
|
|
return SSA_Utils::deprecated_hash( $string_to_tokenize );
|
|
}
|
|
|
|
|
|
/**
|
|
* All tokens verification should be handled here
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function verify_id_token( $input, $token_to_verify ) {
|
|
|
|
if ( empty( $input ) || empty( $token_to_verify ) ) {
|
|
return false;
|
|
}
|
|
|
|
$correct_token = $this->get_id_token( $input );
|
|
if ( ! empty( $correct_token ) && $correct_token == $token_to_verify ) {
|
|
return true;
|
|
}
|
|
|
|
// TODO remove when all users are guaranteed to have updated - after 2026-09-30
|
|
$target_timestamp = strtotime( '2026-09-30 00:00:00' );
|
|
$current_timestamp = current_time( 'timestamp' );
|
|
|
|
if ( $current_timestamp < $target_timestamp ) {
|
|
$deprecated_correct_token = $this->deprecated_get_id_token( $input );
|
|
if ( ! empty( $deprecated_correct_token ) && $deprecated_correct_token == $token_to_verify ) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function id_token_permissions_check( $request ) {
|
|
|
|
$params = $request->get_params();
|
|
if ( empty( $params['token'] ) ) {
|
|
return false;
|
|
}
|
|
|
|
return $this->verify_id_token( $request, sanitize_text_field( $params['token'] ) );
|
|
}
|
|
|
|
/**
|
|
* Returns global SSA Token.
|
|
*
|
|
* @since 5.7.2
|
|
*
|
|
* @return string SSA Token.
|
|
*/
|
|
public function get_token() {
|
|
return apply_filters( 'ssa/api/token', SSA_Utils::site_unique_hash( 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9' ) );
|
|
}
|
|
|
|
/**
|
|
* Validates global SSA Token.
|
|
*
|
|
* @since 5.7.2
|
|
*
|
|
* @param WP_REST_Request $request Full details about the request.
|
|
*
|
|
* @return bool True if the token is valid, false if not.
|
|
*/
|
|
public function token_permissions_check( $request ) {
|
|
$correct_token = $this->get_token();
|
|
|
|
if ( empty( $correct_token ) ) {
|
|
return false;
|
|
}
|
|
|
|
$params = $request->get_params();
|
|
if ( empty( $params['token'] ) ) {
|
|
return false;
|
|
}
|
|
|
|
if ( sanitize_text_field( $params['token'] ) === $correct_token ) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
}
|