class-slug-change-watcher.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. <?php
  2. /**
  3. * WPSEO plugin file.
  4. *
  5. * @package WPSEO\Admin\Watchers
  6. */
  7. /**
  8. * Class WPSEO_Slug_Change_Watcher.
  9. */
  10. class WPSEO_Slug_Change_Watcher implements WPSEO_WordPress_Integration {
  11. /**
  12. * Registers all hooks to WordPress.
  13. *
  14. * @return void
  15. */
  16. public function register_hooks() {
  17. // If the current plugin is Yoast SEO Premium, stop registering.
  18. if ( WPSEO_Utils::is_yoast_seo_premium() ) {
  19. return;
  20. }
  21. add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
  22. // Detect a post trash.
  23. add_action( 'wp_trash_post', [ $this, 'detect_post_trash' ] );
  24. // Detect a post delete.
  25. add_action( 'before_delete_post', [ $this, 'detect_post_delete' ] );
  26. // Detects deletion of a term.
  27. add_action( 'delete_term_taxonomy', [ $this, 'detect_term_delete' ] );
  28. }
  29. /**
  30. * Enqueues the quick edit handler.
  31. *
  32. * @return void
  33. */
  34. public function enqueue_assets() {
  35. global $pagenow;
  36. if ( ! in_array( $pagenow, [ 'edit.php', 'edit-tags.php' ], true ) ) {
  37. return;
  38. }
  39. $asset_manager = new WPSEO_Admin_Asset_Manager();
  40. $asset_manager->enqueue_script( 'quick-edit-handler' );
  41. }
  42. /**
  43. * Shows an message when a post is about to get trashed.
  44. *
  45. * @param integer $post_id The current post ID.
  46. *
  47. * @return void
  48. */
  49. public function detect_post_trash( $post_id ) {
  50. if ( ! $this->is_post_viewable( $post_id ) ) {
  51. return;
  52. }
  53. /* translators: %1$s expands to the translated name of the post type. */
  54. $first_sentence = sprintf( __( 'You just trashed a %1$s.', 'wordpress-seo' ), $this->get_post_type_label( get_post_type( $post_id ) ) );
  55. $message = $this->get_message( $first_sentence );
  56. $this->add_notification( $message );
  57. }
  58. /**
  59. * Shows an message when a post is about to get trashed.
  60. *
  61. * @param integer $post_id The current post ID.
  62. *
  63. * @return void
  64. */
  65. public function detect_post_delete( $post_id ) {
  66. if ( ! $this->is_post_viewable( $post_id ) ) {
  67. return;
  68. }
  69. /* translators: %1$s expands to the translated name of the post type. */
  70. $first_sentence = sprintf( __( 'You just deleted a %1$s.', 'wordpress-seo' ), $this->get_post_type_label( get_post_type( $post_id ) ) );
  71. $message = $this->get_message( $first_sentence );
  72. $this->add_notification( $message );
  73. }
  74. /**
  75. * Shows a message when a term is about to get deleted.
  76. *
  77. * @param integer $term_id The term ID that will be deleted.
  78. *
  79. * @return void
  80. */
  81. public function detect_term_delete( $term_id ) {
  82. if ( ! $this->is_term_viewable( $term_id ) ) {
  83. return;
  84. }
  85. $first_sentence = sprintf(
  86. /* translators: 1: term label */
  87. __( 'You just deleted a %1$s.', 'wordpress-seo' ),
  88. $this->get_taxonomy_label_for_term( $term_id )
  89. );
  90. $message = $this->get_message( $first_sentence );
  91. $this->add_notification( $message );
  92. }
  93. /**
  94. * Checks if the post is viewable.
  95. *
  96. * @param string $post_id The post id to check.
  97. *
  98. * @return bool Whether the post is viewable or not.
  99. */
  100. protected function is_post_viewable( $post_id ) {
  101. $post_type = get_post_type( $post_id );
  102. if ( ! WPSEO_Post_Type::is_post_type_accessible( $post_type ) ) {
  103. return false;
  104. }
  105. $post_status = get_post_status( $post_id );
  106. if ( ! $this->check_visible_post_status( $post_status ) ) {
  107. return false;
  108. }
  109. return true;
  110. }
  111. /**
  112. * Checks if the term is viewable.
  113. *
  114. * @param string $term_id The term ID to check.
  115. *
  116. * @return bool Whether the term is viewable or not.
  117. */
  118. protected function is_term_viewable( $term_id ) {
  119. $term = get_term( $term_id );
  120. if ( ! $term || is_wp_error( $term ) ) {
  121. return false;
  122. }
  123. $taxonomy = get_taxonomy( $term->taxonomy );
  124. if ( ! $taxonomy ) {
  125. return false;
  126. }
  127. return $taxonomy->publicly_queryable || $taxonomy->public;
  128. }
  129. /**
  130. * Gets the taxonomy label to use for a term.
  131. *
  132. * @param int $term_id The term ID.
  133. *
  134. * @return string The taxonomy's singular label.
  135. */
  136. protected function get_taxonomy_label_for_term( $term_id ) {
  137. $term = get_term( $term_id );
  138. $taxonomy = get_taxonomy( $term->taxonomy );
  139. return $taxonomy->labels->singular_name;
  140. }
  141. /**
  142. * Retrieves the singular post type label.
  143. *
  144. * @param string $post_type Post type to retrieve label from.
  145. *
  146. * @return string The singular post type name.
  147. */
  148. protected function get_post_type_label( $post_type ) {
  149. $post_type_object = get_post_type_object( $post_type );
  150. // If the post type of this post wasn't registered default back to post.
  151. if ( $post_type_object === null ) {
  152. $post_type_object = get_post_type_object( 'post' );
  153. }
  154. return $post_type_object->labels->singular_name;
  155. }
  156. /**
  157. * Checks whether the given post status is visible or not.
  158. *
  159. * @param string $post_status The post status to check.
  160. *
  161. * @return bool Whether or not the post is visible.
  162. */
  163. protected function check_visible_post_status( $post_status ) {
  164. $visible_post_statuses = [
  165. 'publish',
  166. 'static',
  167. 'private',
  168. ];
  169. return in_array( $post_status, $visible_post_statuses, true );
  170. }
  171. /**
  172. * Returns the message around changed URLs.
  173. *
  174. * @param string $first_sentence The first sentence of the notification.
  175. *
  176. * @return string The full notification.
  177. */
  178. protected function get_message( $first_sentence ) {
  179. return '<h2>' . __( 'Make sure you don\'t miss out on traffic!', 'wordpress-seo' ) . '</h2>'
  180. . '<p>'
  181. . $first_sentence
  182. . ' ' . __( 'Search engines and other websites can still send traffic to your deleted post.', 'wordpress-seo' )
  183. . ' ' . __( 'You should create a redirect to ensure your visitors do not get a 404 error when they click on the no longer working URL.', 'wordpress-seo' )
  184. /* translators: %s expands to Yoast SEO Premium */
  185. . ' ' . sprintf( __( 'With %s, you can easily create such redirects.', 'wordpress-seo' ), 'Yoast SEO Premium' )
  186. . '</p>'
  187. . '<p><a class="yoast-button-upsell" href="' . WPSEO_Shortlinker::get( 'https://yoa.st/1d0' ) . '" target="_blank">'
  188. /* translators: %s expands to Yoast SEO Premium */
  189. . sprintf( __( 'Get %s', 'wordpress-seo' ), 'Yoast SEO Premium' )
  190. . '<span class="screen-reader-text">' . __( '(Opens in a new browser tab)', 'wordpress-seo' ) . '</span>'
  191. . '<span aria-hidden="true" class="yoast-button-upsell__caret"></span>'
  192. . '</a></p>';
  193. }
  194. /**
  195. * Adds a notification to be shown on the next page request since posts are updated in an ajax request.
  196. *
  197. * @param string $message The message to add to the notification.
  198. *
  199. * @return void
  200. */
  201. protected function add_notification( $message ) {
  202. $notification = new Yoast_Notification(
  203. $message,
  204. [
  205. 'type' => 'notice-warning is-dismissible',
  206. 'yoast_branding' => true,
  207. ]
  208. );
  209. $notification_center = Yoast_Notification_Center::get();
  210. $notification_center->add_notification( $notification );
  211. }
  212. }