diff --git a/admin/oauthlogintokens.php b/admin/oauthlogintokens.php index 47faa86..2467b33 100644 --- a/admin/oauthlogintokens.php +++ b/admin/oauthlogintokens.php @@ -36,7 +36,7 @@ use League\OAuth2\Client\Provider\GoogleUser; // Load translation files required by the page -$langs->loadLangs(["errors","admin","googleapi@googleapi"]); +$langs->loadLangs(['errors', 'oauth', 'admin', 'googleapi@googleapi']); if (!$user->admin) { accessforbidden(); @@ -107,7 +107,7 @@ // This is to use external domain name found into config file $urlwithroot = $urlwithouturlroot . DOL_URL_ROOT; // This is to use same domain name than current -//$urlwithroot=DOL_MAIN_URL_ROOT; +// $urlwithroot=DOL_MAIN_URL_ROOT; $form = new Form($db); @@ -120,7 +120,6 @@ print dol_get_fiche_head($head, 'tokengeneration', '', -1, 'technic'); - if ($user->admin) { print $langs->trans("OAuthSetupForLogin") . "

\n"; @@ -142,26 +141,30 @@ $expiredat = ''; - // Is token expired or will token expire in the next 30 seconds + // Is token expired or will token expire in the next 60 seconds if (is_object($token)) { $expire = $token->hasExpired(); - $isgoingtoexpire = (time() > ($token->getExpires() - 30)); - if ($isgoingtoexpire) { + $isgoingtoexpire = (time() > ($token->getExpires() - 60)); + if ($isgoingtoexpire || $expire) { $provider = new Google([ 'clientId' => getDolGlobalString('OAUTH_GOOGLEAPI_ID'), 'clientSecret' => getDolGlobalString('OAUTH_GOOGLEAPI_SECRET'), 'redirectUri' => dol_buildpath('/googleapi/core/modules/oauth/googleapi_oauthcallback.php', 2), ]); $grant = new RefreshToken(); - $token = $provider->getAccessToken($grant, ['refresh_token' => $tokenrefreshbackup]); - $expire = $token->hasExpired(); - storeAccessToken('GoogleApi', $token, $tokenrefreshbackup, $user->id); - setEventMessages($langs->trans('NewTokenStored'), null, 'mesgs'); // Stored into object managed by class DoliStorage so into table oauth_token + try { + $token = $provider->getAccessToken($grant, ['refresh_token' => $tokenrefreshbackup]); + $expire = $token->hasExpired(); + storeAccessToken('GoogleApi', $token, $tokenrefreshbackup, $user->id); + setEventMessages($langs->trans('NewTokenStored'), null, 'mesgs'); // Stored into object managed by class DoliStorage so into table oauth_token + } catch (Exception $e) { + setEventMessages($e->getMessage(), null, 'errors'); + } } $refreshtoken = $token->getRefreshToken(); $endoflife = $token->getExpires(); - $expiredat = dol_print_date($endoflife, "dayhour"); + $expiredat = dol_print_date($endoflife, "dayhour", "tzuser"); } print '
'; diff --git a/admin/setup.php b/admin/setup.php index 92f96da..6a8807e 100644 --- a/admin/setup.php +++ b/admin/setup.php @@ -24,6 +24,10 @@ // Load Dolibarr environment include '../config.php'; +/** + * @var DoliDB $db + * @var Translate $langs + */ // Libraries require_once DOL_DOCUMENT_ROOT . "/core/lib/admin.lib.php"; @@ -38,7 +42,7 @@ use Cache\Adapter\Filesystem\FilesystemCachePool; // Translations -$langs->loadLangs(["errors","admin","googleapi@googleapi"]); +$langs->loadLangs(['errors', 'admin', 'oauth', 'googleapi@googleapi']); // Access control if (! $user->admin) { @@ -95,6 +99,13 @@ 'CHECKLASTVERSION_EXTERNALMODULE' => 'CHECKLASTVERSION_EXTERNALMODULE', ]; +$googleapicontexts = json_decode(getDolGlobalString('GOOGLEAPI_CONTEXTS_TO_SEND', '{}'), true); +if (empty($googleapicontexts)) { + // set default + dolibarr_set_const($db, 'GOOGLEAPI_CONTEXTS_TO_SEND', json_encode(['standard' => true]), 'chaine', 0, '', $conf->entity); + $googleapicontexts = json_decode(getDolGlobalString('GOOGLEAPI_CONTEXTS_TO_SEND', '{}'), true); +} + /* * Actions */ @@ -108,6 +119,15 @@ //exit; } } +foreach ($googleapicontexts as $constant => $value) { + if ($action == 'contextenable_' . strtolower($constant)) { + $googleapicontexts[$constant] = true; + } + if ($action == 'contextdisable_' . strtolower($constant)) { + $googleapicontexts[$constant] = false; + } + dolibarr_set_const($db, 'GOOGLEAPI_CONTEXTS_TO_SEND', json_encode($googleapicontexts), 'chaine', 0, '', $conf->entity); +} if ($action == 'update') { $error = 0; $db->begin(); @@ -145,7 +165,7 @@ if ($action == 'edit') { print ''; - print ''; + print ''; print ''; //print $langs->trans("ListOfSupportedOauthProviders").'

