class-onpage.php 6.5 KB

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