PageRenderTime 48ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/elementor/core/common/modules/connect/apps/base-app.php

https://bitbucket.org/hcdesenvolvimentos/tiagobalabuch_site
PHP | 464 lines | 251 code | 83 blank | 130 comment | 32 complexity | da31e21eeb744acf3539a676c918d37f MD5 | raw file
Possible License(s): GPL-2.0, Apache-2.0
  1. <?php
  2. namespace Elementor\Core\Common\Modules\Connect\Apps;
  3. use Elementor\Core\Common\Modules\Connect\Admin;
  4. if ( ! defined( 'ABSPATH' ) ) {
  5. exit; // Exit if accessed directly
  6. }
  7. abstract class Base_App {
  8. const OPTION_NAME_PREFIX = 'elementor_connect_';
  9. const SITE_URL = 'https://my.elementor.com/connect/v1';
  10. const API_URL = 'https://my.elementor.com/api/connect/v1';
  11. protected $data = [];
  12. /**
  13. * @since 2.3.0
  14. * @access public
  15. * @abstract
  16. */
  17. abstract public function render_admin_widget();
  18. /**
  19. * @since 2.3.0
  20. * @access protected
  21. * @abstract
  22. */
  23. abstract protected function get_slug();
  24. /**
  25. * @since 2.3.0
  26. * @access protected
  27. * @abstract
  28. */
  29. abstract protected function update_settings();
  30. /**
  31. * @since 2.3.0
  32. * @access public
  33. * @static
  34. */
  35. public static function get_class_name() {
  36. return get_called_class();
  37. }
  38. /**
  39. * @since 2.3.0
  40. * @access protected
  41. */
  42. protected function get_option_name() {
  43. return static::OPTION_NAME_PREFIX . $this->get_slug();
  44. }
  45. /**
  46. * @since 2.3.0
  47. * @access public
  48. */
  49. public function admin_notice() {
  50. $notices = $this->get( 'notices' );
  51. if ( ! $notices ) {
  52. return;
  53. }
  54. echo '<div id="message" class="updated notice is-dismissible"><p>';
  55. foreach ( $notices as $notice ) {
  56. echo wp_kses_post( sprintf( '<div class="%s"><p>%s</p></div>', $notice['type'], wpautop( $notice['content'] ) ) );
  57. }
  58. echo '</p><button type="button" class="notice-dismiss"><span class="screen-reader-text">' .
  59. __( 'Dismiss', 'elementor' ) .
  60. '</span></button></div>';
  61. $this->delete( 'notices' );
  62. }
  63. /**
  64. * @since 2.3.0
  65. * @access public
  66. */
  67. public function action_authorize() {
  68. if ( $this->is_connected() ) {
  69. $this->redirect_to_admin_page();
  70. return;
  71. }
  72. $this->set_client_id();
  73. $this->set_request_state();
  74. wp_redirect( $this->get_remote_authorize_url() );
  75. die;
  76. }
  77. /**
  78. * @since 2.3.0
  79. * @access public
  80. */
  81. public function action_get_token() {
  82. if ( $this->is_connected() ) {
  83. $this->redirect_to_admin_page();
  84. }
  85. if ( $_REQUEST['state'] !== $this->get( 'state' ) ) {
  86. $this->add_notice( 'Get Token: Invalid Request.', 'error' );
  87. $this->redirect_to_admin_page();
  88. }
  89. $response = $this->request( 'get_token', [
  90. 'grant_type' => 'authorization_code',
  91. 'code' => $_REQUEST['code'],
  92. 'redirect_uri' => rawurlencode( $this->get_admin_url( 'get_token' ) ),
  93. 'client_id' => $this->get( 'client_id' ),
  94. ] );
  95. if ( is_wp_error( $response ) ) {
  96. $notice = 'Cannot Get Token:' . $response->get_error_message();
  97. $this->add_notice( $notice, 'error' );
  98. $this->redirect_to_admin_page();
  99. }
  100. $this->delete( 'state' );
  101. $this->set( (array) $response );
  102. $this->after_connect();
  103. // Add the notice *after* the method `after_connect`, so an app can redirect without the notice.
  104. $this->add_notice( __( 'Connected Successfully.', 'elementor' ) );
  105. $this->redirect_to_admin_page();
  106. }
  107. /**
  108. * @since 2.3.0
  109. * @access public
  110. */
  111. public function action_disconnect() {
  112. if ( $this->is_connected() ) {
  113. $this->disconnect();
  114. $this->add_notice( __( 'Disconnected Successfully.', 'elementor' ) );
  115. }
  116. $this->redirect_to_admin_page();
  117. }
  118. /**
  119. * @since 2.3.0
  120. * @access public
  121. */
  122. public function get_admin_url( $action, $params = [] ) {
  123. $params = [
  124. 'app' => $this->get_slug(),
  125. 'action' => $action,
  126. 'nonce' => wp_create_nonce( $this->get_slug() . $action ),
  127. ] + $params;
  128. return add_query_arg( $params, Admin::$url );
  129. }
  130. /**
  131. * @since 2.3.0
  132. * @access public
  133. */
  134. public function is_connected() {
  135. return (bool) $this->get( 'access_token' );
  136. }
  137. /**
  138. * @since 2.3.0
  139. * @access protected
  140. */
  141. protected function init() {}
  142. /**
  143. * @since 2.3.0
  144. * @access protected
  145. */
  146. protected function init_data() {}
  147. /**
  148. * @since 2.3.0
  149. * @access protected
  150. */
  151. protected function after_connect() {}
  152. /**
  153. * @since 2.3.0
  154. * @access public
  155. */
  156. public function get( $key, $default = null ) {
  157. $this->init_data();
  158. return isset( $this->data[ $key ] ) ? $this->data[ $key ] : $default;
  159. }
  160. /**
  161. * @since 2.3.0
  162. * @access protected
  163. */
  164. protected function set( $key, $value = null ) {
  165. $this->init_data();
  166. if ( is_array( $key ) ) {
  167. $this->data = array_replace_recursive( $this->data, $key );
  168. } else {
  169. $this->data[ $key ] = $value;
  170. }
  171. $this->update_settings();
  172. }
  173. /**
  174. * @since 2.3.0
  175. * @access protected
  176. */
  177. protected function delete( $key = null ) {
  178. $this->init_data();
  179. if ( $key ) {
  180. unset( $this->data[ $key ] );
  181. } else {
  182. $this->data = [];
  183. }
  184. $this->update_settings();
  185. }
  186. /**
  187. * @since 2.3.0
  188. * @access protected
  189. */
  190. protected function add( $key, $value, $default = '' ) {
  191. $new_value = $this->get( $key, $default );
  192. if ( is_array( $new_value ) ) {
  193. $new_value[] = $value;
  194. } elseif ( is_string( $new_value ) ) {
  195. $new_value .= $value;
  196. } elseif ( is_numeric( $new_value ) ) {
  197. $new_value += $value;
  198. }
  199. $this->set( $key, $new_value );
  200. }
  201. /**
  202. * @since 2.3.0
  203. * @access protected
  204. */
  205. protected function add_notice( $content, $type = 'success' ) {
  206. $this->add( 'notices', compact( 'content', 'type' ), [] );
  207. }
  208. /**
  209. * @since 2.3.0
  210. * @access protected
  211. */
  212. protected function request( $action, $request_body = [] ) {
  213. $request_body = [
  214. 'app' => $this->get_slug(),
  215. 'access_token' => $this->get( 'access_token' ),
  216. 'client_id' => $this->get( 'client_id' ),
  217. 'local_id' => get_current_user_id(),
  218. 'site_key' => $this->get_site_key(),
  219. 'home_url' => trailingslashit( home_url() ),
  220. ] + $request_body;
  221. $headers = [];
  222. if ( $this->is_connected() ) {
  223. $headers['X-Elementor-Signature'] = hash_hmac( 'sha256', wp_json_encode( $request_body, JSON_NUMERIC_CHECK ), $this->get( 'access_token_secret' ) );
  224. }
  225. $response = wp_remote_post( $this->get_api_url() . '/' . $action, [
  226. 'body' => $request_body,
  227. 'headers' => $headers,
  228. 'timeout' => 25,
  229. ] );
  230. if ( is_wp_error( $response ) ) {
  231. wp_die( $response, [
  232. 'back_link' => true,
  233. ] );
  234. }
  235. $body = wp_remote_retrieve_body( $response );
  236. $response_code = (int) wp_remote_retrieve_response_code( $response );
  237. if ( ! $response_code ) {
  238. return new \WP_Error( 500, 'No Response' );
  239. }
  240. // Server sent a success message without content.
  241. if ( 'null' === $body ) {
  242. $body = true;
  243. }
  244. $body = json_decode( $body );
  245. if ( false === $body ) {
  246. return new \WP_Error( 422, 'Wrong Server Response' );
  247. }
  248. if ( 200 !== $response_code ) {
  249. $message = $body->message ? $body->message : wp_remote_retrieve_response_message( $response );
  250. $code = $body->code ? $body->code : $response_code;
  251. if ( 401 === $code ) {
  252. $this->delete();
  253. $this->action_authorize();
  254. }
  255. return new \WP_Error( $code, $message );
  256. }
  257. return $body;
  258. }
  259. /**
  260. * @since 2.3.0
  261. * @access protected
  262. */
  263. protected function get_api_url() {
  264. return static::API_URL . '/' . $this->get_slug();
  265. }
  266. /**
  267. * @since 2.3.0
  268. * @access protected
  269. */
  270. protected function get_remote_site_url() {
  271. return static::SITE_URL . '/' . $this->get_slug();
  272. }
  273. /**
  274. * @since 2.3.0
  275. * @access protected
  276. */
  277. protected function get_remote_authorize_url() {
  278. $redirect_uri = $this->get_admin_url( 'get_token' );
  279. if ( ! empty( $_REQUEST['mode'] ) && 'popup' === $_REQUEST['mode'] ) {
  280. $redirect_uri = add_query_arg( 'mode', 'popup', $redirect_uri );
  281. }
  282. $url = add_query_arg( [
  283. 'action' => 'authorize',
  284. 'response_type' => 'code',
  285. 'client_id' => $this->get( 'client_id' ),
  286. 'auth_secret' => $this->get( 'auth_secret' ),
  287. 'state' => $this->get( 'state' ),
  288. 'redirect_uri' => rawurlencode( $redirect_uri ),
  289. ], $this->get_remote_site_url() );
  290. return $url;
  291. }
  292. /**
  293. * @since 2.3.0
  294. * @access protected
  295. */
  296. protected function redirect_to_admin_page( $url = '' ) {
  297. if ( ! $url ) {
  298. $url = Admin::$url;
  299. }
  300. if ( ! empty( $_REQUEST['mode'] ) && 'popup' === $_REQUEST['mode'] ) {
  301. $this->print_popup_close_script( $url );
  302. } else {
  303. wp_safe_redirect( $url );
  304. die;
  305. }
  306. }
  307. /**
  308. * @since 2.3.0
  309. * @access protected
  310. */
  311. protected function set_client_id() {
  312. if ( $this->get( 'client_id' ) ) {
  313. return;
  314. }
  315. $response = $this->request( 'get_client_id' );
  316. if ( is_wp_error( $response ) ) {
  317. wp_die( $response, $response->get_error_message() );
  318. }
  319. $this->set( 'client_id', $response->client_id );
  320. $this->set( 'auth_secret', $response->auth_secret );
  321. }
  322. /**
  323. * @since 2.3.0
  324. * @access protected
  325. */
  326. protected function set_request_state() {
  327. $this->set( 'state', wp_generate_password( 12, false ) );
  328. }
  329. /**
  330. * @since 2.3.0
  331. * @access protected
  332. */
  333. protected function print_popup_close_script( $url ) {
  334. ?>
  335. <script>
  336. if ( opener && opener !== window ) {
  337. opener.jQuery( 'body' ).trigger( 'elementorConnected' );
  338. window.close();
  339. opener.focus();
  340. } else {
  341. location = '<?php echo $url; ?>';
  342. }
  343. </script>
  344. <?php
  345. die;
  346. }
  347. /**
  348. * @since 2.3.0
  349. * @access protected
  350. */
  351. protected function disconnect() {
  352. if ( $this->is_connected() ) {
  353. // Try update the server, but not needed to handle errors.
  354. $this->request( 'disconnect' );
  355. }
  356. $this->delete();
  357. }
  358. /**
  359. * @since 2.3.0
  360. * @access protected
  361. */
  362. protected function get_site_key() {
  363. $site_key = get_option( 'elementor_connect_site_key' );
  364. if ( ! $site_key ) {
  365. $site_key = md5( uniqid( wp_generate_password() ) );
  366. update_option( 'elementor_connect_site_key', $site_key );
  367. }
  368. return $site_key;
  369. }
  370. /**
  371. * @since 2.3.0
  372. * @access public
  373. */
  374. public function __construct() {
  375. add_action( 'admin_notices', [ $this, 'admin_notice' ] );
  376. /**
  377. * Allow extended apps to customize the __construct without call parent::__construct.
  378. */
  379. $this->init();
  380. }
  381. }