dolibarr 25.0.0-alpha
printgcp.modules.php
Go to the documentation of this file.
1<?php
2/*
3 * Copyright (C) 2014-2025 Frédéric France <frederic.france@free.fr>
4 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 * or see https://www.gnu.org/
19 */
20
27include_once DOL_DOCUMENT_ROOT.'/core/modules/printing/modules_printing.php';
28require_once DOL_DOCUMENT_ROOT.'/includes/OAuth/bootstrap.php';
29
30use OAuth\Common\Storage\DoliStorage;
31use OAuth\Common\Consumer\Credentials;
32
37{
41 public $name = 'printgcp';
42
46 public $desc = 'PrintGCPDesc';
47
51 public $picto = 'printer';
52
56 public $active = 'PRINTING_PRINTGCP';
57
61 public $conf = array();
62
66 public $google_id = '';
67
71 public $google_secret = '';
72
76 public $db;
77
81 private $OAUTH_SERVICENAME_GOOGLE = 'Google';
82
83 const LOGIN_URL = 'https://accounts.google.com/o/oauth2/token';
84 const PRINTERS_SEARCH_URL = 'https://www.google.com/cloudprint/search';
85 const PRINTERS_GET_JOBS = 'https://www.google.com/cloudprint/jobs';
86 const PRINT_URL = 'https://www.google.com/cloudprint/submit';
87
88
94 public function __construct($db)
95 {
96 global $conf, $langs, $dolibarr_main_url_root;
97
98 // Define $urlwithroot
99 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
100 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
101 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
102
103 $this->db = $db;
104
105 if (!isModEnabled('oauth')) {
106 $this->conf[] = array(
107 'varname' => 'PRINTGCP_INFO',
108 'info' => $langs->transnoentitiesnoconv("WarningModuleNotActive", "OAuth"),
109 'type' => 'info',
110 );
111 } else {
112 $keyforprovider = 'googleprint';
113
114 $this->google_id = getDolGlobalString('OAUTH_GOOGLE_ID');
115 $this->google_secret = getDolGlobalString('OAUTH_GOOGLE_SECRET');
116 // Token storage
117 $storage = new DoliStorage($this->db, $conf, $keyforprovider);
118
119 //$storage->clearToken($this->OAUTH_SERVICENAME_GOOGLE);
120 // Setup the credentials for the requests
121 $credentials = new Credentials(
122 $this->google_id,
123 $this->google_secret,
124 $urlwithroot.'/core/modules/oauth/google_oauthcallback.php'
125 );
126 $access = ($storage->hasAccessToken($this->OAUTH_SERVICENAME_GOOGLE) ? 'HasAccessToken' : 'NoAccessToken');
127
128 $serviceFactory = new \OAuth\ServiceFactory();
129 // Call $serviceFactory->buildV2Service() that do a construct with "new OAuth/OAuth2/Service/Google()" to build the $apiService object
130 $apiService = $serviceFactory->createService($this->OAUTH_SERVICENAME_GOOGLE, $credentials, $storage, array());
131 '@phan-var-force OAuth\OAuth2\Service\Google $apiService'; // createService is only ServiceInterface
132
133 $token = null;
134 try {
135 // Do a select into oauth_token to get existing token
136 $token = $storage->retrieveAccessToken($this->OAUTH_SERVICENAME_GOOGLE);
137 } catch (Exception $e) {
138 $this->errors[] = $e->getMessage();
139 $token = null;
140 }
141
142 $expire = false;
143 // Is token expired or will token expire in the next 30 seconds
144 if ($token !== null) {
145 // time() is used internally in token @phan-suppress-next-line DolibarrForbiddenFunctionPlugin
146 $expire = ($token->getEndOfLife() !== -9002 && $token->getEndOfLife() !== -9001 && time() > ($token->getEndOfLife() - 30));
147 }
148
149 // Token expired so we refresh it
150 if (($token !== null) && $expire) {
151 try {
152 // il faut sauvegarder le refresh token car google ne le donne qu'une seule fois
153 $refreshtoken = $token->getRefreshToken();
154 $token = $apiService->refreshAccessToken($token);
155 $token->setRefreshToken($refreshtoken);
156 $storage->storeAccessToken($this->OAUTH_SERVICENAME_GOOGLE, $token);
157 } catch (Exception $e) {
158 $this->errors[] = $e->getMessage();
159 }
160 }
161 if ($this->google_id != '' && $this->google_secret != '') {
162 $this->conf[] = array(
163 'varname' => 'PRINTGCP_INFO',
164 'info' => 'GoogleAuthConfigured',
165 'type' => 'info'
166 );
167 $this->conf[] = array(
168 'varname' => 'PRINTGCP_TOKEN_ACCESS',
169 'info' => $access,
170 'type' => 'info',
171 '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'),
172 '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') : '')
173 );
174 if ($token !== null) {
175 $expiredat = '';
176
177 $refreshtoken = $token->getRefreshToken();
178
179 $endoflife = $token->getEndOfLife();
180
181 if ($endoflife == $token::EOL_NEVER_EXPIRES) {
182 $expiredat = $langs->trans("Never");
183 } elseif ($endoflife == $token::EOL_UNKNOWN) {
184 $expiredat = $langs->trans("Unknown");
185 } else {
186 $expiredat = dol_print_date($endoflife, "dayhour");
187 }
188
189 $this->conf[] = array('varname' => 'TOKEN_REFRESH', 'info' => ((!empty($refreshtoken)) ? 'Yes' : 'No'), 'type' => 'info');
190 $this->conf[] = array('varname' => 'TOKEN_EXPIRED', 'info' => ($expire ? 'Yes' : 'No'), 'type' => 'info');
191 $this->conf[] = array('varname' => 'TOKEN_EXPIRE_AT', 'info' => ($expiredat), 'type' => 'info');
192 }
193 /*
194 if ($storage->hasAccessToken($this->OAUTH_SERVICENAME_GOOGLE)) {
195 $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');
196 $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');
197 } else {
198 $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');
199 }*/
200 } else {
201 $this->conf[] = array(
202 'varname' => 'PRINTGCP_INFO',
203 'info' => 'GoogleAuthNotConfigured',
204 'type' => 'info'
205 );
206 }
207 }
208 // do not display submit button
209 $this->conf[] = array('enabled' => 0, 'type' => 'submit');
210 }
211
217 public function listAvailablePrinters()
218 {
219 global $conf, $langs;
220 $error = 0;
221 $langs->load('printing');
222
223 $html = '<tr class="liste_titre">';
224 $html .= '<td>'.$langs->trans('GCP_Name').'</td>';
225 $html .= '<td>'.$langs->trans('GCP_displayName').'</td>';
226 $html .= '<td>'.$langs->trans('GCP_Id').'</td>';
227 $html .= '<td>'.$langs->trans('GCP_OwnerName').'</td>';
228 $html .= '<td>'.$langs->trans('GCP_State').'</td>';
229 $html .= '<td>'.$langs->trans('GCP_connectionStatus').'</td>';
230 $html .= '<td>'.$langs->trans('GCP_Type').'</td>';
231 $html .= '<td class="center">'.$langs->trans("Select").'</td>';
232 $html .= '</tr>'."\n";
233 $list = $this->getlistAvailableGcpPrinters();
234 //$html.= '<td><pre>'.print_r($list,true).'</pre></td>';
235 foreach ($list['available'] as $printer_det) {
236 $html .= '<tr class="oddeven">';
237 $html .= '<td>'.$printer_det['name'].'</td>';
238 $html .= '<td>'.$printer_det['displayName'].'</td>';
239 $html .= '<td>'.$printer_det['id'].'</td>'; // id to identify printer to use
240 $html .= '<td>'.$printer_det['ownerName'].'</td>';
241 $html .= '<td>'.$printer_det['status'].'</td>';
242 $html .= '<td>'.$langs->trans('STATE_'.$printer_det['connectionStatus']).'</td>';
243 $html .= '<td>'.$langs->trans('TYPE_'.$printer_det['type']).'</td>';
244 // Default
245 $html .= '<td class="center">';
246 if (getDolGlobalString('PRINTING_GCP_DEFAULT') == $printer_det['id']) {
247 $html .= img_picto($langs->trans("Default"), 'on');
248 } else {
249 $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>';
250 }
251 $html .= '</td>';
252 $html .= '</tr>'."\n";
253 }
254 $this->resprint = $html;
255 return $error;
256 }
257
258
264 public function getlistAvailablePrinters()
265 {
266 /* Compatible with paretn class signature */
267 return $this->getlistAvailableGcpPrinters()['available'];
268 }
269
270
277 {
278 global $conf;
279 $ret = array();
280
281 $keyforprovider = 'googleprint';
282
283 // Token storage
284 $storage = new DoliStorage($this->db, $conf, $keyforprovider);
285 // Setup the credentials for the requests
286 $credentials = new Credentials(
287 $this->google_id,
288 $this->google_secret,
289 DOL_MAIN_URL_ROOT.'/core/modules/oauth/google_oauthcallback.php'
290 );
291 $serviceFactory = new \OAuth\ServiceFactory();
292 $apiService = $serviceFactory->createService($this->OAUTH_SERVICENAME_GOOGLE, $credentials, $storage, array());
293 '@phan-var-force OAuth\OAuth2\Service\Google $apiService'; // createService is only ServiceInterface
294 // Check if we have auth token
295 $token = null;
296 try {
297 $token = $storage->retrieveAccessToken($this->OAUTH_SERVICENAME_GOOGLE);
298 } catch (Exception $e) {
299 $this->errors[] = $e->getMessage();
300 $token = null;
301 }
302 $expire = false;
303 // Is token expired or will token expire in the next 30 seconds
304 if ($token !== null) {
305 $expire = ($token->getEndOfLife() !== -9002 && $token->getEndOfLife() !== -9001 && time() > ($token->getEndOfLife() - 30));
306 }
307
308 // Token expired so we refresh it
309 if (($token !== null) && $expire) {
310 try {
311 // il faut sauvegarder le refresh token car google ne le donne qu'une seule fois
312 $refreshtoken = $token->getRefreshToken();
313 $token = $apiService->refreshAccessToken($token);
314 $token->setRefreshToken($refreshtoken);
315 $storage->storeAccessToken($this->OAUTH_SERVICENAME_GOOGLE, $token);
316 } catch (Exception $e) {
317 $this->errors[] = $e->getMessage();
318 }
319 }
320 // Send a request with api
321 $response = '';
322 try {
323 $response = $apiService->request(self::PRINTERS_SEARCH_URL);
324 } catch (Exception $e) {
325 $this->errors[] = $e->getMessage();
326 print '<pre>'.print_r($e->getMessage(), true).'</pre>';
327 }
328 //print '<tr><td><pre>'.print_r($response, true).'</pre></td></tr>';
329 $responsedata = json_decode($response, true);
330 $printers = $responsedata['printers'];
331 // Check if we have printers?
332 if (is_array($printers) && count($printers) == 0) {
333 // We don't have printers so return blank array
334 $ret['available'] = array();
335 } else {
336 // We have printers so returns printers as array
337 $ret['available'] = $printers;
338 }
339 return $ret;
340 }
341
350 public function printFile($file, $module, $subdir = '')
351 {
352 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
353
354 global $conf, $user;
355 $error = 0;
356
357 $fileprint = $conf->{$module}->dir_output;
358 if ($subdir != '') {
359 $fileprint .= '/'.$subdir;
360 }
361 $fileprint .= '/'.$file;
362 $mimetype = dol_mimetype($fileprint);
363 $printer_id = '';
364 // select printer uri for module order, propal,...
365 $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);
366 $result = $this->db->query($sql);
367 if ($result) {
368 $obj = $this->db->fetch_object($result);
369 if ($obj) {
370 $printer_id = $obj->printer_id;
371 } else {
372 if (getDolGlobalString('PRINTING_GCP_DEFAULT')) {
373 $printer_id = getDolGlobalString('PRINTING_GCP_DEFAULT');
374 } else {
375 $this->errors[] = 'NoDefaultPrinterDefined';
376 $error++;
377 return $error;
378 }
379 }
380 } else {
381 dol_print_error($this->db);
382 }
383
384 $ret = $this->sendPrintToPrinter($printer_id, $file, $fileprint, $mimetype);
385 $this->error = 'PRINTGCP: '.$ret['errormessage'];
386 if ($ret['status'] != 1) {
387 $error++;
388 }
389 return $error;
390 }
391
401 public function sendPrintToPrinter($printerid, $printjobtitle, $filepath, $contenttype)
402 {
403 global $conf;
404 // Check if printer id
405 if (empty($printerid)) {
406 return array('status' => 0, 'errorcode' => '', 'errormessage' => 'No provided printer ID');
407 }
408 // Open the file which needs to be print
409 $handle = fopen($filepath, "rb");
410 if (!$handle) {
411 return array('status' => 0, 'errorcode' => '', 'errormessage' => 'Could not read the file.');
412 }
413 // Read file content
414 $contents = fread($handle, filesize($filepath));
415 fclose($handle);
416 // Prepare post fields for sending print
417 $post_fields = array(
418 'printerid' => $printerid,
419 'title' => $printjobtitle,
420 'contentTransferEncoding' => 'base64',
421 'content' => base64_encode($contents), // encode file content as base64
422 'contentType' => $contenttype,
423 );
424
425 $keyforprovider = 'googleprint';
426
427 // Dolibarr Token storage
428 $storage = new DoliStorage($this->db, $conf, $keyforprovider);
429 // Setup the credentials for the requests
430 $credentials = new Credentials(
431 $this->google_id,
432 $this->google_secret,
433 DOL_MAIN_URL_ROOT.'/core/modules/oauth/google_oauthcallback.php?service=google'
434 );
435 $serviceFactory = new \OAuth\ServiceFactory();
436 $apiService = $serviceFactory->createService($this->OAUTH_SERVICENAME_GOOGLE, $credentials, $storage, array());
437 '@phan-var-force OAuth\OAuth2\Service\Google $apiService'; // createService is only ServiceInterface
438
439 // Check if we have auth token and refresh it
440 $token = null;
441 try {
442 $token = $storage->retrieveAccessToken($this->OAUTH_SERVICENAME_GOOGLE);
443 } catch (Exception $e) {
444 $this->errors[] = $e->getMessage();
445 $token = null;
446 }
447 if ($token !== null) {
448 try {
449 // il faut sauvegarder le refresh token car google ne le donne qu'une seule fois
450 $refreshtoken = $token->getRefreshToken();
451 $token = $apiService->refreshAccessToken($token);
452 $token->setRefreshToken($refreshtoken);
453 $storage->storeAccessToken($this->OAUTH_SERVICENAME_GOOGLE, $token);
454 } catch (Exception $e) {
455 $this->errors[] = $e->getMessage();
456 }
457 }
458
459 // Send a request with api
460 $response = json_decode($apiService->request(self::PRINT_URL, 'POST', $post_fields), true);
461 //print '<tr><td><pre>'.print_r($response, true).'</pre></td></tr>';
462 return array('status' => $response['success'], 'errorcode' => $response['errorCode'], 'errormessage' => $response['message']);
463 }
464
465
473 public function listJobs($module = null)
474 {
475 global $conf, $langs;
476
477 $error = 0;
478 $html = '';
479
480 $keyforprovider = 'googleprint';
481
482 // Token storage
483 $storage = new DoliStorage($this->db, $conf, $keyforprovider);
484 // Setup the credentials for the requests
485 $credentials = new Credentials(
486 $this->google_id,
487 $this->google_secret,
488 DOL_MAIN_URL_ROOT.'/core/modules/oauth/google_oauthcallback.php'
489 );
490 $serviceFactory = new \OAuth\ServiceFactory();
491 $apiService = $serviceFactory->createService($this->OAUTH_SERVICENAME_GOOGLE, $credentials, $storage, array());
492 '@phan-var-force OAuth\OAuth2\Service\Google $apiService'; // createService is only ServiceInterface
493 // Check if we have auth token
494 $token = null;
495 try {
496 $token = $storage->retrieveAccessToken($this->OAUTH_SERVICENAME_GOOGLE);
497 } catch (Exception $e) {
498 $this->errors[] = $e->getMessage();
499 $token = null;
500 $error++;
501 }
502 $expire = false;
503 // Is token expired or will token expire in the next 30 seconds
504 if ($token !== null) {
505 $expire = ($token->getEndOfLife() !== -9002 && $token->getEndOfLife() !== -9001 && time() > ($token->getEndOfLife() - 30));
506 }
507
508 // Token expired so we refresh it
509 if (($token !== null) && $expire) {
510 try {
511 // il faut sauvegarder le refresh token car google ne le donne qu'une seule fois
512 $refreshtoken = $token->getRefreshToken();
513 $token = $apiService->refreshAccessToken($token);
514 $token->setRefreshToken($refreshtoken);
515 $storage->storeAccessToken($this->OAUTH_SERVICENAME_GOOGLE, $token);
516 } catch (Exception $e) {
517 $this->errors[] = $e->getMessage();
518 $error++;
519 }
520 }
521 // Getting Jobs
522 // Send a request with api
523 $response = '';
524 try {
525 $response = $apiService->request(self::PRINTERS_GET_JOBS);
526 } catch (Exception $e) {
527 $this->errors[] = $e->getMessage();
528 $error++;
529 }
530 $responsedata = json_decode($response, true);
531 //$html .= '<pre>'.print_r($responsedata,true).'</pre>';
532 $html .= '<div class="div-table-responsive">';
533 $html .= '<table width="100%" class="noborder">';
534 $html .= '<tr class="liste_titre">';
535 $html .= '<td>'.$langs->trans("Id").'</td>';
536 $html .= '<td>'.$langs->trans("Date").'</td>';
537 $html .= '<td>'.$langs->trans("Owner").'</td>';
538 $html .= '<td>'.$langs->trans("Printer").'</td>';
539 $html .= '<td>'.$langs->trans("Filename").'</td>';
540 $html .= '<td>'.$langs->trans("Status").'</td>';
541 $html .= '<td>'.$langs->trans("Cancel").'</td>';
542 $html .= '</tr>'."\n";
543
544 $jobs = $responsedata['jobs'];
545 //$html .= '<pre>'.print_r($jobs['0'],true).'</pre>';
546 if (is_array($jobs)) {
547 foreach ($jobs as $value) {
548 $html .= '<tr class="oddeven">';
549 $html .= '<td>'.$value['id'].'</td>';
550 $dates = dol_print_date((int) substr($value['createTime'], 0, 10), 'dayhour');
551 $html .= '<td>'.$dates.'</td>';
552 $html .= '<td>'.$value['ownerId'].'</td>';
553 $html .= '<td>'.$value['printerName'].'</td>';
554 $html .= '<td>'.$value['title'].'</td>';
555 $html .= '<td>'.$value['status'].'</td>';
556 $html .= '<td>&nbsp;</td>';
557 $html .= '</tr>';
558 }
559 } else {
560 $html .= '<tr class="oddeven">';
561 $html .= '<td colspan="7" class="opacitymedium">'.$langs->trans("None").'</td>';
562 $html .= '</tr>';
563 }
564 $html .= '</table>';
565 $html .= '</div>';
566
567 $this->resprint = $html;
568
569 return $error;
570 }
571}
global $dolibarr_main_url_root
Parent class of emailing target selectors modules.
Class to provide printing with Google Cloud Print.
listJobs($module=null)
List jobs print.
__construct($db)
Constructor.
listAvailablePrinters()
Return list of available printers.
printFile($file, $module, $subdir='')
Print selected file.
getlistAvailablePrinters()
Return list of available printers.
sendPrintToPrinter($printerid, $printjobtitle, $filepath, $contenttype)
Sends document to the printer.
getlistAvailableGcpPrinters()
Return list of available printers (internal format)
if(! $sortfield) if(! $sortorder) $module
Definition list.php:193
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
dol_mimetype($file, $default='application/octet-stream', $mode=0)
Return MIME type of a file from its name with extension.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
conf($dolibarr_main_document_root, $realpathconf=null)
Load conf file (file must exists)
Definition inc.php:431