PluginProbe
Elementor Website Builder – more than just a page builder / 3.0.7
Elementor Website Builder – more than just a page builder v3.0.7
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / includes / db.php

db.php in Elementor Website Builder – more than just a page builder 3.0.7, at includes/db.php

555 lines 13.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Elementor;
3
4 use Elementor\Core\DynamicTags\Manager;
5
6 if ( ! defined( 'ABSPATH' ) ) {
7 exit; // Exit if accessed directly.
8 }
9
10 /**
11 * Elementor database.
12 *
13 * Elementor database handler class is responsible for communicating with the
14 * DB, save and retrieve Elementor data and meta data.
15 *
16 * @since 1.0.0
17 */
18 class DB {
19
20 /**
21 * Current DB version of the editor.
22 */
23 const DB_VERSION = '0.4';
24
25 /**
26 * Post publish status.
27 */
28 const STATUS_PUBLISH = 'publish';
29
30 /**
31 * Post draft status.
32 */
33 const STATUS_DRAFT = 'draft';
34
35 /**
36 * Post private status.
37 */
38 const STATUS_PRIVATE = 'private';
39
40 /**
41 * Post autosave status.
42 */
43 const STATUS_AUTOSAVE = 'autosave';
44
45 /**
46 * Post pending status.
47 */
48 const STATUS_PENDING = 'pending';
49
50 /**
51 * Switched post data.
52 *
53 * Holds the switched post data.
54 *
55 * @since 1.5.0
56 * @access protected
57 *
58 * @var array Switched post data. Default is an empty array.
59 */
60 protected $switched_post_data = [];
61
62 /**
63 * Switched data.
64 *
65 * Holds the switched data.
66 *
67 * @since 2.0.0
68 * @access protected
69 *
70 * @var array Switched data. Default is an empty array.
71 */
72 protected $switched_data = [];
73
74 /**
75 * Get builder.
76 *
77 * Retrieve editor data from the database.
78 *
79 * @since 1.0.0
80 *
81 * @access public
82 *
83 * @param int $post_id Post ID.
84 * @param string $status Optional. Post status. Default is `publish`.
85 *
86 * @return array Editor data.
87 */
88 public function get_builder( $post_id, $status = self::STATUS_PUBLISH ) {
89 if ( self::STATUS_DRAFT === $status ) {
90 $document = Plugin::$instance->documents->get_doc_or_auto_save( $post_id );
91 } else {
92 $document = Plugin::$instance->documents->get( $post_id );
93 }
94
95 if ( $document ) {
96 $editor_data = $document->get_elements_raw_data( null, true );
97 } else {
98 $editor_data = [];
99 }
100
101 return $editor_data;
102 }
103
104 /**
105 * Get JSON meta.
106 *
107 * Retrieve post meta data, and return the JSON decoded data.
108 *
109 * @since 1.0.0
110 * @access protected
111 *
112 * @param int $post_id Post ID.
113 * @param string $key The meta key to retrieve.
114 *
115 * @return array Decoded JSON data from post meta.
116 */
117 protected function _get_json_meta( $post_id, $key ) {
118 $meta = get_post_meta( $post_id, $key, true );
119
120 if ( is_string( $meta ) && ! empty( $meta ) ) {
121 $meta = json_decode( $meta, true );
122 }
123
124 if ( empty( $meta ) ) {
125 $meta = [];
126 }
127
128 return $meta;
129 }
130
131 /**
132 * Get new editor from WordPress editor.
133 *
134 * When editing the with Elementor the first time, the current page content
135 * is parsed into Text Editor Widget that contains the original data.
136 *
137 * @since 2.1.0
138 * @deprecated 2.3.0 Use `Plugin::$instance->documents->get( $post_id )->convert_to_elementor()` instead
139 * @access public
140 *
141 * @param int $post_id Post ID.
142 *
143 * @return array Content in Elementor format.
144 */
145 public function get_new_editor_from_wp_editor( $post_id ) {
146 _deprecated_function( __METHOD__, '2.3.0', 'Plugin::$instance->documents->get( $post_id )->convert_to_elementor()' );
147
148 $document = Plugin::$instance->documents->get( $post_id );
149
150 if ( $document ) {
151 return $document->convert_to_elementor();
152 }
153
154 return [];
155 }
156
157 /**
158 * Is using Elementor.
159 *
160 * Set whether the page is using Elementor or not.
161 *
162 * @since 1.5.0
163 * @access public
164 *
165 * @param int $post_id Post ID.
166 * @param bool $is_elementor Optional. Whether the page is elementor page.
167 * Default is true.
168 */
169 public function set_is_elementor_page( $post_id, $is_elementor = true ) {
170 if ( $is_elementor ) {
171 // Use the string `builder` and not a boolean for rollback compatibility
172 update_post_meta( $post_id, '_elementor_edit_mode', 'builder' );
173 } else {
174 delete_post_meta( $post_id, '_elementor_edit_mode' );
175 }
176 }
177
178 /**
179 * Render element plain content.
180 *
181 * When saving data in the editor, this method renders recursively the plain
182 * content containing only the content and the HTML. No CSS data.
183 *
184 * @since 2.0.0
185 * @access private
186 *
187 * @param array $element_data Element data.
188 */
189 private function render_element_plain_content( $element_data ) {
190 if ( 'widget' === $element_data['elType'] ) {
191 /** @var Widget_Base $widget */
192 $widget = Plugin::$instance->elements_manager->create_element_instance( $element_data );
193
194 if ( $widget ) {
195 $widget->render_plain_content();
196 }
197 }
198
199 if ( ! empty( $element_data['elements'] ) ) {
200 foreach ( $element_data['elements'] as $element ) {
201 $this->render_element_plain_content( $element );
202 }
203 }
204 }
205
206 /**
207 * Save plain text.
208 *
209 * Retrieves the raw content, removes all kind of unwanted HTML tags and saves
210 * the content as the `post_content` field in the database.
211 *
212 * @since 1.9.0
213 * @access public
214 *
215 * @param int $post_id Post ID.
216 */
217 public function save_plain_text( $post_id ) {
218 // Switch $dynamic_tags to parsing mode = remove.
219 $dynamic_tags = Plugin::$instance->dynamic_tags;
220 $parsing_mode = $dynamic_tags->get_parsing_mode();
221 $dynamic_tags->set_parsing_mode( Manager::MODE_REMOVE );
222
223 $plain_text = $this->get_plain_text( $post_id );
224
225 wp_update_post(
226 [
227 'ID' => $post_id,
228 'post_content' => $plain_text,
229 ]
230 );
231
232 // Restore parsing mode.
233 $dynamic_tags->set_parsing_mode( $parsing_mode );
234 }
235
236 /**
237 * Iterate data.
238 *
239 * Accept any type of Elementor data and a callback function. The callback
240 * function runs recursively for each element and his child elements.
241 *
242 * @since 1.0.0
243 * @access public
244 *
245 * @param array $data_container Any type of elementor data.
246 * @param callable $callback A function to iterate data by.
247 * @param array $args Array of args pointers for passing parameters in & out of the callback
248 *
249 * @return mixed Iterated data.
250 */
251 public function iterate_data( $data_container, $callback, $args = [] ) {
252 if ( isset( $data_container['elType'] ) ) {
253 if ( ! empty( $data_container['elements'] ) ) {
254 $data_container['elements'] = $this->iterate_data( $data_container['elements'], $callback, $args );
255 }
256
257 return call_user_func( $callback, $data_container, $args );
258 }
259
260 foreach ( $data_container as $element_key => $element_value ) {
261 $element_data = $this->iterate_data( $data_container[ $element_key ], $callback, $args );
262
263 if ( null === $element_data ) {
264 continue;
265 }
266
267 $data_container[ $element_key ] = $element_data;
268 }
269
270 return $data_container;
271 }
272
273 /**
274 * Safely copy Elementor meta.
275 *
276 * Make sure the original page was built with Elementor and the post is not
277 * auto-save. Only then copy elementor meta from one post to another using
278 * `copy_elementor_meta()`.
279 *
280 * @since 1.9.2
281 * @access public
282 *
283 * @param int $from_post_id Original post ID.
284 * @param int $to_post_id Target post ID.
285 */
286 public function safe_copy_elementor_meta( $from_post_id, $to_post_id ) {
287 // It's from WP-Admin & not from Elementor.
288 if ( ! did_action( 'elementor/db/before_save' ) ) {
289
290 if ( ! Plugin::$instance->db->is_built_with_elementor( $from_post_id ) ) {
291 return;
292 }
293
294 // It's an exited Elementor auto-save
295 if ( get_post_meta( $to_post_id, '_elementor_data', true ) ) {
296 return;
297 }
298 }
299
300 $this->copy_elementor_meta( $from_post_id, $to_post_id );
301 }
302
303 /**
304 * Copy Elementor meta.
305 *
306 * Duplicate the data from one post to another.
307 *
308 * Consider using `safe_copy_elementor_meta()` method instead.
309 *
310 * @since 1.1.0
311 * @access public
312 *
313 * @param int $from_post_id Original post ID.
314 * @param int $to_post_id Target post ID.
315 */
316 public function copy_elementor_meta( $from_post_id, $to_post_id ) {
317 $from_post_meta = get_post_meta( $from_post_id );
318 $core_meta = [
319 '_wp_page_template',
320 '_thumbnail_id',
321 ];
322
323 foreach ( $from_post_meta as $meta_key => $values ) {
324 // Copy only meta with the `_elementor` prefix
325 if ( 0 === strpos( $meta_key, '_elementor' ) || in_array( $meta_key, $core_meta, true ) ) {
326 $value = $values[0];
327
328 // The elementor JSON needs slashes before saving
329 if ( '_elementor_data' === $meta_key ) {
330 $value = wp_slash( $value );
331 } else {
332 $value = maybe_unserialize( $value );
333 }
334
335 // Don't use `update_post_meta` that can't handle `revision` post type
336 update_metadata( 'post', $to_post_id, $meta_key, $value );
337 }
338 }
339 }
340
341 /**
342 * Is built with Elementor.
343 *
344 * Check whether the post was built with Elementor.
345 *
346 * @since 1.0.10
347 * @access public
348 *
349 * @param int $post_id Post ID.
350 *
351 * @return bool Whether the post was built with Elementor.
352 */
353 public function is_built_with_elementor( $post_id ) {
354 return ! ! get_post_meta( $post_id, '_elementor_edit_mode', true );
355 }
356
357 /**
358 * Switch to post.
359 *
360 * Change the global WordPress post to the requested post.
361 *
362 * @since 1.5.0
363 * @access public
364 *
365 * @param int $post_id Post ID to switch to.
366 */
367 public function switch_to_post( $post_id ) {
368 $post_id = absint( $post_id );
369 // If is already switched, or is the same post, return.
370 if ( get_the_ID() === $post_id ) {
371 $this->switched_post_data[] = false;
372 return;
373 }
374
375 $this->switched_post_data[] = [
376 'switched_id' => $post_id,
377 'original_id' => get_the_ID(), // Note, it can be false if the global isn't set
378 ];
379
380 $GLOBALS['post'] = get_post( $post_id ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
381
382 setup_postdata( $GLOBALS['post'] );
383 }
384
385 /**
386 * Restore current post.
387 *
388 * Rollback to the previous global post, rolling back from `DB::switch_to_post()`.
389 *
390 * @since 1.5.0
391 * @access public
392 */
393 public function restore_current_post() {
394 $data = array_pop( $this->switched_post_data );
395
396 // If not switched, return.
397 if ( ! $data ) {
398 return;
399 }
400
401 // It was switched from an empty global post, restore this state and unset the global post
402 if ( false === $data['original_id'] ) {
403 unset( $GLOBALS['post'] );
404 return;
405 }
406
407 $GLOBALS['post'] = get_post( $data['original_id'] ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
408
409 setup_postdata( $GLOBALS['post'] );
410 }
411
412
413 /**
414 * Switch to query.
415 *
416 * Change the WordPress query to a new query with the requested
417 * query variables.
418 *
419 * @since 2.0.0
420 * @access public
421 *
422 * @param array $query_vars New query variables.
423 * @param bool $force_global_post
424 */
425 public function switch_to_query( $query_vars, $force_global_post = false ) {
426 global $wp_query;
427 $current_query_vars = $wp_query->query;
428
429 // If is already switched, or is the same query, return.
430 if ( $current_query_vars === $query_vars ) {
431 $this->switched_data[] = false;
432 return;
433 }
434
435 $new_query = new \WP_Query( $query_vars );
436
437 $switched_data = [
438 'switched' => $new_query,
439 'original' => $wp_query,
440 ];
441
442 if ( ! empty( $GLOBALS['post'] ) ) {
443 $switched_data['post'] = $GLOBALS['post'];
444 }
445
446 $this->switched_data[] = $switched_data;
447
448 $wp_query = $new_query; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
449
450 // Ensure the global post is set only if needed
451 unset( $GLOBALS['post'] );
452
453 if ( isset( $new_query->posts[0] ) ) {
454 if ( $force_global_post || $new_query->is_singular() ) {
455 $GLOBALS['post'] = $new_query->posts[0]; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
456 setup_postdata( $GLOBALS['post'] );
457 }
458 }
459
460 if ( $new_query->is_author() ) {
461 $GLOBALS['authordata'] = get_userdata( $new_query->get( 'author' ) ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
462 }
463 }
464
465 /**
466 * Restore current query.
467 *
468 * Rollback to the previous query, rolling back from `DB::switch_to_query()`.
469 *
470 * @since 2.0.0
471 * @access public
472 */
473 public function restore_current_query() {
474 $data = array_pop( $this->switched_data );
475
476 // If not switched, return.
477 if ( ! $data ) {
478 return;
479 }
480
481 global $wp_query;
482
483 $wp_query = $data['original']; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
484
485 // Ensure the global post/authordata is set only if needed.
486 unset( $GLOBALS['post'] );
487 unset( $GLOBALS['authordata'] );
488
489 if ( ! empty( $data['post'] ) ) {
490 $GLOBALS['post'] = $data['post']; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
491 setup_postdata( $GLOBALS['post'] );
492 }
493
494 if ( $wp_query->is_author() ) {
495 $GLOBALS['authordata'] = get_userdata( $wp_query->get( 'author' ) ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
496 }
497 }
498
499 /**
500 * Get plain text.
501 *
502 * Retrieve the post plain text.
503 *
504 * @since 1.9.0
505 * @access public
506 *
507 * @param int $post_id Post ID.
508 *
509 * @return string Post plain text.
510 */
511 public function get_plain_text( $post_id ) {
512 $document = Plugin::$instance->documents->get( $post_id );
513 $data = $document ? $document->get_elements_data() : [];
514
515 return $this->get_plain_text_from_data( $data );
516 }
517
518 /**
519 * Get plain text from data.
520 *
521 * Retrieve the post plain text from any given Elementor data.
522 *
523 * @since 1.9.2
524 * @access public
525 *
526 * @param array $data Post ID.
527 *
528 * @return string Post plain text.
529 */
530 public function get_plain_text_from_data( $data ) {
531 ob_start();
532 if ( $data ) {
533 foreach ( $data as $element_data ) {
534 $this->render_element_plain_content( $element_data );
535 }
536 }
537
538 $plain_text = ob_get_clean();
539
540 // Remove unnecessary tags.
541 $plain_text = preg_replace( '/<\/?div[^>]*\>/i', '', $plain_text );
542 $plain_text = preg_replace( '/<\/?span[^>]*\>/i', '', $plain_text );
543 $plain_text = preg_replace( '#<script(.*?)>(.*?)</script>#is', '', $plain_text );
544 $plain_text = preg_replace( '/<i [^>]*><\\/i[^>]*>/', '', $plain_text );
545 $plain_text = preg_replace( '/ class=".*?"/', '', $plain_text );
546
547 // Remove empty lines.
548 $plain_text = preg_replace( '/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/', "\n", $plain_text );
549
550 $plain_text = trim( $plain_text );
551
552 return $plain_text;
553 }
554 }
555