Last active
October 12, 2022 15:46
-
-
Save tamw-wnet/21f0618fe417e99b7702 to your computer and use it in GitHub Desktop.
Foo oAuth plugin, version 2 with persistent logins
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
* Plugin Name: Foo oAuth Demo | |
* Version: 2 | |
* Plugin URI: http://ieg.wnet.org/ | |
* Description: A simple Google oAuth demo plugin | |
* Author: William Tam | |
* Author URI: http://ieg.wnet.org/blog/author/tamw/ | |
* Requires at least: 3.6 | |
* Tested up to: 4.2.2 | |
* | |
* @package WordPress | |
* @author William Tam | |
* @since 1.0.0 | |
* | |
* This program is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation, either version 3 of the License, or | |
* (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |
* | |
*/ | |
if ( ! defined( 'ABSPATH' ) ) exit; | |
class foo_oAuth_Demo { | |
private $dir; | |
private $file; | |
private $token; | |
public function __construct( $file ) { | |
$this->dir = dirname( $file ); | |
$this->file = $file; | |
$this->token = 'foo_oauth_demo'; | |
// Register plugin settings | |
add_action( 'admin_init' , array( $this , 'register_settings' ) ); | |
// Add settings page to menu | |
add_action( 'admin_menu' , array( $this , 'add_menu_item' ) ); | |
// Add settings link to plugins page | |
add_filter( 'plugin_action_links_' . plugin_basename( $this->file ) , array( $this , 'add_settings_link' ) ); | |
// setup meta boxes | |
add_action( 'add_meta_boxes', array( $this, 'meta_box_setup' ), 20 ); | |
add_action( 'save_post', array( $this, 'meta_box_save' ) ); | |
// NEW: setup the wp ajax action for oAuth code exchange | |
add_action( 'wp_ajax_foo_finish_code_exchange', array($this, 'finish_code_exchange') ); | |
// NEW: setup the wp ajax action to logout from oAuth | |
add_action( 'wp_ajax_foo_logout_from_google', array($this, 'logout_from_google') ); | |
} | |
/* The next few functions set up the settings page */ | |
public function add_menu_item() { | |
add_options_page( 'Foo oAuth Demo Settings' , 'Foo oAuth Demo Settings' , 'manage_options' , 'foo_oauth_demo_settings' , array( $this , 'settings_page' ) ); | |
} | |
public function add_settings_link( $links ) { | |
$settings_link = '<a href="options-general.php?page=foo_oauth_demo_settings">Settings</a>'; | |
array_push( $links, $settings_link ); | |
return $links; | |
} | |
public function register_settings() { | |
register_setting( 'foo_oauth_demo_group', 'foo_oauth_demo_settings' ); | |
add_settings_section('settingssection1', 'Google App Settings', array( $this, 'settings_section_callback'), 'foo_oauth_demo_settings'); | |
// you can define EVERYTHING to create, display, and process each settings field as one line per setting below. And all settings defined in this function are stored as a single serialized object. | |
add_settings_field( 'google_app_client_id', 'Google App Client ID', array( $this, 'settings_field'), 'foo_oauth_demo_settings', 'settingssection1', array('setting' => 'foo_oauth_demo_settings', 'field' => 'google_app_client_id', 'label' => '', 'class' => 'regular-text') ); | |
add_settings_field( 'google_app_client_secret', 'Google App Client Secret', array( $this, 'settings_field'), 'foo_oauth_demo_settings', 'settingssection1', array('setting' => 'foo_oauth_demo_settings', 'field' => 'google_app_client_secret', 'label' => '', 'class' => 'regular-text') ); | |
add_settings_field( 'google_app_redirect_uri', 'Google App Redirect URI', array( $this, 'settings_field'), 'foo_oauth_demo_settings', 'settingssection1', array('setting' => 'foo_oauth_demo_settings', 'field' => 'google_app_redirect_uri', 'label' => '', 'class' => 'regular-text') ); | |
} | |
public function settings_section_callback() { echo ' '; } | |
public function settings_field( $args ) { | |
// This is the default processor that will handle standard text input fields. Because it accepts a class, it can be styled or even have jQuery things (like a calendar picker) integrated in it. Pass in a 'default' argument only if you want a non-empty default value. | |
$settingname = esc_attr( $args['setting'] ); | |
$setting = get_option($settingname); | |
$field = esc_attr( $args['field'] ); | |
$label = esc_attr( $args['label'] ); | |
$class = esc_attr( $args['class'] ); | |
$default = ($args['default'] ? esc_attr( $args['default'] ) : '' ); | |
$value = (($setting[$field] && strlen(trim($setting[$field]))) ? $setting[$field] : $default); | |
echo '<input type="text" name="' . $settingname . '[' . $field . ']" id="' . $settingname . '[' . $field . ']" class="' . $class . '" value="' . $value . '" /><p class="description">' . $label . '</p>'; | |
} | |
public function settings_page() { | |
if (!current_user_can('manage_options')) { | |
wp_die( __('You do not have sufficient permissions to access this page.') ); | |
} | |
?> | |
<div class="wrap"> | |
<h2>Foo oAuth Demo Settings</h2> | |
<p>You'll need to go to the <a href="https://console.developers.google.com">Google Developer Console</a> to setup your project and setup the values below.</p> | |
<form action="options.php" method="POST"> | |
<?php settings_fields( 'foo_oauth_demo_group' ); ?> | |
<?php do_settings_sections( 'foo_oauth_demo_settings' ); ?> | |
<?php submit_button(); ?> | |
</form> | |
<!-- We handle the login process on the settings page now --> | |
<?php $this->write_out_oAuth_JavaScript(); ?> | |
</div> | |
<?php | |
} | |
// This function is the clearest way to get the oAuth JavaScript onto a page as needed. | |
private function write_out_oAuth_JavaScript() { | |
$settings = get_option('foo_oauth_demo_settings', true); | |
?> | |
<script language=javascript> | |
// we declare this variable at the top level scope to make it easier to pass around | |
var google_access_token = "<?php echo $this->get_google_access_token(); ?>"; | |
jQuery(document).ready(function($) { | |
var GOOGLECLIENTID = "<?php echo $settings['google_app_client_id']; ?>"; | |
var GOOGLECLIENTREDIRECT = "<?php echo $settings['google_app_redirect_uri']; ?>"; | |
// we don't need the client secret for this, and should not expose it to the web. | |
function requestGoogleoAuthCode() { | |
var OAUTHURL = 'https://accounts.google.com/o/oauth2/auth'; | |
var SCOPE = 'profile email openid https://www.googleapis.com/auth/youtube'; | |
var popupurl = OAUTHURL + '?scope=' + SCOPE + '&client_id=' + GOOGLECLIENTID + '&redirect_uri=' + GOOGLECLIENTREDIRECT + '&response_type=code&access_type=offline&prompt=select_account consent'; | |
var win = window.open(popupurl, "googleauthwindow", 'width=800, height=600'); | |
var pollTimer = window.setInterval(function() { | |
try { | |
if (win.document.URL.indexOf(GOOGLECLIENTREDIRECT) != -1) { | |
window.clearInterval(pollTimer); | |
var response_url = win.document.URL; | |
var auth_code = gup(response_url, 'code'); | |
console.log(response_url); | |
win.close(); | |
// We don't have an access token yet, have to go to the server for it | |
var data = { | |
action: 'foo_finish_code_exchange', | |
auth_code: auth_code | |
}; | |
$.post(ajaxurl, data, function(response) { | |
console.log(response); | |
google_access_token = response; | |
getGoogleUserInfo(google_access_token); | |
}); | |
} | |
} catch(e) {} | |
}, 500); | |
} | |
// helper function to parse out the query string params | |
function gup(url, name) { | |
name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]"); | |
var regexS = "[\\?#&]"+name+"=([^&#]*)"; | |
var regex = new RegExp( regexS ); | |
var results = regex.exec( url ); | |
if( results == null ) | |
return ""; | |
else | |
return results[1]; | |
} | |
function getGoogleUserInfo(google_access_token) { | |
$.ajax({ | |
url: 'https://www.googleapis.com/plus/v1/people/me/openIdConnect', | |
data: { | |
access_token: google_access_token | |
}, | |
success: function(resp) { | |
var user = resp; | |
console.log(user); | |
$('#googleUserName').text('You are logged in as ' + user.name); | |
loggedInToGoogle = true; | |
$('#google-login-block').hide(); | |
$('#google-logout-block').show(); | |
}, | |
dataType: "jsonp" | |
}); | |
} | |
function logoutFromGoogle() { | |
$.ajax({ | |
url: ajaxurl, | |
data: { | |
action: 'foo_logout_from_google' | |
}, | |
success: function(resp) { | |
console.log(resp); | |
$('#googleUserName').text(resp); | |
$('#google-login-block').show(); | |
$('#google-logout-block').hide(); | |
google_access_token = ''; | |
} | |
}); | |
} | |
// We also want to setup the initial click event and page status on document.ready | |
$(function() { | |
$('#google-login-block').click(requestGoogleoAuthCode); | |
$('#google-logout-block').hide(); | |
$('#google-logout-block').click(logoutFromGoogle); | |
// now lets show that they're logged in if they are | |
if (google_access_token) { | |
getGoogleUserInfo(google_access_token); | |
} | |
}); | |
}); | |
</script> | |
<a id="google-login-block">Login to Google </a> | |
<span id="googleUserName">You are not logged in </span> | |
<span id="google-logout-block"><a>Logout from Google</a></span> | |
<iframe id="googleAuthIFrame" style="visibility:hidden;" width=1 height=1></iframe> | |
<?php | |
// END inlined JavaScript and HTML | |
} | |
private function write_out_youtube_js_html() { | |
?> | |
<script language=javascript> | |
var google_access_token = "<?php echo $this->get_google_access_token(); ?>"; | |
jQuery(document).ready(function($) { | |
function getYouTubeVidInfo() { | |
var video_id = $('#youtubevidid').val(); | |
$.ajax({ | |
url: 'https://www.googleapis.com/youtube/v3/videos', | |
method: 'GET', | |
headers: { | |
Authorization: 'Bearer ' + google_access_token | |
}, | |
data: { | |
part: 'snippet', | |
id: video_id | |
} | |
}).done(function(response) { | |
if (response.items[0].snippet){ | |
var thisdata = response.items[0].snippet; | |
$('#youtubevideodata').html('<b>' + thisdata.title + '</b><br />' + thisdata.description); | |
} | |
}); | |
} | |
//write out a click event for to trigger the youtube request | |
$(function() { | |
$('#youtube-get-vidinfo').click(getYouTubeVidInfo); | |
if (! google_access_token ) { | |
$('#youtube-get-vidinfo').hide(); | |
} | |
}); | |
}); | |
</script> | |
<input id="youtubevidid" type=text value="5ywjpbThDpE" /><a id="youtube-get-vidinfo">Get Video Info</a> | |
<div id="youtubevideodata"></div> | |
<?php | |
// END inlined JavaScript and HTML | |
} | |
/* NEW section to handle doing oAuth server-to-server */ | |
// wrapper for wp_ajax to point to reusable function | |
public function finish_code_exchange() { | |
$auth_code = ( isset( $_POST['auth_code'] ) ) ? $_POST['auth_code'] : ''; | |
echo $this->set_google_oauth2_token($auth_code, 'auth_code'); | |
wp_die(); | |
} | |
private function set_google_oauth2_token($grantCode, $grantType) { | |
/* based on code written by Jennifer L Kang that I found here | |
* http://www.jensbits.com/2012/01/09/google-api-offline-access-using-oauth-2-0-refresh-token/ | |
* and modified to integrate with WordPress and to calculate and store the expiration date. | |
*/ | |
$settings = get_option('foo_oauth_demo_settings', true); | |
$success = true; | |
$oauth2token_url = "https://accounts.google.com/o/oauth2/token"; | |
$clienttoken_post = array( | |
"client_id" => $settings['google_app_client_id'], | |
"client_secret" => $settings['google_app_client_secret'] | |
); | |
if ($grantType === "auth_code"){ | |
$clienttoken_post["code"] = $grantCode; | |
$clienttoken_post["redirect_uri"] = $settings['google_app_redirect_uri']; | |
$clienttoken_post["grant_type"] = "authorization_code"; | |
} | |
if ($grantType === "refresh_token"){ | |
$clienttoken_post["refresh_token"] = get_option('foo_google_refresh_token', true); | |
$clienttoken_post["grant_type"] = "refresh_token"; | |
} | |
$postargs = array( | |
'body' => $clienttoken_post | |
); | |
$response = wp_remote_post($oauth2token_url, $postargs ); | |
$authObj = json_decode(wp_remote_retrieve_body( $response ), true); | |
if (isset($authObj['refresh_token'])){ | |
$refreshToken = $authObj['refresh_token']; | |
$success = update_option('foo_google_refresh_token', $refreshToken, false); | |
// the final 'false' is so we don't autoload this value into memory on every page load | |
} | |
if ($success) { | |
$success = update_option('foo_google_access_token_expires', strtotime("+" . $authObj['expires_in'] . " seconds")); | |
} | |
if ($success) { | |
$success = update_option('foo_google_access_token', $authObj[access_token], false); | |
if ($success) { | |
$success = $authObj[access_token]; | |
} | |
} | |
// if there were any errors $success will be false, otherwise it'll be the access token | |
if (!$success) { $success=false; } | |
return $success; | |
} | |
public function get_google_access_token() { | |
$expiration_time = get_option('foo_google_access_token_expires', true); | |
if (! $expiration_time) { | |
return false; | |
} | |
// Give the access token a 5 minute buffer (300 seconds) | |
$expiration_time = $expiration_time - 300; | |
if (time() < $expiration_time) { | |
return get_option('foo_google_access_token', true); | |
} | |
// at this point we have an expiration time but it is in the past or will be very soon | |
return $this->set_google_oauth2_token(null, 'refresh_token'); | |
} | |
public function revoke_google_tokens() { | |
/* This function finds either the access token or refresh token | |
* revokes them with google (revoking the access token does the refresh too) | |
* then deletes the data from the options table | |
*/ | |
$return = ''; | |
$token = get_option('foo_google_access_token', true); | |
$expiration_time = get_option('foo_google_access_token_expires', true); | |
if (!$token || (time() > $expiration_time)){ | |
$token = get_option('foo_google_refresh_token', true); | |
} | |
if ($token) { | |
$return = wp_remote_retrieve_response_code(wp_remote_get("https://accounts.google.com/o/oauth2/revoke?token=" . $token)); | |
} else { | |
$return = "no tokens found"; | |
} | |
if ($return == 200) { | |
delete_option('foo_google_access_token'); | |
delete_option('foo_google_refresh_token'); | |
delete_option('foo_google_access_token_expires'); | |
return true; | |
} else { | |
return $return; | |
} | |
} | |
// wrapper for wp_ajax to point to reusable function | |
public function logout_from_google() { | |
$response = $this->revoke_google_tokens(); | |
if ($response === true) { | |
$response = "success"; | |
} | |
echo $response; | |
wp_die(); | |
} | |
public function submit_youtube_expire_request($videoid){ | |
$access_token = $this->get_google_access_token(); | |
if (! $access_token) { | |
error_log("no access token for $videoid"); | |
return false; | |
} | |
$bodyargs = array( | |
"id" => $videoid, | |
"kind" => "youtube#video", | |
"status" => array( | |
"privacyStatus" => "private" | |
) | |
); | |
$body = json_encode($bodyargs); | |
$url = "https://www.googleapis.com/youtube/v3/videos?part=status&fields=status"; | |
$args = array( | |
"method" => "PUT", | |
"headers" => array( | |
"Authorization" => "Bearer " . $access_token, | |
"Content-Type" => "application/json" | |
), | |
"body" => $body | |
); | |
$request = wp_remote_request($url, $args); | |
if (wp_remote_retrieve_response_code($request) != 200){ | |
error_log("privacy set failed : " . wp_remote_retrieve_body($request)); | |
return false; | |
} | |
return json_decode(wp_remote_retrieve_body($request)); | |
} | |
/* The rest of these functions build and process metaboxes for posts */ | |
public function meta_box_setup( $post_type ) { | |
add_meta_box( 'foo-oAuth-demo-display', __( 'foo oAuth Demo' , 'foo_oauth_demo' ), array( $this, 'meta_box_content' ), $post_type, 'normal', 'high' ); | |
} | |
public function meta_box_content() { | |
global $post_id; | |
add_thickbox(); | |
$fields = get_post_custom( $post_id ); | |
$field_definitions = $this->get_field_definitions(); | |
// Always include a nonce | |
$html .= '<input type="hidden" name="' . $this->token . '_nonce" id="' . $this->token . '_nonce" value="' . wp_create_nonce( plugin_basename( $this->dir ) ) . '" />'; | |
if ( 0 < count( $field_definitions ) ) { | |
$html .= '<table class="form-table">' . "\n"; | |
$html .= '<tbody>' . "\n"; | |
foreach ( $field_definitions as $field => $option ) { | |
$value = $option['default']; | |
if ( isset( $fields[$field] ) && isset( $fields[$field][0] ) ) { | |
$value = $fields[$field][0]; | |
} | |
$html .= $this->format_input_field_as_tablerow($option, $field, $value); | |
} | |
$html .= '</tbody>' . "\n"; | |
$html .= '</table>' . "\n"; | |
} | |
echo $html; | |
// we no longer do the oAuth login here, but we still can get youtube info | |
$this->write_out_youtube_js_html(); | |
} | |
public function meta_box_save() { | |
global $post; | |
$post_id=$post->ID; | |
// Verify nonce | |
if ( ! wp_verify_nonce( $_POST[ $this->token . '_nonce'], plugin_basename( $this->dir ) ) ) { | |
return $post_id; | |
} | |
// Verify user permissions | |
if ( ! current_user_can( 'edit_post', $post_id ) ) { | |
return $post_id; | |
} | |
// Handle custom fields | |
$field_definitions = $this->get_field_definitions(); | |
$fieldlist = array_keys( $field_definitions ); | |
$vidid = null; | |
$vidstatus = null; | |
foreach ( $fieldlist as $field ) { | |
if( isset( $_POST[$field] ) ) { | |
// only operate on fields that were submitted | |
$value = $_POST[$field]; | |
// Escape the URLs. | |
if ( 'url' == $field_definitions[$field]['type'] ) { | |
$value = esc_url( $value ); | |
} | |
update_post_meta( $post_id , $field , $value ); | |
if ($field == 'foo_youtube_videoid') { | |
$vidid = $value; | |
} | |
if ($field == 'foo_youtube_status' && $value == 'setprivate') { | |
$vidstatus = $value; | |
} | |
} | |
} | |
if ($vidid && $vidstatus) { | |
$newstatus = $this->submit_youtube_expire_request($vidid); | |
update_post_meta( $post_id, 'foo_youtube_status', $newstatus->status->privacyStatus ); | |
} | |
} | |
public function get_field_definitions() { | |
$fields = array(); | |
$fields['foo_youtube_videoid'] = array( | |
'name' => __( 'YouTube Video ID:' , 'foo_oauth_demo' ), | |
'type' => 'text', | |
'default' => '', | |
'description' => 'The ID for a YouTube Vid', | |
'section' => 'main' | |
); | |
$fields['foo_youtube_status'] = array( | |
'name' => __( 'YouTube Video status:' , 'foo_oauth_demo' ), | |
'type' => 'text', | |
'default' => '', | |
'description' => 'The status for that video', | |
'section' => 'main' | |
); | |
return $fields; | |
} | |
private function format_input_field_as_tablerow($option, $field, $value) { | |
$html = ''; | |
if ($option['maxlength']) { $maxinput = ' data-limit-input="' . $option['maxlength'] . '" '; } | |
$html .= '<tr valign="top" class="' . $option['section'] . '"><th scope="row"><label for="' . esc_attr( $field ) . '">' . $option['name'] . '</label></th><td><input name="' . esc_attr( $field ) . '" type="text" id="' . esc_attr( $field ) . '" class="regular-text" value="' . esc_attr( $value ) . '"' . $maxinput . ' />' . "\n"; | |
$html .= '<span></span><p class="description">' . $option['description'] . '</p>' . "\n"; | |
$html .= '</td></tr>' . "\n"; | |
return $html; | |
} | |
//end of class | |
} | |
// Instantiate our class | |
global $plugin_obj; | |
$plugin_obj = new foo_oAuth_Demo( __FILE__ ); | |
// always cleanup after yourself | |
register_deactivation_hook(__FILE__, 'foo_deactivation'); | |
function foo_deactivation() { | |
// delete the google tokens | |
$plugin_obj = new foo_oAuth_Demo( __FILE__ ); | |
$plugin_obj->revoke_google_tokens(); | |
error_log('Foo has been deactivated'); | |
} | |
/* END OF FILE */ | |
?> |
These are only warnings, and can be ignored. This code is really only for demo purposes as a start for your own work.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
i am getting warning at editing page, colud you help me?:
`Warning: Undefined variable $html in /home5/linkproje/public_html/linkprojev2/wp-content/plugins/foo/foo.php on line 412
Warning: Undefined array key "maxlength" in /home5/linkproje/public_html/linkprojev2/wp-content/plugins/foo/foo.php on line 493
Warning: Undefined variable $maxinput in /home5/linkproje/public_html/linkprojev2/wp-content/plugins/foo/foo.php on line 494
Warning: Undefined array key "maxlength" in /home5/linkproje/public_html/linkprojev2/wp-content/plugins/foo/foo.php on line 493
Warning: Undefined variable $maxinput in /home5/linkproje/public_html/linkprojev2/wp-content/plugins/foo/foo.php on line 494`