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

1,786 lines 66.9 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.7.7
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|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 'link':
655 ?><a href="<?php echo esc_url( get_permalink( $inserted_page->ID ) ); ?>"><?php echo get_the_title( $inserted_page->ID ); ?></a>
656 <?php
657 break;
658
659 case 'excerpt':
660 ?><h1><a href="<?php echo esc_url( get_permalink( $inserted_page->ID ) ); ?>"><?php echo get_the_title( $inserted_page->ID ); ?></a></h1>
661 <?php
662 echo $this->insert_pages_trim_excerpt( get_post_field( 'post_excerpt', $inserted_page->ID ), $inserted_page->ID, $attributes['should_apply_the_content_filter'] );
663 break;
664
665 case 'excerpt-only':
666 echo $this->insert_pages_trim_excerpt( get_post_field( 'post_excerpt', $inserted_page->ID ), $inserted_page->ID, $attributes['should_apply_the_content_filter'] );
667 break;
668
669 case 'content':
670 // If Elementor is installed, try to render the page with it. If there is no Elementor content, fall back to normal rendering.
671 if ( class_exists( '\Elementor\Plugin' ) ) {
672 $elementor_content = \Elementor\Plugin::$instance->frontend->get_builder_content( $inserted_page->ID );
673 if ( strlen( $elementor_content ) > 0 ) {
674 echo $elementor_content;
675 break;
676 }
677 }
678
679 // Render the content normally.
680 $content = get_post_field( 'post_content', $inserted_page->ID );
681 if ( $attributes['should_apply_the_content_filter'] ) {
682 $content = apply_filters( 'the_content', $content );
683 }
684 echo $content;
685 break;
686
687 case 'post-thumbnail':
688 $size = empty( $attributes['size'] ) ? 'post-thumbnail' : $attributes['size'];
689 ?><a href="<?php echo esc_url( get_permalink( $inserted_page->ID ) ); ?>"><?php echo get_the_post_thumbnail( $inserted_page->ID, $size ); ?></a>
690 <?php
691 break;
692
693 case 'all':
694 // Title.
695 $title_tag = $attributes['inline'] ? 'span' : 'h1';
696 echo "<$title_tag class='insert-page-title'>";
697 echo get_the_title( $inserted_page->ID );
698 echo "</$title_tag>";
699 // Content.
700 $content = get_post_field( 'post_content', $inserted_page->ID );
701 if ( $attributes['should_apply_the_content_filter'] ) {
702 $content = apply_filters( 'the_content', $content );
703 }
704 echo $content;
705 $this->the_meta( $inserted_page->ID );
706 break;
707
708 default: // Display is either invalid, or contains a template file to use.
709 /**
710 * Legacy/compatibility code: In order to use custom templates,
711 * we use query_posts() to provide the template with the global
712 * state it requires for the inserted page (in other words, all
713 * template tags will work with respect to the inserted page
714 * instead of the parent page / main loop). Note that this may
715 * cause some compatibility issues with other plugins.
716 *
717 * @see https://codex.wordpress.org/Function_Reference/query_posts
718 */
719 if ( is_numeric( $attributes['page'] ) ) {
720 $args = array(
721 'p' => intval( $attributes['page'] ),
722 'post_type' => get_post_types(),
723 );
724 } else {
725 $args = array(
726 'name' => esc_attr( $attributes['page'] ),
727 'post_type' => get_post_types(),
728 );
729 }
730 // We save the previous query state here instead of using
731 // wp_reset_query() because wp_reset_query() only has a single stack
732 // variable ($GLOBALS['wp_the_query']). This allows us to support
733 // pages inserted into other pages (multiple nested pages).
734 $old_query = $GLOBALS['wp_query'];
735 $inserted_page = query_posts( $args );
736 if ( have_posts() ) {
737 $template = locate_template( $attributes['display'] );
738 // Only allow templates that don't have any directory traversal in
739 // them (to prevent including php files that aren't in the active
740 // theme directory or the /wp-includes/theme-compat/ directory).
741 $path_in_theme_or_childtheme_or_compat = (
742 // Template is in current theme folder.
743 0 === strpos( realpath( $template ), realpath( get_stylesheet_directory() ) ) ||
744 // Template is in current or parent theme folder.
745 0 === strpos( realpath( $template ), realpath( get_template_directory() ) ) ||
746 // Template is in theme-compat folder.
747 0 === strpos( realpath( $template ), realpath( ABSPATH . WPINC . '/theme-compat/' ) )
748 );
749 if ( strlen( $template ) > 0 && $path_in_theme_or_childtheme_or_compat ) {
750 include $template; // Execute the template code.
751 } else { // Couldn't find template, so fall back to printing a link to the page.
752 the_post();
753 ?><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
754 <?php
755 }
756 }
757 // Restore previous query and update the global template variables.
758 $GLOBALS['wp_query'] = $old_query; // phpcs:ignore WordPress.WP.GlobalVariablesOverride
759 wp_reset_postdata();
760 }
761
762 // Save output buffer contents.
763 $content = ob_get_clean();
764
765 } else { // Use "Legacy" insert method (query_posts).
766
767 // Construct query_posts arguments.
768 if ( is_numeric( $attributes['page'] ) ) {
769 $args = array(
770 'p' => intval( $attributes['page'] ),
771 'post_type' => get_post_types(),
772 'post_status' => $attributes['public'] ? array( 'publish', 'private' ) : array( 'publish' ),
773 );
774 } else {
775 $args = array(
776 'name' => esc_attr( $attributes['page'] ),
777 'post_type' => get_post_types(),
778 'post_status' => $attributes['public'] ? array( 'publish', 'private' ) : array( 'publish' ),
779 );
780 }
781
782 // We save the previous query state here instead of using
783 // wp_reset_query() because wp_reset_query() only has a single stack
784 // variable ($GLOBALS['wp_the_query']). This allows us to support
785 // pages inserted into other pages (multiple nested pages).
786 $old_query = $GLOBALS['wp_query'];
787 $posts = query_posts( $args );
788
789 // Prevent unprivileged users from inserting private posts from others.
790 if ( have_posts() ) {
791 $can_read = true;
792 $parent_post_author_id = intval( get_the_author_meta( 'ID' ) );
793 foreach ( $posts as $post ) {
794 if ( is_object( $post ) && 'publish' !== $post->post_status ) {
795 $post_type = get_post_type_object( $post->post_type );
796 if ( ! user_can( $parent_post_author_id, $post_type->cap->read_post, $post->ID ) ) {
797 $can_read = false;
798 }
799 }
800 }
801 if ( ! $can_read ) {
802 // Force an empty query so we don't show any posts.
803 $posts = query_posts( array( 'post__in' => array( 0 ) ) );
804 }
805 }
806
807 if ( have_posts() ) {
808 // Start output buffering so we can save the output to string.
809 ob_start();
810
811 // If Beaver Builder, SiteOrigin Page Builder, Elementor, or WPBakery
812 // Page Builder (Visual Composer) are enabled, load any cached styles
813 // associated with the inserted page.
814 // Note: Temporarily set the global $post->ID to the inserted page ID,
815 // since both builders rely on the id to load the appropriate styles.
816 if (
817 class_exists( 'UAGB_Post_Assets' ) ||
818 class_exists( 'FLBuilder' ) ||
819 class_exists( 'SiteOrigin_Panels' ) ||
820 class_exists( '\Elementor\Post_CSS_File' ) ||
821 defined( 'VCV_VERSION' ) ||
822 defined( 'WPB_VC_VERSION' )
823 ) {
824 // If we're not in The Loop (i.e., global $post isn't assigned),
825 // temporarily populate it with the post to be inserted so we can
826 // retrieve generated styles for that post. Reset $post to null
827 // after we're done.
828 if ( is_null( $post ) ) {
829 $old_post_id = null;
830 $post = $inserted_page; // phpcs:ignore WordPress.WP.GlobalVariablesOverride
831 } else {
832 $old_post_id = $post->ID;
833 $post->ID = $inserted_page->ID;
834 }
835
836 // Enqueue assets for Ultimate Addons for Gutenberg.
837 // See: https://ultimategutenberg.com/docs/assets-api-third-party-plugins/.
838 if ( class_exists( 'UAGB_Post_Assets' ) ) {
839 $post_assets_instance = new UAGB_Post_Assets( $inserted_page->ID );
840 $post_assets_instance->enqueue_scripts();
841 }
842
843 if ( class_exists( 'FLBuilder' ) ) {
844 FLBuilder::enqueue_layout_styles_scripts( $inserted_page->ID );
845 }
846
847 if ( class_exists( 'SiteOrigin_Panels' ) ) {
848 $renderer = SiteOrigin_Panels::renderer();
849 $renderer->add_inline_css( $inserted_page->ID, $renderer->generate_css( $inserted_page->ID ) );
850 }
851
852 if ( class_exists( '\Elementor\Post_CSS_File' ) ) {
853 $css_file = new \Elementor\Post_CSS_File( $inserted_page->ID );
854 $css_file->enqueue();
855 }
856
857 // Enqueue custom style from WPBakery Page Builder (Visual Composer).
858 if ( defined( 'VCV_VERSION' ) ) {
859 wp_enqueue_style( 'vcv:assets:front:style' );
860 wp_enqueue_script( 'vcv:assets:runtime:script' );
861 wp_enqueue_script( 'vcv:assets:front:script' );
862
863 $bundle_url = get_post_meta( $inserted_page->ID, 'vcvSourceCssFileUrl', true );
864 if ( $bundle_url ) {
865 $version = get_post_meta( $inserted_page->ID, 'vcvSourceCssFileHash', true );
866 if ( ! preg_match( '/^http/', $bundle_url ) ) {
867 if ( ! preg_match( '/assets-bundles/', $bundle_url ) ) {
868 $bundle_url = '/assets-bundles/' . $bundle_url;
869 }
870 }
871 if ( preg_match( '/^http/', $bundle_url ) ) {
872 $bundle_url = set_url_scheme( $bundle_url );
873 } elseif ( defined( 'VCV_TF_ASSETS_IN_UPLOADS' ) && constant( 'VCV_TF_ASSETS_IN_UPLOADS' ) ) {
874 $upload_dir = wp_upload_dir();
875 $bundle_url = set_url_scheme( $upload_dir['baseurl'] . '/' . VCV_PLUGIN_ASSETS_DIRNAME . '/' . ltrim( $bundle_url, '/\\' ) );
876 } elseif ( class_exists( 'VisualComposer\Helpers\AssetsEnqueue' ) ) {
877 // These methods should work for Visual Composer 26.0.
878 // Enqueue custom css/js stored in vcvSourceAssetsFiles postmeta.
879 $vc = new \VisualComposer\Helpers\AssetsEnqueue;
880 if ( method_exists( $vc, 'enqueueAssets' ) ) {
881 $vc->enqueueAssets($inserted_page->ID);
882 }
883 // Enqueue custom CSS stored in vcvSourceCssFileUrl postmeta.
884 $upload_dir = wp_upload_dir();
885 $bundle_url = set_url_scheme( $upload_dir['baseurl'] . '/' . VCV_PLUGIN_ASSETS_DIRNAME . '/' . ltrim( $bundle_url, '/\\' ) );
886 } else {
887 $bundle_url = content_url() . '/' . VCV_PLUGIN_ASSETS_DIRNAME . '/' . ltrim( $bundle_url, '/\\' );
888 }
889 wp_enqueue_style(
890 'vcv:assets:source:main:styles:' . sanitize_title( $bundle_url ),
891 $bundle_url,
892 array(),
893 VCV_VERSION . '.' . $version
894 );
895 }
896 }
897
898 // Visual Composer custom CSS.
899 if ( defined( 'WPB_VC_VERSION' ) ) {
900 // Post custom CSS.
901 $post_custom_css = get_post_meta( $inserted_page->ID, '_wpb_post_custom_css', true );
902 if ( ! empty( $post_custom_css ) ) {
903 $post_custom_css = wp_strip_all_tags( $post_custom_css );
904 echo '<style type="text/css" data-type="vc_custom-css">';
905 echo $post_custom_css;
906 echo '</style>';
907 }
908 // Shortcodes custom CSS.
909 $shortcodes_custom_css = get_post_meta( $inserted_page->ID, '_wpb_shortcodes_custom_css', true );
910 if ( ! empty( $shortcodes_custom_css ) ) {
911 $shortcodes_custom_css = wp_strip_all_tags( $shortcodes_custom_css );
912 echo '<style type="text/css" data-type="vc_shortcodes-custom-css">';
913 echo $shortcodes_custom_css;
914 echo '</style>';
915 }
916 }
917
918 // GoodLayers page builder content (retrieved from post meta).
919 // See: https://docs.goodlayers.com/add-page-builder-in-product/.
920 do_action( 'gdlr_core_print_page_builder' );
921
922 if ( is_null( $old_post_id ) ) {
923 $post = null; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
924 } else {
925 $post->ID = $old_post_id;
926 }
927 }
928
929 /**
930 * Show either the title, link, content, everything, or everything via a
931 * custom template.
932 *
933 * Note: if the sharing_display filter exists, it means Jetpack is
934 * installed and Sharing is enabled; this plugin conflicts with Sharing,
935 * because Sharing assumes the_content and the_excerpt filters are only
936 * getting called once. The fix here is to disable processing of filters
937 * on the_content in the inserted page.
938 *
939 * @see https://codex.wordpress.org/Function_Reference/the_content#Alternative_Usage
940 */
941 switch ( $attributes['display'] ) {
942 case 'title':
943 the_post();
944 $title_tag = $attributes['inline'] ? 'span' : 'h1';
945 echo "<$title_tag class='insert-page-title'>";
946 the_title();
947 echo "</$title_tag>";
948 break;
949 case 'link':
950 the_post();
951 ?><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
952 <?php
953 break;
954 case 'excerpt':
955 the_post();
956 ?><h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>
957 <?php
958 if ( $attributes['should_apply_the_content_filter'] ) {
959 the_excerpt();
960 } else {
961 echo get_the_excerpt();
962 }
963 break;
964 case 'excerpt-only':
965 the_post();
966 if ( $attributes['should_apply_the_content_filter'] ) {
967 the_excerpt();
968 } else {
969 echo get_the_excerpt();
970 }
971 break;
972 case 'content':
973 // If Elementor is installed, try to render the page with it. If there is no Elementor content, fall back to normal rendering.
974 if ( class_exists( '\Elementor\Plugin' ) ) {
975 $elementor_content = \Elementor\Plugin::$instance->frontend->get_builder_content( $inserted_page->ID );
976 if ( strlen( $elementor_content ) > 0 ) {
977 echo $elementor_content;
978 break;
979 }
980 }
981 // Render the content normally.
982 the_post();
983 if ( $attributes['should_apply_the_content_filter'] ) {
984 the_content();
985 } else {
986 echo get_the_content();
987 }
988 // Render any <!--nextpage--> pagination links.
989 wp_link_pages( array(
990 'before' => '<div class="page-links">' . __( 'Pages:', 'twentynineteen' ),
991 'after' => '</div>',
992 ) );
993 break;
994 case 'post-thumbnail':
995 $size = empty( $attributes['size'] ) ? 'post-thumbnail' : $attributes['size'];
996 ?><a href="<?php echo esc_url( get_permalink( $inserted_page->ID ) ); ?>"><?php echo get_the_post_thumbnail( $inserted_page->ID, $size ); ?></a>
997 <?php
998 break;
999 case 'all':
1000 the_post();
1001 $title_tag = $attributes['inline'] ? 'span' : 'h1';
1002 echo "<$title_tag class='insert-page-title'>";
1003 the_title();
1004 echo "</$title_tag>";
1005 if ( $attributes['should_apply_the_content_filter'] ) {
1006 the_content();
1007 } else {
1008 echo get_the_content();
1009 }
1010 $this->the_meta();
1011 // Render any <!--nextpage--> pagination links.
1012 wp_link_pages( array(
1013 'before' => '<div class="page-links">' . __( 'Pages:', 'twentynineteen' ),
1014 'after' => '</div>',
1015 ) );
1016 break;
1017 default: // Display is either invalid, or contains a template file to use.
1018 $template = locate_template( $attributes['display'] );
1019 // Only allow templates that don't have any directory traversal in
1020 // them (to prevent including php files that aren't in the active
1021 // theme directory or the /wp-includes/theme-compat/ directory).
1022 $path_in_theme_or_childtheme_or_compat = (
1023 // Template is in current theme folder.
1024 0 === strpos( realpath( $template ), realpath( get_stylesheet_directory() ) ) ||
1025 // Template is in current or parent theme folder.
1026 0 === strpos( realpath( $template ), realpath( get_template_directory() ) ) ||
1027 // Template is in theme-compat folder.
1028 0 === strpos( realpath( $template ), realpath( ABSPATH . WPINC . '/theme-compat/' ) )
1029 );
1030 if ( strlen( $template ) > 0 && $path_in_theme_or_childtheme_or_compat ) {
1031 include $template; // Execute the template code.
1032 } else { // Couldn't find template, so fall back to printing a link to the page.
1033 the_post();
1034 ?><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
1035 <?php
1036 }
1037 break;
1038 }
1039 // Save output buffer contents.
1040 $content = ob_get_clean();
1041 } else {
1042 /**
1043 * Filter the html that should be displayed if an inserted page was not found.
1044 *
1045 * @param string $content html to be displayed. Defaults to an empty string.
1046 */
1047 $content = apply_filters( 'insert_pages_not_found_message', $content );
1048 }
1049 // Restore previous query and update the global template variables.
1050 $GLOBALS['wp_query'] = $old_query; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
1051 wp_reset_postdata();
1052 }
1053
1054 /**
1055 * Filter the markup generated for the inserted page.
1056 *
1057 * @param string $content The post content of the inserted page.
1058 * @param object $inserted_page The post object returned from querying the inserted page.
1059 * @param array $attributes Extra parameters modifying the inserted page.
1060 * page: Page ID or slug of page to be inserted.
1061 * display: Content to display from inserted page.
1062 * class: Extra classes to add to inserted page wrapper element.
1063 * id: Optional ID for the inserted page wrapper element.
1064 * inline: Boolean indicating wrapper element should be a span.
1065 * public: Boolean indicating anonymous users can see private inserted pages.
1066 * querystring: Extra querystring values provided to the custom template.
1067 * should_apply_the_content_filter: Whether to apply the_content filter to post contents and excerpts.
1068 * wrapper_tag: Tag to use for the wrapper element (e.g., div, span).
1069 */
1070 $content = apply_filters( 'insert_pages_wrap_content', $content, $inserted_page, $attributes );
1071
1072 // Unset any querystring params included in the shortcode.
1073 $_GET = $original_get;
1074 $_REQUEST = $original_request;
1075 $GLOBALS['wp']->query_vars = $original_wp_query_vars;
1076
1077 // Loop detection: remove the page from the stack (so we can still insert
1078 // the same page multiple times on another page, but prevent it from being
1079 // inserted multiple times within the same recursive chain).
1080 if ( isset( $inserted_page->ID ) ) {
1081 foreach ( $this->inserted_page_ids as $key => $page_id ) {
1082 if ( $page_id === $inserted_page->ID ) {
1083 unset( $this->inserted_page_ids[ $key ] );
1084 }
1085 }
1086 } elseif ( is_array( $inserted_page ) && ! empty( $inserted_page ) ) {
1087 // Legacy template code populates $inserted_page with query_posts()
1088 // output. Remove each from the stack (should just be a single page).
1089 foreach ( $inserted_page as $page ) {
1090 foreach ( $this->inserted_page_ids as $key => $page_id ) {
1091 if ( $page_id === $page->ID ) {
1092 unset( $this->inserted_page_ids[ $key ] );
1093 }
1094 }
1095 }
1096 }
1097
1098 return $content;
1099 }
1100
1101 /**
1102 * Default filter for insert_pages_wrap_content.
1103 *
1104 * @param string $content Content of shortcode.
1105 * @param array $posts Post data of inserted page.
1106 * @param array $attributes Shortcode attributes.
1107 * @return string Content to replace shortcode.
1108 */
1109 public function insert_pages_wrap_content( $content, $posts, $attributes ) {
1110 return sprintf(
1111 '<%1$s data-post-id="%2$s" class="insert-page insert-page-%2$s %3$s"%4$s>%5$s</%1$s>',
1112 esc_attr( $attributes['wrapper_tag'] ),
1113 esc_attr( $attributes['page'] ),
1114 esc_attr( $attributes['class'] ),
1115 empty( $attributes['id'] ) ? '' : ' id="' . esc_attr( $attributes['id'] ) . '"',
1116 $content
1117 );
1118 }
1119
1120 /**
1121 * Filter hook: Add a button to the TinyMCE toolbar for our insert page tool.
1122 *
1123 * @param array $buttons TinyMCE buttons.
1124 * @return array TinyMCE buttons with Insert Pages button.
1125 */
1126 public function insert_pages_handle_filter_mce_buttons( $buttons ) {
1127 if ( self::$is_admin_initialized && ! in_array( 'wpInsertPages_button', $buttons, true ) ) {
1128 array_push( $buttons, 'wpInsertPages_button' );
1129 }
1130 return $buttons;
1131 }
1132
1133 /**
1134 * Filter hook: Load the javascript for our custom toolbar button.
1135 *
1136 * @param array $plugins TinyMCE plugins.
1137 * @return array TinyMCE plugins with Insert Pages plugin.
1138 */
1139 public function insert_pages_handle_filter_mce_external_plugins( $plugins ) {
1140 if ( self::$is_admin_initialized && ! array_key_exists( 'wpInsertPages', $plugins ) ) {
1141 $plugins['wpInsertPages'] = plugins_url( '/js/wpinsertpages_plugin.js', __FILE__ );
1142 }
1143 return $plugins;
1144 }
1145
1146 /**
1147 * Helper function to generate an excerpt (outside of the Loop) for a given
1148 * ID (based on wp_trim_excerpt()).
1149 *
1150 * @param string $text Excerpt.
1151 * @param integer $post_id Post ID of excerpt.
1152 * @param boolean $apply_the_content_filter Whether to apply `the_content`.
1153 * @return string Excerpt.
1154 */
1155 public function insert_pages_trim_excerpt( $text = '', $post_id = 0, $apply_the_content_filter = true ) {
1156 $post_id = intval( $post_id );
1157 if ( $post_id < 1 ) {
1158 return '';
1159 }
1160
1161 $raw_excerpt = $text;
1162 if ( '' === $text ) {
1163 $text = get_post_field( 'post_content', $post_id );
1164
1165 $text = strip_shortcodes( $text );
1166
1167 // Look for a <!--more--> quicktag and trim the excerpt there if it exists.
1168 $has_more_quicktag = false;
1169 if ( preg_match( '/<!--more(.*?)?-->/', $text, $matches ) ) {
1170 $has_more_quicktag = true;
1171 $text = explode( $matches[0], $text, 2 );
1172 $text = $text[0];
1173 // Look for a custom <!--crop--> quicktag that will trim any text before
1174 // it out of the excerpt.
1175 if ( preg_match( '/<!--crop-->/', $text, $matches ) ) {
1176 $text = explode( $matches[0], $text, 2 );
1177 $text = $text[1];
1178 }
1179 }
1180
1181 /** This filter is documented in wp-includes/post-template.php */
1182 if ( $apply_the_content_filter ) {
1183 $text = apply_filters( 'the_content', $text );
1184 }
1185 $text = str_replace( ']]>', ']]&gt;', $text );
1186
1187 // Only trim excerpt if there wasn't an existing <!--more--> quicktag.
1188 if ( ! $has_more_quicktag ) {
1189 /**
1190 * Filter the number of words in an excerpt.
1191 *
1192 * @since 2.7.0
1193 *
1194 * @param int $number The number of words. Default 55.
1195 */
1196 $excerpt_length = apply_filters( 'excerpt_length', 55 );
1197
1198 /**
1199 * Filter the string in the "more" link displayed after a trimmed excerpt.
1200 *
1201 * @since 2.9.0
1202 *
1203 * @param string $more_string The string shown within the more link.
1204 */
1205 global $post;
1206 if ( isset( $post->ID ) ) {
1207 $old_post_id = $post->ID;
1208 $post->ID = $post_id;
1209 }
1210 $excerpt_more = apply_filters( 'excerpt_more', ' [&hellip;]' );
1211 if ( isset( $post->ID ) ) {
1212 $post->ID = $old_post_id;
1213 }
1214
1215 $text = wp_trim_words( $text, $excerpt_length, $excerpt_more );
1216 }
1217 }
1218 /**
1219 * Filter the trimmed excerpt string.
1220 *
1221 * @since 2.8.0
1222 *
1223 * @param string $text The trimmed text.
1224 * @param string $raw_excerpt The text prior to trimming.
1225 */
1226 return apply_filters( 'wp_trim_excerpt', $text, $raw_excerpt );
1227 }
1228
1229 /**
1230 * Modified from /wp-includes/class-wp-editor.php, function
1231 * wp_link_dialog().
1232 *
1233 * Dialog for internal linking.
1234 *
1235 * @since 3.1.0
1236 */
1237 public function insert_pages_wp_tinymce_dialog() {
1238 // Don't run if required scripts and styles weren't enqueued.
1239 if ( ! self::$is_admin_initialized ) {
1240 return;
1241 }
1242
1243 // Run once.
1244 if ( self::$link_dialog_printed ) {
1245 return;
1246 }
1247
1248 self::$link_dialog_printed = true;
1249
1250 $formats = array(
1251 'title' => __( 'Title', 'insert-pages' ),
1252 'link' => __( 'Link', 'insert-pages' ),
1253 'excerpt' => __( 'Excerpt with title', 'insert-pages' ),
1254 'excerpt-only' => __( 'Excerpt only (no title)', 'insert-pages' ),
1255 'content' => __( 'Content', 'insert-pages' ),
1256 'post-thumbnail' => __( 'Post Thumbnail', 'insert-pages' ),
1257 'all' => __( 'All (includes custom fields)', 'insert-pages' ),
1258 'template' => __( 'Use a custom template', 'insert-pages' ) . ' &raquo;',
1259 );
1260
1261 $templates = array(
1262 'all' => __( 'Default Template', 'insert-pages' ),
1263 );
1264 foreach ( wp_get_theme()->get_page_templates() as $file => $name ) {
1265 $templates[ $file ] = $name;
1266 }
1267
1268 $sizes = function_exists( 'wp_get_registered_image_subsizes' ) ? array_keys( wp_get_registered_image_subsizes() ) : get_intermediate_image_sizes();
1269
1270 /**
1271 * Filter the available templates shown in the template dropdown.
1272 *
1273 * @param array $templates Array of template names keyed by their filename.
1274 */
1275 $templates = apply_filters( 'insert_pages_available_templates', $templates );
1276
1277 // Get default values for the TinyMCE dialog fields. Note: can be
1278 // overridden by the `insert_pages_tinymce_state` filter.
1279 $tinymce_state = $this->get_tinymce_state();
1280
1281 // Get ID of post currently being edited.
1282 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1283 $post_id = isset( $_REQUEST['post'] ) && intval( $_REQUEST['post'] ) > 0 ? intval( $_REQUEST['post'] ) : '';
1284
1285 // display: none is required here, see #WP27605.
1286 ?>
1287 <div id="wp-insertpage-backdrop" style="display: none"></div>
1288 <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">
1289 <form id="wp-insertpage" tabindex="-1">
1290 <?php wp_nonce_field( 'internal-inserting', '_ajax_inserting_nonce', false ); ?>
1291 <input type="hidden" id="insertpage-parent-page-id" value="<?php echo esc_attr( $post_id ); ?>" />
1292 <h1 id="insertpage-modal-title"><?php _e( 'Insert page', 'insert-pages' ); ?></h1>
1293 <button type="button" id="wp-insertpage-close"><span class="screen-reader-text"><?php _e( 'Close' ); ?></span></button>
1294 <div id="insertpage-selector">
1295 <div id="insertpage-search-panel">
1296 <div class="insertpage-search-wrapper">
1297 <label>
1298 <span class="search-label"><?php _e( 'Search', 'insert-pages' ); ?></span>
1299 <input type="search" id="insertpage-search-field" class="insertpage-search-field" autocomplete="off" />
1300 <span class="spinner"></span>
1301 </label>
1302 </div>
1303 <div id="insertpage-search-results" class="query-results" tabindex="0">
1304 <ul></ul>
1305 <div class="river-waiting">
1306 <span class="spinner"></span>
1307 </div>
1308 </div>
1309 <div id="insertpage-most-recent-results" class="query-results" tabindex="0">
1310 <div class="query-notice" id="insertpage-query-notice-message">
1311 <em class="query-notice-default"><?php _e( 'No search term specified. Showing recent items.', 'insert-pages' ); ?></em>
1312 <em class="query-notice-hint screen-reader-text"><?php _e( 'Search or use up and down arrow keys to select an item.' ); ?></em>
1313 </div>
1314 <ul></ul>
1315 <div class="river-waiting">
1316 <span class="spinner"></span>
1317 </div>
1318 </div>
1319 </div>
1320 <p class="howto" id="insertpage-options-toggle"><?php _e( 'Options', 'insert-pages' ); ?></p>
1321 <div id="insertpage-options-panel">
1322 <div class="insertpage-options-wrapper">
1323 <label for="insertpage-slug-field">
1324 <span><?php _e( 'Slug or ID', 'insert-pages' ); ?></span>
1325 <input id="insertpage-slug-field" type="text" autocomplete="off" />
1326 <input id="insertpage-page-id" type="hidden" />
1327 </label>
1328 </div>
1329 <div class="insertpage-format">
1330 <label for="insertpage-format-select">
1331 <?php _e( 'Display', 'insert-pages' ); ?>
1332 </label>
1333 <select name="insertpage-format-select" id="insertpage-format-select">
1334 <?php foreach ( $formats as $format => $label ) : ?>
1335 <option value='<?php echo esc_attr( $format ); ?>' <?php selected( $tinymce_state['format'], $format ); ?>><?php echo esc_html( $label ); ?></option>
1336 <?php endforeach; ?>
1337 </select>
1338 <select name="insertpage-template-select" id="insertpage-template-select" disabled="true">
1339 <?php foreach ( $templates as $template => $label ) : ?>
1340 <option value='<?php echo esc_attr( $template ); ?>' <?php selected( $tinymce_state['template'], $template ); ?>><?php echo esc_html( $label ); ?></option>
1341 <?php endforeach; ?>
1342 </select>
1343 <select name="insertpage-size-select" id="insertpage-size-select" disabled="true">
1344 <?php foreach ( $sizes as $size ) : ?>
1345 <option value='<?php echo esc_attr( $size ); ?>' <?php selected( $tinymce_state['size'], $size ); ?>><?php echo esc_html( $size ); ?></option>
1346 <?php endforeach; ?>
1347 </select>
1348 </div>
1349 <div class="insertpage-extra">
1350 <label for="insertpage-extra-classes">
1351 <?php _e( 'Extra Classes', 'insert-pages' ); ?>
1352 <input id="insertpage-extra-classes" type="text" autocomplete="off" value="<?php echo empty( $tinymce_state['class'] ) ? '' : esc_attr( $tinymce_state['class'] ); ?>" />
1353 </label>
1354 <label for="insertpage-extra-id">
1355 <?php _e( 'ID', 'insert-pages' ); ?>
1356 <input id="insertpage-extra-id" type="text" autocomplete="off" value="<?php echo empty( $tinymce_state['id'] ) ? '' : esc_attr( $tinymce_state['id'] ); ?>" />
1357 </label>
1358 <label for="insertpage-extra-inline">
1359 <?php _e( 'Inline?', 'insert-pages' ); ?>
1360 <input id="insertpage-extra-inline" type="checkbox" <?php checked( $tinymce_state['inline'] ); ?> />
1361 </label>
1362 <br class="<?php echo empty( $tinymce_state['hide_querystring'] ) ? '' : 'hidden'; ?>" />
1363 <label for="insertpage-extra-querystring" class="<?php echo empty( $tinymce_state['hide_querystring'] ) ? '' : 'hidden'; ?>">
1364 <?php _e( 'Querystring', 'insert-pages' ); ?>
1365 <input id="insertpage-extra-querystring" type="text" autocomplete="off" value="<?php echo empty( $tinymce_state['querystring'] ) ? '' : esc_attr( $tinymce_state['querystring'] ); ?>" />
1366 </label>
1367 <br class="<?php echo empty( $tinymce_state['hide_public'] ) ? '' : 'hidden'; ?>" />
1368 <label for="insertpage-extra-public" class="<?php echo empty( $tinymce_state['hide_public'] ) ? '' : 'hidden'; ?>">
1369 <input id="insertpage-extra-public" type="checkbox" <?php checked( $tinymce_state['public'] ); ?> />
1370 <?php _e( 'Anonymous users can see this inserted even if its status is private', 'insert-pages' ); ?>
1371 </label>
1372 </div>
1373 </div>
1374 </div>
1375 <div class="submitbox">
1376 <div id="wp-insertpage-cancel">
1377 <button type="button" class="button"><?php _e( 'Cancel', 'insert-pages' ); ?></button>
1378 </div>
1379 <div id="wp-insertpage-update">
1380 <input type="submit" value="<?php esc_attr_e( 'Insert Page', 'insert-pages' ); ?>" class="button button-primary" id="wp-insertpage-submit" name="wp-insertpage-submit">
1381 </div>
1382 </div>
1383 </form>
1384 </div>
1385 <?php
1386 }
1387
1388 /**
1389 * Modified from:
1390 * Internal linking functions.
1391 *
1392 * @package WordPress
1393 * @subpackage Administration
1394 * @since 3.1.0
1395 */
1396 public function insert_pages_insert_page_callback() {
1397 check_ajax_referer( 'internal-inserting', '_ajax_inserting_nonce' );
1398 $args = array();
1399
1400 // If a URL is provided, translate it to a post ID and search on that.
1401 if ( filter_var( $_POST['search'], FILTER_VALIDATE_URL ) ) {
1402 $post_id = url_to_postid( $_POST['search'] );
1403 if ( ! empty( $post_id ) ) {
1404 $_POST['search'] = $post_id;
1405 $_POST['type'] = 'post_id';
1406 }
1407 }
1408
1409 if ( isset( $_POST['search'] ) ) {
1410 $args['s'] = wp_unslash( $_POST['search'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1411 }
1412 $args['pagenum'] = ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 1;
1413 $args['pageID'] = ! empty( $_POST['pageID'] ) ? absint( $_POST['pageID'] ) : 0;
1414
1415 // Change search to slug or post ID if we're not doing a plaintext
1416 // search (e.g., if we're editing an existing shortcode and the
1417 // search field is populated with the post's slug or ID).
1418 if ( isset( $_POST['type'] ) && 'slug' === $_POST['type'] ) {
1419 $args['name'] = $args['s'];
1420 unset( $args['s'] );
1421 } elseif ( array_key_exists( 'type', $_POST ) && 'post_id' === $_POST['type'] ) {
1422 $args['p'] = $args['s'];
1423 unset( $args['s'] );
1424 }
1425
1426 $results = $this->insert_pages_wp_query( $args );
1427
1428 // Fail if our query didn't work.
1429 if ( ! isset( $results ) ) {
1430 die( '0' );
1431 }
1432
1433 echo wp_json_encode( $results );
1434 echo "\n";
1435 die();
1436 }
1437
1438 /**
1439 * Save the user's last-selected display or template in the TinyMCE widget
1440 * whenever it changes.
1441 *
1442 * @hook wp_ajax_insertpage_save_presets
1443 */
1444 public function insert_pages_save_presets() {
1445 check_ajax_referer( 'internal-inserting', '_ajax_inserting_nonce' );
1446 $args = array();
1447 if ( isset( $_POST['format'] ) ) {
1448 $args['format'] = sanitize_key( wp_unslash( $_POST['format'] ) );
1449 }
1450 if ( isset( $_POST['template'] ) ) {
1451 $args['template'] = sanitize_file_name( wp_unslash( $_POST['template'] ) );
1452 }
1453
1454 if ( ! empty( $args ) ) {
1455 $tinymce_state = get_user_meta( get_current_user_id(), 'insert_pages_tinymce_state', true );
1456 if ( empty( $tinymce_state ) ) {
1457 $tinymce_state = array(
1458 'format' => 'title',
1459 'template' => 'all',
1460 );
1461 }
1462 $tinymce_state = array_merge( $tinymce_state, $args );
1463 update_user_meta( get_current_user_id(), 'insert_pages_tinymce_state', $tinymce_state );
1464 }
1465
1466 // Fail if our query didn't work.
1467 if ( ! isset( $results ) ) {
1468 die( '0' );
1469 }
1470
1471 echo wp_json_encode( 'Success' );
1472 echo "\n";
1473 die();
1474 }
1475
1476 /**
1477 * Modified from:
1478 * Performs post queries for internal linking.
1479 *
1480 * @since 3.1.0
1481 * @param array $args Optional. Accepts 'pagenum' and 's' (search) arguments.
1482 * @return array Results.
1483 */
1484 private function insert_pages_wp_query( $args = array() ) {
1485 $pts = get_post_types( array( 'public' => true ), 'objects' );
1486 $post_types = array_keys( $pts );
1487
1488 /**
1489 * Filter the post types that appear in the list of pages to insert.
1490 *
1491 * By default, all post types will apear.
1492 *
1493 * @since 2.0
1494 *
1495 * @param array $post_types Array of post type names to include.
1496 */
1497 $post_types = apply_filters( 'insert_pages_available_post_types', $post_types );
1498
1499 $query = array(
1500 'post_type' => $post_types,
1501 'suppress_filters' => true,
1502 'update_post_term_cache' => false,
1503 'update_post_meta_cache' => false,
1504 'post_status' => array( 'publish', 'private' ),
1505 'order' => 'DESC',
1506 'orderby' => 'post_date',
1507 'posts_per_page' => 20,
1508 // 'post__not_in' => array( $args['pageID'] ), // Remove?
1509 );
1510
1511 // Show non-admins only their own posts if the option is enabled.
1512 $options = get_option( 'wpip_settings' );
1513 if (
1514 ! empty( $options['wpip_classic_editor_hide_others_posts'] ) &&
1515 'enabled' === $options['wpip_classic_editor_hide_others_posts'] &&
1516 ! current_user_can( 'edit_others_posts' )
1517 ) {
1518 $query['author'] = get_current_user_id();
1519 }
1520
1521 $args['pagenum'] = isset( $args['pagenum'] ) ? absint( $args['pagenum'] ) : 1;
1522 $query['offset'] = $args['pagenum'] > 1 ? $query['posts_per_page'] * ( $args['pagenum'] - 1 ) : 0;
1523
1524 // Search post content and post title.
1525 if ( isset( $args['s'] ) ) {
1526 $query['s'] = $args['s'];
1527 }
1528
1529 // Search post_name (post slugs).
1530 if ( isset( $args['name'] ) ) {
1531 $query['name'] = $args['name'];
1532 }
1533
1534 // Search post ids.
1535 if ( isset( $args['p'] ) ) {
1536 $query['p'] = $args['p'];
1537 }
1538
1539 // Do main query.
1540 $get_posts = new WP_Query();
1541 $posts = $get_posts->query( $query );
1542 // Check if any posts were found.
1543 if ( ! $get_posts->post_count ) {
1544 return false;
1545 }
1546
1547 // Build results.
1548 $results = array();
1549 foreach ( $posts as $post ) {
1550 // Prevent unprivileged users (e.g., Contributors) from seeing and
1551 // inserting other user's private posts.
1552 $post_type = get_post_type_object( $post->post_type );
1553 if ( 'publish' !== $post->post_status && ! current_user_can( $post_type->cap->read_post, $post->ID ) ) {
1554 continue;
1555 }
1556
1557 if ( 'post' === $post->post_type ) {
1558 $info = mysql2date( 'Y/m/d', $post->post_date );
1559 } else {
1560 $info = $pts[ $post->post_type ]->labels->singular_name;
1561 }
1562 $results[] = array(
1563 'ID' => $post->ID,
1564 'title' => trim( esc_html( wp_strip_all_tags( get_the_title( $post ) ) ) ),
1565 'permalink' => get_permalink( $post->ID ),
1566 'slug' => $post->post_name,
1567 'path' => get_page_uri( $post ),
1568 'info' => $info,
1569 'status' => get_post_status( $post ),
1570 );
1571 }
1572 return $results;
1573 }
1574
1575 /**
1576 * Add Insert Page quicktag button to Text editor.
1577 *
1578 * @hook admin_print_footer_scripts
1579 *
1580 * @return void
1581 */
1582 public function insert_pages_add_quicktags() {
1583 if ( wp_script_is( 'quicktags' ) ) : ?>
1584 <script type="text/javascript">
1585 window.onload = function() {
1586 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 );
1587 }
1588 </script>
1589 <?php
1590 endif;
1591 }
1592
1593 /**
1594 * Indicates whether a particular post type is able to be inserted.
1595 *
1596 * @param boolean $type Post type.
1597 * @return boolean Whether post type is insertable.
1598 */
1599 private function is_post_type_insertable( $type ) {
1600 return ! in_array(
1601 $type,
1602 array(
1603 'nav_menu_item',
1604 'attachment',
1605 'revision',
1606 'customize_changeset',
1607 'oembed_cache',
1608 // Exclude Flamingo messages (created via Contact Form 7 submissions).
1609 // See: https://wordpress.org/support/topic/plugin-hacked-14/
1610 'flamingo_inbound',
1611 ),
1612 true
1613 );
1614 }
1615
1616 /**
1617 * Fetch the default values for the TinyMCE modal fields.
1618 */
1619 private function get_tinymce_state() {
1620 // Get user's previously selected display and template to restore (if any).
1621 $tinymce_state = get_user_meta( get_current_user_id(), 'insert_pages_tinymce_state', true );
1622 if ( empty( $tinymce_state ) ) {
1623 $tinymce_state = array();
1624 }
1625
1626 // Merge user's format and template defaults with global defaults.
1627 $tinymce_state = wp_parse_args(
1628 $tinymce_state,
1629 array(
1630 'format' => 'title',
1631 'template' => 'all',
1632 'class' => '',
1633 'id' => '',
1634 'querystring' => '',
1635 'size' => '',
1636 'inline' => false,
1637 'public' => false,
1638 'hide_querystring' => false,
1639 'hide_public' => false,
1640 )
1641 );
1642
1643 /**
1644 * Filter the TinyMCE dialog field defaults.
1645 *
1646 * @param array $tinymce_state Array of field defaults for the TinyMCE modal.
1647 * 'format' (string) Display format. Default 'title'.
1648 * 'template' (string) Custom template. Default 'all'.
1649 * 'class' (string) HTML wrapper class. Default ''.
1650 * 'id' (string) HTML wrapper id. Default ''.
1651 * 'querystring' (string) Querystring params. Default ''.
1652 * 'size' (string) Image size when using format='thumbnail'.
1653 * 'inline' (bool) Use <span> element for wrapper. Default false.
1654 * 'public' (bool) Whether anonymous users can see this page if
1655 * its status is Private. Default false.
1656 * 'hide_querystring' (bool) Skip rendering querystring field. Default false.
1657 * 'hide_public' (bool) Skip rendering public field. Default false.
1658 */
1659 $tinymce_state = apply_filters( 'insert_pages_tinymce_state', $tinymce_state );
1660
1661 return $tinymce_state;
1662 }
1663
1664 /**
1665 * Registers the theme widget for inserting a page into an area.
1666 *
1667 * @return void
1668 */
1669 public function insert_pages_widgets_init() {
1670 register_widget( 'InsertPagesWidget' );
1671 }
1672
1673 /**
1674 * Render post meta as an unordered list.
1675 *
1676 * Note: This function sanitizes postmeta value via wp_kses_post(); the
1677 * core WordPress function the_meta() does not.
1678 *
1679 * @see https://developer.wordpress.org/reference/functions/the_meta/
1680 *
1681 * @param int $post_id Post ID.
1682 */
1683 public function the_meta( $post_id = 0 ) {
1684 if ( empty( $post_id ) ) {
1685 $post_id = get_the_ID();
1686 }
1687
1688 $keys = get_post_custom_keys( $post_id );
1689 if ( $keys ) {
1690 $li_html = '';
1691 foreach ( (array) $keys as $key ) {
1692 $keyt = trim( $key );
1693 if ( is_protected_meta( $keyt, 'post' ) ) {
1694 continue;
1695 }
1696
1697 $values = array_map( 'trim', get_post_custom_values( $key, $post_id ) );
1698 $value = implode( ', ', $values );
1699
1700 // Sanitize post meta values.
1701 $value = wp_kses_post( $value );
1702
1703 $html = sprintf(
1704 "<li><span class='post-meta-key'>%s</span> %s</li>\n",
1705 /* translators: %s: Post custom field name. */
1706 sprintf( _x( '%s:', 'Post custom field name' ), $key ),
1707 $value
1708 );
1709
1710 /**
1711 * Filters the HTML output of the li element in the post custom fields list.
1712 *
1713 * @since 2.2.0
1714 *
1715 * @param string $html The HTML output for the li element.
1716 * @param string $key Meta key.
1717 * @param string $value Meta value.
1718 */
1719 $li_html .= apply_filters( 'the_meta_key', $html, $key, $value );
1720 }
1721
1722 if ( $li_html ) {
1723 echo "<ul class='post-meta'>\n{$li_html}</ul>\n";
1724 }
1725 }
1726 }
1727
1728 }
1729 }
1730
1731 // Initialize InsertPagesPlugin object.
1732 if ( class_exists( 'InsertPagesPlugin' ) ) {
1733 $insert_pages_plugin = InsertPagesPlugin::get_instance();
1734 }
1735
1736 // Actions and Filters handled by InsertPagesPlugin class.
1737 if ( isset( $insert_pages_plugin ) ) {
1738 // Include the code that generates the options page.
1739 require_once dirname( __FILE__ ) . '/options.php';
1740
1741 // Get options set in WordPress dashboard (Settings > Insert Pages).
1742 $options = get_option( 'wpip_settings' );
1743 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 ) ) {
1744 $options = wpip_set_defaults();
1745 }
1746
1747 // Register shortcode [insert ...].
1748 add_action( 'init', array( $insert_pages_plugin, 'insert_pages_init' ), 1 );
1749 // Register shortcode [insert ...] when TinyMCE is included in a frontend ACF form.
1750 add_action( 'acf_head-input', array( $insert_pages_plugin, 'insert_pages_init' ), 1 ); // ACF 3.
1751 add_action( 'acf/input/admin_head', array( $insert_pages_plugin, 'insert_pages_init' ), 1 ); // ACF 4.
1752
1753 // Add TinyMCE button for shortcode.
1754 add_action( 'admin_head', array( $insert_pages_plugin, 'insert_pages_admin_init' ), 1 );
1755
1756 // Add quicktags button for shortcode.
1757 add_action( 'admin_print_footer_scripts', array( $insert_pages_plugin, 'insert_pages_add_quicktags' ) );
1758
1759 // Preload TinyMCE popup.
1760 add_action( 'before_wp_tiny_mce', array( $insert_pages_plugin, 'insert_pages_wp_tinymce_dialog' ), 1 );
1761
1762 // Ajax: Populate page search in TinyMCE button popup.
1763 add_action( 'wp_ajax_insertpage', array( $insert_pages_plugin, 'insert_pages_insert_page_callback' ) );
1764
1765 // Ajax: save user's last selected display and template inputs.
1766 add_action( 'wp_ajax_insertpage_save_presets', array( $insert_pages_plugin, 'insert_pages_save_presets' ) );
1767
1768 // Use internal filter to wrap inserted content in a div or span.
1769 add_filter( 'insert_pages_wrap_content', array( $insert_pages_plugin, 'insert_pages_wrap_content' ), 10, 3 );
1770
1771 /**
1772 * Register TinyMCE plugin for the toolbar button if in compatibility mode.
1773 * (to work around a SiteOrigin PageBuilder bug).
1774 *
1775 * @see https://wordpress.org/support/topic/button-in-the-toolbar-of-tinymce-disappear-conflict-page-builder/
1776 */
1777 if ( 'compatibility' === $options['wpip_tinymce_filter'] ) {
1778 add_filter( 'mce_external_plugins', array( $insert_pages_plugin, 'insert_pages_handle_filter_mce_external_plugins' ) );
1779 add_filter( 'mce_buttons', array( $insert_pages_plugin, 'insert_pages_handle_filter_mce_buttons' ) );
1780 }
1781
1782 // Register Insert Pages shortcode widget.
1783 require_once dirname( __FILE__ ) . '/widget.php';
1784 add_action( 'widgets_init', array( $insert_pages_plugin, 'insert_pages_widgets_init' ) );
1785 }
1786