PluginProbe
Insert Pages / 3.8
Insert Pages v3.8
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.8, at insert-pages.php

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