Insert Pages - Version 3.7.0

Version Description

  • Security: Prevent unprivileged users from inserting private posts by others.
  • Security: Filter out possible XSS in post meta using wp_kses_post() when using display=all.
  • New Setting: Only show Authors and Contributors their own content in the TinyMCE Insert Pages popup.
Download this release

Release Info

Developer figureone
Plugin Icon wp plugin Insert Pages
Version 3.7.0
Comparing to
See all releases

Code changes from version 3.6.1 to 3.7.0

Files changed (3) hide show
  1. insert-pages.php +103 -35
  2. options.php +28 -0
  3. readme.txt +11 -2
insert-pages.php CHANGED
@@ -7,7 +7,8 @@
7
  * Text Domain: insert-pages
8
  * Domain Path: /languages
9
  * License: GPL2
10
- * Version: 3.6.1
 
11
  *
12
  * @package insert-pages
13
  */
@@ -422,6 +423,15 @@ if ( ! class_exists( 'InsertPagesPlugin' ) ) {
422
  $inserted_page = get_post( intval( $attributes['page'] ) );
423
  }
424
 
 
 
 
 
 
 
 
 
 
425
  // If inserted page's status is private, don't show to anonymous users
426
  // unless 'public' option is set.
427
  if ( is_object( $inserted_page ) && 'private' === $inserted_page->post_status && ! $attributes['public'] ) {
@@ -648,38 +658,7 @@ if ( ! class_exists( 'InsertPagesPlugin' ) ) {
648
  $content = apply_filters( 'the_content', $content );
649
  }
650
  echo $content;
651
- /**
652
- * Meta.
653
- *
654
- * @see https://core.trac.wordpress.org/browser/tags/4.4/src/wp-includes/post-template.php#L968
655
- */
656
- $keys = get_post_custom_keys( $inserted_page->ID );
657
- if ( $keys ) {
658
- echo "<ul class='post-meta'>\n";
659
- foreach ( (array) $keys as $key ) {
660
- $keyt = trim( $key );
661
- if ( is_protected_meta( $keyt, 'post' ) ) {
662
- continue;
663
- }
664
- $value = get_post_custom_values( $key, $inserted_page->ID );
665
- if ( is_array( $value ) ) {
666
- $values = array_map( 'trim', $value );
667
- $value = implode( $values, ', ' );
668
- }
669
-
670
- /**
671
- * Filter the HTML output of the li element in the post custom fields list.
672
- *
673
- * @since 2.2.0
674
- *
675
- * @param string $html The HTML output for the li element.
676
- * @param string $key Meta key.
677
- * @param string $value Meta value.
678
- */
679
- echo apply_filters( 'the_meta_key', "<li><span class='post-meta-key'>$key:</span> $value</li>\n", $key, $value );
680
- }
681
- echo "</ul>\n";
682
- }
683
  break;
684
 
685
  default: // Display is either invalid, or contains a template file to use.
@@ -755,12 +734,30 @@ if ( ! class_exists( 'InsertPagesPlugin' ) ) {
755
  'post_status' => $attributes['public'] ? array( 'publish', 'private' ) : array( 'publish' ),
756
  );
757
  }
 
758
  // We save the previous query state here instead of using
759
  // wp_reset_query() because wp_reset_query() only has a single stack
760
  // variable ($GLOBALS['wp_the_query']). This allows us to support
761
  // pages inserted into other pages (multiple nested pages).
762
  $old_query = $GLOBALS['wp_query'];
763
  $posts = query_posts( $args );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
