class-onpage.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. <?php
  2. /**
  3. * WPSEO plugin file.
  4. *
  5. * @package WPSEO\Admin
  6. */
  7. /**
  8. * Handles the request for getting the Ryte status.
  9. */
  10. class WPSEO_OnPage implements WPSEO_WordPress_Integration {
  11. /**
  12. * The name of the user meta key for storing the dismissed status.
  13. *
  14. * @var string
  15. */
  16. const USER_META_KEY = 'wpseo_dismiss_onpage';
  17. /**
  18. * Is the request started by pressing the fetch button.
  19. *
  20. * @var boolean
  21. */
  22. private $is_manual_request = false;
  23. /**
  24. * Constructs the object.
  25. */
  26. public function __construct() {
  27. $this->catch_redo_listener();
  28. }
  29. /**
  30. * Sets up the hooks.
  31. *
  32. * @return void
  33. */
  34. public function register_hooks() {
  35. // Adds admin notice if necessary.
  36. add_filter( 'admin_init', [ $this, 'show_notice' ] );
  37. if ( ! self::is_active() ) {
  38. return;
  39. }
  40. // Adds weekly schedule to the cron job schedules.
  41. add_filter( 'cron_schedules', [ $this, 'add_weekly_schedule' ] );
  42. // Sets the action for the Ryte fetch.
  43. add_action( 'wpseo_onpage_fetch', [ $this, 'fetch_from_onpage' ] );
  44. }
  45. /**
  46. * Shows a notice when the website is not indexable.
  47. *
  48. * @return void
  49. */
  50. public function show_notice() {
  51. $notification = $this->get_indexability_notification();
  52. $notification_center = Yoast_Notification_Center::get();
  53. if ( $this->should_show_notice() ) {
  54. $notification_center->add_notification( $notification );
  55. return;
  56. }
  57. $notification_center->remove_notification( $notification );
  58. }
  59. /**
  60. * Determines if we can use the functionality.
  61. *
  62. * @return bool True if this functionality can be used.
  63. */
  64. public static function is_active() {
  65. if ( wp_doing_ajax() ) {
  66. return false;
  67. }
  68. if ( ! WPSEO_Options::get( 'onpage_indexability' ) ) {
  69. return false;
  70. }
  71. return true;
  72. }
  73. /**
  74. * Hooks to run on plugin activation.
  75. */
  76. public function activate_hooks() {
  77. if ( $this->get_option()->is_enabled() ) {
  78. $this->schedule_cron();
  79. return;
  80. }
  81. $this->unschedule_cron();
  82. }
  83. /**
  84. * Adds a weekly cron schedule.
  85. *
  86. * @param array $schedules Currently scheduled items.
  87. *
  88. * @return array Enriched list of schedules.
  89. */
  90. public function add_weekly_schedule( $schedules ) {
  91. if ( ! is_array( $schedules ) ) {
  92. $schedules = [];
  93. }
  94. $schedules['weekly'] = [
  95. 'interval' => WEEK_IN_SECONDS,
  96. 'display' => __( 'Once Weekly', 'wordpress-seo' ),
  97. ];
  98. return $schedules;
  99. }
  100. /**
  101. * Fetches the data from Ryte.
  102. *
  103. * @return bool True if this has been run.
  104. */
  105. public function fetch_from_onpage() {
  106. $onpage_option = $this->get_option();
  107. if ( ! $onpage_option->should_be_fetched() ) {
  108. return false;
  109. }
  110. $new_status = $this->request_indexability();
  111. if ( false === $new_status ) {
  112. return false;
  113. }
  114. // Updates the timestamp in the option.
  115. $onpage_option->set_last_fetch( time() );
  116. // The currently indexability status.
  117. $old_status = $onpage_option->get_status();
  118. $onpage_option->set_status( $new_status );
  119. $onpage_option->save_option();
  120. // Check if the status has been changed.
  121. if ( $old_status !== $new_status && $new_status !== WPSEO_OnPage_Option::CANNOT_FETCH ) {
  122. $this->notify_admins();
  123. }
  124. return true;
  125. }
  126. /**
  127. * Retrieves the option to use.
  128. *
  129. * @return WPSEO_OnPage_Option The option.
  130. */
  131. protected function get_option() {
  132. return new WPSEO_OnPage_Option();
  133. }
  134. /**
  135. * Builds the indexability notification.
  136. *
  137. * @return Yoast_Notification The notification.
  138. */
  139. private function get_indexability_notification() {
  140. $notice = sprintf(
  141. /* translators: 1: opens a link to a related knowledge base article. 2: closes the link */
  142. __( '%1$sYour homepage cannot be indexed by search engines%2$s. This is very bad for SEO and should be fixed.', 'wordpress-seo' ),
  143. '<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/onpageindexerror' ) . '" target="_blank">',
  144. '</a>'
  145. );
  146. return new Yoast_Notification(
  147. $notice,
  148. [
  149. 'type' => Yoast_Notification::ERROR,
  150. 'id' => 'wpseo-dismiss-onpageorg',
  151. 'capabilities' => 'wpseo_manage_options',
  152. ]
  153. );
  154. }
  155. /**
  156. * Sends a request to Ryte to get the indexability.
  157. *
  158. * @return int|bool The indexability value.
  159. */
  160. protected function request_indexability() {
  161. $parameters = [];
  162. if ( $this->wordfence_protection_enabled() ) {
  163. $parameters['wf_strict'] = 1;
  164. }
  165. $request = new WPSEO_OnPage_Request();
  166. $response = $request->do_request( get_option( 'home' ), $parameters );
  167. if ( isset( $response['is_indexable'] ) ) {
  168. return (int) $response['is_indexable'];
  169. }
  170. return WPSEO_OnPage_Option::CANNOT_FETCH;
  171. }
  172. /**
  173. * Should the notice being given?
  174. *
  175. * @return bool True if a notice should be shown.
  176. */
  177. protected function should_show_notice() {
  178. if ( ! $this->get_option()->is_enabled() ) {
  179. return false;
  180. }
  181. // If development mode is on or the blog is not public, just don't show this notice.
  182. if ( WPSEO_Utils::is_development_mode() || ( '0' === get_option( 'blog_public' ) ) ) {
  183. return false;
  184. }
  185. return $this->get_option()->get_status() === WPSEO_OnPage_Option::IS_NOT_INDEXABLE;
  186. }
  187. /**
  188. * Notifies the admins.
  189. *
  190. * @return void
  191. */
  192. protected function notify_admins() {
  193. /*
  194. * Let's start showing the notices to all admins by removing the hide-notice meta data for each admin resulting
  195. * in popping up the notice again.
  196. */
  197. delete_metadata( 'user', 0, self::USER_META_KEY, '', true );
  198. }
  199. /**
  200. * Schedules the cronjob to get the new indexibility status.
  201. *
  202. * @return void
  203. */
  204. private function schedule_cron() {
  205. if ( wp_next_scheduled( 'wpseo_onpage_fetch' ) ) {
  206. return;
  207. }
  208. wp_schedule_event( time(), 'weekly', 'wpseo_onpage_fetch' );
  209. }
  210. /**
  211. * Unschedules the cronjob to get the new indexibility status.
  212. *
  213. * @return void
  214. */
  215. private function unschedule_cron() {
  216. if ( ! wp_next_scheduled( 'wpseo_onpage_fetch' ) ) {
  217. return;
  218. }
  219. wp_clear_scheduled_hook( 'wpseo_onpage_fetch' );
  220. }
  221. /**
  222. * Redo the fetch request for Ryte.
  223. *
  224. * @return void
  225. */
  226. private function catch_redo_listener() {
  227. if ( ! self::is_active() ) {
  228. return;
  229. }
  230. if ( filter_input( INPUT_GET, 'wpseo-redo-onpage' ) === '1' ) {
  231. $this->is_manual_request = true;
  232. add_action( 'admin_init', [ $this, 'fetch_from_onpage' ] );
  233. }
  234. }
  235. /**
  236. * Checks if WordFence protects the site against 'fake' Google crawlers.
  237. *
  238. * @return boolean True if WordFence protects the site.
  239. */
  240. private function wordfence_protection_enabled() {
  241. if ( ! class_exists( 'wfConfig' ) ) {
  242. return false;
  243. }
  244. if ( ! method_exists( 'wfConfig', 'get' ) ) {
  245. return false;
  246. }
  247. return (bool) wfConfig::get( 'blockFakeBots' );
  248. }
  249. }