WP Subtitle - Version 3.4.1

Version Description

  • Security: Resolve XSS issue by improving sanitization when saving subtitle custom field.
  • Pass the current post object to the wps_subtitle_field_placeholder filter. Props Dominik Schilling.
Download this release

Release Info

Developer husobj
Plugin Icon 128x128 WP Subtitle
Version 3.4.1
Comparing to
See all releases

Code changes from version 3.4 to 3.4.1

CHANGELOG.md CHANGED
@@ -2,8 +2,16 @@
2
  All notable changes to this project will be documented in this file.
3
  This project adheres to [Semantic Versioning](http://semver.org/).
4
 
 
 
 
 
 
5
  ## [3.4] - 2020-01-31
6
 
 
 
 
7
  ### Added
8
  - Added support for the SEOPress plugin. Props @chriselkins.
9
  - You can now update the subtitle via the REST API. Props @chriselkins.
2
  All notable changes to this project will be documented in this file.
3
  This project adheres to [Semantic Versioning](http://semver.org/).
4
 
5
+ ## [3.4.1] - 2022-04-21
6
+
7
+ ### Security
8
+ - Resolve XSS issue by improving sanitization when saving subtitle custom field.
9
+
10
  ## [3.4] - 2020-01-31
11
 
12
+ ### Changed
13
+ - Pass the current post object to the `wps_subtitle_field_placeholder` filter. Props [Dominik Schilling](https://github.com/ocean90).
14
+
15
  ### Added
16
  - Added support for the SEOPress plugin. Props @chriselkins.
17
  - You can now update the subtitle via the REST API. Props @chriselkins.
README.md CHANGED
@@ -140,6 +140,9 @@ The plugin is [hosted on GitHub](https://github.com/benhuson/wp-subtitle) and pu
140
  Upgrade Notice
141
  --------------
142
 
 
 
 
143
  ### 3.4
144
  Added support for the SEOPress plugin and updating the subtitle via the REST API.
145
 
140
  Upgrade Notice
141
  --------------
142
 
143
+ ### 3.4.1
144
+ Resolve XSS issue by improving sanitization when saving subtitle custom field.
145
+
146
  ### 3.4
147
  Added support for the SEOPress plugin and updating the subtitle via the REST API.
148
 
plugin/admin/admin.php CHANGED
@@ -28,6 +28,8 @@ class WPSubtitle_Admin {
28
  add_filter( '_wp_post_revision_fields', array( 'WPSubtitle_Admin', '_wp_post_revision_fields' ), 9 );
29
  add_action( 'wp_restore_post_revision', array( 'WPSubtitle_Admin', 'wp_restore_post_revision' ), 10, 2 );
30
 
 
 
31
  }
32
 
33
  /**
@@ -45,10 +47,10 @@ class WPSubtitle_Admin {
45
 
46
  $position = self::subtitle_field_position( $post_type );
47
 
48
- if ( 'after_title' == $position ) {
49
  add_action( 'admin_head', array( 'WPSubtitle_Admin', '_add_admin_styles' ) );
50
  add_action( 'edit_form_after_title', array( 'WPSubtitle_Admin', '_add_subtitle_field' ) );
51
- } elseif ( 'before_title' == $position ) {
52
  add_action( 'admin_head', array( 'WPSubtitle_Admin', '_add_admin_styles' ) );
53
  add_action( 'edit_form_top', array( 'WPSubtitle_Admin', '_add_subtitle_field' ) );
54
  } else {
@@ -73,10 +75,10 @@ class WPSubtitle_Admin {
73
  global $pagenow;
74
 
75
  if ( isset( $_REQUEST['post_type'] ) ) {
76
- return sanitize_text_field( $_REQUEST['post_type'] );
77
  } elseif ( isset( $_GET['post'] ) ) {
78
  return get_post_type( absint( $_GET['post'] ) );
79
- } elseif ( in_array( $pagenow, array( 'post-new.php', 'edit.php' ) ) ) {
80
  return 'post';
81
  }
82
 
@@ -91,12 +93,12 @@ class WPSubtitle_Admin {
91
  *
92
  * @uses add_action( 'quick_edit_custom_box' )
93
  *
94
- * @param string $column_name Column name.
95
- * @param string $post_type Post type
96
  */
97
  public static function quick_edit_custom_box( $column_name, $post_type ) {
98
 
99
- if ( $column_name !== 'wps_subtitle' ) {
100
  return;
101
  }
102
 
@@ -104,7 +106,7 @@ class WPSubtitle_Admin {
104
 
105
  ?>
106
  <fieldset class="inline-edit-col-left inline-edit-col-left-wps-subtitle">
107
- <div class="inline-edit-col column-<?php echo $column_name; ?>">
108
  <label>
109
  <span class="title"><?php esc_html_e( 'Subtitle', 'wp-subtitle' ); ?></span>
110
  <span class="input-text-wrap"><input type="text" name="wps_subtitle" class="wps_subtitle" value=""></span>
@@ -120,7 +122,7 @@ class WPSubtitle_Admin {
120
  *
121
  * @since 2.4
122
  *
123
- * @param array $columns A columns
124
  * @return array Updated columns.
125
  */
126
  public static function manage_subtitle_columns( $columns ) {
@@ -146,7 +148,7 @@ class WPSubtitle_Admin {
146
  // Insert column
147
  foreach ( $columns as $column => $value ) {
148
  $new_columns[ $column ] = $value;
149
- if ( $after_column == $column ) {
150
  $new_columns['wps_subtitle'] = $column_name;
151
  }
152
  }
@@ -160,12 +162,12 @@ class WPSubtitle_Admin {
160
  *
161
  * @since 2.4
162
  *
163
- * @param string $column_name Column name.
164
- * @param int $post_id Post ID
165
  */
166
  public static function manage_subtitle_columns_content( $column_name, $post_id ) {
167
 
168
- if ( $column_name == 'wps_subtitle' ) {
169
 
170
  $subtitle = new WP_Subtitle( $post_id );
171
  echo '<span data-wps_subtitle="' . esc_attr( $subtitle->get_subtitle() ) . '">' . esc_html( $subtitle->get_subtitle() ) . '</span>';
@@ -182,11 +184,11 @@ class WPSubtitle_Admin {
182
  */
183
  public static function _add_admin_scripts( $hook ) {
184
 
185
- if ( 'edit.php' != $hook ) {
186
  return;
187
  }
188
 
189
- wp_enqueue_script( 'wps_subtitle', plugins_url( 'js/admin-edit.js', __FILE__ ), false, null, true );
190
 
191
  }
192
 
@@ -196,7 +198,7 @@ class WPSubtitle_Admin {
196
  * @since 2.9
197
  * @internal
198
  *
199
- * @param array $fields Revision fields.
200
  */
201
  public static function _wp_post_revision_fields( $fields ) {
202
 
@@ -211,8 +213,8 @@ class WPSubtitle_Admin {
211
  *
212
  * @since 2.9
213
  *
214
- * @param int $post_id Post ID.
215
- * @param int $revision_id Revision ID.
216
  */
217
  public static function wp_restore_post_revision( $post_id, $revision_id ) {
218
 
@@ -290,7 +292,7 @@ class WPSubtitle_Admin {
290
 
291
  $positiom = self::gutenberg_supported( $post_type ) ? 'side' : 'normal';
292
 
293
- add_meta_box( 'wps_subtitle_panel', self::get_meta_box_title( $post_type ), array( 'WPSubtitle_Admin', '_add_subtitle_meta_box' ), $post_type, $positiom, 'high' );
294
 
295
  }
296
  }
@@ -310,13 +312,13 @@ class WPSubtitle_Admin {
310
 
311
  $value = self::get_admin_subtitle_value( $post );
312
 
313
- echo '<input type="hidden" name="wps_noncename" id="wps_noncename" value="' . wp_create_nonce( 'wp-subtitle' ) . '" />';
314
 
315
  // As of WordPress 4.3 no need to esc_attr() AND htmlentities().
316
  // @see https://core.trac.wordpress.org/changeset/33271
317
- echo '<input type="text" id="wpsubtitle" name="wps_subtitle" value="' . esc_attr( $value ) . '" autocomplete="off" placeholder="' . esc_attr( apply_filters( 'wps_subtitle_field_placeholder', __( 'Enter subtitle here', 'wp-subtitle' ) ) ) . '" style="width:99%;" />';
318
 
319
- echo apply_filters( 'wps_subtitle_field_description', '', $post );
320
 
321
  }
322
 
@@ -335,20 +337,20 @@ class WPSubtitle_Admin {
335
 
336
  $value = self::get_admin_subtitle_value( $post );
337
 
338
- echo '<input type="hidden" name="wps_noncename" id="wps_noncename" value="' . wp_create_nonce( 'wp-subtitle' ) . '" />';
339
  echo '<div id="subtitlediv" class="top">';
340
  echo '<div id="subtitlewrap">';
341
 
342
  // As of WordPress 4.3 no need to esc_attr() AND htmlentities().
343
  // @see https://core.trac.wordpress.org/changeset/33271
344
- echo '<input type="text" id="wpsubtitle" name="wps_subtitle" value="' . esc_attr( $value ) . '" autocomplete="off" placeholder="' . esc_attr( apply_filters( 'wps_subtitle_field_placeholder', __( 'Enter subtitle here', 'wp-subtitle' ) ) ) . '" />';
345
 
346
  echo '</div>';
347
 
348
  // Description
349
  $description = apply_filters( 'wps_subtitle_field_description', '', $post );
350
  if ( ! empty( $description ) ) {
351
- echo '<div id="subtitledescription">' . $description . '</div>';
352
  }
353
  echo '</div>';
354
  }
@@ -359,7 +361,7 @@ class WPSubtitle_Admin {
359
  * @since 2.8
360
  * @internal
361
  *
362
- * @param WP_Post $post Post object.
363
  * @return string Subtitle value.
364
  */
365
  private static function get_admin_subtitle_value( $post ) {
@@ -371,7 +373,7 @@ class WPSubtitle_Admin {
371
  // Default subtitle if adding new post
372
  if ( function_exists( 'get_current_screen' ) && empty( $value ) ) {
373
  $screen = get_current_screen();
374
- if ( isset( $screen->action ) && 'add' == $screen->action ) {
375
  $value = $subtitle->get_default_subtitle( $post );
376
  }
377
  }
@@ -388,11 +390,11 @@ class WPSubtitle_Admin {
388
  *
389
  * @uses WPSubtitle::get_supported_post_types()
390
  *
391
- * @param int $post_id Post ID or object.
392
  */
393
  public static function _save_post( $post_id ) {
394
 
395
- // Verify if this is an auto save routine.
396
  // If it is our form has not been submitted, so we dont want to do anything
397
  if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
398
  return;
@@ -406,7 +408,7 @@ class WPSubtitle_Admin {
406
  // Check data and save
407
  if ( isset( $_POST['wps_subtitle'] ) ) {
408
 
409
- $new_value = wp_kses_post( $_POST['wps_subtitle'] );
410
 
411
  $subtitle = new WP_Subtitle( $post_id );
412
 
@@ -418,7 +420,6 @@ class WPSubtitle_Admin {
418
  if ( $subtitle->current_user_can_edit() ) {
419
  $subtitle->update_subtitle( $new_value );
420
  }
421
-
422
  }
423
 
424
  }
@@ -430,7 +431,7 @@ class WPSubtitle_Admin {
430
  * @deprecated 2.7 Use WP_Subtitle->current_user_can_edit() instead.
431
  * @internal
432
  *
433
- * @param int $post_id Post ID.
434
  * @return bool
435
  */
436
  private static function _verify_post_edit_capability( $post_id ) {
@@ -449,12 +450,12 @@ class WPSubtitle_Admin {
449
  * @since 2.0.1
450
  * @internal
451
  *
452
- * @param string $nonce Posted nonce name.
453
- * @param string $action Nonce action.
454
  * @return bool
455
  */
456
  private static function _verify_posted_nonce( $nonce, $action ) {
457
- if ( isset( $_POST[ $nonce ] ) && wp_verify_nonce( $_POST[ $nonce ], $action ) ) {
458
  return true;
459
  }
460
  return false;
@@ -465,7 +466,7 @@ class WPSubtitle_Admin {
465
  *
466
  * @since 2.2
467
  *
468
- * @param string $post_type Post type.
469
  * @return bool
470
  */
471
  private static function edit_form_after_title_supported( $post_type = '' ) {
@@ -482,7 +483,7 @@ class WPSubtitle_Admin {
482
  *
483
  * @since 3.1
484
  *
485
- * @param string $post_type Post type.
486
  * @return bool
487
  */
488
  private static function gutenberg_supported( $post_type = '' ) {
@@ -500,8 +501,8 @@ class WPSubtitle_Admin {
500
  *
501
  * @since 3.1
502
  *
503
- * @param string $post_type Post type.
504
- * @param string Position.
505
  */
506
  private static function subtitle_field_position( $post_type = '' ) {
507
 
@@ -519,4 +520,10 @@ class WPSubtitle_Admin {
519
 
520
  }
521
 
 
 
 
 
 
 
522
  }
28
  add_filter( '_wp_post_revision_fields', array( 'WPSubtitle_Admin', '_wp_post_revision_fields' ), 9 );
29
  add_action( 'wp_restore_post_revision', array( 'WPSubtitle_Admin', 'wp_restore_post_revision' ), 10, 2 );
30
 
31
+ add_filter( 'sanitize_post_meta_wps_subtitle', array( 'WPSubtitle_Admin', 'sanitize_subtitle_value' ) );
32
+
33
  }
34
 
35
  /**
47
 
48
  $position = self::subtitle_field_position( $post_type );
49
 
50
+ if ( 'after_title' === $position ) {
51
  add_action( 'admin_head', array( 'WPSubtitle_Admin', '_add_admin_styles' ) );
52
  add_action( 'edit_form_after_title', array( 'WPSubtitle_Admin', '_add_subtitle_field' ) );
53
+ } elseif ( 'before_title' === $position ) {
54
  add_action( 'admin_head', array( 'WPSubtitle_Admin', '_add_admin_styles' ) );
55
  add_action( 'edit_form_top', array( 'WPSubtitle_Admin', '_add_subtitle_field' ) );
56
  } else {
75
  global $pagenow;
76
 
77
  if ( isset( $_REQUEST['post_type'] ) ) {
78
+ return sanitize_text_field( wp_unslash( $_REQUEST['post_type'] ) );
79
  } elseif ( isset( $_GET['post'] ) ) {
80
  return get_post_type( absint( $_GET['post'] ) );
81
+ } elseif ( in_array( $pagenow, array( 'post-new.php', 'edit.php' ), true ) ) {
82
  return 'post';
83
  }
84
 
93
  *
94
  * @uses add_action( 'quick_edit_custom_box' )
95
  *
96
+ * @param string $column_name Column name.
97
+ * @param string $post_type Post type
98
  */
99
  public static function quick_edit_custom_box( $column_name, $post_type ) {
100
 
101
+ if ( 'wps_subtitle' !== $column_name ) {
102
  return;
103
  }
104
 
106
 
107
  ?>
108
  <fieldset class="inline-edit-col-left inline-edit-col-left-wps-subtitle">
109
+ <div class="inline-edit-col column-wps_subtitle">
110
  <label>
111
  <span class="title"><?php esc_html_e( 'Subtitle', 'wp-subtitle' ); ?></span>
112
  <span class="input-text-wrap"><input type="text" name="wps_subtitle" class="wps_subtitle" value=""></span>
122
  *
123
  * @since 2.4
124
  *
125
+ * @param array $columns A columns
126
  * @return array Updated columns.
127
  */
128
  public static function manage_subtitle_columns( $columns ) {
148
  // Insert column
149
  foreach ( $columns as $column => $value ) {
150
  $new_columns[ $column ] = $value;
151
+ if ( $after_column === $column ) {
152
  $new_columns['wps_subtitle'] = $column_name;
153
  }
154
  }
162
  *
163
  * @since 2.4
164
  *
165
+ * @param string $column_name Column name.
166
+ * @param int $post_id Post ID
167
  */
168
  public static function manage_subtitle_columns_content( $column_name, $post_id ) {
169
 
170
+ if ( 'wps_subtitle' === $column_name ) {
171
 
172
  $subtitle = new WP_Subtitle( $post_id );
173
  echo '<span data-wps_subtitle="' . esc_attr( $subtitle->get_subtitle() ) . '">' . esc_html( $subtitle->get_subtitle() ) . '</span>';
184
  */
185
  public static function _add_admin_scripts( $hook ) {
186
 
187
+ if ( 'edit.php' !== $hook ) {
188
  return;
189
  }
190
 
191
+ wp_enqueue_script( 'wps_subtitle', plugins_url( 'js/admin-edit.js', __FILE__ ), false, '3.4.1', true );
192
 
193
  }
194
 
198
  * @since 2.9
199
  * @internal
200
  *
201
+ * @param array $fields Revision fields.
202
  */
203
  public static function _wp_post_revision_fields( $fields ) {
204
 
213
  *
214
  * @since 2.9
215
  *
216
+ * @param int $post_id Post ID.
217
+ * @param int $revision_id Revision ID.
218
  */
219
  public static function wp_restore_post_revision( $post_id, $revision_id ) {
220
 
292
 
293
  $positiom = self::gutenberg_supported( $post_type ) ? 'side' : 'normal';
294
 
295
+ add_meta_box( 'wps_subtitle_panel', self::get_meta_box_title( $post_type ), array( 'WPSubtitle_Admin', '_add_subtitle_meta_box' ), $post_type, $positiom, 'high' );
296
 
297
  }
298
  }
312
 
313
  $value = self::get_admin_subtitle_value( $post );
314
 
315
+ echo '<input type="hidden" name="wps_noncename" id="wps_noncename" value="' . esc_attr( wp_create_nonce( 'wp-subtitle' ) ) . '" />';
316
 
317
  // As of WordPress 4.3 no need to esc_attr() AND htmlentities().
318
  // @see https://core.trac.wordpress.org/changeset/33271
319
+ echo '<input type="text" id="wpsubtitle" name="wps_subtitle" value="' . esc_attr( $value ) . '" autocomplete="off" placeholder="' . esc_attr( apply_filters( 'wps_subtitle_field_placeholder', __( 'Enter subtitle here', 'wp-subtitle' ), $post ) ) . '" style="width:99%;" />';
320
 
321
+ echo wp_kses_post( apply_filters( 'wps_subtitle_field_description', '', $post ) );
322
 
323
  }
324
 
337
 
338
  $value = self::get_admin_subtitle_value( $post );
339
 
340
+ echo '<input type="hidden" name="wps_noncename" id="wps_noncename" value="' . esc_attr( wp_create_nonce( 'wp-subtitle' ) ) . '" />';
341
  echo '<div id="subtitlediv" class="top">';
342
  echo '<div id="subtitlewrap">';
343
 
344
  // As of WordPress 4.3 no need to esc_attr() AND htmlentities().
345
  // @see https://core.trac.wordpress.org/changeset/33271
346
+ echo '<input type="text" id="wpsubtitle" name="wps_subtitle" value="' . esc_attr( $value ) . '" autocomplete="off" placeholder="' . esc_attr( apply_filters( 'wps_subtitle_field_placeholder', __( 'Enter subtitle here', 'wp-subtitle' ), $post ) ) . '" />';
347
 
348
  echo '</div>';
349
 
350
  // Description
351
  $description = apply_filters( 'wps_subtitle_field_description', '', $post );
352
  if ( ! empty( $description ) ) {
353
+ echo '<div id="subtitledescription">' . wp_kses_post( $description ) . '</div>';
354
  }
355
  echo '</div>';
356
  }
361
  * @since 2.8
362
  * @internal
363
  *
364
+ * @param WP_Post $post Post object.
365
  * @return string Subtitle value.
366
  */
367
  private static function get_admin_subtitle_value( $post ) {
373
  // Default subtitle if adding new post
374
  if ( function_exists( 'get_current_screen' ) && empty( $value ) ) {
375
  $screen = get_current_screen();
376
+ if ( isset( $screen->action ) && 'add' === $screen->action ) {
377
  $value = $subtitle->get_default_subtitle( $post );
378
  }
379
  }
390
  *
391
  * @uses WPSubtitle::get_supported_post_types()
392
  *
393
+ * @param int $post_id Post ID or object.
394
  */
395
  public static function _save_post( $post_id ) {
396
 
397
+ // Verify if this is an auto save routine.
398
  // If it is our form has not been submitted, so we dont want to do anything
399
  if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
400
  return;
408
  // Check data and save
409
  if ( isset( $_POST['wps_subtitle'] ) ) {
410
 
411
+ $new_value = wp_kses_post( wp_unslash( $_POST['wps_subtitle'] ) );
412
 
413
  $subtitle = new WP_Subtitle( $post_id );
414
 
420
  if ( $subtitle->current_user_can_edit() ) {
421
  $subtitle->update_subtitle( $new_value );
422
  }
 
423
  }
424
 
425
  }
431
  * @deprecated 2.7 Use WP_Subtitle->current_user_can_edit() instead.
432
  * @internal
433
  *
434
+ * @param int $post_id Post ID.
435
  * @return bool
436
  */
437
  private static function _verify_post_edit_capability( $post_id ) {
450
  * @since 2.0.1
451
  * @internal
452
  *
453
+ * @param string $nonce Posted nonce name.
454
+ * @param string $action Nonce action.
455
  * @return bool
456
  */
457
  private static function _verify_posted_nonce( $nonce, $action ) {
458
+ if ( isset( $_POST[ $nonce ] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST[ $nonce ] ) ), $action ) ) {
459
  return true;
460
  }
461
  return false;
466
  *
467
  * @since 2.2
468
  *
469
+ * @param string $post_type Post type.
470
  * @return bool
471
  */
472
  private static function edit_form_after_title_supported( $post_type = '' ) {
483
  *
484
  * @since 3.1
485
  *
486
+ * @param string $post_type Post type.
487
  * @return bool
488
  */
489
  private static function gutenberg_supported( $post_type = '' ) {
501
  *
502
  * @since 3.1
503
  *
504
+ * @param string $post_type Post type.
505
+ * @return string Position.
506
  */
507
  private static function subtitle_field_position( $post_type = '' ) {
508
 
520
 
521
  }
522
 
523
+ public static function sanitize_subtitle_value( $value ) {
524
+
525
+ return wp_kses( $value, wp_kses_allowed_html( 'data' ) );
526
+
527
+ }
528
+
529
  }
plugin/admin/js/admin-edit.js CHANGED
@@ -8,7 +8,7 @@
8
  // inlineEditPost does not invoke any events, but does ensure to stop
9
  // propagation to all other event handlers; swap it out.
10
  inlineEditPost.editPreWpSubtitle = inlineEditPost.edit;
11
- inlineEditPost.edit = function ( id ) {
12
 
13
  // Invoke original edit event handler.
14
  this.editPreWpSubtitle.apply( this, arguments );
@@ -35,4 +35,4 @@
35
 
36
  }
37
 
38
- } ) ( jQuery, inlineEditPost );
8
  // inlineEditPost does not invoke any events, but does ensure to stop
9
  // propagation to all other event handlers; swap it out.
10
  inlineEditPost.editPreWpSubtitle = inlineEditPost.edit;
11
+ inlineEditPost.edit = function ( id ) {
12
 
13
  // Invoke original edit event handler.
14
  this.editPreWpSubtitle.apply( this, arguments );
35
 
36
  }
37
 
38
+ } )( jQuery, inlineEditPost );
plugin/admin/js/pointers.js CHANGED
@@ -1,25 +1,32 @@
1
-
2
  /**
3
  * @package WP Subtitle
4
  * @subpackage JavaScript > Pointers
5
  */
6
 
7
- jQuery( document ).ready( function( $ ) {
 
8
 
9
- function wps_subtitle_open_pointer( i ) {
10
- pointer = wpsSubtitlePointer.pointers[ i ];
11
- options = $.extend( pointer.options, {
12
- close : function() {
13
- $.post( ajaxurl, {
14
- pointer : pointer.pointer_id,
15
- action : 'dismiss-wp-pointer'
16
- } );
17
- }
18
- });
 
 
 
 
 
 
19
 
20
- $( pointer.target ).pointer( options ).pointer( 'open' );
21
- }
22
 
23
- wps_subtitle_open_pointer( 0 );
24
 
25
- } );
 
 
1
  /**
2
  * @package WP Subtitle
3
  * @subpackage JavaScript > Pointers
4
  */
5
 
6
+ jQuery( document ).ready(
7
+ function( $ ) {
8
 
9
+ function wps_subtitle_open_pointer( i ) {
10
+ pointer = wpsSubtitlePointer.pointers[ i ];
11
+ options = $.extend(
12
+ pointer.options,
13
+ {
14
+ close : function() {
15
+ $.post(
16
+ ajaxurl,
17
+ {
18
+ pointer : pointer.pointer_id,
19
+ action : 'dismiss-wp-pointer'
20
+ }
21
+ );
22
+ }
23
+ }
24
+ );
25
 
26
+ $( pointer.target ).pointer( options ).pointer( 'open' );
27
+ }
28
 
29
+ wps_subtitle_open_pointer( 0 );
30
 
31
+ }
32
+ );
plugin/admin/pointers.php CHANGED
@@ -31,7 +31,7 @@ class WPSubtitle_Pointers {
31
  * @since 2.2
32
  * @internal
33
  *
34
- * @param string $hook_suffix Page hook.
35
  */
36
  public static function _pointer_load( $hook_suffix ) {
37
 
@@ -54,7 +54,7 @@ class WPSubtitle_Pointers {
54
 
55
  // Enqueue pointer scripts and styles.
56
  wp_enqueue_style( 'wp-pointer' );
57
- wp_enqueue_script( 'wps-subtitle-pointer', plugins_url( 'js/pointers.js', __FILE__ ), array( 'wp-pointer' ) );
58
  wp_localize_script( 'wps-subtitle-pointer', 'wpsSubtitlePointer', $valid_pointers );
59
 
60
  }
@@ -71,7 +71,7 @@ class WPSubtitle_Pointers {
71
  */
72
  private static function get_current_pointers() {
73
 
74
- $screen = get_current_screen();
75
  $pointers = apply_filters( 'wps_subtitle_admin_pointers-' . $screen->id, array() );
76
 
77
  // Only return valid array of pointers.
@@ -89,19 +89,19 @@ class WPSubtitle_Pointers {
89
  * @since 2.4
90
  * @internal
91
  *
92
- * @param array $pointers Pointers.
93
  * @return array Active pointers.
94
  */
95
  private static function remove_dismissed_pointers( $pointers ) {
96
 
97
- $dismissed = self::get_dismissed_pointers();
98
  $valid_pointers = array();
99
 
100
  // Check pointers and remove dismissed ones.
101
  foreach ( $pointers as $pointer_id => $pointer ) {
102
 
103
  // Sanity check
104
- if ( in_array( $pointer_id, $dismissed ) || empty( $pointer ) || empty( $pointer_id ) || empty( $pointer['target'] ) || empty( $pointer['options'] ) ) {
105
  continue;
106
  }
107
 
@@ -136,7 +136,7 @@ class WPSubtitle_Pointers {
136
  * @since 2.2
137
  * @internal
138
  *
139
- * @param array $pointers Pointers.
140
  * @return array Pointers.
141
  */
142
  public static function _post_type_pointers( $pointers ) {
@@ -145,15 +145,16 @@ class WPSubtitle_Pointers {
145
  $pointers['wps_subtitle_field_to_top'] = array(
146
  'target' => '#subtitlewrap',
147
  'options' => array(
148
- 'content' => sprintf( '<h3>%s</h3><p>%s</p>',
 
149
  sprintf( __( '%s Field', 'wp-subtitle' ), WPSubtitle_Admin::get_meta_box_title( get_post_type( get_queried_object_id() ) ) ),
150
  __( 'This field has moved from a meta box to below the post title.', 'wp-subtitle' )
151
  ),
152
  'position' => array(
153
  'edge' => 'top',
154
- 'align' => 'middle'
155
- )
156
- )
157
  );
158
 
159
  return $pointers;
31
  * @since 2.2
32
  * @internal
33
  *
34
+ * @param string $hook_suffix Page hook.
35
  */
36
  public static function _pointer_load( $hook_suffix ) {
37
 
54
 
55
  // Enqueue pointer scripts and styles.
56
  wp_enqueue_style( 'wp-pointer' );
57
+ wp_enqueue_script( 'wps-subtitle-pointer', plugins_url( 'js/pointers.js', __FILE__ ), array( 'wp-pointer' ), '3.4.1', true );
58
  wp_localize_script( 'wps-subtitle-pointer', 'wpsSubtitlePointer', $valid_pointers );
59
 
60
  }
71
  */
72
  private static function get_current_pointers() {
73
 
74
+ $screen = get_current_screen();
75
  $pointers = apply_filters( 'wps_subtitle_admin_pointers-' . $screen->id, array() );
76
 
77
  // Only return valid array of pointers.
89
  * @since 2.4
90
  * @internal
91
  *
92
+ * @param array $pointers Pointers.
93
  * @return array Active pointers.
94
  */
95
  private static function remove_dismissed_pointers( $pointers ) {
96
 
97
+ $dismissed = self::get_dismissed_pointers();
98
  $valid_pointers = array();
99
 
100
  // Check pointers and remove dismissed ones.
101
  foreach ( $pointers as $pointer_id => $pointer ) {
102
 
103
  // Sanity check
104
+ if ( in_array( $pointer_id, $dismissed ) || empty( $pointer ) || empty( $pointer_id ) || empty( $pointer['target'] ) || empty( $pointer['options'] ) ) {
105
  continue;
106
  }
107
 
136
  * @since 2.2
137
  * @internal
138
  *
139
+ * @param array $pointers Pointers.
140
  * @return array Pointers.
141
  */
142
  public static function _post_type_pointers( $pointers ) {
145
  $pointers['wps_subtitle_field_to_top'] = array(
146
  'target' => '#subtitlewrap',
147
  'options' => array(
148
+ 'content' => sprintf(
149
+ '<h3>%s</h3><p>%s</p>',
150
  sprintf( __( '%s Field', 'wp-subtitle' ), WPSubtitle_Admin::get_meta_box_title( get_post_type( get_queried_object_id() ) ) ),
151
  __( 'This field has moved from a meta box to below the post title.', 'wp-subtitle' )
152
  ),
153
  'position' => array(
154
  'edge' => 'top',
155
+ 'align' => 'middle',
156
+ ),
157
+ ),
158
  );
159
 
160
  return $pointers;
plugin/includes/class-api.php CHANGED
@@ -22,7 +22,9 @@
22
  * ) );
23
  */
24
 
25
- if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
 
 
26
 
27
  class WP_Subtitle_API {
28
 
@@ -39,7 +41,7 @@ class WP_Subtitle_API {
39
  /**
40
  * The Subtitle
41
  *
42
- * @param array $args Display args.
43
  *
44
  * @internal Private. Called via the `the_subtitle` action.
45
  */
@@ -47,29 +49,32 @@ class WP_Subtitle_API {
47
 
48
  $default_value = isset( $args['default_value'] ) ? $args['default_value'] : '';
49
 
50
- echo $this->get_subtitle( $default_value, $args );
51
 
52
  }
53
 
54
  /**
55
  * Get Subtitle
56
  *
57
- * @param string $default_subtitle Default/fallback subtitle.
58
- * @param array $args Display args.
59
  * @return string The subtitle.
60
  *
61
  * @internal Private. Called via the `get_subtitle` action.
62
  */
63
  public function get_subtitle( $default_subtitle, $args = '' ) {
64
 
65
- $args = wp_parse_args( $args, array(
66
- 'post_id' => get_the_ID(), // Post ID
67
- 'before' => '', // Before subtitle HTML output
68
- 'after' => '' // After subtitle HTML output
69
- ) );
 
 
 
70
 
71
  $subtitle_obj = new WP_Subtitle( $args['post_id'] );
72
- $subtitle = $subtitle_obj->get_subtitle( $args );
73
 
74
  if ( ! empty( $subtitle ) ) {
75
  return $subtitle;
22
  * ) );
23
  */
24
 
25
+ if ( ! defined( 'ABSPATH' ) ) {
26
+ exit; // Exit if accessed directly
27
+ }
28
 
29
  class WP_Subtitle_API {
30
 
41
  /**
42
  * The Subtitle
43
  *
44
+ * @param array $args Display args.
45
  *
46
  * @internal Private. Called via the `the_subtitle` action.
47
  */
49
 
50
  $default_value = isset( $args['default_value'] ) ? $args['default_value'] : '';
51
 
52
+ echo wp_kses_post( $this->get_subtitle( $default_value, $args ) );
53
 
54
  }
55
 
56
  /**
57
  * Get Subtitle
58
  *
59
+ * @param string $default_subtitle Default/fallback subtitle.
60
+ * @param array $args Display args.
61
  * @return string The subtitle.
62
  *
63
  * @internal Private. Called via the `get_subtitle` action.
64
  */
65
  public function get_subtitle( $default_subtitle, $args = '' ) {
66
 
67
+ $args = wp_parse_args(
68
+ $args,
69
+ array(
70
+ 'post_id' => get_the_ID(), // Post ID
71
+ 'before' => '', // Before subtitle HTML output
72
+ 'after' => '', // After subtitle HTML output
73
+ )
74
+ );
75
 
76
  $subtitle_obj = new WP_Subtitle( $args['post_id'] );
77
+ $subtitle = $subtitle_obj->get_subtitle( $args );
78
 
79
  if ( ! empty( $subtitle ) ) {
80
  return $subtitle;
plugin/includes/compat/seopress.php CHANGED
@@ -50,7 +50,7 @@ class WPSubtitle_SEOPress {
50
  *
51
  * @since 3.4
52
  *
53
- * @param array $replacements SEO replacements variables.
54
  * @return array Filtered replacements variables.
55
  *
56
  * @internal Called via the `seopress_titles_template_variables_array` filter.
@@ -70,7 +70,7 @@ class WPSubtitle_SEOPress {
70
  *
71
  * @since 3.4
72
  *
73
- * @param array $replacements SEO replacements values.
74
  * @return array Filtered replacements values.
75
  *
76
  * @internal Called via the `seopress_titles_template_replace_array` filter.
@@ -80,19 +80,19 @@ class WPSubtitle_SEOPress {
80
  global $post;
81
 
82
  $wp_subtitle = new WP_Subtitle( $post );
83
- $subtitle = $wp_subtitle->get_subtitle();
84
 
85
  $replacements[] = $subtitle;
86
 
87
  $sep = ' ' . $replacements[0] . ' ';
88
 
89
  $before_sep = '';
90
- $after_sep = '';
91
 
92
  if ( ! empty( $subtitle ) ) {
93
 
94
  $before_sep = apply_filters( 'wps_subtitle_seo_before_sep', $sep );
95
- $after_sep = apply_filters( 'wps_subtitle_seo_after_sep', $sep );
96
 
97
  }
98
 
50
  *
51
  * @since 3.4
52
  *
53
+ * @param array $replacements SEO replacements variables.
54
  * @return array Filtered replacements variables.
55
  *
56
  * @internal Called via the `seopress_titles_template_variables_array` filter.
70
  *
71
  * @since 3.4
72
  *
73
+ * @param array $replacements SEO replacements values.
74
  * @return array Filtered replacements values.
75
  *
76
  * @internal Called via the `seopress_titles_template_replace_array` filter.
80
  global $post;
81
 
82
  $wp_subtitle = new WP_Subtitle( $post );
83
+ $subtitle = $wp_subtitle->get_subtitle();
84
 
85
  $replacements[] = $subtitle;
86
 
87
  $sep = ' ' . $replacements[0] . ' ';
88
 
89
  $before_sep = '';
90
+ $after_sep = '';
91
 
92
  if ( ! empty( $subtitle ) ) {
93
 
94
  $before_sep = apply_filters( 'wps_subtitle_seo_before_sep', $sep );
95
+ $after_sep = apply_filters( 'wps_subtitle_seo_after_sep', $sep );
96
 
97
  }
98
 
plugin/includes/compat/woocommerce.php CHANGED
@@ -21,21 +21,20 @@ class WPSubtitle_WooCommerce {
21
  */
22
  public function __construct() {
23
 
24
- if ( 'yes' == get_option( 'wp_subtitle_woocommerce_enabled' ) ) {
25
 
26
  add_action( 'init', array( $this, 'add_product_post_type_support' ) );
27
 
28
- if ( 'yes' == get_option( 'wp_subtitle_woocommerce_show_on_single' ) ) {
29
- add_action( 'woocommerce_single_product_summary' , array( $this, 'single_product_summary' ), 6 );
30
  }
31
 
32
- if ( 'yes' == get_option( 'wp_subtitle_woocommerce_show_in_loop' ) ) {
33
- add_action( 'woocommerce_shop_loop_item_title' , array( $this, 'shop_loop_item_title' ) );
34
  }
35
-
36
  }
37
 
38
- add_filter( 'woocommerce_product_settings' , array( $this, 'product_settings' ) );
39
 
40
  }
41
 
@@ -61,10 +60,13 @@ class WPSubtitle_WooCommerce {
61
  */
62
  public function single_product_summary() {
63
 
64
- do_action( 'plugins/wp_subtitle/the_subtitle', array(
65
- 'before' => '<h2 class="product_subtitle entry-subtitle wp-subtitle">',
66
- 'after' => '</h2>'
67
- ) );
 
 
 
68
 
69
  }
70
 
@@ -77,10 +79,13 @@ class WPSubtitle_WooCommerce {
77
  */
78
  public function shop_loop_item_title() {
79
 
80
- do_action( 'plugins/wp_subtitle/the_subtitle', array(
81
- 'before' => '<p class="woocommerce-loop-product__subtitle wp-subtitle">',
82
- 'after' => '</p>'
83
- ) );
 
 
 
84
 
85
  }
86
 
@@ -89,7 +94,7 @@ class WPSubtitle_WooCommerce {
89
  *
90
  * @since 3.1
91
  *
92
- * @param array $settings Settings.
93
  * @return array Settings.
94
  *
95
  * @internal Private. Called via the `woocommerce_product_settings` filter.
@@ -102,7 +107,7 @@ class WPSubtitle_WooCommerce {
102
  'title' => __( 'WP Subtitle', 'wp-subtitle' ),
103
  'type' => 'title',
104
  'desc' => '',
105
- 'id' => 'wp_subtitle_options'
106
  ),
107
 
108
  array(
@@ -119,7 +124,7 @@ class WPSubtitle_WooCommerce {
119
  'id' => 'wp_subtitle_woocommerce_show_on_single',
120
  'default' => 'yes',
121
  'type' => 'checkbox',
122
- 'checkboxgroup' => 'start'
123
  ),
124
 
125
  array(
@@ -127,13 +132,13 @@ class WPSubtitle_WooCommerce {
127
  'id' => 'wp_subtitle_woocommerce_show_in_loop',
128
  'default' => 'yes',
129
  'type' => 'checkbox',
130
- 'checkboxgroup' => 'end'
131
  ),
132
 
133
  array(
134
  'type' => 'sectionend',
135
- 'id' => 'wp_subtitle_options'
136
- )
137
 
138
  );
139
 
21
  */
22
  public function __construct() {
23
 
24
+ if ( 'yes' === get_option( 'wp_subtitle_woocommerce_enabled' ) ) {
25
 
26
  add_action( 'init', array( $this, 'add_product_post_type_support' ) );
27
 
28
+ if ( 'yes' === get_option( 'wp_subtitle_woocommerce_show_on_single' ) ) {
29
+ add_action( 'woocommerce_single_product_summary', array( $this, 'single_product_summary' ), 6 );
30
  }
31
 
32
+ if ( 'yes' === get_option( 'wp_subtitle_woocommerce_show_in_loop' ) ) {
33
+ add_action( 'woocommerce_shop_loop_item_title', array( $this, 'shop_loop_item_title' ) );
34
  }
 
35
  }
36
 
37
+ add_filter( 'woocommerce_product_settings', array( $this, 'product_settings' ) );
38
 
39
  }
40
 
60
  */
61
  public function single_product_summary() {
62
 
63
+ do_action(
64
+ 'plugins/wp_subtitle/the_subtitle',
65
+ array(
66
+ 'before' => '<h2 class="product_subtitle entry-subtitle wp-subtitle">',
67
+ 'after' => '</h2>',
68
+ )
69
+ );
70
 
71
  }
72
 
79
  */
80
  public function shop_loop_item_title() {
81
 
82
+ do_action(
83
+ 'plugins/wp_subtitle/the_subtitle',
84
+ array(
85
+ 'before' => '<p class="woocommerce-loop-product__subtitle wp-subtitle">',
86
+ 'after' => '</p>',
87
+ )
88
+ );
89
 
90
  }
91
 
94
  *
95
  * @since 3.1
96
  *
97
+ * @param array $settings Settings.
98
  * @return array Settings.
99
  *
100
  * @internal Private. Called via the `woocommerce_product_settings` filter.
107
  'title' => __( 'WP Subtitle', 'wp-subtitle' ),
108
  'type' => 'title',
109
  'desc' => '',
110
+ 'id' => 'wp_subtitle_options',
111
  ),
112
 
113
  array(
124
  'id' => 'wp_subtitle_woocommerce_show_on_single',
125
  'default' => 'yes',
126
  'type' => 'checkbox',
127
+ 'checkboxgroup' => 'start',
128
  ),
129
 
130
  array(
132
  'id' => 'wp_subtitle_woocommerce_show_in_loop',
133
  'default' => 'yes',
134
  'type' => 'checkbox',
135
+ 'checkboxgroup' => 'end',
136
  ),
137
 
138
  array(
139
  'type' => 'sectionend',
140
+ 'id' => 'wp_subtitle_options',
141
+ ),
142
 
143
  );
144
 
plugin/includes/compat/wordpress-seo.php CHANGED
@@ -49,7 +49,7 @@ class WPSubtitle_WPSEO {
49
  *
50
  * @since 3.1
51
  *
52
- * @param array $replacements SEO replacements.
53
  * @return array Filtered replacements.
54
  *
55
  * @internal Called via the `wpseo_replacements` filter.
@@ -59,18 +59,18 @@ class WPSubtitle_WPSEO {
59
  global $post;
60
 
61
  $wp_subtitle = new WP_Subtitle( $post );
62
- $subtitle = $wp_subtitle->get_subtitle();
63
 
64
- $replacements['%%wps_subtitle%%'] = $subtitle;
65
  $replacements['%%wps_subtitle_before_sep%%'] = '';
66
- $replacements['%%wps_subtitle_after_sep%%'] = '';
67
 
68
  if ( ! empty( $subtitle ) ) {
69
 
70
  $sep = isset( $replacements['%%sep%%'] ) ? ' ' . $replacements['%%sep%%'] . ' ' : ' - ';
71
 
72
  $replacements['%%wps_subtitle_before_sep%%'] = apply_filters( 'wps_subtitle_seo_before_sep', $sep );
73
- $replacements['%%wps_subtitle_after_sep%%'] = apply_filters( 'wps_subtitle_seo_after_sep', $sep );
74
 
75
  }
76
 
49
  *
50
  * @since 3.1
51
  *
52
+ * @param array $replacements SEO replacements.
53
  * @return array Filtered replacements.
54
  *
55
  * @internal Called via the `wpseo_replacements` filter.
59
  global $post;
60
 
61
  $wp_subtitle = new WP_Subtitle( $post );
62
+ $subtitle = $wp_subtitle->get_subtitle();
63
 
64
+ $replacements['%%wps_subtitle%%'] = $subtitle;
65
  $replacements['%%wps_subtitle_before_sep%%'] = '';
66
+ $replacements['%%wps_subtitle_after_sep%%'] = '';
67
 
68
  if ( ! empty( $subtitle ) ) {
69
 
70
  $sep = isset( $replacements['%%sep%%'] ) ? ' ' . $replacements['%%sep%%'] . ' ' : ' - ';
71
 
72
  $replacements['%%wps_subtitle_before_sep%%'] = apply_filters( 'wps_subtitle_seo_before_sep', $sep );
73
+ $replacements['%%wps_subtitle_after_sep%%'] = apply_filters( 'wps_subtitle_seo_after_sep', $sep );
74
 
75
  }
76
 
plugin/includes/deprecated.php CHANGED
@@ -13,26 +13,33 @@
13
  *
14
  * @uses WP_Subtitle::get_subtitle()
15
  *
16
- * @param string $before Before the subtitle.
17
- * @param string $after After the subtitle.
18
- * @param bool $echo Output if true, return if false.
19
  * @return string The subtitle string.
20
  */
21
  function the_subtitle( $before = '', $after = '', $echo = true ) {
22
 
23
  if ( ! $echo ) {
24
 
25
- return apply_filters( 'plugins/wp_subtitle/get_subtitle', '', array(
26
- 'before' => $before,
27
- 'after' => $after
28
- ) );
 
 
 
 
29
 
30
  }
31
 
32
- do_action( 'plugins/wp_subtitle/the_subtitle', array(
33
- 'before' => $before,
34
- 'after' => $after
35
- ) );
 
 
 
36
 
37
  }
38
 
@@ -44,25 +51,29 @@ function the_subtitle( $before = '', $after = '', $echo = true ) {
44
  *
45
  * @uses WP_Subtitle::get_subtitle()
46
  *
47
- * @param int|object $post Post ID or object.
48
- * @param string $before Before the subtitle.
49
- * @param string $after After the subtitle.
50
- * @param bool $echo Output if true, return if false.
51
  * @return string The subtitle string.
52
  */
53
  function get_the_subtitle( $post = 0, $before = '', $after = '', $echo = true ) {
54
 
55
- $output = apply_filters( 'plugins/wp_subtitle/get_subtitle', '', array(
56
- 'post_id' => is_a( $post, 'WP_Post' ) ? $post->ID : $post,
57
- 'before' => $before,
58
- 'after' => $after
59
- ) );
 
 
 
 
60
 
61
  if ( ! $echo ) {
62
  return $output;
63
  }
64
 
65
- echo $output;
66
 
67
  }
68
 
@@ -127,6 +138,8 @@ function wps_showSubtitlePanelOld() {
127
  *
128
  * @since 1.0
129
  * @deprecated 2.0 Legacy function.
 
 
130
  */
131
  function wps_saveSubtitle( $post_id ) {
132
  _deprecated_function( 'wps_saveSubtitle()', '2.0' );
13
  *
14
  * @uses WP_Subtitle::get_subtitle()
15
  *
16
+ * @param string $before Before the subtitle.
17
+ * @param string $after After the subtitle.
18
+ * @param bool $echo Output if true, return if false.
19
  * @return string The subtitle string.
20
  */
21
  function the_subtitle( $before = '', $after = '', $echo = true ) {
22
 
23
  if ( ! $echo ) {
24
 
25
+ return apply_filters(
26
+ 'plugins/wp_subtitle/get_subtitle',
27
+ '',
28
+ array(
29
+ 'before' => $before,
30
+ 'after' => $after,
31
+ )
32
+ );
33
 
34
  }
35
 
36
+ do_action(
37
+ 'plugins/wp_subtitle/the_subtitle',
38
+ array(
39
+ 'before' => $before,
40
+ 'after' => $after,
41
+ )
42
+ );
43
 
44
  }
45
 
51
  *
52
  * @uses WP_Subtitle::get_subtitle()
53
  *
54
+ * @param int|object $post Post ID or object.
55
+ * @param string $before Before the subtitle.
56
+ * @param string $after After the subtitle.
57
+ * @param bool $echo Output if true, return if false.
58
  * @return string The subtitle string.
59
  */
60
  function get_the_subtitle( $post = 0, $before = '', $after = '', $echo = true ) {
61
 
62
+ $output = apply_filters(
63
+ 'plugins/wp_subtitle/get_subtitle',
64
+ '',
65
+ array(
66
+ 'post_id' => is_a( $post, 'WP_Post' ) ? $post->ID : $post,
67
+ 'before' => $before,
68
+ 'after' => $after,
69
+ )
70
+ );
71
 
72
  if ( ! $echo ) {
73
  return $output;
74
  }
75
 
76
+ echo wp_kses_post( $output );
77
 
78
  }
79
 
138
  *
139
  * @since 1.0
140
  * @deprecated 2.0 Legacy function.
141
+ *
142
+ * @param int $post_id Post ID.
143
  */
144
  function wps_saveSubtitle( $post_id ) {
145
  _deprecated_function( 'wps_saveSubtitle()', '2.0' );
plugin/includes/rest.php CHANGED
@@ -35,11 +35,15 @@ class WPSubtitle_REST {
35
 
36
  foreach ( $post_types as $post_type ) {
37
 
38
- register_rest_field( $post_types, 'wps_subtitle', array(
39
- 'get_callback' => array( $this, 'get_rest_field' ),
40
- 'update_callback' => array( $this, 'update_rest_field' ),
41
- 'schema' => null
42
- ) );
 
 
 
 
43
 
44
  }
45
 
@@ -52,9 +56,9 @@ class WPSubtitle_REST {
52
  *
53
  * @internal Called via register_rest_field() callback.
54
  *
55
- * @param array $object Current post details.
56
- * @param string $field_name Name of field.
57
- * @param WP_REST_Request $request Current request.
58
  * @return string Subtitle
59
  */
60
  public function get_rest_field( $object, $field_name, $request ) {
@@ -72,8 +76,8 @@ class WPSubtitle_REST {
72
  *
73
  * @internal Called via register_rest_field() callback.
74
  *
75
- * @param string $value New value for the field.
76
- * @param array $object Current post details.
77
  */
78
  public function update_rest_field( $value, $object ) {
79
 
35
 
36
  foreach ( $post_types as $post_type ) {
37
 
38
+ register_rest_field(
39
+ $post_types,
40
+ 'wps_subtitle',
41
+ array(
42
+ 'get_callback' => array( $this, 'get_rest_field' ),
43
+ 'update_callback' => array( $this, 'update_rest_field' ),
44
+ 'schema' => null,
45
+ )
46
+ );
47
 
48
  }
49
 
56
  *
57
  * @internal Called via register_rest_field() callback.
58
  *
59
+ * @param array $object Current post details.
60
+ * @param string $field_name Name of field.
61
+ * @param WP_REST_Request $request Current request.
62
  * @return string Subtitle
63
  */
64
  public function get_rest_field( $object, $field_name, $request ) {
76
  *
77
  * @internal Called via register_rest_field() callback.
78
  *
79
+ * @param string $value New value for the field.
80
+ * @param array $object Current post details.
81
  */
82
  public function update_rest_field( $value, $object ) {
83
 
plugin/includes/shortcode.php CHANGED
@@ -15,40 +15,46 @@ class WPSubtitle_Shortcode {
15
  * content will be used as a fallback if no subtitle is specified.
16
  * e.g. [wp_subtitle]Fallback Subtitle[/wp_subtitle]
17
  *
18
- * @param array $atts Shortcode attributes.
19
- * @param string $content Fallback content (content between the shortcode tags).
20
  * @return string Subtitle HTML.
21
  */
22
  public static function shortcode( $atts, $content = null ) {
23
 
24
  global $post;
25
 
26
- $atts = shortcode_atts( array(
27
- 'tag' => self::get_default_tag(),
28
- 'before' => '',
29
- 'after' => ''
30
- ), $atts, 'wp_subtitle' );
 
 
 
 
31
 
32
  // Get HTML tag
33
  if ( ! empty( $atts['tag'] ) ) {
34
- $tag = self::validate_tag( $atts['tag'] );
35
  $before = sprintf( '<%s class="wp-subtitle">', $tag );
36
- $after = sprintf( '</%s>', $tag );
37
  } else {
38
  $before = '';
39
- $after = '';
40
  }
41
 
42
  // Add before/after content
43
  $before .= self::format_subtitle_content( $atts['before'], 'before' );
44
- $after = self::format_subtitle_content( $atts['after'], 'after' ) . $after;
45
 
46
  $subtitle = new WP_Subtitle( $post );
47
 
48
- return $subtitle->get_subtitle( array(
49
- 'before' => $before,
50
- 'after' => $after
51
- ) );
 
 
52
 
53
  }
54
 
@@ -88,12 +94,12 @@ class WPSubtitle_Shortcode {
88
  * @since 2.5
89
  * @internal
90
  *
91
- * @param string $tag Tag to validate.
92
  * @return string Validated tag.
93
  */
94
  private static function validate_tag( $tag ) {
95
 
96
- if ( ! in_array( $tag, self::get_allowed_tags() ) ) {
97
  $tag = self::get_default_tag();
98
  }
99
 
@@ -107,8 +113,8 @@ class WPSubtitle_Shortcode {
107
  * @since 2.5
108
  * @internal
109
  *
110
- * @param string $content Content.
111
- * @param string $type Content type.
112
  * @return string HTML formatted content.
113
  */
114
  private static function format_subtitle_content( $content, $type ) {
15
  * content will be used as a fallback if no subtitle is specified.
16
  * e.g. [wp_subtitle]Fallback Subtitle[/wp_subtitle]
17
  *
18
+ * @param array $atts Shortcode attributes.
19
+ * @param string $content Fallback content (content between the shortcode tags).
20
  * @return string Subtitle HTML.
21
  */
22
  public static function shortcode( $atts, $content = null ) {
23
 
24
  global $post;
25
 
26
+ $atts = shortcode_atts(
27
+ array(
28
+ 'tag' => self::get_default_tag(),
29
+ 'before' => '',
30
+ 'after' => '',
31
+ ),
32
+ $atts,
33
+ 'wp_subtitle'
34
+ );
35
 
36
  // Get HTML tag
37
  if ( ! empty( $atts['tag'] ) ) {
38
+ $tag = self::validate_tag( $atts['tag'] );
39
  $before = sprintf( '<%s class="wp-subtitle">', $tag );
40
+ $after = sprintf( '</%s>', $tag );
41
  } else {
42
  $before = '';
43
+ $after = '';
44
  }
45
 
46
  // Add before/after content
47
  $before .= self::format_subtitle_content( $atts['before'], 'before' );
48
+ $after = self::format_subtitle_content( $atts['after'], 'after' ) . $after;
49
 
50
  $subtitle = new WP_Subtitle( $post );
51
 
52
+ return $subtitle->get_subtitle(
53
+ array(
54
+ 'before' => $before,
55
+ 'after' => $after,
56
+ )
57
+ );
58
 
59
  }
60
 
94
  * @since 2.5
95
  * @internal
96
  *
97
+ * @param string $tag Tag to validate.
98
  * @return string Validated tag.
99
  */
100
  private static function validate_tag( $tag ) {
101
 
102
+ if ( ! in_array( $tag, self::get_allowed_tags(), true ) ) {
103
  $tag = self::get_default_tag();
104
  }
105
 
113
  * @since 2.5
114
  * @internal
115
  *
116
+ * @param string $content Content.
117
+ * @param string $type Content type.
118
  * @return string HTML formatted content.
119
  */
120
  private static function format_subtitle_content( $content, $type ) {
plugin/includes/subtitle.php CHANGED
@@ -17,7 +17,7 @@ class WP_Subtitle {
17
  /**
18
  * Constructor
19
  *
20
- * @param int|WP_Post $post Post object or ID.
21
  */
22
  public function __construct( $post ) {
23
 
@@ -33,11 +33,11 @@ class WP_Subtitle {
33
  /**
34
  * The Subtitle
35
  *
36
- * @param array $args Display parameters.
37
  */
38
  public function the_subtitle( $args = '' ) {
39
 
40
- echo $this->get_subtitle( $args );
41
 
42
  }
43
 
@@ -46,17 +46,20 @@ class WP_Subtitle {
46
  *
47
  * @uses apply_filters( 'wps_subtitle' )
48
  *
49
- * @param array $args Display parameters.
50
  * @return string The filtered subtitle meta value.
51
  */
52
  public function get_subtitle( $args = '' ) {
53
 
54
  if ( $this->post_id && $this->is_supported_post_type() ) {
55
 
56
- $args = wp_parse_args( $args, array(
57
- 'before' => '',
58
- 'after' => ''
59
- ) );
 
 
 
60
 
61
  $subtitle = apply_filters( 'wps_subtitle', $this->get_raw_subtitle(), get_post( $this->post_id ) );
62
 
@@ -82,15 +85,16 @@ class WP_Subtitle {
82
  if ( is_preview() ) {
83
 
84
  if ( isset( $_GET['preview_id'] ) ) {
85
- $p = wp_get_post_autosave( $this->post_id );
86
  return get_post_meta( $p->ID, $this->get_post_meta_key(), true );
87
  }
88
 
89
- if ( $revisions = wp_get_post_revisions( $this->post_id ) ) {
 
 
90
  $p = array_shift( $revisions );
91
  return get_post_meta( $p->ID, $this->get_post_meta_key(), true );
92
  }
93
-
94
  }
95
 
96
  return get_post_meta( $this->post_id, $this->get_post_meta_key(), true );
@@ -113,7 +117,7 @@ class WP_Subtitle {
113
  /**
114
  * Update Subtitle
115
  *
116
- * @param string $subtitle Subtitle.
117
  * @return int|bool Meta ID if new entry. True if updated, false if not updated or the same as current value.
118
  */
119
  public function update_subtitle( $subtitle ) {
@@ -128,12 +132,12 @@ class WP_Subtitle {
128
  *
129
  * @since 2.9
130
  *
131
- * @param string $subtitle Subtitle value.
132
  * @return boolean
133
  */
134
  public function is_current_subtitle( $subtitle ) {
135
 
136
- return $subtitle === get_metadata( 'post', $this->post_id, 'wps_subtitle', true );
137
 
138
  }
139
 
@@ -155,7 +159,7 @@ class WP_Subtitle {
155
  *
156
  * @since 2.9
157
  *
158
- * @param int $revision_id Revision ID.
159
  */
160
  public function restore_post_revision( $revision_id ) {
161
 
@@ -178,7 +182,7 @@ class WP_Subtitle {
178
 
179
  $post_types = $this->get_supported_post_types();
180
 
181
- return in_array( get_post_type( $this->post_id ), $post_types );
182
 
183
  }
184
 
@@ -191,9 +195,11 @@ class WP_Subtitle {
191
  */
192
  private function get_supported_post_types() {
193
 
194
- $post_types = (array) get_post_types( array(
195
- '_builtin' => false
196
- ) );
 
 
197
 
198
  $post_types = array_merge( $post_types, array( 'post', 'page', 'revision' ) );
199
 
@@ -222,8 +228,9 @@ class WP_Subtitle {
222
  if ( $this->is_supported_post_type() ) {
223
 
224
  $post_type = get_post_type( $this->post_id );
 
225
 
226
- if ( $revision = wp_is_post_revision( $this->post_id ) ) {
227
  $post_type = get_post_type( $revision );
228
  }
229
 
@@ -240,15 +247,16 @@ class WP_Subtitle {
240
 
241
  // ... edit other post type
242
  default:
243
-
244
- $post_types = (array) get_post_types( array(
245
- '_builtin' => false
246
- ), 'objects' );
 
 
247
 
248
  return current_user_can( $post_types[ $post_type ]->cap->edit_post, $this->post_id );
249
 
250
  }
251
-
252
  }
253
 
254
  return false;
17
  /**
18
  * Constructor
19
  *
20
+ * @param int|WP_Post $post Post object or ID.
21
  */
22
  public function __construct( $post ) {
23
 
33
  /**
34
  * The Subtitle
35
  *
36
+ * @param array $args Display parameters.
37
  */
38
  public function the_subtitle( $args = '' ) {
39
 
40
+ echo wp_kses_post( $this->get_subtitle( $args ) );
41
 
42
  }
43
 
46
  *
47
  * @uses apply_filters( 'wps_subtitle' )
48
  *
49
+ * @param array $args Display parameters.
50
  * @return string The filtered subtitle meta value.
51
  */
52
  public function get_subtitle( $args = '' ) {
53
 
54
  if ( $this->post_id && $this->is_supported_post_type() ) {
55
 
56
+ $args = wp_parse_args(
57
+ $args,
58
+ array(
59
+ 'before' => '',
60
+ 'after' => '',
61
+ )
62
+ );
63
 
64
  $subtitle = apply_filters( 'wps_subtitle', $this->get_raw_subtitle(), get_post( $this->post_id ) );
65
 
85
  if ( is_preview() ) {
86
 
87
  if ( isset( $_GET['preview_id'] ) ) {
88
+ $p = wp_get_post_autosave( $this->post_id );
89
  return get_post_meta( $p->ID, $this->get_post_meta_key(), true );
90
  }
91
 
92
+ $revisions = wp_get_post_revisions( $this->post_id );
93
+
94
+ if ( $revisions ) {
95
  $p = array_shift( $revisions );
96
  return get_post_meta( $p->ID, $this->get_post_meta_key(), true );
97
  }
 
98
  }
99
 
100
  return get_post_meta( $this->post_id, $this->get_post_meta_key(), true );
117
  /**
118
  * Update Subtitle
119
  *
120
+ * @param string $subtitle Subtitle.
121
  * @return int|bool Meta ID if new entry. True if updated, false if not updated or the same as current value.
122
  */
123
  public function update_subtitle( $subtitle ) {
132
  *
133
  * @since 2.9
134
  *
135
+ * @param string $subtitle Subtitle value.
136
  * @return boolean
137
  */
138
  public function is_current_subtitle( $subtitle ) {
139
 
140
+ return get_metadata( 'post', $this->post_id, 'wps_subtitle', true ) === $subtitle;
141
 
142
  }
143
 
159
  *
160
  * @since 2.9
161
  *
162
+ * @param int $revision_id Revision ID.
163
  */
164
  public function restore_post_revision( $revision_id ) {
165
 
182
 
183
  $post_types = $this->get_supported_post_types();
184
 
185
+ return in_array( get_post_type( $this->post_id ), $post_types, true );
186
 
187
  }
188
 
195
  */
196
  private function get_supported_post_types() {
197
 
198
+ $post_types = (array) get_post_types(
199
+ array(
200
+ '_builtin' => false,
201
+ )
202
+ );
203
 
204
  $post_types = array_merge( $post_types, array( 'post', 'page', 'revision' ) );
205
 
228
  if ( $this->is_supported_post_type() ) {
229
 
230
  $post_type = get_post_type( $this->post_id );
231
+ $revision = wp_is_post_revision( $this->post_id );
232
 
233
+ if ( $revision ) {
234
  $post_type = get_post_type( $revision );
235
  }
236
 
247
 
248
  // ... edit other post type
249
  default:
250
+ $post_types = (array) get_post_types(
251
+ array(
252
+ '_builtin' => false,
253
+ ),
254
+ 'objects'
255
+ );
256
 
257
  return current_user_can( $post_types[ $post_type ]->cap->edit_post, $this->post_id );
258
 
259
  }
 
260
  }
261
 
262
  return false;
plugin/plugin.php CHANGED
@@ -12,22 +12,22 @@ define( 'WPSUBTITLE_URL', plugins_url( WPSUBTITLE_SUBDIR ) );
12
  define( 'WPSUBTITLE_DIR', plugin_dir_path( __FILE__ ) );
13
 
14
  // Includes
15
- include_once( WPSUBTITLE_DIR . 'includes/class-api.php' );
16
- include_once( WPSUBTITLE_DIR . 'includes/subtitle.php' );
17
- include_once( WPSUBTITLE_DIR . 'includes/deprecated.php' );
18
- include_once( WPSUBTITLE_DIR . 'includes/shortcode.php' );
19
- include_once( WPSUBTITLE_DIR . 'includes/rest.php' );
20
- include_once( WPSUBTITLE_DIR . 'includes/compat/wordpress-seo.php' );
21
- include_once( WPSUBTITLE_DIR . 'includes/compat/seopress.php' );
22
- include_once( WPSUBTITLE_DIR . 'includes/compat/woocommerce.php' );
23
 
24
  // Include admin-only functionality
25
  if ( is_admin() ) {
26
- require_once( WPSUBTITLE_DIR . 'admin/admin.php' );
27
  if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
28
  // Load AJAX functions here if required...
29
  } else {
30
- require_once( WPSUBTITLE_DIR . 'admin/pointers.php' );
31
  }
32
  }
33
 
@@ -90,10 +90,10 @@ class WPSubtitle {
90
  */
91
  public static function load() {
92
 
93
- self::$api = new WP_Subtitle_API();
94
- self::$rest = new WPSubtitle_REST();
95
- self::$wpseo = new WPSubtitle_WPSEO();
96
- self::$seopress = new WPSubtitle_SEOPress();
97
  self::$woocommerce = new WPSubtitle_WooCommerce();
98
 
99
  self::$api->setup_hooks();
@@ -122,11 +122,13 @@ class WPSubtitle {
122
  * @return array Array of supported post types.
123
  */
124
  public static function get_supported_post_types() {
125
- $post_types = (array) get_post_types( array(
126
- '_builtin' => false
127
- ) );
 
 
128
  $post_types = array_merge( $post_types, array( 'post', 'page', 'revision' ) );
129
- $supported = array();
130
  foreach ( $post_types as $post_type ) {
131
  if ( post_type_supports( $post_type, 'wps_subtitle' ) ) {
132
  $supported[] = $post_type;
@@ -140,12 +142,12 @@ class WPSubtitle {
140
  *
141
  * @since 2.3
142
  *
143
- * @param string $post_type Post Type.
144
  * @return boolean
145
  */
146
  public static function is_supported_post_type( $post_type ) {
147
  $post_types = self::get_supported_post_types();
148
- if ( in_array( $post_type, $post_types ) ) {
149
  return true;
150
  }
151
  return false;
@@ -158,7 +160,7 @@ class WPSubtitle {
158
  *
159
  * @uses WP_Subtitle::get_subtitle()
160
  *
161
- * @param int|object $post Post ID or object.
162
  * @return string The filtered subtitle meta value.
163
  */
164
  public static function get_the_subtitle( $post = 0 ) {
@@ -177,7 +179,7 @@ class WPSubtitle {
177
  *
178
  * @uses WP_Subtitle::get_raw_subtitle()
179
  *
180
- * @param int|object $post Post ID or object.
181
  * @return string The subtitle meta value.
182
  */
183
  public static function _get_post_meta( $post = 0 ) {
@@ -194,7 +196,7 @@ class WPSubtitle {
194
  * @since 2.5.x
195
  * @internal
196
  *
197
- * @param int $post Post ID.
198
  * @return string The subtitle meta key.
199
  */
200
  public static function _get_post_meta_key( $post_id = 0 ) {
12
  define( 'WPSUBTITLE_DIR', plugin_dir_path( __FILE__ ) );
13
 
14
  // Includes
15
+ require_once WPSUBTITLE_DIR . 'includes/class-api.php';
16
+ require_once WPSUBTITLE_DIR . 'includes/subtitle.php';
17
+ require_once WPSUBTITLE_DIR . 'includes/deprecated.php';
18
+ require_once WPSUBTITLE_DIR . 'includes/shortcode.php';
19
+ require_once WPSUBTITLE_DIR . 'includes/rest.php';
20
+ require_once WPSUBTITLE_DIR . 'includes/compat/wordpress-seo.php';
21
+ require_once WPSUBTITLE_DIR . 'includes/compat/seopress.php';
22
+ require_once WPSUBTITLE_DIR . 'includes/compat/woocommerce.php';
23
 
24
  // Include admin-only functionality
25
  if ( is_admin() ) {
26
+ require_once WPSUBTITLE_DIR . 'admin/admin.php';
27
  if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
28
  // Load AJAX functions here if required...
29
  } else {
30
+ require_once WPSUBTITLE_DIR . 'admin/pointers.php';
31
  }
32
  }
33
 
90
  */
91
  public static function load() {
92
 
93
+ self::$api = new WP_Subtitle_API();
94
+ self::$rest = new WPSubtitle_REST();
95
+ self::$wpseo = new WPSubtitle_WPSEO();
96
+ self::$seopress = new WPSubtitle_SEOPress();
97
  self::$woocommerce = new WPSubtitle_WooCommerce();
98
 
99
  self::$api->setup_hooks();
122
  * @return array Array of supported post types.
123
  */
124
  public static function get_supported_post_types() {
125
+ $post_types = (array) get_post_types(
126
+ array(
127
+ '_builtin' => false,
128
+ )
129
+ );
130
  $post_types = array_merge( $post_types, array( 'post', 'page', 'revision' ) );
131
+ $supported = array();
132
  foreach ( $post_types as $post_type ) {
133
  if ( post_type_supports( $post_type, 'wps_subtitle' ) ) {
134
  $supported[] = $post_type;
142
  *
143
  * @since 2.3
144
  *
145
+ * @param string $post_type Post Type.
146
  * @return boolean
147
  */
148
  public static function is_supported_post_type( $post_type ) {
149
  $post_types = self::get_supported_post_types();
150
+ if ( in_array( $post_type, $post_types, true ) ) {
151
  return true;
152
  }
153
  return false;
160
  *
161
  * @uses WP_Subtitle::get_subtitle()
162
  *
163
+ * @param int|object $post Post ID or object.
164
  * @return string The filtered subtitle meta value.
165
  */
166
  public static function get_the_subtitle( $post = 0 ) {
179
  *
180
  * @uses WP_Subtitle::get_raw_subtitle()
181
  *
182
+ * @param int|object $post Post ID or object.
183
  * @return string The subtitle meta value.
184
  */
185
  public static function _get_post_meta( $post = 0 ) {
196
  * @since 2.5.x
197
  * @internal
198
  *
199
+ * @param int $post_id Post ID.
200
  * @return string The subtitle meta key.
201
  */
202
  public static function _get_post_meta_key( $post_id = 0 ) {
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: husobj, husani
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=SLZUF4XJTS4E6
4
  Tags: subtitle, content, title, subheading, subhead, alternate title
5
  Requires at least: 3.7
6
- Tested up to: 5.3.2
7
- Stable tag: 3.4
8
  License: GPLv2
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.txt
10
 
@@ -153,6 +153,10 @@ The plugin is [hosted on GitHub](https://github.com/benhuson/wp-subtitle) and pu
153
 
154
  == Changelog ==
155
 
 
 
 
 
156
  = 3.4 =
157
  * Added support for the SEOPress plugin. Props @chriselkins.
158
  * You can now update the subtitle via the REST API. Props @chriselkins.
@@ -254,6 +258,9 @@ The plugin is [hosted on GitHub](https://github.com/benhuson/wp-subtitle) and pu
254
 
255
  == Upgrade Notice ==
256
 
 
 
 
257
  = 3.4 =
258
  Added support for the SEOPress plugin and updating the subtitle via the REST API.
259
 
3
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=SLZUF4XJTS4E6
4
  Tags: subtitle, content, title, subheading, subhead, alternate title
5
  Requires at least: 3.7
6
+ Tested up to: 5.9.3
7
+ Stable tag: 3.4.1
8
  License: GPLv2
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.txt
10
 
153
 
154
  == Changelog ==
155
 
156
+ = 3.4.1 =
157
+ * Security: Resolve XSS issue by improving sanitization when saving subtitle custom field.
158
+ * Pass the current post object to the `wps_subtitle_field_placeholder` filter. Props [Dominik Schilling](https://github.com/ocean90).
159
+
160
  = 3.4 =
161
  * Added support for the SEOPress plugin. Props @chriselkins.
162
  * You can now update the subtitle via the REST API. Props @chriselkins.
258
 
259
  == Upgrade Notice ==
260
 
261
+ = 3.4.1 =
262
+ Resolve XSS issue by improving sanitization when saving subtitle custom field.
263
+
264
  = 3.4 =
265
  Added support for the SEOPress plugin and updating the subtitle via the REST API.
266
 
wp-subtitle.php CHANGED
@@ -4,7 +4,7 @@
4
  Plugin Name: WP Subtitle
5
  Plugin URI: http://wordpress.org/plugins/wp-subtitle/
6
  Description: Adds a subtitle field to pages and posts. Possible to add support for custom post types.
7
- Version: 3.4
8
  Author: Ben Huson, Husani Oakley
9
  Author URI: https://github.com/benhuson/wp-subtitle
10
  License: GPLv2
@@ -31,4 +31,4 @@ along with this program; if not, write to the Free Software
31
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32
  */
33
 
34
- require_once( plugin_dir_path( __FILE__ ) . 'plugin/plugin.php' );
4
  Plugin Name: WP Subtitle
5
  Plugin URI: http://wordpress.org/plugins/wp-subtitle/
6
  Description: Adds a subtitle field to pages and posts. Possible to add support for custom post types.
7
+ Version: 3.4.1
8
  Author: Ben Huson, Husani Oakley
9
  Author URI: https://github.com/benhuson/wp-subtitle
10
  License: GPLv2
31
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
32
  */
33
 
34
+ require_once plugin_dir_path( __FILE__ ) . 'plugin/plugin.php';