• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wp-graphql / wp-graphql / 14716683875

28 Apr 2025 07:58PM UTC coverage: 84.287% (+1.6%) from 82.648%
14716683875

push

github

actions-user
release: merge develop into master for v2.3.0

15905 of 18870 relevant lines covered (84.29%)

257.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.04
/src/Mutation/PostObjectUpdate.php
1
<?php
2
namespace WPGraphQL\Mutation;
3

4
use GraphQL\Error\UserError;
5
use GraphQL\Type\Definition\ResolveInfo;
6
use WPGraphQL\AppContext;
7
use WPGraphQL\Data\PostObjectMutation;
8
use WPGraphQL\Utils\Utils;
9
use WP_Post_Type;
10

11
class PostObjectUpdate {
12
        /**
13
         * Registers the PostObjectUpdate mutation.
14
         *
15
         * @param \WP_Post_Type $post_type_object The post type of the mutation.
16
         *
17
         * @return void
18
         * @throws \Exception
19
         */
20
        public static function register_mutation( WP_Post_Type $post_type_object ) {
593✔
21
                $mutation_name = 'update' . ucwords( $post_type_object->graphql_single_name );
593✔
22

23
                register_graphql_mutation(
593✔
24
                        $mutation_name,
593✔
25
                        [
593✔
26
                                'inputFields'         => self::get_input_fields( $post_type_object ),
593✔
27
                                'outputFields'        => self::get_output_fields( $post_type_object ),
593✔
28
                                'mutateAndGetPayload' => self::mutate_and_get_payload( $post_type_object, $mutation_name ),
593✔
29
                        ]
593✔
30
                );
593✔
31
        }
32

33
        /**
34
         * Defines the mutation input field configuration.
35
         *
36
         * @param \WP_Post_Type $post_type_object The post type of the mutation.
37
         *
38
         * @return array<string,array<string,mixed>>
39
         */
40
        public static function get_input_fields( $post_type_object ) {
593✔
41
                return array_merge(
593✔
42
                        PostObjectCreate::get_input_fields( $post_type_object ),
593✔
43
                        [
593✔
44
                                'id'             => [
593✔
45
                                        'type'        => [
593✔
46
                                                'non_null' => 'ID',
593✔
47
                                        ],
593✔
48
                                        'description' => static function () use ( $post_type_object ) {
593✔
49
                                                // translators: the placeholder is the name of the type of post object being updated
50
                                                return sprintf( __( 'The ID of the %1$s object', 'wp-graphql' ), $post_type_object->graphql_single_name );
15✔
51
                                        },
593✔
52
                                ],
593✔
53
                                'ignoreEditLock' => [
593✔
54
                                        'type'        => 'Boolean',
593✔
55
                                        'description' => static function () {
593✔
56
                                                return __( 'Override the edit lock when another user is editing the post', 'wp-graphql' );
15✔
57
                                        },
593✔
58
                                ],
593✔
59
                        ]
593✔
60
                );
593✔
61
        }
62

63
        /**
64
         * Defines the mutation output field configuration.
65
         *
66
         * @param \WP_Post_Type $post_type_object The post type of the mutation.
67
         *
68
         * @return array<string,array<string,mixed>>
69
         */
70
        public static function get_output_fields( $post_type_object ) {
593✔
71
                return PostObjectCreate::get_output_fields( $post_type_object );
593✔
72
        }
73

74
        /**
75
         * Defines the mutation data modification closure.
76
         *
77
         * @param \WP_Post_Type $post_type_object The post type of the mutation.
78
         * @param string        $mutation_name      The mutation name.
79
         *
80
         * @return callable(array<string,mixed>$input,\WPGraphQL\AppContext $context,\GraphQL\Type\Definition\ResolveInfo $info):array<string,mixed>
81
         */
82
        public static function mutate_and_get_payload( $post_type_object, $mutation_name ) {
593✔
83
                return static function ( $input, AppContext $context, ResolveInfo $info ) use ( $post_type_object, $mutation_name ) {
593✔
84
                        // Get the database ID for the comment.
85
                        $post_id       = Utils::get_database_id_from_id( $input['id'] );
6✔
86
                        $existing_post = ! empty( $post_id ) ? get_post( $post_id ) : null;
6✔
87

88
                        /**
89
                         * If there's no existing post, throw an exception
90
                         */
91
                        if ( null === $existing_post ) {
6✔
92
                                // translators: the placeholder is the name of the type of post being updated
93
                                throw new UserError( esc_html( sprintf( __( 'No %1$s could be found to update', 'wp-graphql' ), $post_type_object->graphql_single_name ) ) );
2✔
94
                        }
95

96
                        if ( $post_type_object->name !== $existing_post->post_type ) {
6✔
97
                                // translators: The first placeholder is an ID and the second placeholder is the name of the post type being edited
98
                                throw new UserError( esc_html( sprintf( __( 'The id %1$d is not of the type "%2$s"', 'wp-graphql' ), $post_id, $post_type_object->name ) ) );
1✔
99
                        }
100

101
                        /**
102
                         * Stop now if a user isn't allowed to edit posts
103
                         */
104
                        if ( ! isset( $post_type_object->cap->edit_posts ) || ! current_user_can( $post_type_object->cap->edit_posts ) ) {
5✔
105
                                // translators: the $post_type_object->graphql_single_name placeholder is the name of the object being mutated
106
                                throw new UserError( esc_html( sprintf( __( 'Sorry, you are not allowed to update a %1$s', 'wp-graphql' ), $post_type_object->graphql_single_name ) ) );
2✔
107
                        }
108

109
                        /**
110
                         * If the existing post was authored by another author, ensure the requesting user has permission to edit it
111
                         */
112
                        if ( get_current_user_id() !== (int) $existing_post->post_author && ( ! isset( $post_type_object->cap->edit_others_posts ) || true !== current_user_can( $post_type_object->cap->edit_others_posts ) ) ) {
4✔
113
                                // translators: the $post_type_object->graphql_single_name placeholder is the name of the object being mutated
114
                                throw new UserError( esc_html( sprintf( __( 'Sorry, you are not allowed to update another author\'s %1$s', 'wp-graphql' ), $post_type_object->graphql_single_name ) ) );
2✔
115
                        }
116

117
                        $author_id = absint( $existing_post->post_author );
3✔
118

119
                        /**
120
                         * If the mutation is setting the author to be someone other than the user making the request
121
                         * make sure they have permission to edit others posts
122
                         */
123
                        if ( ! empty( $input['authorId'] ) ) {
3✔
124
                                // Ensure authorId is a valid databaseId.
125
                                $input['authorId'] = Utils::get_database_id_from_id( $input['authorId'] );
×
126
                                // Use the new author for checks.
127
                                $author_id = $input['authorId'];
×
128
                        }
129

130
                        /**
131
                         * Check to see if the existing_media_item author matches the current user,
132
                         * if not they need to be able to edit others posts to proceed
133
                         */
134
                        if ( get_current_user_id() !== $author_id && ( ! isset( $post_type_object->cap->edit_others_posts ) || ! current_user_can( $post_type_object->cap->edit_others_posts ) ) ) {
3✔
135
                                // translators: the $post_type_object->graphql_single_name placeholder is the name of the object being mutated
136
                                throw new UserError( esc_html( sprintf( __( 'Sorry, you are not allowed to update %1$s as this user.', 'wp-graphql' ), $post_type_object->graphql_plural_name ) ) );
×
137
                        }
138

139
                        // If post is locked and the override is not specified, do not allow the edit
140
                        $locked_user_id = PostObjectMutation::check_edit_lock( $post_id, $input );
3✔
141
                        if ( false !== $locked_user_id ) {
3✔
142
                                $user         = get_userdata( (int) $locked_user_id );
1✔
143
                                $display_name = isset( $user->display_name ) ? $user->display_name : 'unknown';
1✔
144
                                /* translators: %s: User's display name. */
145
                                throw new UserError( esc_html( sprintf( __( 'You cannot update this item. %s is currently editing.', 'wp-graphql' ), $display_name ) ) );
1✔
146
                        }
147

148
                        /**
149
                         * @todo: when we add support for making posts sticky, we should check permissions to make sure users can make posts sticky
150
                         * @see : https://github.com/WordPress/WordPress/blob/e357195ce303017d517aff944644a7a1232926f7/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php#L640-L642
151
                         */
152

153
                        /**
154
                         * @todo: when we add support for assigning terms to posts, we should check permissions to make sure they can assign terms
155
                         * @see : https://github.com/WordPress/WordPress/blob/e357195ce303017d517aff944644a7a1232926f7/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php#L644-L646
156
                         */
157

158
                        /**
159
                         * Insert the post object and get the ID
160
                         */
161
                        $post_args       = PostObjectMutation::prepare_post_object( $input, $post_type_object, $mutation_name );
3✔
162
                        $post_args['ID'] = $post_id;
3✔
163

164
                        $clean_args = wp_slash( (array) $post_args );
3✔
165

166
                        if ( ! is_array( $clean_args ) || empty( $clean_args ) ) {
3✔
167
                                throw new UserError( esc_html__( 'The object failed to update.', 'wp-graphql' ) );
×
168
                        }
169

170
                        /**
171
                         * Insert the post and retrieve the ID
172
                         */
173
                        $updated_post_id = wp_update_post( $clean_args, true );
3✔
174

175
                        /**
176
                         * Throw an exception if the post failed to update
177
                         */
178
                        if ( is_wp_error( $updated_post_id ) ) {
3✔
179
                                $error_message = $updated_post_id->get_error_message();
×
180
                                if ( ! empty( $error_message ) ) {
×
181
                                        throw new UserError( esc_html( $error_message ) );
×
182
                                }
183

184
                                throw new UserError( esc_html__( 'The object failed to update but no error was provided', 'wp-graphql' ) );
×
185
                        }
186

187
                        /**
188
                         * Fires after a single term is created or updated via a GraphQL mutation
189
                         *
190
                         * The dynamic portion of the hook name, `$taxonomy->name` refers to the taxonomy of the term being mutated
191
                         *
192
                         * @param int                 $post_id          Inserted post ID
193
                         * @param \WP_Post_Type       $post_type_object The Post Type object for the post being mutated
194
                         * @param array<string,mixed> $args             The args used to insert the term
195
                         * @param string              $mutation_name    The name of the mutation being performed
196
                         */
197
                        do_action( 'graphql_insert_post_object', absint( $post_id ), $post_type_object, $post_args, $mutation_name );
3✔
198

199
                        /**
200
                         * Fires after a single term is created or updated via a GraphQL mutation
201
                         *
202
                         * The dynamic portion of the hook name, `$taxonomy->name` refers to the taxonomy of the term being mutated
203
                         *
204
                         * @param int                 $post_id       Inserted post ID
205
                         * @param array<string,mixed> $args          The args used to insert the term
206
                         * @param string              $mutation_name The name of the mutation being performed
207
                         */
208
                        do_action( "graphql_insert_{$post_type_object->name}", absint( $post_id ), $post_args, $mutation_name );
3✔
209

210
                        /**
211
                         * This updates additional data not part of the posts table (postmeta, terms, other relations, etc)
212
                         *
213
                         * The input for the postObjectMutation will be passed, along with the $new_post_id for the
214
                         * postObject that was updated so that relations can be set, meta can be updated, etc.
215
                         */
216
                        PostObjectMutation::update_additional_post_object_data( (int) $post_id, $input, $post_type_object, $mutation_name, $context, $info );
3✔
217

218
                        /**
219
                         * Return the payload
220
                         */
221
                        return [
3✔
222
                                'postObjectId' => $post_id,
3✔
223
                        ];
3✔
224
                };
593✔
225
        }
226
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc