PluginProbe
Insert Pages / 3.9.3
Insert Pages v3.9.3
3.11.5 trunk 1.4 2.4 2.5 2.6 2.7 2.7.1 2.7.2 2.8 2.9 2.9.1 3.0 3.0.1 3.0.2 3.1 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 All 79 releases
insert-pages / insert-pages.php

insert-pages.php in Insert Pages 3.9.3, at insert-pages.php

1,890 lines 71.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: Insert Pages
4 * Plugin URI: https://github.com/uhm-coe/insert-pages
5 * Description: Insert Pages lets you embed any WordPress content (e.g., pages, posts, custom post types) into other WordPress content using the Shortcode API.
6 * Author: Paul Ryan
7 * Text Domain: insert-pages
8 * Domain Path: /languages
9 * License: GPL2
10 * Requires at least: 3.3.0
11 * Version: 3.9.3
12 *
13 * @package insert-pages
14 */
15
16 /*
17 Copyright 2011 Paul Ryan (email: prar@hawaii.edu)
18
19 This program is free software; you can redistribute it and/or modify
20 it under the terms of the GNU General Public License, version 2, as
21 published by the Free Software Foundation.
22
23 This program is distributed in the hope that it will be useful,
24 but WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 GNU General Public License for more details.
27
28 You should have received a copy of the GNU General Public License
29 along with this program; if not, write to the Free Software
30 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
31 */
32
33 /**
34 * Shortcode Format:
35 * [insert page='{slug}|{id}|{url}' display='title|link|excerpt|excerpt-only|content|title-content|post-thumbnail|all|{custom-template.php}' class='any-classes' id='any-id' [inline] [public] querystring='{url-encoded-values}' size='post-thumbnail|thumbnail|medium|large|full|{custom-size}']
36 */
37
38 if ( ! class_exists( 'InsertPagesPlugin' ) ) {
39 /**
40 * Class InsertPagesPlugin
41 */
42 class InsertPagesPlugin {
43 /**
44 * Stack tracking inserted pages (for loop detection).
45 *
46 * @var array Array of page ids inserted.
47 */
48 protected $inserted_page_ids;
49
50 /**
51 * Flag to only render the TinyMCE plugin dialog once.
52 *
53 * @var boolean
54 */
55 private static $link_dialog_printed = false;
56
57 /**
58 * Flag checked when rendering TinyMCE modal to ensure that required scripts
59 * and styles were enqueued (normally done in `admin_init` hook).
60 *
61 * @var boolean
62 */
63 private static $is_admin_initialized = false;
64
65 /**
66 * Singleton plugin instance.
67 *
68 * @var object Plugin instance.
69 */
70 protected static $instance = null;
71
72
73 /**
74 * Access this plugin's working instance.
75 *
76 * @return object Object of this class.
77 */
78 public static function get_instance() {
79 if ( null === self::$instance ) {
80 self::$instance = new self();
81 }
82
83 return self::$instance;
84 }
85
86
87 /**
88 * Disable constructor to enforce a single plugin instance..
89 */
90 protected function __construct() {
91 }
92
93
94 /**
95 * Action hook: WordPress 'init'.
96 *
97 * @return void
98 */
99 public function insert_pages_init() {
100 $options = get_option( 'wpip_settings' );
101
102 // Register the [insert] shortcode.
103 add_shortcode( 'insert', array( $this, 'insert_pages_handle_shortcode_insert' ) );
104
105 // Register the gutenberg block so we can populate it via server side
106 // rendering. Note: only register it once (some plugins, like Advanced
107 // Custom Fields, create a scenario where this init hook gets called
108 // multiple times).
109 if (
110 function_exists( 'register_block_type' ) &&
111 isset( $options['wpip_gutenberg_block'] ) &&
112 'enabled' === $options['wpip_gutenberg_block'] &&
113 class_exists( 'WP_Block_Type_Registry' ) &&
114 ! WP_Block_Type_Registry::get_instance()->is_registered( 'insert-pages/block' )
115 ) {
116 // Automatically load dependencies and version.
117 $asset_file = include plugin_dir_path( __FILE__ ) . 'lib/gutenberg-block/build/index.asset.php';
118
119 wp_register_script(
120 'insert-pages-gutenberg-block',
121 plugins_url( 'lib/gutenberg-block/build/index.js', __FILE__ ),
122 $asset_file['dependencies'],
123 $asset_file['version'],
124 array(
125 'in_footer' => false,
126 )
127 );
128
129 wp_register_style(
130 'insert-pages-gutenberg-block',
131 plugins_url( 'lib/gutenberg-block/build/index.css', __FILE__ ),
132 array(),
133 $asset_file['version']
134 );
135
136 register_block_type(
137 'insert-pages/block',
138 array(
139 'editor_style' => 'insert-pages-gutenberg-block',
140 'editor_script' => 'insert-pages-gutenberg-block',
141 'attributes' => array(
142 'url' => array(
143 'type' => 'string',
144 'default' => '',
145 ),
146 'page' => array(
147 'type' => 'number',
148 'default' => 0,
149 ),
150 'display' => array(
151 'type' => 'string',
152 'default' => 'title',
153 ),
154 'template' => array(
155 'type' => 'string',
156 'default' => '',
157 ),
158 'class' => array(
159 'type' => 'string',
160 'default' => '',
161 ),
162 'id' => array(
163 'type' => 'string',
164 'default' => '',
165 ),
166 'inline' => array(
167 'type' => 'bool',
168 'default' => false,
169 ),
170 'public' => array(
171 'type' => 'bool',
172 'default' => false,
173 ),
174 'querystring' => array(
175 'type' => 'string',
176 'default' => '',
177 ),
178 'size' => array(
179 'type' => 'string',
180 'default' => '',
181 ),
182 ),
183 'render_callback' => array( $this, 'block_render_callback' ),
184 )
185 );
186 }
187 }
188
189
190 /**
191 * Renders the gutenberg block (using legacy server-side rendering).
192 *
193 * @param array $attr Array of block attributes.
194 * @return string Rendered inserted page.
195 */
196 public function block_render_callback( $attr ) {
197 // Display attribute defaults to 'title'; otherwise it is the passed param,
198 // and if the display param is 'custom', it is the value of the 'template'
199 // param.
200 $display = 'title';
201 if ( isset( $attr['display'] ) && strlen( $attr['display'] ) > 0 ) {
202 $display = esc_attr( $attr['display'] );
203 }
204 if ( 'custom' === $display && isset( $attr['template'] ) && strlen( $attr['template'] ) > 0 ) {
205 $display = esc_attr( $attr['template'] );
206 }
207
208 $shortcode = sprintf(
209 '[insert page="%1$s" display="%2$s"%3$s%4$s%5$s%6$s%7$s%8$s]',
210 isset( $attr['page'] ) && strlen( $attr['page'] ) > 0 ? esc_attr( $attr['page'] ) : '0',
211 $display,
212 isset( $attr['class'] ) && strlen( $attr['class'] ) > 0 ? ' class="' . esc_attr( $attr['class'] ) . '"' : '',
213 isset( $attr['id'] ) && strlen( $attr['id'] ) > 0 ? ' id="' . esc_attr( $attr['id'] ) . '"' : '',
214 isset( $attr['querystring'] ) && strlen( $attr['querystring'] ) > 0 ? ' querystring="' . esc_attr( $attr['querystring'] ) . '"' : '',
215 isset( $attr['size'] ) && strlen( $attr['size'] ) > 0 ? ' size="' . esc_attr( $attr['size'] ) . '"' : '',
216 isset( $attr['inline'] ) && 'true' === $attr['inline'] ? ' inline' : '',
217 isset( $attr['public'] ) && 'true' === $attr['public'] ? ' public' : ''
218 );
219
220 $rendered_shortcode = do_shortcode( $shortcode );
221
222 // If we're in the block editor, enqueue any layout styles for blocks
223 // (normally this is done in core but since we're not in the main context,
224 // we need to do so manually). For example, the Grid block uses layout
225 // styles to set the number of columns.
226 // See: https://github.com/WordPress/WordPress/blob/6.6.2/wp-includes/block-supports/layout.php#L539-L551.
227 // See: https://developer.wordpress.org/reference/functions/wp_style_engine_get_stylesheet_from_css_rules/.
228 // See: https://developer.wordpress.org/reference/functions/wp_add_inline_style/.
229 $current_screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
230 if (
231 ! empty( $current_screen ) && $current_screen->is_block_editor() &&
232 ! in_array( $display, array( 'title', 'link', 'post-thumbnail' ), true ) &&
233 function_exists( 'wp_style_engine_get_stylesheet_from_context' )
234 ) {
235 $layout_styles = wp_style_engine_get_stylesheet_from_context( 'block-supports' );
236 if ( ! empty( $layout_styles ) ) {
237 wp_add_inline_style( 'wp-block-library', $layout_styles );
238 }
239 }
240
241 return $rendered_shortcode;
242 }
243
244
245 /**
246 * Load gutenberg block resources only when editing (only if Gutenberg block
247 * setting is enabled in Insert Pages settings).
248 *
249 * Action hook: enqueue_block_editor_assets
250 *
251 * @return void
252 */
253 public function insert_pages_enqueue_block_editor_assets() {
254 $options = get_option( 'wpip_settings' );
255 if ( isset( $options['wpip_gutenberg_block'] ) && 'enabled' === $options['wpip_gutenberg_block'] ) {
256 }
257 }
258
259
260 /**
261 * Action hook: WordPress 'admin_init'.
262 *
263 * @return void
264 */
265 public function insert_pages_admin_init() {
266 // Get options set in WordPress dashboard (Settings > Insert Pages).
267 $options = get_option( 'wpip_settings' );
268 if ( false === $options || ! is_array( $options ) || ! array_key_exists( 'wpip_format', $options ) || ! array_key_exists( 'wpip_wrapper', $options ) || ! array_key_exists( 'wpip_insert_method', $options ) || ! array_key_exists( 'wpip_tinymce_filter', $options ) ) {
269 $options = wpip_set_defaults();
270 }
271
272 // Register the TinyMCE toolbar button script.
273 wp_enqueue_script(
274 'wpinsertpages',
275 plugins_url( '/js/wpinsertpages.js', __FILE__ ),
276 array( 'wpdialogs' ),
277 '20221216',
278 false
279 );
280 wp_localize_script(
281 'wpinsertpages',
282 'wpInsertPagesL10n',
283 array(
284 'update' => __( 'Update', 'insert-pages' ),
285 'save' => __( 'Insert Page', 'insert-pages' ),
286 'noTitle' => __( '(no title)', 'insert-pages' ),
287 'noMatchesFound' => __( 'No matches found.', 'insert-pages' ),
288 'l10n_print_after' => 'try{convertEntities(wpInsertPagesL10n);}catch(e){};',
289 'format' => $options['wpip_format'],
290 'private' => __( 'Private', 'insert-pages' ),
291 'tinymce_state' => $this->get_tinymce_state(),
292 )
293 );
294
295 // Register the TinyMCE toolbar button styles.
296 wp_enqueue_style(
297 'wpinsertpagescss',
298 plugins_url( '/css/wpinsertpages.css', __FILE__ ),
299 array( 'wp-jquery-ui-dialog' ),
300 '20221216'
301 );
302
303 /**
304 * Register TinyMCE plugin for the toolbar button in normal mode (register
305 * TinyMCE plugin filters below before plugins_loaded in compatibility
306 * mode, to work around a SiteOrigin PageBuilder bug).
307 *
308 * @see https://wordpress.org/support/topic/button-in-the-toolbar-of-tinymce-disappear-conflict-page-builder/
309 */
310 if ( 'normal' === $options['wpip_tinymce_filter'] ) {
311 add_filter( 'mce_external_plugins', array( $this, 'insert_pages_handle_filter_mce_external_plugins' ) );
312 add_filter( 'mce_buttons', array( $this, 'insert_pages_handle_filter_mce_buttons' ) );
313 }
314
315 // Load the translations.
316 load_plugin_textdomain(
317 'insert-pages',
318 false,
319 plugin_basename( __DIR__ ) . '/languages'
320 );
321
322 self::$is_admin_initialized = true;
323 }
324
325
326 /**
327 * Shortcode hook: Replace the [insert ...] shortcode with the inserted page's content.
328 *
329 * @param array $atts Shortcode attributes.
330 * @param string $content Content to replace shortcode.
331 * @return string Content to replace shortcode.
332 */
333 public function insert_pages_handle_shortcode_insert( $atts, $content = null ) {
334 global $wp_query, $post, $wp_current_filter;
335
336 // Shortcode attributes.
337 $attributes = shortcode_atts(
338 array(
339 'page' => '0',
340 'display' => 'all',
341 'class' => '',
342 'id' => '',
343 'querystring' => '',
344 'size' => '',
345 'inline' => false,
346 'public' => false,
347 ),
348 $atts,
349 'insert'
350 );
351
352 // Validation checks.
353 if ( '0' === $attributes['page'] ) {
354 return $content;
355 }
356
357 // Short circuit if trying to embed same page in itself.
358 if (
359 ( is_object( $post ) && property_exists( $post, 'ID' ) && intval( $attributes['page'] ) === $post->ID ) ||
360 ( is_object( $post ) && property_exists( $post, 'post_name' ) && $attributes['page'] === $post->post_name ) ||
361 ( is_int( $post ) && intval( $attributes['page'] ) === $post )
362 ) {
363 return $content;
364 }
365
366 // Get options set in WordPress dashboard (Settings > Insert Pages).
367 $options = get_option( 'wpip_settings' );
368 if ( false === $options || ! is_array( $options ) || ! array_key_exists( 'wpip_format', $options ) || ! array_key_exists( 'wpip_wrapper', $options ) || ! array_key_exists( 'wpip_insert_method', $options ) || ! array_key_exists( 'wpip_tinymce_filter', $options ) ) {
369 $options = wpip_set_defaults();
370 }
371
372 $attributes['inline'] = ( false !== $attributes['inline'] && 'false' !== $attributes['inline'] ) || array_search( 'inline', $atts, true ) === 0 || ( array_key_exists( 'wpip_wrapper', $options ) && 'inline' === $options['wpip_wrapper'] );
373 /**
374 * Filter the flag indicating whether to wrap the inserted content in inline tags (span).
375 *
376 * @param bool $use_inline_wrapper Indicates whether to wrap the content in span tags.
377 */
378 $attributes['inline'] = apply_filters( 'insert_pages_use_inline_wrapper', $attributes['inline'] );
379 $attributes['wrapper_tag'] = $attributes['inline'] ? 'span' : 'div';
380
381 $attributes['public'] = ( false !== $attributes['public'] && 'false' !== $attributes['public'] ) || array_search( 'public', $atts, true ) === 0 || is_user_logged_in();
382
383 /**
384 * Filter the querystring values applied to every inserted page. Useful
385 * for admins who want to provide the same querystring value to all
386 * inserted pages sitewide.
387 *
388 * @since 3.2.9
389 *
390 * @param string $querystring The querystring value for the inserted page.
391 */
392 $attributes['querystring'] = apply_filters(
393 'insert_pages_override_querystring',
394 str_replace( '{', '[', str_replace( '}', ']', htmlspecialchars_decode( $attributes['querystring'] ) ) )
395 );
396
397 $attributes['should_apply_the_content_filter'] = true;
398 /**
399 * Filter the flag indicating whether to apply the_content filter to post
400 * contents and excerpts that are being inserted.
401 *
402 * @param bool $apply_the_content_filter Indicates whether to apply the_content filter.
403 */
404 $attributes['should_apply_the_content_filter'] = apply_filters( 'insert_pages_apply_the_content_filter', $attributes['should_apply_the_content_filter'] );
405
406 // Disable the_content filter if using inline tags, since wpautop
407 // inserts p tags and we can't have any inside inline elements.
408 if ( $attributes['inline'] ) {
409 $attributes['should_apply_the_content_filter'] = false;
410 }
411
412 /**
413 * Filter the chosen display method, where display can be one of:
414 * title, link, excerpt, excerpt-only, content, post-thumbnail, all, {custom-template.php}
415 * Useful for admins who want to restrict the display sitewide.
416 *
417 * @since 3.2.7
418 *
419 * @param string $display The display method for the inserted page.
420 */
421 $attributes['display'] = apply_filters( 'insert_pages_override_display', $attributes['display'] );
422
423 // If a URL is provided, translate it to a post ID.
424 if ( filter_var( $attributes['page'], FILTER_VALIDATE_URL ) ) {
425 $attributes['page'] = url_to_postid( $attributes['page'] );
426 }
427
428 // Get the WP_Post object from the provided slug, or ID.
429 if ( ! is_numeric( $attributes['page'] ) ) {
430 // Get list of post types that can be inserted (page, post, custom
431 // types), excluding builtin types (nav_menu_item, attachment).
432 $insertable_post_types = array_filter(
433 get_post_types(),
434 array( $this, 'is_post_type_insertable' )
435 );
436 $inserted_page = get_page_by_path( $attributes['page'], OBJECT, $insertable_post_types );
437
438 // If get_page_by_path() didn't find the page, check to see if the slug
439 // was provided instead of the full path (useful for hierarchical pages
440 // that are nested under another page).
441 if ( is_null( $inserted_page ) ) {
442 global $wpdb;
443 $page = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery
444 $wpdb->prepare(
445 "SELECT ID FROM $wpdb->posts WHERE post_name = %s AND (post_status = 'publish' OR post_status = 'private') LIMIT 1",
446 $attributes['page']
447 )
448 );
449 if ( $page ) {
450 $inserted_page = get_post( $page );
451 }
452 }
453
454 $attributes['page'] = $inserted_page ? $inserted_page->ID : $attributes['page'];
455 } else {
456 $inserted_page = get_post( intval( $attributes['page'] ) );
457 }
458
459 // Integration: If WPML is enabled, ensure the inserted page matches the
460 // language of the parent page.
461 if ( is_object( $inserted_page ) && class_exists( 'Sitepress' ) ) {
462 $translated_inserted_page = apply_filters( 'wpml_object_id', $inserted_page->ID, 'any' );
463 if ( ! empty( $translated_inserted_page ) && intval( $translated_inserted_page ) !== $inserted_page->ID ) {
464 $inserted_page = get_post( intval( $translated_inserted_page ) );
465 }
466 }
467
468 // Prevent unprivileged users from inserting private posts from others.
469 if ( is_object( $inserted_page ) && 'publish' !== $inserted_page->post_status ) {
470 $post_type = get_post_type_object( $inserted_page->post_type );
471 $parent_post_author_id = intval( get_the_author_meta( 'ID' ) );
472 if ( ! user_can( $parent_post_author_id, $post_type->cap->read_post, $inserted_page->ID ) ) {
473 $inserted_page = null;
474 }
475 }
476
477 // If inserted page's status is private, don't show to anonymous users
478 // unless 'public' option is set.
479 if ( is_object( $inserted_page ) && 'private' === $inserted_page->post_status && ! $attributes['public'] ) {
480 $inserted_page = null;
481 }
482
483 // Integration: if Simple Membership plugin is used, check that the
484 // current user has permission to see the inserted post.
485 // See: https://simple-membership-plugin.com/simple-membership-miscellaneous-php-tweaks/
486 if ( class_exists( 'SwpmAccessControl' ) ) {
487 $access_ctrl = SwpmAccessControl::get_instance();
488 if ( ! $access_ctrl->can_i_read_post( $inserted_page ) && ! current_user_can( 'edit_files' ) ) {
489 $inserted_page = null;
490 $content = wp_kses_post( $access_ctrl->why() );
491 }
492 }
493
494 // Integration: if the Otter Blocks plugin is active, enqueue any assets
495 // for blocks in the inserted page.
496 // See: https://github.com/Codeinwp/otter-blocks/blob/master/inc/css/class-block-frontend.php#L662.
497 add_filter(
498 'themeisle_gutenberg_blocks_enqueue_assets',
499 function ( $posts ) use ( $inserted_page ) {
500 if ( ! empty( $inserted_page ) ) {
501 $posts[] = $inserted_page;
502 }
503
504 return $posts;
505 }
506 );
507
508 // Loop detection: check if the page we are inserting has already been
509 // inserted; if so, short circuit here.
510 if ( ! is_array( $this->inserted_page_ids ) ) {
511 // Initialize stack to the main page that contains inserted page(s).
512 $this->inserted_page_ids = array( get_the_ID() );
513 }
514 if ( isset( $inserted_page->ID ) ) {
515 if ( ! in_array( $inserted_page->ID, $this->inserted_page_ids, true ) ) {
516 // Add the page being inserted to the stack.
517 $this->inserted_page_ids[] = $inserted_page->ID;
518 } else {
519 // Loop detected, so exit without rendering this post.
520 return $content;
521 }
522 }
523
524 // Set any querystring params included in the shortcode.
525 parse_str( $attributes['querystring'], $querystring );
526 $original_get = $_GET; // phpcs:ignore WordPress.Security.NonceVerification
527 $original_request = $_REQUEST; // phpcs:ignore WordPress.Security.NonceVerification
528 foreach ( $querystring as $param => $value ) {
529 $_GET[ $param ] = $value;
530 $_REQUEST[ $param ] = $value;
531 }
532 $original_wp_query_vars = $GLOBALS['wp']->query_vars;
533 if (
534 ! empty( $querystring ) &&
535 isset( $GLOBALS['wp'] ) &&
536 method_exists( $GLOBALS['wp'], 'parse_request' ) &&
537 empty( $GLOBALS['wp']->query_vars['rest_route'] )
538 ) {
539 $GLOBALS['wp']->parse_request( $querystring );
540 }
541
542 // Use "Normal" insert method (get_post).
543 if ( 'legacy' !== $options['wpip_insert_method'] ) {
544
545 // If we couldn't retrieve the page, fire the filter hook showing a not-found message.
546 if ( null === $inserted_page ) {
547 /**
548 * Filter the html that should be displayed if an inserted page was not found.
549 *
550 * @param string $content html to be displayed. Defaults to an empty string.
551 */
552 $content = apply_filters( 'insert_pages_not_found_message', $content );
553
554 // Short-circuit since we didn't find the page.
555 return $content;
556 }
557
558 // Start output buffering so we can save the output to a string.
559 ob_start();
560
561 // If Beaver Builder, SiteOrigin Page Builder, Elementor, or WPBakery
562 // Page Builder (Visual Composer) are enabled, load any cached styles
563 // associated with the inserted page.
564 // Note: Temporarily set the global $post->ID to the inserted page ID,
565 // since both builders rely on the id to load the appropriate styles.
566 if (
567 class_exists( 'UAGB_Post_Assets' ) ||
568 class_exists( 'FLBuilder' ) ||
569 class_exists( 'SiteOrigin_Panels' ) ||
570 class_exists( '\Elementor\Post_CSS_File' ) ||
571 defined( 'VCV_VERSION' ) ||
572 defined( 'WPB_VC_VERSION' )
573 ) {
574 // If we're not in The Loop (i.e., global $post isn't assigned),
575 // temporarily populate it with the post to be inserted so we can
576 // retrieve generated styles for that post. Reset $post to null
577 // after we're done.
578 if ( is_null( $post ) ) {
579 $old_post_id = null;
580 $post = $inserted_page; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
581 } elseif ( is_int( $post ) ) {
582 $old_post_id = $post;
583 $post = $inserted_page->ID;
584 } else {
585 $old_post_id = $post->ID;
586 $post->ID = $inserted_page->ID;
587 }
588
589 // Enqueue assets for Ultimate Addons for Gutenberg.
590 // See: https://ultimategutenberg.com/docs/assets-api-third-party-plugins/.
591 if ( class_exists( 'UAGB_Post_Assets' ) ) {
592 $post_assets_instance = new UAGB_Post_Assets( $inserted_page->ID );
593 $post_assets_instance->enqueue_scripts();
594 }
595
596 if ( class_exists( 'FLBuilder' ) ) {
597 FLBuilder::enqueue_layout_styles_scripts( $inserted_page->ID );
598 }
599
600 if ( class_exists( 'SiteOrigin_Panels' ) ) {
601 $renderer = SiteOrigin_Panels::renderer();
602 $renderer->add_inline_css( $inserted_page->ID, $renderer->generate_css( $inserted_page->ID ) );
603 }
604
605 if ( class_exists( '\Elementor\Post_CSS_File' ) ) {
606 $css_file = new \Elementor\Post_CSS_File( $inserted_page->ID );
607 $css_file->enqueue();
608 }
609
610 // Enqueue custom style from WPBakery Page Builder (Visual Composer).
611 if ( defined( 'VCV_VERSION' ) ) {
612 wp_enqueue_style( 'vcv:assets:front:style' );
613 wp_enqueue_script( 'vcv:assets:runtime:script' );
614 wp_enqueue_script( 'vcv:assets:front:script' );
615
616 $bundle_url = get_post_meta( $inserted_page->ID, 'vcvSourceCssFileUrl', true );
617 if ( $bundle_url ) {
618 $version = get_post_meta( $inserted_page->ID, 'vcvSourceCssFileHash', true );
619 if ( ! preg_match( '/^http/', $bundle_url ) ) {
620 if ( ! preg_match( '/assets-bundles/', $bundle_url ) ) {
621 $bundle_url = '/assets-bundles/' . $bundle_url;
622 }
623 }
624 if ( preg_match( '/^http/', $bundle_url ) ) {
625 $bundle_url = set_url_scheme( $bundle_url );
626 } elseif ( defined( 'VCV_TF_ASSETS_IN_UPLOADS' ) && constant( 'VCV_TF_ASSETS_IN_UPLOADS' ) ) {
627 $upload_dir = wp_upload_dir();
628 $bundle_url = set_url_scheme( $upload_dir['baseurl'] . '/' . VCV_PLUGIN_ASSETS_DIRNAME . '/' . ltrim( $bundle_url, '/\\' ) );
629 } elseif ( class_exists( 'VisualComposer\Helpers\AssetsEnqueue' ) ) {
630 // These methods should work for Visual Composer 26.0.
631 // Enqueue custom css/js stored in vcvSourceAssetsFiles postmeta.
632 $vc = new \VisualComposer\Helpers\AssetsEnqueue;
633 if ( method_exists( $vc, 'enqueueAssets' ) ) {
634 $vc->enqueueAssets($inserted_page->ID);
635 }
636 // Enqueue custom CSS stored in vcvSourceCssFileUrl postmeta.
637 $upload_dir = wp_upload_dir();
638 $bundle_url = set_url_scheme( $upload_dir['baseurl'] . '/' . VCV_PLUGIN_ASSETS_DIRNAME . '/' . ltrim( $bundle_url, '/\\' ) );
639 } else {
640 $bundle_url = content_url() . '/' . VCV_PLUGIN_ASSETS_DIRNAME . '/' . ltrim( $bundle_url, '/\\' );
641 }
642 wp_enqueue_style(
643 'vcv:assets:source:main:styles:' . sanitize_title( $bundle_url ),
644 $bundle_url,
645 array(),
646 VCV_VERSION . '.' . $version
647 );
648 }
649 }
650
651 // Visual Composer custom CSS.
652 if ( defined( 'WPB_VC_VERSION' ) ) {
653 // Post custom CSS.
654 $post_custom_css = get_post_meta( $inserted_page->ID, '_wpb_post_custom_css', true );
655 if ( ! empty( $post_custom_css ) ) {
656 echo '<style type="text/css" data-type="vc_custom-css">';
657 echo esc_html( wp_strip_all_tags( $post_custom_css ) );
658 echo '</style>';
659 }
660 // Shortcodes custom CSS.
661 $shortcodes_custom_css = get_post_meta( $inserted_page->ID, '_wpb_shortcodes_custom_css', true );
662 if ( ! empty( $shortcodes_custom_css ) ) {
663 echo '<style type="text/css" data-type="vc_shortcodes-custom-css">';
664 echo esc_html( wp_strip_all_tags( $shortcodes_custom_css ) );
665 echo '</style>';
666 }
667 }
668
669 // GoodLayers page builder content (retrieved from post meta).
670 // See: https://docs.goodlayers.com/add-page-builder-in-product/.
671 do_action( 'gdlr_core_print_page_builder' );
672
673 if ( is_null( $old_post_id ) ) {
674 $post = null; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
675 } else {
676 $post->ID = $old_post_id;
677 }
678 }
679
680 /**
681 * Show either the title, link, content, everything, or everything via a
682 * custom template.
683 *
684 * Note: if the sharing_display filter exists, it means Jetpack is
685 * installed and Sharing is enabled; this plugin conflicts with Sharing,
686 * because Sharing assumes the_content and the_excerpt filters are only
687 * getting called once. The fix here is to disable processing of filters
688 * on the_content in the inserted page.
689 *
690 * @see https://codex.wordpress.org/Function_Reference/the_content#Alternative_Usage
691 */
692 switch ( $attributes['display'] ) {
693 case 'title':
694 $title_tag = $attributes['inline'] ? 'span' : 'h1';
695 echo wp_kses_post( "<$title_tag class='insert-page-title'>" );
696 echo esc_html( get_the_title( $inserted_page->ID ) );
697 echo wp_kses_post( "</$title_tag>" );
698 break;
699
700 case 'title-content':
701 // Title.
702 $title_tag = $attributes['inline'] ? 'span' : 'h1';
703 echo wp_kses_post( "<$title_tag class='insert-page-title'>" );
704 echo esc_html( get_the_title( $inserted_page->ID ) );
705 echo wp_kses_post( "</$title_tag>" );
706 // Content.
707 // If Elementor is installed, try to render the page with it. If there is no Elementor content, fall back to normal rendering.
708 if ( class_exists( '\Elementor\Plugin' ) ) {
709 $elementor_content = \Elementor\Plugin::$instance->frontend->get_builder_content( $inserted_page->ID );
710 if ( strlen( $elementor_content ) > 0 ) {
711 echo $elementor_content;
712 break;
713 }
714 }
715 // Render the content normally.
716 $content = get_post_field( 'post_content', $inserted_page->ID );
717 if ( $attributes['should_apply_the_content_filter'] ) {
718 $content = apply_filters( 'the_content', $content );
719 }
720 echo $content;
721 break;
722
723 case 'link':
724 ?><a href="<?php echo esc_url( get_permalink( $inserted_page->ID ) ); ?>"><?php echo get_the_title( $inserted_page->ID ); ?></a>
725 <?php
726 break;
727
728 case 'excerpt':
729 ?><h1><a href="<?php echo esc_url( get_permalink( $inserted_page->ID ) ); ?>"><?php echo get_the_title( $inserted_page->ID ); ?></a></h1>
730 <?php
731 echo $this->insert_pages_trim_excerpt( get_post_field( 'post_excerpt', $inserted_page->ID ), $inserted_page->ID, $attributes['should_apply_the_content_filter'] );
732 break;
733
734 case 'excerpt-only':
735 echo $this->insert_pages_trim_excerpt( get_post_field( 'post_excerpt', $inserted_page->ID ), $inserted_page->ID, $attributes['should_apply_the_content_filter'] );
736 break;
737
738 case 'content':
739 // If Elementor is installed, try to render the page with it. If there is no Elementor content, fall back to normal rendering.
740 if ( class_exists( '\Elementor\Plugin' ) ) {
741 $elementor_content = \Elementor\Plugin::$instance->frontend->get_builder_content( $inserted_page->ID );
742 if ( strlen( $elementor_content ) > 0 ) {
743 echo $elementor_content;
744 break;
745 }
746 }
747
748 // Render the content normally.
749 $content = get_post_field( 'post_content', $inserted_page->ID );
750 if ( $attributes['should_apply_the_content_filter'] ) {
751 $content = apply_filters( 'the_content', $content );
752 }
753 echo $content;
754 break;
755
756 case 'post-thumbnail':
757 $size = empty( $attributes['size'] ) ? 'post-thumbnail' : $attributes['size'];
758 ?><a href="<?php echo esc_url( get_permalink( $inserted_page->ID ) ); ?>"><?php echo get_the_post_thumbnail( $inserted_page->ID, $size ); ?></a>
759 <?php
760 break;
761
762 case 'all':
763 // Title.
764 $title_tag = $attributes['inline'] ? 'span' : 'h1';
765 echo wp_kses_post( "<$title_tag class='insert-page-title'>" );
766 echo esc_html( get_the_title( $inserted_page->ID ) );
767 echo wp_kses_post( "</$title_tag>" );
768 // Content.
769 $content = get_post_field( 'post_content', $inserted_page->ID );
770 if ( $attributes['should_apply_the_content_filter'] ) {
771 $content = apply_filters( 'the_content', $content );
772 }
773 echo $content;
774 $this->the_meta( $inserted_page->ID );
775 break;
776
777 default: // Display is either invalid, or contains a template file to use.
778 /**
779 * Legacy/compatibility code: In order to use custom templates,
780 * we use query_posts() to provide the template with the global
781 * state it requires for the inserted page (in other words, all
782 * template tags will work with respect to the inserted page
783 * instead of the parent page / main loop). Note that this may
784 * cause some compatibility issues with other plugins.
785 *
786 * @see https://codex.wordpress.org/Function_Reference/query_posts
787 */
788 if ( is_numeric( $attributes['page'] ) ) {
789 $args = array(
790 'p' => intval( $attributes['page'] ),
791 'post_type' => get_post_types(),
792 );
793 } else {
794 $args = array(
795 'name' => esc_attr( $attributes['page'] ),
796 'post_type' => get_post_types(),
797 );
798 }
799 // We save the previous query state here instead of using
800 // wp_reset_query() because wp_reset_query() only has a single stack
801 // variable ($GLOBALS['wp_the_query']). This allows us to support
802 // pages inserted into other pages (multiple nested pages).
803 $old_query = $GLOBALS['wp_query'];
804 $inserted_page = query_posts( $args );
805 if ( have_posts() ) {
806 $template = locate_template( $attributes['display'] );
807 // Only allow templates that don't have any directory traversal in
808 // them (to prevent including php files that aren't in the active
809 // theme directory or the /wp-includes/theme-compat/ directory).
810 $path_in_theme_or_childtheme_or_compat = (
811 // Template is in current theme folder.
812 0 === strpos( realpath( $template ), realpath( get_stylesheet_directory() ) ) ||
813 // Template is in current or parent theme folder.
814 0 === strpos( realpath( $template ), realpath( get_template_directory() ) ) ||
815 // Template is in theme-compat folder.
816 0 === strpos( realpath( $template ), realpath( ABSPATH . WPINC . '/theme-compat/' ) )
817 );
818 if ( strlen( $template ) > 0 && $path_in_theme_or_childtheme_or_compat ) {
819 include $template; // Execute the template code.
820 } else { // Bad path, so fall back to printing a link to the page.
821 the_post();
822 ?><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
823 <?php
824 }
825 }
826 // Restore previous query and update the global template variables.
827 $GLOBALS['wp_query'] = $old_query; // phpcs:ignore WordPress.WP.GlobalVariablesOverride
828 wp_reset_postdata();
829 }
830
831 // Save output buffer contents.
832 $content = ob_get_clean();
833
834 } else { // Use "Legacy" insert method (query_posts).
835
836 // Construct query_posts arguments.
837 if ( is_numeric( $attributes['page'] ) ) {
838 $args = array(
839 'p' => intval( $attributes['page'] ),
840 'post_type' => get_post_types(),
841 'post_status' => $attributes['public'] ? array( 'publish', 'private' ) : array( 'publish' ),
842 );
843 } else {
844 $args = array(
845 'name' => esc_attr( $attributes['page'] ),
846 'post_type' => get_post_types(),
847 'post_status' => $attributes['public'] ? array( 'publish', 'private' ) : array( 'publish' ),
848 );
849 }
850
851 // We save the previous query state here instead of using
852 // wp_reset_query() because wp_reset_query() only has a single stack
853 // variable ($GLOBALS['wp_the_query']). This allows us to support
854 // pages inserted into other pages (multiple nested pages).
855 $old_query = $GLOBALS['wp_query'];
856 $posts = query_posts( $args );
857
858 // Prevent unprivileged users from inserting private posts from others.
859 if ( have_posts() ) {
860 $can_read = true;
861 $parent_post_author_id = intval( get_the_author_meta( 'ID' ) );
862 foreach ( $posts as $post ) {
863 if ( is_object( $post ) && 'publish' !== $post->post_status ) {
864 $post_type = get_post_type_object( $post->post_type );
865 if ( ! user_can( $parent_post_author_id, $post_type->cap->read_post, $post->ID ) ) {
866 $can_read = false;
867 }
868 }
869 }
870 if ( ! $can_read ) {
871 // Force an empty query so we don't show any posts.
872 $posts = query_posts( array( 'post__in' => array( 0 ) ) );
873 }
874 }
875
876 if ( have_posts() ) {
877 // Start output buffering so we can save the output to string.
878 ob_start();
879
880 // If Beaver Builder, SiteOrigin Page Builder, Elementor, or WPBakery
881 // Page Builder (Visual Composer) are enabled, load any cached styles
882 // associated with the inserted page.
883 // Note: Temporarily set the global $post->ID to the inserted page ID,
884 // since both builders rely on the id to load the appropriate styles.
885 if (
886 class_exists( 'UAGB_Post_Assets' ) ||
887 class_exists( 'FLBuilder' ) ||
888 class_exists( 'SiteOrigin_Panels' ) ||
889 class_exists( '\Elementor\Post_CSS_File' ) ||
890 defined( 'VCV_VERSION' ) ||
891 defined( 'WPB_VC_VERSION' )
892 ) {
893 // If we're not in The Loop (i.e., global $post isn't assigned),
894 // temporarily populate it with the post to be inserted so we can
895 // retrieve generated styles for that post. Reset $post to null
896 // after we're done.
897 if ( is_null( $post ) ) {
898 $old_post_id = null;
899 $post = $inserted_page; // phpcs:ignore WordPress.WP.GlobalVariablesOverride
900 } elseif ( is_int( $post ) ) {
901 $old_post_id = $post;
902 $post = $inserted_page->ID;
903 } else {
904 $old_post_id = $post->ID;
905 $post->ID = $inserted_page->ID;
906 }
907
908 // Enqueue assets for Ultimate Addons for Gutenberg.
909 // See: https://ultimategutenberg.com/docs/assets-api-third-party-plugins/.
910 if ( class_exists( 'UAGB_Post_Assets' ) ) {
911 $post_assets_instance = new UAGB_Post_Assets( $inserted_page->ID );
912 $post_assets_instance->enqueue_scripts();
913 }
914
915 if ( class_exists( 'FLBuilder' ) ) {
916 FLBuilder::enqueue_layout_styles_scripts( $inserted_page->ID );
917 }
918
919 if ( class_exists( 'SiteOrigin_Panels' ) ) {
920 $renderer = SiteOrigin_Panels::renderer();
921 $renderer->add_inline_css( $inserted_page->ID, $renderer->generate_css( $inserted_page->ID ) );
922 }
923
924 if ( class_exists( '\Elementor\Post_CSS_File' ) ) {
925 $css_file = new \Elementor\Post_CSS_File( $inserted_page->ID );
926 $css_file->enqueue();
927 }
928
929 // Enqueue custom style from WPBakery Page Builder (Visual Composer).
930 if ( defined( 'VCV_VERSION' ) ) {
931 wp_enqueue_style( 'vcv:assets:front:style' );
932 wp_enqueue_script( 'vcv:assets:runtime:script' );
933 wp_enqueue_script( 'vcv:assets:front:script' );
934
935 $bundle_url = get_post_meta( $inserted_page->ID, 'vcvSourceCssFileUrl', true );
936 if ( $bundle_url ) {
937 $version = get_post_meta( $inserted_page->ID, 'vcvSourceCssFileHash', true );
938 if ( ! preg_match( '/^http/', $bundle_url ) ) {
939 if ( ! preg_match( '/assets-bundles/', $bundle_url ) ) {
940 $bundle_url = '/assets-bundles/' . $bundle_url;
941 }
942 }
943 if ( preg_match( '/^http/', $bundle_url ) ) {
944 $bundle_url = set_url_scheme( $bundle_url );
945 } elseif ( defined( 'VCV_TF_ASSETS_IN_UPLOADS' ) && constant( 'VCV_TF_ASSETS_IN_UPLOADS' ) ) {
946 $upload_dir = wp_upload_dir();
947 $bundle_url = set_url_scheme( $upload_dir['baseurl'] . '/' . VCV_PLUGIN_ASSETS_DIRNAME . '/' . ltrim( $bundle_url, '/\\' ) );
948 } elseif ( class_exists( 'VisualComposer\Helpers\AssetsEnqueue' ) ) {
949 // These methods should work for Visual Composer 26.0.
950 // Enqueue custom css/js stored in vcvSourceAssetsFiles postmeta.
951 $vc = new \VisualComposer\Helpers\AssetsEnqueue;
952 if ( method_exists( $vc, 'enqueueAssets' ) ) {
953 $vc->enqueueAssets( $inserted_page->ID );
954 }
955 // Enqueue custom CSS stored in vcvSourceCssFileUrl postmeta.
956 $upload_dir = wp_upload_dir();
957 $bundle_url = set_url_scheme( $upload_dir['baseurl'] . '/' . VCV_PLUGIN_ASSETS_DIRNAME . '/' . ltrim( $bundle_url, '/\\' ) );
958 } else {
959 $bundle_url = content_url() . '/' . VCV_PLUGIN_ASSETS_DIRNAME . '/' . ltrim( $bundle_url, '/\\' );
960 }
961 wp_enqueue_style(
962 'vcv:assets:source:main:styles:' . sanitize_title( $bundle_url ),
963 $bundle_url,
964 array(),
965 VCV_VERSION . '.' . $version
966 );
967 }
968 }
969
970 // Visual Composer custom CSS.
971 if ( defined( 'WPB_VC_VERSION' ) ) {
972 // Post custom CSS.
973 $post_custom_css = get_post_meta( $inserted_page->ID, '_wpb_post_custom_css', true );
974 if ( ! empty( $post_custom_css ) ) {
975 $post_custom_css = wp_strip_all_tags( $post_custom_css );
976 echo '<style type="text/css" data-type="vc_custom-css">';
977 echo $post_custom_css;
978 echo '</style>';
979 }
980 // Shortcodes custom CSS.
981 $shortcodes_custom_css = get_post_meta( $inserted_page->ID, '_wpb_shortcodes_custom_css', true );
982 if ( ! empty( $shortcodes_custom_css ) ) {
983 $shortcodes_custom_css = wp_strip_all_tags( $shortcodes_custom_css );
984 echo '<style type="text/css" data-type="vc_shortcodes-custom-css">';
985 echo $shortcodes_custom_css;
986 echo '</style>';
987 }
988 }
989
990 // GoodLayers page builder content (retrieved from post meta).
991 // See: https://docs.goodlayers.com/add-page-builder-in-product/.
992 do_action( 'gdlr_core_print_page_builder' );
993
994 if ( is_null( $old_post_id ) ) {
995 $post = null; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
996 } elseif ( is_int( $post ) ) {
997 $post = $old_post_id;
998 } else {
999 $post->ID = $old_post_id;
1000 }
1001 }
1002
1003 /**
1004 * Show either the title, link, content, everything, or everything via a
1005 * custom template.
1006 *
1007 * Note: if the sharing_display filter exists, it means Jetpack is
1008 * installed and Sharing is enabled; this plugin conflicts with Sharing,
1009 * because Sharing assumes the_content and the_excerpt filters are only
1010 * getting called once. The fix here is to disable processing of filters
1011 * on the_content in the inserted page.
1012 *
1013 * @see https://codex.wordpress.org/Function_Reference/the_content#Alternative_Usage
1014 */
1015 switch ( $attributes['display'] ) {
1016 case 'title':
1017 the_post();
1018 $title_tag = $attributes['inline'] ? 'span' : 'h1';
1019 echo "<$title_tag class='insert-page-title'>";
1020 the_title();
1021 echo "</$title_tag>";
1022 break;
1023 case 'title-content':
1024 // Title.
1025 the_post();
1026 $title_tag = $attributes['inline'] ? 'span' : 'h1';
1027 echo "<$title_tag class='insert-page-title'>";
1028 the_title();
1029 echo "</$title_tag>";
1030 // Content.
1031 // If Elementor is installed, try to render the page with it. If there is no Elementor content, fall back to normal rendering.
1032 if ( class_exists( '\Elementor\Plugin' ) ) {
1033 $elementor_content = \Elementor\Plugin::$instance->frontend->get_builder_content( $inserted_page->ID );
1034 if ( strlen( $elementor_content ) > 0 ) {
1035 echo $elementor_content;
1036 break;
1037 }
1038 }
1039 // Render the content normally.
1040 if ( $attributes['should_apply_the_content_filter'] ) {
1041 the_content();
1042 } else {
1043 echo get_the_content();
1044 }
1045 // Render any <!--nextpage--> pagination links.
1046 wp_link_pages(
1047 array(
1048 'before' => '<div class="page-links">' . __( 'Pages:', 'insert-pages' ),
1049 'after' => '</div>',
1050 )
1051 );
1052 break;
1053 case 'link':
1054 the_post();
1055 ?><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
1056 <?php
1057 break;
1058 case 'excerpt':
1059 the_post();
1060 ?><h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>
1061 <?php
1062 if ( $attributes['should_apply_the_content_filter'] ) {
1063 the_excerpt();
1064 } else {
1065 echo get_the_excerpt();
1066 }
1067 break;
1068 case 'excerpt-only':
1069 the_post();
1070 if ( $attributes['should_apply_the_content_filter'] ) {
1071 the_excerpt();
1072 } else {
1073 echo get_the_excerpt();
1074 }
1075 break;
1076 case 'content':
1077 // If Elementor is installed, try to render the page with it. If there is no Elementor content, fall back to normal rendering.
1078 if ( class_exists( '\Elementor\Plugin' ) ) {
1079 $elementor_content = \Elementor\Plugin::$instance->frontend->get_builder_content( $inserted_page->ID );
1080 if ( strlen( $elementor_content ) > 0 ) {
1081 echo $elementor_content;
1082 break;
1083 }
1084 }
1085 // Render the content normally.
1086 the_post();
1087 if ( $attributes['should_apply_the_content_filter'] ) {
1088 the_content();
1089 } else {
1090 echo get_the_content();
1091 }
1092 // Render any <!--nextpage--> pagination links.
1093 wp_link_pages(
1094 array(
1095 'before' => '<div class="page-links">' . __( 'Pages:', 'insert-pages' ),
1096 'after' => '</div>',
1097 )
1098 );
1099 break;
1100 case 'post-thumbnail':
1101 $size = empty( $attributes['size'] ) ? 'post-thumbnail' : $attributes['size'];
1102 ?><a href="<?php echo esc_url( get_permalink( $inserted_page->ID ) ); ?>"><?php echo get_the_post_thumbnail( $inserted_page->ID, $size ); ?></a>
1103 <?php
1104 break;
1105 case 'all':
1106 the_post();
1107 $title_tag = $attributes['inline'] ? 'span' : 'h1';
1108 echo "<$title_tag class='insert-page-title'>";
1109 the_title();
1110 echo "</$title_tag>";
1111 if ( $attributes['should_apply_the_content_filter'] ) {
1112 the_content();
1113 } else {
1114 echo get_the_content();
1115 }
1116 $this->the_meta();
1117 // Render any <!--nextpage--> pagination links.
1118 wp_link_pages( array(
1119 'before' => '<div class="page-links">' . __( 'Pages:', 'insert-pages' ),
1120 'after' => '</div>',
1121 ) );
1122 break;
1123 default: // Display is either invalid, or contains a template file to use.
1124 $template = locate_template( $attributes['display'] );
1125 // Only allow templates that don't have any directory traversal in
1126 // them (to prevent including php files that aren't in the active
1127 // theme directory or the /wp-includes/theme-compat/ directory).
1128 $path_in_theme_or_childtheme_or_compat = (
1129 // Template is in current theme folder.
1130 0 === strpos( realpath( $template ), realpath( get_stylesheet_directory() ) ) ||
1131 // Template is in current or parent theme folder.
1132 0 === strpos( realpath( $template ), realpath( get_template_directory() ) ) ||
1133 // Template is in theme-compat folder.
1134 0 === strpos( realpath( $template ), realpath( ABSPATH . WPINC . '/theme-compat/' ) )
1135 );
1136 if ( strlen( $template ) > 0 && $path_in_theme_or_childtheme_or_compat ) {
1137 include $template; // Execute the template code.
1138 } else { // Couldn't find template, so fall back to printing a link to the page.
1139 the_post();
1140 ?><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
1141 <?php
1142 }
1143 break;
1144 }
1145 // Save output buffer contents.
1146 $content = ob_get_clean();
1147 } else {
1148 /**
1149 * Filter the html that should be displayed if an inserted page was not found.
1150 *
1151 * @param string $content html to be displayed. Defaults to an empty string.
1152 */
1153 $content = apply_filters( 'insert_pages_not_found_message', $content );
1154 }
1155 // Restore previous query and update the global template variables.
1156 $GLOBALS['wp_query'] = $old_query; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
1157 wp_reset_postdata();
1158 }
1159
1160 /**
1161 * Filter the markup generated for the inserted page.
1162 *
1163 * @param string $content The post content of the inserted page.
1164 * @param object $inserted_page The post object returned from querying the inserted page.
1165 * @param array $attributes Extra parameters modifying the inserted page.
1166 * page: Page ID or slug of page to be inserted.
1167 * display: Content to display from inserted page.
1168 * class: Extra classes to add to inserted page wrapper element.
1169 * id: Optional ID for the inserted page wrapper element.
1170 * inline: Boolean indicating wrapper element should be a span.
1171 * public: Boolean indicating anonymous users can see private inserted pages.
1172 * querystring: Extra querystring values provided to the custom template.
1173 * should_apply_the_content_filter: Whether to apply the_content filter to post contents and excerpts.
1174 * wrapper_tag: Tag to use for the wrapper element (e.g., div, span).
1175 */
1176 $content = apply_filters( 'insert_pages_wrap_content', $content, $inserted_page, $attributes );
1177
1178 // Unset any querystring params included in the shortcode.
1179 $_GET = $original_get;
1180 $_REQUEST = $original_request;
1181 $GLOBALS['wp']->query_vars = $original_wp_query_vars;
1182
1183 // Loop detection: remove the page from the stack (so we can still insert
1184 // the same page multiple times on another page, but prevent it from being
1185 // inserted multiple times within the same recursive chain).
1186 if ( isset( $inserted_page->ID ) ) {
1187 foreach ( $this->inserted_page_ids as $key => $page_id ) {
1188 if ( $page_id === $inserted_page->ID ) {
1189 unset( $this->inserted_page_ids[ $key ] );
1190 }
1191 }
1192 } elseif ( is_array( $inserted_page ) && ! empty( $inserted_page ) ) {
1193 // Legacy template code populates $inserted_page with query_posts()
1194 // output. Remove each from the stack (should just be a single page).
1195 foreach ( $inserted_page as $page ) {
1196 foreach ( $this->inserted_page_ids as $key => $page_id ) {
1197 if ( $page_id === $page->ID ) {
1198 unset( $this->inserted_page_ids[ $key ] );
1199 }
1200 }
1201 }
1202 }
1203
1204 return $content;
1205 }
1206
1207 /**
1208 * Default filter for insert_pages_wrap_content.
1209 *
1210 * @param string $content Content of shortcode.
1211 * @param array $posts Post data of inserted page.
1212 * @param array $attributes Shortcode attributes.
1213 * @return string Content to replace shortcode.
1214 */
1215 public function insert_pages_wrap_content( $content, $posts, $attributes ) {
1216 return sprintf(
1217 '<%1$s data-post-id="%2$s" class="insert-page insert-page-%2$s %3$s"%4$s>%5$s</%1$s>',
1218 esc_attr( $attributes['wrapper_tag'] ),
1219 esc_attr( $attributes['page'] ),
1220 esc_attr( $attributes['class'] ),
1221 empty( $attributes['id'] ) ? '' : ' id="' . esc_attr( $attributes['id'] ) . '"',
1222 $content
1223 );
1224 }
1225
1226 /**
1227 * Filter hook: Add a button to the TinyMCE toolbar for our insert page tool.
1228 *
1229 * @param array $buttons TinyMCE buttons.
1230 * @return array TinyMCE buttons with Insert Pages button.
1231 */
1232 public function insert_pages_handle_filter_mce_buttons( $buttons ) {
1233 if ( self::$is_admin_initialized && ! in_array( 'wpInsertPages_button', $buttons, true ) ) {
1234 array_push( $buttons, 'wpInsertPages_button' );
1235 }
1236 return $buttons;
1237 }
1238
1239 /**
1240 * Filter hook: Load the javascript for our custom toolbar button.
1241 *
1242 * @param array $plugins TinyMCE plugins.
1243 * @return array TinyMCE plugins with Insert Pages plugin.
1244 */
1245 public function insert_pages_handle_filter_mce_external_plugins( $plugins ) {
1246 if ( self::$is_admin_initialized && ! array_key_exists( 'wpInsertPages', $plugins ) ) {
1247 $plugins['wpInsertPages'] = plugins_url( '/js/wpinsertpages_plugin.js', __FILE__ );
1248 }
1249 return $plugins;
1250 }
1251
1252 /**
1253 * Helper function to generate an excerpt (outside of the Loop) for a given
1254 * ID (based on wp_trim_excerpt()).
1255 *
1256 * @param string $text Excerpt.
1257 * @param integer $post_id Post ID of excerpt.
1258 * @param boolean $apply_the_content_filter Whether to apply `the_content`.
1259 * @return string Excerpt.
1260 */
1261 public function insert_pages_trim_excerpt( $text = '', $post_id = 0, $apply_the_content_filter = true ) {
1262 $post_id = intval( $post_id );
1263 if ( $post_id < 1 ) {
1264 return '';
1265 }
1266
1267 $raw_excerpt = $text;
1268 if ( '' === $text ) {
1269 $text = get_post_field( 'post_content', $post_id );
1270
1271 $text = strip_shortcodes( $text );
1272
1273 // Look for a <!--more--> quicktag and trim the excerpt there if it exists.
1274 $has_more_quicktag = false;
1275 if ( preg_match( '/<!--more(.*?)?-->/', $text, $matches ) ) {
1276 $has_more_quicktag = true;
1277 $text = explode( $matches[0], $text, 2 );
1278 $text = $text[0];
1279 // Look for a custom <!--crop--> quicktag that will trim any text before
1280 // it out of the excerpt.
1281 if ( preg_match( '/<!--crop-->/', $text, $matches ) ) {
1282 $text = explode( $matches[0], $text, 2 );
1283 $text = $text[1];
1284 }
1285 }
1286
1287 /** This filter is documented in wp-includes/post-template.php */
1288 if ( $apply_the_content_filter ) {
1289 $text = apply_filters( 'the_content', $text );
1290 }
1291 $text = str_replace( ']]>', ']]&gt;', $text );
1292
1293 // Only trim excerpt if there wasn't an existing <!--more--> quicktag.
1294 if ( ! $has_more_quicktag ) {
1295 /**
1296 * Filter the number of words in an excerpt.
1297 *
1298 * @since 2.7.0
1299 *
1300 * @param int $number The number of words. Default 55.
1301 */
1302 $excerpt_length = apply_filters( 'excerpt_length', 55 );
1303
1304 /**
1305 * Filter the string in the "more" link displayed after a trimmed excerpt.
1306 *
1307 * @since 2.9.0
1308 *
1309 * @param string $more_string The string shown within the more link.
1310 */
1311 global $post;
1312 if ( isset( $post->ID ) ) {
1313 $old_post_id = $post->ID;
1314 $post->ID = $post_id;
1315 }
1316 $excerpt_more = apply_filters( 'excerpt_more', ' [&hellip;]' );
1317 if ( isset( $post->ID ) ) {
1318 $post->ID = $old_post_id;
1319 }
1320
1321 $text = wp_trim_words( $text, $excerpt_length, $excerpt_more );
1322 }
1323 }
1324 /**
1325 * Filter the trimmed excerpt string.
1326 *
1327 * @since 2.8.0
1328 *
1329 * @param string $text The trimmed text.
1330 * @param string $raw_excerpt The text prior to trimming.
1331 */
1332 return apply_filters( 'wp_trim_excerpt', $text, $raw_excerpt );
1333 }
1334
1335 /**
1336 * Modified from /wp-includes/class-wp-editor.php, function
1337 * wp_link_dialog().
1338 *
1339 * Dialog for internal linking.
1340 *
1341 * @since 3.1.0
1342 */
1343 public function insert_pages_wp_tinymce_dialog() {
1344 // Don't run if required scripts and styles weren't enqueued.
1345 if ( ! self::$is_admin_initialized ) {
1346 return;
1347 }
1348
1349 // Run once.
1350 if ( self::$link_dialog_printed ) {
1351 return;
1352 }
1353
1354 self::$link_dialog_printed = true;
1355
1356 $formats = array(
1357 'title' => __( 'Title', 'insert-pages' ),
1358 'title-content' => __( 'Title and content', 'insert-pages' ),
1359 'link' => __( 'Link', 'insert-pages' ),
1360 'excerpt' => __( 'Excerpt with title', 'insert-pages' ),
1361 'excerpt-only' => __( 'Excerpt only (no title)', 'insert-pages' ),
1362 'content' => __( 'Content', 'insert-pages' ),
1363 'post-thumbnail' => __( 'Post Thumbnail', 'insert-pages' ),
1364 'all' => __( 'All (includes custom fields)', 'insert-pages' ),
1365 'template' => __( 'Use a custom template', 'insert-pages' ) . ' &raquo;',
1366 );
1367
1368 $templates = array(
1369 'all' => __( 'Default Template', 'insert-pages' ),
1370 );
1371 foreach ( wp_get_theme()->get_page_templates() as $file => $name ) {
1372 $templates[ $file ] = $name;
1373 }
1374
1375 $sizes = function_exists( 'wp_get_registered_image_subsizes' ) ? array_keys( wp_get_registered_image_subsizes() ) : get_intermediate_image_sizes();
1376
1377 /**
1378 * Filter the available templates shown in the template dropdown.
1379 *
1380 * @param array $templates Array of template names keyed by their filename.
1381 */
1382 $templates = apply_filters( 'insert_pages_available_templates', $templates );
1383
1384 // Get default values for the TinyMCE dialog fields. Note: can be
1385 // overridden by the `insert_pages_tinymce_state` filter.
1386 $tinymce_state = $this->get_tinymce_state();
1387
1388 // Get ID of post currently being edited.
1389 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1390 $post_id = isset( $_REQUEST['post'] ) && intval( $_REQUEST['post'] ) > 0 ? intval( $_REQUEST['post'] ) : '';
1391
1392 // display: none is required here, see #WP27605.
1393 ?>
1394 <div id="wp-insertpage-backdrop" style="display: none"></div>
1395 <div id="wp-insertpage-wrap" class="wp-core-ui<?php echo 1 === intval( get_user_setting( 'wpinsertpage', 0 ) ) ? ' options-panel-visible' : ''; ?><?php echo empty( $tinymce_state['hide_querystring'] ) ? '' : ' querystring-hidden'; ?><?php echo empty( $tinymce_state['hide_public'] ) ? '' : ' public-hidden'; ?>" style="display: none;" role="dialog" aria-labelledby="insertpage-modal-title">
1396 <form id="wp-insertpage" tabindex="-1">
1397 <?php wp_nonce_field( 'internal-inserting', '_ajax_inserting_nonce', false ); ?>
1398 <input type="hidden" id="insertpage-parent-page-id" value="<?php echo esc_attr( $post_id ); ?>" />
1399 <h1 id="insertpage-modal-title"><?php esc_html_e( 'Insert page', 'insert-pages' ); ?></h1>
1400 <button type="button" id="wp-insertpage-close"><span class="screen-reader-text"><?php esc_html_e( 'Close', 'insert-pages' ); ?></span></button>
1401 <div id="insertpage-selector">
1402 <div id="insertpage-search-panel">
1403 <div class="insertpage-search-wrapper">
1404 <label>
1405 <span class="search-label"><?php esc_html_e( 'Search', 'insert-pages' ); ?></span>
1406 <input type="search" id="insertpage-search-field" class="insertpage-search-field" autocomplete="off" />
1407 <span class="spinner"></span>
1408 </label>
1409 </div>
1410 <div id="insertpage-search-results" class="query-results" tabindex="0">
1411 <ul></ul>
1412 <div class="river-waiting">
1413 <span class="spinner"></span>
1414 </div>
1415 </div>
1416 <div id="insertpage-most-recent-results" class="query-results" tabindex="0">
1417 <div class="query-notice" id="insertpage-query-notice-message">
1418 <em class="query-notice-default"><?php esc_html_e( 'No search term specified. Showing recent items.', 'insert-pages' ); ?></em>
1419 <em class="query-notice-hint screen-reader-text"><?php esc_html_e( 'Search or use up and down arrow keys to select an item.', 'insert-pages' ); ?></em>
1420 </div>
1421 <ul></ul>
1422 <div class="river-waiting">
1423 <span class="spinner"></span>
1424 </div>
1425 </div>
1426 </div>
1427 <p class="howto" id="insertpage-options-toggle"><?php esc_html_e( 'Options', 'insert-pages' ); ?></p>
1428 <div id="insertpage-options-panel">
1429 <div class="insertpage-options-wrapper">
1430 <label for="insertpage-slug-field">
1431 <span><?php esc_html_e( 'Slug or ID', 'insert-pages' ); ?></span>
1432 <input id="insertpage-slug-field" type="text" autocomplete="off" />
1433 <input id="insertpage-page-id" type="hidden" />
1434 </label>
1435 </div>
1436 <div class="insertpage-format">
1437 <label for="insertpage-format-select">
1438 <?php esc_html_e( 'Display', 'insert-pages' ); ?>
1439 </label>
1440 <select name="insertpage-format-select" id="insertpage-format-select">
1441 <?php foreach ( $formats as $format => $label ) : ?>
1442 <option value='<?php echo esc_attr( $format ); ?>' <?php selected( $tinymce_state['format'], $format ); ?>><?php echo esc_html( $label ); ?></option>
1443 <?php endforeach; ?>
1444 </select>
1445 <select name="insertpage-template-select" id="insertpage-template-select" disabled="true">
1446 <?php foreach ( $templates as $template => $label ) : ?>
1447 <option value='<?php echo esc_attr( $template ); ?>' <?php selected( $tinymce_state['template'], $template ); ?>><?php echo esc_html( $label ); ?></option>
1448 <?php endforeach; ?>
1449 </select>
1450 <select name="insertpage-size-select" id="insertpage-size-select" disabled="true">
1451 <?php foreach ( $sizes as $size ) : ?>
1452 <option value='<?php echo esc_attr( $size ); ?>' <?php selected( $tinymce_state['size'], $size ); ?>><?php echo esc_html( $size ); ?></option>
1453 <?php endforeach; ?>
1454 </select>
1455 </div>
1456 <div class="insertpage-extra">
1457 <label for="insertpage-extra-classes">
1458 <?php esc_html_e( 'Extra Classes', 'insert-pages' ); ?>
1459 <input id="insertpage-extra-classes" type="text" autocomplete="off" value="<?php echo empty( $tinymce_state['class'] ) ? '' : esc_attr( $tinymce_state['class'] ); ?>" />
1460 </label>
1461 <label for="insertpage-extra-id">
1462 <?php esc_html_e( 'ID', 'insert-pages' ); ?>
1463 <input id="insertpage-extra-id" type="text" autocomplete="off" value="<?php echo empty( $tinymce_state['id'] ) ? '' : esc_attr( $tinymce_state['id'] ); ?>" />
1464 </label>
1465 <label for="insertpage-extra-inline">
1466 <?php esc_html_e( 'Inline?', 'insert-pages' ); ?>
1467 <input id="insertpage-extra-inline" type="checkbox" <?php checked( $tinymce_state['inline'] ); ?> />
1468 </label>
1469 <br class="<?php echo empty( $tinymce_state['hide_querystring'] ) ? '' : 'hidden'; ?>" />
1470 <label for="insertpage-extra-querystring" class="<?php echo empty( $tinymce_state['hide_querystring'] ) ? '' : 'hidden'; ?>">
1471 <?php esc_html_e( 'Querystring', 'insert-pages' ); ?>
1472 <input id="insertpage-extra-querystring" type="text" autocomplete="off" value="<?php echo empty( $tinymce_state['querystring'] ) ? '' : esc_attr( $tinymce_state['querystring'] ); ?>" />
1473 </label>
1474 <br class="<?php echo empty( $tinymce_state['hide_public'] ) ? '' : 'hidden'; ?>" />
1475 <label for="insertpage-extra-public" class="<?php echo empty( $tinymce_state['hide_public'] ) ? '' : 'hidden'; ?>">
1476 <input id="insertpage-extra-public" type="checkbox" <?php checked( $tinymce_state['public'] ); ?> />
1477 <?php esc_html_e( 'Anonymous users can see this inserted even if its status is private', 'insert-pages' ); ?>
1478 </label>
1479 </div>
1480 </div>
1481 </div>
1482 <div class="submitbox">
1483 <div id="wp-insertpage-cancel">
1484 <button type="button" class="button"><?php esc_html_e( 'Cancel', 'insert-pages' ); ?></button>
1485 </div>
1486 <div id="wp-insertpage-update">
1487 <input type="submit" value="<?php esc_attr_e( 'Insert Page', 'insert-pages' ); ?>" class="button button-primary" id="wp-insertpage-submit" name="wp-insertpage-submit">
1488 </div>
1489 </div>
1490 </form>
1491 </div>
1492 <?php
1493 }
1494
1495 /**
1496 * Modified from:
1497 * Internal linking functions.
1498 *
1499 * @package WordPress
1500 * @subpackage Administration
1501 * @since 3.1.0
1502 */
1503 public function insert_pages_insert_page_callback() {
1504 check_ajax_referer( 'internal-inserting', '_ajax_inserting_nonce' );
1505 $args = array();
1506
1507 // If a URL is provided, translate it to a post ID and search on that.
1508 if ( ! empty( $_POST['search'] ) && filter_var( wp_unslash( $_POST['search'] ), FILTER_VALIDATE_URL ) ) {
1509 $post_id = url_to_postid( sanitize_url( wp_unslash( $_POST['search'] ) ) );
1510 if ( ! empty( $post_id ) ) {
1511 $_POST['search'] = $post_id;
1512 $_POST['type'] = 'post_id';
1513 }
1514 }
1515
1516 if ( isset( $_POST['search'] ) ) {
1517 $args['s'] = wp_unslash( $_POST['search'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1518 }
1519 $args['pagenum'] = ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 1;
1520 $args['pageID'] = ! empty( $_POST['pageID'] ) ? absint( $_POST['pageID'] ) : 0;
1521
1522 // Change search to slug or post ID if we're not doing a plaintext
1523 // search (e.g., if we're editing an existing shortcode and the
1524 // search field is populated with the post's slug or ID).
1525 if ( isset( $_POST['type'] ) && 'slug' === $_POST['type'] ) {
1526 $args['name'] = $args['s'];
1527 unset( $args['s'] );
1528 } elseif ( array_key_exists( 'type', $_POST ) && 'post_id' === $_POST['type'] ) {
1529 $args['p'] = $args['s'];
1530 unset( $args['s'] );
1531 }
1532
1533 $results = $this->insert_pages_wp_query( $args );
1534
1535 // Fail if our query didn't work.
1536 if ( ! isset( $results ) ) {
1537 die( '0' );
1538 }
1539
1540 echo wp_json_encode( $results );
1541 echo "\n";
1542 die();
1543 }
1544
1545 /**
1546 * Save the user's last-selected display or template in the TinyMCE widget
1547 * whenever it changes.
1548 *
1549 * @hook wp_ajax_insertpage_save_presets
1550 */
1551 public function insert_pages_save_presets() {
1552 check_ajax_referer( 'internal-inserting', '_ajax_inserting_nonce' );
1553 $args = array();
1554 if ( isset( $_POST['format'] ) ) {
1555 $args['format'] = sanitize_key( wp_unslash( $_POST['format'] ) );
1556 }
1557 if ( isset( $_POST['template'] ) ) {
1558 $args['template'] = sanitize_file_name( wp_unslash( $_POST['template'] ) );
1559 }
1560
1561 if ( ! empty( $args ) ) {
1562 $tinymce_state = get_user_meta( get_current_user_id(), 'insert_pages_tinymce_state', true );
1563 if ( empty( $tinymce_state ) ) {
1564 $tinymce_state = array(
1565 'format' => 'title',
1566 'template' => 'all',
1567 );
1568 }
1569 $tinymce_state = array_merge( $tinymce_state, $args );
1570 update_user_meta( get_current_user_id(), 'insert_pages_tinymce_state', $tinymce_state );
1571 }
1572
1573 // Fail if our query didn't work.
1574 if ( ! isset( $results ) ) {
1575 die( '0' );
1576 }
1577
1578 echo wp_json_encode( 'Success' );
1579 echo "\n";
1580 die();
1581 }
1582
1583 /**
1584 * Modified from:
1585 * Performs post queries for internal linking.
1586 *
1587 * @since 3.1.0
1588 * @param array $args Optional. Accepts 'pagenum' and 's' (search) arguments.
1589 * @return array Results.
1590 */
1591 private function insert_pages_wp_query( $args = array() ) {
1592 $pts = get_post_types( array( 'public' => true ), 'objects' );
1593 $post_types = array_keys( $pts );
1594
1595 /**
1596 * Filter the post types that appear in the list of pages to insert.
1597 *
1598 * By default, all post types will apear.
1599 *
1600 * @since 2.0
1601 *
1602 * @param array $post_types Array of post type names to include.
1603 */
1604 $post_types = apply_filters( 'insert_pages_available_post_types', $post_types );
1605
1606 $query = array(
1607 'post_type' => $post_types,
1608 'update_post_term_cache' => false,
1609 'update_post_meta_cache' => false,
1610 'post_status' => array( 'publish', 'private' ),
1611 'order' => 'DESC',
1612 'orderby' => 'post_date',
1613 'posts_per_page' => 20,
1614 );
1615
1616 // Show non-admins only their own posts if the option is enabled.
1617 $options = get_option( 'wpip_settings' );
1618 if (
1619 ! empty( $options['wpip_classic_editor_hide_others_posts'] ) &&
1620 'enabled' === $options['wpip_classic_editor_hide_others_posts'] &&
1621 ! current_user_can( 'edit_others_posts' )
1622 ) {
1623 $query['author'] = get_current_user_id();
1624 }
1625
1626 $args['pagenum'] = isset( $args['pagenum'] ) ? absint( $args['pagenum'] ) : 1;
1627 $query['offset'] = $args['pagenum'] > 1 ? $query['posts_per_page'] * ( $args['pagenum'] - 1 ) : 0;
1628
1629 // Search post content and post title.
1630 if ( isset( $args['s'] ) ) {
1631 $query['s'] = $args['s'];
1632 }
1633
1634 // Search post_name (post slugs).
1635 if ( isset( $args['name'] ) ) {
1636 $query['name'] = $args['name'];
1637 }
1638
1639 // Search post ids.
1640 if ( isset( $args['p'] ) ) {
1641 $query['p'] = $args['p'];
1642 }
1643
1644 // Do main query.
1645 $get_posts = new WP_Query();
1646 $posts = $get_posts->query( $query );
1647 // Check if any posts were found.
1648 if ( ! $get_posts->post_count ) {
1649 return false;
1650 }
1651
1652 // Build results.
1653 $results = array();
1654 foreach ( $posts as $post ) {
1655 // Prevent unprivileged users (e.g., Contributors) from seeing and
1656 // inserting other user's private posts.
1657 $post_type = get_post_type_object( $post->post_type );
1658 if ( 'publish' !== $post->post_status && ! current_user_can( $post_type->cap->read_post, $post->ID ) ) {
1659 continue;
1660 }
1661
1662 if ( 'post' === $post->post_type ) {
1663 $info = mysql2date( 'Y/m/d', $post->post_date );
1664 } else {
1665 $info = $pts[ $post->post_type ]->labels->singular_name;
1666 }
1667 $results[] = array(
1668 'ID' => $post->ID,
1669 'title' => trim( esc_html( wp_strip_all_tags( get_the_title( $post ) ) ) ),
1670 'permalink' => get_permalink( $post->ID ),
1671 'slug' => $post->post_name,
1672 'path' => get_page_uri( $post ),
1673 'info' => $info,
1674 'status' => get_post_status( $post ),
1675 );
1676 }
1677 return $results;
1678 }
1679
1680 /**
1681 * Add Insert Page quicktag button to Text editor.
1682 *
1683 * @hook admin_print_footer_scripts
1684 *
1685 * @return void
1686 */
1687 public function insert_pages_add_quicktags() {
1688 if ( wp_script_is( 'quicktags' ) ) : ?>
1689 <script type="text/javascript">
1690 window.onload = function() {
1691 QTags.addButton( 'ed_insert_page', '[insert page]', "[insert page='your-page-slug' display='title|link|excerpt|excerpt-only|content|post-thumbnail|all']\n", '', '', 'Insert Page', 999 );
1692 }
1693 </script>
1694 <?php
1695 endif;
1696 }
1697
1698 /**
1699 * Indicates whether a particular post type is able to be inserted.
1700 *
1701 * @param boolean $type Post type.
1702 * @return boolean Whether post type is insertable.
1703 */
1704 private function is_post_type_insertable( $type ) {
1705 return ! in_array(
1706 $type,
1707 array(
1708 'nav_menu_item',
1709 'attachment',
1710 'revision',
1711 'customize_changeset',
1712 'oembed_cache',
1713 // Exclude Flamingo messages (created via Contact Form 7 submissions).
1714 // See: https://wordpress.org/support/topic/plugin-hacked-14/.
1715 'flamingo_inbound',
1716 ),
1717 true
1718 );
1719 }
1720
1721 /**
1722 * Fetch the default values for the TinyMCE modal fields.
1723 */
1724 private function get_tinymce_state() {
1725 // Get user's previously selected display and template to restore (if any).
1726 $tinymce_state = get_user_meta( get_current_user_id(), 'insert_pages_tinymce_state', true );
1727 if ( empty( $tinymce_state ) ) {
1728 $tinymce_state = array();
1729 }
1730
1731 // Merge user's format and template defaults with global defaults.
1732 $tinymce_state = wp_parse_args(
1733 $tinymce_state,
1734 array(
1735 'format' => 'title',
1736 'template' => 'all',
1737 'class' => '',
1738 'id' => '',
1739 'querystring' => '',
1740 'size' => '',
1741 'inline' => false,
1742 'public' => false,
1743 'hide_querystring' => false,
1744 'hide_public' => false,
1745 )
1746 );
1747
1748 /**
1749 * Filter the TinyMCE dialog field defaults.
1750 *
1751 * @param array $tinymce_state Array of field defaults for the TinyMCE modal.
1752 * 'format' (string) Display format. Default 'title'.
1753 * 'template' (string) Custom template. Default 'all'.
1754 * 'class' (string) HTML wrapper class. Default ''.
1755 * 'id' (string) HTML wrapper id. Default ''.
1756 * 'querystring' (string) Querystring params. Default ''.
1757 * 'size' (string) Image size when using format='thumbnail'.
1758 * 'inline' (bool) Use <span> element for wrapper. Default false.
1759 * 'public' (bool) Whether anonymous users can see this page if
1760 * its status is Private. Default false.
1761 * 'hide_querystring' (bool) Skip rendering querystring field. Default false.
1762 * 'hide_public' (bool) Skip rendering public field. Default false.
1763 */
1764 $tinymce_state = apply_filters( 'insert_pages_tinymce_state', $tinymce_state );
1765
1766 return $tinymce_state;
1767 }
1768
1769 /**
1770 * Registers the theme widget for inserting a page into an area.
1771 *
1772 * @return void
1773 */
1774 public function insert_pages_widgets_init() {
1775 register_widget( 'InsertPagesWidget' );
1776 }
1777
1778 /**
1779 * Render post meta as an unordered list.
1780 *
1781 * Note: This function sanitizes postmeta value via wp_kses_post(); the
1782 * core WordPress function the_meta() does not.
1783 *
1784 * @see https://developer.wordpress.org/reference/functions/the_meta/
1785 *
1786 * @param int $post_id Post ID.
1787 */
1788 public function the_meta( $post_id = 0 ) {
1789 if ( empty( $post_id ) ) {
1790 $post_id = get_the_ID();
1791 }
1792
1793 $keys = get_post_custom_keys( $post_id );
1794 if ( $keys ) {
1795 $li_html = '';
1796 foreach ( (array) $keys as $key ) {
1797 $keyt = trim( $key );
1798 if ( is_protected_meta( $keyt, 'post' ) ) {
1799 continue;
1800 }
1801
1802 $values = array_map( 'trim', get_post_custom_values( $key, $post_id ) );
1803 $value = implode( ', ', $values );
1804
1805 // Sanitize post meta values.
1806 $value = wp_kses_post( $value );
1807
1808 $html = sprintf(
1809 "<li><span class='post-meta-key'>%s</span> %s</li>\n",
1810 /* translators: %s: Post custom field name. */
1811 sprintf( _x( '%s:', 'Post custom field name', 'insert-pages' ), $key ),
1812 $value
1813 );
1814
1815 /**
1816 * Filters the HTML output of the li element in the post custom fields list.
1817 *
1818 * @since 2.2.0
1819 *
1820 * @param string $html The HTML output for the li element.
1821 * @param string $key Meta key.
1822 * @param string $value Meta value.
1823 */
1824 $li_html .= apply_filters( 'the_meta_key', $html, $key, $value );
1825 }
1826
1827 if ( $li_html ) {
1828 echo "<ul class='post-meta'>\n{$li_html}</ul>\n";
1829 }
1830 }
1831 }
1832 }
1833 }
1834
1835 // Initialize InsertPagesPlugin object.
1836 if ( class_exists( 'InsertPagesPlugin' ) ) {
1837 $insert_pages_plugin = InsertPagesPlugin::get_instance();
1838 }
1839
1840 // Actions and Filters handled by InsertPagesPlugin class.
1841 if ( isset( $insert_pages_plugin ) ) {
1842 // Include the code that generates the options page.
1843 require_once __DIR__ . '/options.php';
1844
1845 // Get options set in WordPress dashboard (Settings > Insert Pages).
1846 $options = get_option( 'wpip_settings' );
1847 if ( false === $options || ! is_array( $options ) || ! array_key_exists( 'wpip_format', $options ) || ! array_key_exists( 'wpip_wrapper', $options ) || ! array_key_exists( 'wpip_insert_method', $options ) || ! array_key_exists( 'wpip_tinymce_filter', $options ) ) {
1848 $options = wpip_set_defaults();
1849 }
1850
1851 // Register shortcode [insert ...].
1852 add_action( 'init', array( $insert_pages_plugin, 'insert_pages_init' ), 1 );
1853 // Register shortcode [insert ...] when TinyMCE is included in a frontend ACF form.
1854 add_action( 'acf_head-input', array( $insert_pages_plugin, 'insert_pages_init' ), 1 ); // ACF 3.
1855 add_action( 'acf/input/admin_head', array( $insert_pages_plugin, 'insert_pages_init' ), 1 ); // ACF 4.
1856
1857 // Add TinyMCE button for shortcode.
1858 add_action( 'admin_head', array( $insert_pages_plugin, 'insert_pages_admin_init' ), 1 );
1859
1860 // Add quicktags button for shortcode.
1861 add_action( 'admin_print_footer_scripts', array( $insert_pages_plugin, 'insert_pages_add_quicktags' ) );
1862
1863 // Preload TinyMCE popup.
1864 add_action( 'before_wp_tiny_mce', array( $insert_pages_plugin, 'insert_pages_wp_tinymce_dialog' ), 1 );
1865
1866 // Ajax: Populate page search in TinyMCE button popup.
1867 add_action( 'wp_ajax_insertpage', array( $insert_pages_plugin, 'insert_pages_insert_page_callback' ) );
1868
1869 // Ajax: save user's last selected display and template inputs.
1870 add_action( 'wp_ajax_insertpage_save_presets', array( $insert_pages_plugin, 'insert_pages_save_presets' ) );
1871
1872 // Use internal filter to wrap inserted content in a div or span.
1873 add_filter( 'insert_pages_wrap_content', array( $insert_pages_plugin, 'insert_pages_wrap_content' ), 10, 3 );
1874
1875 /**
1876 * Register TinyMCE plugin for the toolbar button if in compatibility mode.
1877 * (to work around a SiteOrigin PageBuilder bug).
1878 *
1879 * @see https://wordpress.org/support/topic/button-in-the-toolbar-of-tinymce-disappear-conflict-page-builder/
1880 */
1881 if ( 'compatibility' === $options['wpip_tinymce_filter'] ) {
1882 add_filter( 'mce_external_plugins', array( $insert_pages_plugin, 'insert_pages_handle_filter_mce_external_plugins' ) );
1883 add_filter( 'mce_buttons', array( $insert_pages_plugin, 'insert_pages_handle_filter_mce_buttons' ) );
1884 }
1885
1886 // Register Insert Pages shortcode widget.
1887 require_once __DIR__ . '/widget.php';
1888 add_action( 'widgets_init', array( $insert_pages_plugin, 'insert_pages_widgets_init' ) );
1889 }
1890