764
  if ( have_posts() ) {
765
  // Start output buffering so we can save the output to string.
766
  ob_start();
@@ -947,7 +944,7 @@ if ( ! class_exists( 'InsertPagesPlugin' ) ) {
947
  } else {
948
  echo get_the_content();
949
  }
950
- the_meta();
951
  // Render any <!--nextpage--> pagination links.
952
  wp_link_pages( array(
953
  'before' => '<div class="page-links">' . __( 'Pages:', 'twentynineteen' ),
@@ -1035,7 +1032,6 @@ if ( ! class_exists( 'InsertPagesPlugin' ) ) {
1035
  }
1036
  }
1037
 
1038
-
1039
  return $content;
1040
  }
1041
 
@@ -1426,6 +1422,16 @@ if ( ! class_exists( 'InsertPagesPlugin' ) ) {
1426
  // 'post__not_in' => array( $args['pageID'] ), // Remove?
1427
  );
1428
 
 
 
 
 
 
 
 
 
 
 
1429
  $args['pagenum'] = isset( $args['pagenum'] ) ? absint( $args['pagenum'] ) : 1;
1430
  $query['offset'] = $args['pagenum'] > 1 ? $query['posts_per_page'] * ( $args['pagenum'] - 1 ) : 0;
1431
 
@@ -1455,6 +1461,13 @@ if ( ! class_exists( 'InsertPagesPlugin' ) ) {
1455
  // Build results.
1456
  $results = array();
1457
  foreach ( $posts as $post ) {
 
 
 
 
 
 
 
1458
  if ( 'post' === $post->post_type ) {
1459
  $info = mysql2date( 'Y/m/d', $post->post_date );
1460
  } else {
@@ -1565,6 +1578,61 @@ if ( ! class_exists( 'InsertPagesPlugin' ) ) {
1565
  register_widget( 'InsertPagesWidget' );
1566
  }
1567
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1568
  }
1569
  }
1570
 
7
  * Text Domain: insert-pages
8
  * Domain Path: /languages
9
  * License: GPL2
10
+ * Requires at least: 3.0.1
11
+ * Version: 3.7.0
12
  *
13
  * @package insert-pages
14
  */
423
  $inserted_page = get_post( intval( $attributes['page'] ) );
424
  }
425
 
426
+ // Prevent unprivileged users from inserting private posts from others.
427
+ if ( is_object( $inserted_page ) && 'publish' !== $inserted_page->post_status ) {
428
+ $post_type = get_post_type_object( $inserted_page->post_type );
429
+ $parent_post_author_id = intval( get_the_author_meta( 'ID' ) );
430
+ if ( ! user_can( $parent_post_author_id, $post_type->cap->read_post, $inserted_page->ID ) ) {
431
+ $inserted_page = null;
432
+ }
433
+ }
434
+
435
  // If inserted page's status is private, don't show to anonymous users
436
  // unless 'public' option is set.
437
  if ( is_object( $inserted_page ) && 'private' === $inserted_page->post_status && ! $attributes['public'] ) {
658
  $content = apply_filters( 'the_content', $content );
659
  }
660
  echo $content;
661
+ $this->the_meta( $inserted_page->ID );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
662
  break;
663
 
664
  default: // Display is either invalid, or contains a template file to use.
734
  'post_status' => $attributes['public'] ? array( 'publish', 'private' ) : array( 'publish' ),
735
  );
736
  }
737
+
738
  // We save the previous query state here instead of using
739
  // wp_reset_query() because wp_reset_query() only has a single stack
740
  // variable ($GLOBALS['wp_the_query']). This allows us to support
741
  // pages inserted into other pages (multiple nested pages).
742
  $old_query = $GLOBALS['wp_query'];
743
  $posts = query_posts( $args );
744
+
745
+ // Prevent unprivileged users from inserting private posts from others.
746
+ if ( have_posts() ) {
747
+ $can_read = true;
748
+ $parent_post_author_id = intval( get_the_author_meta( 'ID' ) );
749
+ foreach ( $posts as $post ) {
750
+ $post_type = get_post_type_object( $post->post_type );
751
+ if ( ! user_can( $parent_post_author_id, $post_type->cap->read_post, $post->ID ) ) {
752
+ $can_read = false;
753
+ }
754
+ }
755
+ if ( ! $can_read ) {
756
+ // Force an empty query so we don't show any posts.
757
+ $posts = query_posts( array( 'post__in' => array( 0 ) ) );
758
+ }
759
+ }
760
+
761
  if ( have_posts() ) {
762
  // Start output buffering so we can save the output to string.
763
  ob_start();
944
  } else {
945
  echo get_the_content();
946
  }
947
+ $this->the_meta();
948
  // Render any <!--nextpage--> pagination links.
949
  wp_link_pages( array(
950
  'before' => '<div class="page-links">' . __( 'Pages:', 'twentynineteen' ),
1032
  }
1033
  }
1034
 
 
1035
  return $content;
1036
  }
1037
 
1422
  // 'post__not_in' => array( $args['pageID'] ), // Remove?
1423
  );
1424
 
1425
+ // Show non-admins only their own posts if the option is enabled.
1426
+ $options = get_option( 'wpip_settings' );
1427
+ if (
1428
+ ! empty( $options['wpip_classic_editor_hide_others_posts'] ) &&
1429
+ 'enabled' === $options['wpip_classic_editor_hide_others_posts'] &&
1430
+ ! current_user_can( 'edit_others_posts' )
1431
+ ) {
1432
+ $query['author'] = get_current_user_id();
1433
+ }
1434
+
1435
  $args['pagenum'] = isset( $args['pagenum'] ) ? absint( $args['pagenum'] ) : 1;
