google-contacts.php 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. <?php
  2. /**
  3. * Google (Contacts) service definition for Keyring.
  4. * Contacts API: https://developers.google.com/google-apps/contacts/v3/
  5. * OAuth implementation: https://developers.google.com/accounts/docs/OAuth2WebServer
  6. * App registration: https://code.google.com/apis/console/
  7. */
  8. class Keyring_Service_GoogleContacts extends Keyring_Service_OAuth2 {
  9. const NAME = 'google-contacts';
  10. const LABEL = 'Google Contacts';
  11. const SCOPE = 'https://www.google.com/m8/feeds https://www.googleapis.com/auth/userinfo.profile'; // See https://developers.google.com/google-apps/contacts/v3/#authorizing_requests_with_oauth_20
  12. const API_VERSION = '3.0';
  13. var $self_url = '';
  14. var $self_method = '';
  15. function __construct() {
  16. parent::__construct();
  17. // Enable "basic" UI for entering key/secret
  18. if ( ! KEYRING__HEADLESS_MODE ) {
  19. add_action( 'keyring_google-contacts_manage_ui', array( $this, 'basic_ui' ) );
  20. add_filter( 'keyring_google-contacts_basic_ui_intro', array( $this, 'basic_ui_intro' ) );
  21. }
  22. // Set scope
  23. add_filter( 'keyring_google-contacts_request_token_params', array( $this, 'request_token_params' ) );
  24. // Handle Google's annoying limitation of not allowing us to redirect to a dynamic URL
  25. add_action( 'pre_keyring_google-contacts_verify', array( $this, 'redirect_incoming_verify' ) );
  26. $this->set_endpoint( 'authorize', 'https://accounts.google.com/o/oauth2/auth', 'GET' );
  27. $this->set_endpoint( 'access_token', 'https://accounts.google.com/o/oauth2/token', 'POST' );
  28. $this->set_endpoint( 'self', 'https://www.googleapis.com/oauth2/v1/userinfo', 'GET' );
  29. $creds = $this->get_credentials();
  30. $this->redirect_uri = $creds['redirect_uri'];
  31. $this->key = $creds['key'];
  32. $this->secret = $creds['secret'];
  33. $this->consumer = new OAuthConsumer( $this->key, $this->secret, $this->callback_url );
  34. $this->signature_method = new OAuthSignatureMethod_HMAC_SHA1;
  35. $this->authorization_header = 'Bearer'; // Oh, you
  36. $this->authorization_parameter = false;
  37. // Need to reset the callback because Google is very strict about where it sends people
  38. if ( !empty( $creds['redirect_uri'] ) )
  39. $this->callback_url = $creds['redirect_uri']; // Allow user to manually enter a redirect URI
  40. else
  41. $this->callback_url = remove_query_arg( array( 'nonce', 'kr_nonce' ), $this->callback_url ); // At least strip nonces, since you can't save them in your app config
  42. }
  43. function basic_ui_intro() {
  44. echo '<p>' . sprintf( __( "Google controls access to all of their APIs through their API Console. <a href='%s'>Go to the console</a> and click the project dropdown just under the logo in the upper left of the screen. Click <strong>Create&hellip;</strong> to create a new project. Enter a name and then click <strong>Create project</strong>. You don't technically need access to any of the additional APIs, but if you want to, then feel free to enable them", 'keyring' ), 'https://code.google.com/apis/console' ) . '</p>';
  45. echo '<p>' . __( "Now you need to set up an OAuth Client ID.", 'keyring' ) . '</p>';
  46. echo '<ol>';
  47. echo '<li>' . __( "Click <strong>API Access</strong> in the menu on the left.", 'keyring' ) . '</li>';
  48. echo '<li>' . __( "Click the big blue button labelled <strong>Create an OAuth 2.0 client ID&hellip;</strong>", 'keyring' ) . '</li>';
  49. echo '<li>' . __( "You must enter a <strong>Product name</strong>, but you can skip the logo and home page URL", 'keyring' ) . '</li>';
  50. echo '<li>' . __( "Leave the Application type set to <strong>Web application</strong>", 'keyring' ) . '</li>';
  51. echo '<li>' . __( "Next to <strong>Your site or hostname</strong>, click <strong>(more options)</strong>", 'keyring' ) . '</li>';
  52. echo '<li>' . sprintf( __( "In the <strong>Authorized Redirect URIs</strong> box, enter the URL <code>%s</code>", 'keyring' ), Keyring_Util::admin_url( $this->get_name(), array( 'action' => 'verify' ) ) ) . '</li>';
  53. echo '<li>' . sprintf( __( "For the <strong>Authorized JavaScript Origins</strong>, enter the URL of your domain, e.g. <code>http://%s</code>", 'keyring' ), $_SERVER['HTTP_HOST'] ) . '</li>';
  54. echo '<li>' . __( "Click <strong>Create client ID</strong> when you're done", 'keyring' ) . '</li>';
  55. echo '</ol>';
  56. echo '<p>' . __( "Once you've saved your details, copy the <strong>Client ID</strong> into the <strong>Client ID</strong> field below, and the <strong>Client secret</strong> value into <strong>Client Secret</strong>. The Redirect URI box should fill itself out for you.", 'keyring' ) . '</p>';
  57. }
  58. function _get_credentials() {
  59. if (
  60. defined( 'KEYRING__GOOGLECONTACTS_KEY' )
  61. &&
  62. defined( 'KEYRING__GOOGLECONTACTS_SECRET' )
  63. ) {
  64. return array(
  65. 'redirect_uri' => defined( 'KEYRING__GOOGLECONTACTS_URI' ) ? constant( 'KEYRING__GOOGLECONTACTS_URI' ) : '', // optional
  66. 'key' => constant( 'KEYRING__GOOGLECONTACTS_KEY' ),
  67. 'secret' => constant( 'KEYRING__GOOGLECONTACTS_SECRET' ),
  68. );
  69. } else {
  70. return null;
  71. }
  72. }
  73. function request_token_params( $params ) {
  74. $params['scope'] = self::SCOPE;
  75. return $params;
  76. }
  77. function redirect_incoming_verify( $request ) {
  78. if ( !isset( $request['kr_nonce'] ) ) {
  79. // First request, from Google. Nonce it and move on.
  80. $kr_nonce = wp_create_nonce( 'keyring-verify' );
  81. $nonce = wp_create_nonce( 'keyring-verify-' . $this->get_name() );
  82. wp_safe_redirect(
  83. Keyring_Util::admin_url(
  84. $this->get_name(),
  85. array(
  86. 'action' => 'verify',
  87. 'kr_nonce' => $kr_nonce,
  88. 'nonce' => $nonce,
  89. 'state' => $request['state'],
  90. 'code' => $request['code'], // Auth code from successful response (maybe)
  91. )
  92. )
  93. );
  94. exit;
  95. }
  96. }
  97. function build_token_meta( $token ) {
  98. $meta = array();
  99. if ( !$token )
  100. return $meta;
  101. $token = new Keyring_Access_Token( $this->get_name(), new OAuthToken( $token['access_token'], '' ), array() );
  102. $this->set_token( $token );
  103. $response = $this->request( $this->self_url, array( 'method' => $this->self_method ) );
  104. if ( !Keyring_Util::is_error( $response ) ) {
  105. $meta = array(
  106. 'user_id' => $response->id,
  107. 'name' => $response->name,
  108. 'profile' => $response->link,
  109. 'picture' => $response->picture,
  110. );
  111. }
  112. return apply_filters( 'keyring_access_token_meta', $meta, 'google-contacts', $token, $response, $this );
  113. }
  114. function get_display( Keyring_Access_Token $token ) {
  115. return $token->get_meta( 'name' );
  116. }
  117. function request( $url, array $params = array() ) {
  118. // add header (version), required for all requests
  119. $params['headers']['GData-Version'] = self::API_VERSION;
  120. return parent::request( $url, $params );
  121. }
  122. // Minor modifications from Keyring_Service::basic_ui
  123. function basic_ui() {
  124. if ( !isset( $_REQUEST['nonce'] ) || !wp_verify_nonce( $_REQUEST['nonce'], 'keyring-manage-' . $this->get_name() ) ) {
  125. Keyring::error( __( 'Invalid/missing management nonce.', 'keyring' ) );
  126. exit;
  127. }
  128. // Common Header
  129. echo '<div class="wrap">';
  130. screen_icon( 'ms-admin' );
  131. echo '<h2>' . __( 'Keyring Service Management', 'keyring' ) . '</h2>';
  132. echo '<p><a href="' . Keyring_Util::admin_url( false, array( 'action' => 'services' ) ) . '">' . __( '&larr; Back', 'keyring' ) . '</a></p>';
  133. echo '<h3>' . sprintf( __( '%s API Credentials', 'keyring' ), esc_html( $this->get_label() ) ) . '</h3>';
  134. // Handle actually saving credentials
  135. if ( isset( $_POST['api_key'] ) && isset( $_POST['api_secret'] ) ) {
  136. // Store credentials against this service
  137. $this->update_credentials( array(
  138. 'key' => stripslashes( $_POST['api_key'] ),
  139. 'secret' => stripslashes( $_POST['api_secret'] ),
  140. 'redirect_uri' => stripslashes( $_POST['redirect_uri'] ),
  141. ) );
  142. echo '<div class="updated"><p>' . __( 'Credentials saved.', 'keyring' ) . '</p></div>';
  143. }
  144. $api_key = $api_secret = $redirect_uri = '';
  145. if ( $creds = $this->get_credentials() ) {
  146. $api_key = $creds['key'];
  147. $api_secret = $creds['secret'];
  148. $redirect_uri = $creds['redirect_uri'];
  149. }
  150. echo apply_filters( 'keyring_' . $this->get_name() . '_basic_ui_intro', '' );
  151. if ( ! $redirect_uri )
  152. $redirect_uri = Keyring_Util::admin_url( $this->get_name(), array( 'action' => 'verify' ) );
  153. // Output basic form for collecting key/secret
  154. echo '<form method="post" action="">';
  155. echo '<input type="hidden" name="service" value="' . esc_attr( $this->get_name() ) . '" />';
  156. echo '<input type="hidden" name="action" value="manage" />';
  157. wp_nonce_field( 'keyring-manage', 'kr_nonce', false );
  158. wp_nonce_field( 'keyring-manage-' . $this->get_name(), 'nonce', false );
  159. echo '<table class="form-table">';
  160. echo '<tr><th scope="row">' . __( 'Client ID', 'keyring' ) . '</th>';
  161. echo '<td><input type="text" name="api_key" value="' . esc_attr( $api_key ) . '" id="api_key" class="regular-text"></td></tr>';
  162. echo '<tr><th scope="row">' . __( 'Client Secret', 'keyring' ) . '</th>';
  163. echo '<td><input type="text" name="api_secret" value="' . esc_attr( $api_secret ) . '" id="api_secret" class="regular-text"></td></tr>';
  164. echo '<tr><th scope="row">' . __( 'Redirect URI', 'keyring' ) . '</th>';
  165. echo '<td><input type="text" name="redirect_uri" value="' . esc_attr( $redirect_uri ) . '" id="redirect_uri" class="regular-text"></td></tr>';
  166. echo '</table>';
  167. echo '<p class="submitbox">';
  168. echo '<input type="submit" name="submit" value="' . __( 'Save Changes', 'keyring' ) . '" id="submit" class="button-primary">';
  169. echo '<a href="' . esc_url( $_SERVER['HTTP_REFERER'] ) . '" class="submitdelete" style="margin-left:2em;">' . __( 'Cancel', 'keyring' ) . '</a>';
  170. echo '</p>';
  171. echo '</form>';
  172. echo '</div>';
  173. }
  174. function test_connection() {
  175. $res = $this->request( $this->self_url, array( 'method' => $this->self_method ) );
  176. if ( !Keyring_Util::is_error( $res ) )
  177. return true;
  178. return $res;
  179. }
  180. }
  181. add_action( 'keyring_load_services', array( 'Keyring_Service_GoogleContacts', 'init' ) );