├── .distignore
├── .github
└── workflows
│ └── release.yml
├── .gitignore
├── composer.json
├── composer.lock
├── mb-rest-api.php
├── phpcs.xml
├── readme.md
├── readme.txt
├── src
├── Base.php
├── Comment.php
├── Post.php
├── Setting.php
├── Term.php
└── User.php
└── tests
└── 01-hide-from-rest.php
/.distignore:
--------------------------------------------------------------------------------
1 | /.git
2 | /.github
3 | /node_modules
4 |
5 | .DS_Store
6 | Thumbs.db
7 |
8 | /tests
9 |
10 | .distignore
11 | .gitattributes
12 | .gitignore
13 | composer.json
14 | composer.lock
15 | package-lock.json
16 | phpcs.xml
17 | readme.md
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 | on:
3 | workflow_dispatch:
4 | branch:
5 | - master
6 | push:
7 | tags:
8 | - "*"
9 | jobs:
10 | call-workflow:
11 | uses: wpmetabox/meta-box/.github/workflows/wordpressorg.yml@master
12 | secrets:
13 | SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
14 | SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
15 | SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .svn
2 | vendor
3 | node_modules
4 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wpmetabox/mb-rest-api",
3 | "type": "wordpress-plugin",
4 | "keywords": [
5 | "wordpress",
6 | "ui",
7 | "select2",
8 | "wp-admin",
9 | "meta-box",
10 | "wordpress-plugin",
11 | "custom-field",
12 | "custom-post-type",
13 | "rest",
14 | "rest-api"
15 | ],
16 | "description": "Add Meta Box custom fields to the WordPress REST API responses.",
17 | "homepage": "https://metabox.io/plugins/mb-rest-api/",
18 | "license": "GPL-2.0",
19 | "minimum-stability": "stable",
20 | "authors": [
21 | {
22 | "name": "Tran Ngoc Tuan Anh",
23 | "email": "anhtnt@elightup.com",
24 | "homepage": "https://deluxeblogtips.com",
25 | "role": "Developer"
26 | }
27 | ],
28 | "autoload": {
29 | "psr-4": {
30 | "MetaBox\\RestApi\\": "src/"
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "125dc02c9fe2d10cb1bbc3587dd6254e",
8 | "packages": [],
9 | "packages-dev": [],
10 | "aliases": [],
11 | "minimum-stability": "stable",
12 | "stability-flags": [],
13 | "prefer-stable": false,
14 | "prefer-lowest": false,
15 | "platform": [],
16 | "platform-dev": [],
17 | "plugin-api-version": "2.3.0"
18 | }
19 |
--------------------------------------------------------------------------------
/mb-rest-api.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
13 |
14 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | .
29 |
30 |
31 | /vendor/*
32 | /.github/*
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
71 |
72 |
73 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # MB Rest API
2 |
3 | [**MB Rest API**](https://metabox.io/plugins/mb-rest-api/) is an extension for [Meta Box](https://metabox.io) which helps you to get and update custom fields' values (meta value) from posts, pages, custom post types, terms via the WordPress REST API.
4 |
5 | > **Meta Box Lite**
6 | > We recommend using [Meta Box Lite](https://metabox.io/lite/), a feature-rich free UI version of Meta Box that provides UI and all free features for managing custom fields and dynamic content on WordPress, including post types, taxonomies, custom fields, and relationships.
7 |
8 | ### Plugin Links
9 |
10 | - [Project Page](https://metabox.io/plugins/mb-rest-api/)
11 | - [Documentation](https://docs.metabox.io/extensions/mb-rest-api/)
12 | - [Github Repo](https://github.com/rilwis/mb-rest-api/)
13 |
14 | See more [Meta Box plugins](https://metabox.io/plugins/).
15 |
16 | ### You might also like
17 |
18 | If you like this plugin, you might also like our other WordPress products:
19 |
20 | - [Meta Box](https://metabox.io) - A powerful WordPress plugin for creating custom post types and custom fields.
21 | - [Slim SEO](https://wpslimseo.com) - A fast, lightweight and full-featured SEO plugin for WordPress with minimal configuration.
22 | - [Slim SEO Schema](https://wpslimseo.com/products/slim-seo-schema/) - An advanced, powerful and flexible plugin to add schemas to WordPress.
23 | - [Slim SEO Link Manager](https://wpslimseo.com/products/slim-seo-link-manager/) - Build internal link easier in WordPress with real-time reports.
24 | - [GretaThemes](https://gretathemes.com) - Free and premium WordPress themes that clean, simple and just work.
25 | - [Auto Listings](https://wpautolistings.com) - A car sale and dealership plugin for WordPress.
26 |
--------------------------------------------------------------------------------
/readme.txt:
--------------------------------------------------------------------------------
1 | === MB Rest API ===
2 | Contributors: metabox, rilwis, barcavn2
3 | Donate link: https://metabox.io/pricing/
4 | Tags: meta box, custom fields, rest api
5 | Requires at least: 4.1
6 | Tested up to: 6.6.2
7 | Stable tag: 2.0.5
8 | Requires PHP: 7.0
9 | License: GPLv2 or later
10 |
11 | Get and update Meta Box custom fields to the WordPress REST API responses.
12 |
13 | == Description ==
14 |
15 | [**MB Rest API**](https://metabox.io/plugins/mb-rest-api/) is an extension for [Meta Box](https://metabox.io) which helps you to get and update custom fields' values (meta value) from posts, pages, custom post types, terms via the WordPress REST API.
16 |
17 | > **Meta Box Lite**
18 | > We recommend using [Meta Box Lite](https://metabox.io/lite/), a feature-rich free UI version of Meta Box that provides UI and all free features for managing custom fields and dynamic content on WordPress, including post types, taxonomies, custom fields, and relationships.
19 |
20 | ### Plugin Links
21 |
22 | - [Project Page](https://metabox.io/plugins/mb-rest-api/)
23 | - [Documentation](https://docs.metabox.io/extensions/mb-rest-api/)
24 | - [Github Repo](https://github.com/rilwis/mb-rest-api/)
25 |
26 | See more [Meta Box plugins](https://metabox.io/plugins/).
27 |
28 | ### You might also like
29 |
30 | If you like this plugin, you might also like our other WordPress products:
31 |
32 | - [Meta Box](https://metabox.io) - A powerful WordPress plugin for creating custom post types and custom fields.
33 | - [Slim SEO](https://wpslimseo.com) - A fast, lightweight and full-featured SEO plugin for WordPress with minimal configuration.
34 | - [Slim SEO Schema](https://wpslimseo.com/products/slim-seo-schema/) - An advanced, powerful and flexible plugin to add schemas to WordPress.
35 | - [Slim SEO Link Manager](https://wpslimseo.com/products/slim-seo-link-manager/) - Build internal link easier in WordPress with real-time reports.
36 | - [GretaThemes](https://gretathemes.com) - Free and premium WordPress themes that clean, simple and just work.
37 | - [Auto Listings](https://wpautolistings.com) - A car sale and dealership plugin for WordPress.
38 |
39 | == Installation ==
40 |
41 | You need to install [Meta Box](https://metabox.io) plugin and WordPress REST API first
42 |
43 | - Go to **Plugins | Add New** and search for **Meta Box**
44 | - Click **Install Now** button to install the plugin
45 | - After installing, click **Activate Plugin** to activate the plugin
46 |
47 | Repeat the same process for **WP REST API** and **MB Rest API**.
48 |
49 | == Frequently Asked Questions ==
50 |
51 | == Screenshots ==
52 |
53 | == Changelog ==
54 |
55 | = 2.0.5 - 2024-11-01 =
56 | Fix direct file access
57 |
58 | = 2.0.4 - 2024-08-19 =
59 | Fix running PHP Codesniffer when installing & autoload the plugin's main file via Composer
60 |
61 | = 2.0.3 - 2024-07-22 =
62 | - Fix PHP warning in WordPress 6.6
63 |
64 | = 2.0.2 - 2024-07-02 =
65 | - Fix not updating WooCommerce products
66 |
67 | = 2.0.1 - 2023-10-14 =
68 | - Ensure group value is always an array. Fix PHP warning when group has no values.
69 |
70 | = 2.0.0 - 2023-10-11 =
71 | - Complete rewrite the plugin for clarity and maintainability
72 | - Add support for settings page, allow you to get and update data for settings pages
73 | - Show errors when updating non-existing fields
74 |
75 | = 1.5.1 - 2023-08-16 =
76 | - Fix cannot update user meta in a custom table (#18)
77 |
78 | = 1.5.0 - 2023-07-29
79 | - Remove fields in the Rest API responses with hide_from_rest = true
80 | - Do not show MB User Profile fields in the Rest API responses
81 |
82 | = 1.4.1 - 2023-05-17 =
83 | - Fix not working with `_filter`.
84 |
85 | = 1.4.0 - 2019-12-12 =
86 | - Add support for comment meta. Requires MB Comment Meta plugin.
87 |
88 | = 1.3.6 - 2019-11-07 =
89 | - Fix term meta not available.
90 |
91 | = 1.3.5 =
92 | * Fixed not updating fields in custom tables.
93 |
94 | = 1.3.4 =
95 | * Make it safe to include into AIO plugin.
96 | * Removed _state from returned value for groups.
97 |
98 | = 1.3.3 =
99 | * Fixed not updating user meta.
100 |
101 | = 1.3.2 =
102 | * Fixed custom fields for terms not saving for POST request. Props Mirza Pandzo.
103 | * Fixed wrong key for `post_tag`. Props Mirza Pandzo.
104 |
105 | = 1.3.1 =
106 | * Removed fields that have no values from the response (divider, heading, etc.).
107 |
108 | = 1.3 =
109 | * Added fully support for terms and users. Both get and update meta values.
110 |
111 | = 1.2 =
112 | * Improvement: The update callback now can accept array of params
113 |
114 | = 1.1 =
115 | * Improvement: Add update callback
116 | * Fix: Make sure the returned values of image/file fields are always array
117 |
118 | = 1.0.1 =
119 | * Fix: error when MB Term Meta is not installed
120 |
121 | = 1.0.0 =
122 | * Initial release
123 |
124 | == Upgrade Notice ==
125 |
--------------------------------------------------------------------------------
/src/Base.php:
--------------------------------------------------------------------------------
1 | object_type = strtolower( ( new ReflectionClass( $this ) )->getShortName() );
34 | add_action( 'rest_api_init', [ $this, 'init' ] );
35 | }
36 |
37 | public function init() {
38 | register_rest_field( $this->get_types(), self::KEY, [
39 | 'get_callback' => [ $this, 'get' ],
40 | 'update_callback' => [ $this, 'update' ],
41 | ] );
42 | }
43 |
44 | protected function get_types(): array {
45 | return [ $this->object_type ];
46 | }
47 |
48 | public function get( array $object ): array {
49 | return empty( $object['id'] ) ? [] : $this->get_values( $object['id'] );
50 | }
51 |
52 | /**
53 | * Get all fields' values from list of meta boxes.
54 | *
55 | * @param int|string $object_id Object ID.
56 | * @param array $fields List of fields.
57 | */
58 | protected function get_values( $object_id, array $fields = [] ): array {
59 | $fields = $fields ?: $this->get_fields( $object_id );
60 |
61 | $values = [];
62 | $args = [ 'object_type' => $this->object_type ];
63 | foreach ( $fields as $field ) {
64 | $value = rwmb_get_value( $field['id'], $args, $object_id );
65 | $value = $this->normalize_value( $field, $value );
66 |
67 | $values[ $field['id'] ] = $value;
68 | }
69 |
70 | return $values;
71 | }
72 |
73 | protected function get_fields( $type_or_id ): array {
74 | $fields = rwmb_get_object_fields( $type_or_id, $this->object_type );
75 |
76 | // Remove fields with with hide_from_rest = true or has no values.
77 | return array_filter( $fields, function ( $field ) {
78 | return empty( $field['hide_from_rest'] ) && ! empty( $field['id'] ) && ! in_array( $field['type'], $this->no_value_fields, true );
79 | } );
80 | }
81 |
82 | private function normalize_value( array $field, $value ) {
83 | $value = $this->normalize_group_value( $field, $value );
84 | $value = $this->normalize_media_value( $field, $value );
85 |
86 | return $value;
87 | }
88 |
89 | private function normalize_group_value( array $field, $value ) {
90 | if ( 'group' !== $field['type'] ) {
91 | return $value;
92 | }
93 | if ( ! is_array( $value ) ) {
94 | $value = [];
95 | }
96 |
97 | unset( $value['_state'] );
98 |
99 | foreach ( $field['fields'] as $subfield ) {
100 | if ( empty( $subfield['id'] ) || empty( $value[ $subfield['id'] ] ) ) {
101 | continue;
102 | }
103 | $subvalue = $value[ $subfield['id'] ];
104 | $subvalue = $this->normalize_value( $subfield, $subvalue );
105 |
106 | $value[ $subfield['id'] ] = $subvalue;
107 | }
108 |
109 | return $value;
110 | }
111 |
112 | private function normalize_media_value( array $field, $value ) {
113 | // Make sure values of file/image fields are always indexed 0, 1, 2, ...
114 | return is_array( $value ) && in_array( $field['type'], $this->media_fields, true ) ? array_values( $value ) : $value;
115 | }
116 |
117 | protected function update_values( $data, $object_id, $object_subtype ) {
118 | $data = is_string( $data ) ? json_decode( $data, true ) : $data;
119 |
120 | foreach ( $data as $field_id => $value ) {
121 | $field = rwmb_get_registry( 'field' )->get( $field_id, $object_subtype, $this->object_type );
122 | $this->check_field_exists( $field_id, $field );
123 | $this->update_value( $field, $value, $object_id );
124 | }
125 |
126 | rwmb_request()->set_post_data( [ 'object_type' => $this->object_type ] );
127 | do_action( 'rwmb_after_save_post', $object_id );
128 | }
129 |
130 | protected function update_value( array $field, $value, $object_id ) {
131 | $old = RWMB_Field::call( $field, 'raw_meta', $object_id );
132 |
133 | $new = RWMB_Field::process_value( $value, $object_id, $field );
134 | $new = RWMB_Field::filter( 'rest_value', $new, $field, $old, $object_id );
135 |
136 | // Call defined method to save meta value, if there's no methods, call common one.
137 | RWMB_Field::call( $field, 'save', $new, $old, $object_id );
138 | }
139 |
140 | private function check_field_exists( $field_id, $field ) {
141 | if ( $field ) {
142 | return;
143 | }
144 |
145 | // Translators: %s - Field ID.
146 | $this->send_error_message( 'field_not_exists', sprintf( __( "Field '%s' does not exists.", 'mb-rest-api' ), $field_id ) );
147 | }
148 |
149 | protected function send_error_message( $id, $message, $status_code = 400 ) {
150 | // Send an error, mimic how WordPress returns an error for a Rest request.
151 | status_header( $status_code );
152 |
153 | $error = new WP_Error( $id, $message, [ 'status' => $status_code ] );
154 | $response = rest_convert_error_to_response( $error );
155 |
156 | echo wp_json_encode( $response->data );
157 | die;
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/src/Comment.php:
--------------------------------------------------------------------------------
1 | update_values( $data, $comment->comment_ID, 'comment' );
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Post.php:
--------------------------------------------------------------------------------
1 | post_type ) {
9 | $post_id = $post->get_id();
10 | } else {
11 | $post_id = $post->ID;
12 | }
13 |
14 | $this->update_values( $data, $post_id, $post->post_type );
15 | }
16 |
17 | protected function get_types(): array {
18 | $post_types = get_post_types( [], 'objects' );
19 | foreach ( $post_types as $key => $post_type_object ) {
20 | if ( empty( $post_type_object->show_in_rest ) ) {
21 | unset( $post_types[ $key ] );
22 | }
23 | }
24 |
25 | return array_keys( $post_types );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Setting.php:
--------------------------------------------------------------------------------
1 | WP_REST_Server::READABLE,
11 | 'callback' => [ $this, 'get_data' ],
12 | 'permission_callback' => [ $this, 'has_permission' ],
13 | ] );
14 |
15 | register_rest_route( self::NAMESPACE, '/settings-page/', [
16 | 'methods' => WP_REST_Server::CREATABLE,
17 | 'callback' => [ $this, 'update_data' ],
18 | 'permission_callback' => [ $this, 'has_permission' ],
19 | ] );
20 | }
21 |
22 | public function has_permission( WP_REST_Request $request ) {
23 | $settings_page = $this->get_settings_page( $request );
24 | return current_user_can( $settings_page['capability'] ?? 'edit_theme_options' );
25 | }
26 |
27 | public function get_data( WP_REST_Request $request ) {
28 | $settings_page = $this->get_settings_page( $request );
29 | $option_name = $settings_page['option_name'] ?: $settings_page['id'];
30 | $fields = $this->get_fields( $settings_page['id'] );
31 | return $this->get_values( $option_name, $fields );
32 | }
33 |
34 | public function update_data( WP_REST_Request $request ) {
35 | $settings_page = $this->get_settings_page( $request );
36 | $option_name = $settings_page['option_name'] ?: $settings_page['id'];
37 | $data = $request->get_param( 'data' );
38 |
39 | $this->update_values( $data, $option_name, $option_name );
40 |
41 | return $this->get_data( $request );
42 | }
43 |
44 | private function get_settings_page( WP_REST_Request $request ): array {
45 | $id = $request->get_param( 'id' );
46 | if ( ! $id ) {
47 | $this->send_error_message( 'no_settings_page_id', __( 'No settings page id.', 'mb-rest-api' ) );
48 | }
49 |
50 | $settings_pages = apply_filters( 'mb_settings_pages', [] );
51 | foreach ( $settings_pages as $settings_page ) {
52 | if ( $settings_page['id'] === $id ) {
53 | return $settings_page;
54 | }
55 | }
56 |
57 | // Translators: %s - settings page id.
58 | $this->send_error_message( 'settings_page_not_exists', sprintf( __( "Settings page '%s' does not exist.", 'mb-rest-api' ), $id ) );
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Term.php:
--------------------------------------------------------------------------------
1 | update_values( $data, $term->term_id, $term->taxonomy );
9 | }
10 |
11 | protected function get_types(): array {
12 | $taxonomies = get_taxonomies( [], 'objects' );
13 | foreach ( $taxonomies as $key => $taxonomy_object ) {
14 | if ( empty( $taxonomy_object->show_in_rest ) ) {
15 | unset( $taxonomies[ $key ] );
16 | }
17 | }
18 |
19 | $taxonomies = array_keys( $taxonomies );
20 | if ( in_array( 'post_tag', $taxonomies, true ) ) {
21 | $index = array_search( 'post_tag', $taxonomies, true );
22 | $taxonomies[ $index ] = 'tag';
23 | }
24 |
25 | return $taxonomies;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/User.php:
--------------------------------------------------------------------------------
1 | update_values( $data, $user->ID, 'user' );
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/tests/01-hide-from-rest.php:
--------------------------------------------------------------------------------
1 | 'In REST',
9 | 'fields' => [
10 | 'Name',
11 | [
12 | 'id' => 'email',
13 | 'name' => 'Email',
14 | 'hide_from_rest' => true,
15 | ],
16 | ],
17 | ];
18 | return $meta_boxes;
19 | } );
20 |
--------------------------------------------------------------------------------