handle_url_actions(); } /** * Add options page */ public function add_plugin_page() { add_submenu_page('options-general.php', 'Google Cloud Print Setings', 'Google Cloud Print Settings', 'manage_options', 'xc_woo_cloud_settings', array( &$this, 'create_admin_page' )); } /** * Options page callback */ public function create_admin_page() { if (!current_user_can('manage_options')) { wp_die(__('You do not have sufficient permissions to access this page.')); } //!current_user_can('manage_options') // Set class property $this->options = get_option('xc_woo_cloud_options'); wp_enqueue_script('jquery-ui-spinner', false, array( 'jquery' )); $pver = XC_WOO_CLOUD_VERSION; $title = htmlspecialchars("XC Woo Google Cloud Printer Options"); echo '
'; echo "

$title (version $pver)

"; echo '

Authored by Xperts Club

'; echo "
\n"; echo '
'; echo '

' . __('Instructions', XC_WOO_CLOUD) . ':

'; ?> ' . htmlspecialchars(sprintf(__("%s does not allow authorisation of sites hosted on direct IP addresses. You will need to change your site's address (%s) before you can use %s for storage.", XC_WOO_CLOUD), __('Google Cloud Print', XC_WOO_CLOUD), $matches[1], __('Google Cloud Print', XC_WOO_CLOUD))) . '

'; } //preg_match('#^(https?://(\d+)\.(\d+)\.(\d+)\.(\d+))/#', $admin_page_url, $matches) else { ?>

:

' . __('Choose one, and then save the settings for the second time.', XC_WOO_CLOUD) . '

'; ?>

'; ?>
show_admin_warning(htmlspecialchars($_GET['error']), 'error'); } //!empty($_GET['error']) echo __('Google Cloud Print links:', XC_WOO_CLOUD) . ' '; echo '' . __('Learn about Google Cloud Print', XC_WOO_CLOUD) . ''; echo ' | '; echo '' . __('Your printers', XC_WOO_CLOUD) . ''; echo ' | '; echo '' . __('Your print jobs', XC_WOO_CLOUD) . ''; if (current_user_can('manage_options')) { $opts = get_option('xc_woo_cloud_print_options'); if (empty($opts['clientid']) && !empty($opts['username'])) { $this->show_admin_warning_changedgoogleauth(true); } //empty($opts['clientid']) && !empty($opts['username']) $clientid = empty($opts['clientid']) ? '' : $opts['clientid']; $token = empty($opts['token']) ? '' : $opts['token']; if (!empty($clientid) && empty($token)) { $this->show_admin_warning_googleauth(true); } //!empty($clientid) && empty($token) elseif (!empty($clientid) && !empty($token)) { echo '

' . sprintf(__('You appear to be authenticated with Google Cloud Print, but if you are seeing authorisation errors, then you can click here to authenticate your %s account again.', XC_WOO_CLOUD), 'Google Cloud Print', 'Google Cloud Print') . '

'; } //!empty($clientid) && !empty($token) } //current_user_can('manage_options') } public function options_validate($google) { $opts = get_option('xc_woo_cloud_print_options', array()); // Remove legacy options unset($opts['username']); unset($opts['password']); if (!is_array($google)) return $opts; $old_client_id = (empty($opts['clientid'])) ? '' : $opts['clientid']; if (!empty($opts['token']) && $old_client_id != $google['clientid']) { $this->googleauth_auth_revoke($opts['token'], false); $google['token'] = ''; delete_transient('xc_woo_cloud_print_printers'); } //!empty($opts['token']) && $old_client_id != $google['clientid'] foreach ($google as $key => $value) { // Trim spaces - I got support requests from users who didn't spot the spaces they introduced when copy/pasting $opts[$key] = ('clientid' == $key || 'clientsecret' == $key) ? trim($value) : $value; } //$google as $key => $value return $opts; } public function xc_woo_cloud_print_options_clientid() { $options = get_option('xc_woo_cloud_print_options', array()); $clientid = (empty($options['clientid'])) ? '' : $options['clientid']; echo ''; echo '
' . __('See the instructions above to learn how to get this', XC_WOO_CLOUD) . ''; } public function xc_woo_cloud_print_options_clientsecret() { $options = get_option('xc_woo_cloud_print_options', array()); $clientsecret = (empty($options['clientsecret'])) ? '' : $options['clientsecret']; echo '
'; } // This function is both an options field printer, and called via AJAX public function xc_woo_cloud_print_options_printer() { if (defined('DOING_AJAX') && DOING_AJAX == true && (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'xc-woo-cloud-nonce'))) die; $options = get_option('xc_woo_cloud_print_options'); $printers = $this->get_printers(); $this->printers_found = is_wp_error($printers) ? 0 : count($printers); $this->debug_output = ''; if (is_wp_error($printers)) { echo '' . strip_tags($printers->get_error_message()) . ''; } //is_wp_error($printers) elseif (count($printers) == 0) { echo '(' . __('Account either not connected, or no printers available)', XC_WOO_CLOUD) . ''; } //count($printers) == 0 else { echo '
'; // Make sure the option gets saved //echo ''; foreach ($printers as $printer) { echo '
'; echo 'id, $options['printer'])) || (!is_array($options['printer']) && $options['printer'] == $printer->id))) ? 'checked="checked"' : '') . 'value="' . htmlspecialchars($printer->id) . '">
'; echo '
'; } //$printers as $printer // echo ''; echo '
'; if (defined('DOING_AJAX') && DOING_AJAX == true) die; echo '
(' . __('refresh', XC_WOO_CLOUD) . ')
'; } } public function show_admin_warning_googleauth($suppress_title = false) { $warning = ($suppress_title) ? '' : '' . __('Google Cloud Print notice:', XC_WOO_CLOUD) . ' '; $warning .= '' . sprintf(__('Click here to authenticate your %s account (you will not be able to print via %s without it).', XC_WOO_CLOUD), 'Google Cloud Print', 'Google Cloud Print') . ''; $this->show_admin_warning($warning); } public function show_admin_warning($message, $class = "updated") { echo '
' . "

