class-my-yoast-route.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. <?php
  2. /**
  3. * WPSEO plugin file.
  4. *
  5. * @package WPSEO\Admin
  6. */
  7. /**
  8. * Represents the route for MyYoast.
  9. */
  10. class WPSEO_MyYoast_Route implements WPSEO_WordPress_Integration {
  11. /**
  12. * The identifier of the page in the My Yoast route.
  13. *
  14. * @var string
  15. */
  16. const PAGE_IDENTIFIER = 'wpseo_myyoast';
  17. /**
  18. * The instance of the MyYoast client.
  19. *
  20. * @var WPSEO_MyYoast_Client
  21. */
  22. protected $client;
  23. /**
  24. * The actions that are supported.
  25. *
  26. * Each action should have a method named equally to the action.
  27. *
  28. * For example:
  29. * The connect action is handled by a method named 'connect'.
  30. *
  31. * @var array
  32. */
  33. protected static $allowed_actions = [ 'connect', 'authorize', 'complete' ];
  34. /**
  35. * Sets the hooks when the user has enough rights and is on the right page.
  36. *
  37. * @return void
  38. */
  39. public function register_hooks() {
  40. $route = filter_input( INPUT_GET, 'page' );
  41. if ( ! ( $this->is_myyoast_route( $route ) && $this->can_access_route() ) ) {
  42. return;
  43. }
  44. if ( ! $this->is_valid_action( $this->get_action() ) ) {
  45. return;
  46. }
  47. add_action( 'admin_menu', [ $this, 'register_route' ] );
  48. add_action( 'admin_init', [ $this, 'handle_route' ] );
  49. }
  50. /**
  51. * Registers the page for the MyYoast route.
  52. *
  53. * @codeCoverageIgnore
  54. *
  55. * @return void
  56. */
  57. public function register_route() {
  58. add_dashboard_page(
  59. '', // Is empty because we don't render a page.
  60. '', // Is empty because we don't want a menu item.
  61. 'wpseo_manage_options',
  62. self::PAGE_IDENTIFIER
  63. );
  64. }
  65. /**
  66. * Abstracts the action from the URL and follows the appropriate route.
  67. *
  68. * @return void
  69. */
  70. public function handle_route() {
  71. $action = $this->get_action();
  72. if ( ! $this->is_valid_action( $action ) || ! method_exists( $this, $action ) ) {
  73. return;
  74. }
  75. // Dynamically call the method.
  76. $this->$action();
  77. }
  78. /**
  79. * Checks if the current page is the MyYoast route.
  80. *
  81. * @param string $route The MyYoast route.
  82. *
  83. * @return bool True when url is the MyYoast route.
  84. */
  85. protected function is_myyoast_route( $route ) {
  86. return ( $route === self::PAGE_IDENTIFIER );
  87. }
  88. /**
  89. * Compares an action to a list of allowed actions to see if it is valid.
  90. *
  91. * @param string $action The action to check.
  92. *
  93. * @return bool True if the action is valid.
  94. */
  95. protected function is_valid_action( $action ) {
  96. return in_array( $action, self::$allowed_actions, true );
  97. }
  98. /**
  99. * Connects to MyYoast and generates a new clientId.
  100. *
  101. * @return void
  102. */
  103. protected function connect() {
  104. $client_id = $this->generate_uuid();
  105. $this->save_client_id( $client_id );
  106. $this->redirect(
  107. 'https://my.yoast.com/connect',
  108. [
  109. 'url' => WPSEO_Utils::get_home_url(),
  110. 'client_id' => $client_id,
  111. 'extensions' => $this->get_extensions(),
  112. 'redirect_url' => admin_url( 'admin.php?page=' . self::PAGE_IDENTIFIER . '&action=complete' ),
  113. 'credentials_url' => rest_url( 'yoast/v1/myyoast/connect' ),
  114. 'type' => 'wordpress',
  115. ]
  116. );
  117. }
  118. /**
  119. * Redirects the user to the oAuth authorization page.
  120. *
  121. * @return void
  122. */
  123. protected function authorize() {
  124. $client = $this->get_client();
  125. if ( ! $client->has_configuration() ) {
  126. return;
  127. }
  128. $this->redirect(
  129. $client->get_provider()->getAuthorizationUrl()
  130. );
  131. }
  132. /**
  133. * Completes the oAuth connection flow.
  134. *
  135. * @return void
  136. */
  137. protected function complete() {
  138. $client = $this->get_client();
  139. if ( ! $client->has_configuration() ) {
  140. return;
  141. }
  142. try {
  143. $access_token = $client
  144. ->get_provider()
  145. ->getAccessToken(
  146. 'authorization_code',
  147. [
  148. 'code' => $this->get_authorization_code(),
  149. ]
  150. );
  151. $client->save_access_token(
  152. $this->get_current_user_id(),
  153. $access_token
  154. );
  155. }
  156. // @codingStandardsIgnoreLine Generic.CodeAnalysis.EmptyStatement.DetectedCATCH -- There is nothing to do.
  157. catch ( Exception $e ) {
  158. // Do nothing.
  159. }
  160. $this->redirect_to_premium_page();
  161. }
  162. /**
  163. * Saves the client id.
  164. *
  165. * @codeCoverageIgnore
  166. *
  167. * @param string $client_id The client id to save.
  168. *
  169. * @return void
  170. */
  171. protected function save_client_id( $client_id ) {
  172. $this->get_client()->save_configuration(
  173. [
  174. 'clientId' => $client_id,
  175. ]
  176. );
  177. }
  178. /**
  179. * Creates a new MyYoast Client instance.
  180. *
  181. * @codeCoverageIgnore
  182. *
  183. * @return WPSEO_MyYoast_Client Instance of the myyoast client.
  184. */
  185. protected function get_client() {
  186. if ( ! $this->client ) {
  187. $this->client = new WPSEO_MyYoast_Client();
  188. }
  189. return $this->client;
  190. }
  191. /**
  192. * Abstracts the action from the url.
  193. *
  194. * @codeCoverageIgnore
  195. *
  196. * @return string The action from the url.
  197. */
  198. protected function get_action() {
  199. return filter_input( INPUT_GET, 'action' );
  200. }
  201. /**
  202. * Abstracts the authorization code from the url.
  203. *
  204. * @codeCoverageIgnore
  205. *
  206. * @return string The action from the url.
  207. */
  208. protected function get_authorization_code() {
  209. return filter_input( INPUT_GET, 'code' );
  210. }
  211. /**
  212. * Retrieves a list of activated extensions slugs.
  213. *
  214. * @codeCoverageIgnore
  215. *
  216. * @return array The extensions slugs.
  217. */
  218. protected function get_extensions() {
  219. $addon_manager = new WPSEO_Addon_Manager();
  220. return array_keys( $addon_manager->get_subscriptions_for_active_addons() );
  221. }
  222. /**
  223. * Generates an URL-encoded query string, redirects there.
  224. *
  225. * @codeCoverageIgnore
  226. *
  227. * @param string $url The url to redirect to.
  228. * @param array $query_args The additional arguments to build the url from.
  229. *
  230. * @return void
  231. */
  232. protected function redirect( $url, $query_args = [] ) {
  233. if ( ! empty( $query_args ) ) {
  234. $url .= '?' . http_build_query( $query_args );
  235. }
  236. wp_redirect( $url );
  237. exit;
  238. }
  239. /**
  240. * Checks if current user is allowed to access the route.
  241. *
  242. * @codeCoverageIgnore
  243. *
  244. * @return bool True when current user has rights to manage options.
  245. */
  246. protected function can_access_route() {
  247. return WPSEO_Utils::has_access_token_support() && current_user_can( 'wpseo_manage_options' );
  248. }
  249. /**
  250. * Generates an unique user id.
  251. *
  252. * @codeCoverageIgnore
  253. *
  254. * @return string The generated unique user id.
  255. */
  256. protected function generate_uuid() {
  257. return wp_generate_uuid4();
  258. }
  259. /**
  260. * Retrieves the current user id.
  261. *
  262. * @codeCoverageIgnore
  263. *
  264. * @return int The user id.
  265. */
  266. protected function get_current_user_id() {
  267. return get_current_user_id();
  268. }
  269. /**
  270. * Redirects to the premium page.
  271. *
  272. * @codeCoverageIgnore
  273. *
  274. * @return void
  275. */
  276. protected function redirect_to_premium_page() {
  277. wp_safe_redirect( admin_url( 'admin.php?page=wpseo_licenses' ) );
  278. exit;
  279. }
  280. }