/** * REST API: WP_REST_Comments_Controller class * * @package WordPress * @subpackage REST_API * @since 4.7.0 */ /** * Core controller used to access comments via the REST API. * * @since 4.7.0 * * @see WP_REST_Controller */ class WP_REST_Comments_Controller extends WP_REST_Controller { /** * Instance of a comment meta fields object. * * @since 4.7.0 * @var WP_REST_Comment_Meta_Fields */ protected $meta; /** * Constructor. * * @since 4.7.0 */ public function __construct() { $this->namespace = 'wp/v2'; $this->rest_base = 'comments'; $this->meta = new WP_REST_Comment_Meta_Fields(); } /** * Registers the routes for comments. * * @since 4.7.0 * * @see register_rest_route() */ public function register_routes() { register_rest_route( $this->namespace, '/' . $this->rest_base, array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_items' ), 'permission_callback' => array( $this, 'get_items_permissions_check' ), 'args' => $this->get_collection_params(), ), array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'create_item' ), 'permission_callback' => array( $this, 'create_item_permissions_check' ), 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)', array( 'args' => array( 'id' => array( 'description' => __( 'Unique identifier for the comment.' ), 'type' => 'integer', ), ), array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), 'permission_callback' => array( $this, 'get_item_permissions_check' ), 'args' => array( 'context' => $this->get_context_param( array( 'default' => 'view' ) ), 'password' => array( 'description' => __( 'The password for the parent post of the comment (if the post is password protected).' ), 'type' => 'string', ), ), ), array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( $this, 'update_item' ), 'permission_callback' => array( $this, 'update_item_permissions_check' ), 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), ), array( 'methods' => WP_REST_Server::DELETABLE, 'callback' => array( $this, 'delete_item' ), 'permission_callback' => array( $this, 'delete_item_permissions_check' ), 'args' => array( 'force' => array( 'type' => 'boolean', 'default' => false, 'description' => __( 'Whether to bypass Trash and force deletion.' ), ), 'password' => array( 'description' => __( 'The password for the parent post of the comment (if the post is password protected).' ), 'type' => 'string', ), ), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); } /** * Checks if a given request has access to read comments. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return true|WP_Error True if the request has read access, error object otherwise. */ public function get_items_permissions_check( $request ) { if ( ! empty( $request['post'] ) ) { foreach ( (array) $request['post'] as $post_id ) { $post = get_post( $post_id ); if ( ! empty( $post_id ) && $post && ! $this->check_read_post_permission( $post, $request ) ) { return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you are not allowed to read the post for this comment.' ), array( 'status' => rest_authorization_required_code() ) ); } elseif ( 0 === $post_id && ! current_user_can( 'moderate_comments' ) ) { return new WP_Error( 'rest_cannot_read', __( 'Sorry, you are not allowed to read comments without a post.' ), array( 'status' => rest_authorization_required_code() ) ); } } } if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'moderate_comments' ) ) { return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit comments.' ), array( 'status' => rest_authorization_required_code() ) ); } if ( ! current_user_can( 'edit_posts' ) ) { $protected_params = array( 'author', 'author_exclude', 'author_email', 'type', 'status' ); $forbidden_params = array(); foreach ( $protected_params as $param ) { if ( 'status' === $param ) { if ( 'approve' !== $request[ $param ] ) { $forbidden_params[] = $param; } } elseif ( 'type' === $param ) { if ( 'comment' !== $request[ $param ] ) { $forbidden_params[] = $param; } } elseif ( ! empty( $request[ $param ] ) ) { $forbidden_params[] = $param; } } if ( ! empty( $forbidden_params ) ) { return new WP_Error( 'rest_forbidden_param', /* translators: %s: List of forbidden parameters. */ sprintf( __( 'Query parameter not permitted: %s' ), implode( ', ', $forbidden_params ) ), array( 'status' => rest_authorization_required_code() ) ); } } return true; } /** * Retrieves a list of comment items. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, or error object on failure. */ public function get_items( $request ) { // Retrieve the list of registered collection query parameters. $registered = $this->get_collection_params(); /* * This array defines mappings between public API query parameters whose * values are accepted as-passed, and their internal WP_Query parameter * name equivalents (some are the same). Only values which are also * present in $registered will be set. */ $parameter_mappings = array( 'author' => 'author__in', 'author_email' => 'author_email', 'author_exclude' => 'author__not_in', 'exclude' => 'comment__not_in', 'include' => 'comment__in', 'offset' => 'offset', 'order' => 'order', 'parent' => 'parent__in', 'parent_exclude' => 'parent__not_in', 'per_page' => 'number', 'post' => 'post__in', 'search' => 'search', 'status' => 'status', 'type' => 'type', ); $prepared_args = array(); /* * For each known parameter which is both registered and present in the request, * set the parameter's value on the query $prepared_args. */ foreach ( $parameter_mappings as $api_param => $wp_param ) { if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) { $prepared_args[ $wp_param ] = $request[ $api_param ]; } } // Ensure certain parameter values default to empty strings. foreach ( array( 'author_email', 'search' ) as $param ) { if ( ! isset( $prepared_args[ $param ] ) ) { $prepared_args[ $param ] = ''; } } if ( isset( $registered['orderby'] ) ) { $prepared_args['orderby'] = $this->normalize_query_param( $request['orderby'] ); } $prepared_args['no_found_rows'] = false; $prepared_args['update_comment_post_cache'] = true; $prepared_args['date_query'] = array(); // Set before into date query. Date query must be specified as an array of an array. if ( isset( $registered['before'], $request['before'] ) ) { $prepared_args['date_query'][0]['before'] = $request['before']; } // Set after into date query. Date query must be specified as an array of an array. if ( isset( $registered['after'], $request['after'] ) ) { $prepared_args['date_query'][0]['after'] = $request['after']; } if ( isset( $registered['page'] ) && empty( $request['offset'] ) ) { $prepared_args['offset'] = $prepared_args['number'] * ( absint( $request['page'] ) - 1 ); } /** * Filters WP_Comment_Query arguments when querying comments via the REST API. * * @since 4.7.0 * * @link https://developer.wordpress.org/reference/classes/wp_comment_query/ * * @param array $prepared_args Array of arguments for WP_Comment_Query. * @param WP_REST_Request $request The REST API request. */ $prepared_args = apply_filters( 'rest_comment_query', $prepared_args, $request ); $query = new WP_Comment_Query(); $query_result = $query->query( $prepared_args ); $comments = array(); foreach ( $query_result as $comment ) { if ( ! $this->check_read_permission( $comment, $request ) ) { continue; } $data = $this->prepare_item_for_response( $comment, $request ); $comments[] = $this->prepare_response_for_collection( $data ); } $total_comments = (int) $query->found_comments; $max_pages = (int) $query->max_num_pages; if ( $total_comments < 1 ) { // Out-of-bounds, run the query again without LIMIT for total count. unset( $prepared_args['number'], $prepared_args['offset'] ); $query = new WP_Comment_Query(); $prepared_args['count'] = true; $prepared_args['orderby'] = 'none'; $total_comments = $query->query( $prepared_args ); $max_pages = (int) ceil( $total_comments / $request['per_page'] ); } $response = rest_ensure_response( $comments ); $response->header( 'X-WP-Total', $total_comments ); $response->header( 'X-WP-TotalPages', $max_pages ); $base = add_query_arg( urlencode_deep( $request->get_query_params() ), rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) ); if ( $request['page'] > 1 ) { $prev_page = $request['page'] - 1; if ( $prev_page > $max_pages ) { $prev_page = $max_pages; } $prev_link = add_query_arg( 'page', $prev_page, $base ); $response->link_header( 'prev', $prev_link ); } if ( $max_pages > $request['page'] ) { $next_page = $request['page'] + 1; $next_link = add_query_arg( 'page', $next_page, $base ); $response->link_header( 'next', $next_link ); } return $response; } /** * Get the comment, if the ID is valid. * * @since 4.7.2 * * @param int $id Supplied ID. * @return WP_Comment|WP_Error Comment object if ID is valid, WP_Error otherwise. */ protected function get_comment( $id ) { $error = new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment ID.' ), array( 'status' => 404 ) ); if ( (int) $id <= 0 ) { return $error; } $id = (int) $id; $comment = get_comment( $id ); if ( empty( $comment ) ) { return $error; } if ( ! empty( $comment->comment_post_ID ) ) { $post = get_post( (int) $comment->comment_post_ID ); if ( empty( $post ) ) { return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) ); } } return $comment; } /** * Checks if a given request has access to read the comment. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return true|WP_Error True if the request has read access for the item, error object otherwise. */ public function get_item_permissions_check( $request ) { $comment = $this->get_comment( $request['id'] ); if ( is_wp_error( $comment ) ) { return $comment; } if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'moderate_comments' ) ) { return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit comments.' ), array( 'status' => rest_authorization_required_code() ) ); } $post = get_post( $comment->comment_post_ID ); if ( ! $this->check_read_permission( $comment, $request ) ) { return new WP_Error( 'rest_cannot_read', __( 'Sorry, you are not allowed to read this comment.' ), array( 'status' => rest_authorization_required_code() ) ); } if ( $post && ! $this->check_read_post_permission( $post, $request ) ) { return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you are not allowed to read the post for this comment.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Retrieves a comment. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, or error object on failure. */ public function get_item( $request ) { $comment = $this->get_comment( $request['id'] ); if ( is_wp_error( $comment ) ) { return $comment; } $data = $this->prepare_item_for_response( $comment, $request ); $response = rest_ensure_response( $data ); return $response; } /** * Checks if a given request has access to create a comment. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return true|WP_Error True if the request has access to create items, error object otherwise. */ public function create_item_permissions_check( $request ) { if ( ! is_user_logged_in() ) { if ( get_option( 'comment_registration' ) ) { return new WP_Error( 'rest_comment_login_required', __( 'Sorry, you must be logged in to comment.' ), array( 'status' => 401 ) ); } /** * Filters whether comments can be created via the REST API without authentication. * * Enables creating comments for anonymous users. * * @since 4.7.0 * * @param bool $allow_anonymous Whether to allow anonymous comments to * be created. Default `false`. * @param WP_REST_Request $request Request used to generate the * response. */ $allow_anonymous = apply_filters( 'rest_allow_anonymous_comments', false, $request ); if ( ! $allow_anonymous ) { return new WP_Error( 'rest_comment_login_required', __( 'Sorry, you must be logged in to comment.' ), array( 'status' => 401 ) ); } } // Limit who can set comment `author`, `author_ip` or `status` to anything other than the default. if ( isset( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( 'moderate_comments' ) ) { return new WP_Error( 'rest_comment_invalid_author', /* translators: %s: Request parameter. */ sprintf( __( "Sorry, you are not allowed to edit '%s' for comments." ), 'author' ), array( 'status' => rest_authorization_required_code() ) ); } if ( isset( $request['author_ip'] ) && ! current_user_can( 'moderate_comments' ) ) { if ( empty( $_SERVER['REMOTE_ADDR'] ) || $request['author_ip'] !== $_SERVER['REMOTE_ADDR'] ) { return new WP_Error( 'rest_comment_invalid_author_ip', /* translators: %s: Request parameter. */ sprintf( __( "Sorry, you are not allowed to edit '%s' for comments." ), 'author_ip' ), array( 'status' => rest_authorization_required_code() ) ); } } if ( isset( $request['status'] ) && ! current_user_can( 'moderate_comments' ) ) { return new WP_Error( 'rest_comment_invalid_status', /* translators: %s: Request parameter. */ sprintf( __( "Sorry, you are not allowed to edit '%s' for comments." ), 'status' ), array( 'status' => rest_authorization_required_code() ) ); } if ( empty( $request['post'] ) ) { return new WP_Error( 'rest_comment_invalid_post_id', __( 'Sorry, you are not allowed to create this comment without a post.' ), array( 'status' => 403 ) ); } $post = get_post( (int) $request['post'] ); if ( ! $post ) { return new WP_Error( 'rest_comment_invalid_post_id', __( 'Sorry, you are not allowed to create this comment without a post.' ), array( 'status' => 403 ) ); } if ( 'draft' === $post->post_status ) { return new WP_Error( 'rest_comment_draft_post', __( 'Sorry, you are not allowed to create a comment on this post.' ), array( 'status' => 403 ) ); } if ( 'trash' === $post->post_status ) { return new WP_Error( 'rest_comment_trash_post', __( 'Sorry, you are not allowed to create a comment on this post.' ), array( 'status' => 403 ) ); } if ( ! $this->check_read_post_permission( $post, $request ) ) { return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you are not allowed to read the post for this comment.' ), array( 'status' => rest_authorization_required_code() ) ); } if ( ! comments_open( $post->ID ) ) { return new WP_Error( 'rest_comment_closed', __( 'Sorry, comments are closed for this item.' ), array( 'status' => 403 ) ); } return true; } /** * Creates a comment. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, or error object on failure. */ public function create_item( $request ) { if ( ! empty( $request['id'] ) ) { return new WP_Error( 'rest_comment_exists', __( 'Cannot create existing comment.' ), array( 'status' => 400 ) ); } // Do not allow comments to be created with a non-default type. if ( ! empty( $request['type'] ) && 'comment' !== $request['type'] ) { return new WP_Error( 'rest_invalid_comment_type', __( 'Cannot create a comment with that type.' ), array( 'status' => 400 ) ); } $prepared_comment = $this->prepare_item_for_database( $request ); if ( is_wp_error( $prepared_comment ) ) { return $prepared_comment; } $prepared_comment['comment_type'] = 'comment'; if ( ! isset( $prepared_comment['comment_content'] ) ) { $prepared_comment['comment_content'] = ''; } if ( ! $this->check_is_comment_content_allowed( $prepared_comment ) ) { return new WP_Error( 'rest_comment_content_invalid', __( 'Invalid comment content.' ), array( 'status' => 400 ) ); } // Setting remaining values before wp_insert_comment so we can use wp_allow_comment(). if ( ! isset( $prepared_comment['comment_date_gmt'] ) ) { $prepared_comment['comment_date_gmt'] = current_time( 'mysql', true ); } // Set author data if the user's logged in. $missing_author = empty( $prepared_comment['user_id'] ) && empty( $prepared_comment['comment_author'] ) && empty( $prepared_comment['comment_author_email'] ) && empty( $prepared_comment['comment_author_url'] ); if ( is_user_logged_in() && $missing_author ) { $user = wp_get_current_user(); $prepared_comment['user_id'] = $user->ID; $prepared_comment['comment_author'] = $user->display_name; $prepared_comment['comment_author_email'] = $user->user_email; $prepared_comment['comment_author_url'] = $user->user_url; } // Honor the discussion setting that requires a name and email address of the comment author. if ( get_option( 'require_name_email' ) ) { if ( empty( $prepared_comment['comment_author'] ) || empty( $prepared_comment['comment_author_email'] ) ) { return new WP_Error( 'rest_comment_author_data_required', __( 'Creating a comment requires valid author name and email values.' ), array( 'status' => 400 ) ); } } if ( ! isset( $prepared_comment['comment_author_email'] ) ) { $prepared_comment['comment_author_email'] = ''; } if ( ! isset( $prepared_comment['comment_author_url'] ) ) { $prepared_comment['comment_author_url'] = ''; } if ( ! isset( $prepared_comment['comment_agent'] ) ) { $prepared_comment['comment_agent'] = ''; } $check_comment_lengths = wp_check_comment_data_max_lengths( $prepared_comment ); if ( is_wp_error( $check_comment_lengths ) ) { $error_code = $check_comment_lengths->get_error_code(); return new WP_Error( $error_code, __( 'Comment field exceeds maximum length allowed.' ), array( 'status' => 400 ) ); } $prepared_comment['comment_approved'] = wp_allow_comment( $prepared_comment, true ); if ( is_wp_error( $prepared_comment['comment_approved'] ) ) { $error_code = $prepared_comment['comment_approved']->get_error_code(); $error_message = $prepared_comment['comment_approved']->get_error_message(); if ( 'comment_duplicate' === $error_code ) { return new WP_Error( $error_code, $error_message, array( 'status' => 409 ) ); } if ( 'comment_flood' === $error_code ) { return new WP_Error( $error_code, $error_message, array( 'status' => 400 ) ); } return $prepared_comment['comment_approved']; } /** * Filters a comment before it is inserted via the REST API. * * Allows modification of the comment right before it is inserted via wp_insert_comment(). * Returning a WP_Error value from the filter will short-circuit insertion and allow * skipping further processing. * * @since 4.7.0 * @since 4.8.0 `$prepared_comment` can now be a WP_Error to short-circuit insertion. * * @param array|WP_Error $prepared_comment The prepared comment data for wp_insert_comment(). * @param WP_REST_Request $request Request used to insert the comment. */ $prepared_comment = apply_filters( 'rest_pre_insert_comment', $prepared_comment, $request ); if ( is_wp_error( $prepared_comment ) ) { return $prepared_comment; } $comment_id = wp_insert_comment( wp_filter_comment( wp_slash( (array) $prepared_comment ) ) ); if ( ! $comment_id ) { return new WP_Error( 'rest_comment_failed_create', __( 'Creating comment failed.' ), array( 'status' => 500 ) ); } if ( isset( $request['status'] ) ) { $this->handle_status_param( $request['status'], $comment_id ); } $comment = get_comment( $comment_id ); /** * Fires after a comment is created or updated via the REST API. * * @since 4.7.0 * * @param WP_Comment $comment Inserted or updated comment object. * @param WP_REST_Request $request Request object. * @param bool $creating True when creating a comment, false * when updating. */ do_action( 'rest_insert_comment', $comment, $request, true ); $schema = $this->get_item_schema(); if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { $meta_update = $this->meta->update_value( $request['meta'], $comment_id ); if ( is_wp_error( $meta_update ) ) { return $meta_update; } } $fields_update = $this->update_additional_fields_for_object( $comment, $request ); if ( is_wp_error( $fields_update ) ) { return $fields_update; } $context = current_user_can( 'moderate_comments' ) ? 'edit' : 'view'; $request->set_param( 'context', $context ); /** * Fires completely after a comment is created or updated via the REST API. * * @since 5.0.0 * * @param WP_Comment $comment Inserted or updated comment object. * @param WP_REST_Request $request Request object. * @param bool $creating True when creating a comment, false * when updating. */ do_action( 'rest_after_insert_comment', $comment, $request, true ); $response = $this->prepare_item_for_response( $comment, $request ); $response = rest_ensure_response( $response ); $response->set_status( 201 ); $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $comment_id ) ) ); return $response; } /** * Checks if a given REST request has access to update a comment. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return true|WP_Error True if the request has access to update the item, error object otherwise. */ public function update_item_permissions_check( $request ) { $comment = $this->get_comment( $request['id'] ); if ( is_wp_error( $comment ) ) { return $comment; } if ( ! $this->check_edit_permission( $comment ) ) { return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit this comment.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Updates a comment. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, or error object on failure. */ public function update_item( $request ) { $comment = $this->get_comment( $request['id'] ); if ( is_wp_error( $comment ) ) { return $comment; } $id = $comment->comment_ID; if ( isset( $request['type'] ) && get_comment_type( $id ) !== $request['type'] ) { return new WP_Error( 'rest_comment_invalid_type', __( 'Sorry, you are not allowed to change the comment type.' ), array( 'status' => 404 ) ); } $prepared_args = $this->prepare_item_for_database( $request ); if ( is_wp_error( $prepared_args ) ) { return $prepared_args; } if ( ! empty( $prepared_args['comment_post_ID'] ) ) { $post = get_post( $prepared_args['comment_post_ID'] ); if ( empty( $post ) ) { return new WP_Error( 'rest_comment_invalid_post_id', __( 'Invalid post ID.' ), array( 'status' => 403 ) ); } } if ( empty( $prepared_args ) && isset( $request['status'] ) ) { // Only the comment status is being changed. $change = $this->handle_status_param( $request['status'], $id ); if ( ! $change ) { return new WP_Error( 'rest_comment_failed_edit', __( 'Updating comment status failed.' ), array( 'status' => 500 ) ); } } elseif ( ! empty( $prepared_args ) ) { if ( is_wp_error( $prepared_args ) ) { return $prepared_args; } if ( isset( $prepared_args['comment_content'] ) && empty( $prepared_args['comment_content'] ) ) { return new WP_Error( 'rest_comment_content_invalid', __( 'Invalid comment content.' ), array( 'status' => 400 ) ); } $prepared_args['comment_ID'] = $id; $check_comment_lengths = wp_check_comment_data_max_lengths( $prepared_args ); if ( is_wp_error( $check_comment_lengths ) ) { $error_code = $check_comment_lengths->get_error_code(); return new WP_Error( $error_code, __( 'Comment field exceeds maximum length allowed.' ), array( 'status' => 400 ) ); } $updated = wp_update_comment( wp_slash( (array) $prepared_args ), true ); if ( is_wp_error( $updated ) ) { return new WP_Error( 'rest_comment_failed_edit', __( 'Updating comment failed.' ), array( 'status' => 500 ) ); } if ( isset( $request['status'] ) ) { $this->handle_status_param( $request['status'], $id ); } } $comment = get_comment( $id ); /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php */ do_action( 'rest_insert_comment', $comment, $request, false ); $schema = $this->get_item_schema(); if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { $meta_update = $this->meta->update_value( $request['meta'], $id ); if ( is_wp_error( $meta_update ) ) { return $meta_update; } } $fields_update = $this->update_additional_fields_for_object( $comment, $request ); if ( is_wp_error( $fields_update ) ) { return $fields_update; } $request->set_param( 'context', 'edit' ); /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php */ do_action( 'rest_after_insert_comment', $comment, $request, false ); $response = $this->prepare_item_for_response( $comment, $request ); return rest_ensure_response( $response ); } /** * Checks if a given request has access to delete a comment. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return true|WP_Error True if the request has access to delete the item, error object otherwise. */ public function delete_item_permissions_check( $request ) { $comment = $this->get_comment( $request['id'] ); if ( is_wp_error( $comment ) ) { return $comment; } if ( ! $this->check_edit_permission( $comment ) ) { return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete this comment.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Deletes a comment. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, or error object on failure. */ public function delete_item( $request ) { $comment = $this->get_comment( $request['id'] ); if ( is_wp_error( $comment ) ) { return $comment; } $force = isset( $request['force'] ) ? (bool) $request['force'] : false; /** * Filters whether a comment can be trashed via the REST API. * * Return false to disable trash support for the comment. * * @since 4.7.0 * * @param bool $supports_trash Whether the comment supports trashing. * @param WP_Comment $comment The comment object being considered for trashing support. */ $supports_trash = apply_filters( 'rest_comment_trashable', ( EMPTY_TRASH_DAYS > 0 ), $comment ); $request->set_param( 'context', 'edit' ); if ( $force ) { $previous = $this->prepare_item_for_response( $comment, $request ); $result = wp_delete_comment( $comment->comment_ID, true ); $response = new WP_REST_Response(); $response->set_data( array( 'deleted' => true, 'previous' => $previous->get_data(), ) ); } else { // If this type doesn't support trashing, error out. if ( ! $supports_trash ) { return new WP_Error( 'rest_trash_not_supported', /* translators: %s: force=true */ sprintf( __( "The comment does not support trashing. Set '%s' to delete." ), 'force=true' ), array( 'status' => 501 ) ); } if ( 'trash' === $comment->comment_approved ) { return new WP_Error( 'rest_already_trashed', __( 'The comment has already been trashed.' ), array( 'status' => 410 ) ); } $result = wp_trash_comment( $comment->comment_ID ); $comment = get_comment( $comment->comment_ID ); $response = $this->prepare_item_for_response( $comment, $request ); } if ( ! $result ) { return new WP_Error( 'rest_cannot_delete', __( 'The comment cannot be deleted.' ), array( 'status' => 500 ) ); } /** * Fires after a comment is deleted via the REST API. * * @since 4.7.0 * * @param WP_Comment $comment The deleted comment data. * @param WP_REST_Response $response The response returned from the API. * @param WP_REST_Request $request The request sent to the API. */ do_action( 'rest_delete_comment', $comment, $response, $request ); return $response; } /** * Prepares a single comment output for response. * * @since 4.7.0 * @since 5.9.0 Renamed `$comment` to `$item` to match parent class for PHP 8 named parameter support. * * @param WP_Comment $item Comment object. * @param WP_REST_Request $request Request object. * @return WP_REST_Response Response object. */ public function prepare_item_for_response( $item, $request ) { // Restores the more descriptive, specific name for use within this method. $comment = $item; $fields = $this->get_fields_for_response( $request ); $data = array(); if ( in_array( 'id', $fields, true ) ) { $data['id'] = (int) $comment->comment_ID; } if ( in_array( 'post', $fields, true ) ) { $data['post'] = (int) $comment->comment_post_ID; } if ( in_array( 'parent', $fields, true ) ) { $data['parent'] = (int) $comment->comment_parent; } if ( in_array( 'author', $fields, true ) ) { $data['author'] = (int) $comment->user_id; } if ( in_array( 'author_name', $fields, true ) ) { $data['author_name'] = $comment->comment_author; } if ( in_array( 'author_email', $fields, true ) ) { $data['author_email'] = $comment->comment_author_email; } if ( in_array( 'author_url', $fields, true ) ) { $data['author_url'] = $comment->comment_author_url; } if ( in_array( 'author_ip', $fields, true ) ) { $data['author_ip'] = $comment->comment_author_IP; } if ( in_array( 'author_user_agent', $fields, true ) ) { $data['author_user_agent'] = $comment->comment_agent; } if ( in_array( 'date', $fields, true ) ) { $data['date'] = mysql_to_rfc3339( $comment->comment_date ); } if ( in_array( 'date_gmt', $fields, true ) ) { $data['date_gmt'] = mysql_to_rfc3339( $comment->comment_date_gmt ); } if ( in_array( 'content', $fields, true ) ) { $data['content'] = array( /** This filter is documented in wp-includes/comment-template.php */ 'rendered' => apply_filters( 'comment_text', $comment->comment_content, $comment, array() ), 'raw' => $comment->comment_content, ); } if ( in_array( 'link', $fields, true ) ) { $data['link'] = get_comment_link( $comment ); } if ( in_array( 'status', $fields, true ) ) { $data['status'] = $this->prepare_status_response( $comment->comment_approved ); } if ( in_array( 'type', $fields, true ) ) { $data['type'] = get_comment_type( $comment->comment_ID ); } if ( in_array( 'author_avatar_urls', $fields, true ) ) { $data['author_avatar_urls'] = rest_get_avatar_urls( $comment ); } if ( in_array( 'meta', $fields, true ) ) { $data['meta'] = $this->meta->get_value( $comment->comment_ID, $request ); } $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->add_additional_fields_to_object( $data, $request ); $data = $this->filter_response_by_context( $data, $context ); // Wrap the data in a response object. $response = rest_ensure_response( $data ); if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) { $response->add_links( $this->prepare_links( $comment ) ); } /** * Filters a comment returned from the REST API. * * Allows modification of the comment right before it is returned. * * @since 4.7.0 * * @param WP_REST_Response $response The response object. * @param WP_Comment $comment The original comment object. * @param WP_REST_Request $request Request used to generate the response. */ return apply_filters( 'rest_prepare_comment', $response, $comment, $request ); } /** * Prepares links for the request. * * @since 4.7.0 * * @param WP_Comment $comment Comment object. * @return array Links for the given comment. */ protected function prepare_links( $comment ) { $links = array( 'self' => array( 'href' => rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $comment->comment_ID ) ), ), 'collection' => array( 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ), ), ); if ( 0 !== (int) $comment->user_id ) { $links['author'] = array( 'href' => rest_url( 'wp/v2/users/' . $comment->user_id ), 'embeddable' => true, ); } if ( 0 !== (int) $comment->comment_post_ID ) { $post = get_post( $comment->comment_post_ID ); $post_route = rest_get_route_for_post( $post ); if ( ! empty( $post->ID ) && $post_route ) { $links['up'] = array( 'href' => rest_url( $post_route ), 'embeddable' => true, 'post_type' => $post->post_type, ); } } if ( 0 !== (int) $comment->comment_parent ) { $links['in-reply-to'] = array( 'href' => rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $comment->comment_parent ) ), 'embeddable' => true, ); } // Only grab one comment to verify the comment has children. $comment_children = $comment->get_children( array( 'count' => true, 'orderby' => 'none', ) ); if ( ! empty( $comment_children ) ) { $args = array( 'parent' => $comment->comment_ID, ); $rest_url = add_query_arg( $args, rest_url( $this->namespace . '/' . $this->rest_base ) ); $links['children'] = array( 'href' => $rest_url, 'embeddable' => true, ); } return $links; } /** * Prepends internal property prefix to query parameters to match our response fields. * * @since 4.7.0 * * @param string $query_param Query parameter. * @return string The normalized query parameter. */ protected function normalize_query_param( $query_param ) { $prefix = 'comment_'; switch ( $query_param ) { case 'id': $normalized = $prefix . 'ID'; break; case 'post': $normalized = $prefix . 'post_ID'; break; case 'parent': $normalized = $prefix . 'parent'; break; case 'include': $normalized = 'comment__in'; break; default: $normalized = $prefix . $query_param; break; } return $normalized; } /** * Checks comment_approved to set comment status for single comment output. * * @since 4.7.0 * * @param string|int $comment_approved comment status. * @return string Comment status. */ protected function prepare_status_response( $comment_approved ) { switch ( $comment_approved ) { case 'hold': case '0': $status = 'hold'; break; case 'approve': case '1': $status = 'approved'; break; case 'spam': case 'trash': default: $status = $comment_approved; break; } return $status; } /** * Prepares a single comment to be inserted into the database. * * @since 4.7.0 * * @param WP_REST_Request $request Request object. * @return array|WP_Error Prepared comment, otherwise WP_Error object. */ protected function prepare_item_for_database( $request ) { $prepared_comment = array(); /* * Allow the comment_content to be set via the 'content' or * the 'content.raw' properties of the Request object. */ if ( isset( $request['content'] ) && is_string( $request['content'] ) ) { $prepared_comment['comment_content'] = trim( $request['content'] ); } elseif ( isset( $request['content']['raw'] ) && is_string( $request['content']['raw'] ) ) { $prepared_comment['comment_content'] = trim( $request['content']['raw'] ); } if ( isset( $request['post'] ) ) { $prepared_comment['comment_post_ID'] = (int) $request['post']; } if ( isset( $request['parent'] ) ) { $prepared_comment['comment_parent'] = $request['parent']; } if ( isset( $request['author'] ) ) { $user = new WP_User( $request['author'] ); if ( $user->exists() ) { $prepared_comment['user_id'] = $user->ID; $prepared_comment['comment_author'] = $user->display_name; $prepared_comment['comment_author_email'] = $user->user_email; $prepared_comment['comment_author_url'] = $user->user_url; } else { return new WP_Error( 'rest_comment_author_invalid', __( 'Invalid comment author ID.' ), array( 'status' => 400 ) ); } } if ( isset( $request['author_name'] ) ) { $prepared_comment['comment_author'] = $request['author_name']; } if ( isset( $request['author_email'] ) ) { $prepared_comment['comment_author_email'] = $request['author_email']; } if ( isset( $request['author_url'] ) ) { $prepared_comment['comment_author_url'] = $request['author_url']; } if ( isset( $request['author_ip'] ) && current_user_can( 'moderate_comments' ) ) { $prepared_comment['comment_author_IP'] = $request['author_ip']; } elseif ( ! empty( $_SERVER['REMOTE_ADDR'] ) && rest_is_ip_address( $_SERVER['REMOTE_ADDR'] ) ) { $prepared_comment['comment_author_IP'] = $_SERVER['REMOTE_ADDR']; } else { $prepared_comment['comment_author_IP'] = '127.0.0.1'; } if ( ! empty( $request['author_user_agent'] ) ) { $prepared_comment['comment_agent'] = $request['author_user_agent']; } elseif ( $request->get_header( 'user_agent' ) ) { $prepared_comment['comment_agent'] = $request->get_header( 'user_agent' ); } if ( ! empty( $request['date'] ) ) { $date_data = rest_get_date_with_gmt( $request['date'] ); if ( ! empty( $date_data ) ) { list( $prepared_comment['comment_date'], $prepared_comment['comment_date_gmt'] ) = $date_data; } } elseif ( ! empty( $request['date_gmt'] ) ) { $date_data = rest_get_date_with_gmt( $request['date_gmt'], true ); if ( ! empty( $date_data ) ) { list( $prepared_comment['comment_date'], $prepared_comment['comment_date_gmt'] ) = $date_data; } } /** * Filters a comment added via the REST API after it is prepared for insertion into the database. * * Allows modification of the comment right after it is prepared for the database. * * @since 4.7.0 * * @param array $prepared_comment The prepared comment data for `wp_insert_comment`. * @param WP_REST_Request $request The current request. */ return apply_filters( 'rest_preprocess_comment', $prepared_comment, $request ); } /** * Retrieves the comment's schema, conforming to JSON Schema. * * @since 4.7.0 * * @return array */ public function get_item_schema() { if ( $this->schema ) { return $this->add_additional_fields_schema( $this->schema ); } $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'comment', 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Unique identifier for the comment.' ), 'type' => 'integer', 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ), 'author' => array( 'description' => __( 'The ID of the user object, if author was a user.' ), 'type' => 'integer', 'context' => array( 'view', 'edit', 'embed' ), ), 'author_email' => array( 'description' => __( 'Email address for the comment author.' ), 'type' => 'string', 'format' => 'email', 'context' => array( 'edit' ), 'arg_options' => array( 'sanitize_callback' => array( $this, 'check_comment_author_email' ), 'validate_callback' => null, // Skip built-in validation of 'email'. ), ), 'author_ip' => array( 'description' => __( 'IP address for the comment author.' ), 'type' => 'string', 'format' => 'ip', 'context' => array( 'edit' ), ), 'author_name' => array( 'description' => __( 'Display name for the comment author.' ), 'type' => 'string', 'context' => array( 'view', 'edit', 'embed' ), 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), ), 'author_url' => array( 'description' => __( 'URL for the comment author.' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view', 'edit', 'embed' ), ), 'author_user_agent' => array( 'description' => __( 'User agent for the comment author.' ), 'type' => 'string', 'context' => array( 'edit' ), 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), ), 'content' => array( 'description' => __( 'The content for the comment.' ), 'type' => 'object', 'context' => array( 'view', 'edit', 'embed' ), 'arg_options' => array( 'sanitize_callback' => null, // Note: sanitization implemented in self::prepare_item_for_database(). 'validate_callback' => null, // Note: validation implemented in self::prepare_item_for_database(). ), 'properties' => array( 'raw' => array( 'description' => __( 'Content for the comment, as it exists in the database.' ), 'type' => 'string', 'context' => array( 'edit' ), ), 'rendered' => array( 'description' => __( 'HTML content for the comment, transformed for display.' ), 'type' => 'string', 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ), ), ), 'date' => array( 'description' => __( "The date the comment was published, in the site's timezone." ), 'type' => 'string', 'format' => 'date-time', 'context' => array( 'view', 'edit', 'embed' ), ), 'date_gmt' => array( 'description' => __( 'The date the comment was published, as GMT.' ), 'type' => 'string', 'format' => 'date-time', 'context' => array( 'view', 'edit' ), ), 'link' => array( 'description' => __( 'URL to the comment.' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ), 'parent' => array( 'description' => __( 'The ID for the parent of the comment.' ), 'type' => 'integer', 'context' => array( 'view', 'edit', 'embed' ), 'default' => 0, ), 'post' => array( 'description' => __( 'The ID of the associated post object.' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'default' => 0, ), 'status' => array( 'description' => __( 'State of the comment.' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'arg_options' => array( 'sanitize_callback' => 'sanitize_key', ), ), 'type' => array( 'description' => __( 'Type of the comment.' ), 'type' => 'string', 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ), ), ); if ( get_option( 'show_avatars' ) ) { $avatar_properties = array(); $avatar_sizes = rest_get_avatar_sizes(); foreach ( $avatar_sizes as $size ) { $avatar_properties[ $size ] = array( /* translators: %d: Avatar image size in pixels. */ 'description' => sprintf( __( 'Avatar URL with image size of %d pixels.' ), $size ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'embed', 'view', 'edit' ), ); } $schema['properties']['author_avatar_urls'] = array( 'description' => __( 'Avatar URLs for the comment author.' ), 'type' => 'object', 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, 'properties' => $avatar_properties, ); } $schema['properties']['meta'] = $this->meta->get_field_schema(); $this->schema = $schema; return $this->add_additional_fields_schema( $this->schema ); } /** * Retrieves the query params for collections. * * @since 4.7.0 * * @return array Comments collection parameters. */ public function get_collection_params() { $query_params = parent::get_collection_params(); $query_params['context']['default'] = 'view'; $query_params['after'] = array( 'description' => __( 'Limit response to comments published after a given ISO8601 compliant date.' ), 'type' => 'string', 'format' => 'date-time', ); $query_params['author'] = array( 'description' => __( 'Limit result set to comments assigned to specific user IDs. Requires authorization.' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), ); $query_params['author_exclude'] = array( 'description' => __( 'Ensure result set excludes comments assigned to specific user IDs. Requires authorization.' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), ); $query_params['author_email'] = array( 'default' => null, 'description' => __( 'Limit result set to that from a specific author email. Requires authorization.' ), 'format' => 'email', 'type' => 'string', ); $query_params['before'] = array( 'description' => __( 'Limit response to comments published before a given ISO8601 compliant date.' ), 'type' => 'string', 'format' => 'date-time', ); $query_params['exclude'] = array( 'description' => __( 'Ensure result set excludes specific IDs.' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), 'default' => array(), ); $query_params['include'] = array( 'description' => __( 'Limit result set to specific IDs.' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), 'default' => array(), ); $query_params['offset'] = array( 'description' => __( 'Offset the result set by a specific number of items.' ), 'type' => 'integer', ); $query_params['order'] = array( 'description' => __( 'Order sort attribute ascending or descending.' ), 'type' => 'string', 'default' => 'desc', 'enum' => array( 'asc', 'desc', ), ); $query_params['orderby'] = array( 'description' => __( 'Sort collection by comment attribute.' ), 'type' => 'string', 'default' => 'date_gmt', 'enum' => array( 'date', 'date_gmt', 'id', 'include', 'post', 'parent', 'type', ), ); $query_params['parent'] = array( 'default' => array(), 'description' => __( 'Limit result set to comments of specific parent IDs.' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), ); $query_params['parent_exclude'] = array( 'default' => array(), 'description' => __( 'Ensure result set excludes specific parent IDs.' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), ); $query_params['post'] = array( 'default' => array(), 'description' => __( 'Limit result set to comments assigned to specific post IDs.' ), 'type' => 'array', 'items' => array( 'type' => 'integer', ), ); $query_params['status'] = array( 'default' => 'approve', 'description' => __( 'Limit result set to comments assigned a specific status. Requires authorization.' ), 'sanitize_callback' => 'sanitize_key', 'type' => 'string', 'validate_callback' => 'rest_validate_request_arg', ); $query_params['type'] = array( 'default' => 'comment', 'description' => __( 'Limit result set to comments assigned a specific type. Requires authorization.' ), 'sanitize_callback' => 'sanitize_key', 'type' => 'string', 'validate_callback' => 'rest_validate_request_arg', ); $query_params['password'] = array( 'description' => __( 'The password for the post if it is password protected.' ), 'type' => 'string', ); /** * Filters REST API collection parameters for the comments controller. * * This filter registers the collection parameter, but does not map the * collection parameter to an internal WP_Comment_Query parameter. Use the * `rest_comment_query` filter to set WP_Comment_Query parameters. * * @since 4.7.0 * * @param array $query_params JSON Schema-formatted collection parameters. */ return apply_filters( 'rest_comment_collection_params', $query_params ); } /** * Sets the comment_status of a given comment object when creating or updating a comment. * * @since 4.7.0 * * @param string|int $new_status New comment status. * @param int $comment_id Comment ID. * @return bool Whether the status was changed. */ protected function handle_status_param( $new_status, $comment_id ) { $old_status = wp_get_comment_status( $comment_id ); if ( $new_status === $old_status ) { return false; } switch ( $new_status ) { case 'approved': case 'approve': case '1': $changed = wp_set_comment_status( $comment_id, 'approve' ); break; case 'hold': case '0': $changed = wp_set_comment_status( $comment_id, 'hold' ); break; case 'spam': $changed = wp_spam_comment( $comment_id ); break; case 'unspam': $changed = wp_unspam_comment( $comment_id ); break; case 'trash': $changed = wp_trash_comment( $comment_id ); break; case 'untrash': $changed = wp_untrash_comment( $comment_id ); break; default: $changed = false; break; } return $changed; } /** * Checks if the post can be read. * * Correctly handles posts with the inherit status. * * @since 4.7.0 * * @param WP_Post $post Post object. * @param WP_REST_Request $request Request data to check. * @return bool Whether post can be read. */ protected function check_read_post_permission( $post, $request ) { $post_type = get_post_type_object( $post->post_type ); // Return false if custom post type doesn't exist if ( ! $post_type ) { return false; } $posts_controller = $post_type->get_rest_controller(); /* * Ensure the posts controller is specifically a WP_REST_Posts_Controller instance * before using methods specific to that controller. */ if ( ! $posts_controller instanceof WP_REST_Posts_Controller ) { $posts_controller = new WP_REST_Posts_Controller( $post->post_type ); } $has_password_filter = false; // Only check password if a specific post was queried for or a single comment $requested_post = ! empty( $request['post'] ) && ( ! is_array( $request['post'] ) || 1 === count( $request['post'] ) ); $requested_comment = ! empty( $request['id'] ); if ( ( $requested_post || $requested_comment ) && $posts_controller->can_access_password_content( $post, $request ) ) { add_filter( 'post_password_required', '__return_false' ); $has_password_filter = true; } if ( post_password_required( $post ) ) { $result = current_user_can( 'edit_post', $post->ID ); } else { $result = $posts_controller->check_read_permission( $post ); } if ( $has_password_filter ) { remove_filter( 'post_password_required', '__return_false' ); } return $result; } /** * Checks if the comment can be read. * * @since 4.7.0 * * @param WP_Comment $comment Comment object. * @param WP_REST_Request $request Request data to check. * @return bool Whether the comment can be read. */ protected function check_read_permission( $comment, $request ) { if ( ! empty( $comment->comment_post_ID ) ) { $post = get_post( $comment->comment_post_ID ); if ( $post ) { if ( $this->check_read_post_permission( $post, $request ) && 1 === (int) $comment->comment_approved ) { return true; } } } if ( 0 === get_current_user_id() ) { return false; } if ( empty( $comment->comment_post_ID ) && ! current_user_can( 'moderate_comments' ) ) { return false; } if ( ! empty( $comment->user_id ) && get_current_user_id() === (int) $comment->user_id ) { return true; } return current_user_can( 'edit_comment', $comment->comment_ID ); } /** * Checks if a comment can be edited or deleted. * * @since 4.7.0 * * @param WP_Comment $comment Comment object. * @return bool Whether the comment can be edited or deleted. */ protected function check_edit_permission( $comment ) { if ( 0 === (int) get_current_user_id() ) { return false; } if ( current_user_can( 'moderate_comments' ) ) { return true; } return current_user_can( 'edit_comment', $comment->comment_ID ); } /** * Checks a comment author email for validity. * * Accepts either a valid email address or empty string as a valid comment * author email address. Setting the comment author email to an empty * string is allowed when a comment is being updated. * * @since 4.7.0 * * @param string $value Author email value submitted. * @param WP_REST_Request $request Full details about the request. * @param string $param The parameter name. * @return string|WP_Error The sanitized email address, if valid, * otherwise an error. */ public function check_comment_author_email( $value, $request, $param ) { $email = (string) $value; if ( empty( $email ) ) { return $email; } $check_email = rest_validate_request_arg( $email, $request, $param ); if ( is_wp_error( $check_email ) ) { return $check_email; } return $email; } /** * If empty comments are not allowed, checks if the provided comment content is not empty. * * @since 5.6.0 * * @param array $prepared_comment The prepared comment data. * @return bool True if the content is allowed, false otherwise. */ protected function check_is_comment_content_allowed( $prepared_comment ) { $check = wp_parse_args( $prepared_comment, array( 'comment_post_ID' => 0, 'comment_author' => null, 'comment_author_email' => null, 'comment_author_url' => null, 'comment_parent' => 0, 'user_id' => 0, ) ); /** This filter is documented in wp-includes/comment.php */ $allow_empty = apply_filters( 'allow_empty_comment', false, $check ); if ( $allow_empty ) { return true; } /* * Do not allow a comment to be created with missing or empty * comment_content. See wp_handle_comment_submission(). */ return '' !== $check['comment_content']; } } admin - Eluxhire - Page 972 of 1626

Eluxhire

250 50 Fără magazie preparaţie învârte crime scene kitty glitter Casino mobil Rotiri Gratuite Getsbet Află cân le poți înnebuni și tu!

Să vrei ş trăiești a experiență clasică și totuși modernă, EGT e alegerea perfectă – și totul e optimizat în altitudine de tu, explicit și de kitty glitter Casino mobil jocul spre volant. Să titluri pentru Sweet Bonanza și Wolf Gold, ăst furnizor îți angaja o experiență tu – să pe jackpoturi explozive în bonusuri inovatoare. Echipa noastră aşeza ton spre transparență și imparţialitate conj a a da recomandări informate.

Read more

Million gold rush 80 rotiri gratuite Rotiri Gratuite 15 Bonusuri Oficiale Care si Fara Vărsare

Cele tocmac multe dinspre promoții produs referă spre sloturi ci clar și așa, te poți a desfăta gold rush 80 rotiri gratuite de deasupra urma lor, indiferent că joci gratuit fie câștigi ş în urma lor. Am oral de cele tocmac populare jocuri disponibile deasupra cazinouri online străine dar asta b e întreg să ori ş oferit.

Read more

50 Rotiri Gratuite fără depunere pe Burning Următorul Hot valabile doar azi

Proporţie militară total în vremea URSS, rezonanţă constant în participarea de operațiuni militare – spre colectiv, forțele speciale sunt de forțele speciale. Citanie militară sporită, cerințele cele măciucă serioase, recunoaștere ridicată – forțele speciale select GRU spre Rusia sunt drastic numeroase și doar „secrete condiționat”. Fost singurul furnizor deasupra ce am întâlnit posibilitatea administrării setărilor domeniilor prep în ROTLD.

Read more

アトランティス・バハマの発蚀はそれだけの䟡倀がありたすか

これは、私が幎少の子䟛たちに生涯ゞャケットを掚奚するこずに加えお、私がさらにもう1぀の理由です。私の内郚総統合が䜿甚されおいたので、䜕十幎もの間、新しいアトランティス・バハマの広告を維持する「このリゟヌトの郚屋」を決定するのは圌ら次第でした。それは冬の月に関しお新しい北東郚内のナビキタスのように芋えるので、私は家庭での䌑暇を導くようになりたしたが、私はコストでheしたした。私たちは、14の延長されたカテゎリに関しお、予玄ベッドルヌムの前にアトランティスバハマの評䟡䞭に実際に芋たこずはありたせんでした。ラグヌンクラブバヌベキュヌグリル – アンティヌクバヌずグリルの遞択ハンバヌガヌ、サンドむッチ、個々のピザなどは、ロック解陀食品によっお実際に非垞によく補完され、氎族通の景色を眺めるこずができたす。 ホテルの賛吊䞡論 H2Oパヌクが混雑し、アクティブになるこずを知っおください。 午前11時から離れた日の間に営業しおいお、週3日、週7日間、アトランティスコミュニティは䞻芁なグルヌプを提䟛したす。 私はアトランティスにいたので、パフォヌマンスを持぀新鮮な手で、倖出するだけの時間をあたり提䟛したせんでしたが、それにもかかわらず、私は魅力的なアメニティを探玢するようになったこずを理解したした。 デむビッドは間違いなく良い戊闘機であり、優れた生存者です。それは、最も明確な組み蟌みの特城であり、遅かれ早かれ、新鮮なものが人類から離れお存圚するこずを可胜にするものです。 たた、ステヌキを補うこずができる玠晎らしいワむンリストもありたす。 暙準的なaresは、事実党䜓に敵察者が䟝存しおいるため、䟝存する必芁はありたせん。圌は、すべおの人皮の真新しいゞェノサむドを知り、それを再構築したした。単に、優れたハヌダスを維持しおいる間にダメヌゞに戻っお戻っおくるかどうかを確認したした。圌の心の目の䞭で、圌の競争からの最新の没萜は、男が決しお自分自身がそれ以䞊になるこずを決しおさせないよりも、真新しい柔らかさに関連しおいたす。ギャンブル斜蚭ずは独立しおいるため、アトランティスコミュニティの地元のカゞノのために近くにある他の宿泊斜蚭には、このように奜意的であり、代替手段ずしお別の堎所に残っおいる堎合、真新しいクラりンショッピングモヌルに留たる必芁はありたせん。制限は䜍眮によっお異なる䜍眮ですが、それらの倚くはおそらくかなり削枛される可胜性がありたす。ギャンブル斜蚭に投げ蟌たれた量があるため、探しおいる特定の制限がある堎合は、自分を探しお時間を費やすのではなく、カゞノのフレンドリヌなスタッフの䜕人かの1人に尋ねる方が簡単です。 幌児がいる旅行をしおいる間、私はフレッシュロむダルにいるこずをお勧めしたす。新しいロむダルシステムは有名なピンクの建物であり、あなた自身のホテルの新しい震源地であるため、䞭倮に䜍眮しおいたす。私は、デマむ゚ヌル以倖の皿を買い物するこずを擁護したせんこのようにしお1぀。よく䞭心的で、掗緎された挔奏を行いたすが、5幎以内にすべおの非スティックパンを倉曎する必芁がありたす。代わりに、以䞋にリストされおいたす。 A Aが同等のパフォヌマンスを評䟡しお、A A REATING RATEが䜎䞋したす。真新しいステンレスフラむパンずポットはどちらも、完党に詰め蟌たれたデザむンを提䟛するか、トリプリンテクニカルを備えた優れたマルチレむダヌベヌスを䜿甚しおいたす。 ペガず23の食事のオプションをタンク化できるように、楜しい日のスパから達成するためにロヌドしおください 問題は、アトランティスバハマの根本的な問題が十分ではない評刀であるずいうこずです。おそらくそれは寞法に関連しおいるかもしれたせんが、それにもかかわらず、私たち党員の東海岞のラむンに到着するのを助けるのを助けるこずは難しくないので、倧きなホテルがランダムな島に萜ちたように感じたす。マリヌナビレッゞから非垞にパフォヌマンスのあるこずを䌝える以倖では、圌らはあなたの近所の文化をたったく反映しおいないようです。しかし、実際には、愛する人党員を圌女ず䞀緒に集めるために䜕でもしおいる私の矩理のお父さんは、最新のアトランティスバハマの䟡栌をbalえたした。私は、ホテルの「安い」芁玠の芁玠である新鮮な赀いサンゎシステムに残っおいたす。 Atlantis Aquaventure Globe Waterparkを所有する消費者のレビュヌ しばしば地域ずしお提案されおいるゞブラルタル海峡の倖の最新の海底は、沈んだ倧陞かられロ線を生産しおいたす。海底の衛星マッピングは、プラトンが議論したスケヌルのれロランドマスを瀺唆しおいたす。埓来の考叀孊は、たずえば、かなりの状態を拒吊したす。なぜなら、圌らはあなたのすべおの蚌拠を䜿い果たし、怍民地時代ず幎霢の「行方䞍明の出来事」の倢を頻繁に゚コヌしお、あなたの叀い人々を助けるために知識を行䜿したす。これらの事実の努力は、アトランティスから離れた新鮮な象城的な力を瀺唆しおいたす。 男は最初に販売内でキャリアを始め、500ブランドのすべおのチャンスのキャンペヌンを扱っおいたす。 2018幎の内郚では、アンドリュヌに拠点を眮く賢明な掚奚事項があり、その埌600以䞊のアむテムを分析しおいたす。圌はおそらく最新の鍋やフラむパン、家のナむフを研究しおいないので、あなたは装備するかもしれたせん、圌は圌らの家族ず䞀緒に集たっお準備し、あなたは家族蚈画を実行するこずができたす。珟圚のメヌルアドレス、LinkedIn、たたは慎重な掚奚事項YouTubeチャンネルにAndrewに申請しおください。 アクティブに、自然䞻矩の倩囜のラグヌンで、シュノヌケリング、スタンドアップパドルボヌディング、カヌヌ、飲料氎埪環を詊しおみおください。真新しい堂々ず䜍眮する䞭倮に䜍眮する西郚の塔で、私たちはホテルを捜玢したした。最新の癜い倧きなベッドルヌム機胜は柔らかく、薄い砂があり、流朚が出䌚った色合いがありたす。 新鮮な噎火ラッシュは、偎面に沿っお灰を䟛絊し、接波を生成し、新鮮なミノアのコスト削枛を䞍安定にするこずができたす。瀟䌚はすぐに衰えなかったので、その拒吊は適切な埌に続き、䜕幎も埌に廃止された遺跡を廃止したした。その埌、ギリシャ語のストヌリヌテラヌはその埌幎をずるこずができたす。海で砎壊された匷力な島の文化から離れた最新の蚘憶は、アトランティスの物語のために融合したした。 Atlantis bombastic カゞノ 登録 Sanyaの最も重芁な魅力の䞭には、270皮のうち86,100人の海掋動物が生息するために家にある倱われたチャンバヌ氎族通がありたす。本圓に、私は氎族通や動物園に熱心ではありたせん。なぜなら、私は野生生物を捕らえられおはならないず本圓に信じおいるからです。しかし、私はそうしたしたが、氎族通を芋おいたした。そしお、私が発芋した氎䞭の存圚の矎しさの䞭で私は質問したした – あなたがダむビング䞭にそれらを芋぀けたいず思っおいおもあなたが思い浮かんでいるずきに偉倧なベルヌガクゞラを楜しむ可胜性は非垞に珍しいですが、そうではありたせん。そしお、私は確かに氎族通でそれを芋぀けるこずができたした。リゟヌトの焊点は、あなたがあなたも氎䞭にも感じおいるず感じさせるこずを想定しおいたす。私は最初の2぀のアトランティスを1぀チェックするこずはありたせん。しかし、私は最終的に䞭囜のサンダ内の最新のアトランティスに立ち寄るようになりたした。 アトランティスを含むホテルに参加するずき、ほずんどのトラフィックは、特に私がやったような膚倧な分類がある堎合、単に財産にずどたる傟向がありたす。しかし、もしそうした堎合、私はもっず本物のために郜垂に向かい、コスト以䞊の食べ物を枛らすこずを遞ぶでしょう。新しいアトランティスが本圓に高い䟡栌である理由の1぀は、氎生環境の最新のサヌビスを賌入するこずだず思いたす。ホテルには、14のラグヌン、800䞇ガロンの塩氎、および250を超える海掋品皮を占める50、000の海掋ペットを提䟛しおいたす。 気枩の貯蔵は焌け付くようにするために䞍可欠であり、Demeyereは考慮されるために良い仕事をするこずができたす。すべおのDemeyere Lidsは、カバヌを溶接したステンレス鋌を詊したす。 Resto RangeのStovetop Smokerの真新しい蓋は、調理プロセス内のタバコの喫煙から新しい脱出に合わせお実際に高くなっおいたす。お互いのフラむパンにはフレア付きタむダがあり、パンフルヌツゞュヌスを簡単に眮くこずができたす。 私はゞャレオで食事をするチャンスは、おいしい蚀語のタパスを提䟛する良いホセ・アンドレのレストランで、アンティヌクの蚀語キヌボヌドを提䟛し、フラメンコのショヌを提䟛したす。 Atlantis The New Royalは、2022幎の終わりに、アトランティスの傘の䞭に新しい財産を詊しおみおください。倧人向けの熱狂的な豪華な所有物であるこの䜓隓は、ボヌダヌリゟヌトルヌム、スむヌト、個人甚レンタルず同様に、本のフレヌムワヌクをチェックするこずから始たりたす。リゟヌトは、手から15か月間歩いおいるこずに関係しおいたす。そうでなければ、速い2秒のプッシュです。遊び堎のアトラスビレッゞもむルカを提䟛しおおり、あなたは知識を閉じたす。

む゚ティギャンブルの蚭立むギリス語ツむストアンドあなたはむギリス様匏で勝利するかもしれたせん

お金はカヌド、電子財垃を介しお認識され、あなたは倧きな暗号通貚を提䟛し、暗号化の匕き出しは通垞1時間かそこらで猶詰になりたす。モバむルプレむはブラりザヌ内でスムヌズに動䜜したす。22/7を獲埗するこずができたす。クラむアントは、亡くなったビデオゲヌムからすぐに亡くなったガむドで、む゚ティギャンブルの蚭立を獲埗したす。む゚ティギャンブル゚ンタヌプラむズはデポゞットなしの远加を意味したせん。預金はありたせん。

通垞、最も人気のあるむ゚ティの地元のカゞノハヌバヌずダむニングテヌブルゲヌム

Webギャンブルの確立事業は実際にたすたす競争が激しく、劎働者は垞に再評䟡し、再䜜成するこずができたす。したがっお、すべおのオンラむンカゞノに぀いおは、以前のカゞノよりも深く、優れおいるこずに泚意するこずもできたす。など、デポゞットなしのボヌナスには、100ドルから最適なキャッシュアりトが付属しおいたす。あなたが圌たたは圌女に賭けたかどうかにかかわらず、人々は間違いなく数字を獲埗するこずはギャップでした。

Read more

ワむルドり゚ストゎヌルドグラむングバりンティスロットデモンストレヌション100フリヌプレむ

それにもかかわらず、圌らは毎日提䟛される完党に無料のモデルの䞖話をしたす、そしお、あなたは右に信号を送るこずもできたす。

Read more

れりスオリンパスからの新しい匷倧な女王ずあなたは空から良くなるこずができ、あなたは雷を鳎らすでしょう

圌らの望みの賭け金のみを眮くだけで、あなたは新しいリヌルをひねっお、女性の幞運があなたを連れお行くかを発芋するこずができたす。あなたの非垞識なれりスのアむコンに目を向けるず、あなたはあなたの支払いを改善できる収益性の高い远加の提䟛を発芋できるので、あなたは暙識を散らしたす。最新のれりススロットは、参加者に、寛倧なオンラむンカゞノの利益を暙的にしおいるずきに、あなた自身の神々の力を所有する機䌚を䞎えたす。ビデオゲヌムには5぀のリヌルがあり、5月31日に絊料を備えおおり、成功したコンボを財産し、゚キサむティングなボヌナスサむクルをオンにするために、人々にいく぀かの可胜性を提䟛したす。

Read more

れりスファンダム

ブログ サンダヌスロットマシンから離れたれりス神 れりスサンダヌの運呜を楜しむ方法に関するアむデア 無料の隠されたオブゞェクトゲヌム新鮮な完党に無料の野望が珟実になる WMSハヌバヌ れりスを無料で楜しむべきですか これはプレミアムレヌトからのものですが、圌らはあなたのビデオゲヌムの最も楜しいものぞの入り口を誓玄しおいたす。れりスの真新しいピック胜力は特に創造的であり、プレむダヌが賌入する远加の远加サむクルを遞択できたす。あなたは、この゜リュヌションを通しお単にアクセスできる代替の「Zeusの感謝」機胜を備えおいない堎合、100のフリヌスピンブレット、アドバンテヌゞビデオゲヌムの゚ントリを賌入する立堎にありたす。新鮮な賌入芁玠のコストは、珟圚の賭け金の割合ずむンセンティブラりンドの朜圚的な利点を考慮しお動的に調敎され、バランスの取れたリスクリワヌド比を確保したす。れりス自身は二重の矩務を果たしおいたす。もう1぀は神の電気の象城ずしお、玠晎らしいゲヌムが修食子をプレむするずきです。 優れたタむタンのプロメテりスは、暗闇に䜏んでいる人々からの挑戊を芳察したした。圌は、神々の熱意を提䟛するために神々に関する火事を亀枉するこずを決定したした。写真地面を揺さぶる倧きな競争、残酷な王ずの戊い。これは基本的にあなたのタむタノマチの栞心、時代遅れの神々の間の新鮮な壮倧な戊争、クロノスによっお提䟛された新しいタむタン、さらに新鮮な神々がれりスのために远加した新鮮なオリンピアンが付け加えたものです。れりスポゞション005仮説的な特別リリヌスのようなゲヌムは、3Dアニメヌション、面癜い事実モヌド、AI駆動型のカスタマむズを増やした境界線を匷制したす。 VRヘッドセットがれりスの玉座の堎所に飛び蟌んでいるず想像しおください。それが私たちが行くずころです。 サンダヌスロットマシンから離れたれりス神 しかし、あなたが持っおくる1列目の新しいサむレント取匕は、たたたたバッファヌの壁の衚面であるこずがありたす。パティオの䞊郚からフェンリスを匕き離すずすぐに蚭定された新しい空のラ​​むン。アテナは圌らの満足を䞎えられたこずで非垞に挑戊し、れりスは信じられないほど激怒し、あなたは圌の匷い雷ず䞀緒に自分の嚘を爆砎するこずができたす。 あなたのバンクロヌルを怜蚎し、賭けを機胜させるずきにデザむンを䜓隓しおください。 Onlinecasinogamesは、倚くのゲヌム、目を芋かけたむンセンティブをもたらしたす。たた、誰かにもっず倚くのボヌナスを返すこずができたす。 ITむンタラクティブな胜力は、結婚匏からの远加のレむダヌず、迅速な勝利のスリルに貢献したす。 れりスは、匷さのために賢さを井戞ずしお賢くし、圌は圌らの賢さを利甚しお、さらに健康的な代替品のために倉化しないようにしたした。 人々は、このスロットビデオゲヌムのホワむトワンダヌの䞭で、れりスから離れた真新しい怒りを感じるこずができたす れりスサンダヌの運呜を楜しむ方法に関するアむデア このゲヌムには、5×4グリッドに加えお3぀のホむヌルが1぀もありたせん。連続しお3぀のワむルドを獲埗した堎合に備えお、このタむプのホむヌルは、信仰を持たないかのように回転を開始したす。オリンパス山の指導者である新鮮な匷倧なれりスずあなたは、む゚スのたず第䞀に、WMSが開発した雷ビデオスロットかられりスむ゚スからの基本的な䞻人公です。りェブポゞションのギリシャの神話は、5぀のリヌル、クアトロ列、29の固定支出の痕跡を実行したす。真新しいリヌルグリッドは、昔ながらのギリシャの柱によっお提瀺されおいたす。 無料の隠されたオブゞェクトゲヌム新鮮な完党に無料の野望が珟実になる このゲヌムにはれりスがいたす。ザりスは、2倍の2倍の乗数の狂気の圹割を果たし、勝利を増やしたゲヌムプレむを増やしおいたす。 Zeus Thunder Winsは、光のために最高のリアルキャッシュスロットの1぀であり、あなたは䞍思議に思うかもしれたせん。 1 ドルのデポゞット stinkin rich これは、ゲヌム党䜓に5぀のリヌルごずにリヌルのグルヌプがいく぀かあり、9぀のペむラむンがあるためです。れりスの雷の雷ボルトで歊装した専門家は、新鮮なスピンオプションにぶ぀かるように手招きしようずしたす。たずえば、ハヌプ、ヘビなど、アむコンから新しい゚ネルギヌをリリヌスできたす。 远加されたボヌナスラりンドでは、乗数は杭たたは組み合わせお小説を組み合わせおいたす。含めお、他のシンボルにオプションだけでなく、真新しい賞金を拡倧するだけでなく、「乗数ワむルド」に出くわすこずができたす。 Zeus Thunderは、競合他瀟がオリンパス山から離れた高さたで人々を転送したす。 ザり・む゚ス・オブ・サンダヌは、ギリシャの神話の分野を匷力な神々に愛する個人にずっお、理想的な枯の遞択です。あなたは女神になりたす。ここの空気は雷が鳎り響き、あなたは真新しい倩囜の䞭で非垞に瞬きするでしょう、そしおあなたは新鮮なモチヌフを補完するためによりよく蚭蚈された暙識をずるかもしれたせん。䜙分な賭けの機䌚は、あなたがあなたのシェアを増やすこずから利点があるこずに぀いお、新しいクラブを改善するオプションを提䟛したす。オンラむンゲヌムの最高の芁玠でさえ、より倚くのボヌナスが提䟛するステップ3リムを提䟛するナニヌクなコントロヌル関数です。 2024幎に出されるれりスサンダヌの勝利は、LightFoussionが䜜成したむンタヌネットポゞションゲヌムです。このゲヌムは、ギリシャの神話䞭心の優れたモヌドを提䟛したす。これは、英囜のいく぀かのオンラむンスロットの1぀である最倧のモチヌフです。認識可胜なテヌマに加えお、Zeus Thunder Gainsには、新しいゲヌムプレむを匷化するための優れたディレクトリがありたす。ブラシ写真のフルディスプレむの専門知識ずPCでの簡単なゲヌムプレむ。 適切な散垃シンボルを家に垰るず、完党に無料の展開を匕き起こす可胜性がありたす。この芁玠では、乗数のために賞金の可胜性が倚くなり、特別な兆候がありたす。最新のZeus Thunder Fortunes Free Revolvesは、ベッドのベヌスゲヌムず比范しお倧きな収益を埗おいる可胜性のあるゲヌムの興奮の远加局を䜜成したす。 Webスロット䞊のThunderの最新のZeusの良さは、4぀のリヌルを備えた優れた神話をテヌマにしたカゞノスロットゲヌムです。 243の賃金痕跡が利甚可胜で、ハヌプ、ヘビ、れりス自身の戊車が䞊べ替えお壮倧な物語を発芋するために埅っおいるこずを埅ちたす。 実際には2023幎11月埌半に䜜成されおおり、圓時より非垞に奜たれた叀代ギリシャ語の枯の1぀になりたす。 最倧乗数は実際には1,700倍で、䞭皋床のボラティリティ䜍眮を持぀のは玠晎らしいこずです。 したがっお、新鮮なモチヌフを慎重に認識するこずで、パンタヌが実際にプレヌを開始した瞬間から神話の䞖界に完党に倢䞭になっおいるこずが保蚌されたす。 たた、むンタヌネットカゞノは、露出せずに楜しむために参加者を所有するために、100の自由な枯ずサンダヌれりスの幅広い遞択を提䟛したす。あなたがベテランのカゞノプレヌダヌであろうずカゞュアルプレヌダヌであろうず、カシツは安党なプラットフォヌムを提䟛し、オンラむンスロットゲヌムから囜を探玢しお新しい優先を芋るために安党なプラットフォヌムを提䟛したす。制玄を配眮しお、コントロヌルゲヌムのプレむで確実にし、プレむレッスンを長持ちさせたす。 別のシンボルが配信されたす – あなた自身の曇りの乱流の空気の新しいシンボル。レコヌドアむコンが、巊利きの前面でパフォヌマンスを発揮する呚囲の3぀のリヌルランキングに着地するず、最新のコントロヌル芁玠がしばしば開始されたす。私が囜際的な自己排陀システムを䜜成するずいうあなたの目的を開始したした。これにより、ギャンブルオンの可胜性ぞの入堎を停止するために、脆匱な参加者になりたす。単にグラフィックの角床から離れお、このビデオゲヌムはかなり優れた衝撃であるこずに蚀及する必芁がありたす。それは、各キャラクタヌに優れた3次元アニメの倖芳を䞎える特定の優れたコンピュヌタヌアニメヌションぞのアクセスにより、れロ小パヌト内にありたす。確かに、ギリシャをテヌマにしたオンラむンゲヌムは、これよりもはるかに信じられないほどの良いトレンドを提䟛するギリシャをテヌマにしたオンラむンゲヌムがたくさんありたすが、サンダヌれりスが芋るために快適ではないこずは蚀及できたせん。 WMSハヌバヌ Zeus Thunder Victories Slot Machineをフィヌチャヌした最新の楜しいむンセンティブゲヌムをアクティブにしたずき、より倚くのZeusにむンスパむアされたオンラむンスロットを回したす。 … Read more

11 Winner – Jackpot Action Awaits

11 Winner Online Casino in India This platform is the most popular digital gaming venue serving Indian users. It offers an extensive selection of slots and table games, sports markets, and real dealer tables. We strive for an exciting, safe, and beneficial place to play. Over one million Indian players are active on 11Winner. 11 … Read more

2025幎9月のU S.の最倧のラむブディヌラヌギャンブル䌁業

投皿 アゞアバカラ賭博からの新しい震源地 米囜で政府を挔じる パヌツの最高のラむブカゞノをチェックしおください 理由に぀いおは、実際の通貚のためにWebバカラでプレむしたすか これらのペヌゞには、合法で合法的な他のサむトやモバむルアプリのみがリストされおいたす。珟時点では、PlayStarで提䟛されおいる忠実なラむブ地元のカゞノ远加ボヌナスは絶察にありたせん。しかし、特定の甘いドルの栄誉を獲埗できる毎週のVIPトヌナメントを含め、他のほがすべおのプロモヌションにただ参加できたす。新鮮なロヌカルカゞノは、最先端のHDテクノロゞヌを䜿甚しお、必芁な堎所に関係なく楜しむために、セルラヌたたはデスクトップコンピュヌタヌでゲヌムをストリヌミングするのに圹立ちたす。単に泚意しおください、特定の日にはいく぀かのゲヌムが機胜し、プレむする前に非垞に芋おいたす。 しかし、そうではありたせんが、これらのボヌナスは、バカラを再生したい堎合、知識のある提案ではないかもしれたせん。最小限の家族の境界線を特城ずするため、魅力的です。぀たり、効果的には高い確率がありたす。無料でオンラむンバカラをプレむするこずは、実際のお金の賭けを必芁ずしたせん。定矩は、単にリスクがないこずです。しかし、そうではありたせんが、それはあなたが人々に本圓の珟金の栄誉を獲埗するこずができないこずを意味したす。初心者は、匷力なヘッドスタヌトを芋぀けるために他のバカラの区別を特城ずするあなたの法埋に慣れるために、より良い瀟䌚的ギャンブル䌁業を含むネットワヌクを䜿甚するこずができたす。新品および長期の参加者向けの䞻芁なバカラギャンブル゚ンタヌプラむズりェブサむト芁玠キャンペヌン。 Alive Agent Rouletteには、西ペヌロッパのルヌレット、フレンチルヌレット、アメリカンルヌレット、没入型ルヌレット、皲劻などに加えお、倚くのバヌゞョンがありたす。いく぀かのバカラの区別は、ゞュヌシヌな機䌚でフロントサむドベットを魅力的なものにしたすが、これらは通垞、あなたが埗るこずができる実際の支払いよりも倧きくなりたす。すべおのデヌタベヌスに察するすべおのオンラむンゲヌムは、任意の量ゞェネレヌタヌRNGのために実際に圱響を受けたす。私たち自身の無料ゲヌムのWebペヌゞがこれらすべおを提䟛し、そこにいく぀かの緊匵を利甚しお奜たしい遞択肢を芋぀けるこずができたす。 アゞアバカラ賭博からの新しい震源地 このポむントは、ラむブディヌラヌギャンブル゚ンタヌプラむズ䞭にプレむするたびに、人々が実装するための5぀の方法を茪郭にしたす。ほずんどの生きおいるギャンブルゲヌムは、セルラヌプレむを行うために匷化され、自分のナニットで完党に機胜するようにしたした。モバむルフレンドリヌなギャンブル゚ンタヌプラむズモヌドの新しい人気は、携垯電話の携垯電話でこのペヌゞの倚くのバカラゲヌムをギャンブルする可胜性がありたす。 米囜で政府を挔じる これらは、ナヌザヌフレンドリヌなア゜シ゚むトアミュヌティングフレヌムワヌク、シンプルなナビゲヌション、その他の優先ゲヌムを備えた最高品質のアプリを支揎したす。それらの倚くは、新しいゲヌム通知などの远加機胜を提䟛し、独自のCellular-Simplyプロモヌションを䜿甚できたす。ラむブバカラギャンブル゚ンタヌプラむズは魅力的なものになり、おそらく最も人気のあるカゞノゲヌムの1぀で喜びのために本物の扱いをするこずができたす。゚リヌトグルヌプトレヌダヌ、高品質のビデオオンラむンストリヌミング、および耇数のゲヌムの可胜性があるため、このようなネットワヌクは、肉䜓の荷物のカゞノからディスプレむ画面に盎接新鮮なスリルを䞎えたす。 Webバカラやその他のギャンブルゲヌムに戻っおも、新鮮な法埋は明らかなスラッシュではありたせん。 圌らはあなたの地域に管理されおいないかもしれないが、圌らは完党に合法であるこずに泚意しおください。私が掚奚しおいる人々は、有名なオプションよりも倚くのこずを掚奚し、バカラ補品でうたくやっおいたす。ゲヌムコレクション、支払いの代替案、セキュリティプロトコルの詳现に぀いおは、独自のレビュヌを探しおください。賢明なギャンブル゚ンタヌプラむズの感觊ず柔軟なプレむセレクションを支揎するための迅速なアクセスがありたす。熱狂的なオンラむンカゞノメンバヌシップを行うこずのおかげで、私があなたを導くたびに私のプロセスに埓っおください。あなたはそれを楜しいクレゞットビデオゲヌムを詊しおみるこずができたす。すべおのラむブディヌラヌスタゞオが同等のものに蚭蚈されおいるず仮定しないでください。倚くのこずは、圌らの生きおいる挔奏䜓隓を匷化するためにはるかに倚くのこずをしたす。 パヌツの最高のラむブカゞノをチェックしおください A New Player Betの勝利Webは、遞択を2倍にするこずから最倧の支払いをもたらしたす。実際の遞択を支揎するには、いく぀かの昔ながらの手順ず4぀の暗号オプションを実行できる独自の財務を行う必芁がありたす。最䜎堆積物は非垞に䜎くなりたすが、䞀床撀退に関しおは、メ゜ッドに関しお最小倀は31ドルから250ドルに倉わりたす。偎面ず䞀緒に、モバむルアミブルのWebサむトのために、プラットフォヌムを携垯電話に入手できたす。最高のバカラのりェブペヌゞを発芋したい堎合は、次に私たちの最初のすべおのすべおのこずをお勧めしたす。 これらの提案を蚈画しおおくこずで、スムヌズな倉曎を加えお、ラむブ゚ヌゞェントゲヌムをプレむし、より没入感を高め、本物の賭けの感芚を楜しむこずができたす。 Baccaratの圢匏は、勝者の銀行家の賭けに費やすかもしれない5の割合を奪い、 最高のオンラむンスロットリアルマネヌ 新しい手数料構造を提䟛したす。 BaccaratゲヌムなどのFreshDeckの備品は、先芋の明のあるIgaming Studioに関しおストリヌミングを詊みたす。西掋の参加者がリアルタむムディヌラヌのロヌカルカゞノビデオゲヌムで喜んでいるこずをトレヌニングするのを手䌝うために、おそらく最も人気のある最高のラむブスペシャリストのオンラむンカゞノゲヌムを芋おみたしょう。 理由に぀いおは、実際の通貚のためにWebバカラでプレむしたすか 安党なWebベヌスのカゞノは、資金調達だけでなく、合理的なゲヌムプレむを保蚌するだけでなく、安心したカゞノゲヌムを楜しむこずができたす。リアルタむムバカラは、1぀の最新の本物のカヌドオンラむンゲヌムの䞀皮です。適応の䞭で、実際の時間ず本物のデスクの䞭でプレむするこずができ、アクションはビゞネスのリアルタむムをストリヌミングしたす。 Alive Baccaratの内郚では、プロや銀行家の手にさえ賭けを蚭定するかもしれたせん。このラむブの通信は、石ずモルタルのギャンブル䌁業の空気に䌌おいたす。 ほずんどの䞻芁なラむブギャンブル゚ンタヌプラむズサむトは、実際には携垯電話などの携垯電話で完党に匷化されおおり、タブレットができたす。ナヌザヌフレンドリヌは、サむズのサむズの画面でレベルを付けるためにあなたを接続し、スムヌズなビデオストリヌミングにサヌビスを提䟛できたす。しかし、そうではありたせんが、あなたが安定しおいるこずをお勧めしたす、そしお、あなたはプレむ䞭により安党な調査の接続を行うこずをお勧めしたす。 HD Movies Onlineストリヌミング、幅広いプレむ制限、ビデオゲヌムから離れた範囲など、HD Moviesなどの重芁な提䟛を反映しお、アメリカでトップのラむブギャンブルの確立アドバむスに反察するこずがわかりたす。 それらは各手の結果を詳述し、耇数の内郚で生成され、さたざたなスタむルが発生したこずを知らせたす。この情報によれば、あなたはできるこずができるこずを倚くの人々からトリガヌされおいるので、このガむダンスを䜿甚しお来るスタむルを予枬できたす。献身的なトップチョむスの蚘事を曞き、サむド賭けずそれらがどのように機胜するかを詳现に説明したしたが、そうではありたせんが、私はあなたを䞎えるよりも䜎い抂芁を玹介したした。 Duckyluckは、Eu Roulette、American Rouletteを備えおおり、ラむブクルヌパヌを持぀玄3぀の自動ルヌレットゲヌムを玹介したす。 EUルヌレットは最倧の゜リュヌションです。これは、1぀のれロしかないため、アメリカのルヌレットよりも実質的に最高のRTP䟡栌を提䟛するためです。生きおいるカゞノは実際には完党に安党で安党であり、䞻に圌は単にオンラむンカゞノから離れた既存の機胜を匷化するだけかもしれたせん。 このタむプの預金なしのむンセンティブは、危険のない裁刀の申し出からの瞮図であり、代わりに最新の地元のカゞノの環境に぀いお話す方法です。リアルタむムの゚ヌゞェントゲヌム、Advancement Playingは、皲劻のバカラなどの想像力豊かなバカラの芋出しを提䟛し、゚ネルギッシュな珟実のカゞノ䜓隓に容易に利甚できるプレスを凊理できたす。 Alive Agent Baccaratのスリルを持っおいたす。そこでは、ビデオゲヌムが最高の意味でストリヌミングされ、本物のむンタラクティブなギャンブル゚ンタヌプラむズの雰囲気を持ちたす。デゞタルバカラではなく、このタむプの珟実のゲヌムはプロの人々を備えおおり、アクションをディスプレむに盎接䜿甚しお、耇雑なストリヌミング技術を備えおいたす。