/** * REST API: WP_REST_Attachments_Controller class * * @package WordPress * @subpackage REST_API * @since 4.7.0 */ /** * Core controller used to access attachments via the REST API. * * @since 4.7.0 * * @see WP_REST_Posts_Controller */ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { /** * Whether the controller supports batching. * * @since 5.9.0 * @var false */ protected $allow_batch = false; /** * Registers the routes for attachments. * * @since 5.3.0 * * @see register_rest_route() */ public function register_routes() { parent::register_routes(); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)/post-process', array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'post_process_item' ), 'permission_callback' => array( $this, 'post_process_item_permissions_check' ), 'args' => array( 'id' => array( 'description' => __( 'Unique identifier for the attachment.' ), 'type' => 'integer', ), 'action' => array( 'type' => 'string', 'enum' => array( 'create-image-subsizes' ), 'required' => true, ), ), ) ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)/edit', array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'edit_media_item' ), 'permission_callback' => array( $this, 'edit_media_item_permissions_check' ), 'args' => $this->get_edit_media_item_args(), ) ); } /** * Determines the allowed query_vars for a get_items() response and * prepares for WP_Query. * * @since 4.7.0 * * @param array $prepared_args Optional. Array of prepared arguments. Default empty array. * @param WP_REST_Request $request Optional. Request to prepare items for. * @return array Array of query arguments. */ protected function prepare_items_query( $prepared_args = array(), $request = null ) { $query_args = parent::prepare_items_query( $prepared_args, $request ); if ( empty( $query_args['post_status'] ) ) { $query_args['post_status'] = 'inherit'; } $media_types = $this->get_media_types(); if ( ! empty( $request['media_type'] ) && isset( $media_types[ $request['media_type'] ] ) ) { $query_args['post_mime_type'] = $media_types[ $request['media_type'] ]; } if ( ! empty( $request['mime_type'] ) ) { $parts = explode( '/', $request['mime_type'] ); if ( isset( $media_types[ $parts[0] ] ) && in_array( $request['mime_type'], $media_types[ $parts[0] ], true ) ) { $query_args['post_mime_type'] = $request['mime_type']; } } // Filter query clauses to include filenames. if ( isset( $query_args['s'] ) ) { add_filter( 'wp_allow_query_attachment_by_filename', '__return_true' ); } return $query_args; } /** * Checks if a given request has access to create an attachment. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return true|WP_Error Boolean true if the attachment may be created, or a WP_Error if not. */ public function create_item_permissions_check( $request ) { $ret = parent::create_item_permissions_check( $request ); if ( ! $ret || is_wp_error( $ret ) ) { return $ret; } if ( ! current_user_can( 'upload_files' ) ) { return new WP_Error( 'rest_cannot_create', __( 'Sorry, you are not allowed to upload media on this site.' ), array( 'status' => 400 ) ); } // Attaching media to a post requires ability to edit said post. if ( ! empty( $request['post'] ) && ! current_user_can( 'edit_post', (int) $request['post'] ) ) { return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to upload media to this post.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Creates a single attachment. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, WP_Error object on failure. */ public function create_item( $request ) { if ( ! empty( $request['post'] ) && in_array( get_post_type( $request['post'] ), array( 'revision', 'attachment' ), true ) ) { return new WP_Error( 'rest_invalid_param', __( 'Invalid parent type.' ), array( 'status' => 400 ) ); } $insert = $this->insert_attachment( $request ); if ( is_wp_error( $insert ) ) { return $insert; } $schema = $this->get_item_schema(); // Extract by name. $attachment_id = $insert['attachment_id']; $file = $insert['file']; if ( isset( $request['alt_text'] ) ) { update_post_meta( $attachment_id, '_wp_attachment_image_alt', sanitize_text_field( $request['alt_text'] ) ); } if ( ! empty( $schema['properties']['featured_media'] ) && isset( $request['featured_media'] ) ) { $thumbnail_update = $this->handle_featured_media( $request['featured_media'], $attachment_id ); if ( is_wp_error( $thumbnail_update ) ) { return $thumbnail_update; } } if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { $meta_update = $this->meta->update_value( $request['meta'], $attachment_id ); if ( is_wp_error( $meta_update ) ) { return $meta_update; } } $attachment = get_post( $attachment_id ); $fields_update = $this->update_additional_fields_for_object( $attachment, $request ); if ( is_wp_error( $fields_update ) ) { return $fields_update; } $terms_update = $this->handle_terms( $attachment_id, $request ); if ( is_wp_error( $terms_update ) ) { return $terms_update; } $request->set_param( 'context', 'edit' ); /** * Fires after a single attachment is completely created or updated via the REST API. * * @since 5.0.0 * * @param WP_Post $attachment Inserted or updated attachment object. * @param WP_REST_Request $request Request object. * @param bool $creating True when creating an attachment, false when updating. */ do_action( 'rest_after_insert_attachment', $attachment, $request, true ); wp_after_insert_post( $attachment, false, null ); if ( wp_is_serving_rest_request() ) { /* * Set a custom header with the attachment_id. * Used by the browser/client to resume creating image sub-sizes after a PHP fatal error. */ header( 'X-WP-Upload-Attachment-ID: ' . $attachment_id ); } // Include media and image functions to get access to wp_generate_attachment_metadata(). require_once ABSPATH . 'wp-admin/includes/media.php'; require_once ABSPATH . 'wp-admin/includes/image.php'; /* * Post-process the upload (create image sub-sizes, make PDF thumbnails, etc.) and insert attachment meta. * At this point the server may run out of resources and post-processing of uploaded images may fail. */ wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $file ) ); $response = $this->prepare_item_for_response( $attachment, $request ); $response = rest_ensure_response( $response ); $response->set_status( 201 ); $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $attachment_id ) ) ); return $response; } /** * Inserts the attachment post in the database. Does not update the attachment meta. * * @since 5.3.0 * * @param WP_REST_Request $request * @return array|WP_Error */ protected function insert_attachment( $request ) { // Get the file via $_FILES or raw data. $files = $request->get_file_params(); $headers = $request->get_headers(); $time = null; // Matches logic in media_handle_upload(). if ( ! empty( $request['post'] ) ) { $post = get_post( $request['post'] ); // The post date doesn't usually matter for pages, so don't backdate this upload. if ( $post && 'page' !== $post->post_type && substr( $post->post_date, 0, 4 ) > 0 ) { $time = $post->post_date; } } if ( ! empty( $files ) ) { $file = $this->upload_from_file( $files, $headers, $time ); } else { $file = $this->upload_from_data( $request->get_body(), $headers, $time ); } if ( is_wp_error( $file ) ) { return $file; } $name = wp_basename( $file['file'] ); $name_parts = pathinfo( $name ); $name = trim( substr( $name, 0, -( 1 + strlen( $name_parts['extension'] ) ) ) ); $url = $file['url']; $type = $file['type']; $file = $file['file']; // Include image functions to get access to wp_read_image_metadata(). require_once ABSPATH . 'wp-admin/includes/image.php'; // Use image exif/iptc data for title and caption defaults if possible. $image_meta = wp_read_image_metadata( $file ); if ( ! empty( $image_meta ) ) { if ( empty( $request['title'] ) && trim( $image_meta['title'] ) && ! is_numeric( sanitize_title( $image_meta['title'] ) ) ) { $request['title'] = $image_meta['title']; } if ( empty( $request['caption'] ) && trim( $image_meta['caption'] ) ) { $request['caption'] = $image_meta['caption']; } } $attachment = $this->prepare_item_for_database( $request ); $attachment->post_mime_type = $type; $attachment->guid = $url; // If the title was not set, use the original filename. if ( empty( $attachment->post_title ) && ! empty( $files['file']['name'] ) ) { // Remove the file extension (after the last `.`) $tmp_title = substr( $files['file']['name'], 0, strrpos( $files['file']['name'], '.' ) ); if ( ! empty( $tmp_title ) ) { $attachment->post_title = $tmp_title; } } // Fall back to the original approach. if ( empty( $attachment->post_title ) ) { $attachment->post_title = preg_replace( '/\.[^.]+$/', '', wp_basename( $file ) ); } // $post_parent is inherited from $attachment['post_parent']. $id = wp_insert_attachment( wp_slash( (array) $attachment ), $file, 0, true, false ); if ( is_wp_error( $id ) ) { if ( 'db_update_error' === $id->get_error_code() ) { $id->add_data( array( 'status' => 500 ) ); } else { $id->add_data( array( 'status' => 400 ) ); } return $id; } $attachment = get_post( $id ); /** * Fires after a single attachment is created or updated via the REST API. * * @since 4.7.0 * * @param WP_Post $attachment Inserted or updated attachment * object. * @param WP_REST_Request $request The request sent to the API. * @param bool $creating True when creating an attachment, false when updating. */ do_action( 'rest_insert_attachment', $attachment, $request, true ); return array( 'attachment_id' => $id, 'file' => $file, ); } /** * Determines the featured media based on a request param. * * @since 6.5.0 * * @param int $featured_media Featured Media ID. * @param int $post_id Post ID. * @return bool|WP_Error Whether the post thumbnail was successfully deleted, otherwise WP_Error. */ protected function handle_featured_media( $featured_media, $post_id ) { $post_type = get_post_type( $post_id ); $thumbnail_support = current_theme_supports( 'post-thumbnails', $post_type ) && post_type_supports( $post_type, 'thumbnail' ); // Similar check as in wp_insert_post(). if ( ! $thumbnail_support && get_post_mime_type( $post_id ) ) { if ( wp_attachment_is( 'audio', $post_id ) ) { $thumbnail_support = post_type_supports( 'attachment:audio', 'thumbnail' ) || current_theme_supports( 'post-thumbnails', 'attachment:audio' ); } elseif ( wp_attachment_is( 'video', $post_id ) ) { $thumbnail_support = post_type_supports( 'attachment:video', 'thumbnail' ) || current_theme_supports( 'post-thumbnails', 'attachment:video' ); } } if ( $thumbnail_support ) { return parent::handle_featured_media( $featured_media, $post_id ); } return new WP_Error( 'rest_no_featured_media', sprintf( /* translators: %s: attachment mime type */ __( 'This site does not support post thumbnails on attachments with MIME type %s.' ), get_post_mime_type( $post_id ) ), array( 'status' => 400 ) ); } /** * Updates a single attachment. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, WP_Error object on failure. */ public function update_item( $request ) { if ( ! empty( $request['post'] ) && in_array( get_post_type( $request['post'] ), array( 'revision', 'attachment' ), true ) ) { return new WP_Error( 'rest_invalid_param', __( 'Invalid parent type.' ), array( 'status' => 400 ) ); } $attachment_before = get_post( $request['id'] ); $response = parent::update_item( $request ); if ( is_wp_error( $response ) ) { return $response; } $response = rest_ensure_response( $response ); $data = $response->get_data(); if ( isset( $request['alt_text'] ) ) { update_post_meta( $data['id'], '_wp_attachment_image_alt', $request['alt_text'] ); } $attachment = get_post( $request['id'] ); if ( ! empty( $schema['properties']['featured_media'] ) && isset( $request['featured_media'] ) ) { $thumbnail_update = $this->handle_featured_media( $request['featured_media'], $attachment->ID ); if ( is_wp_error( $thumbnail_update ) ) { return $thumbnail_update; } } $fields_update = $this->update_additional_fields_for_object( $attachment, $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-attachments-controller.php */ do_action( 'rest_after_insert_attachment', $attachment, $request, false ); wp_after_insert_post( $attachment, true, $attachment_before ); $response = $this->prepare_item_for_response( $attachment, $request ); $response = rest_ensure_response( $response ); return $response; } /** * Performs post processing on an attachment. * * @since 5.3.0 * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, WP_Error object on failure. */ public function post_process_item( $request ) { switch ( $request['action'] ) { case 'create-image-subsizes': require_once ABSPATH . 'wp-admin/includes/image.php'; wp_update_image_subsizes( $request['id'] ); break; } $request['context'] = 'edit'; return $this->prepare_item_for_response( get_post( $request['id'] ), $request ); } /** * Checks if a given request can perform post processing on an attachment. * * @since 5.3.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, WP_Error object otherwise. */ public function post_process_item_permissions_check( $request ) { return $this->update_item_permissions_check( $request ); } /** * Checks if a given request has access to editing media. * * @since 5.5.0 * * @param WP_REST_Request $request Full details about the request. * @return true|WP_Error True if the request has read access, WP_Error object otherwise. */ public function edit_media_item_permissions_check( $request ) { if ( ! current_user_can( 'upload_files' ) ) { return new WP_Error( 'rest_cannot_edit_image', __( 'Sorry, you are not allowed to upload media on this site.' ), array( 'status' => rest_authorization_required_code() ) ); } return $this->update_item_permissions_check( $request ); } /** * Applies edits to a media item and creates a new attachment record. * * @since 5.5.0 * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, WP_Error object on failure. */ public function edit_media_item( $request ) { require_once ABSPATH . 'wp-admin/includes/image.php'; $attachment_id = $request['id']; // This also confirms the attachment is an image. $image_file = wp_get_original_image_path( $attachment_id ); $image_meta = wp_get_attachment_metadata( $attachment_id ); if ( ! $image_meta || ! $image_file || ! wp_image_file_matches_image_meta( $request['src'], $image_meta, $attachment_id ) ) { return new WP_Error( 'rest_unknown_attachment', __( 'Unable to get meta information for file.' ), array( 'status' => 404 ) ); } $supported_types = array( 'image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/avif' ); $mime_type = get_post_mime_type( $attachment_id ); if ( ! in_array( $mime_type, $supported_types, true ) ) { return new WP_Error( 'rest_cannot_edit_file_type', __( 'This type of file cannot be edited.' ), array( 'status' => 400 ) ); } // The `modifiers` param takes precedence over the older format. if ( isset( $request['modifiers'] ) ) { $modifiers = $request['modifiers']; } else { $modifiers = array(); if ( ! empty( $request['rotation'] ) ) { $modifiers[] = array( 'type' => 'rotate', 'args' => array( 'angle' => $request['rotation'], ), ); } if ( isset( $request['x'], $request['y'], $request['width'], $request['height'] ) ) { $modifiers[] = array( 'type' => 'crop', 'args' => array( 'left' => $request['x'], 'top' => $request['y'], 'width' => $request['width'], 'height' => $request['height'], ), ); } if ( 0 === count( $modifiers ) ) { return new WP_Error( 'rest_image_not_edited', __( 'The image was not edited. Edit the image before applying the changes.' ), array( 'status' => 400 ) ); } } /* * If the file doesn't exist, attempt a URL fopen on the src link. * This can occur with certain file replication plugins. * Keep the original file path to get a modified name later. */ $image_file_to_edit = $image_file; if ( ! file_exists( $image_file_to_edit ) ) { $image_file_to_edit = _load_image_to_edit_path( $attachment_id ); } $image_editor = wp_get_image_editor( $image_file_to_edit ); if ( is_wp_error( $image_editor ) ) { return new WP_Error( 'rest_unknown_image_file_type', __( 'Unable to edit this image.' ), array( 'status' => 500 ) ); } foreach ( $modifiers as $modifier ) { $args = $modifier['args']; switch ( $modifier['type'] ) { case 'rotate': // Rotation direction: clockwise vs. counter clockwise. $rotate = 0 - $args['angle']; if ( 0 !== $rotate ) { $result = $image_editor->rotate( $rotate ); if ( is_wp_error( $result ) ) { return new WP_Error( 'rest_image_rotation_failed', __( 'Unable to rotate this image.' ), array( 'status' => 500 ) ); } } break; case 'crop': $size = $image_editor->get_size(); $crop_x = (int) round( ( $size['width'] * $args['left'] ) / 100.0 ); $crop_y = (int) round( ( $size['height'] * $args['top'] ) / 100.0 ); $width = (int) round( ( $size['width'] * $args['width'] ) / 100.0 ); $height = (int) round( ( $size['height'] * $args['height'] ) / 100.0 ); if ( $size['width'] !== $width || $size['height'] !== $height ) { $result = $image_editor->crop( $crop_x, $crop_y, $width, $height ); if ( is_wp_error( $result ) ) { return new WP_Error( 'rest_image_crop_failed', __( 'Unable to crop this image.' ), array( 'status' => 500 ) ); } } break; } } // Calculate the file name. $image_ext = pathinfo( $image_file, PATHINFO_EXTENSION ); $image_name = wp_basename( $image_file, ".{$image_ext}" ); /* * Do not append multiple `-edited` to the file name. * The user may be editing a previously edited image. */ if ( preg_match( '/-edited(-\d+)?$/', $image_name ) ) { // Remove any `-1`, `-2`, etc. `wp_unique_filename()` will add the proper number. $image_name = preg_replace( '/-edited(-\d+)?$/', '-edited', $image_name ); } else { // Append `-edited` before the extension. $image_name .= '-edited'; } $filename = "{$image_name}.{$image_ext}"; // Create the uploads sub-directory if needed. $uploads = wp_upload_dir(); // Make the file name unique in the (new) upload directory. $filename = wp_unique_filename( $uploads['path'], $filename ); // Save to disk. $saved = $image_editor->save( $uploads['path'] . "/$filename" ); if ( is_wp_error( $saved ) ) { return $saved; } // Create new attachment post. $new_attachment_post = array( 'post_mime_type' => $saved['mime-type'], 'guid' => $uploads['url'] . "/$filename", 'post_title' => $image_name, 'post_content' => '', ); // Copy post_content, post_excerpt, and post_title from the edited image's attachment post. $attachment_post = get_post( $attachment_id ); if ( $attachment_post ) { $new_attachment_post['post_content'] = $attachment_post->post_content; $new_attachment_post['post_excerpt'] = $attachment_post->post_excerpt; $new_attachment_post['post_title'] = $attachment_post->post_title; } $new_attachment_id = wp_insert_attachment( wp_slash( $new_attachment_post ), $saved['path'], 0, true ); if ( is_wp_error( $new_attachment_id ) ) { if ( 'db_update_error' === $new_attachment_id->get_error_code() ) { $new_attachment_id->add_data( array( 'status' => 500 ) ); } else { $new_attachment_id->add_data( array( 'status' => 400 ) ); } return $new_attachment_id; } // Copy the image alt text from the edited image. $image_alt = get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ); if ( ! empty( $image_alt ) ) { // update_post_meta() expects slashed. update_post_meta( $new_attachment_id, '_wp_attachment_image_alt', wp_slash( $image_alt ) ); } if ( wp_is_serving_rest_request() ) { /* * Set a custom header with the attachment_id. * Used by the browser/client to resume creating image sub-sizes after a PHP fatal error. */ header( 'X-WP-Upload-Attachment-ID: ' . $new_attachment_id ); } // Generate image sub-sizes and meta. $new_image_meta = wp_generate_attachment_metadata( $new_attachment_id, $saved['path'] ); // Copy the EXIF metadata from the original attachment if not generated for the edited image. if ( isset( $image_meta['image_meta'] ) && isset( $new_image_meta['image_meta'] ) && is_array( $new_image_meta['image_meta'] ) ) { // Merge but skip empty values. foreach ( (array) $image_meta['image_meta'] as $key => $value ) { if ( empty( $new_image_meta['image_meta'][ $key ] ) && ! empty( $value ) ) { $new_image_meta['image_meta'][ $key ] = $value; } } } // Reset orientation. At this point the image is edited and orientation is correct. if ( ! empty( $new_image_meta['image_meta']['orientation'] ) ) { $new_image_meta['image_meta']['orientation'] = 1; } // The attachment_id may change if the site is exported and imported. $new_image_meta['parent_image'] = array( 'attachment_id' => $attachment_id, // Path to the originally uploaded image file relative to the uploads directory. 'file' => _wp_relative_upload_path( $image_file ), ); /** * Filters the meta data for the new image created by editing an existing image. * * @since 5.5.0 * * @param array $new_image_meta Meta data for the new image. * @param int $new_attachment_id Attachment post ID for the new image. * @param int $attachment_id Attachment post ID for the edited (parent) image. */ $new_image_meta = apply_filters( 'wp_edited_image_metadata', $new_image_meta, $new_attachment_id, $attachment_id ); wp_update_attachment_metadata( $new_attachment_id, $new_image_meta ); $response = $this->prepare_item_for_response( get_post( $new_attachment_id ), $request ); $response->set_status( 201 ); $response->header( 'Location', rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $new_attachment_id ) ) ); return $response; } /** * Prepares a single attachment for create or update. * * @since 4.7.0 * * @param WP_REST_Request $request Request object. * @return stdClass|WP_Error Post object. */ protected function prepare_item_for_database( $request ) { $prepared_attachment = parent::prepare_item_for_database( $request ); // Attachment caption (post_excerpt internally). if ( isset( $request['caption'] ) ) { if ( is_string( $request['caption'] ) ) { $prepared_attachment->post_excerpt = $request['caption']; } elseif ( isset( $request['caption']['raw'] ) ) { $prepared_attachment->post_excerpt = $request['caption']['raw']; } } // Attachment description (post_content internally). if ( isset( $request['description'] ) ) { if ( is_string( $request['description'] ) ) { $prepared_attachment->post_content = $request['description']; } elseif ( isset( $request['description']['raw'] ) ) { $prepared_attachment->post_content = $request['description']['raw']; } } if ( isset( $request['post'] ) ) { $prepared_attachment->post_parent = (int) $request['post']; } return $prepared_attachment; } /** * Prepares a single attachment output for response. * * @since 4.7.0 * @since 5.9.0 Renamed `$post` to `$item` to match parent class for PHP 8 named parameter support. * * @param WP_Post $item Attachment 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. $post = $item; $response = parent::prepare_item_for_response( $post, $request ); $fields = $this->get_fields_for_response( $request ); $data = $response->get_data(); if ( in_array( 'description', $fields, true ) ) { $data['description'] = array( 'raw' => $post->post_content, /** This filter is documented in wp-includes/post-template.php */ 'rendered' => apply_filters( 'the_content', $post->post_content ), ); } if ( in_array( 'caption', $fields, true ) ) { /** This filter is documented in wp-includes/post-template.php */ $caption = apply_filters( 'get_the_excerpt', $post->post_excerpt, $post ); /** This filter is documented in wp-includes/post-template.php */ $caption = apply_filters( 'the_excerpt', $caption ); $data['caption'] = array( 'raw' => $post->post_excerpt, 'rendered' => $caption, ); } if ( in_array( 'alt_text', $fields, true ) ) { $data['alt_text'] = get_post_meta( $post->ID, '_wp_attachment_image_alt', true ); } if ( in_array( 'media_type', $fields, true ) ) { $data['media_type'] = wp_attachment_is_image( $post->ID ) ? 'image' : 'file'; } if ( in_array( 'mime_type', $fields, true ) ) { $data['mime_type'] = $post->post_mime_type; } if ( in_array( 'media_details', $fields, true ) ) { $data['media_details'] = wp_get_attachment_metadata( $post->ID ); // Ensure empty details is an empty object. if ( empty( $data['media_details'] ) ) { $data['media_details'] = new stdClass(); } elseif ( ! empty( $data['media_details']['sizes'] ) ) { foreach ( $data['media_details']['sizes'] as $size => &$size_data ) { if ( isset( $size_data['mime-type'] ) ) { $size_data['mime_type'] = $size_data['mime-type']; unset( $size_data['mime-type'] ); } // Use the same method image_downsize() does. $image_src = wp_get_attachment_image_src( $post->ID, $size ); if ( ! $image_src ) { continue; } $size_data['source_url'] = $image_src[0]; } $full_src = wp_get_attachment_image_src( $post->ID, 'full' ); if ( ! empty( $full_src ) ) { $data['media_details']['sizes']['full'] = array( 'file' => wp_basename( $full_src[0] ), 'width' => $full_src[1], 'height' => $full_src[2], 'mime_type' => $post->post_mime_type, 'source_url' => $full_src[0], ); } } else { $data['media_details']['sizes'] = new stdClass(); } } if ( in_array( 'post', $fields, true ) ) { $data['post'] = ! empty( $post->post_parent ) ? (int) $post->post_parent : null; } if ( in_array( 'source_url', $fields, true ) ) { $data['source_url'] = wp_get_attachment_url( $post->ID ); } if ( in_array( 'missing_image_sizes', $fields, true ) ) { require_once ABSPATH . 'wp-admin/includes/image.php'; $data['missing_image_sizes'] = array_keys( wp_get_missing_image_subsizes( $post->ID ) ); } $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->filter_response_by_context( $data, $context ); $links = $response->get_links(); // Wrap the data in a response object. $response = rest_ensure_response( $data ); foreach ( $links as $rel => $rel_links ) { foreach ( $rel_links as $link ) { $response->add_link( $rel, $link['href'], $link['attributes'] ); } } /** * Filters an attachment returned from the REST API. * * Allows modification of the attachment right before it is returned. * * @since 4.7.0 * * @param WP_REST_Response $response The response object. * @param WP_Post $post The original attachment post. * @param WP_REST_Request $request Request used to generate the response. */ return apply_filters( 'rest_prepare_attachment', $response, $post, $request ); } /** * Retrieves the attachment's schema, conforming to JSON Schema. * * @since 4.7.0 * * @return array Item schema as an array. */ public function get_item_schema() { if ( $this->schema ) { return $this->add_additional_fields_schema( $this->schema ); } $schema = parent::get_item_schema(); $schema['properties']['alt_text'] = array( 'description' => __( 'Alternative text to display when attachment is not displayed.' ), 'type' => 'string', 'context' => array( 'view', 'edit', 'embed' ), 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), ); $schema['properties']['caption'] = array( 'description' => __( 'The attachment caption.' ), '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' => __( 'Caption for the attachment, as it exists in the database.' ), 'type' => 'string', 'context' => array( 'edit' ), ), 'rendered' => array( 'description' => __( 'HTML caption for the attachment, transformed for display.' ), 'type' => 'string', 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ), ), ); $schema['properties']['description'] = array( 'description' => __( 'The attachment description.' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), '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' => __( 'Description for the attachment, as it exists in the database.' ), 'type' => 'string', 'context' => array( 'edit' ), ), 'rendered' => array( 'description' => __( 'HTML description for the attachment, transformed for display.' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), ), ); $schema['properties']['media_type'] = array( 'description' => __( 'Attachment type.' ), 'type' => 'string', 'enum' => array( 'image', 'file' ), 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ); $schema['properties']['mime_type'] = array( 'description' => __( 'The attachment MIME type.' ), 'type' => 'string', 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ); $schema['properties']['media_details'] = array( 'description' => __( 'Details about the media file, specific to its type.' ), 'type' => 'object', 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ); $schema['properties']['post'] = array( 'description' => __( 'The ID for the associated post of the attachment.' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ); $schema['properties']['source_url'] = array( 'description' => __( 'URL to the original attachment file.' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ); $schema['properties']['missing_image_sizes'] = array( 'description' => __( 'List of the missing image sizes of the attachment.' ), 'type' => 'array', 'items' => array( 'type' => 'string' ), 'context' => array( 'edit' ), 'readonly' => true, ); unset( $schema['properties']['password'] ); $this->schema = $schema; return $this->add_additional_fields_schema( $this->schema ); } /** * Handles an upload via raw POST data. * * @since 4.7.0 * @since 6.6.0 Added the `$time` parameter. * * @param string $data Supplied file data. * @param array $headers HTTP headers from the request. * @param string|null $time Optional. Time formatted in 'yyyy/mm'. Default null. * @return array|WP_Error Data from wp_handle_sideload(). */ protected function upload_from_data( $data, $headers, $time = null ) { if ( empty( $data ) ) { return new WP_Error( 'rest_upload_no_data', __( 'No data supplied.' ), array( 'status' => 400 ) ); } if ( empty( $headers['content_type'] ) ) { return new WP_Error( 'rest_upload_no_content_type', __( 'No Content-Type supplied.' ), array( 'status' => 400 ) ); } if ( empty( $headers['content_disposition'] ) ) { return new WP_Error( 'rest_upload_no_content_disposition', __( 'No Content-Disposition supplied.' ), array( 'status' => 400 ) ); } $filename = self::get_filename_from_disposition( $headers['content_disposition'] ); if ( empty( $filename ) ) { return new WP_Error( 'rest_upload_invalid_disposition', __( 'Invalid Content-Disposition supplied. Content-Disposition needs to be formatted as `attachment; filename="image.png"` or similar.' ), array( 'status' => 400 ) ); } if ( ! empty( $headers['content_md5'] ) ) { $content_md5 = array_shift( $headers['content_md5'] ); $expected = trim( $content_md5 ); $actual = md5( $data ); if ( $expected !== $actual ) { return new WP_Error( 'rest_upload_hash_mismatch', __( 'Content hash did not match expected.' ), array( 'status' => 412 ) ); } } // Get the content-type. $type = array_shift( $headers['content_type'] ); // Include filesystem functions to get access to wp_tempnam() and wp_handle_sideload(). require_once ABSPATH . 'wp-admin/includes/file.php'; // Save the file. $tmpfname = wp_tempnam( $filename ); $fp = fopen( $tmpfname, 'w+' ); if ( ! $fp ) { return new WP_Error( 'rest_upload_file_error', __( 'Could not open file handle.' ), array( 'status' => 500 ) ); } fwrite( $fp, $data ); fclose( $fp ); // Now, sideload it in. $file_data = array( 'error' => null, 'tmp_name' => $tmpfname, 'name' => $filename, 'type' => $type, ); $size_check = self::check_upload_size( $file_data ); if ( is_wp_error( $size_check ) ) { return $size_check; } $overrides = array( 'test_form' => false, ); $sideloaded = wp_handle_sideload( $file_data, $overrides, $time ); if ( isset( $sideloaded['error'] ) ) { @unlink( $tmpfname ); return new WP_Error( 'rest_upload_sideload_error', $sideloaded['error'], array( 'status' => 500 ) ); } return $sideloaded; } /** * Parses filename from a Content-Disposition header value. * * As per RFC6266: * * content-disposition = "Content-Disposition" ":" * disposition-type *( ";" disposition-parm ) * * disposition-type = "inline" | "attachment" | disp-ext-type * ; case-insensitive * disp-ext-type = token * * disposition-parm = filename-parm | disp-ext-parm * * filename-parm = "filename" "=" value * | "filename*" "=" ext-value * * disp-ext-parm = token "=" value * | ext-token "=" ext-value * ext-token = * * @since 4.7.0 * * @link https://tools.ietf.org/html/rfc2388 * @link https://tools.ietf.org/html/rfc6266 * * @param string[] $disposition_header List of Content-Disposition header values. * @return string|null Filename if available, or null if not found. */ public static function get_filename_from_disposition( $disposition_header ) { // Get the filename. $filename = null; foreach ( $disposition_header as $value ) { $value = trim( $value ); if ( ! str_contains( $value, ';' ) ) { continue; } list( $type, $attr_parts ) = explode( ';', $value, 2 ); $attr_parts = explode( ';', $attr_parts ); $attributes = array(); foreach ( $attr_parts as $part ) { if ( ! str_contains( $part, '=' ) ) { continue; } list( $key, $value ) = explode( '=', $part, 2 ); $attributes[ trim( $key ) ] = trim( $value ); } if ( empty( $attributes['filename'] ) ) { continue; } $filename = trim( $attributes['filename'] ); // Unquote quoted filename, but after trimming. if ( str_starts_with( $filename, '"' ) && str_ends_with( $filename, '"' ) ) { $filename = substr( $filename, 1, -1 ); } } return $filename; } /** * Retrieves the query params for collections of attachments. * * @since 4.7.0 * * @return array Query parameters for the attachment collection as an array. */ public function get_collection_params() { $params = parent::get_collection_params(); $params['status']['default'] = 'inherit'; $params['status']['items']['enum'] = array( 'inherit', 'private', 'trash' ); $media_types = $this->get_media_types(); $params['media_type'] = array( 'default' => null, 'description' => __( 'Limit result set to attachments of a particular media type.' ), 'type' => 'string', 'enum' => array_keys( $media_types ), ); $params['mime_type'] = array( 'default' => null, 'description' => __( 'Limit result set to attachments of a particular MIME type.' ), 'type' => 'string', ); return $params; } /** * Handles an upload via multipart/form-data ($_FILES). * * @since 4.7.0 * @since 6.6.0 Added the `$time` parameter. * * @param array $files Data from the `$_FILES` superglobal. * @param array $headers HTTP headers from the request. * @param string|null $time Optional. Time formatted in 'yyyy/mm'. Default null. * @return array|WP_Error Data from wp_handle_upload(). */ protected function upload_from_file( $files, $headers, $time = null ) { if ( empty( $files ) ) { return new WP_Error( 'rest_upload_no_data', __( 'No data supplied.' ), array( 'status' => 400 ) ); } // Verify hash, if given. if ( ! empty( $headers['content_md5'] ) ) { $content_md5 = array_shift( $headers['content_md5'] ); $expected = trim( $content_md5 ); $actual = md5_file( $files['file']['tmp_name'] ); if ( $expected !== $actual ) { return new WP_Error( 'rest_upload_hash_mismatch', __( 'Content hash did not match expected.' ), array( 'status' => 412 ) ); } } // Pass off to WP to handle the actual upload. $overrides = array( 'test_form' => false, ); // Bypasses is_uploaded_file() when running unit tests. if ( defined( 'DIR_TESTDATA' ) && DIR_TESTDATA ) { $overrides['action'] = 'wp_handle_mock_upload'; } $size_check = self::check_upload_size( $files['file'] ); if ( is_wp_error( $size_check ) ) { return $size_check; } // Include filesystem functions to get access to wp_handle_upload(). require_once ABSPATH . 'wp-admin/includes/file.php'; $file = wp_handle_upload( $files['file'], $overrides, $time ); if ( isset( $file['error'] ) ) { return new WP_Error( 'rest_upload_unknown_error', $file['error'], array( 'status' => 500 ) ); } return $file; } /** * Retrieves the supported media types. * * Media types are considered the MIME type category. * * @since 4.7.0 * * @return array Array of supported media types. */ protected function get_media_types() { $media_types = array(); foreach ( get_allowed_mime_types() as $mime_type ) { $parts = explode( '/', $mime_type ); if ( ! isset( $media_types[ $parts[0] ] ) ) { $media_types[ $parts[0] ] = array(); } $media_types[ $parts[0] ][] = $mime_type; } return $media_types; } /** * Determine if uploaded file exceeds space quota on multisite. * * Replicates check_upload_size(). * * @since 4.9.8 * * @param array $file $_FILES array for a given file. * @return true|WP_Error True if can upload, error for errors. */ protected function check_upload_size( $file ) { if ( ! is_multisite() ) { return true; } if ( get_site_option( 'upload_space_check_disabled' ) ) { return true; } $space_left = get_upload_space_available(); $file_size = filesize( $file['tmp_name'] ); if ( $space_left < $file_size ) { return new WP_Error( 'rest_upload_limited_space', /* translators: %s: Required disk space in kilobytes. */ sprintf( __( 'Not enough space to upload. %s KB needed.' ), number_format( ( $file_size - $space_left ) / KB_IN_BYTES ) ), array( 'status' => 400 ) ); } if ( $file_size > ( KB_IN_BYTES * get_site_option( 'fileupload_maxk', 1500 ) ) ) { return new WP_Error( 'rest_upload_file_too_big', /* translators: %s: Maximum allowed file size in kilobytes. */ sprintf( __( 'This file is too big. Files must be less than %s KB in size.' ), get_site_option( 'fileupload_maxk', 1500 ) ), array( 'status' => 400 ) ); } // Include multisite admin functions to get access to upload_is_user_over_quota(). require_once ABSPATH . 'wp-admin/includes/ms.php'; if ( upload_is_user_over_quota( false ) ) { return new WP_Error( 'rest_upload_user_quota_exceeded', __( 'You have used your space quota. Please delete files before uploading.' ), array( 'status' => 400 ) ); } return true; } /** * Gets the request args for the edit item route. * * @since 5.5.0 * * @return array */ protected function get_edit_media_item_args() { return array( 'src' => array( 'description' => __( 'URL to the edited image file.' ), 'type' => 'string', 'format' => 'uri', 'required' => true, ), 'modifiers' => array( 'description' => __( 'Array of image edits.' ), 'type' => 'array', 'minItems' => 1, 'items' => array( 'description' => __( 'Image edit.' ), 'type' => 'object', 'required' => array( 'type', 'args', ), 'oneOf' => array( array( 'title' => __( 'Rotation' ), 'properties' => array( 'type' => array( 'description' => __( 'Rotation type.' ), 'type' => 'string', 'enum' => array( 'rotate' ), ), 'args' => array( 'description' => __( 'Rotation arguments.' ), 'type' => 'object', 'required' => array( 'angle', ), 'properties' => array( 'angle' => array( 'description' => __( 'Angle to rotate clockwise in degrees.' ), 'type' => 'number', ), ), ), ), ), array( 'title' => __( 'Crop' ), 'properties' => array( 'type' => array( 'description' => __( 'Crop type.' ), 'type' => 'string', 'enum' => array( 'crop' ), ), 'args' => array( 'description' => __( 'Crop arguments.' ), 'type' => 'object', 'required' => array( 'left', 'top', 'width', 'height', ), 'properties' => array( 'left' => array( 'description' => __( 'Horizontal position from the left to begin the crop as a percentage of the image width.' ), 'type' => 'number', ), 'top' => array( 'description' => __( 'Vertical position from the top to begin the crop as a percentage of the image height.' ), 'type' => 'number', ), 'width' => array( 'description' => __( 'Width of the crop as a percentage of the image width.' ), 'type' => 'number', ), 'height' => array( 'description' => __( 'Height of the crop as a percentage of the image height.' ), 'type' => 'number', ), ), ), ), ), ), ), ), 'rotation' => array( 'description' => __( 'The amount to rotate the image clockwise in degrees. DEPRECATED: Use `modifiers` instead.' ), 'type' => 'integer', 'minimum' => 0, 'exclusiveMinimum' => true, 'maximum' => 360, 'exclusiveMaximum' => true, ), 'x' => array( 'description' => __( 'As a percentage of the image, the x position to start the crop from. DEPRECATED: Use `modifiers` instead.' ), 'type' => 'number', 'minimum' => 0, 'maximum' => 100, ), 'y' => array( 'description' => __( 'As a percentage of the image, the y position to start the crop from. DEPRECATED: Use `modifiers` instead.' ), 'type' => 'number', 'minimum' => 0, 'maximum' => 100, ), 'width' => array( 'description' => __( 'As a percentage of the image, the width to crop the image to. DEPRECATED: Use `modifiers` instead.' ), 'type' => 'number', 'minimum' => 0, 'maximum' => 100, ), 'height' => array( 'description' => __( 'As a percentage of the image, the height to crop the image to. DEPRECATED: Use `modifiers` instead.' ), 'type' => 'number', 'minimum' => 0, 'maximum' => 100, ), ); } } Παίξτε ελεύθερα στο Sporting Bet Casino – Sanathan Dharm Veda
Έκτακτη είδηση: Η Amusnet στην Sportingbet!

Στο Sporting Bet Casino, μπορούμε να βυθιστούμε σε ένα φανταστικό περιβάλλον παιχνιδιού όπου οι επιλογές φαίνονται ατελείωτες. Από δημοφιλή επιτραπέζια παιχνίδια μέχρι διαδραστικούς κουλοχέρηδες και ζωντανούς κρουπιέρηδες, υπάρχει κάτι για κάθε γούστο. Επιπλέον, ο εύχρηστος σχεδιασμός τους κάνει την ανακάλυψη των αγαπημένων μας παιχνιδιών παιχνιδάκι. Έτσι, καθώς ανακαλύπτουμε τι προσφέρει αυτή η εντυπωσιακή κοινότητα, θα ανακαλύψουμε τα μπόνους και τις επιλογές που πραγματικά βελτιώνουν την απόλαυσή μας. Ας μάθουμε τι μας αναμένει στη συνέχεια. Sportingbetκαζίνο

Βασικά σημεία

  • Χαρείτε μια ποικιλία από δημοφιλή και ειδικά παιχνίδια, μεταξύ των οποίων συναρπαστικών επιλογών με ζωντανό κρουπιέρη για ατελείωτη διασκέδαση.
  • Απολαύστε λειτουργίες γρήγορης πρόσβασης όπως άμεση αναπαραγωγή και καρτέλα αγαπημένων, απλοποιώντας την επιλογή παιχνιδιών.
  • Επωφεληθείτε από μεγάλα μπόνους καλωσορίσματος και συνεχείς προσφορές, μεγιστοποιώντας το κεφάλαιό σας και τον χρόνο παιχνιδιού σας.
  • Χρησιμοποιήστε μια mobile-friendly πλατφόρμα, που επιτρέπει την αδιάκοπη απόλαυση παιχνιδιών από κάθε μέρος, κάθε στιγμή.
  • Βασιστείτε στην υποστήριξη πελατών 24/7 με ποικιλία επιλογών επικοινωνίας, διασφαλίζοντας ότι η υποστήριξη είναι πάντα διαθέσιμη όταν χρειάζεται.

Επιλογή Παιχνιδιού: Ένας Κόσμος Επιλογών

Όσον αφορά την διαθεσιμότητα παιχνιδιών στο Sporting Bet Casino, υπάρχει μια μεγάλη γκάμα επιλογών για να ανακαλύψουμε. Από κλασικά επιτραπέζια παιχνίδια όπως 21 και ρουλέτα μέχρι τα καινούργια slots, υπάρχει πραγματικά κάτι για όλους. Μπορούμε να απολαύσουμε σε συναρπαστικές εμπειρίες με ζωντανό ντίλερ όπου η συγκίνηση του καζίνο έρχεται κατευθείαν στις οθόνες μας.

Înregistrare Sportingbet Casino 2025 👠 SloturipeTocuri.ro

Προσθέτως, η μεγάλη συλλογή μας περιλαμβάνει ενδιαφέροντα θεματικά παιχνίδια κουλοχέρηδων που μας μεταφέρουν σε ποικίλους κόσμους! Όποια και αν είναι η διάθεσή μας, έχουμε επιλογές που συναρπάζουν και καθηλώνουν.

Με συχνές ενημερώσεις στη βιβλιοθήκη παιχνιδιών μας, θα βρίσκουμε πάντα νέα αγαπημένα. Μαζί, ας εξερευνήσουμε και ας εντοπίσουμε το ιδανικό παιχνίδι που μας μιλάει, ενισχύοντας την αίσθηση του ανήκειν σε αυτήν τη δυναμική κοινότητα παιχνιδιών. Ελάτε μαζί μας και ας παίξουμε!

Μπόνους και Προσφορές: Μεγιστοποιώντας το Παιχνίδι σας

Αφού εξερευνήσαμε την τεράστια ποικιλία παιχνιδιών στο Sporting Bet Casino, ήρθε η ώρα να συζητήσουμε για το πώς μπορούμε να βελτιώσουμε την εμπειρία παιχνιδιού μας μέσω μπόνους και προσφορών. Όλοι εκτιμούμε μια καλή προσφορά, σωστά; Το Sporting Bet Casino προσφέρει μια σειρά από συναρπαστικά μπόνους σχεδιασμένα τόσο για καινούργιους όσο και για πιστούς παίκτες. Από μπόνους καλωσορίσματος μέχρι δωρεάν γυρίσματα και επιστροφές μετρητών, αυτές οι προσφορές μπορούν να αναβαθμίσουν σημαντικά το κεφάλαιό μας, παρέχοντάς μας περισσότερες ευκαιρίες να νικήσουμε. Ας μην ξεχνάμε τις τακτικές προωθητικές εκδηλώσεις που κρατούν έντονο τον ενθουσιασμό και μας ενθαρρύνουν να επιστρέψουμε. Χρησιμοποιώντας από αυτές τις προσφορές, μπορούμε να αυξήσουμε τον χρόνο παιχνιδιού μας και να αισθανθούμε πιο δεμένοι με αυτήν την ζωντανή κοινότητα παιχνιδιών. Ας εμβαθύνουμε και ας αξιοποιήσουμε στο έπακρο ό,τι είναι διαθέσιμο!

Εμπειρία χρήστη: Ομαλή πλοήγηση και σχεδιασμός

Όταν μελετάμε την εμπειρία χρήστη στο Sporting Bet Casino, δεν μπορούμε να παρακάμψουμε τον φιλικό προς το χρήστη σχεδιασμό διάταξης που κάνει την πλοήγηση απλή. Με άμεσες λειτουργίες, η εύρεση των αγαπημένων μας παιχνιδιών είναι εύκολη και η φιλική προς τα κινητά εμπειρία τους διασφαλίζει ότι μπορούμε να απολαμβάνουμε παιχνίδια εν κινήσει. Ας εξερευνήσουμε πώς αυτά τα στοιχεία ενώνονται για να ενισχύσουν τη συνολική μας διασκέδαση.

Διαισθητικός Σχεδιασμός Διάταξης

Ένας διορατικός σχεδιασμός διάταξης είναι ζωτικής σημασίας για τη αναβάθμιση της εμπειρίας μας στο Sporting Bet Casino. Όταν ξεκινάμε στην πλατφόρμα, μας δίνει μια αίσθηση ευχάριστης ατμόσφαιρας, με μια διεπαφή κατασκευασμένη να μας καθοδηγεί να κινούμαστε εύκολα. Κάθε λειτουργία είναι τακτοποιημένη με φροντίδα, κατευθύνοντάς μας στα αγαπημένα μας παιχνίδια χωρίς καμία σύγχυση ή δυσκολία. Φιλικά προς το χρήστη εικόνες και ετικέτες μας διευκολύνουν στην ανακάλυψη, υποσχόμενοι ότι περνάμε λιγότερο χρόνο ψάχνοντας και περισσότερο χρόνο παίζοντας. Το χρωματική παλέτα και τα σχέδια δεν είναι απλώς ελκυστικά. Κατασκευάζουν μια φιλόξενη ατμόσφαιρα που μας καλεί, δημιουργώντας μας να αντιλαμβανόμαστε μια αίσθηση κοινότητας. Αυτός ο καλοσχεδιασμένος σχεδιασμός δεν σχετίζεται με μόνο την εμφάνιση. Ενισχύει τη σχέση μας με το καζίνο, δίνοντάς μας να απολαύσουμε πλήρως την εμπειρία παιχνιδιού μας.

Λειτουργίες γρήγορης πρόσβασης

Πώς μπορούμε να διαχειριστούμε το Sporting Bet Casino με άνεση και ταχέως; Όλοι έχουμε αντιμετωπίσει εκείνες τις στιγμές που απλά θέλουμε να βυθιστούμε στα αγαπημένα μας παιχνίδια. Με το Sporting Bet Casino, οι λειτουργίες γρήγορης πρόσβασης υπόσχονται ότι μπορούμε να ξεκινήσουμε χωρίς καμία δυσκολία. Ακολουθούν μερικές υπέροχες επιλογές που βελτιώνουν το περιπέτεια μας στο παιχνίδι:

  • Επιλογές Άμεσης Αναπαραγωγής
  • Καρτέλα Προτιμήσεις
  • Λειτουργικότητα αναζήτησης
  • Κέντρο Προσφορών
  • Μαζί, αυτά τα χαρακτηριστικά δημιουργούν μια άνετη εμπειρία πλοήγησης που μας κάνει να αισθανόμαστε σαν στο σπίτι μας, ενισχύοντας την αίσθηση του ανήκειν καθώς εμβαθύνουμε σε ατελείωτη ψυχαγωγία.

    Εμπειρία φιλική προς κινητά

    Καθώς βυθιζόμαστε στον κόσμο του Sporting Bet Casino στις κινητές συσκευές μας, αντιλαμβανόμαστε γρήγορα ότι η εμπειρία χρήστη είναι προσαρμοσμένη για άνετη πλοήγηση και σχεδιασμό. Η διεπαφή μας υποδέχεται με έντονα γραφικά και εύχρηστες προς το χρήστη διατάξεις, διευκολύνοντας την εύρεση των αγαπημένων μας παιχνιδιών χωρίς κόπο. Είτε δοκιμάζουμε κουλοχέρηδες είτε εξετάζουμε την τύχη μας στο πόκερ, όλα είναι απλά. Οι άμεσοι χρόνοι φόρτωσης μας κρατούν αφοσιωμένους, ώστε να μην μένουμε ποτέ σε αναμονή. Επιπλέον, ο σχεδιασμός με δυνατότητα απόκρισης εγγυάται ότι μπορούμε να παίζουμε ομαλά, είτε ξεκουραζόμαστε στο σπίτι είτε εν κινήσει. Αυτό δημιουργεί μια αίσθηση ότι ανήκουμε σε μια ενεργή κοινότητα παιχνιδιών όπου μπορούμε να επικοινωνήσουμε, να ανταγωνιστούμε και να απολαύσουμε το προτιμώμενο μας χόμπι, όλα από την χούφτα του χεριού μας.

    Μέθοδοι πληρωμής: Εύκολες καταθέσεις και αναλήψεις

    Όταν έρχεται για την απόλαυση της εμπειρίας παιχνιδιού μας, οι άνετες μέθοδοι πληρωμής είναι απαραίτητες για απλές καταθέσεις και αναλήψεις. Στο Sporting Bet Casino, σας καλύπτουμε με λύσεις που συμβαδίζουν στις ανάγκες μας χωρίς κόπο. Μπορούμε να ενισχύσουμε άμεσα τους λογαριασμούς μας και να αποσύρουμε των κερδών μας χωρίς καμία ταλαιπωρία.

    Ακολουθούν μερικές από τις μεθόδους πληρωμής που μπορούμε να χρησιμοποιήσουμε άνετα:

    • Πιστωτικές και debit κάρτες για instant επεξεργασία
    • Ηλεκτρονικά πορτοφόλια όπως το Skrill για secure συναλλαγές
    • Τραπεζικές μεταφορές για μεγαλύτερες αναλήψεις
    • Προπληρωμένες κάρτες για controlled δαπάνες

    Με αυτές τις ευέλικτες επιλογές, μπορούμε να focus σε αυτό που truly έχει σημασία—να have fun και να win! Η ευκολία των συναλλαγών μας allows us να εξερευνήσουμε βαθύτερα τις περιπέτειές μας στα παιχνίδια, δημιουργώντας μια truly συναρπαστική εμπειρία κοινότητας.

    Κινητά παιχνίδια: Παίξτε anytime, anywhere

    Έχουμε imagined ποτέ μια εμπειρία παιχνιδιού που να fits τέλεια στην τσέπη μας; Με τα παιχνίδια για κινητά του Sporting Bet Casino, δεν χρειάζεται! Μπορούμε να immerse ourselves σε έναν exciting κόσμο κουλοχέρηδων, επιτραπέζιων παιχνιδιών και ζωντανών ντίλερ από οπουδήποτε. Είτε we are σε διάλειμμα για lunch, είτε commuting, είτε relaxing στο σπίτι, τα αγαπημένα μας παιχνίδια είναι μόνο ένα tap μακριά. Η user-friendly διεπαφή makes it easy την εξερεύνηση και τα captivating γραφικά μας keep us αφοσιωμένους. Επιπλέον, μπορούμε να benefit από αποκλειστικές προσφορές για κινητά, βελτιώνοντας την εμπειρία παιχνιδιού μας. Η συμμετοχή στην κοινότητα παιχνιδιών μας anytime, οπουδήποτε, σημαίνει ότι μπορούμε να bond για τις νίκες και να επευφημήσουμε ο ένας τον άλλον whenever μας comes inspiration. Ας embrace λοιπόν αυτήν την ελευθερία και ας ενισχύσουμε μαζί τις περιπέτειές μας στο παιχνίδι!

    Υποστήριξη πελατών: Βοήθεια when τη χρειάζεστε

    Στο Sporting Bet Casino, we understand ότι η reliable υποστήριξη πελατών είναι απαραίτητη για τους παίκτες μας. Γι’ αυτό we offer βοήθεια 24/7 μέσω πολλαπλών επιλογών επικοινωνίας, ensuring ότι δεν θα μείνετε ποτέ στο dark. Είτε πρόκειται για μια γρήγορη ερώτηση είτε για ένα πιο complex ζήτημα, we are εδώ για να σας help anytime!

    Διαθεσιμότητα υποστήριξης 24/7

    Πολλοί χρήστες εκτιμούν την ηρεμία που προσφέρει η γνώση ότι η βοήθεια είναι μόλις ένα κλικ μακριά. Στο Sporting Bet Casino, η παρουσία υποστήριξής μας εγγυάται ότι είμαστε εδώ για εσάς κάθε φορά που μας χρειαστείτε. Είτε σας βοηθάμε στα παιχνίδια είτε έχετε ερωτήσεις σχετικά με τον λογαριασμό σας, είμαστε στη διάθεσή σας.

    Ακολουθούν ορισμένα από τα κύρια σημεία της υποστήριξής μας:

    • 24/7 βοήθεια, ανεξάρτητα από την ώρα
    • Έμπειροι εκπρόσωποι, έτοιμοι να υποστηρίξουν σε οποιοδήποτε πρόβλημα
    • Φιλικό προς το χρήστη interface για εύκολη πρόσβαση στην υποστήριξη
    • Γρήγοροι χρόνοι αντίδρασης για να επιστρέψετε στο παιχνίδι

    Κατανοούμε ότι το να νιώθετε στήριξη είναι κρίσιμο για την εμπειρία gaming σας και δεσμευόμαστε να εγγυηθούμε ότι δεν θα νιώσετε ποτέ μόνοι στο ταξίδι σας μαζί μας.

    Πολλαπλές επιλογές επικοινωνίας

    Η απόφαση του σωστού τρόπου για να λάβετε υποστήριξη μπορεί να κάνει τη διαφορά στην εμπειρία gaming σας. Στο Sporting Bet Casino, προσφέρουμε πολλαπλές δυνατότητες επικοινωνίας, διασφαλίζοντας ότι δεν θα μείνετε ποτέ στο σκοτάδι. Είτε μέσω live chat, email είτε τηλεφώνου, είμαστε εδώ για εσάς όταν χρειάζεστε βοήθεια. Η φιλική ομάδα υποστήριξής μας απέχει μόνο ένα κλικ ή μια κλήση, έτοιμη να αντιμετωπίσει οποιοδήποτε πρόβλημα ή να ανταποκριθεί γρήγορα στις ερωτήσεις σας.

    Κατανοούμε τη σημασία του να νιώθουμε ενσωματωμένοι και πολύτιμοι στην κοινότητα gaming μας. Γι’ αυτό δίνουμε σημασία στην διαθεσιμότητα—επειδή θέλουμε να νιώθετε ότι σας στηρίζουν, ανεξάρτητα από το πού σας κατευθύνει το μονοπάτι σας στο gaming. Ας αγκαλιάσουμε την περιπέτεια μαζί γνωρίζοντας ότι είμαστε πάντα εδώ για να σας υποστηρίξουμε!

    Ασφάλεια και ασφάλεια: Παίζοντας με ηρεμία

    Όταν βυθιζόμαστε στον δυναμικό κόσμο των διαδικτυακών παιχνιδιών, η διασφάλιση της ασφάλειας και της προστασίας μας γίνεται κορυφαία προτεραιότητα. Στο Sporting Bet Casino, μπορούμε να παίξουμε με σιγουριά, γνωρίζοντας ότι τα προσωπικά και χρηματοοικονομικά μας στοιχεία προστατεύονται καλά. Να τι μας διαφοροποιεί:

    • Προηγμένη κρυπτογράφηση για την ασφάλεια των δεδομένων μας
    • Σκληρές διαδικασίες επαλήθευσης ταυτότητας
    • Συχνές έλεγχοι από αυτόνομες αρχές
    • Πλήρης υποστήριξη πελατών όταν τη απαιτούμε

    Αυτά τα χαρακτηριστικά όχι μόνο δημιουργούν ένα προστατευμένο περιβάλλον, αλλά ενισχύουν επίσης ένα συναίσθημα ότι ανήκουμε κάπου στην κοινότητα των gaming. Αξίζουμε να απολαμβάνουμε τα προτιμώμενα μας παιχνίδια χωρίς να φοβόμαστε για ζητήματα ασφάλειας. Με το Sporting Bet Casino, μπορούμε να επικεντρωθούμε σε αυτό που όντως έχει σημασία – να διασκεδάζουμε και να πανηγυρίζουμε κάθε νίκη μαζί!

    Leave a comment

    Your email address will not be published. Required fields are marked *