1436
  $query['offset'] = $args['pagenum'] > 1 ? $query['posts_per_page'] * ( $args['pagenum'] - 1 ) : 0;
1437
 
1461
  // Build results.
1462
  $results = array();
1463
  foreach ( $posts as $post ) {
1464
+ // Prevent unprivileged users (e.g., Contributors) from seeing and
1465
+ // inserting other user's private posts.
1466
+ $post_type = get_post_type_object( $post->post_type );
1467
+ if ( 'publish' !== $post->post_status && ! current_user_can( $post_type->cap->read_post, $post->ID ) ) {
1468
+ continue;
1469
+ }
1470
+
1471
  if ( 'post' === $post->post_type ) {
1472
  $info = mysql2date( 'Y/m/d', $post->post_date );
1473
  } else {
1578
  register_widget( 'InsertPagesWidget' );
1579
  }
1580
 
1581
+ /**
1582
+ * Render post meta as an unordered list.
1583
+ *
1584
+ * Note: This function sanitizes postmeta value via wp_kses_post(); the
1585
+ * core WordPress function the_meta() does not.
1586
+ *
1587
+ * @see https://developer.wordpress.org/reference/functions/the_meta/
1588
+ *
1589
+ * @param int $post_id Post ID.
1590
+ */
1591
+ public function the_meta( $post_id = 0 ) {
1592
+ if ( empty( $post_id ) ) {
1593
+ $post_id = get_the_ID();
1594
+ }
1595
+
1596
+ $keys = get_post_custom_keys( $post_id );
1597
+ if ( $keys ) {
1598
+ $li_html = '';
1599
+ foreach ( (array) $keys as $key ) {
1600
+ $keyt = trim( $key );
1601
+ if ( is_protected_meta( $keyt, 'post' ) ) {
1602
+ continue;
1603
+ }
1604
+
1605
+ $values = array_map( 'trim', get_post_custom_values( $key, $post_id ) );
1606
+ $value = implode( ', ', $values );
1607
+
1608
+ // Sanitize post meta values.
1609
+ $value = wp_kses_post( $value );
1610
+
1611
+ $html = sprintf(
1612
+ "<li><span class='post-meta-key'>%s</span> %s</li>\n",
1613
+ /* translators: %s: Post custom field name. */
1614
+ sprintf( _x( '%s:', 'Post custom field name' ), $key ),
1615
+ $value
1616
+ );
1617
+
1618
+ /**
1619
+ * Filters the HTML output of the li element in the post custom fields list.
1620
+ *
1621
+ * @since 2.2.0
1622
+ *
1623
+ * @param string $html The HTML output for the li element.
1624
+ * @param string $key Meta key.
1625
+ * @param string $value Meta value.
1626
+ */
1627
+ $li_html .= apply_filters( 'the_meta_key', $html, $key, $value );
1628
+ }
1629
+
1630
+ if ( $li_html ) {
1631
+ echo "<ul class='post-meta'>\n{$li_html}</ul>\n";
1632
+ }
1633
+ }
1634
+ }
1635
+
1636
  }
1637
  }
1638
 
options.php CHANGED
@@ -63,6 +63,13 @@ function wpip_settings_init() {
63
  'wpipSettings',
64
  'wpip_section'
65
  );
 
 
 
 
 
 
 
66
  }
67
  add_action( 'admin_init', 'wpip_settings_init' );
68
 
@@ -104,6 +111,10 @@ function wpip_set_defaults() {
104
  $options['wpip_gutenberg_block'] = 'enabled';
105
  }
106
 
 
 
 
 
107
  update_option( 'wpip_settings', $options );
108
 
109
  return $options;
@@ -219,3 +230,20 @@ function wpip_gutenberg_block_render() {
219
  <input type='radio' name='wpip_settings[wpip_gutenberg_block]' <?php checked( $options['wpip_gutenberg_block'], 'disabled' ); ?> id="wpip_gutenberg_block_disabled" value='disabled'><label for="wpip_gutenberg_block_disabled">Disable Insert Pages Gutenberg block.</label>
220
  <?php
221
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  'wpipSettings',
64
  'wpip_section'
65
  );
