PluginProbe
Content Blocks Builder – Create blocks, repeater blocks with carousel, grid, popup layouts / 2.8.9
Content Blocks Builder – Create blocks, repeater blocks with carousel, grid, popup layouts v2.8.9
2.8.14 2.8.13 2.8.12 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.10 2.8.11 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 trunk 1.0.1 1.0.2 1.1.0 1.1.1 All 147 releases
content-blocks-builder / includes / meta-revisioning.php

meta-revisioning.php in Content Blocks Builder – Create blocks, repeater blocks with carousel, grid, popup layouts 2.8.9, at includes/meta-revisioning.php

316 lines 10.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Meta field revisioning
4 * Adapted from https://github.com/adamsilverstein/wp-post-meta-revisions
5 *
6 * @package BoldBlocks
7 * @author Phi Phan <mrphipv@gmail.com>
8 * @copyright Copyright (c) 2022, Phi Phan
9 */
10
11 namespace BoldBlocks;
12
13 // Exit if accessed directly.
14 defined( 'ABSPATH' ) || exit;
15
16 if ( ! class_exists( MetaRevisioning::class ) ) :
17 /**
18 * The controller class for meta revisioning.
19 */
20 class MetaRevisioning extends CoreComponent {
21 /**
22 * Run main hooks
23 *
24 * @return void
25 */
26 public function run() {
27 // When restoring a revision, also restore that revisions's revisioned meta.
28 add_action( 'wp_restore_post_revision', [ $this, 'wp_restore_post_revision_meta' ], 10, 2 );
29
30 // When creating or updating an autosave, save any revisioned meta fields.
31 add_action( 'wp_creating_autosave', [ $this, 'wp_autosave_post_revisioned_meta_fields' ] );
32 add_action( 'wp_before_creating_autosave', [ $this, 'wp_autosave_post_revisioned_meta_fields' ] );
33
34 // When creating a revision, also save any revisioned meta.
35 add_action( '_wp_put_post_revision', [ $this, 'wp_save_revisioned_meta_fields' ] );
36
37 // When revisioned post meta has changed, trigger a revision save.
38 add_filter( 'wp_save_post_revision_post_has_changed', [ $this, 'wp_check_revisioned_meta_fields_have_changed' ], 10, 3 );
39
40 // Add the revisioned meta to the JS data for the revisions interface.
41 add_filter( 'wp_prepare_revision_for_js', [ $this, 'wp_add_meta_to_prepare_revision_for_js' ], 10, 3 );
42
43 // Filter the diff ui returned for the revisions screen.
44 add_filter( 'wp_get_revision_ui_diff', [ $this, 'wp_filter_revision_ui_diff' ], 10, 3 );
45
46 }
47
48 /**
49 * Autosave the revisioned meta fields.
50 *
51 * Iterates thru the revisioned meta fields and checks each to see if they are set,
52 * and have a changed value. If so, the meta value is saved and attached to the autosave.
53 *
54 * @since 1.0.0
55 *
56 * @param Post object $new_autosave The new post being autosaved.
57 */
58 public function wp_autosave_post_revisioned_meta_fields( $new_autosave ) {
59 /*
60 * The post data arrives as either $_POST['data']['wp_autosave'] or the $_POST
61 * itself. This sets $posted_data to the correct variable.
62 *
63 * Ignoring sanitization to avoid altering meta. Ignoring the nonce check because
64 * this is hooked on inner core hooks where a valid nonce was already checked.
65 *
66 * @phpcs:disable WordPress.Security
67 */
68 $posted_data = isset( $_POST['data']['wp_autosave'] ) ? $_POST['data']['wp_autosave'] : $_POST;
69 // phpcs:enable
70
71 /*
72 * Go thru the revisioned meta keys and save them as part of the autosave, if
73 * the meta key is part of the posted data, the meta value is not blank and
74 * the the meta value has changes from the last autosaved value.
75 */
76 foreach ( $this->wp_post_revision_meta_keys() as $meta_key ) {
77
78 if (
79 isset( $posted_data[ $meta_key ] ) &&
80 ! $this->compare_meta_value( get_post_meta( $new_autosave['ID'], $meta_key, true ), wp_unslash( $posted_data[ $meta_key ] ) )
81 ) {
82 /*
83 * Use the underlying delete_metadata() and add_metadata() functions
84 * vs delete_post_meta() and add_post_meta() to make sure we're working
85 * with the actual revision meta.
86 */
87 delete_metadata( 'post', $new_autosave['ID'], $meta_key );
88
89 /*
90 * One last check to ensure meta value not empty().
91 */
92 if ( ! empty( $posted_data[ $meta_key ] ) ) {
93 /*
94 * Add the revisions meta data to the autosave.
95 */
96 add_metadata( 'post', $new_autosave['ID'], $meta_key, $posted_data[ $meta_key ] );
97 }
98 }
99 }
100 }
101
102 /**
103 * Determine which post meta fields should be revisioned.
104 *
105 * @access public
106 * @since 1.0.0
107 *
108 * @return array An array of meta keys to be revisioned.
109 */
110 public function wp_post_revision_meta_keys() {
111 /**
112 * Filter the list of post meta keys to be revisioned.
113 *
114 * @since 1.0.0
115 *
116 * @param array $keys An array of default meta fields to be revisioned.
117 */
118 return apply_filters( 'boldblocks_post_revision_meta_keys', [] ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals
119 }
120
121 /**
122 * Check whether revisioned post meta fields have changed.
123 *
124 * @param bool $post_has_changed Whether the post has changed.
125 * @param \WP_Post $last_revision The last revision post object.
126 * @param \WP_Post $post The post object.
127 *
128 * @since 1.0.0
129 */
130 public function wp_check_revisioned_meta_fields_have_changed( $post_has_changed, \WP_Post $last_revision, \WP_Post $post ) {
131 foreach ( $this->wp_post_revision_meta_keys() as $meta_key ) {
132 $meta = get_post_meta( $post->ID, $meta_key, true );
133 $last_revision_meta = get_post_meta( $last_revision->ID, $meta_key, true );
134 if ( ! $this->compare_meta_value( $meta, $last_revision_meta ) ) {
135 $post_has_changed = true;
136 break;
137 }
138 }
139 return $post_has_changed;
140 }
141
142 /**
143 * Compare two meta value.
144 *
145 * @param mixed $value1
146 * @param mixed $value2
147 * @return void
148 */
149 private function compare_meta_value( $value1, $value2 ) {
150 if ( \is_array( $value1 ) && \is_array( $value2 ) ) {
151 if ( count( $value1 ) === count( $value2 ) ) {
152 $value1_serialize = array_map( 'serialize', $value1 );
153 $value2_serialize = array_map( 'serialize', $value2 );
154 return array_diff( $value1_serialize, $value2_serialize ) === array_diff( $value2_serialize, $value1_serialize );
155 } else {
156 return false;
157 }
158 } else {
159 return $value1 === $value2;
160 }
161 }
162
163 /**
164 * Save the revisioned meta fields.
165 *
166 * @param int $revision_id The ID of the revision to save the meta to.
167 *
168 * @since 1.0.0
169 */
170 public function wp_save_revisioned_meta_fields( $revision_id ) {
171 $revision = get_post( $revision_id );
172 $post_id = $revision->post_parent;
173
174 // Save revisioned meta fields.
175 foreach ( $this->wp_post_revision_meta_keys() as $meta_key ) {
176 $this->copy_post_meta( $post_id, $revision_id, $meta_key );
177 }
178 }
179
180 /**
181 * Restore the revisioned meta values for a post.
182 *
183 * @param int $post_id The ID of the post to restore the meta to.
184 * @param int $revision_id The ID of the revision to restore the meta from.
185 *
186 * @since 1.0.0
187 */
188 public function wp_restore_post_revision_meta( $post_id, $revision_id ) {
189
190 // Restore revisioned meta fields.
191 foreach ( (array) $this->wp_post_revision_meta_keys() as $meta_key ) {
192
193 // Clear any existing meta.
194 delete_post_meta( $post_id, $meta_key );
195
196 $this->copy_post_meta( $revision_id, $post_id, $meta_key );
197 }
198 }
199
200 /**
201 * Copy post meta for the given key from one post to another.
202 *
203 * @param int $source_post_id Post ID to copy meta value(s) from.
204 * @param int $target_post_id Post ID to copy meta value(s) to.
205 * @param string $meta_key Meta key to copy.
206 *
207 * @since 2.0.0
208 */
209 protected function copy_post_meta( $source_post_id, $target_post_id, $meta_key ) {
210 foreach ( get_post_meta( $source_post_id, $meta_key ) as $meta_value ) {
211 /**
212 * We use add_metadata() function vs add_post_meta() here
213 * to allow for a revision post target OR regular post.
214 */
215 add_metadata( 'post', $target_post_id, $meta_key, wp_slash( $meta_value ) );
216 }
217 }
218
219 /**
220 * Get the diff between two meta value
221 *
222 * @param mixed $meta_from
223 * @param mixed $meta_to
224 * @param array $args additional params for wp_text_diff
225 * @return string
226 */
227 private function get_meta_diff( $meta_from, $meta_to, $args = [] ) {
228 $meta_from = \is_array( $meta_from ) ? wp_json_encode( $meta_from ) : $meta_from;
229 $meta_to = \is_array( $meta_to ) ? wp_json_encode( $meta_to ) : $meta_to;
230
231 $diff = wp_text_diff( (string) $meta_from, (string) $meta_to, $args );
232
233 return $diff;
234 }
235
236 /**
237 * Filter the revisions ui diff, adding revisioned meta fields.
238 *
239 * @param array fields Revision UI fields. Each item is an array of id, name and diff.
240 * @param WP_Post $compare_from The revision post to compare from.
241 * @param WP_Post $compare_to The revision post to compare to.
242 */
243 public function wp_filter_revision_ui_diff( $fields, $compare_from, $compare_to ) {
244 if ( ! $compare_from instanceof \WP_Post || ! $compare_to instanceof \WP_Post ) {
245 return $fields;
246 }
247
248 // Do we have revisioned meta fields?
249 $revisioned_meta_keys = $this->wp_post_revision_meta_keys();
250 if ( ! empty( $revisioned_meta_keys ) ) {
251
252 // Only add the header once, if we have a non-empty meta field.
253 $meta_header_added = false;
254
255 // Check each meta comparison for non empty diffs.
256 foreach ( $revisioned_meta_keys as $meta_key ) {
257 $meta_from = get_post_meta( $compare_from->ID, $meta_key, true );
258 $meta_to = get_post_meta( $compare_to->ID, $meta_key, true );
259
260 $args = array( 'show_split_view' => true );
261 $args = apply_filters( 'revision_text_diff_options', $args, end( $fields ), $compare_from, $compare_to );
262
263 $diff = $this->get_meta_diff( $meta_from, $meta_to, $args );
264
265 // Add this meta field if it has a diff.
266 if ( ! empty( $diff ) ) {
267
268 $new_field = array(
269 'id' => $meta_key,
270 'name' => $meta_key,
271 'diff' => $diff,
272 );
273
274 /**
275 * Filter revisioned meta fields used for the revisions UI.
276 *
277 * The dynamic portion of the hook name, `$meta_key`, refers to
278 * the revisioned meta key.
279 *
280 * @since 4.6.0
281 *
282 * @param object $new_field Object with id, name and diff for the UI.
283 * @param WP_Post $compare_from The revision post to compare from.
284 * @param WP_Post $compare_to The revision post to compare to.
285 */
286 $new_field = apply_filters( 'revisioned_meta_ui_field_{$meta_key}', $new_field, $compare_from, $compare_to );
287
288 $fields[ sizeof( $fields ) ] = $new_field;
289 }
290 }
291 }
292
293 return $fields;
294 }
295
296 /**
297 * Add the revisioned meta fields to the revisions interface.
298 *
299 * Include filters to enable customization of the meta display.
300 */
301 public function wp_add_meta_to_prepare_revision_for_js( $revisions_data, $revision, $post ) {
302
303 $revisions_data['revisionedMeta'] = array();
304
305 // Go thru revisioned meta fields, adding them to the display data.
306 foreach ( $this->wp_post_revision_meta_keys() as $meta_key ) {
307 $revisions_data['revisionedMeta'][] = array(
308 $meta_key => get_post_meta( $revisions_data['id'], $meta_key, true ),
309 );
310 }
311 return $revisions_data;
312 }
313 }
314 endif;
315
316