1<?php 2/* 3 * Copyright (C) 2014-2019 Frédéric France <frederic.france@netlogic.fr> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <https://www.gnu.org/licenses/>. 17 * or see https://www.gnu.org/ 18 */ 19 20/** 21 * \file htdocs/core/modules/printing/printgcp.modules.php 22 * \ingroup printing 23 * \brief File to provide printing with Google Cloud Print 24 */ 25 26include_once DOL_DOCUMENT_ROOT.'/core/modules/printing/modules_printing.php'; 27require_once DOL_DOCUMENT_ROOT.'/includes/OAuth/bootstrap.php'; 28 29use OAuth\Common\Storage\DoliStorage; 30use OAuth\Common\Consumer\Credentials; 31use OAuth\OAuth2\Service\Google; 32 33/** 34 * Class to provide printing with Google Cloud Print 35 */ 36class printing_printgcp extends PrintingDriver 37{ 38 /** 39 * @var string module name 40 */ 41 public $name = 'printgcp'; 42 43 /** 44 * @var string module description 45 */ 46 public $desc = 'PrintGCPDesc'; 47 48 /** 49 * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png 50 */ 51 public $picto = 'printer'; 52 53 /** 54 * @var string module description 55 */ 56 public $active = 'PRINTING_PRINTGCP'; 57 58 /** 59 * @var array module parameters 60 */ 61 public $conf = array(); 62 63 /** 64 * @var string google id 65 */ 66 public $google_id = ''; 67 68 /** 69 * @var string google secret 70 */ 71 public $google_secret = ''; 72 73 /** 74 * @var string Error code (or message) 75 */ 76 public $error = ''; 77 78 /** 79 * @var string[] Error codes (or messages) 80 */ 81 public $errors = array(); 82 83 /** 84 * @var DoliDB Database handler. 85 */ 86 public $db; 87 88 private $OAUTH_SERVICENAME_GOOGLE = 'Google'; 89 90 const LOGIN_URL = 'https://accounts.google.com/o/oauth2/token'; 91 const PRINTERS_SEARCH_URL = 'https://www.google.com/cloudprint/search'; 92 const PRINTERS_GET_JOBS = 'https://www.google.com/cloudprint/jobs'; 93 const PRINT_URL = 'https://www.google.com/cloudprint/submit'; 94 const LANGFILE = 'printgcp'; 95 96 /** 97 * Constructor 98 * 99 * @param DoliDB $db Database handler 100 */ 101 public function __construct($db) 102 { 103 global $conf, $langs, $dolibarr_main_url_root; 104 105 // Define $urlwithroot 106 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root)); 107 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file 108 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current 109 110 $this->db = $db; 111 112 if (!$conf->oauth->enabled) { 113 $this->conf[] = array( 114 'varname'=>'PRINTGCP_INFO', 115 'info'=>$langs->transnoentitiesnoconv("WarningModuleNotActive", "OAuth"), 116 'type'=>'info', 117 ); 118 } else { 119 $this->google_id = $conf->global->OAUTH_GOOGLE_ID; 120 $this->google_secret = $conf->global->OAUTH_GOOGLE_SECRET; 121 // Token storage 122 $storage = new DoliStorage($this->db, $this->conf); 123 //$storage->clearToken($this->OAUTH_SERVICENAME_GOOGLE); 124 // Setup the credentials for the requests 125 $credentials = new Credentials( 126 $this->google_id, 127 $this->google_secret, 128 $urlwithroot.'/core/modules/oauth/google_oauthcallback.php' 129 ); 130 $access = ($storage->hasAccessToken($this->OAUTH_SERVICENAME_GOOGLE) ? 'HasAccessToken' : 'NoAccessToken'); 131 $serviceFactory = new \OAuth\ServiceFactory(); 132 $apiService = $serviceFactory->createService($this->OAUTH_SERVICENAME_GOOGLE, $credentials, $storage, array()); 133 $token_ok = true; 134 try { 135 $token = $storage->retrieveAccessToken($this->OAUTH_SERVICENAME_GOOGLE); 136 } catch (Exception $e) { 137 $this->errors[] = $e->getMessage(); 138 $token_ok = false; 139 } 140 //var_dump($this->errors);exit; 141 142 $expire = false; 143 // Is token expired or will token expire in the next 30 seconds 144 if ($token_ok) { 145 $expire = ($token->getEndOfLife() !== -9002 && $token->getEndOfLife() !== -9001 && time() > ($token->getEndOfLife() - 30)); 146 } 147 148 // Token expired so we refresh it 149 if ($token_ok && $expire) { 150 try { 151 // il faut sauvegarder le refresh token car google ne le donne qu'une seule fois 152 $refreshtoken = $token->getRefreshToken(); 153 $token = $apiService->refreshAccessToken($token); 154 $token->setRefreshToken($refreshtoken); 155 $storage->storeAccessToken($this->OAUTH_SERVICENAME_GOOGLE, $token); 156 } catch (Exception $e) { 157 $this->errors[] = $e->getMessage(); 158 } 159 } 160 if ($this->google_id != '' && $this->google_secret != '') { 161 $this->conf[] = array('varname'=>'PRINTGCP_INFO', 'info'=>'GoogleAuthConfigured', 'type'=>'info'); 162 $this->conf[] = array( 163 'varname'=>'PRINTGCP_TOKEN_ACCESS', 164 'info'=>$access, 165 'type'=>'info', 166 'renew'=>$urlwithroot.'/core/modules/oauth/google_oauthcallback.php?state=userinfo_email,userinfo_profile,cloud_print&backtourl='.urlencode(DOL_URL_ROOT.'/printing/admin/printing.php?mode=setup&driver=printgcp'), 167 'delete'=>($storage->hasAccessToken($this->OAUTH_SERVICENAME_GOOGLE) ? $urlwithroot.'/core/modules/oauth/google_oauthcallback.php?action=delete&token='.newToken().'&backtourl='.urlencode(DOL_URL_ROOT.'/printing/admin/printing.php?mode=setup&driver=printgcp') : '') 168 ); 169 if ($token_ok) { 170 $expiredat = ''; 171 172 $refreshtoken = $token->getRefreshToken(); 173 174 $endoflife = $token->getEndOfLife(); 175 176 if ($endoflife == $token::EOL_NEVER_EXPIRES) { 177 $expiredat = $langs->trans("Never"); 178 } elseif ($endoflife == $token::EOL_UNKNOWN) { 179 $expiredat = $langs->trans("Unknown"); 180 } else { 181 $expiredat = dol_print_date($endoflife, "dayhour"); 182 } 183 184 $this->conf[] = array('varname'=>'TOKEN_REFRESH', 'info'=>((!empty($refreshtoken)) ? 'Yes' : 'No'), 'type'=>'info'); 185 $this->conf[] = array('varname'=>'TOKEN_EXPIRED', 'info'=>($expire ? 'Yes' : 'No'), 'type'=>'info'); 186 $this->conf[] = array('varname'=>'TOKEN_EXPIRE_AT', 'info'=>($expiredat), 'type'=>'info'); 187 } 188 /* 189 if ($storage->hasAccessToken($this->OAUTH_SERVICENAME_GOOGLE)) { 190 $this->conf[] = array('varname'=>'PRINTGCP_AUTHLINK', 'link'=>$urlwithroot.'/core/modules/oauth/google_oauthcallback.php?backtourl='.urlencode(DOL_URL_ROOT.'/printing/admin/printing.php?mode=setup&driver=printgcp'), 'type'=>'authlink'); 191 $this->conf[] = array('varname'=>'DELETE_TOKEN', 'link'=>$urlwithroot.'/core/modules/oauth/google_oauthcallback.php?action=delete&token='.newToken().'&backtourl='.urlencode(DOL_URL_ROOT.'/printing/admin/printing.php?mode=setup&driver=printgcp'), 'type'=>'delete'); 192 } else { 193 $this->conf[] = array('varname'=>'PRINTGCP_AUTHLINK', 'link'=>$urlwithroot.'/core/modules/oauth/google_oauthcallback.php?backtourl='.urlencode(DOL_URL_ROOT.'/printing/admin/printing.php?mode=setup&driver=printgcp'), 'type'=>'authlink'); 194 }*/ 195 } else { 196 $this->conf[] = array('varname'=>'PRINTGCP_INFO', 'info'=>'GoogleAuthNotConfigured', 'type'=>'info'); 197 } 198 } 199 // do not display submit button 200 $this->conf[] = array('enabled'=>0, 'type'=>'submit'); 201 } 202 203 /** 204 * Return list of available printers 205 * 206 * @return int 0 if OK, >0 if KO 207 */ 208 public function listAvailablePrinters() 209 { 210 global $conf, $langs; 211 $error = 0; 212 $langs->load('printing'); 213 214 $html = '<tr class="liste_titre">'; 215 $html .= '<td>'.$langs->trans('GCP_Name').'</td>'; 216 $html .= '<td>'.$langs->trans('GCP_displayName').'</td>'; 217 $html .= '<td>'.$langs->trans('GCP_Id').'</td>'; 218 $html .= '<td>'.$langs->trans('GCP_OwnerName').'</td>'; 219 $html .= '<td>'.$langs->trans('GCP_State').'</td>'; 220 $html .= '<td>'.$langs->trans('GCP_connectionStatus').'</td>'; 221 $html .= '<td>'.$langs->trans('GCP_Type').'</td>'; 222 $html .= '<td class="center">'.$langs->trans("Select").'</td>'; 223 $html .= '</tr>'."\n"; 224 $list = $this->getlistAvailablePrinters(); 225 //$html.= '<td><pre>'.print_r($list,true).'</pre></td>'; 226 foreach ($list['available'] as $printer_det) { 227 $html .= '<tr class="oddeven">'; 228 $html .= '<td>'.$printer_det['name'].'</td>'; 229 $html .= '<td>'.$printer_det['displayName'].'</td>'; 230 $html .= '<td>'.$printer_det['id'].'</td>'; // id to identify printer to use 231 $html .= '<td>'.$printer_det['ownerName'].'</td>'; 232 $html .= '<td>'.$printer_det['status'].'</td>'; 233 $html .= '<td>'.$langs->trans('STATE_'.$printer_det['connectionStatus']).'</td>'; 234 $html .= '<td>'.$langs->trans('TYPE_'.$printer_det['type']).'</td>'; 235 // Defaut 236 $html .= '<td class="center">'; 237 if ($conf->global->PRINTING_GCP_DEFAULT == $printer_det['id']) { 238 $html .= img_picto($langs->trans("Default"), 'on'); 239 } else { 240 $html .= '<a href="'.$_SERVER["PHP_SELF"].'?action=setvalue&token='.newToken().'&mode=test&varname=PRINTING_GCP_DEFAULT&driver=printgcp&value='.urlencode($printer_det['id']).'" alt="'.$langs->trans("Default").'">'.img_picto($langs->trans("Disabled"), 'off').'</a>'; 241 } 242 $html .= '</td>'; 243 $html .= '</tr>'."\n"; 244 } 245 $this->resprint = $html; 246 return $error; 247 } 248 249 250 /** 251 * Return list of available printers 252 * 253 * @return array list of printers 254 */ 255 public function getlistAvailablePrinters() 256 { 257 $ret = array(); 258 // Token storage 259 $storage = new DoliStorage($this->db, $this->conf); 260 // Setup the credentials for the requests 261 $credentials = new Credentials( 262 $this->google_id, 263 $this->google_secret, 264 DOL_MAIN_URL_ROOT.'/core/modules/oauth/google_oauthcallback.php' 265 ); 266 $serviceFactory = new \OAuth\ServiceFactory(); 267 $apiService = $serviceFactory->createService($this->OAUTH_SERVICENAME_GOOGLE, $credentials, $storage, array()); 268 // Check if we have auth token 269 $token_ok = true; 270 try { 271 $token = $storage->retrieveAccessToken($this->OAUTH_SERVICENAME_GOOGLE); 272 } catch (Exception $e) { 273 $this->errors[] = $e->getMessage(); 274 $token_ok = false; 275 } 276 $expire = false; 277 // Is token expired or will token expire in the next 30 seconds 278 if ($token_ok) { 279 $expire = ($token->getEndOfLife() !== -9002 && $token->getEndOfLife() !== -9001 && time() > ($token->getEndOfLife() - 30)); 280 } 281 282 // Token expired so we refresh it 283 if ($token_ok && $expire) { 284 try { 285 // il faut sauvegarder le refresh token car google ne le donne qu'une seule fois 286 $refreshtoken = $token->getRefreshToken(); 287 $token = $apiService->refreshAccessToken($token); 288 $token->setRefreshToken($refreshtoken); 289 $storage->storeAccessToken($this->OAUTH_SERVICENAME_GOOGLE, $token); 290 } catch (Exception $e) { 291 $this->errors[] = $e->getMessage(); 292 } 293 } 294 // Send a request with api 295 try { 296 $response = $apiService->request(self::PRINTERS_SEARCH_URL); 297 } catch (Exception $e) { 298 $this->errors[] = $e->getMessage(); 299 print '<pre>'.print_r($e->getMessage(), true).'</pre>'; 300 } 301 //print '<tr><td><pre>'.print_r($response, true).'</pre></td></tr>'; 302 $responsedata = json_decode($response, true); 303 $printers = $responsedata['printers']; 304 // Check if we have printers? 305 if (is_array($printers) && count($printers) == 0) { 306 // We dont have printers so return blank array 307 $ret['available'] = array(); 308 } else { 309 // We have printers so returns printers as array 310 $ret['available'] = $printers; 311 } 312 return $ret; 313 } 314 315 /** 316 * Print selected file 317 * 318 * @param string $file file 319 * @param string $module module 320 * @param string $subdir subdir for file 321 * @return int 0 if OK, >0 if KO 322 */ 323 public function printFile($file, $module, $subdir = '') 324 { 325 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; 326 327 global $conf, $user; 328 $error = 0; 329 330 $fileprint = $conf->{$module}->dir_output; 331 if ($subdir != '') { 332 $fileprint .= '/'.$subdir; 333 } 334 $fileprint .= '/'.$file; 335 $mimetype = dol_mimetype($fileprint); 336 // select printer uri for module order, propal,... 337 $sql = "SELECT rowid, printer_id, copy FROM ".MAIN_DB_PREFIX."printing WHERE module='".$this->db->escape($module)."' AND driver='printgcp' AND userid=".((int) $user->id); 338 $result = $this->db->query($sql); 339 if ($result) { 340 $obj = $this->db->fetch_object($result); 341 if ($obj) { 342 $printer_id = $obj->printer_id; 343 } else { 344 if (!empty($conf->global->PRINTING_GCP_DEFAULT)) { 345 $printer_id = $conf->global->PRINTING_GCP_DEFAULT; 346 } else { 347 $this->errors[] = 'NoDefaultPrinterDefined'; 348 $error++; 349 return $error; 350 } 351 } 352 } else { 353 dol_print_error($this->db); 354 } 355 356 $ret = $this->sendPrintToPrinter($printer_id, $file, $fileprint, $mimetype); 357 $this->error = 'PRINTGCP: '.$ret['errormessage']; 358 if ($ret['status'] != 1) { 359 $error++; 360 } 361 return $error; 362 } 363 364 /** 365 * Sends document to the printer 366 * 367 * @param string $printerid Printer id returned by Google Cloud Print 368 * @param string $printjobtitle Job Title 369 * @param string $filepath File Path to be send to Google Cloud Print 370 * @param string $contenttype File content type by example application/pdf, image/png 371 * @return array status array 372 */ 373 public function sendPrintToPrinter($printerid, $printjobtitle, $filepath, $contenttype) 374 { 375 // Check if printer id 376 if (empty($printerid)) { 377 return array('status' =>0, 'errorcode' =>'', 'errormessage'=>'No provided printer ID'); 378 } 379 // Open the file which needs to be print 380 $handle = fopen($filepath, "rb"); 381 if (!$handle) { 382 return array('status' =>0, 'errorcode' =>'', 'errormessage'=>'Could not read the file.'); 383 } 384 // Read file content 385 $contents = fread($handle, filesize($filepath)); 386 fclose($handle); 387 // Prepare post fields for sending print 388 $post_fields = array( 389 'printerid' => $printerid, 390 'title' => $printjobtitle, 391 'contentTransferEncoding' => 'base64', 392 'content' => base64_encode($contents), // encode file content as base64 393 'contentType' => $contenttype, 394 ); 395 // Dolibarr Token storage 396 $storage = new DoliStorage($this->db, $this->conf); 397 // Setup the credentials for the requests 398 $credentials = new Credentials( 399 $this->google_id, 400 $this->google_secret, 401 DOL_MAIN_URL_ROOT.'/core/modules/oauth/google_oauthcallback.php?service=google' 402 ); 403 $serviceFactory = new \OAuth\ServiceFactory(); 404 $apiService = $serviceFactory->createService($this->OAUTH_SERVICENAME_GOOGLE, $credentials, $storage, array()); 405 406 // Check if we have auth token and refresh it 407 $token_ok = true; 408 try { 409 $token = $storage->retrieveAccessToken($this->OAUTH_SERVICENAME_GOOGLE); 410 } catch (Exception $e) { 411 $this->errors[] = $e->getMessage(); 412 $token_ok = false; 413 } 414 if ($token_ok) { 415 try { 416 // il faut sauvegarder le refresh token car google ne le donne qu'une seule fois 417 $refreshtoken = $token->getRefreshToken(); 418 $token = $apiService->refreshAccessToken($token); 419 $token->setRefreshToken($refreshtoken); 420 $storage->storeAccessToken($this->OAUTH_SERVICENAME_GOOGLE, $token); 421 } catch (Exception $e) { 422 $this->errors[] = $e->getMessage(); 423 } 424 } 425 426 // Send a request with api 427 $response = json_decode($apiService->request(self::PRINT_URL, 'POST', $post_fields), true); 428 //print '<tr><td><pre>'.print_r($response, true).'</pre></td></tr>'; 429 return array('status' => $response['success'], 'errorcode' => $response['errorCode'], 'errormessage' => $response['message']); 430 } 431 432 433 /** 434 * List jobs print 435 * 436 * @return int 0 if OK, >0 if KO 437 */ 438 public function listJobs() 439 { 440 global $conf, $langs; 441 442 $error = 0; 443 $html = ''; 444 // Token storage 445 $storage = new DoliStorage($this->db, $this->conf); 446 // Setup the credentials for the requests 447 $credentials = new Credentials( 448 $this->google_id, 449 $this->google_secret, 450 DOL_MAIN_URL_ROOT.'/core/modules/oauth/google_oauthcallback.php' 451 ); 452 $serviceFactory = new \OAuth\ServiceFactory(); 453 $apiService = $serviceFactory->createService($this->OAUTH_SERVICENAME_GOOGLE, $credentials, $storage, array()); 454 // Check if we have auth token 455 $token_ok = true; 456 try { 457 $token = $storage->retrieveAccessToken($this->OAUTH_SERVICENAME_GOOGLE); 458 } catch (Exception $e) { 459 $this->errors[] = $e->getMessage(); 460 $token_ok = false; 461 $error++; 462 } 463 $expire = false; 464 // Is token expired or will token expire in the next 30 seconds 465 if ($token_ok) { 466 $expire = ($token->getEndOfLife() !== -9002 && $token->getEndOfLife() !== -9001 && time() > ($token->getEndOfLife() - 30)); 467 } 468 469 // Token expired so we refresh it 470 if ($token_ok && $expire) { 471 try { 472 // il faut sauvegarder le refresh token car google ne le donne qu'une seule fois 473 $refreshtoken = $token->getRefreshToken(); 474 $token = $apiService->refreshAccessToken($token); 475 $token->setRefreshToken($refreshtoken); 476 $storage->storeAccessToken($this->OAUTH_SERVICENAME_GOOGLE, $token); 477 } catch (Exception $e) { 478 $this->errors[] = $e->getMessage(); 479 $error++; 480 } 481 } 482 // Getting Jobs 483 // Send a request with api 484 try { 485 $response = $apiService->request(self::PRINTERS_GET_JOBS); 486 } catch (Exception $e) { 487 $this->errors[] = $e->getMessage(); 488 $error++; 489 } 490 $responsedata = json_decode($response, true); 491 //$html .= '<pre>'.print_r($responsedata,true).'</pre>'; 492 $html .= '<div class="div-table-responsive">'; 493 $html .= '<table width="100%" class="noborder">'; 494 $html .= '<tr class="liste_titre">'; 495 $html .= '<td>'.$langs->trans("Id").'</td>'; 496 $html .= '<td>'.$langs->trans("Date").'</td>'; 497 $html .= '<td>'.$langs->trans("Owner").'</td>'; 498 $html .= '<td>'.$langs->trans("Printer").'</td>'; 499 $html .= '<td>'.$langs->trans("Filename").'</td>'; 500 $html .= '<td>'.$langs->trans("Status").'</td>'; 501 $html .= '<td>'.$langs->trans("Cancel").'</td>'; 502 $html .= '</tr>'."\n"; 503 504 $jobs = $responsedata['jobs']; 505 //$html .= '<pre>'.print_r($jobs['0'],true).'</pre>'; 506 if (is_array($jobs)) { 507 foreach ($jobs as $value) { 508 $html .= '<tr class="oddeven">'; 509 $html .= '<td>'.$value['id'].'</td>'; 510 $dates = dol_print_date((int) substr($value['createTime'], 0, 10), 'dayhour'); 511 $html .= '<td>'.$dates.'</td>'; 512 $html .= '<td>'.$value['ownerId'].'</td>'; 513 $html .= '<td>'.$value['printerName'].'</td>'; 514 $html .= '<td>'.$value['title'].'</td>'; 515 $html .= '<td>'.$value['status'].'</td>'; 516 $html .= '<td> </td>'; 517 $html .= '</tr>'; 518 } 519 } else { 520 $html .= '<tr class="oddeven">'; 521 $html .= '<td colspan="7" class="opacitymedium">'.$langs->trans("None").'</td>'; 522 $html .= '</tr>'; 523 } 524 $html .= '</table>'; 525 $html .= '</div>'; 526 527 $this->resprint = $html; 528 529 return $error; 530 } 531} 532