/** * REST API: WP_REST_Terms_Controller class * * @package WordPress * @subpackage REST_API * @since 4.7.0 */ /** * Core class used to managed terms associated with a taxonomy via the REST API. * * @since 4.7.0 * * @see WP_REST_Controller */ class WP_REST_Terms_Controller extends WP_REST_Controller { /** * Taxonomy key. * * @since 4.7.0 * @var string */ protected $taxonomy; /** * Instance of a term meta fields object. * * @since 4.7.0 * @var WP_REST_Term_Meta_Fields */ protected $meta; /** * Column to have the terms be sorted by. * * @since 4.7.0 * @var string */ protected $sort_column; /** * Number of terms that were found. * * @since 4.7.0 * @var int */ protected $total_terms; /** * Constructor. * * @since 4.7.0 * * @param string $taxonomy Taxonomy key. */ public function __construct( $taxonomy ) { $this->taxonomy = $taxonomy; $this->namespace = 'wp/v2'; $tax_obj = get_taxonomy( $taxonomy ); $this->rest_base = ! empty( $tax_obj->rest_base ) ? $tax_obj->rest_base : $tax_obj->name; $this->meta = new WP_REST_Term_Meta_Fields( $taxonomy ); } /** * Registers the routes for the objects of the controller. * * @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 term.' ), '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' ) ), ), ), 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' => __( 'Required to be true, as terms do not support trashing.' ), ), ), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); } /** * Checks if a request has access to read terms in the specified taxonomy. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return bool|WP_Error True if the request has read access, otherwise false or WP_Error object. */ public function get_items_permissions_check( $request ) { $tax_obj = get_taxonomy( $this->taxonomy ); if ( ! $tax_obj || ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) { return false; } if ( 'edit' === $request['context'] && ! current_user_can( $tax_obj->cap->edit_terms ) ) { return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit terms in this taxonomy.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Retrieves terms associated with a taxonomy. * * @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 WP_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( 'exclude' => 'exclude', 'include' => 'include', 'order' => 'order', 'orderby' => 'orderby', 'post' => 'post', 'hide_empty' => 'hide_empty', 'per_page' => 'number', 'search' => 'search', 'slug' => 'slug', ); $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 ]; } } if ( isset( $prepared_args['orderby'] ) && isset( $request['orderby'] ) ) { $orderby_mappings = array( 'include_slugs' => 'slug__in', ); if ( isset( $orderby_mappings[ $request['orderby'] ] ) ) { $prepared_args['orderby'] = $orderby_mappings[ $request['orderby'] ]; } } if ( isset( $registered['offset'] ) && ! empty( $request['offset'] ) ) { $prepared_args['offset'] = $request['offset']; } else { $prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number']; } $taxonomy_obj = get_taxonomy( $this->taxonomy ); if ( $taxonomy_obj->hierarchical && isset( $registered['parent'], $request['parent'] ) ) { if ( 0 === $request['parent'] ) { // Only query top-level terms. $prepared_args['parent'] = 0; } else { if ( $request['parent'] ) { $prepared_args['parent'] = $request['parent']; } } } /** * Filters the query arguments before passing them to get_terms(). * * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug. * * Enables adding extra arguments or setting defaults for a terms * collection request. * * @since 4.7.0 * * @link https://developer.wordpress.org/reference/functions/get_terms/ * * @param array $prepared_args Array of arguments to be * passed to get_terms(). * @param WP_REST_Request $request The current request. */ $prepared_args = apply_filters( "rest_{$this->taxonomy}_query", $prepared_args, $request ); if ( ! empty( $prepared_args['post'] ) ) { $query_result = wp_get_object_terms( $prepared_args['post'], $this->taxonomy, $prepared_args ); // Used when calling wp_count_terms() below. $prepared_args['object_ids'] = $prepared_args['post']; } else { $query_result = get_terms( $this->taxonomy, $prepared_args ); } $count_args = $prepared_args; unset( $count_args['number'], $count_args['offset'] ); $total_terms = wp_count_terms( $this->taxonomy, $count_args ); // wp_count_terms can return a falsy value when the term has no children. if ( ! $total_terms ) { $total_terms = 0; } $response = array(); foreach ( $query_result as $term ) { $data = $this->prepare_item_for_response( $term, $request ); $response[] = $this->prepare_response_for_collection( $data ); } $response = rest_ensure_response( $response ); // Store pagination values for headers. $per_page = (int) $prepared_args['number']; $page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 ); $response->header( 'X-WP-Total', (int) $total_terms ); $max_pages = ceil( $total_terms / $per_page ); $response->header( 'X-WP-TotalPages', (int) $max_pages ); $base = add_query_arg( $request->get_query_params(), rest_url( $this->namespace . '/' . $this->rest_base ) ); if ( $page > 1 ) { $prev_page = $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 > $page ) { $next_page = $page + 1; $next_link = add_query_arg( 'page', $next_page, $base ); $response->link_header( 'next', $next_link ); } return $response; } /** * Get the term, if the ID is valid. * * @since 4.7.2 * * @param int $id Supplied ID. * @return WP_Term|WP_Error Term object if ID is valid, WP_Error otherwise. */ protected function get_term( $id ) { $error = new WP_Error( 'rest_term_invalid', __( 'Term does not exist.' ), array( 'status' => 404 ) ); if ( ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) { return $error; } if ( (int) $id <= 0 ) { return $error; } $term = get_term( (int) $id, $this->taxonomy ); if ( empty( $term ) || $term->taxonomy !== $this->taxonomy ) { return $error; } return $term; } /** * Checks if a request has access to read or edit the specified term. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return bool|WP_Error True if the request has read access for the item, otherwise false or WP_Error object. */ public function get_item_permissions_check( $request ) { $term = $this->get_term( $request['id'] ); if ( is_wp_error( $term ) ) { return $term; } if ( 'edit' === $request['context'] && ! current_user_can( 'edit_term', $term->term_id ) ) { return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit this term.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Gets a single term from a taxonomy. * * @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 WP_Error object on failure. */ public function get_item( $request ) { $term = $this->get_term( $request['id'] ); if ( is_wp_error( $term ) ) { return $term; } $response = $this->prepare_item_for_response( $term, $request ); return rest_ensure_response( $response ); } /** * Checks if a request has access to create a term. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return bool|WP_Error True if the request has access to create items, false or WP_Error object otherwise. */ public function create_item_permissions_check( $request ) { if ( ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) { return false; } $taxonomy_obj = get_taxonomy( $this->taxonomy ); if ( ! current_user_can( $taxonomy_obj->cap->edit_terms ) ) { return new WP_Error( 'rest_cannot_create', __( 'Sorry, you are not allowed to create new terms.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Creates a single term in a taxonomy. * * @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 WP_Error object on failure. */ public function create_item( $request ) { if ( isset( $request['parent'] ) ) { if ( ! is_taxonomy_hierarchical( $this->taxonomy ) ) { return new WP_Error( 'rest_taxonomy_not_hierarchical', __( 'Cannot set parent term, taxonomy is not hierarchical.' ), array( 'status' => 400 ) ); } $parent = get_term( (int) $request['parent'], $this->taxonomy ); if ( ! $parent ) { return new WP_Error( 'rest_term_invalid', __( 'Parent term does not exist.' ), array( 'status' => 400 ) ); } } $prepared_term = $this->prepare_item_for_database( $request ); $term = wp_insert_term( wp_slash( $prepared_term->name ), $this->taxonomy, wp_slash( (array) $prepared_term ) ); if ( is_wp_error( $term ) ) { /* * If we're going to inform the client that the term already exists, * give them the identifier for future use. */ if ( $term_id = $term->get_error_data( 'term_exists' ) ) { $existing_term = get_term( $term_id, $this->taxonomy ); $term->add_data( $existing_term->term_id, 'term_exists' ); $term->add_data( array( 'status' => 409, 'term_id' => $term_id ) ); } return $term; } $term = get_term( $term['term_id'], $this->taxonomy ); /** * Fires after a single term is created or updated via the REST API. * * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug. * * @since 4.7.0 * * @param WP_Term $term Inserted or updated term object. * @param WP_REST_Request $request Request object. * @param bool $creating True when creating a term, false when updating. */ do_action( "rest_insert_{$this->taxonomy}", $term, $request, true ); $schema = $this->get_item_schema(); if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { $meta_update = $this->meta->update_value( $request['meta'], (int) $request['id'] ); if ( is_wp_error( $meta_update ) ) { return $meta_update; } } $fields_update = $this->update_additional_fields_for_object( $term, $request ); if ( is_wp_error( $fields_update ) ) { return $fields_update; } $request->set_param( 'context', 'view' ); $response = $this->prepare_item_for_response( $term, $request ); $response = rest_ensure_response( $response ); $response->set_status( 201 ); $response->header( 'Location', rest_url( $this->namespace . '/' . $this->rest_base . '/' . $term->term_id ) ); return $response; } /** * Checks if a request has access to update the specified term. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return bool|WP_Error True if the request has access to update the item, false or WP_Error object otherwise. */ public function update_item_permissions_check( $request ) { $term = $this->get_term( $request['id'] ); if ( is_wp_error( $term ) ) { return $term; } if ( ! current_user_can( 'edit_term', $term->term_id ) ) { return new WP_Error( 'rest_cannot_update', __( 'Sorry, you are not allowed to edit this term.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Updates a single term from a taxonomy. * * @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 WP_Error object on failure. */ public function update_item( $request ) { $term = $this->get_term( $request['id'] ); if ( is_wp_error( $term ) ) { return $term; } if ( isset( $request['parent'] ) ) { if ( ! is_taxonomy_hierarchical( $this->taxonomy ) ) { return new WP_Error( 'rest_taxonomy_not_hierarchical', __( 'Cannot set parent term, taxonomy is not hierarchical.' ), array( 'status' => 400 ) ); } $parent = get_term( (int) $request['parent'], $this->taxonomy ); if ( ! $parent ) { return new WP_Error( 'rest_term_invalid', __( 'Parent term does not exist.' ), array( 'status' => 400 ) ); } } $prepared_term = $this->prepare_item_for_database( $request ); // Only update the term if we haz something to update. if ( ! empty( $prepared_term ) ) { $update = wp_update_term( $term->term_id, $term->taxonomy, wp_slash( (array) $prepared_term ) ); if ( is_wp_error( $update ) ) { return $update; } } $term = get_term( $term->term_id, $this->taxonomy ); /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php */ do_action( "rest_insert_{$this->taxonomy}", $term, $request, false ); $schema = $this->get_item_schema(); if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { $meta_update = $this->meta->update_value( $request['meta'], $term->term_id ); if ( is_wp_error( $meta_update ) ) { return $meta_update; } } $fields_update = $this->update_additional_fields_for_object( $term, $request ); if ( is_wp_error( $fields_update ) ) { return $fields_update; } $request->set_param( 'context', 'view' ); $response = $this->prepare_item_for_response( $term, $request ); return rest_ensure_response( $response ); } /** * Checks if a request has access to delete the specified term. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return bool|WP_Error True if the request has access to delete the item, otherwise false or WP_Error object. */ public function delete_item_permissions_check( $request ) { $term = $this->get_term( $request['id'] ); if ( is_wp_error( $term ) ) { return $term; } if ( ! current_user_can( 'delete_term', $term->term_id ) ) { return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete this term.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Deletes a single term from a taxonomy. * * @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 WP_Error object on failure. */ public function delete_item( $request ) { $term = $this->get_term( $request['id'] ); if ( is_wp_error( $term ) ) { return $term; } $force = isset( $request['force'] ) ? (bool) $request['force'] : false; // We don't support trashing for terms. if ( ! $force ) { /* translators: %s: force=true */ return new WP_Error( 'rest_trash_not_supported', sprintf( __( "Terms do not support trashing. Set '%s' to delete." ), 'force=true' ), array( 'status' => 501 ) ); } $request->set_param( 'context', 'view' ); $previous = $this->prepare_item_for_response( $term, $request ); $retval = wp_delete_term( $term->term_id, $term->taxonomy ); if ( ! $retval ) { return new WP_Error( 'rest_cannot_delete', __( 'The term cannot be deleted.' ), array( 'status' => 500 ) ); } $response = new WP_REST_Response(); $response->set_data( array( 'deleted' => true, 'previous' => $previous->get_data() ) ); /** * Fires after a single term is deleted via the REST API. * * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug. * * @since 4.7.0 * * @param WP_Term $term The deleted term. * @param WP_REST_Response $response The response data. * @param WP_REST_Request $request The request sent to the API. */ do_action( "rest_delete_{$this->taxonomy}", $term, $response, $request ); return $response; } /** * Prepares a single term for create or update. * * @since 4.7.0 * * @param WP_REST_Request $request Request object. * @return object $prepared_term Term object. */ public function prepare_item_for_database( $request ) { $prepared_term = new stdClass; $schema = $this->get_item_schema(); if ( isset( $request['name'] ) && ! empty( $schema['properties']['name'] ) ) { $prepared_term->name = $request['name']; } if ( isset( $request['slug'] ) && ! empty( $schema['properties']['slug'] ) ) { $prepared_term->slug = $request['slug']; } if ( isset( $request['taxonomy'] ) && ! empty( $schema['properties']['taxonomy'] ) ) { $prepared_term->taxonomy = $request['taxonomy']; } if ( isset( $request['description'] ) && ! empty( $schema['properties']['description'] ) ) { $prepared_term->description = $request['description']; } if ( isset( $request['parent'] ) && ! empty( $schema['properties']['parent'] ) ) { $parent_term_id = 0; $parent_term = get_term( (int) $request['parent'], $this->taxonomy ); if ( $parent_term ) { $parent_term_id = $parent_term->term_id; } $prepared_term->parent = $parent_term_id; } /** * Filters term data before inserting term via the REST API. * * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug. * * @since 4.7.0 * * @param object $prepared_term Term object. * @param WP_REST_Request $request Request object. */ return apply_filters( "rest_pre_insert_{$this->taxonomy}", $prepared_term, $request ); } /** * Prepares a single term output for response. * * @since 4.7.0 * * @param obj $item Term object. * @param WP_REST_Request $request Request object. * @return WP_REST_Response $response Response object. */ public function prepare_item_for_response( $item, $request ) { $schema = $this->get_item_schema(); $data = array(); if ( ! empty( $schema['properties']['id'] ) ) { $data['id'] = (int) $item->term_id; } if ( ! empty( $schema['properties']['count'] ) ) { $data['count'] = (int) $item->count; } if ( ! empty( $schema['properties']['description'] ) ) { $data['description'] = $item->description; } if ( ! empty( $schema['properties']['link'] ) ) { $data['link'] = get_term_link( $item ); } if ( ! empty( $schema['properties']['name'] ) ) { $data['name'] = $item->name; } if ( ! empty( $schema['properties']['slug'] ) ) { $data['slug'] = $item->slug; } if ( ! empty( $schema['properties']['taxonomy'] ) ) { $data['taxonomy'] = $item->taxonomy; } if ( ! empty( $schema['properties']['parent'] ) ) { $data['parent'] = (int) $item->parent; } if ( ! empty( $schema['properties']['meta'] ) ) { $data['meta'] = $this->meta->get_value( $item->term_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 ); $response = rest_ensure_response( $data ); $response->add_links( $this->prepare_links( $item ) ); /** * Filters a term item returned from the API. * * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug. * * Allows modification of the term data right before it is returned. * * @since 4.7.0 * * @param WP_REST_Response $response The response object. * @param object $item The original term object. * @param WP_REST_Request $request Request used to generate the response. */ return apply_filters( "rest_prepare_{$this->taxonomy}", $response, $item, $request ); } /** * Prepares links for the request. * * @since 4.7.0 * * @param object $term Term object. * @return array Links for the given term. */ protected function prepare_links( $term ) { $base = $this->namespace . '/' . $this->rest_base; $links = array( 'self' => array( 'href' => rest_url( trailingslashit( $base ) . $term->term_id ), ), 'collection' => array( 'href' => rest_url( $base ), ), 'about' => array( 'href' => rest_url( sprintf( 'wp/v2/taxonomies/%s', $this->taxonomy ) ), ), ); if ( $term->parent ) { $parent_term = get_term( (int) $term->parent, $term->taxonomy ); if ( $parent_term ) { $links['up'] = array( 'href' => rest_url( trailingslashit( $base ) . $parent_term->term_id ), 'embeddable' => true, ); } } $taxonomy_obj = get_taxonomy( $term->taxonomy ); if ( empty( $taxonomy_obj->object_type ) ) { return $links; } $post_type_links = array(); foreach ( $taxonomy_obj->object_type as $type ) { $post_type_object = get_post_type_object( $type ); if ( empty( $post_type_object->show_in_rest ) ) { continue; } $rest_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name; $post_type_links[] = array( 'href' => add_query_arg( $this->rest_base, $term->term_id, rest_url( sprintf( 'wp/v2/%s', $rest_base ) ) ), ); } if ( ! empty( $post_type_links ) ) { $links['https://api.w.org/post_type'] = $post_type_links; } return $links; } /** * Retrieves the term's schema, conforming to JSON Schema. * * @since 4.7.0 * * @return array Item schema data. */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'post_tag' === $this->taxonomy ? 'tag' : $this->taxonomy, 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Unique identifier for the term.' ), 'type' => 'integer', 'context' => array( 'view', 'embed', 'edit' ), 'readonly' => true, ), 'count' => array( 'description' => __( 'Number of published posts for the term.' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), 'description' => array( 'description' => __( 'HTML description of the term.' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), ), 'link' => array( 'description' => __( 'URL of the term.' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view', 'embed', 'edit' ), 'readonly' => true, ), 'name' => array( 'description' => __( 'HTML title for the term.' ), 'type' => 'string', 'context' => array( 'view', 'embed', 'edit' ), 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), 'required' => true, ), 'slug' => array( 'description' => __( 'An alphanumeric identifier for the term unique to its type.' ), 'type' => 'string', 'context' => array( 'view', 'embed', 'edit' ), 'arg_options' => array( 'sanitize_callback' => array( $this, 'sanitize_slug' ), ), ), 'taxonomy' => array( 'description' => __( 'Type attribution for the term.' ), 'type' => 'string', 'enum' => array_keys( get_taxonomies() ), 'context' => array( 'view', 'embed', 'edit' ), 'readonly' => true, ), ), ); $taxonomy = get_taxonomy( $this->taxonomy ); if ( $taxonomy->hierarchical ) { $schema['properties']['parent'] = array( 'description' => __( 'The parent term ID.' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), ); } $schema['properties']['meta'] = $this->meta->get_field_schema(); return $this->add_additional_fields_schema( $schema ); } /** * Retrieves the query params for collections. * * @since 4.7.0 * * @return array Collection parameters. */ public function get_collection_params() { $query_params = parent::get_collection_params(); $taxonomy = get_taxonomy( $this->taxonomy ); $query_params['context']['default'] = 'view'; $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(), ); if ( ! $taxonomy->hierarchical ) { $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' => 'asc', 'enum' => array( 'asc', 'desc', ), ); $query_params['orderby'] = array( 'description' => __( 'Sort collection by term attribute.' ), 'type' => 'string', 'default' => 'name', 'enum' => array( 'id', 'include', 'name', 'slug', 'include_slugs', 'term_group', 'description', 'count', ), ); $query_params['hide_empty'] = array( 'description' => __( 'Whether to hide terms not assigned to any posts.' ), 'type' => 'boolean', 'default' => false, ); if ( $taxonomy->hierarchical ) { $query_params['parent'] = array( 'description' => __( 'Limit result set to terms assigned to a specific parent.' ), 'type' => 'integer', ); } $query_params['post'] = array( 'description' => __( 'Limit result set to terms assigned to a specific post.' ), 'type' => 'integer', 'default' => null, ); $query_params['slug'] = array( 'description' => __( 'Limit result set to terms with one or more specific slugs.' ), 'type' => 'array', 'items' => array( 'type' => 'string' ), ); /** * Filter collection parameters for the terms controller. * * The dynamic part of the filter `$this->taxonomy` refers to the taxonomy * slug for the controller. * * This filter registers the collection parameter, but does not map the * collection parameter to an internal WP_Term_Query parameter. Use the * `rest_{$this->taxonomy}_query` filter to set WP_Term_Query parameters. * * @since 4.7.0 * * @param array $query_params JSON Schema-formatted collection parameters. * @param WP_Taxonomy $taxonomy Taxonomy object. */ return apply_filters( "rest_{$this->taxonomy}_collection_params", $query_params, $taxonomy ); } /** * Checks that the taxonomy is valid. * * @since 4.7.0 * * @param string $taxonomy Taxonomy to check. * @return bool Whether the taxonomy is allowed for REST management. */ protected function check_is_taxonomy_allowed( $taxonomy ) { $taxonomy_obj = get_taxonomy( $taxonomy ); if ( $taxonomy_obj && ! empty( $taxonomy_obj->show_in_rest ) ) { return true; } return false; } } /** * REST API: WP_REST_Users_Controller class * * @package WordPress * @subpackage REST_API * @since 4.7.0 */ /** * Core class used to manage users via the REST API. * * @since 4.7.0 * * @see WP_REST_Controller */ class WP_REST_Users_Controller extends WP_REST_Controller { /** * Instance of a user meta fields object. * * @since 4.7.0 * @var WP_REST_User_Meta_Fields */ protected $meta; /** * Constructor. * * @since 4.7.0 */ public function __construct() { $this->namespace = 'wp/v2'; $this->rest_base = 'users'; $this->meta = new WP_REST_User_Meta_Fields(); } /** * Registers the routes for the objects of the controller. * * @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 user.' ), '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' ) ), ), ), 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' => __( 'Required to be true, as users do not support trashing.' ), ), 'reassign' => array( 'type' => 'integer', 'description' => __( 'Reassign the deleted user\'s posts and links to this user ID.' ), 'required' => true, 'sanitize_callback' => array( $this, 'check_reassign' ), ), ), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/me', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_current_item' ), 'args' => array( 'context' => $this->get_context_param( array( 'default' => 'view' ) ), ), ), array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( $this, 'update_current_item' ), 'permission_callback' => array( $this, 'update_current_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_current_item' ), 'permission_callback' => array( $this, 'delete_current_item_permissions_check' ), 'args' => array( 'force' => array( 'type' => 'boolean', 'default' => false, 'description' => __( 'Required to be true, as users do not support trashing.' ), ), 'reassign' => array( 'type' => 'integer', 'description' => __( 'Reassign the deleted user\'s posts and links to this user ID.' ), 'required' => true, 'sanitize_callback' => array( $this, 'check_reassign' ), ), ), ), 'schema' => array( $this, 'get_public_item_schema' ), )); } /** * Checks for a valid value for the reassign parameter when deleting users. * * The value can be an integer, 'false', false, or ''. * * @since 4.7.0 * * @param int|bool $value The value passed to the reassign parameter. * @param WP_REST_Request $request Full details about the request. * @param string $param The parameter that is being sanitized. * * @return int|bool|WP_Error */ public function check_reassign( $value, $request, $param ) { if ( is_numeric( $value ) ) { return $value; } if ( empty( $value ) || false === $value || 'false' === $value ) { return false; } return new WP_Error( 'rest_invalid_param', __( 'Invalid user parameter(s).' ), array( 'status' => 400 ) ); } /** * Permissions check for getting all users. * * @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, otherwise WP_Error object. */ public function get_items_permissions_check( $request ) { // Check if roles is specified in GET request and if user can list users. if ( ! empty( $request['roles'] ) && ! current_user_can( 'list_users' ) ) { return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you are not allowed to filter users by role.' ), array( 'status' => rest_authorization_required_code() ) ); } if ( 'edit' === $request['context'] && ! current_user_can( 'list_users' ) ) { return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to list users.' ), array( 'status' => rest_authorization_required_code() ) ); } if ( in_array( $request['orderby'], array( 'email', 'registered_date' ), true ) && ! current_user_can( 'list_users' ) ) { return new WP_Error( 'rest_forbidden_orderby', __( 'Sorry, you are not allowed to order users by this parameter.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Retrieves all users. * * @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 WP_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( 'exclude' => 'exclude', 'include' => 'include', 'order' => 'order', 'per_page' => 'number', 'search' => 'search', 'roles' => 'role__in', 'slug' => 'nicename__in', ); $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 ]; } } if ( isset( $registered['offset'] ) && ! empty( $request['offset'] ) ) { $prepared_args['offset'] = $request['offset']; } else { $prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number']; } if ( isset( $registered['orderby'] ) ) { $orderby_possibles = array( 'id' => 'ID', 'include' => 'include', 'name' => 'display_name', 'registered_date' => 'registered', 'slug' => 'user_nicename', 'include_slugs' => 'nicename__in', 'email' => 'user_email', 'url' => 'user_url', ); $prepared_args['orderby'] = $orderby_possibles[ $request['orderby'] ]; } if ( ! current_user_can( 'list_users' ) ) { $prepared_args['has_published_posts'] = get_post_types( array( 'show_in_rest' => true ), 'names' ); } if ( ! empty( $prepared_args['search'] ) ) { $prepared_args['search'] = '*' . $prepared_args['search'] . '*'; } /** * Filters WP_User_Query arguments when querying users via the REST API. * * @link https://developer.wordpress.org/reference/classes/wp_user_query/ * * @since 4.7.0 * * @param array $prepared_args Array of arguments for WP_User_Query. * @param WP_REST_Request $request The current request. */ $prepared_args = apply_filters( 'rest_user_query', $prepared_args, $request ); $query = new WP_User_Query( $prepared_args ); $users = array(); foreach ( $query->results as $user ) { $data = $this->prepare_item_for_response( $user, $request ); $users[] = $this->prepare_response_for_collection( $data ); } $response = rest_ensure_response( $users ); // Store pagination values for headers then unset for count query. $per_page = (int) $prepared_args['number']; $page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 ); $prepared_args['fields'] = 'ID'; $total_users = $query->get_total(); if ( $total_users < 1 ) { // Out-of-bounds, run the query again without LIMIT for total count. unset( $prepared_args['number'], $prepared_args['offset'] ); $count_query = new WP_User_Query( $prepared_args ); $total_users = $count_query->get_total(); } $response->header( 'X-WP-Total', (int) $total_users ); $max_pages = ceil( $total_users / $per_page ); $response->header( 'X-WP-TotalPages', (int) $max_pages ); $base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) ); if ( $page > 1 ) { $prev_page = $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 > $page ) { $next_page = $page + 1; $next_link = add_query_arg( 'page', $next_page, $base ); $response->link_header( 'next', $next_link ); } return $response; } /** * Get the user, if the ID is valid. * * @since 4.7.2 * * @param int $id Supplied ID. * @return WP_User|WP_Error True if ID is valid, WP_Error otherwise. */ protected function get_user( $id ) { $error = new WP_Error( 'rest_user_invalid_id', __( 'Invalid user ID.' ), array( 'status' => 404 ) ); if ( (int) $id <= 0 ) { return $error; } $user = get_userdata( (int) $id ); if ( empty( $user ) || ! $user->exists() ) { return $error; } if ( is_multisite() && ! is_user_member_of_blog( $user->ID ) ) { return $error; } return $user; } /** * Checks if a given request has access to read a user. * * @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, otherwise WP_Error object. */ public function get_item_permissions_check( $request ) { $user = $this->get_user( $request['id'] ); if ( is_wp_error( $user ) ) { return $user; } $types = get_post_types( array( 'show_in_rest' => true ), 'names' ); if ( get_current_user_id() === $user->ID ) { return true; } if ( 'edit' === $request['context'] && ! current_user_can( 'list_users' ) ) { return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you are not allowed to list users.' ), array( 'status' => rest_authorization_required_code() ) ); } elseif ( ! count_user_posts( $user->ID, $types ) && ! current_user_can( 'edit_user', $user->ID ) && ! current_user_can( 'list_users' ) ) { return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you are not allowed to list users.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Retrieves a single user. * * @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 WP_Error object on failure. */ public function get_item( $request ) { $user = $this->get_user( $request['id'] ); if ( is_wp_error( $user ) ) { return $user; } $user = $this->prepare_item_for_response( $user, $request ); $response = rest_ensure_response( $user ); return $response; } /** * Retrieves the current user. * * @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 WP_Error object on failure. */ public function get_current_item( $request ) { $current_user_id = get_current_user_id(); if ( empty( $current_user_id ) ) { return new WP_Error( 'rest_not_logged_in', __( 'You are not currently logged in.' ), array( 'status' => 401 ) ); } $user = wp_get_current_user(); $response = $this->prepare_item_for_response( $user, $request ); $response = rest_ensure_response( $response ); return $response; } /** * Checks if a given request has access create users. * * @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, WP_Error object otherwise. */ public function create_item_permissions_check( $request ) { if ( ! current_user_can( 'create_users' ) ) { return new WP_Error( 'rest_cannot_create_user', __( 'Sorry, you are not allowed to create new users.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Creates a single user. * * @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 WP_Error object on failure. */ public function create_item( $request ) { if ( ! empty( $request['id'] ) ) { return new WP_Error( 'rest_user_exists', __( 'Cannot create existing user.' ), array( 'status' => 400 ) ); } $schema = $this->get_item_schema(); if ( ! empty( $request['roles'] ) && ! empty( $schema['properties']['roles'] ) ) { $check_permission = $this->check_role_update( $request['id'], $request['roles'] ); if ( is_wp_error( $check_permission ) ) { return $check_permission; } } $user = $this->prepare_item_for_database( $request ); if ( is_multisite() ) { $ret = wpmu_validate_user_signup( $user->user_login, $user->user_email ); if ( is_wp_error( $ret['errors'] ) && ! empty( $ret['errors']->errors ) ) { $error = new WP_Error( 'rest_invalid_param', __( 'Invalid user parameter(s).' ), array( 'status' => 400 ) ); foreach ( $ret['errors']->errors as $code => $messages ) { foreach ( $messages as $message ) { $error->add( $code, $message ); } if ( $error_data = $error->get_error_data( $code ) ) { $error->add_data( $error_data, $code ); } } return $error; } } if ( is_multisite() ) { $user_id = wpmu_create_user( $user->user_login, $user->user_pass, $user->user_email ); if ( ! $user_id ) { return new WP_Error( 'rest_user_create', __( 'Error creating new user.' ), array( 'status' => 500 ) ); } $user->ID = $user_id; $user_id = wp_update_user( wp_slash( (array) $user ) ); if ( is_wp_error( $user_id ) ) { return $user_id; } $result= add_user_to_blog( get_site()->id, $user_id, '' ); if ( is_wp_error( $result ) ) { return $result; } } else { $user_id = wp_insert_user( wp_slash( (array) $user ) ); if ( is_wp_error( $user_id ) ) { return $user_id; } } $user = get_user_by( 'id', $user_id ); /** * Fires immediately after a user is created or updated via the REST API. * * @since 4.7.0 * * @param WP_User $user Inserted or updated user object. * @param WP_REST_Request $request Request object. * @param bool $creating True when creating a user, false when updating. */ do_action( 'rest_insert_user', $user, $request, true ); if ( ! empty( $request['roles'] ) && ! empty( $schema['properties']['roles'] ) ) { array_map( array( $user, 'add_role' ), $request['roles'] ); } if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { $meta_update = $this->meta->update_value( $request['meta'], $user_id ); if ( is_wp_error( $meta_update ) ) { return $meta_update; } } $user = get_user_by( 'id', $user_id ); $fields_update = $this->update_additional_fields_for_object( $user, $request ); if ( is_wp_error( $fields_update ) ) { return $fields_update; } $request->set_param( 'context', 'edit' ); $response = $this->prepare_item_for_response( $user, $request ); $response = rest_ensure_response( $response ); $response->set_status( 201 ); $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $user_id ) ) ); return $response; } /** * Checks if a given request has access to update a user. * * @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, WP_Error object otherwise. */ public function update_item_permissions_check( $request ) { $user = $this->get_user( $request['id'] ); if ( is_wp_error( $user ) ) { return $user; } if ( ! empty( $request['roles'] ) ) { if ( ! current_user_can( 'promote_user', $user->ID ) ) { return new WP_Error( 'rest_cannot_edit_roles', __( 'Sorry, you are not allowed to edit roles of this user.' ), array( 'status' => rest_authorization_required_code() ) ); } $request_params = array_keys( $request->get_params() ); sort( $request_params ); // If only 'id' and 'roles' are specified (we are only trying to // edit roles), then only the 'promote_user' cap is required. if ( $request_params === array( 'id', 'roles' ) ) { return true; } } if ( ! current_user_can( 'edit_user', $user->ID ) ) { return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit this user.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Updates a single user. * * @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 WP_Error object on failure. */ public function update_item( $request ) { $user = $this->get_user( $request['id'] ); if ( is_wp_error( $user ) ) { return $user; } $id = $user->ID; if ( ! $user ) { return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user ID.' ), array( 'status' => 404 ) ); } if ( email_exists( $request['email'] ) && $request['email'] !== $user->user_email ) { return new WP_Error( 'rest_user_invalid_email', __( 'Invalid email address.' ), array( 'status' => 400 ) ); } if ( ! empty( $request['username'] ) && $request['username'] !== $user->user_login ) { return new WP_Error( 'rest_user_invalid_argument', __( "Username isn't editable." ), array( 'status' => 400 ) ); } if ( ! empty( $request['slug'] ) && $request['slug'] !== $user->user_nicename && get_user_by( 'slug', $request['slug'] ) ) { return new WP_Error( 'rest_user_invalid_slug', __( 'Invalid slug.' ), array( 'status' => 400 ) ); } if ( ! empty( $request['roles'] ) ) { $check_permission = $this->check_role_update( $id, $request['roles'] ); if ( is_wp_error( $check_permission ) ) { return $check_permission; } } $user = $this->prepare_item_for_database( $request ); // Ensure we're operating on the same user we already checked. $user->ID = $id; $user_id = wp_update_user( wp_slash( (array) $user ) ); if ( is_wp_error( $user_id ) ) { return $user_id; } $user = get_user_by( 'id', $user_id ); /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php */ do_action( 'rest_insert_user', $user, $request, false ); if ( ! empty( $request['roles'] ) ) { array_map( array( $user, 'add_role' ), $request['roles'] ); } $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; } } $user = get_user_by( 'id', $user_id ); $fields_update = $this->update_additional_fields_for_object( $user, $request ); if ( is_wp_error( $fields_update ) ) { return $fields_update; } $request->set_param( 'context', 'edit' ); $response = $this->prepare_item_for_response( $user, $request ); $response = rest_ensure_response( $response ); return $response; } /** * Checks if a given request has access to update the current user. * * @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, WP_Error object otherwise. */ public function update_current_item_permissions_check( $request ) { $request['id'] = get_current_user_id(); return $this->update_item_permissions_check( $request ); } /** * Updates the current user. * * @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 WP_Error object on failure. */ function update_current_item( $request ) { $request['id'] = get_current_user_id(); return $this->update_item( $request ); } /** * Checks if a given request has access delete a user. * * @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, WP_Error object otherwise. */ public function delete_item_permissions_check( $request ) { $user = $this->get_user( $request['id'] ); if ( is_wp_error( $user ) ) { return $user; } if ( ! current_user_can( 'delete_user', $user->ID ) ) { return new WP_Error( 'rest_user_cannot_delete', __( 'Sorry, you are not allowed to delete this user.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Deletes a single user. * * @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 WP_Error object on failure. */ public function delete_item( $request ) { // We don't support delete requests in multisite. if ( is_multisite() ) { return new WP_Error( 'rest_cannot_delete', __( 'The user cannot be deleted.' ), array( 'status' => 501 ) ); } $user = $this->get_user( $request['id'] ); if ( is_wp_error( $user ) ) { return $user; } $id = $user->ID; $reassign = false === $request['reassign'] ? null : absint( $request['reassign'] ); $force = isset( $request['force'] ) ? (bool) $request['force'] : false; // We don't support trashing for users. if ( ! $force ) { /* translators: %s: force=true */ return new WP_Error( 'rest_trash_not_supported', sprintf( __( "Users do not support trashing. Set '%s' to delete." ), 'force=true' ), array( 'status' => 501 ) ); } if ( ! empty( $reassign ) ) { if ( $reassign === $id || ! get_userdata( $reassign ) ) { return new WP_Error( 'rest_user_invalid_reassign', __( 'Invalid user ID for reassignment.' ), array( 'status' => 400 ) ); } } $request->set_param( 'context', 'edit' ); $previous = $this->prepare_item_for_response( $user, $request ); /** Include admin user functions to get access to wp_delete_user() */ require_once ABSPATH . 'wp-admin/includes/user.php'; $result = wp_delete_user( $id, $reassign ); if ( ! $result ) { return new WP_Error( 'rest_cannot_delete', __( 'The user cannot be deleted.' ), array( 'status' => 500 ) ); } $response = new WP_REST_Response(); $response->set_data( array( 'deleted' => true, 'previous' => $previous->get_data() ) ); /** * Fires immediately after a user is deleted via the REST API. * * @since 4.7.0 * * @param WP_User $user The user 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_user', $user, $response, $request ); return $response; } /** * Checks if a given request has access to delete the current user. * * @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, WP_Error object otherwise. */ public function delete_current_item_permissions_check( $request ) { $request['id'] = get_current_user_id(); return $this->delete_item_permissions_check( $request ); } /** * Deletes the current user. * * @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 WP_Error object on failure. */ function delete_current_item( $request ) { $request['id'] = get_current_user_id(); return $this->delete_item( $request ); } /** * Prepares a single user output for response. * * @since 4.7.0 * * @param WP_User $user User object. * @param WP_REST_Request $request Request object. * @return WP_REST_Response Response object. */ public function prepare_item_for_response( $user, $request ) { $data = array(); $schema = $this->get_item_schema(); if ( ! empty( $schema['properties']['id'] ) ) { $data['id'] = $user->ID; } if ( ! empty( $schema['properties']['username'] ) ) { $data['username'] = $user->user_login; } if ( ! empty( $schema['properties']['name'] ) ) { $data['name'] = $user->display_name; } if ( ! empty( $schema['properties']['first_name'] ) ) { $data['first_name'] = $user->first_name; } if ( ! empty( $schema['properties']['last_name'] ) ) { $data['last_name'] = $user->last_name; } if ( ! empty( $schema['properties']['email'] ) ) { $data['email'] = $user->user_email; } if ( ! empty( $schema['properties']['url'] ) ) { $data['url'] = $user->user_url; } if ( ! empty( $schema['properties']['description'] ) ) { $data['description'] = $user->description; } if ( ! empty( $schema['properties']['link'] ) ) { $data['link'] = get_author_posts_url( $user->ID, $user->user_nicename ); } if ( ! empty( $schema['properties']['locale'] ) ) { $data['locale'] = get_user_locale( $user ); } if ( ! empty( $schema['properties']['nickname'] ) ) { $data['nickname'] = $user->nickname; } if ( ! empty( $schema['properties']['slug'] ) ) { $data['slug'] = $user->user_nicename; } if ( ! empty( $schema['properties']['roles'] ) ) { // Defensively call array_values() to ensure an array is returned. $data['roles'] = array_values( $user->roles ); } if ( ! empty( $schema['properties']['registered_date'] ) ) { $data['registered_date'] = date( 'c', strtotime( $user->user_registered ) ); } if ( ! empty( $schema['properties']['capabilities'] ) ) { $data['capabilities'] = (object) $user->allcaps; } if ( ! empty( $schema['properties']['extra_capabilities'] ) ) { $data['extra_capabilities'] = (object) $user->caps; } if ( ! empty( $schema['properties']['avatar_urls'] ) ) { $data['avatar_urls'] = rest_get_avatar_urls( $user->user_email ); } if ( ! empty( $schema['properties']['meta'] ) ) { $data['meta'] = $this->meta->get_value( $user->ID, $request ); } $context = ! empty( $request['context'] ) ? $request['context'] : 'embed'; $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 ); $response->add_links( $this->prepare_links( $user ) ); /** * Filters user data returned from the REST API. * * @since 4.7.0 * * @param WP_REST_Response $response The response object. * @param object $user User object used to create response. * @param WP_REST_Request $request Request object. */ return apply_filters( 'rest_prepare_user', $response, $user, $request ); } /** * Prepares links for the user request. * * @since 4.7.0 * * @param WP_Post $user User object. * @return array Links for the given user. */ protected function prepare_links( $user ) { $links = array( 'self' => array( 'href' => rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $user->ID ) ), ), 'collection' => array( 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ), ), ); return $links; } /** * Prepares a single user for creation or update. * * @since 4.7.0 * * @param WP_REST_Request $request Request object. * @return object $prepared_user User object. */ protected function prepare_item_for_database( $request ) { $prepared_user = new stdClass; $schema = $this->get_item_schema(); // required arguments. if ( isset( $request['email'] ) && ! empty( $schema['properties']['email'] ) ) { $prepared_user->user_email = $request['email']; } if ( isset( $request['username'] ) && ! empty( $schema['properties']['username'] ) ) { $prepared_user->user_login = $request['username']; } if ( isset( $request['password'] ) && ! empty( $schema['properties']['password'] ) ) { $prepared_user->user_pass = $request['password']; } // optional arguments. if ( isset( $request['id'] ) ) { $prepared_user->ID = absint( $request['id'] ); } if ( isset( $request['name'] ) && ! empty( $schema['properties']['name'] ) ) { $prepared_user->display_name = $request['name']; } if ( isset( $request['first_name'] ) && ! empty( $schema['properties']['first_name'] ) ) { $prepared_user->first_name = $request['first_name']; } if ( isset( $request['last_name'] ) && ! empty( $schema['properties']['last_name'] ) ) { $prepared_user->last_name = $request['last_name']; } if ( isset( $request['nickname'] ) && ! empty( $schema['properties']['nickname'] ) ) { $prepared_user->nickname = $request['nickname']; } if ( isset( $request['slug'] ) && ! empty( $schema['properties']['slug'] ) ) { $prepared_user->user_nicename = $request['slug']; } if ( isset( $request['description'] ) && ! empty( $schema['properties']['description'] ) ) { $prepared_user->description = $request['description']; } if ( isset( $request['url'] ) && ! empty( $schema['properties']['url'] ) ) { $prepared_user->user_url = $request['url']; } if ( isset( $request['locale'] ) && ! empty( $schema['properties']['locale'] ) ) { $prepared_user->locale = $request['locale']; } // setting roles will be handled outside of this function. if ( isset( $request['roles'] ) ) { $prepared_user->role = false; } /** * Filters user data before insertion via the REST API. * * @since 4.7.0 * * @param object $prepared_user User object. * @param WP_REST_Request $request Request object. */ return apply_filters( 'rest_pre_insert_user', $prepared_user, $request ); } /** * Determines if the current user is allowed to make the desired roles change. * * @since 4.7.0 * * @param integer $user_id User ID. * @param array $roles New user roles. * @return true|WP_Error True if the current user is allowed to make the role change, * otherwise a WP_Error object. */ protected function check_role_update( $user_id, $roles ) { global $wp_roles; foreach ( $roles as $role ) { if ( ! isset( $wp_roles->role_objects[ $role ] ) ) { /* translators: %s: role key */ return new WP_Error( 'rest_user_invalid_role', sprintf( __( 'The role %s does not exist.' ), $role ), array( 'status' => 400 ) ); } $potential_role = $wp_roles->role_objects[ $role ]; /* * Don't let anyone with 'edit_users' (admins) edit their own role to something without it. * Multisite super admins can freely edit their blog roles -- they possess all caps. */ if ( ! ( is_multisite() && current_user_can( 'manage_sites' ) ) && get_current_user_id() === $user_id && ! $potential_role->has_cap( 'edit_users' ) ) { return new WP_Error( 'rest_user_invalid_role', __( 'Sorry, you are not allowed to give users that role.' ), array( 'status' => rest_authorization_required_code() ) ); } /** Include admin functions to get access to get_editable_roles() */ require_once ABSPATH . 'wp-admin/includes/admin.php'; // The new role must be editable by the logged-in user. $editable_roles = get_editable_roles(); if ( empty( $editable_roles[ $role ] ) ) { return new WP_Error( 'rest_user_invalid_role', __( 'Sorry, you are not allowed to give users that role.' ), array( 'status' => 403 ) ); } } return true; } /** * Check a username for the REST API. * * Performs a couple of checks like edit_user() in wp-admin/includes/user.php. * * @since 4.7.0 * * @param mixed $value The username submitted in the request. * @param WP_REST_Request $request Full details about the request. * @param string $param The parameter name. * @return WP_Error|string The sanitized username, if valid, otherwise an error. */ public function check_username( $value, $request, $param ) { $username = (string) $value; if ( ! validate_username( $username ) ) { return new WP_Error( 'rest_user_invalid_username', __( 'Username contains invalid characters.' ), array( 'status' => 400 ) ); } /** This filter is documented in wp-includes/user.php */ $illegal_logins = (array) apply_filters( 'illegal_user_logins', array() ); if ( in_array( strtolower( $username ), array_map( 'strtolower', $illegal_logins ) ) ) { return new WP_Error( 'rest_user_invalid_username', __( 'Sorry, that username is not allowed.' ), array( 'status' => 400 ) ); } return $username; } /** * Check a user password for the REST API. * * Performs a couple of checks like edit_user() in wp-admin/includes/user.php. * * @since 4.7.0 * * @param mixed $value The password submitted in the request. * @param WP_REST_Request $request Full details about the request. * @param string $param The parameter name. * @return WP_Error|string The sanitized password, if valid, otherwise an error. */ public function check_user_password( $value, $request, $param ) { $password = (string) $value; if ( empty( $password ) ) { return new WP_Error( 'rest_user_invalid_password', __( 'Passwords cannot be empty.' ), array( 'status' => 400 ) ); } if ( false !== strpos( $password, "\\" ) ) { return new WP_Error( 'rest_user_invalid_password', __( 'Passwords cannot contain the "\\" character.' ), array( 'status' => 400 ) ); } return $password; } /** * Retrieves the user's schema, conforming to JSON Schema. * * @since 4.7.0 * * @return array Item schema data. */ public function get_item_schema() { $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'user', 'type' => 'object', 'properties' => array( 'id' => array( 'description' => __( 'Unique identifier for the user.' ), 'type' => 'integer', 'context' => array( 'embed', 'view', 'edit' ), 'readonly' => true, ), 'username' => array( 'description' => __( 'Login name for the user.' ), 'type' => 'string', 'context' => array( 'edit' ), 'required' => true, 'arg_options' => array( 'sanitize_callback' => array( $this, 'check_username' ), ), ), 'name' => array( 'description' => __( 'Display name for the user.' ), 'type' => 'string', 'context' => array( 'embed', 'view', 'edit' ), 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), ), 'first_name' => array( 'description' => __( 'First name for the user.' ), 'type' => 'string', 'context' => array( 'edit' ), 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), ), 'last_name' => array( 'description' => __( 'Last name for the user.' ), 'type' => 'string', 'context' => array( 'edit' ), 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), ), 'email' => array( 'description' => __( 'The email address for the user.' ), 'type' => 'string', 'format' => 'email', 'context' => array( 'edit' ), 'required' => true, ), 'url' => array( 'description' => __( 'URL of the user.' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'embed', 'view', 'edit' ), ), 'description' => array( 'description' => __( 'Description of the user.' ), 'type' => 'string', 'context' => array( 'embed', 'view', 'edit' ), ), 'link' => array( 'description' => __( 'Author URL of the user.' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'embed', 'view', 'edit' ), 'readonly' => true, ), 'locale' => array( 'description' => __( 'Locale for the user.' ), 'type' => 'string', 'enum' => array_merge( array( '', 'en_US' ), get_available_languages() ), 'context' => array( 'edit' ), ), 'nickname' => array( 'description' => __( 'The nickname for the user.' ), 'type' => 'string', 'context' => array( 'edit' ), 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', ), ), 'slug' => array( 'description' => __( 'An alphanumeric identifier for the user.' ), 'type' => 'string', 'context' => array( 'embed', 'view', 'edit' ), 'arg_options' => array( 'sanitize_callback' => array( $this, 'sanitize_slug' ), ), ), 'registered_date' => array( 'description' => __( 'Registration date for the user.' ), 'type' => 'string', 'format' => 'date-time', 'context' => array( 'edit' ), 'readonly' => true, ), 'roles' => array( 'description' => __( 'Roles assigned to the user.' ), 'type' => 'array', 'items' => array( 'type' => 'string', ), 'context' => array( 'edit' ), ), 'password' => array( 'description' => __( 'Password for the user (never included).' ), 'type' => 'string', 'context' => array(), // Password is never displayed. 'required' => true, 'arg_options' => array( 'sanitize_callback' => array( $this, 'check_user_password' ), ), ), 'capabilities' => array( 'description' => __( 'All capabilities assigned to the user.' ), 'type' => 'object', 'context' => array( 'edit' ), 'readonly' => true, ), 'extra_capabilities' => array( 'description' => __( 'Any extra capabilities assigned to the user.' ), 'type' => 'object', 'context' => array( 'edit' ), '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']['avatar_urls'] = array( 'description' => __( 'Avatar URLs for the user.' ), 'type' => 'object', 'context' => array( 'embed', 'view', 'edit' ), 'readonly' => true, 'properties' => $avatar_properties, ); } $schema['properties']['meta'] = $this->meta->get_field_schema(); return $this->add_additional_fields_schema( $schema ); } /** * Retrieves the query params for collections. * * @since 4.7.0 * * @return array Collection parameters. */ public function get_collection_params() { $query_params = parent::get_collection_params(); $query_params['context']['default'] = 'view'; $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( 'default' => 'asc', 'description' => __( 'Order sort attribute ascending or descending.' ), 'enum' => array( 'asc', 'desc' ), 'type' => 'string', ); $query_params['orderby'] = array( 'default' => 'name', 'description' => __( 'Sort collection by object attribute.' ), 'enum' => array( 'id', 'include', 'name', 'registered_date', 'slug', 'include_slugs', 'email', 'url', ), 'type' => 'string', ); $query_params['slug'] = array( 'description' => __( 'Limit result set to users with one or more specific slugs.' ), 'type' => 'array', 'items' => array( 'type' => 'string', ), ); $query_params['roles'] = array( 'description' => __( 'Limit result set to users matching at least one specific role provided. Accepts csv list or single role.' ), 'type' => 'array', 'items' => array( 'type' => 'string', ), ); /** * Filter collection parameters for the users controller. * * This filter registers the collection parameter, but does not map the * collection parameter to an internal WP_User_Query parameter. Use the * `rest_user_query` filter to set WP_User_Query arguments. * * @since 4.7.0 * * @param array $query_params JSON Schema-formatted collection parameters. */ return apply_filters( 'rest_user_collection_params', $query_params ); } } /** * REST API: WP_REST_Settings_Controller class * * @package WordPress * @subpackage REST_API * @since 4.7.0 */ /** * Core class used to manage a site's settings via the REST API. * * @since 4.7.0 * * @see WP_REST_Controller */ class WP_REST_Settings_Controller extends WP_REST_Controller { /** * Constructor. * * @since 4.7.0 */ public function __construct() { $this->namespace = 'wp/v2'; $this->rest_base = 'settings'; } /** * Registers the routes for the objects of the controller. * * @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_item' ), 'args' => array(), 'permission_callback' => array( $this, 'get_item_permissions_check' ), ), array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( $this, 'update_item' ), 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), 'permission_callback' => array( $this, 'get_item_permissions_check' ), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); } /** * Checks if a given request has access to read and manage settings. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return bool True if the request has read access for the item, otherwise false. */ public function get_item_permissions_check( $request ) { return current_user_can( 'manage_options' ); } /** * Retrieves the settings. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return array|WP_Error Array on success, or WP_Error object on failure. */ public function get_item( $request ) { $options = $this->get_registered_options(); $response = array(); foreach ( $options as $name => $args ) { /** * Filters the value of a setting recognized by the REST API. * * Allow hijacking the setting value and overriding the built-in behavior by returning a * non-null value. The returned value will be presented as the setting value instead. * * @since 4.7.0 * * @param mixed $result Value to use for the requested setting. Can be a scalar * matching the registered schema for the setting, or null to * follow the default get_option() behavior. * @param string $name Setting name (as shown in REST API responses). * @param array $args Arguments passed to register_setting() for this setting. */ $response[ $name ] = apply_filters( 'rest_pre_get_setting', null, $name, $args ); if ( is_null( $response[ $name ] ) ) { // Default to a null value as "null" in the response means "not set". $response[ $name ] = get_option( $args['option_name'], $args['schema']['default'] ); } /* * Because get_option() is lossy, we have to * cast values to the type they are registered with. */ $response[ $name ] = $this->prepare_value( $response[ $name ], $args['schema'] ); } return $response; } /** * Prepares a value for output based off a schema array. * * @since 4.7.0 * * @param mixed $value Value to prepare. * @param array $schema Schema to match. * @return mixed The prepared value. */ protected function prepare_value( $value, $schema ) { // If the value is not valid by the schema, set the value to null. Null // values are specifcally non-destructive so this will not cause overwriting // the current invalid value to null. if ( is_wp_error( rest_validate_value_from_schema( $value, $schema ) ) ) { return null; } return rest_sanitize_value_from_schema( $value, $schema ); } /** * Updates settings for the settings object. * * @since 4.7.0 * * @param WP_REST_Request $request Full details about the request. * @return array|WP_Error Array on success, or error object on failure. */ public function update_item( $request ) { $options = $this->get_registered_options(); $params = $request->get_params(); foreach ( $options as $name => $args ) { if ( ! array_key_exists( $name, $params ) ) { continue; } /** * Filters whether to preempt a setting value update. * * Allows hijacking the setting update logic and overriding the built-in behavior by * returning true. * * @since 4.7.0 * * @param bool $result Whether to override the default behavior for updating the * value of a setting. * @param string $name Setting name (as shown in REST API responses). * @param mixed $value Updated setting value. * @param array $args Arguments passed to register_setting() for this setting. */ $updated = apply_filters( 'rest_pre_update_setting', false, $name, $request[ $name ], $args ); if ( $updated ) { continue; } /* * A null value for an option would have the same effect as * deleting the option from the database, and relying on the * default value. */ if ( is_null( $request[ $name ] ) ) { /* * A null value is returned in the response for any option * that has a non-scalar value. * * To protect clients from accidentally including the null * values from a response object in a request, we do not allow * options with values that don't pass validation to be updated to null. * Without this added protection a client could mistakenly * delete all options that have invalid values from the * database. */ if ( is_wp_error( rest_validate_value_from_schema( get_option( $args['option_name'], false ), $args['schema'] ) ) ) { return new WP_Error( 'rest_invalid_stored_value', sprintf( __( 'The %s property has an invalid stored value, and cannot be updated to null.' ), $name ), array( 'status' => 500 ) ); } delete_option( $args['option_name'] ); } else { update_option( $args['option_name'], $request[ $name ] ); } } return $this->get_item( $request ); } /** * Retrieves all of the registered options for the Settings API. * * @since 4.7.0 * * @return array Array of registered options. */ protected function get_registered_options() { $rest_options = array(); foreach ( get_registered_settings() as $name => $args ) { if ( empty( $args['show_in_rest'] ) ) { continue; } $rest_args = array(); if ( is_array( $args['show_in_rest'] ) ) { $rest_args = $args['show_in_rest']; } $defaults = array( 'name' => ! empty( $rest_args['name'] ) ? $rest_args['name'] : $name, 'schema' => array(), ); $rest_args = array_merge( $defaults, $rest_args ); $default_schema = array( 'type' => empty( $args['type'] ) ? null : $args['type'], 'description' => empty( $args['description'] ) ? '' : $args['description'], 'default' => isset( $args['default'] ) ? $args['default'] : null, ); $rest_args['schema'] = array_merge( $default_schema, $rest_args['schema'] ); $rest_args['option_name'] = $name; // Skip over settings that don't have a defined type in the schema. if ( empty( $rest_args['schema']['type'] ) ) { continue; } /* * Whitelist the supported types for settings, as we don't want invalid types * to be updated with arbitrary values that we can't do decent sanitizing for. */ if ( ! in_array( $rest_args['schema']['type'], array( 'number', 'integer', 'string', 'boolean', 'array', 'object' ), true ) ) { continue; } $rest_args['schema'] = $this->set_additional_properties_to_false( $rest_args['schema'] ); $rest_options[ $rest_args['name'] ] = $rest_args; } return $rest_options; } /** * Retrieves the site setting schema, conforming to JSON Schema. * * @since 4.7.0 * * @return array Item schema data. */ public function get_item_schema() { $options = $this->get_registered_options(); $schema = array( '$schema' => 'http://json-schema.org/draft-04/schema#', 'title' => 'settings', 'type' => 'object', 'properties' => array(), ); foreach ( $options as $option_name => $option ) { $schema['properties'][ $option_name ] = $option['schema']; $schema['properties'][ $option_name ]['arg_options'] = array( 'sanitize_callback' => array( $this, 'sanitize_callback' ), ); } return $this->add_additional_fields_schema( $schema ); } /** * Custom sanitize callback used for all options to allow the use of 'null'. * * By default, the schema of settings will throw an error if a value is set to * `null` as it's not a valid value for something like "type => string". We * provide a wrapper sanitizer to whitelist the use of `null`. * * @since 4.7.0 * * @param mixed $value The value for the setting. * @param WP_REST_Request $request The request object. * @param string $param The parameter name. * @return mixed|WP_Error */ public function sanitize_callback( $value, $request, $param ) { if ( is_null( $value ) ) { return $value; } return rest_parse_request_arg( $value, $request, $param ); } /** * Recursively add additionalProperties = false to all objects in a schema. * * This is need to restrict properties of objects in settings values to only * registered items, as the REST API will allow additional properties by * default. * * @since 4.9.0 * * @param array $schema The schema array. * @return array */ protected function set_additional_properties_to_false( $schema ) { switch ( $schema['type'] ) { case 'object': foreach ( $schema['properties'] as $key => $child_schema ) { $schema['properties'][ $key ] = $this->set_additional_properties_to_false( $child_schema ); } $schema['additionalProperties'] = false; break; case 'array': $schema['items'] = $this->set_additional_properties_to_false( $schema['items'] ); break; } return $schema; } } coal producers indonesia | Coal Investor : Indonesian Coal Suppliers & Coal Mining

Tag Archives: coal producers indonesia

admin

Indonesia is part of Top 10 coal producers worldwide

Indonesia is part of Top 10 coal producers worldwide

Coal is the key to the worldwide structure of energy. It accounts for about 40% of the world’s electricity production, hence it is a leading source of electricity. It will soon replace oil and become the largest source of primary energy. Coal dominates the global energy arena due to its abundance, affordability and wide distribution across the world. Coal reserves are estimated at 869 billion tons based on the current production rate. This means that coal should last about 115 years longer compared to the conventional reserves of oil and gas. Especially noteworthy are the significant coal reserves in Asia and southern Africa, two areas of the world that face major challenges in supplying energy to their populations. Coal reserves are highly underestimated in comparison with conventional reserves of oil and gas.
The ten leading countries based on hard coal production

China is the chief coal producer while the United States comes in second. Other major coal producers are India and Australia. Five countries, namely China, the United States, Russia, India and Japan accounted for over 75% of worldwide coal consumption. Despite the swift deployment of renewable energy, mainly in the background of debates around climate change, it is coal that is responsible for the largest upsurge in energy requirement of all energy sources.

Approximately 90% of the total global coal is produced by ten countries with China running in the lead. The statistics below show countries that have substantial coal resources. The data has been based on a wide assortment of material, as well as data acquired from the World Energy Council and both national and international publications.

10.UKRINE
Coal production in Ukraine in 2013 was about 64.976 Million tons. With the ongoing conflict in the country, there has been a decline in coking coal production and in coal power generation, particularly in the affected eastern regions of the country. Indeed, the Donetsk region has experienced a fall of nearly 30% in coal production. As a result, Ukraine has started importing coal from South Africa and Russia for the purpose of power generation.

9.COLUMBIA
Colombia’s coal production was around 85.5 million tons in 2013, an output which represented a 4% drop off from its target of 89 Million tons. Coal exports were estimated at 94.3%. The country’s National Mining Agency reported an increase of 18% in mineral production.

8.KAZAKHSTAN
Kazakhstan stands in eighth with a coal production of 116.6 million tons as of December 2012. Regarding consumption, Kazakhstan came in 12th, with coal accounting for nearly 85% of the nation’s entire connected power capacity. The country has an estimated reserve of about 33.6 billion tons and thus holds the eighth largest coal reserve. Kazakhstan has over 400 coal mines.

7.SOUTH AFRICA
With about 260 million tons produced, South Africa comes in seventh in global coal production. The country is the sixth largest coal exporter, having traded about 74 million tons in 2012. South Africa mostly exports its coal to Europe, China, and India. It is estimated that over 90% of electricity production in South Africa depends on coal. South Africa’s established coal reserves had about 30.15 billion tons in

6.RUSSIA
Russia comes in sixth regarding worldwide coal production. It produced 354.8 million tons of coal in 2012, of which 80% was steam coal and the rest coking coal. Russia is also the fifth largest consumer of coal. It exported 134 million tons in 2012, becoming the third largest coal exporter. With reserves of up to 157 billion tons, Russia is second in the world as pertains coal reserves. Open pit mining accounts for more than half of Russia’s coal production.

coal-mines-indonesia

5.INDONESIA
Indonesia comes fifth in coal production, having produced 386 million tons of coal. Indonesia and Australia have been head to head in coal production, and while in 2011 Indonesia overtook Australia in coal production by now the situation has been reversed. Coal is responsible for 44% of Indonesian electricity production. The country has about 5.5 billion tons of coal reserves based on 2012 statistics.

4.AUSTRALIA
Coal output in Australia reached 413 million tons in 2013, positioning it at number four in the world. The country exports about 90% of its coal, coming in second after Indonesia, and in 2012 it exported 384 million tons. Australia otherwise maintains 76.4 billion tons in its reserves. The country has about 100 private coal miners carrying out open pit operations, and this method of mining accounts for 74% of Australia’s total coal production.

3.INDIA
India’s coal production was about 605 million tons, making it the third largest producer globally. India consumed 8% of the total world?s coal, making it the third largest consumer of the resource as well. It is also the third largest importer of coal with a total of 160 million tons in imports, trailing behind China and Japan. Around three-quarters (68%) of electricity generation in India depends on coal. The verified coal reserves in India are estimated at 60.6 billion tons as of 2013, ranking again as number three globally.

2.THE UNITED STATES
The United States is second worldwide in coal production, generating 922 Million tons of coal in the 2012/2013 period representing approximately 13% of global coal production. It is also the second biggest coal consumer. US consumption of coal is estimated at 11% of the world’s total usage, and nearly 37% of the nation’s electricity production depends on coal. The US also has the world’s largest coal reserves, measuring in at approximately 237 billion tons.

1.CHINA
China has been the biggest coal producer in the past three decades. China produced nearly 3.7 billion tons of coal in 2013 representing 47% of global total coal yield. The country also consumes more than half of the world’s total coal consumption. China is the third worldwide as relates to coal reserves, with an estimate of 114.5 billion tons per December 2012 statistics. It is estimated that China uses half of its coal for power generation, which accounts for over 80% of the country’s electricity production.

Coal is proving critical in the world’s energy growth. The need for coal is ever increasing, and ever larger percentages of electricity produced in the world is becoming reliant on power plants that use the resource. Regardless of the enormous distribution of coal reserves worldwide, these amounts are proving to not be enough. Furthermore, the ecological harms that come as a result of activities related to coal activities are grave matters and, thus, proper actions have to be taken. Consequently, it is essential for governments to discover innovative technologies for improved mining and coal processing, while also taking into account efficiency and the importance of environmental sustainability. It is paramount for policy makers come up with long-lasting technological solutions that look into future, hence putting the coal sector on a path that would allow it to respond better to future global challenges.