<?php
/**
* REST API Endpoints
*
* @category API
* @package My Calendar Pro
* @author Joe Dolson
* @license GPLv3
* @link https://www.joedolson.com/my-calendar-pro/
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
require_once __DIR__ . '/classes/class-my-calendar-api-route.php';
// Resources: Events, Categories, Locations.
// URL formats: v1/events/add, v1/events/update/ID, v1/events/delete/ID.
// No getters at this time; retain original getter API.
/**
* Basic authentication. Default false, filter to add custom authentication methods.
*
* Notes for authentication:
* - Authentication key sent from originating site
* - Authentication key matched at remote site
* - Standard User permissions manage whether API is executed at originating site
* - Valid auth key must be present to do anything at remote site
* - Need to know whether API is executing to filter permissions on event editing.
*
* @param object $request REST API request.
*
* @return boolean
*/
function mcs_api_authenticate( $request ) {
$sources = mcs_api_sources();
// referer can be found in headers > user_agent.
// should match request > source.
$authenticated = false;
$params = $request->get_params();
$referer = explode( ';', $request->get_header( 'user_agent' ) );
$referer = trim( $referer[1] );
$source = $params['source'] ?? false;
// If the referer value doesn't match the sent value, don't authenticate.
if ( $referer !== $source ) {
return $authenticated;
}
$auth_key = ( isset( $params['mc_api_auth_key'] ) ) ? $params['mc_api_auth_key'] : false;
$salt = wp_salt();
$hashed = crypt( mcs_api_server_authentication_key(), $salt );
if ( $auth_key && hash_equals( $hashed, crypt( $auth_key, $salt ) ) ) {
$authenticated = true;
}
/**
* Filter the authentication status of a particular request.
*
* @hook mcs_api_authenticated
*
* @param {bool} $authenticated True of request is authenticated.
* @param {object} $request REST API $request.
*
* @return {bool}
*/
$authenticated = apply_filters( 'mcs_api_authenticated', $authenticated, $request );
if ( true === $authenticated ) {
// after authenticating, add filter allowing edits.
add_filter(
'mc_api_can_edit_event',
function () {
return true;
}
);
}
return $authenticated;
}
/**
* Gets local authentication key.
*
* @return string
*/
function mcs_api_server_authentication_key() {
$settings = ( is_array( get_option( 'mc_api_server_settings' ) ) ) ? get_option( 'mc_api_server_settings' ) : array();
$settings = array_merge(
array(
'mc_api_key' => '',
'mc_api_whitelist' => array(),
),
$settings
);
$mc_api_key = $settings['mc_api_key'];
return apply_filters( 'mc_api_authentication_key', $mc_api_key );
}
/**
* Get list of allowed API sources.
*
* These are the sites allowed to submit posts to the current site. Default none, white list specific sources.
*
* Usage: return true to generally allow; otherwise, do verification postback to URL using public key
*
* @return array
*/
function mcs_api_sources() {
$settings = ( is_array( get_option( 'mc_api_server_settings' ) ) ) ? get_option( 'mc_api_server_settings' ) : array();
$settings = array_merge(
array(
'mc_api_key' => '',
'mc_api_whitelist' => array(),
),
$settings
);
$whitelist = isset( $settings['mc_api_whitelist'] ) ? $settings['mc_api_whitelist'] : array();
return apply_filters( 'my_calendar_api_sources', $whitelist );
}
/**
* Handle save and generate response
*
* @param object $request Request object from REST.
* @param array $check Checked data from My Calendar.
* @param int $original_id Original event ID.
* @param string $action What action is being ordered.
* @param int $event_id Event ID if exists.
*
* @return array server response.
*/
function mcs_api_create_item( $request, $check, $original_id, $action = 'add', $event_id = false ) {
// Keeping this will result in double posting.
remove_action( 'mc_save_event', 'mcs_api_post', 10 );
if ( $check[0] ) {
// Categories: if category doesn't exist, create new category.
// Get category data & remove from checkset.
$categories = $check['category'];
unset( $check['category'] );
$cat_submit = array();
foreach ( $categories as $category ) {
// see whether current site has matching category.
$cat_id = mc_category_by_name( $category['category_name'] );
if ( $cat_id ) {
$check[2]['event_category'] = $cat_id;
} else {
// insert new category (function new in 3.0.0).
$cat_id = mc_create_category( $category );
$check[2]['event_category'] = $cat_id;
}
$cat_submit[] = $cat_id;
}
$check[2]['event_categories'] = $cat_submit;
if ( 'edit' === $action ) {
$response = my_calendar_save( $action, $check, $event_id );
} else {
$response = my_calendar_save( $action, $check );
}
$event_id = isset( $response['event_id'] ) ? $response['event_id'] : $event_id;
$e = mc_get_first_event( $event_id );
$params = $request->get_params();
$source = sanitize_text_field( $params['source'] );
if ( is_object( $e ) ) {
$post_id = $e->event_post;
// Where did this event come from?
update_post_meta( $post_id, '_mc_api_source', $source );
} else {
if ( defined( 'MC_API_SERVER_DEBUG' ) && true === MC_API_SERVER_DEBUG ) {
mc_debug( 'Event is not an object', $e );
}
}
$event_id = $response['event_id'];
if ( isset( $event['event_image'] ) && _mc_is_url( $event['event_image'] ) ) {
$image = media_sideload_image( $event['event_image'], $post_id, null, 'id' );
set_post_thumbnail( $post_id, $image );
}
if ( isset( $event['UID'] ) ) {
$uid = apply_filters( 'mcs_set_alternate_guid', $event['UID'], $event );
update_post_meta( $post_id, '_mc_guid', $uid );
}
} else {
$response = array(
'event_id' => false,
'message' => 'Verification of passed data failed. Cannot process this request.',
);
}
return array_map( 'htmlentities', $response );
}
/**
* Update a post.
*
* @param object $request REST Request.
* @param object $check Item being updated.
* @param int $original_id Original ID.
* @param string $action Action performed.
* @param int $event_id Current event ID.
*
* @uses mcs_api_create_item
*
* @return array
*/
function mcs_api_update_item( $request, $check, $original_id, $action = 'edit', $event_id = false ) {
return mcs_api_create_item( $request, $check, $original_id, $action, $event_id );
}
/**
* Delete an event.
*
* @param int $request Event ID from REST request.
*
* @return int Deleted event.
*/
function mcs_api_delete_item( $request ) {
$delete_id = mc_delete_event( $request );
return $delete_id;
}
/**
* Register My Calendar route.
*/
function mcs_api_register_custom_routes() {
$controller = new My_Calendar_API_Route();
$controller->register_routes();
}
add_action( 'rest_api_init', 'mcs_api_register_custom_routes' );