'; @@ -234,6 +254,32 @@ } print '' . PHP_EOL; print '
' . PHP_EOL; + + // Contexts + print ''; + print ''; + print ''; + print ''; + print "\n"; + foreach ($googleapicontexts as $constant => $value) { + print ''; + print ''; + print '"; + print ''; + } + print "
' . $langs->trans("GoogleApiSendContexts") . '' . $langs->trans("Action") . '
' . $langs->trans('GoogleApiEnabledContext', $constant) . ''; + // $value = (isset($conf->global->$constant) ? $conf->global->$constant : 0); + if (!$value) { + print ''; + print img_picto($langs->trans("Disabled"), 'switch_off'); + print ''; + } elseif ($value) { + print ''; + print img_picto($langs->trans("Enabled"), 'switch_on'); + print ''; + } + print "
\n"; + print "
\n"; } print dol_get_fiche_end(); diff --git a/class/actions_googleapi.class.php b/class/actions_googleapi.class.php index 82231e8..fc4de40 100644 --- a/class/actions_googleapi.class.php +++ b/class/actions_googleapi.class.php @@ -137,10 +137,14 @@ public function doActions(&$parameters, $object, &$action, $hookmanager) 'googleapiremindemail' => [ 'label' => $langs->trans('GoogleApiRemindByEmail'), 'disabled' => 0, + 'mode' => 'email', + 'data-html' => img_picto('', 'googleapi@googleapi', 'class="pictofixedwidth"') . $langs->trans('GoogleApiRemindByEmail'), ], 'googleapiremindnotif' => [ 'label' => $langs->trans('GoogleApiRemindByNotification'), 'disabled' => 0, + 'mode' => 'browser', + 'data-html' => img_picto('', 'googleapi@googleapi', 'class="pictofixedwidth"') . $langs->trans('GoogleApiRemindByNotification'), ] ] ); @@ -191,9 +195,6 @@ public function completeTabsHead(&$parameters, $object, &$action, $hookmanager) { global $langs, $conf, $user; - // var_dump($parameters['object']); - // var_dump($parameters['mode']); - // var_dump($parameters); if (!isset($parameters['object']->element)) { return 0; } @@ -224,7 +225,7 @@ public function completeTabsHead(&$parameters, $object, &$action, $hookmanager) $sql .= ' AND fk_object=' . (int) $id; $resql = $this->db->query($sql); if ($resql && $obj = $this->db->fetch_object($resql)) { - $emailcount = $obj->nb; + $emailcount = (int) $obj->nb; } // If setting cache fails, this is not a problem, so we do not test result. dol_setcache($cachekey, $emailcount, 120); @@ -280,29 +281,40 @@ public function sendMail($parameters, $object, &$action, $hookmanager) $error = 0; // Error counter $contexts = explode(':', $parameters['context']); - // print '
'.print_r($parameters, true).'
'; - // print '
'.print_r($object, true).'
'; - // echo "action: " . $action;exit; + $context = $object->sendcontext ?? 'standard'; + $googleapicontexts = json_decode(getDolGlobalString('GOOGLEAPI_CONTEXTS_TO_SEND', '{}'), true); + if (empty($googleapicontexts)) { + // set default + dolibarr_set_const($this->db, 'GOOGLEAPI_CONTEXTS_TO_SEND', json_encode(['standard' => true]), 'chaine', 0, '', $conf->entity); + $googleapicontexts = json_decode(getDolGlobalString('GOOGLEAPI_CONTEXTS_TO_SEND', '{}'), true); + } + if (!array_key_exists($context, $googleapicontexts)) { + // we found a new context + $googleapicontexts = array_merge($googleapicontexts, [$context => false]); + dolibarr_set_const($this->db, 'GOOGLEAPI_CONTEXTS_TO_SEND', json_encode($googleapicontexts), 'chaine', 0, '', $conf->entity); + } + $googleapicontextsok = []; + foreach ($googleapicontexts as $key => $item) { + if ($item) { + $googleapicontextsok[] = $key; + } + } // what TODO with context 'emailing' // context notification? // https://developers.google.com/resources/api-libraries/documentation/gmail/v1/php/latest/class-Google_Service_Gmail_Message.html - if (in_array('mail', $contexts) && !in_array($object->sendcontext, ['emailing', 'notification'])) { + if (in_array($context, $googleapicontextsok)) { dol_include_once('/googleapi/lib/googleapi.lib.php'); $fromsender = $this->getArrayAddress($object->addr_from); if (!empty($user->array_options['options_googleapi_email']) && $fromsender[0]['address'] == $user->array_options['options_googleapi_email']) { $client = getGoogleApiClient($user); $service = new Google\Service\Gmail($client); - $replytosender = $this->getArrayAddress($object->reply_to); - $addrtorecipients = $this->getArrayAddress($object->addr_to); - $addrccrecipients = $this->getArrayAddress($object->addr_cc); - $addrbccrecipients = $this->getArrayAddress($object->addr_bcc); - $message = new Google\Service\Gmail\Message(); - $mime = rtrim(strtr(base64_encode($object->message), '+/', '-_'), '='); + $mime = rtrim(strtr(base64_encode($this->buildRawMessage($object)), '+/', '-_'), '='); $message->setRaw($mime); $mailsent = false; + $response = null; try { $response = $service->users_messages->send('me', $message); $mailsent = true; @@ -314,7 +326,6 @@ public function sendMail($parameters, $object, &$action, $hookmanager) if ($mailsent) { $googleapiMessageId = $response->getId(); } - //var_dump($response);exit; } else { // nothing done return 0; @@ -322,16 +333,60 @@ public function sendMail($parameters, $object, &$action, $hookmanager) } if (!$error) { - //$this->results = array('msgid' => 'azerty'); - //$this->resprints = 'A text to show'; // 1 si on a envoyé avec googleapi sinon 0 return 1; // or return 1 to replace standard code } else { - $this->errors[] = 'Error message'; + $this->errors[] = 'Error in googleapi module'; return -1; } } + /** + * Build a full RFC 822 raw message (headers + body + attachments) from a CMailFile object, + * suitable for the Gmail API 'raw' field. + * + * CMailFile only populates $object->headers/$object->message (the MIME parts used here) when + * $object->sendmode == 'mail'. On this instance MAIN_MAIL_SENDMODE is 'smtps', so those + * properties are never filled and used to be encoded empty, silently breaking every mail sent + * through this hook. Rebuilding the message here from data that CMailFile always populates + * (subject, addr_to/cc/bcc, html/msg, attachments, ...) makes this independent of sendmode. + * + * @param CMailFile $object The mail object to process + * @return string Full raw RFC 822 message + */ + private function buildRawMessage($object) + { + $subjecttouse = $object->subject; + if (!ascii_check($subjecttouse)) { + $subjecttouse = CMailFile::encodetorfc2822($subjecttouse); + } + + $headers = "To: ".CMailFile::getValidAddress($object->addr_to, 0, 1).$object->eol2; + $headers .= "Subject: ".$subjecttouse.$object->eol2; + $headers .= $object->write_smtpheaders(); + $headers .= $object->write_mimeheaders($object->filename_list, $object->mimefilename_list); + $headers = preg_replace("/([\r\n]+)$/i", "", $headers); + + $msgforbody = $object->msgishtml ? $object->html : $object->msg; + $body = $object->write_body($msgforbody); + + $filesencoded = ''; + if (!empty($object->atleastonefile) && is_array($object->filename_list)) { + $refmethod = new ReflectionMethod($object, 'write_files'); + $refmethod->setAccessible(true); + $result = $refmethod->invoke($object, $object->filename_list, $object->mimetype_list, $object->mimefilename_list, $object->cid_list); + if (is_string($result)) { + $filesencoded = $result; + } + } + + $rawmessage = $headers.$object->eol.$object->eol; // Blank line to separate headers from body + $rawmessage .= $body.$filesencoded; + $rawmessage .= "--".$object->mixed_boundary."--".$object->eol; + + return $rawmessage; + } + /** * Overloading the sendMailAfter function : replacing the parent's function with the one below * diff --git a/class/googleapi.class.php b/class/googleapi.class.php index b40851e..1f4d679 100644 --- a/class/googleapi.class.php +++ b/class/googleapi.class.php @@ -135,6 +135,7 @@ private function chekValidPushNotificationForEvents($user, $urlfornotification) dol_include_once('/prune/vendor/autoload.php'); $client = getGoogleApiClient($user); $service = new Google\Service\Calendar($client); + $calendarId = $user->array_options['options_googleapi_calendarId'] ?: 'primary'; $sql = "SELECT rowid, userid, uuid, id, resourcetype, resourceUri, ressourceId, expirationDateTime, lastmessagenumber FROM " . MAIN_DB_PREFIX . "googleapi_watchs"; $sql .= ' WHERE userid=' . (int) $user->id . ' AND resourcetype="events"'; @@ -143,7 +144,6 @@ private function chekValidPushNotificationForEvents($user, $urlfornotification) if ($resql && $this->db->num_rows($resql) > 0) { // on a déjà quelquechose $row = $this->db->fetch_object($resql); - $calendarId = $user->array_options['options_googleapi_calendarId'] ?: 'primary'; // is it going to expire in 30min // expiration is gmt @@ -217,6 +217,7 @@ private function chekValidPushNotificationForEvents($user, $urlfornotification) $resql = $this->db->query($sql); } catch (Exception $e) { dol_syslog($e->getmessage(), LOG_ERR); + return -1; } //exit; } @@ -313,7 +314,7 @@ private function chekValidPushNotificationForContacts($user, $urlfornotification $sql .= ", 'contacts'"; $sql .= ", '" . $this->db->escape($watch->getResourceUri()) . "'"; $sql .= ", '" . $this->db->escape($watch->getResourceId()) . "'"; - //$sql .= ", '" . ($watch->getExpiration())->format('Y-m-d H:i:s') . "'"; + // $sql .= ", '" . ($watch->getExpiration())->format('Y-m-d H:i:s') . "'"; // timestamp in ms $sql .= ", '" . ($this->db->idate(substr($watch->getExpiration(), 0, -3))) . "'"; $sql .= ", '1')"; @@ -321,9 +322,11 @@ private function chekValidPushNotificationForContacts($user, $urlfornotification $resql = $this->db->query($sql); } catch (Exception $e) { dol_syslog($e->getmessage(), LOG_ERR); + return -1; } //exit; } + return 0; } @@ -333,6 +336,7 @@ private function chekValidPushNotificationForContacts($user, $urlfornotification */ private function getUuid() { + $data = '0123456789012345'; try { $data = random_bytes(16); } catch (Exception $e) { diff --git a/config.php b/config.php index fe169c5..489796f 100644 --- a/config.php +++ b/config.php @@ -1,6 +1,6 @@ + * Copyright (C) 2019-2026 Frédéric France * * 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 diff --git a/core/ajax/ecmgoogledrivedownload.php b/core/ajax/ecmgoogledrivedownload.php new file mode 100644 index 0000000..d07c26a --- /dev/null +++ b/core/ajax/ecmgoogledrivedownload.php @@ -0,0 +1,73 @@ + + * + * 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 . + */ + +/** + * \file googleapi/core/ajax/ecmgoogledrivedownload.php + * \ingroup googleapi + * \brief Streams a Google Drive file to the browser + */ + +$defines = [ + 'NOTOKENRENEWAL', + 'NOREQUIREMENU', + 'NOREQUIREHTML', + 'NOREQUIREAJAX', +]; + +// Load Dolibarr environment +include '../../config.php'; +require_once '../../lib/googleapi.lib.php'; + +if (!$user->hasRight('googleapi', 'read')) { + accessforbidden(); +} + +$fileid = GETPOST('fileid', 'alpha'); +if (empty($fileid)) { + http_response_code(400); + print 'Missing fileid'; + exit; +} + +$driveservice = getGoogleDriveService($user); +if (!is_object($driveservice)) { + http_response_code(403); + print 'Not connected to Google Drive'; + exit; +} + +try { + $metadata = $driveservice->files->get($fileid, array('fields' => 'name,mimeType,size')); + if (strpos((string) $metadata->getMimeType(), 'application/vnd.google-apps.') === 0) { + // Native Google file (Docs/Sheets/Slides/...) has no direct binary content to stream + http_response_code(400); + print 'This file type cannot be downloaded directly, open it in Google Drive instead'; + exit; + } + + $response = $driveservice->files->get($fileid, array('alt' => 'media')); + $content = $response->getBody()->getContents(); + + top_httphead($metadata->getMimeType() ? $metadata->getMimeType() : 'application/octet-stream'); + header('Content-Disposition: attachment; filename="'.dol_sanitizeFileName($metadata->getName()).'"'); + header('Content-Length: '.strlen($content)); + print $content; +} catch (Exception $e) { + dol_syslog('ecmgoogledrivedownload: '.$e->getMessage(), LOG_ERR); + http_response_code(500); + print dol_escape_htmltag($e->getMessage()); +} diff --git a/core/ajax/ecmgoogledrivelist.php b/core/ajax/ecmgoogledrivelist.php new file mode 100644 index 0000000..b40f185 --- /dev/null +++ b/core/ajax/ecmgoogledrivelist.php @@ -0,0 +1,133 @@ + + * + * 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 . + */ + +/** + * \file googleapi/core/ajax/ecmgoogledrivelist.php + * \ingroup googleapi + * \brief Returns the HTML table listing the content (folders and files) of a Google Drive folder + */ + +$defines = [ + 'NOTOKENRENEWAL', + 'NOREQUIREMENU', + 'NOREQUIREHTML', + 'NOREQUIREAJAX', +]; + +// Load Dolibarr environment +include '../../config.php'; +require_once '../../lib/googleapi.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + +top_httphead(); + +if (!$user->hasRight('googleapi', 'read')) { + accessforbidden(); +} + +$permissiontowrite = $user->hasRight('googleapi', 'write'); +$permissiontodelete = $user->hasRight('googleapi', 'delete'); + +$dir = GETPOST('dir', 'alpha'); +$parentid = ($dir == '' ? 'root' : $dir); + +$langs->loadLangs(array('googleapi@googleapi')); + +$driveservice = getGoogleDriveService($user); + +print ''."\n"; +print ''; +print ''; +print ''; +print ''; +print ''; +print ''."\n"; + +if (is_object($driveservice)) { + try { + $query = "'".googleapiDriveEscapeId($parentid)."' in parents and trashed=false"; + $result = $driveservice->files->listFiles(array( + 'q' => $query, + 'fields' => 'files(id,name,mimeType,size,modifiedTime,webViewLink)', + 'orderBy' => 'folder,name', + 'pageSize' => 1000, + )); + + foreach ($result->getFiles() as $file) { + $isfolder = ($file->getMimeType() == 'application/vnd.google-apps.folder'); + $isnativegoogletype = (!$isfolder && strpos((string) $file->getMimeType(), 'application/vnd.google-apps.') === 0); + $fileid = $file->getId(); + $filename = $file->getName(); + + print ''; + + print ''; + + print ''; + + print ''; + + print ''; + + print ''."\n"; + } + } catch (Exception $e) { + print ''."\n"; + dol_syslog('ecmgoogledrivelist: '.$e->getMessage(), LOG_ERR); + } +} + +print '
'.$langs->trans("Name").''.$langs->trans("Size").''.$langs->trans("DateModification").'
'; + if ($isfolder) { + print img_picto('', 'folder', 'class="paddingright"'); + // Drive names are attacker-controlled. dol_escape_js() alone is not enough inside an HTML + // attribute: the browser HTML-decodes the attribute before running it as JS, so an entity + // such as ' would become a real quote and break out of the JS string. The whole handler + // is therefore also HTML-escaped. $escapeonlyhtmltags=1 is required: the default mode of + // dol_escape_htmltag() re-emits the literal sequence "'" untouched. + $onclick = "ecmGoogleDriveNavigate('".dol_escape_js($fileid)."', '".dol_escape_js($filename)."'); return false;"; + print ''; + print dol_escape_htmltag($filename); + print ''; + } else { + print img_mime($filename); + print dol_escape_htmltag($filename); + } + print ''; + if (!$isfolder && $file->getSize()) { + print dol_print_size((int) $file->getSize(), 1); + } + print ''; + if ($file->getModifiedTime()) { + print dol_print_date(strtotime((string) $file->getModifiedTime()), 'dayhour'); + } + print ''; + if (!$isfolder && !$isnativegoogletype) { + $downloadurl = dol_buildpath('/googleapi/core/ajax/ecmgoogledrivedownload.php', 1).'?token='.currentToken().'&fileid='.urlencode($fileid); + print ''.img_picto($langs->trans("Download"), 'download').''; + } elseif ($isnativegoogletype && $file->getWebViewLink()) { + print ''.img_picto($langs->trans("GoogleApiOpenInDrive"), 'globe').''; + } + if ($permissiontowrite) { + $onclick = "ecmGoogleDriveRename('".dol_escape_js($fileid)."', '".dol_escape_js($filename)."'); return false;"; + print ' '.img_picto($langs->trans("GoogleApiRename"), 'edit').''; + } + if ($permissiontodelete) { + $onclick = "ecmGoogleDriveDelete('".dol_escape_js($fileid)."', '".dol_escape_js($filename)."'); return false;"; + print ' '.img_picto($langs->trans("Delete"), 'delete').''; + } + print '
'.dol_escape_htmltag($e->getMessage()).'
'."\n"; diff --git a/core/ajax/ecmgoogledrivetree.php b/core/ajax/ecmgoogledrivetree.php new file mode 100644 index 0000000..16a26bd --- /dev/null +++ b/core/ajax/ecmgoogledrivetree.php @@ -0,0 +1,75 @@ + + * + * 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 . + */ + +/** + * \file googleapi/core/ajax/ecmgoogledrivetree.php + * \ingroup googleapi + * \brief Returns the sub-folders of a Google Drive folder, for the jqueryFileTree plugin + */ + +$defines = [ + 'NOTOKENRENEWAL', + 'NOREQUIREMENU', + 'NOREQUIREHTML', + 'NOREQUIREAJAX', +]; + +// Load Dolibarr environment +include '../../config.php'; +require_once '../../lib/googleapi.lib.php'; + +top_httphead(); + +if (!$user->hasRight('googleapi', 'read')) { + accessforbidden(); +} + +$dir = GETPOST('dir', 'alpha'); +$parentid = trim((string) $dir, '/'); +if ($parentid == '') { + $parentid = 'root'; +} + +$driveservice = getGoogleDriveService($user); + +print ''."\n"; diff --git a/core/modules/modGoogleApi.class.php b/core/modules/modGoogleApi.class.php index 60dabbb..3b3f42e 100644 --- a/core/modules/modGoogleApi.class.php +++ b/core/modules/modGoogleApi.class.php @@ -71,7 +71,7 @@ public function __construct($db) // Possible values for version are: 'development', 'experimental', 'dolibarr', // 'dolibarr_deprecated' or a version string like 'x.y.z' - $this->version = '1.0.2'; + $this->version = '1.1.0'; // Url to the file with your last numberversion of this module $this->url_last_version = 'https://wiki.netlogic.fr/versionmodule.php?module=googleapi'; @@ -174,7 +174,9 @@ public function __construct($db) } // Array to add new pages in new tabs - $this->tabs = []; + $this->tabs = [ + array('data' => 'ecm:+googledrive:GoogleApiDriveTab:googleapi@googleapi:$user->rights->googleapi->read:/googleapi/ecmgoogledrive.php'), + ]; // Dictionaries $this->dictionaries = []; @@ -194,9 +196,9 @@ public function __construct($db) 'method' => 'checkExpiredSheduledWatch', 'parameters' => '', 'comment' => 'Crée si nécessaire les notifications push de GoogleApi', - 'frequency' => 12, + 'frequency' => 1, 'unitfrequency' => 3600, - 'status' => 0, + 'status' => 1, 'test' => true, ], ]; @@ -207,7 +209,7 @@ public function __construct($db) // Permission id (must not be already used) $this->rights[$r][0] = $this->numero + $r; // Permission label - $this->rights[$r][1] = 'Read myobject of Googleapi'; + $this->rights[$r][1] = 'Read object of Googleapi'; // Permission by default for new user (0/1) $this->rights[$r][3] = 1; // In php code, permission will be checked by test if ($user->rights->googleapi->level1->level2) @@ -217,14 +219,14 @@ public function __construct($db) $r++; $this->rights[$r][0] = $this->numero + $r; - $this->rights[$r][1] = 'Create/Update myobject of Googleapi'; + $this->rights[$r][1] = 'Create/Update object of Googleapi'; $this->rights[$r][3] = 1; $this->rights[$r][4] = 'write'; $this->rights[$r][5] = ''; $r++; $this->rights[$r][0] = $this->numero + $r; - $this->rights[$r][1] = 'Delete myobject of Googleapi'; + $this->rights[$r][1] = 'Delete object of Googleapi'; $this->rights[$r][3] = 1; $this->rights[$r][4] = 'delete'; $this->rights[$r][5] = ''; @@ -325,6 +327,27 @@ public function init($options = '') 'googleapi@googleapi', 'isModEnabled("googleapi")' ); + // ecmfiles + $result = $extrafields->addExtraField( + 'googleapiId', + 'GoogleApiIdId', + 'varchar', + $this->numero, + 180, + 'ecmfiles', + 0, + 0, + '', + '', + 1, + '', + 'getDolGloblaInt("GOOGLEAPI_ENABLE_EXTRAFIELDS_DEBUG") ? 3:0', + 0, + '', + '', + 'googleapi@googleapi', + 'isModEnabled("googleapi")' + ); $sql = []; diff --git a/core/modules/oauth/googleapi_oauthcallback.php b/core/modules/oauth/googleapi_oauthcallback.php index b24a952..77a7740 100644 --- a/core/modules/oauth/googleapi_oauthcallback.php +++ b/core/modules/oauth/googleapi_oauthcallback.php @@ -81,8 +81,8 @@ 'https://www.googleapis.com/auth/contacts', ]; // https://developers.google.com/identity/protocols/oauth2/scopes#docs - // $scopes[] = 'https://www.googleapis.com/auth/documents'; - // $scopes[] = 'https://www.googleapis.com/auth/drive'; + $scopes[] = 'https://www.googleapis.com/auth/documents'; + $scopes[] = 'https://www.googleapis.com/auth/drive'; // $scopes[] = 'https://www.googleapis.com/auth/spreadsheets'; $authUrl = $provider->getAuthorizationUrl([ 'prompt' => 'consent', @@ -127,14 +127,6 @@ unset($_SESSION["backtourlsavedbeforeoauthjump"]); header('Location: ' . $backtourl); + $db->close(); exit(); } - - -/* - * View - */ - -// No view at all, just actions - -$db->close(); diff --git a/core/triggers/interface_99_modGoogleApi_GoogleApiTriggers.class.php b/core/triggers/interface_99_modGoogleApi_GoogleApiTriggers.class.php index 270f627..fe71f01 100644 --- a/core/triggers/interface_99_modGoogleApi_GoogleApiTriggers.class.php +++ b/core/triggers/interface_99_modGoogleApi_GoogleApiTriggers.class.php @@ -119,7 +119,7 @@ public function runTrigger($action, $object, User $user, Translate $langs, Conf // object comes from api googleapi return 0; } - if ($object instanceof Contact && !empty($object->context['googleapi'])) { + if (get_class($object) == 'Contact' && !empty($object->context['googleapi'])) { // object comes from api googleapi return 0; } diff --git a/ecmgoogledrive.php b/ecmgoogledrive.php new file mode 100644 index 0000000..034fcb8 --- /dev/null +++ b/ecmgoogledrive.php @@ -0,0 +1,155 @@ + + * + * 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 . + */ + +/** + * \file googleapi/ecmgoogledrive.php + * \ingroup googleapi + * \brief ECM tab: browse and manage the connected user's Google Drive + */ + +// Load Dolibarr environment +include 'config.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/ecm.lib.php'; +require_once 'lib/googleapi.lib.php'; + +/** + * @var Conf $conf + * @var DoliDB $db + * @var Translate $langs + * @var User $user + */ + +// Load translation files required by the page +$langs->loadLangs(array('ecm', 'googleapi@googleapi')); + +if (!$user->hasRight('googleapi', 'read')) { + accessforbidden(); +} + +$action = GETPOST('action', 'aZ09'); + +$permissiontowrite = $user->hasRight('googleapi', 'write'); +$permissiontodelete = $user->hasRight('googleapi', 'delete'); + +$driveservice = getGoogleDriveService($user); + +/* + * Actions + */ + +if ($action == 'upload' && $permissiontowrite) { + if (!empty($_FILES['userfile']['tmp_name']) && is_uploaded_file($_FILES['userfile']['tmp_name'])) { + $parentid = GETPOST('folderid', 'alpha') ? GETPOST('folderid', 'alpha') : 'root'; + if (is_object($driveservice)) { + try { + $drivefile = new \Google\Service\Drive\DriveFile(); + $drivefile->setName($_FILES['userfile']['name']); + $drivefile->setParents(array($parentid)); + $driveservice->files->create($drivefile, array( + 'data' => file_get_contents($_FILES['userfile']['tmp_name']), + 'mimeType' => $_FILES['userfile']['type'] ? $_FILES['userfile']['type'] : 'application/octet-stream', + 'uploadType' => 'multipart', + )); + setEventMessages($langs->trans("GoogleApiDriveFileUploaded"), null, 'mesgs'); + } catch (Exception $e) { + setEventMessages($langs->trans("GoogleApiErrorDriveApi", $e->getMessage()), null, 'errors'); + } + } + } else { + setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("File")), null, 'errors'); + } +} + +if ($action == 'renamedrivefile' && $permissiontowrite) { + $fileid = GETPOST('fileid', 'alpha'); + $newname = GETPOST('newname', 'alphanohtml'); + if ($fileid && $newname && is_object($driveservice)) { + try { + $drivefile = new \Google\Service\Drive\DriveFile(); + $drivefile->setName($newname); + $driveservice->files->update($fileid, $drivefile); + setEventMessages($langs->trans("GoogleApiDriveFileRenamed"), null, 'mesgs'); + } catch (Exception $e) { + setEventMessages($langs->trans("GoogleApiErrorDriveApi", $e->getMessage()), null, 'errors'); + } + } +} + +if ($action == 'deletedrivefile' && $permissiontodelete) { + $fileid = GETPOST('fileid', 'alpha'); + if ($fileid && is_object($driveservice)) { + try { + $drivefile = new \Google\Service\Drive\DriveFile(); + $drivefile->setTrashed(true); + $driveservice->files->update($fileid, $drivefile); + setEventMessages($langs->trans("GoogleApiDriveFileDeleted"), null, 'mesgs'); + } catch (Exception $e) { + setEventMessages($langs->trans("GoogleApiErrorDriveApi", $e->getMessage()), null, 'errors'); + } + } +} + +/* + * View + */ + +// The jqueryFileTree plugin is not loaded globally by Dolibarr core: each page using it must load it +// itself (same as htdocs/ecm/index.php does). It must come before our own JS, which calls .fileTree(). +$morejs = array( + 'public/includes/jquery/plugins/jqueryFileTree/jqueryFileTree.js', + '/googleapi/js/ecmgoogledrive.js.php', +); + +llxHeader('', $langs->trans("ECMArea"), '', '', 0, 0, $morejs, '', '', 'mod-googleapi page-ecmgoogledrive'); + +$head = ecm_prepare_dasboard_head(); + +print dol_get_fiche_head($head, 'googledrive', '', -1, ''); + +if (!is_object($driveservice)) { + print '
'.$langs->trans("GoogleApiDriveNotConnected").'

'."\n"; + $urltoconnect = dol_buildpath('/googleapi/core/modules/oauth/googleapi_oauthcallback.php', 1).'?backtourl='.urlencode(dol_buildpath('/googleapi/ecmgoogledrive.php', 1)); + print ''.$langs->trans("GoogleApiConnectDrive").''."\n"; +} else { + print '
'."\n"; + print '
'."\n"; + print '
'."\n"; + print '
'."\n"; + print '
'."\n"; + print '
'."\n"; + if ($permissiontowrite) { + print ''."\n"; + print ''."\n"; + print ''."\n"; + print ''."\n"; + print ''."\n"; + print ''."\n"; + print ''."\n"; + } else { + print ''."\n"; + } + print '
'."\n"; + print '
'."\n"; + print '
'."\n"; + print '
'."\n"; +} + +print dol_get_fiche_end(); + +llxFooter(); + +$db->close(); diff --git a/js/ecmgoogledrive.js.php b/js/ecmgoogledrive.js.php new file mode 100644 index 0000000..35c2cfb --- /dev/null +++ b/js/ecmgoogledrive.js.php @@ -0,0 +1,142 @@ + + * + * 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 . + */ + +/** + * \file googleapi/js/ecmgoogledrive.js.php + * \ingroup googleapi + * \brief JS glue for the Google Drive ECM tab + */ + +if (!defined('NOTOKENRENEWAL')) { + define('NOTOKENRENEWAL', 1); +} +if (!defined('NOREQUIREMENU')) { + define('NOREQUIREMENU', '1'); +} +if (!defined('NOREQUIREHTML')) { + define('NOREQUIREHTML', '1'); +} +if (!defined('NOREQUIREAJAX')) { + define('NOREQUIREAJAX', '1'); +} + +include '../config.php'; + +$langs->loadLangs(array('googleapi@googleapi')); + +top_httphead('application/javascript'); +?> +var ecmGoogleDriveBreadcrumb = [{id: 'root', name: 'trans("Home")); ?>'}]; + +/** + * Escape a string so it can safely be concatenated into an HTML fragment that is later + * injected with .html(). Drive file and folder names are attacker-controlled: anybody + * sharing a folder with the connected account chooses its name. + */ +function ecmGoogleDriveEscapeHtml(str) +{ + return jQuery('
').text(str === null || typeof str === 'undefined' ? '' : str).html(); +} + +function ecmGoogleDriveRenderBreadcrumb() +{ + var html = ''; + for (var i = 0; i < ecmGoogleDriveBreadcrumb.length; i++) { + if (i > 0) { + html += ' / '; + } + html += ''+ecmGoogleDriveEscapeHtml(ecmGoogleDriveBreadcrumb[i].name)+''; + } + jQuery('#ecmgdrive-breadcrumb').html(html); +} + +function ecmGoogleDriveGoToBreadcrumb(index) +{ + var entry = ecmGoogleDriveBreadcrumb[index]; + ecmGoogleDriveBreadcrumb = ecmGoogleDriveBreadcrumb.slice(0, index + 1); + jQuery('#ecmgdrive_folderid').val(entry.id); + ecmGoogleDriveLoadList(entry.id); +} + +function ecmGoogleDriveNavigate(folderid, foldername) +{ + ecmGoogleDriveBreadcrumb.push({id: folderid, name: foldername}); + jQuery('#ecmgdrive_folderid').val(folderid); + ecmGoogleDriveLoadList(folderid); +} + +function ecmGoogleDriveLoadList(folderid) +{ + ecmGoogleDriveRenderBreadcrumb(); + jQuery('#ecmgdrive-filelist').html('trans("PleaseBePatient")); ?>'); + jQuery.get('', {dir: folderid, token: ''}, function(data) { + jQuery('#ecmgdrive-filelist').html(data); + }); +} + +function ecmGoogleDriveRename(fileid, currentname) +{ + var newname = prompt('trans("GoogleApiNewName")); ?>', currentname); + if (newname === null || newname === '' || newname === currentname) { + return; + } + jQuery.post('', { + action: 'renamedrivefile', + token: '', + fileid: fileid, + newname: newname + }, function() { + ecmGoogleDriveLoadList(jQuery('#ecmgdrive_folderid').val()); + }); +} + +function ecmGoogleDriveDelete(fileid, filename) +{ + var msgtemplate = 'trans("GoogleApiConfirmDeleteDriveFile")); ?>'; + if (!confirm(msgtemplate.replace('__FILENAME__', filename))) { + return; + } + jQuery.post('', { + action: 'deletedrivefile', + token: '', + fileid: fileid + }, function() { + ecmGoogleDriveLoadList(jQuery('#ecmgdrive_folderid').val()); + }); +} + +jQuery(document).ready(function() { + // Load the file list first, and isolate the folder tree init: a failure of the jqueryFileTree + // plugin (not loaded, error, ...) must never prevent the file list from being displayed. + ecmGoogleDriveLoadList('root'); + + try { + jQuery('#filetree').fileTree( + { + root: 'root/', + script: '?token=', + folderEvent: 'click', + multiFolder: false + }, + function(file) { + // Files are not shown in the left tree (folders only): nothing to do here. + } + ); + } catch (e) { + console.error('ecmgoogledrive: unable to initialize the folder tree', e); + } +}); diff --git a/langs/en_US/googleapi.lang b/langs/en_US/googleapi.lang index ba2d66b..ce42ff5 100644 --- a/langs/en_US/googleapi.lang +++ b/langs/en_US/googleapi.lang @@ -37,3 +37,16 @@ GoogleApiRemindByNotification=Google Notification Reminder GoogleApiOwnerEmail=Google email of the connected account GoogleApiCalendarTZ=Google Calendar Timezone GoogleApiIdId=Google Resource ID +GoogleApiDriveTab=Google Drive +GoogleApiDriveNotConnected=Your Google account is not connected yet, or the access token has expired. +GoogleApiConnectDrive=Connect to Google Drive +GoogleApiFolder=Folder +GoogleApiRename=Rename +GoogleApiNewName=New name +GoogleApiOpenInDrive=Open in Drive +GoogleApiConfirmDeleteDriveFile=Are you sure you want to move "__FILENAME__" to the Google Drive trash? +GoogleApiUploadToThisFolder=Upload a file into this folder +GoogleApiDriveFileUploaded=File uploaded to Google Drive +GoogleApiDriveFileRenamed=Renamed on Google Drive +GoogleApiDriveFileDeleted=Moved to the Google Drive trash +GoogleApiErrorDriveApi=Google Drive API error: %s diff --git a/langs/fr_FR/googleapi.lang b/langs/fr_FR/googleapi.lang index e4a97f2..fb42a53 100644 --- a/langs/fr_FR/googleapi.lang +++ b/langs/fr_FR/googleapi.lang @@ -47,3 +47,18 @@ GoogleApiOwnerEmail=Email Google du compte connecté GoogleApiRemindByNotification=Rappel par notification Google GoogleApiCalendarTZ=Timezone de l'agenda Google GoogleApiIdId=Google ressource ID +GoogleApiSendContexts = Contextes d'envoi +GoogleApiEnabledContext = Contexte d'envoi %s +GoogleApiDriveTab=Google Drive +GoogleApiDriveNotConnected=Votre compte Google n'est pas encore connecté, ou le jeton d'accès a expiré. +GoogleApiConnectDrive=Se connecter à Google Drive +GoogleApiFolder=Dossier +GoogleApiRename=Renommer +GoogleApiNewName=Nouveau nom +GoogleApiOpenInDrive=Ouvrir dans Drive +GoogleApiConfirmDeleteDriveFile=Voulez-vous vraiment mettre "__FILENAME__" à la corbeille Google Drive ? +GoogleApiUploadToThisFolder=Envoyer un fichier dans ce dossier +GoogleApiDriveFileUploaded=Fichier envoyé sur Google Drive +GoogleApiDriveFileRenamed=Renommé sur Google Drive +GoogleApiDriveFileDeleted=Déplacé vers la corbeille Google Drive +GoogleApiErrorDriveApi=Erreur API Google Drive : %s diff --git a/lib/googleapi.lib.php b/lib/googleapi.lib.php index d60ec8c..18e5675 100644 --- a/lib/googleapi.lib.php +++ b/lib/googleapi.lib.php @@ -68,15 +68,16 @@ function googleapiAdminPrepareHead() /** * Complete $object to change ->label and ->note before pushing event to Google Calendar. * - * @param Object $object Object event to complete + * @param ActionComm $object Object event to complete * @param Translate $langs Language object * @return void */ -function googleapi_complete_label_and_note(&$object, $langs) +function googleapi_complete_label_and_note($object, $langs) { global $conf, $db, $langs; global $dolibarr_main_url_root; + $langs->load('googleapi@googleapi'); $eventlabel = trim($object->label); // Define $urlwithroot $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_URL_ROOT, '/') . '$/i', '', trim($dolibarr_main_url_root)); @@ -165,9 +166,9 @@ function getGoogleApiClient($fuser) ]); $token = retrieveAccessToken('GoogleApi', $fuser->id); - // Is token expired or will token expire in the next 30 seconds + // Is token expired or will token expire in the next 60 seconds if (is_object($token)) { - $expire = time() > ($token->getExpires() - 30); + $expire = time() > ($token->getExpires() - 60); if ($expire) { try { // il faut sauvegarder le refresh token car google ne le donne qu'une seule fois @@ -180,6 +181,8 @@ function getGoogleApiClient($fuser) $token = $provider->getAccessToken($grant, ['refresh_token' => $refreshtoken]); //$token->setRefreshToken($refreshtoken); storeAccessToken('GoogleApi', $token, $refreshtoken, $fuser->id); + } catch (Throwable $t) { + dol_syslog($t->getMessage(), LOG_ERR); } catch (Exception $e) { dol_syslog($e->getMessage(), LOG_WARNING); } @@ -191,6 +194,32 @@ function getGoogleApiClient($fuser) return $client; } +/** + * Get an authenticated Google Drive service for a user's connected Google account + * + * @param User $fuser User owning the Google OAuth token + * @return \Google\Service\Drive|false Drive service, or false if the user has no valid token + */ +function getGoogleDriveService($fuser) +{ + $client = getGoogleApiClient($fuser); + if (!is_object($client)) { + return false; + } + return new \Google\Service\Drive($client); +} + +/** + * Escape a Google Drive object id for safe use inside a Drive API 'q' query string + * + * @param string $id Drive file or folder id + * @return string Escaped id + */ +function googleapiDriveEscapeId($id) +{ + return str_replace("'", "\\'", (string) $id); +} + /** * Create agenda event from task * diff --git a/notifications.php b/notifications.php index 0093ede..61cf1c5 100644 --- a/notifications.php +++ b/notifications.php @@ -36,6 +36,7 @@ // Load Dolibarr environment include 'config.php'; +require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php'; dol_include_once('/prune/vendor/autoload.php'); dol_include_once('/googleapi/lib/googleapi.lib.php'); @@ -203,8 +204,16 @@ $evt->context['googleapi'] = $db->escape($item->getId()); $res = $evt->update($fuser, 0); if ($res < 0) { - dol_syslog("googleapi notifications update actioncomm " . $evt->error, LOG_ERR); + dol_syslog("googleapi notifications update actioncomm FAILED " . $evt->error, LOG_ERR); } else { + $evt->loadReminders('', 0, false); + foreach ($evt->reminders as $reminder) { + if ($reminder->status != 0) { + continue; + } + $reminder->dateremind = dol_time_plus_duree($evt->datep, -$reminder->offsetvalue, $reminder->offsetunit); + $reminder->update($fuser); + } dol_syslog("googleapi notifications update actioncomm OK", LOG_NOTICE); } } diff --git a/sql/update_0.0.0.-1.0.0.sql b/sql/update_0.0.0.-1.0.0.sql index 23a4ed0..262a273 100644 --- a/sql/update_0.0.0.-1.0.0.sql +++ b/sql/update_0.0.0.-1.0.0.sql @@ -15,5 +15,5 @@ -- TO FIX PHP WARNINGS UPDATE llx_extrafields SET enabled='isModEnabled("googleapi")' WHERE enabled='!empty($conf->googleapi->enabled)'; -UPDATE llx_extrafields set enabled = 'isModEnabled("googleapi")' WHERE enabled = '$conf->googleapi->enabled'; +UPDATE llx_extrafields SET enabled = 'isModEnabled("googleapi")' WHERE enabled = '$conf->googleapi->enabled'; UPDATE llx_c_actioncomm SET type='module', module='googleapi@googleapi' WHERE type='system' AND module='googleapi'; diff --git a/tabs/googleapi_emails.php b/tabs/googleapi_emails.php index c44253c..8ade873 100644 --- a/tabs/googleapi_emails.php +++ b/tabs/googleapi_emails.php @@ -46,7 +46,7 @@ $toselect = GETPOST('toselect', 'array'); // Array of ids of elements selected into a list $contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'googleapilist'; // To manage different context of search $backtopage = GETPOST('backtopage', 'alpha'); // Go back to a dedicated page -$optioncss = GETPOST('optioncss', 'aZ'); // Option for the css output (always '' except when 'print') +$optioncss = GETPOST('optioncss', 'aZ'); // Option for the css output (always '' except when 'print') $id = (int) GETPOST('id', 'int'); // for member diff --git a/tabs/usertoken.php b/tabs/usertoken.php index 47b72b1..10a67bf 100644 --- a/tabs/usertoken.php +++ b/tabs/usertoken.php @@ -25,7 +25,10 @@ // Load Dolibarr environment include '../config.php'; - +/** + * @var Translate $langs + * @var string $dolibarr_main_url_root + */ require_once DOL_DOCUMENT_ROOT . '/core/lib/admin.lib.php'; require_once DOL_DOCUMENT_ROOT . '/core/lib/usergroups.lib.php'; require_once DOL_DOCUMENT_ROOT . '/user/class/user.class.php'; @@ -175,10 +178,10 @@ $refreshtoken = false; $expiredat = ''; -// Is token expired or will token expire in the next 30 seconds +// Is token expired or will token expire in the next 60 seconds if (is_object($token)) { $expire = $token->hasExpired(); - $isgoingtoexpire = (time() > ($token->getExpires() - 30)); + $isgoingtoexpire = (time() > ($token->getExpires() - 60)); if ($isgoingtoexpire) { $provider = new Google([ 'clientId' => getDolGlobalString('OAUTH_GOOGLEAPI_ID'), @@ -186,10 +189,17 @@ 'redirectUri' => dol_buildpath('/googleapi/core/modules/oauth/googleapi_oauthcallback.php', 2), ]); $grant = new RefreshToken(); - $token = $provider->getAccessToken($grant, ['refresh_token' => $tokenrefreshbackup]); - $expire = $token->hasExpired(); - storeAccessToken('GoogleApi', $token, $tokenrefreshbackup, $object->id); - setEventMessages($langs->trans('NewTokenStored'), null, 'mesgs'); // Stored into object managed by class DoliStorage so into table oauth_token + try { + $token = $provider->getAccessToken($grant, ['refresh_token' => $tokenrefreshbackup]); + $expire = $token->hasExpired(); + storeAccessToken('GoogleApi', $token, $tokenrefreshbackup, $object->id); + setEventMessages($langs->trans('NewTokenStored'), null, 'mesgs'); // Stored into object managed by class DoliStorage so into table oauth_token + } catch (Throwable $t) { + dol_syslog($t->getMessage(), LOG_ERR); + } catch (Exception $e) { + dol_syslog($e->getMessage(), LOG_WARNING); + setEventMessage($e->getMessage()); + } } $refreshtoken = $token->getRefreshToken(); @@ -309,9 +319,11 @@ // 'hostedDomain' => 'example.com', // optional; used to restrict access to users on your G Suite/Google Apps for Business accounts 'accessType' => 'offline', ]); - - $owner = $provider->getResourceOwner($token); - // var_dump($owner->toArray()); + try { + $owner = $provider->getResourceOwner($token); + } catch (Exception $e) { + setEventMessage($e->getMessage()); + } if (!empty($owner)) { $object->array_options['options_googleapi_Id'] = $owner->getId(); $object->array_options['options_googleapi_email'] = $owner->toArray()['email'];