$message

"; } public function handle_url_actions() { // First, basic security check: must be an admin page, with ability to manage options, with the right parameters // Also, only on GET because WordPress on the options page repeats parameters sometimes when POST-ing via the _wp_referer field if (isset($_SERVER['REQUEST_METHOD']) && 'GET' == $_SERVER['REQUEST_METHOD'] && isset($_GET['action']) && 'xc-woo-google-cloud-print-auth' == $_GET['action'] && current_user_can('manage_options')) { $_GET['page'] = $this->option_page; $_REQUEST['page'] = $this->option_page; if (isset($_GET['state'])) { if ('success' == $_GET['state']) add_action('all_admin_notices', array( $this, 'show_authed_admin_success' )); elseif ('token' == $_GET['state']) $this->googleauth_auth_token(); elseif ('revoke' == $_GET['state']) $this->googleauth_auth_revoke(); } //isset($_GET['state']) elseif (isset($_GET['gcpl_googleauth'])) { $this->googleauth_auth_request(); } //isset($_GET['gcpl_googleauth']) } //isset($_SERVER['REQUEST_METHOD']) && 'GET' == $_SERVER['REQUEST_METHOD'] && isset($_GET['action']) && 'xc-woo-google-cloud-print-auth' == $_GET['action'] && current_user_can('manage_options') } // Acquire single-use authorization code from Google OAuth 2.0 public function googleauth_auth_request() { $opts = get_option('xc_woo_cloud_print_options', array()); // First, revoke any existing token, since Google doesn't appear to like issuing new ones if (!empty($opts['token'])) $this->googleauth_auth_revoke(); // We use 'force' here for the approval_prompt, not 'auto', as that deals better with messy situations where the user authenticated, then changed settings $params = array( 'response_type' => 'code', 'client_id' => $opts['clientid'], 'redirect_uri' => $this->redirect_uri(), 'scope' => 'https://www.googleapis.com/auth/cloudprint', 'state' => 'token', 'access_type' => 'offline', 'approval_prompt' => 'force' ); if (headers_sent()) { add_action('all_admin_notices', array( $this, 'admin_notice_something_breaking' )); } //headers_sent() else { header('Location: https://accounts.google.com/o/oauth2/auth?' . http_build_query($params, null, '&')); } } private function redirect_uri() { return admin_url('options-general.php') . '?action=xc-woo-google-cloud-print-auth'; } // Revoke a Google account refresh token public function googleauth_auth_revoke($token = false, $unsetopt = true) { if (empty($token)) { $opts = get_option('xc_woo_cloud_print_options', array()); $token = empty($opts['token']) ? '' : $opts['token']; } //empty($token) if ($token) wp_remote_get('https://accounts.google.com/o/oauth2/revoke?token=' . $token); if ($unsetopt) { $opts = get_option('xc_woo_cloud_print_options', array()); $opts['token'] = ''; update_option('xc_woo_cloud_print_options', $opts); } //$unsetopt } // Get a Google account refresh token using the code received from googleauth_auth_request public function googleauth_auth_token() { $opts = get_option('xc_woo_cloud_print_options', array()); if (isset($_GET['code'])) { $post_vars = array( 'code' => $_GET['code'], 'client_id' => $opts['clientid'], 'client_secret' => $opts['clientsecret'], 'redirect_uri' => $this->redirect_uri(), 'grant_type' => 'authorization_code' ); $googleauth_request_options = apply_filters('google_cloud_print_googleauth_request_options', array( 'timeout' => 25, 'method' => 'POST', 'body' => $post_vars )); $result = wp_remote_post('https://accounts.google.com/o/oauth2/token', $googleauth_request_options); if (is_wp_error($result)) { $add_to_url = "Bad response when contacting Google: "; foreach ($result->get_error_messages() as $message) { error_log("Google Drive authentication error: " . $message); $add_to_url .= $message . ". "; } //$result->get_error_messages() as $message header('Location: ' . admin_url('options-general.php') . '?page=' . $this->option_page . '&error=' . urlencode($add_to_url)); } //is_wp_error($result) else { $json_values = json_decode($result['body'], true); if (isset($json_values['refresh_token'])) { // Save token $opts['token'] = $json_values['refresh_token']; update_option('xc_woo_cloud_print_options', $opts); if (isset($json_values['access_token'])) { $opts['tmp_access_token'] = $json_values['access_token']; update_option('xc_woo_cloud_print_options', $opts); // We do this to clear the GET parameters, otherwise WordPress sticks them in the _wp_referer in the form and brings them back, leading to confusion + errors header('Location: ' . admin_url('options-general.php') . '?action=xc-woo-google-cloud-print-auth&page=' . $this->option_page . '&state=success'); } //isset($json_values['access_token']) } //isset($json_values['refresh_token']) else { $msg = __('No refresh token was received from Google. This often means that you entered your client secret wrongly, or that you have not yet re-authenticated (below) since correcting it. Re-check it, then follow the link to authenticate again. Finally, if that does not work, then use expert mode to wipe all your settings, create a new Google client ID/secret, and start again.', XC_WOO_CLOUD); if (isset($json_values['error'])) $msg .= ' ' . sprintf(__('Error: %s', XC_WOO_CLOUD), $json_values['error']); header('Location: ' . admin_url('options-general.php') . '?page=' . $this->option_page . '&error=' . urlencode($msg)); } } } //isset($_GET['code']) else { $err_msg = __('Authorization failed', XC_WOO_CLOUD); if (!empty($_GET['error'])) $err_msg .= ': ' . $_GET['error']; header('Location: ' . admin_url('options-general.php') . '?page=' . $this->option_page . '&error=' . urlencode($err_msg)); } } public function show_authed_admin_success() { // global $updraftplus_admin; $opts = get_option('xc_woo_cloud_print_options', array()); if (empty($opts['tmp_access_token'])) return; $tmp_access_token = $opts['tmp_access_token']; $message = ''; $this->show_admin_warning(__('Success', XC_WOO_CLOUD) . ': ' . sprintf(__('you have authenticated your %s account.', XC_WOO_CLOUD), __('Google Cloud Print', XC_WOO_CLOUD)) . ' '); unset($opts['tmp_access_token']); update_option('xc_woo_cloud_print_options', $opts); } public function get_printers() { if (!defined('DOING_AJAX') || DOING_AJAX != true) { $printers = get_transient('xc_woo_cloud_print_printers'); if (is_array($printers)) return $printers; } //!defined('DOING_AJAX') || DOING_AJAX != true // Wanted key: access token $options = apply_filters('xc_woo_cloud_print_options', array()); // This should only be set if authenticated if (isset($options['token'])) { $post = array(); $printers = $this->process_request('https://www.google.com/cloudprint/interface/search', $post, $options); if (is_wp_error($printers)) return $printers; if (is_string($printers)) $printers = json_decode($printers); if (is_object($printers) && isset($printers->success) && $printers->success == true && isset($printers->printers) && is_array($printers->printers)) { foreach ($printers->printers as $index => $printer) { $get_printer_result = $this->process_request('https://www.google.com/cloudprint/printer', array( 'use_cdd' => 'true', 'printerid' => $printer->id ), $options); if (false !== $get_printer_result && null !== ($printer_result = json_decode($get_printer_result))) { if (isset($printer_result->success) && true == $printer_result->success && isset($printer_result->printers) && is_array($printer_result->printers)) { $printer = $printer_result->printers[0]; // $hashed_id = md5($printer->id); // set_transient('gcpl_popts_'.$hashed_id, $printer_result, 86400); $printers->printers[$index]->_gcpl_printer_opts = $printer; } //isset($printer_result->success) && true == $printer_result->success && isset($printer_result->printers) && is_array($printer_result->printers) } //false !== $get_printer_result && null !== ($printer_result = json_decode($get_printer_result)) } //$printers->printers as $index => $printer if (false !== $get_printer_result) { set_transient('xc_woo_cloud_print_printers', $printers->printers, 86400); } //false !== $get_printer_result return $printers->printers; } //is_object($printers) && isset($printers->success) && $printers->success == true && isset($printers->printers) && is_array($printers->printers) } //isset($options['token']) return array(); } public function xc_woo_cloud_print_options($options) { if (!empty($options)) return $options; return get_option('xc_woo_cloud_print_options', array()); } public function process_request($url, $post_fields, $options = array(), $referer = '') { $ret = ""; $wp_post_opts = array( 'user-agent' => "XC Woo Google Cloud Print" . XC_WOO_CLOUD_VERSION, 'headers' => array( 'X-CloudPrint-Proxy' => "xc-woo-cloud-print", 'Referer' => $referer ), 'sslverify' => true, 'redirection' => 5, 'body' => $post_fields, 'timeout' => 25 ); if (!empty($options['username']) && empty($options['clientid'])) { // Legacy/deprecated - tokens from the now-removed ClientLogin API $wp_post_opts['headers']['Authorization'] = "GoogleLogin auth=$token"; } //!empty($options['username']) && empty($options['clientid']) else { $access_token = $this->access_token($options); if (is_wp_error($access_token) || empty($access_token)) return $access_token; $wp_post_opts['headers']['Authorization'] = "Bearer $access_token"; } $wp_post_opts = apply_filters('xc_woo_cloud_print_process_request_options', $wp_post_opts); $post = wp_remote_post($url, $wp_post_opts); if (is_wp_error($post)) { error_log('POST error: ' . $post->get_error_code() . ': ' . $post->get_error_message()); return $post; } //is_wp_error($post) if (!is_array($post['response']) || !isset($post['response']['code'])) { error_log('POST error: Unexpected response: ' . serialize($post)); return false; } //!is_array($post['response']) || !isset($post['response']['code']) if ($post['response']['code'] >= 400 && $post['response']['code'] < 500) { $extra = ''; error_log('POST error: Unexpected response (code ' . $post['response']['code'] . '): ' . serialize($post)); return new WP_Error('http_badauth', $extra . "Authentication failed (" . $post['response']['code'] . "): " . $post['body']); } //$post['response']['code'] >= 400 && $post['response']['code'] < 500 if ($post['response']['code'] >= 400) { error_log('POST error: Unexpected response (code ' . $post['response']['code'] . '): ' . serialize($post)); return new WP_Error('http_error', 'POST error: Unexpected response (code ' . $post['response']['code'] . '): ' . serialize($post)); } //$post['response']['code'] >= 400 return $post['body']; } private function access_token($options) { $refresh_token = $options['token']; $query_body = array( 'refresh_token' => $refresh_token, 'client_id' => $options['clientid'], 'client_secret' => $options['clientsecret'], 'grant_type' => 'refresh_token' ); $result = wp_remote_post('https://accounts.google.com/o/oauth2/token', array( 'timeout' => 15, 'method' => 'POST', 'body' => $query_body )); if (is_wp_error($result)) { return $result; } //is_wp_error($result) else { $json_values = json_decode(wp_remote_retrieve_body($result), true); if (isset($json_values['access_token'])) { // error_log("Google Drive: successfully obtained access token"); return $json_values['access_token']; } //isset($json_values['access_token']) else { // error_log("Google Drive error when requesting access token: response does not contain access_token"); if (!empty($json_values['error']) && !empty($json_values['error_description'])) { return new WP_Error($json_values['error'], $json_values['error_description']); } //!empty($json_values['error']) && !empty($json_values['error_description']) return false; } } } private function generate_selector($printer_id, $cap_id, $capabilities, $title = '', $current_options) { if ('__google__docs' == $printer_id || !is_array($capabilities) || empty($capabilities[$cap_id])) return ''; $cap = $capabilities[$cap_id]; if (!is_object($cap) || !isset($cap->option) || !is_array($cap->option)) return ''; $option = $cap->option; $selector_id = esc_attr('gcpl_capability_' . $printer_id . '_' . $cap_id); $output = ' '; $output .= '
\n"; return $output; } public function add_action_links($links) { $mylinks = array( 'Settings', 'Cloud Print Options' ); return array_merge($links, $mylinks); } }