class-statistics-service.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. <?php
  2. /**
  3. * WPSEO plugin file.
  4. *
  5. * @package WPSEO\Admin\Statistics
  6. */
  7. /**
  8. * Class WPSEO_Statistics_Service
  9. */
  10. class WPSEO_Statistics_Service {
  11. const CACHE_TRANSIENT_KEY = 'wpseo-statistics-totals';
  12. /**
  13. * @var WPSEO_Statistics
  14. */
  15. protected $statistics;
  16. /**
  17. * @var string[]
  18. */
  19. protected $labels;
  20. /**
  21. * WPSEO_Statistics_Service contructor.
  22. *
  23. * @param WPSEO_Statistics $statistics The statistics class to retrieve statistics from.
  24. */
  25. public function __construct( WPSEO_Statistics $statistics ) {
  26. $this->statistics = $statistics;
  27. $this->labels = $this->labels();
  28. }
  29. /**
  30. * Fetches statistics by REST request.
  31. *
  32. * @return WP_REST_Response The response object.
  33. */
  34. public function get_statistics() {
  35. $statistics = $this->statistic_items();
  36. $data = array(
  37. 'header' => $this->get_header_from_statistics( $statistics ),
  38. 'seo_scores' => $statistics['scores'],
  39. );
  40. return new WP_REST_Response( $data );
  41. }
  42. /**
  43. * Gets a header summarizing the given statistics results.
  44. *
  45. * @param array $statistics The statistics results.
  46. *
  47. * @return string The header summing up the statistics results.
  48. */
  49. private function get_header_from_statistics( array $statistics ) {
  50. // Personal interpretation to allow release, should be looked at later.
  51. if ( $statistics['division'] === false ) {
  52. return __( 'You don\'t have any published posts, your SEO scores will appear here once you make your first post!', 'wordpress-seo' );
  53. }
  54. if ( $statistics['division']['good'] > 0.66 ) {
  55. return __( 'Hey, your SEO is doing pretty well! Check out the stats:', 'wordpress-seo' );
  56. }
  57. return __( 'Below are your published posts\' SEO scores. Now is as good a time as any to start improving some of your posts!', 'wordpress-seo' );
  58. }
  59. /**
  60. * An array representing items to be added to the At a Glance dashboard widget
  61. *
  62. * @return array The statistics for the current user.
  63. */
  64. private function statistic_items() {
  65. $transient = $this->get_transient();
  66. $user_id = get_current_user_id();
  67. if ( isset( $transient[ $user_id ] ) ) {
  68. return $transient[ $user_id ];
  69. }
  70. return $this->set_statistic_items_for_user( $transient, $user_id );
  71. }
  72. /**
  73. * Gets the statistics transient value. Returns array if transient wasn't set.
  74. *
  75. * @return array|mixed Returns the transient or an empty array if the transient doesn't exist.
  76. */
  77. private function get_transient() {
  78. $transient = get_transient( self::CACHE_TRANSIENT_KEY );
  79. if ( $transient === false ) {
  80. return array();
  81. }
  82. return $transient;
  83. }
  84. /**
  85. * Set the statistics transient cache for a specific user
  86. *
  87. * @param array $transient The current stored transient with the cached data.
  88. * @param int $user The user's ID to assign the retrieved values to.
  89. *
  90. * @return array The statistics transient for the user.
  91. */
  92. private function set_statistic_items_for_user( $transient, $user ) {
  93. $scores = $this->get_seo_scores_with_post_count();
  94. $division = $this->get_seo_score_division( $scores );
  95. $transient[ $user ] = array(
  96. // Use array_values because array_filter may return non-zero indexed arrays.
  97. 'scores' => array_values( array_filter( $scores, array( $this, 'filter_items' ) ) ),
  98. 'division' => $division,
  99. );
  100. set_transient( self::CACHE_TRANSIENT_KEY, $transient, DAY_IN_SECONDS );
  101. return $transient[ $user ];
  102. }
  103. /**
  104. * Gets the division of SEO scores.
  105. *
  106. * @param array $scores The SEO scores.
  107. *
  108. * @return array|bool The division of SEO scores, false if there are no posts.
  109. */
  110. private function get_seo_score_division( array $scores ) {
  111. $total = 0;
  112. $division = array();
  113. foreach ( $scores as $score ) {
  114. $total += $score['count'];
  115. }
  116. if ( $total === 0 ) {
  117. return false;
  118. }
  119. foreach ( $scores as $score ) {
  120. $division[ $score['seo_rank'] ] = ( $score['count'] / $total );
  121. }
  122. return $division;
  123. }
  124. /**
  125. * Get all SEO ranks and data associated with them.
  126. *
  127. * @return array An array of SEO scores and associated data.
  128. */
  129. private function get_seo_scores_with_post_count() {
  130. $ranks = WPSEO_Rank::get_all_ranks();
  131. return array_map( array( $this, 'map_rank_to_widget' ), $ranks );
  132. }
  133. /**
  134. * Converts a rank to data usable in the dashboard widget.
  135. *
  136. * @param WPSEO_Rank $rank The rank to map.
  137. *
  138. * @return array The mapped rank.
  139. */
  140. private function map_rank_to_widget( WPSEO_Rank $rank ) {
  141. return array(
  142. 'seo_rank' => $rank->get_rank(),
  143. 'label' => $this->get_label_for_rank( $rank ),
  144. 'count' => $this->statistics->get_post_count( $rank ),
  145. 'link' => $this->get_link_for_rank( $rank ),
  146. );
  147. }
  148. /**
  149. * Returns a dashboard widget label to use for a certain rank.
  150. *
  151. * @param WPSEO_Rank $rank The rank to return a label for.
  152. *
  153. * @return string The label for the rank.
  154. */
  155. private function get_label_for_rank( WPSEO_Rank $rank ) {
  156. return $this->labels[ $rank->get_rank() ];
  157. }
  158. /**
  159. * Determines the labels for the various scoring ranks that are known within Yoast SEO.
  160. *
  161. * @return array Array containing the translateable labels.
  162. */
  163. private function labels() {
  164. return array(
  165. /* translators: %1$s expands to an opening strong tag, %2$s expands to a closing strong tag */
  166. WPSEO_Rank::NO_FOCUS => sprintf( __( 'Posts %1$swithout%2$s a focus keyword', 'wordpress-seo' ), '<strong>', '</strong>' ),
  167. /* translators: %1$s expands to an opening strong tag, %2$s expands to a closing strong tag */
  168. WPSEO_Rank::BAD => sprintf( __( 'Posts with the SEO score: %1$sneeds improvement%2$s', 'wordpress-seo' ), '<strong>', '</strong>' ),
  169. /* translators: %1$s expands to an opening strong tag, %2$s expands to a closing strong tag */
  170. WPSEO_Rank::OK => sprintf( __( 'Posts with the SEO score: %1$sOK%2$s', 'wordpress-seo' ), '<strong>', '</strong>' ),
  171. /* translators: %1$s expands to an opening strong tag, %2$s expands to a closing strong tag */
  172. WPSEO_Rank::GOOD => sprintf( __( 'Posts with the SEO score: %1$sgood%2$s', 'wordpress-seo' ), '<strong>', '</strong>' ),
  173. WPSEO_Rank::NO_INDEX => __( 'Posts that should not show up in search results', 'wordpress-seo' ),
  174. );
  175. }
  176. /**
  177. * Filter items if they have a count of zero.
  178. *
  179. * @param array $item The item to potentially filter out.
  180. *
  181. * @return bool Whether or not the count is zero.
  182. */
  183. private function filter_items( $item ) {
  184. return $item['count'] !== 0;
  185. }
  186. /**
  187. * Returns a link for the overview of posts of a certain rank.
  188. *
  189. * @param WPSEO_Rank $rank The rank to return a link for.
  190. *
  191. * @return string The link that shows an overview of posts with that rank.
  192. */
  193. private function get_link_for_rank( WPSEO_Rank $rank ) {
  194. if ( current_user_can( 'edit_others_posts' ) === false ) {
  195. return esc_url( admin_url( 'edit.php?post_status=publish&post_type=post&seo_filter=' . $rank->get_rank() . '&author=' . get_current_user_id() ) );
  196. }
  197. return esc_url( admin_url( 'edit.php?post_status=publish&post_type=post&seo_filter=' . $rank->get_rank() ) );
  198. }
  199. }