66
+ add_settings_field(
67
+ 'wpip_classic_editor_hide_others_posts',
68
+ __( 'TinyMCE capabilities', 'insert-pages' ),
69
+ 'wpip_classic_editor_hide_others_posts_render',
70
+ 'wpipSettings',
71
+ 'wpip_section'
72
+ );
73
  }
74
  add_action( 'admin_init', 'wpip_settings_init' );
75
 
111
  $options['wpip_gutenberg_block'] = 'enabled';
112
  }
113
 
114
+ if ( empty( $options['wpip_classic_editor_hide_others_posts'] ) ) {
115
+ $options['wpip_classic_editor_hide_others_posts'] = 'disabled';
116
+ }
117
+
118
  update_option( 'wpip_settings', $options );
119
 
120
  return $options;
230
  <input type='radio' name='wpip_settings[wpip_gutenberg_block]' <?php checked( $options['wpip_gutenberg_block'], 'disabled' ); ?> id="wpip_gutenberg_block_disabled" value='disabled'><label for="wpip_gutenberg_block_disabled">Disable Insert Pages Gutenberg block.</label>
231
  <?php
232
  }
233
+
234
+ /**
235
+ * Print 'TinyMCE capabilities' setting.
236
+ *
237
+ * @return void
238
+ */
239
+ function wpip_classic_editor_hide_others_posts_render() {
240
+ $options = get_option( 'wpip_settings' );
241
+ if ( false === $options || ! is_array( $options ) || empty( $options['wpip_classic_editor_hide_others_posts'] ) ) {
242
+ $options = wpip_set_defaults();
243
+ }
244
+ ?>
245
+ <input type='radio' name='wpip_settings[wpip_classic_editor_hide_others_posts]' <?php checked( $options['wpip_classic_editor_hide_others_posts'], 'enabled' ); ?> id="wpip_classic_editor_hide_others_posts_enabled" value='enabled'><label for="wpip_classic_editor_hide_others_posts_enabled">Authors and Contributors only see their own content to insert.</label><br />
246
+ <input type='radio' name='wpip_settings[wpip_classic_editor_hide_others_posts]' <?php checked( $options['wpip_classic_editor_hide_others_posts'], 'disabled' ); ?> id="wpip_classic_editor_hide_others_posts_disabled" value='disabled'><label for="wpip_classic_editor_hide_others_posts_disabled">Authors and Contributors see all published content to insert.</label><br />
247
+ <small><em>Note: this option only restricts Contributors and Authors (i.e., the roles without the <code>edit_others_posts</code> capability) from seeing other's content in the TinyMCE Insert Page popup; they can still insert any published content if they know the page slug.</em></small>
248
+ <?php
249
+ }
readme.txt CHANGED
@@ -1,8 +1,7 @@
1
  === Insert Pages ===
2
  Contributors: figureone, the_magician
3
  Tags: insert, pages, shortcode, embed
4
- Requires at least: 3.0.1
5
- Tested up to: 5.7
6
  Stable tag: trunk
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -111,6 +110,11 @@ Just one! The plugin prevents you from embedding a page in itself, but you can t
111
 
112
  == Changelog ==
113
 
 
 
 
 
 
114
  = 3.6.1 =
115
  * Fix TinyMCE dialog not closing properly. Props @astaryne for the report!
116
 
@@ -460,3 +464,8 @@ Added retina toolbar icon.
460
 
461
  = 1.0 =
462
  Upgrade to v1.0 to get the first stable version.
 
 
 
 
 
1
  === Insert Pages ===
2
  Contributors: figureone, the_magician
3
  Tags: insert, pages, shortcode, embed
4
+ Tested up to: 5.8.1
 
5
  Stable tag: trunk
6
  License: GPLv2 or later
7
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
110
 
111
  == Changelog ==
112
 
113
+ = 3.7.0 =
114
+ * Security: Prevent unprivileged users from inserting private posts by others.
115
+ * Security: Filter out possible XSS in post meta using wp_kses_post() when using display=all.
116
+ * New Setting: Only show Authors and Contributors their own content in the TinyMCE Insert Pages popup.
117
+
118
  = 3.6.1 =
119
  * Fix TinyMCE dialog not closing properly. Props @astaryne for the report!
120
 
464
 
465
  = 1.0 =
466
  Upgrade to v1.0 to get the first stable version.
467
+
468
+ == Upgrade Notice ==
469
+
470
+ = 3.7.0 =
471
+ Note: if you insert private pages/posts, please review the post authors of the pages containing the inserted page and confirm they have the capability to read the private content. This upgrade enforces private page visibility based on the role of the author of the page that inserts any private content.