File "api.php"
Full path: /home/dora/public_html/wp-content/themes/bricks/includes/api.php
File size: 14.95 KB
MIME-type: --
Charset: utf-8
<?php
namespace Bricks;
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
class Api {
const API_NAMESPACE = 'bricks/v1';
/**
* WordPress REST API help docs:
*
* https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-custom-endpoints/
* https://developer.wordpress.org/rest-api/extending-the-rest-api/modifying-responses/
*/
public function __construct() {
add_action( 'rest_api_init', [ $this, 'rest_api_init_custom_endpoints' ] );
}
/**
* Custom REST API endpoints
*/
public function rest_api_init_custom_endpoints() {
// Server-side render (SSR) for builder elements via window.fetch API requests
register_rest_route(
self::API_NAMESPACE,
'render_element',
[
'methods' => 'POST',
'callback' => [ $this, 'render_element' ],
'permission_callback' => [ $this, 'render_element_permissions_check' ],
]
);
// Get all templates data (templates, authors, bundles, tags etc.)
register_rest_route(
self::API_NAMESPACE,
'/get-templates-data/',
[
'methods' => 'GET',
'callback' => [ $this, 'get_templates_data' ],
'permission_callback' => '__return_true',
]
);
register_rest_route(
self::API_NAMESPACE,
'/get-templates/',
[
'methods' => 'GET',
'callback' => [ $this, 'get_templates' ],
'permission_callback' => '__return_true',
]
);
// Get individual template by ID
register_rest_route(
self::API_NAMESPACE,
'/get-templates/(?P<args>[a-zA-Z0-9-=&]+)',
[
'methods' => 'GET',
'callback' => [ $this, 'get_templates' ],
'permission_callback' => '__return_true',
'args' => [
'args' => [
'required' => true
],
],
]
);
register_rest_route(
self::API_NAMESPACE,
'/get-template-authors/',
[
'methods' => 'GET',
'callback' => [ $this, 'get_template_authors' ],
'permission_callback' => '__return_true',
]
);
register_rest_route(
self::API_NAMESPACE,
'/get-template-bundles/',
[
'methods' => 'GET',
'callback' => [ $this, 'get_template_bundles' ],
'permission_callback' => '__return_true',
]
);
register_rest_route(
self::API_NAMESPACE,
'/get-template-tags/',
[
'methods' => 'GET',
'callback' => [ $this, 'get_template_tags' ],
'permission_callback' => '__return_true',
]
);
/**
* Query loop: Infinite scroll
*
* @since 1.5
*/
register_rest_route(
self::API_NAMESPACE,
'load_query_page',
[
'methods' => 'POST',
'callback' => [ $this, 'render_query_page' ],
'permission_callback' => [ $this, 'render_query_page_permissions_check' ],
]
);
}
/**
* Return element HTML retrieved via Fetch API
*
* @since 1.5
*/
public static function render_element( $request ) {
$data = $request->get_json_params();
if ( ! empty( $data['postId'] ) ) {
Database::set_page_data( $data['postId'] );
}
// Include WooCommerce frontend classes and hooks to enable the WooCommerce element preview inside the builder (since 1.5)
if ( Woocommerce::$is_active ) {
WC()->frontend_includes();
Woocommerce_Helpers::maybe_load_cart();
}
// Get rendered element HTML
$html = Ajax::render_element( $data );
// Prepare response
$response = [ 'html' => $html ];
// Template element (send template elements to run template element scripts on the canvas)
if ( ! empty( $data['element']['name'] ) && $data['element']['name'] === 'template' ) {
$template_id = isset( $data['element']['settings']['template'] ) ? $data['element']['settings']['template'] : false;
if ( $template_id ) {
$additional_data = Element_Template::get_builder_call_additional_data( $template_id );
$response = array_merge( $response, $additional_data );
}
}
return [ 'data' => $response ];
}
/**
* Element render permission check
*
* @since 1.5
*/
public function render_element_permissions_check( $request ) {
$data = $request->get_json_params();
if ( empty( $data['postId'] ) || empty( $data['element'] ) || empty( $data['nonce'] ) ) {
return new \WP_Error( 'bricks_api_missing', __( 'Missing parameters' ), [ 'status' => 400 ] );
}
$result = wp_verify_nonce( $data['nonce'], 'bricks-nonce' );
if ( ! $result ) {
return new \WP_Error( 'rest_cookie_invalid_nonce', __( 'Cookie check failed' ), [ 'status' => 403 ] );
}
// Not in use as builder access capability check is already performed on page load
// Capabilities::current_user_can_use_builder();
return true;
}
/**
* Return all templates data in one call (templates, authors, bundles, tags, theme style)
*
* @param array $data
* @return array
*
* @since 1.0
*/
public function get_templates_data( $data ) {
$templates_args = isset( $data['args'] ) ? $data['args'] : [];
$templates = $this->get_templates( $templates_args );
// STEP: Check for template error
if ( isset( $templates['error'] ) ) {
return $templates;
}
$theme_styles = get_option( BRICKS_DB_THEME_STYLES, false );
$global_classes = get_option( BRICKS_DB_GLOBAL_CLASSES, [] );
// STEP: Add theme style to template data to import when inserting a template (@since 1.3.2)
foreach ( $templates as $index => $template ) {
$theme_style_id = Theme_Styles::set_active_style( $template['id'], true );
$theme_style = isset( $theme_styles[ $theme_style_id ] ) ? $theme_styles[ $theme_style_id ] : false;
if ( $theme_style ) {
// Remove theme style conditions
if ( isset( $theme_style['settings']['conditions'] ) ) {
unset( $theme_style['settings']['conditions'] );
}
$theme_style['id'] = $theme_style_id;
$templates[ $index ]['themeStyle'] = $theme_style;
}
/**
* Loop over all template elements to add 'global_classes' data to remote template data
*
* To import global classes when importing remote template locally.
*
* @since 1.5
*/
if ( count( $global_classes ) ) {
$template_classes = [];
$template_elements = [];
if ( ! empty( $template['content'] ) && is_array( $template['content'] ) ) {
$template_elements = $template['content'];
} elseif ( ! empty( $template['header'] ) && is_array( $template['header'] ) ) {
$template_elements = $template['header'];
} elseif ( ! empty( $template['footer'] ) && is_array( $template['footer'] ) ) {
$template_elements = $template['footer'];
}
foreach ( $template_elements as $element ) {
if ( ! empty( $element['settings']['_cssGlobalClasses'] ) ) {
$template_classes = array_unique( array_merge( $template_classes, $element['settings']['_cssGlobalClasses'] ) );
}
}
if ( count( $template_classes ) ) {
$templates[ $index ]['global_classes'] = [];
foreach ( $template_classes as $template_class ) {
foreach ( $global_classes as $global_class ) {
if ( $global_class['id'] === $template_class ) {
$templates[ $index ]['global_classes'][] = $global_class;
}
}
}
}
}
}
// Return all templates data
$templates_data = [
'timestamp' => current_time( 'timestamp' ),
'date' => current_time( get_option( 'date_format' ) . ' (' . get_option( 'time_format' ) . ')' ),
'templates' => $templates,
'authors' => Templates::get_template_authors(),
'bundles' => Templates::get_template_bundles(),
'tags' => Templates::get_template_tags(),
'get' => $_GET, // Pass URL params to perform additional checks (e.g. 'password' as license key, etc.) @since 1.5.5
];
$templates_data = apply_filters( 'bricks/api/get_templates_data', $templates_data );
// Remove 'get' data to avoid storing it in db
unset( $templates_data['get'] );
return $templates_data;
}
/**
* Return templates array OR specific template by array index
*
* @since 1.0
*
* @param array $data
*
* @return array
*/
public function get_templates( $data ) {
$parameters = $_GET;
$templates_response = Templates::can_get_templates( $parameters );
// Check for templates error (no site/password etc. provided)
if ( isset( $templates_response['error'] ) ) {
return $templates_response;
}
$templates_args = isset( $data['args'] ) ? $data['args'] : [];
// Merge $parameters with $templates_response args
$templates_args = array_merge( $templates_args, $templates_response );
$templates = Templates::get_templates( $templates_args );
return $templates;
}
/**
* Get API endpoint
*
* Default: /api (to get Bricks Community Templates)
* Remote URL or 'render_element' set: /wp-json (to use default WP REST API prefix)
*
* @param string $endpoint Custom endpoint.
* @param string $default Default base URL.
*
* @since 1.0
*
* @return string
*/
public static function get_endpoint( $endpoint = 'get-templates', $default_base_url = BRICKS_REMOTE_URL ) {
$remote_templates_url = Database::get_setting( 'remoteTemplatesUrl', false );
$api_base_url = $remote_templates_url ? $remote_templates_url : $default_base_url;
$api_prefix = $remote_templates_url || $endpoint === 'render_element' ? rest_get_url_prefix() : 'api';
return trailingslashit( $api_base_url ) . trailingslashit( $api_prefix ) . trailingslashit( self::API_NAMESPACE ) . $endpoint;
}
/**
* Get the Bricks REST API url
*
* @since 1.5
*
* @return string
*/
public static function get_rest_api_url() {
return trailingslashit( get_rest_url( null, '/' . self::API_NAMESPACE ) );
}
/**
* Check if current endpoint is Bricks API endpoint
*
* @since 1.8.1
*
* @param string $endpoint (e.g. 'render_element' or 'load_query_page' for our infinite scroll)
*
* @return bool
*/
public static function is_current_endpoint( $endpoint ) {
if ( ! $endpoint ) {
return false;
}
global $wp;
// REST route (example: /bricks/v1/load_query_page)
$current_rest_route = isset( $wp->query_vars['rest_route'] ) ? $wp->query_vars['rest_route'] : '';
if ( ! $current_rest_route ) {
return false;
}
// Example: /bricks/v1/load_query_page
$bricks_rest_route = '/' . self::API_NAMESPACE . '/' . $endpoint;
return $current_rest_route === $bricks_rest_route;
}
/**
* Get template authors
*
* @since 1.0
*
* @return array
*/
public static function get_template_authors() {
return Templates::get_template_authors();
}
/**
* Get template bundles
*
* @since 1.0
*
* @return array
*/
public static function get_template_bundles() {
return Templates::get_template_bundles();
}
/**
* Get template tags
*
* @since 1.0
*
* @return array
*/
public static function get_template_tags() {
return Templates::get_template_tags();
}
/**
* Get news feed
*
* NOTE: Not in use.
*
* @return array
*/
public static function get_feed() {
$remote_base_url = BRICKS_REMOTE_URL;
$feed_url = trailingslashit( $remote_base_url ) . trailingslashit( rest_get_url_prefix() ) . trailingslashit( self::API_NAMESPACE ) . trailingslashit( 'feed' );
$response = Helpers::remote_get( $feed_url );
if ( is_wp_error( $response ) ) {
return [];
} else {
return json_decode( wp_remote_retrieve_body( $response ), true );
}
}
/**
* Query loop: Infinite scroll permissions callback
*
* @since 1.5
*/
public function render_query_page_permissions_check( $request ) {
$data = $request->get_json_params();
if ( empty( $data['queryElementId'] ) || empty( $data['nonce'] ) || empty( $data['page'] ) ) {
return new \WP_Error( 'bricks_api_missing', __( 'Missing parameters' ), [ 'status' => 400 ] );
}
$result = wp_verify_nonce( $data['nonce'], 'bricks-nonce' );
if ( $result === false ) {
return new \WP_Error( 'rest_cookie_invalid_nonce', __( 'Bricks cookie check failed' ), [ 'status' => 403 ] );
}
return true;
}
/**
* Query loop: Infinite scroll callback
*
* @since 1.5
*/
public function render_query_page( $request ) {
$request_data = $request->get_json_params();
$query_element_id = $request_data['queryElementId'];
$post_id = $request_data['postId'];
$page = $request_data['page'];
$query_vars = json_decode( $request_data['queryVars'], true ); // @since 1.5.1
$data = Helpers::get_element_data( $post_id, $query_element_id );
if ( empty( $data['elements'] ) ) {
return rest_ensure_response(
[
'html' => '',
'styles' => '',
'error' => 'Template data not found',
]
);
}
// STEP: Build the flat list index
$indexed_elements = [];
foreach ( $data['elements'] as $element ) {
$indexed_elements[ $element['id'] ] = $element;
}
if ( ! array_key_exists( $query_element_id, $indexed_elements ) ) {
return rest_ensure_response(
[
'html' => '',
'styles' => '',
'error' => 'Element not found',
]
);
}
// STEP: Set the query element pagination
$query_element = $indexed_elements[ $query_element_id ];
$query_element['settings']['query']['paged'] = $page;
// STEP: Add merge query vars, used to simulate the global query merge in the archives (@since 1.5.1)
$query_element['settings']['query']['_merge_vars'] = $query_vars;
// Remove the parent
if ( ! empty( $query_element['parent'] ) ) {
$query_element['parent'] = 0;
$query_element['_noRootClass'] = 1;
}
// STEP: Get the query loop elements (main and children)
$loop_elements = [ $query_element ];
$children = $query_element['children'];
while ( ! empty( $children ) ) {
$child_id = array_shift( $children );
if ( array_key_exists( $child_id, $indexed_elements ) ) {
$loop_elements[] = $indexed_elements[ $child_id ];
if ( ! empty( $indexed_elements[ $child_id ]['children'] ) ) {
$children = array_merge( $children, $indexed_elements[ $child_id ]['children'] );
}
}
}
// Set Theme Styles (for correct preview of query loop nodes)
Theme_Styles::load_set_styles( $post_id );
// STEP: Generate the styles again to catch dynamic data changes (eg. background-image)
$scroll_query_page_id = "scroll_{$query_element_id}_{$page}";
Assets::generate_css_from_elements( $loop_elements, $scroll_query_page_id );
$inline_css = ! empty( Assets::$inline_css[ $scroll_query_page_id ] ) ? Assets::$inline_css[ $scroll_query_page_id ] : '';
$inline_css .= Assets::$inline_css_dynamic_data;
// STEP: Render the element after styles are generated as data-query-loop-index might be inserted through hook in Assets class (@since 1.7.2)
$html = Frontend::render_data( $loop_elements );
// Add popup HTML plus styles (@since 1.7.1)
$popups = Popups::$looping_popup_html;
// STEP: Add Template Styles (After the loop templates are rendered to catch dynamic data styles changes) (@since 1.8)
$inline_css .= Templates::generate_final_template_styles();
$styles = ! empty( $inline_css ) ? "\n<style>/* INFINITE SCROLL CSS */\n{$inline_css}</style>\n" : '';
return rest_ensure_response(
[
'html' => $html,
'styles' => $styles,
'popups' => $popups,
]
);
}
}