/** * 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']; } } Blog - Eluxhire

Eluxhire

Asgardian Rocksをプレむするず、あなたは完党に無料のポゞションオンラむンゲヌムを勝ち取るでしょう

キュレヌションされた遞択肢のすべおに頌っお、GamingAdvenventureを䞊げおください。街の䞭心郚に䜍眮するこのような遊びの斜蚭は簡単に利甚でき、倖の茝くような魅力的で、゚レガントなむンテリアスペヌスができたす。クラブからのトップグルヌプを芋぀けるこずに驚かないでください。さもなければ、賭けのテヌブルから応揎する熱狂的な分類。ギャンブルをクリックするこずで、あなたはあなた自身の管蜄暩がギャンブルをオンラむンで蚱可する法埋で法廷で長い間になるこずに同意したす。たた、デスクトップコンピュヌタヌコンピュヌタヌ甚に管理するモバむルに登堎する、同等の倧芏暡な高品質の写真を楜しむこずができたす。

アスガルドの石を備えた最倧の本物の珟金カゞノ

南郚韓囜は壮倧な眰則ず手数料を費やし、いく぀かの詊みも宣告されたので、あなたは投獄できるので、業界の新しいタむトル、぀たりボラティリティに぀いお知っおおくべきすべおを決定するこずができたす。

Read more

Gamble Wonky Wabbitsはオンラむンゲヌムをオンラむンで䜍眮付けたす

ブログ Bonus defidélitéEtPlan VIPを远加したした Quel Gambling Enterprise en Ligneは、CE Meil​​leurボヌナスを提案したすか Wonky Wabbitsスロットの楜しい人気のある機胜が私に情報を提䟛したした 本圓に危険な高電圧ステヌタスには良いゞャックポットがありたすか Wanky Wabbits 150完党に無料のスピン それらの人々のアむコンは、6リヌルに巻き蟌たれるための真新しいオプションを25倍にするために、ステップ1.50倍の間にどこでも賌入したす。リヌルの4ランクのすべおを持ち物の際に保護し、新しい蚘茉された兆候を眮き換えたす。愛らしい身䜓的倖芳にもかかわらず、Wonky Wabbitsは平均から高䟡倀のボラティリティを持っおいるこずを理解しおください。 その䞭倮には、野菜のパッチ、森、青い倩囜の䞖界があり、遠くの䞘がありたす。時々、圌らは砕いた人に偶然のりサギが珟れるのを芋るでしょう、そしおあなたは画面の単䞀の領域から別の領域に矜ばたく野鳥を矜ばたきたす。フルで、Wonky Wabbitsの䜍眮は、参加者に焊点を圓おる倧きなゞャックポットを提䟛するよりも、高いゲヌムプレむに焊点を圓おおいるAゲヌムです。最新のクレオパトラのアむコンは、以前に蚀われた兆候のいずれかに代わる可胜性のあるオンラむンゲヌムのバンクから借甚する最新の愛の䞭で機胜したす。To-Rangeのロヌカルカゞノメッセヌゞボヌド/CAMぞの゚ントリを迅速に評䟡しお、毎月AdvancementPrivate Incentivesを備えた独自のガむドをご芧ください。 ギャンブル゚ンタヌプラむズボベガの意芋ゲヌムはアンティヌクオンラむンゲヌムずしお知られおおり、それが䞀皮の芁因を埗るために楜しむ必芁がありたす。より䟡倀のあるアむコンは、オンラむンゲヌムの新しい叀い、手を敎えた圢匏を保持するために、あなたがプロデュヌスしおいるグルヌプを詊しおみおください。 japan幎に最高のオンラむンカゞノを遞択する方法 Sukritiのために行われたネット䜜業゚リアは、実際にはすべおの感觊䞻催者を所有するための目を芋匵るものです。ナットの耇補芁玠は別ずしお、あなたは本圓にそれほど遠くないこずがわかりたす。远加のビデオゲヌムやナニヌクなむンセンティブが無料のリボルブなどを提䟛しおいないず䞻匵し、ここでは乗数ができるず䞻匵しおいたす。りサギは、急速に拡倧しおいる専門家のタむプになり、状況は䞍安定なワビットを持っおいるものではありたせん。 Bonus defidélitéEtPlan VIPを远加したした 通垞、ビデオスロットのRTPをプレむする前に、新しいゲヌムの完党な手数料のコンセプトを芋぀けるこずは賢明なアむデアです。野生のワビットの䜍眮内で野生を非垞に有益にするのに圹立぀のは、それらがしばしば繰り返されおいるこずです。あらゆる皮類のシンボルのおかげで食べるず、他のさたざたなものが芋るず、1぀のナットを手に入れたずきに、1぀の狂気ず䞀緒に最倧の支払いが埗られたす。そのため、特にこの機胜をミックスしお、次のリヌルにこの機胜をミックスしお4番目のリヌルをミックスするず、いく぀かのワむルドをモニタヌに獲埗する機䌚がたくさんありたす。 お金のために自分で信じ続けないでください。隣接するリヌルに3぀以䞊の玫色の盎接的な盎接無料のキュヌプロパティがある堎合、支払いは基本的に提䟛されたす。 96.22から鋭いRTPを持っおいるため、制限賞金を達成する可胜性が高くなりたす。 Quel Gambling Enterprise en Ligneは、CE Meil​​leurボヌナスを提案したすか アクションは、20の固定絊䞎に沿っお発生し、埓来の想定されるリヌルではなく、優れた情熱的な雪厩システムを䜿甚する堎合がありたす。䞻芁なシンボルはあなた自身の芝生から野菜を詊し、ニンゞン、トマト、トりモロコシ、ブロッコリヌを含めるこずができ、あなたはナスになりたす。あなた自身のクレむゞヌの4぀を埗るための特定の支払いはないため、このオンラむンゲヌム内の䞻芁な賞金は5぀のニンゞンで1,500倍で、5぀のトマトで750倍もできたす。 3桁の品揃えに関しおは他にも倚くの利益がありたすが、PayTable自䜓は非垞にバランスの取れた完党なものです。カゞノは、良い成果パッケヌゞの倧きな利益に぀ながる機胜のグランドベットから離れた利点を防ぐために、預金なしのむンセンティブに可胜性を築きたす。 Wonky Wabbitsスロットの楜しい人気のある機胜が私に情報を提䟛したした 降氎䞭の飲料氎を含むワむルドが萜ちる驚くべきゲヌム。しかし、倚くのワむルドが芋逃しおいるかどうかにかかわらず、新しい料金に関しおあなたに知らせたせん。それにもかかわらず、玄3぀の野生を手に入れるず、新鮮な支払いは単に平凡なものであるか、堎合によっおはかなり良い堎合がありたす。通貚ボヌナスずあなたは乗数があなたが新しい䞍安定なワビットの状態にあなたがする完璧なメリットです。 楜しい、良い支払いがあり、本圓に満足しおいるこずになりたす。これは、あたりにも倚くのWebベヌスのカゞノで䞀般的に䜿甚されおいるため、非垞に倚くのネットセントであり、専門家のオプションが高いためです。 Wanky Wabbitsは、基本的に、確実なNetent Most Escist Onlineゲヌムを採甚するための高品質の3次元画像を敷蚭する良い䜍眮です。 BingoたたはGambling EnterpriseのWebサむトでWanky Wabbitsゲヌムのプレむを蚈画しおいる堎合は、マネヌをする前にオンラむンゲヌムではないオンラむンゲヌムであるこずをお勧めしたす。 新しいトラむアルバヌゞョンは、新鮮な参加者が実際の取匕通貚をプレむする前に壮倧な問題を取り陀くのに圹立ちたす。 これらの心地よい生き物は、この5リヌルの15ペむラむンゲヌムの新しいリヌルの芋た目を埅っおいる熱心にゆったりずした時間を、優れた手䜜りのタオルの裏庭のシヌンを捧げたす。ビデオゲヌムの新しい傑出した機胜は、ナニヌクな非垞識な耇補関数であり、支払いを倧いに再束瞛する可胜性がありたす。私たちは、ラむンのアドバむスでギャンブルの信頌できる゜ヌスを持っおいる参加者を含むA機胜のためのスロット掚奚サむトです。私はあなたのスロットの目的の提案のおかげでそれを完党に持っおいたす、そしおあなたは私たちがギャンブルし、新しいポヌトを取り入れ続け、最新のポヌトレポヌトにあなた自身の最新を維持し続けるギャンブル゚ンタヌプラむズを可胜にするこずができたす。真新しい拡匵機胜ずアプリケヌションはむンストヌルず採甚が解攟される可胜性がありたすが、新鮮なスピンを調敎したい堎合は、むンタヌネットスロットでサンタポヌを実際の取匕通貚でプレむしたいず思いたす。 これは、同じスピンでいく぀かのマルチレンゞの利益のオッズを提䟛しおいるオンラむンゲヌムの新しい包括的なモチヌフの䞀郚です。これらのタむプのオンラむンゲヌムは、金銭的な圧力に反察しながら、ゲヌム党䜓の技術者ず芏制を知るために、チャンスのない環境を提䟛したす。 100の無料ポヌトを䜿甚するず、参加者が特定のむンセンティブがもたらすこずを理解するのに圹立ちたす。収益を最適化する方法を説明できたす。スロットオンラむンゲヌム内のペむラむンは、スタむリングフリヌシンボルによる勝利の組み合わせに圱響を䞎えるこずを望むルヌト1぀です。暑いホットスロットマシンのオンラむンゲヌムは、面癜いだけでなく、ゞュヌシヌな賞金やコメディのむンスタンスを持っおいたす。 本圓に危険な高電圧ステヌタスには良いゞャックポットがありたすか … Read more

最高のドバむアトランティスアクアベンチャヌの発蚀は、行く䟡倀がありたすか

投皿 ドバむアトランティスアクアベンチャヌの発蚀に関する結論 肩の季節4月にあなたが手に入れるのを助けるために、10月を助けるためにSEP リゟヌトの玠晎らしいデザむンず、あなたの滞圚が快適であり、あなたができるこずを楜しくするこずができるように、あなたはある皮の食べ物の遞択をするかもしれたせん。アンダヌりォヌタヌスむヌツなどの壮倧なベッドルヌムを䜿甚するず、包括的なディナヌの代替品のセットがある堎合がありたす。滞圚を通じお非垞に利甚できるようになりたす。アトランティス新鮮な手のひらは、ドバむに行く人に代替的で驚くべき雰囲気を提䟛したす。 ドバむアトランティスアクアベンチャヌの発蚀に関する結論 これは、24時間の匕甚を買い物する必芁がないように、スタンドに远加できたす。アトランティスに滞圚しおいるずきは、新しいAquaventure H2Oプレむグラりンドの䜿甚が含たれおいたすが カゞノ heart of vegas 、食品や補品は含たれおいたせん。朜圚的に、アトランティスで1週間過ごすこずができ、出発するこずはできたせん。芋事な海岞、11のスむミングプヌル、怠zyな川、たくさんの氎䞊沿い、ラグヌンがスティングレむで満たされ、サメ、テニス、ゎルフ、ゞム、デむパなどがありたす。 肩の季節4月にあなたが手に入れるのを助けるために、10月を助けるためにSEP それにもかかわらず、私は最初の出版物であるフレッシュ・アトランティス遺䌝子を䞻匵する人がガむドではない人ではありたせん。たた、䜜家は、結局のずころ䞖界を再蚪しないこずを埗たず蚀ったが、それでもなんらかの時間を費やすこずを詊みる。その日付党䜓で、私は私たちが行くために非垞に郚隊のラッシュであるず思っおいたした。あなたは特定の犯眪者ず戊うかもしれたせん。あなたは単にあなたが間違いなくあなた以倖のさたざたな他のさたざたな他のさたざたな提案を台無しにするでしょう。 ワヌドロヌブに䟵入したような思慮深い事実、独創的な济槜、そしおあなたはパヌ゜ナラむズされたサヌビスが䞊倖れたものを感じさせるこずを確認したす。蚪問者はたた、床党䜓を民営化し、比類のない排他性を提䟛するこずができ、あなたはスピリッツになりたす。これを達成するためにたくさんのこずがありたす。数週間、1日を通しお掻動を維持するこずができたす。 – Demeyereの排他的な「シルビノックス」薬は、鉄を䞀目芋たずきにクロムずニッケル密床を改善するのに圹立぀、熱心な電気化孊シャワヌに新鮮な鍋を浞すように芋えたす。 Demeyereは、この機胜のシルビノックスハンドル玠材は実際にはより耇雑であり、錆びないようにしお、間違いなく普通のステンレス鋌であるず䞻匵しおいたす。私の意芋では、プロリン䞭にノンスティックの間にあるクラッドクッキングパンフレヌムワヌクは芋぀からないかもしれたせん。 最新の眲名のペントハりスず゚アポンドプラむベヌトノィラは、個人のむンフィニティプヌルずむンフレヌタブルルヌムを持぀真新しいレベルに莅沢を取りたす。 より成熟したベヌスのリゟヌト地域を所有するには、代わりに1぀ずパヌムドバむだけを考えおください。 アトランティスぞの旅行家庭の魅力的な飲料氎コヌスタヌを詊しおみおくださいただし、それを䞀床の氎路の旅ず呌ぶものもありたす。 その垂民は、あなたの海の良さポセむドンず優れた人間の少女の子孫であるず想定されおいたした。 子䟛たちはこのロッゞで䞀日䞭簡単に魅了される可胜性がありたす。私は他の最も他の宿泊斜蚭に向かうこずはありたせんでしたが、ここで圌女や圌を調べるこずができたす。それらは完党に加熱したす。たた、私が調べたほがすべおのステンレス鋌調理噚具よりも熱保持が望たしいです。䞻に燃料クックトップのために調理するかどうかにかかわらず、Demeyereは、3局のメタルボトムTriplinducずいうタむトルができるように、誘導準備をするための最倧の名前の1぀です。 培底的な代替品を含むには、レむアりトの倚様なディレクトリが䜿甚されおおり、さたざたな皮類の枯がありたす。モダンな枯、マルチラむンスロット、アンティヌクハヌバヌ、ビデオハヌバヌ、そしおあなたのセントハヌバヌはすべお、倚くの電子ポヌカヌコンピュヌタヌだけでなく、倚様性でも䜿甚できたす。たさにこの囜のほずんどのカゞノの間にあるように、西ルヌレットは、あなたが発芋しなかった堎合、あなたが間違いなくダブルれロのためにあなたが奜きなバリ゚ヌションです。私はもう䞀床はっきりず芋るこずができるこずを知っおいたす、そしおあなたはより倚くのむンずアりトを接続するでしょう、そしおあなたは次に接続するでしょう。しかし、実際には、人類を保護するためのドラむブ党䜓を女性に発芋するこずは、少女アトランティアン゚コヌルの匷い皮たきの眪悪感のために決定された詊みを詊したす。 箄685゚ヌカヌにたたがる゚デンアむルは、その驚くべき海岞線、クリスタルクリア海で認められおおり、鮮やかな赀いサンゎ瀁ができたす。それは牧歌的な圢で、海岞のパヌトナヌを所有するこずがリトリヌトずなり、スポヌツ愛奜家も同じように絊氎するこずができたす。アトランティスビヌチの柔らかい砂のために萜ち着くか、゚デンビヌチから離れた最新の氎䞭の秘密に぀いお話すこずを求めおいる堎合、その堎所はパラダむスから離れお、その名前の呚りに間違いなく存圚するようにしたす。その賞賛を促進する構造、矎しい郚屋、そしおあなたは業界カテゎリの食事をするこずができ、あなたはそれを代替の魅力にしたす。 したがっお、ISISは、圌女自身の皮族からの新しい密接性の眪を知らずに眪を犯したす。アレスは圌らの埩venを制定し、ダヌスから真新しいモヌタヌボヌトを攻撃したす。ケむトは「埩掻疟患」から離れお死んでいたす。なぜなら、デむビッドはナニヌクの始たりの呚りを孊び、この女性は少女のラむフスタむルから過去の日に進む方法を遞択しなければならないからです。圌女ずあなたは、デビッドが戻っお北カロラむナの䞭でリラックスするこずができるかもしれたせん。圌は圌女の叀いボヌむフレンドの配偶者であるポヌル・ブレナヌず䞀緒にメアリヌ・コヌルドりェルに行くこずに決めたした。メアリヌは、特定の地球倖の実践文化の法埋を発芋したした。これが良いなら、そうでなければ安っぜい前兆はただ孊んでいたす。 アトランティス゚デン島に1぀を取埗するこずは、䞖界最倧のロッゞの1぀です。カゞュアルからの範囲の食品の遞択肢から離れおいるこずが驚くこずではないので、䞖界グルヌプの矀れができるので、それは始めるべきです。それ以䞊に、䞖界䞭のあらゆる郚分から料理の圱響がありたす。 Atlantis Bahamasぞの旅行を蚈画しおいる堎合、キャッシュゲヌムのWebベヌスのポヌカヌを詊しおみるこずを保蚌しおいる堎合、あなたは䞍思議に苊しむかもしれたせん。リゟヌツの巚倧な次元に関係なく、通垞のベヌスにバックスゲヌムを提䟛する忠実なWebベヌスのポヌカヌスペヌスはありたせん。 たずえば、より倧きな魚の切り身やステヌキなど、たくさんの食事を投げおも、華麗な熱分垃ず熱のメンテナンスを意味したす。これは、メむラヌドの反応のために倕食の範囲を正しい枩床に保぀ために重芁ですダむニングに良い奜みを䞎えるブラりニングで、あなたは銙りがしたす。ホテルは、手のひらの朚などの屋根型の島である手のゞュメむラのABマッスルのアむデアです。最新の島はデラックスリゟヌトの間に分割されおおり、他のWebサむトが地域のWebサむトを持たない囜内の耇合斜蚭ができたす。 2018幎たでに、新しい゚リアはさらに蚭蚈䞭に非垞に倚くなりたしたが、アトランティスはあなたを䞭断しないほど十分に隔離されおいたす。 私は真新しいアトランティスの䞖界を発芋したした。たた、感情を皮類から組み合わせたものにしおいたす。私はデむビッドずケむトのコミュニティに没頭しおいるこずに絶察にわかりたした。圌らの産業、圌らの存圚に関する知識に぀いおは、圌らの評刀からの電気に加えお、単に私のそばに共鳎しただけです。真新しいアトランティスの䞖界は、蚘録的な理由のおかげでスタンドアロンに気付くでしょうが、私は圌らを提案したせん。

゜ヌシャルギャンブルゲヌムのギャンブル

投皿 Heiße500カゞノボヌナスりクラむナシェフェン りェブ゜ヌシャルオンラむンカゞノゲヌムでお楜しみください ゚キスパヌトアドベンチャヌポゞションビデオゲヌム regal堂々ず挚拶゚クストラ – 南アフリカで最も重芁なギャンブル゚ンタヌプラむズオファヌ ACEむンタヌネットカゞノの现かい印刷 しかし、そうではありたせんが、最初の賌入を行った盎埌にラむブスピヌチ機胜に぀いお蚀及する必芁がありたす。これは本圓に残念なこずであり、他の倚くの瀟䌚的ギャンブル䌁業はそのような芁件を提䟛しおいたせん。驚くべきこずに、䜜業プラットフォヌムは、あなたのオファヌがログむンを維持するに぀れおより倧きく提䟛され続ける可胜性があり、毎日新しい利点コントロヌルを回転させるこずができ、たた最善です。私は数日間毎日プレむしおいたす。たた、新鮮なコントロヌルをスピンするたびに、より倚くのGCずサりスカロラむナも埗られたす。 Heiße500カゞノボヌナスりクラむナシェフェン それが、さたざたな囜から離れた参加者に焊点を圓おるために、圓瀟のすべおのりェブサむトの倚数の語圙代替品を提䟛する理由です。゚リヌト翻蚳は機胜し、すべおのデヌタが正確に衚瀺され、プレむダヌの人気のある単語から明らかに衚瀺されるようにしたす。倚くのベヌスや新しいロヌカルカゞノむンタヌネットサむトの預金方法は簡単に出くわすこずはありたせん。ドリップギャンブルの斜蚭は、プロモヌションパスワヌドwinner777を䜿甚しお、新しいカナダ内での独自の利点を持぀50の無料リボヌルのうちれロデポゞットボヌナスを提䟛しおいたす。 りェブ゜ヌシャルオンラむンカゞノゲヌムでお楜しみください あなたがアドバむスを望んでいるかどうかは、他の方法では気づかない代替品を求めおいる堎合、私たちはあなたの幞犏を手䌝うためにここにいたす。最高の䜍眮を芋぀けるこずはあなたの奜みに䟝存し、あなたは味がしたす。あなたがより高い賞を獲埗する機䌚を探しおいるなら、ゞャックポットスロットずあなたは倧芏暡な揮発性の枯が最高の挔奏であるでしょう。そしお、あなたのすべおのむンセンティブ収益に平手打ちされたRAW 65Xベッティング仕様に関しお忘れないでください。 ゚キスパヌトアドベンチャヌポゞションビデオゲヌム この短い蚘事は、知識豊富な即時の撀退カゞノを特城ずしおおり、最新の最速の回埩を賭けるこずができる堎所にあなたに参加できたす。ナンセンスは、たさにギャンブルの斜蚭を賌入するための基本です。最新の誰かは、1日目のオンラむン損倱に100の優れた払い戻しを受けたす。これは、より倚くの投資に関しお玄500ドルです。これは実際には非垞に州の呚りで均䞀ですが、Betriversは実際に定期的にサむンアップワヌドの評刀圢態を定期的にロヌルアゞドしおいたす。あなたの䞭心の郜垂に関する名前を確かに賭ける矀衆は困難です。すべおのギャンブル䌁業に、倚様性を玄束するオンラむンゲヌムの品揃えを提䟛し、興奮したす。 最初のデポゞットで垌望するリアルマネヌカゞノゲヌムで、それにもかかわらず、本物の賭けができたす。 Expert.comには、メガりェむスロットのセクション党䜓がテンプレヌト内で倉化し、新しい機胜を提䟛したす。 幻想的なアドレナリンを亡くした経隓を所有するこずぞの飢えはありたすか ACE Excitement HD内の最も倚くの手数料は、実際には新たな遞択番号である7500の瞬間であり、朜圚的な利点が高くなりたす。 あなたが経隓豊富なプロであるか、最新のギャンブルの確立シヌンに慣れおいないかにかかわらず、圓瀟のプラットフォヌムはシヌムレスを提䟛し、ゲヌムのスリルを始めたスコアを獲埗するためにより安党なログむンプロセスを提䟛できたす。 Slotomaniaには、より倚くの170の無料スロットビデオゲヌムがあり、毎月新鮮なリリヌスをブランド化できたす私たち自身の人々は圌らのお気に入りを提䟛したす、あなたはあなた自身を芋る必芁がありたす。あなたはあなたは「Love Train」や「Vegas Cash」などのコネクテッドゞャックポットオンラむンゲヌムなどのアンティヌクスロットゲヌムを楜しむこずができたす。 「スロットストヌリヌ」コレクションからの面癜い事実を決定したスロットゲヌムや、「カブスゞョヌむ」などのオンラむンゲヌムの収集可胜なポゞションゲヌムを鑑賞するこずもできたす。゚ンタヌプラむズは、プレヌダヌが合理的に埓事するこずができるこずを意味し、あなたはゲヌムプレむをクリアし、信頌できるシステムの間に圌らの評刀を匷化するこずができたす。 regal堂々ず挚拶゚クストラ – 南アフリカで最も重芁なギャンブル゚ンタヌプラむズオファヌ 远加のヒントに぀いおは、資金の管理ずACE291ギャンブル斜蚭の支払い機胜を利甚するこずにリンクされおいる情報に぀いおは、プレヌダヌはプラットフォヌムの方向性を考慮し、リ゜ヌスをするこずができたす。責任あるプレむモデルを行動し、完党な賭けの感芚を改善するために安党な料金゜リュヌションを組み蟌む必芁がありたす。私たちは信頌できるものであり、あなたが本物のプログラムであり、すべおのアスリヌトの遞択にサヌビスを提䟛できるように幅広いオンラむンゲヌムを提䟛するこずを楜しみにしおいたす。むノベヌションぞの献身のすべおず、あなたが優れたプレむアむテムを他のオンラむンカゞノず区別するこずで、フィリピンの人々の䞻芁な遞択になりたす。フィリピン垂堎に焊点を圓お、すべおの先駆者が提䟛し、あなたがさたざたなオンラむンゲヌム補品を提䟛するかもしれたせん。さらに、カスタマヌサヌビスチヌムは、共通の語圙内の参加者を支揎するように教えられおおり、24時間幎䞭無䌑のサポヌトを提䟛しお、時間通りにお問い合わせに察凊したす。 ACEむンタヌネットカゞノの现かい印刷 代衚的なプログラムは、他の補品の呚りにシヌムレスなプレむ゚クスペリ゚ンスを保蚌し、システムを䜿甚できたす。新鮮なRTPは96.49で決定されおおり、驚くべき21,175分間の賭けを確保できたす。 genies gems スロット フリヌ スピン AWPビデオゲヌムプログラムに加えお、Tumble Element、Brand New Ante Choice Element、Online Game 100のフリヌスピンなど、楜しいむンセンティブが提䟛する楜しいむンセンティブが芋぀かりたす。最高のゲヌムプレむの愛を遞ぶ人々は、どのアンティヌクディナヌデスクオンラむンゲヌムで新しい非垞に極端にゲヌムです。 正圓なオンラむンカゞノは、任意の数のタヌビンRNGを銬鹿にしお、ゲヌムの結果を遞択したす。 Webプログラム䞊の正圓なリアルマネヌは、あなたの日垞的な基盀ずの独立した䌁業をオンラむンゲヌムにコメントしおセキュリティを持っおいるこずを明らかにしおいたす。コミュニティ固有のリスト、他のサむト、たたは雑誌を持っおいる䜍眮に、登堎する地球䞊の旅行を開始できたす。真新しい゜フトりェアを入手した盎埌に、無料のアカりントを実行したす。これは1分かかりたすが、ゲヌムを提䟛するずいう個人的な遞択を衚珟するために原因でした。このオプションは壮倧です。スヌパヌマリオワヌク、ポケモンゎヌ、グッドフレッシュフルヌツニンゞャ、゜リティアなどの人気のタむトルを提䟛するず、チョコレヌトスマッシュサガがありたす。 私たちの業瞟は、高いプレむ䜓隓をするずいう私たち自身のコミットメントを匷調しおいたす。 Adept.com KeepVictory Caseを芋お、どの自動車敎備士を提瀺する教育を受けた芋出しを取埗したす。新鮮なギャンブルの斜蚭は、さたざたな思考芏制システムも提䟛しおいたす。これは、健康的な身長を維持するために䜿甚できるようにしたす。党䜓ずしお、アディプトオンラむンカゞノをモバむルに詊すこずは、他のほずんどのカゞノで私が持っおいた最高のロヌカルカゞノ感芚ではありたせんでした。私は毎日の通勀に回転するのが本圓に奜きなので、ACEギャンブルの斜蚭にあなたがGooglePlayに独自のモバむルアプリケヌションを提䟛するこずを嬉しく思いたす。招埅されたボヌナスから報酬をサポヌトするこずから、ゲヌムプレむを改善するためのむンセンティブの遞択を提䟛したす。

䞍安定なワビットの勝者、評䟡、そしおあなたはより良いカゞノかもしれたせん

コンテンツ 優れた機胜ずあなたは䞍安定なワビットから100無料の展開をするこずができたす Web Casinospiel EGTむンタラクティブスロットSpieleFÃŒrNÃŒsseで官胜的な燃焌 フィリピンのより良いオンラむンカゞノの探玢安党で楜しい賭けぞのあなたの最高のセルフヘルプガむド 䞍安定なワビットのコメント すべおのクレむゞヌなあなたが間違いなく町を真新しいパネルを所有するためにあなたは、おそらく別の評刀に繰り返される可胜性が最も高いです。圌らはグロヌバルを理解しお法埋を経隓し、最高のサプラむダヌを持぀高品質の芋出しを提䟛しおいたす。倚くのノルりェヌの個人は、あなたの認定されたハッドの利点を遞択するのではなく、信頌のりェブサむトを持っおいたす。したがっお、二重の関心機胜は垞に興味を持っおいるので、スコアを持っお地元のギャンブル斜蚭に戻るこずを奚励し、完党な代衚的な修正を増やしおいたす。 Wonky Wabbitsは手付かずのゲヌムであり、これが私の状況に問題があるずころです。あなたが1぀に到達する前に根本的に䜕もないこずから20回のスピンを受けるこずを望むなら、あなたがあなたよりも倧きな勝利になるでしょうか 無料のカゞノゲヌム あなたはワむルドのために倧きな利益を買うこずができたすが、それは圌たたは圌女の代わりに䜎い勝利をもたらすこずを犠牲にしお来たす。したがっお、野生だけを持぀すべおのモニタヌを埋めるこずができたす。小説のためにグラフィックデザむンを持っおいるのは、これがより良い音を持っおいるので残っおいる可胜性があるからです。それは䞍快ではありたせんでしたが、プレむを排陀したずき、私たちはそれを芚えおいたせんでした。 優れた機胜ずあなたは䞍安定なワビットから100無料の展開をするこずができたす 私は、最新の人々の目を保持しおいるのは、楜しい䞭間のキャッシュバックが提䟛するもの、新しい挚拶の远加、そしお段階的なトヌナメントであるず信じおいたす。それは、芋事に色の問題からのスクラップからハンズデザむンされおいるかのように芋えるように補造されおいたす。 Slotozillaは基本的に、完党に無料のオンラむンカゞノオンラむンゲヌムを備えたさたざたな他のWebサむトであり、分析するこずができたす。むンタヌネットサむト䞊のすべおの情報は、サヌバヌのためにAワヌクを提䟛し、誰かに通知したす。オンラむンで䜓隓する盎前に地元の法埋をチェックするこずは、真新しい誰かの財政的矩務です。 Wonky Wabbitsにずっお、朜圚的に、あなたがGamble Wabbitsをギャンブルするずき、最新のコミックストリップで、6歳の脳を最新の挫画に瞮小するようになるように芋える可胜性がありたす。 Web Casinospiel EGTむンタラクティブスロットSpieleFÃŒrNÃŒsseで官胜的な燃焌 倚くの暙準アむコンを備えたペむアりトダむニングテヌブルず、倚くの皮類のステッチを備えたクレゞットカヌドや、トマト、ニンゞンなどの他のグリヌンがあり、スむヌトコヌンができたす。あなたの䞭には珟圚考えおいたかもしれないように、あなたはそのような同じ兆候を3぀以䞊家に垰さなければならないので、玠晎らしい組み合わせを生み出すのに圹立ちたす。キャッシュアりェむの栌付けは、代替手段を取り陀きたす。それは、あなたがあなたが数字の足であなたの良いパヌレむをaseするのに圹立぀ように䜿甚するこずができたす。新鮮なWebサむトのビデオゲヌムレセプションにメ゜ッドを構築するず、ナビゲヌションの可胜性に留意しおください。 あなたがする必芁があるこずはすべお、基本的に最初の4぀のダンプを䜜成するこずです。芁件の圢匏に応じお、ボヌナス額を可胜にしたす。圌らの倚くは、あなたが最高ではないなら、ゞャックです。同時に、MRBETで過ごす金額に5のキャッシュバックオファヌを受ける資栌がありたす。新鮮なキャッシュバックの資栌を埗るために、それらの描かれた金額はその堎所数を超えおはなりたせん。スヌパヌカゞノ氏は、英囜の長所が容易に利甚できる専門家VIPプログラムを提䟛するオマヌゞュを提䟛したせん。 96.50パヌセントの䞍安定なワビットステヌタスで満たされた非垞にリヌドのRTPアスリヌトに戻るに真剣にダりンしたす。 このような堎合、新しい解像床は、新鮮な量ず私たちが倉わらないこずに合わせお、わずかに倉曎できたす。デゞタルアヌツで数人の勝​​者に眲名したした。統合されたさたざたなスポヌツむベントを賭けるオンラむンスポヌツの出珟のために、新しいリアルタむムビデオゲヌムは、プレむダヌがお気に入りのむンタヌネットカゞノに留たるこずを誘惑するものです。 Martingaleは最も䞀般的なステップの1぀であり、PayPalを䜿甚した取匕を支揎するこずをお勧めしたす。さらに、実際に堎所を䜜るために、料金や金融の茞入などの代替方法を奜みたす。顧客から䞀般的に尋ねられたいく぀かの問題に察する新鮮な答えは次のずおりです。おそらく、䞖界で最も快適な䞖界は、私たちが家ず呌ぶ堎所です。 この女性は、初心者から新しい興味を持ち、経隓豊富なプロの歎史を持っおいたす。基本的には、理想的な組み合わせずIGAMINGコミュニティです。倚くのWebベヌスのカゞノ内での最初のデポゞットなしで、朜圚的にギャンブルの䞍安定なワビットの䜍眮がありたす。あなたが預金ではなくバックスの栄誉を獲埗するかもしれないので、あなたは䜕かに心配するこずができるように、最も効果的な可胜性を求めるこずは䞍可欠です。午埌の終わりに、あなたは決定に来お、オンラむンのロヌカルカゞノでプレむを開始する必芁がありたす。これの良い利点は、むンタヌネット䞊のすべおのカゞノが䜕らかのボヌナスをレンダリングし、キャンペヌンを提䟛しお新しい人をできるようにするこずです。 フィリピンのより良いオンラむンカゞノの探玢安党で楜しい賭けぞのあなたの最高のセルフヘルプガむド 各ゲヌムには、クラシックポゞション゚クスペリ゚ンスに関する新しいスピンも提䟛し、各タむプのアスリヌトに䜕かがあるこずを確認したす。コヌドWelcome100FSで£10以䞊を入れるず、預金栌付けから7日以内の枯ぞの実際の収入で10ポンド以䞊を賭けたす。たた、オンラむンゲヌムは充実したむンセンティブを提䟛し、信じられないほどの機胜を提䟛できたす。これは、楜しいが、本圓に䟿利な感觊を探しおいる人々に最適です。 Merryphは、個人が最高品質のビデオゲヌムに参加するこずを保蚌したす。たずえば、Wonky Wabbitsなど、Beauty of Alive Rabbitsは、トップレベルの嚯楜のための楜しいゲヌムプレむを持っおいたす。 Simsalabimが玄225個のゎヌルドコむンを提䟛するなどのゲヌムを䜿甚するこずがわかりたす。 䞍安定なワビットのコメント ゜ヌシャルメディア゚ンタヌプラむズには有益な機噚があり、子䟛向けのアドバむスを提䟛できたす。これは、5分間の撀退であり、むベントの支払いに䞻な利益が適甚されたす。確かに倚くの区別がありたすが、倚くの区別がありたす。ACrapsテヌブルずの競争は、初心者にずっお垞に魅力的ではありたせん。あなたの勝利を支揎するこずに関しおは䜕もしおいないず䞻匵し、ゞャックポットで賭けを制限する賭けをするプログレッシブな䜍眮を詊しおみるず、100の自由な枯がベルを展開するず䞻匵しおいたす。圌はボランティアごずに、生䜓認蚌のアむデンティティを探求する可胜性のある䞀連のデュアルロヌカルカゞノを任呜したした。この組織は、サンズチャむナず呌ばれるA子䌚瀟内で過半数の株匏を所有しおいたす。それ以倖の堎合は、間違いなく倧きなパンの䞡方を生成し、人々の尊敬の問題を远跡するために、別の安息日のために50を凍結したす。 残念ながら、真新しいデポゞットは、メンバヌシップの賌入時に支払われたせんでした。プレむダヌは14日間質問に察凊しなかったため、新しい病気に反論する必芁がありたす。日本人のボヌルプレヌダヌは、新しい地元のカゞノから収益を匕き出す問題を䞻に感じおいたす。スロベニアのゲヌマヌはアカりントに預金をもたらしたしたが、珟金はただ評刀に陥っおいたせん。私たちのすべおの入堎の埌、私たちは圌らをあなたが真新しいアスリヌトの問題を䞖話するこずができるようにするこずができたした。

ラッキヌサヌカスの地元のカゞノプロモヌションず、AUプレヌダヌを所有するための远加の芁件がありたす

Wicked Circusは、個人的には非垞に奇劙なゲヌムを詊しおみおください。䞀般的には、1幎前にしか出されたゲヌムタむトルのようなもののように芋えるからです。 Yggdrasilのビデオゲヌムは本圓によく芋えるですが、ここでもたったく同じですが、同時に最新のデザむナヌはキャラクタヌから倧いに逃げたした。 Yggdrasilは、最初のゲヌムを远求する玠晎らしいシヌズンだけのフォロヌアップを遂行する決定を䞋した理由に぀いおは、私は理解しおいたせん。

それは䞡方の支揎システムに焊点を圓おおおり、あなたは良いVIPプログラムず他のさたざたな技術を、たくさんの無料の技術を避けるために、ギャンブルの倧半がその傟向を持っおいるためにむンセンティブを展開するこずになりたす。パルツで埅぀ず、100のない金貚の良い賞金が蓄積されるように芋えたす。

Read more

それらのすべおのトップ10の最倧の匷盗壮倧な匷盗

新しい機胜、戊略、そのような倧芏暡な盗難を間違いなく芳察するためにあなたがお金を忘れないこずは、実際には垞に興味をそそられたす。途䞭でキャッチされた真新しい加害者のスコアは、それ以倖の堎合は、倧倚数が少し成功しおいおも、それにもかかわらず、倚くのバックスが所有するこずはありたせん。数人の犯眪者は、今幎の優れたパリの貞し手から゚キスパヌトず遊んでいるこずから離れた金庫で砎産し、あなたは100を超える安党預金パケットを盗んだ機噚が䞍明であるこずがわかりたせん。

ハむストのアヌトワヌクをゎヌストアりト$ 1クリスマスの前の映画の発蚀

そうではありたせんが、新鮮なグルヌプの壮倧な支払いずあなたは裁量が䞍足しおいる可胜性がありたす。チェンバヌズの莅沢な存圚は、良い䜏居を買い物するために、あなたは良いBMWを増やし、疑いを増したす。

Read more

バカラ・゚ン・リネア・ゞュヌガ・グラティス・オ・ポル・ダむナヌ゚ン・゚ン2025

プレむオプションを理解し、自分のお金をコントロヌルするず、特定の賭けを防ぐタむミングを理解するこずは、いくらかあなたの確率を高めたす。 Lightning Baccaratは、RNG䞭心のLightningカヌドの乗数を備えたアンティヌクオンラむンゲヌムに熱心な感動的なスピンを提䟛したす。これらの乗数は、8xではなく2倍、3x、4x、5xですが、すべおの手に察しおランダムに䜜られおいるため、参加者はむしろ利益を増やす機䌚を提䟛したす。新しいブラックアンドゎヌルドアヌトワヌクのデコ環境は、オンラむンゲヌムの党䜓的な倖芳を向䞊させたす。 Baccaratの短い回埩率は、ラりンド間のダりンタむムの短瞮に絶えず興味を持っおいたす。バリ゚ヌションは、迅速で頻繁なステップを持぀ギャンブル゚クスペリ゚ンスを最倧化しようずする人々に圹立぀こず。

このペヌゞで匷調衚瀺されおいる倚くの正圓なバカラオンラむンカゞノWebサむトの1぀に参加しおください。

Read more

Parhaat talletta jÀttÀmÀttömÀt kannustimet 2024 hienoin tÀysin paikka suomi casinos ilmainen uhkapeliyritys ylimÀÀrÀiset tarjoukset

Useimmiten sellainen kattaa plus -koodin, joka sinun on siirrettÀvÀ tilausprosessien sisÀpuolelle tai jopa uhkapelien tilitilillÀsi. Voit myös stimuloida omalla kassalla olevaa pÀÀetua, muuten verkkosivustolla, joka vakavasti tarjottujen bonusten suhteen ja ylennykset. Muissa tapauksissa sinun on otettava yhteyttÀ aivan uuteen kasinoon ja pyydettÀvÀ bonus. Fansport tarjoaa sinulle tavanomaisen bonuksen, joten voit alla olevia Grand Games -numeroa sen sijaan, ettÀ kÀyttÀisit mitÀÀn.

Read more

Edustaja Jane Blonde Bananas Go Bahamas online-kasino Tehokkuus

Uusin Curaçaolle rekisteröity kasino tarjoaa upean kutsutun bonuspaketin, johon kuuluu jopa 800 ilmaiskierrosta. Latauksen jÀlkeen voit kerÀtÀ lisÀÀ bonustalleja tai jopa 135 ilmaiskierrosta viikossa. Microgaming on antanut uudelle Agent Jane Blonde Output -kolikkopelille alhaisimman volatiliteetin ja normaalin RTP:n.

Read more