diff --git a/Api/V8/Config/services/middlewares.php b/Api/V8/Config/services/middlewares.php index d932173f6..5af7a7f65 100644 --- a/Api/V8/Config/services/middlewares.php +++ b/Api/V8/Config/services/middlewares.php @@ -9,6 +9,7 @@ use Api\V8\OAuth2\Repository\ClientRepository; use Api\V8\OAuth2\Repository\RefreshTokenRepository; use Api\V8\OAuth2\Repository\ScopeRepository; use Api\V8\OAuth2\Repository\UserRepository; +use League\OAuth2\Server\Grant\ClientCredentialsGrant; use Psr\Container\ContainerInterface as Container; use League\OAuth2\Server\AuthorizationServer; use League\OAuth2\Server\Grant\PasswordGrant; @@ -19,7 +20,7 @@ use Api\V8\Helper\OsHelper; use League\OAuth2\Server\CryptKey; return CustomLoader::mergeCustomArray([ - AuthorizationServer::class => function (Container $container) { + AuthorizationServer::class => static function (Container $container) { // base dir must exist in entryPoint.php $baseDir = $GLOBALS['BASE_DIR']; @@ -41,7 +42,7 @@ return CustomLoader::mergeCustomArray([ $shouldCheckPermissions ), new CryptKey( - sprintf('file://%s/%s', $baseDir, ApiConfig::OAUTH2_PRIVATE_KEY), + sprintf('file://%s/%s', $baseDir, ApiConfig::OAUTH2_PUBLIC_KEY), null, $shouldCheckPermissions ) @@ -60,8 +61,8 @@ return CustomLoader::mergeCustomArray([ // Client credentials grant $server->enableGrantType( - new \League\OAuth2\Server\Grant\ClientCredentialsGrant(), - new \DateInterval('PT1H') + new ClientCredentialsGrant(), + new DateInterval('PT1H') ); // Password credentials grant @@ -70,31 +71,37 @@ return CustomLoader::mergeCustomArray([ new UserRepository($container->get(BeanManager::class)), new RefreshTokenRepository($container->get(BeanManager::class)) ), - new \DateInterval('PT1H') + new DateInterval('PT1H') ); $refreshGrant = new RefreshTokenGrant( new RefreshTokenRepository($container->get(BeanManager::class)) ); - $refreshGrant->setRefreshTokenTTL(new \DateInterval('P1M')); + $refreshGrant->setRefreshTokenTTL(new DateInterval('P1M')); $server->enableGrantType( $refreshGrant, - new \DateInterval('PT1H') + new DateInterval('PT1H') ); return $server; }, - ResourceServer::class => function (Container $container) { + ResourceServer::class => static function (Container $container) { $baseDir = $GLOBALS['BASE_DIR']; + $shouldCheckPermissions = OsHelper::getOS() !== OsHelper::OS_WINDOWS; + return new ResourceServer( new AccessTokenRepository( new AccessTokenEntity(), $container->get(BeanManager::class) ), - sprintf('file://%s/%s', $baseDir, ApiConfig::OAUTH2_PUBLIC_KEY) + new CryptKey( + sprintf('file://%s/%s', $baseDir, ApiConfig::OAUTH2_PUBLIC_KEY), + null, + $shouldCheckPermissions + ) ); }, ], basename(__FILE__)); diff --git a/data/SugarBean.php b/data/SugarBean.php index cd3636597..4372b2814 100755 --- a/data/SugarBean.php +++ b/data/SugarBean.php @@ -415,6 +415,11 @@ class SugarBean */ public $old_modified_by_name; + /** + * @var bool $createdAuditRecords + */ + public $createdAuditRecords; + /** * @var SugarBean[][] $line_item_entries */ diff --git a/files.md5 b/files.md5 index 6c720d0ac..70d89f960 100755 --- a/files.md5 +++ b/files.md5 @@ -1,7 +1,6 @@ 'deaa6f875467f423cf0e8e193b552172', './Api/Core/Config/ApiConfig.php' => '69a1e7b3d7755a2a63499a16ddae81cf', './Api/Core/Config/slim.php' => 'b134e68765e6a1403577e2a5a06322b8', './Api/Core/Loader/ContainerLoader.php' => '6d5e0db5708f5e34bec7ba8fb8196bdc', @@ -18,7 +17,7 @@ $md5_string = array ( './Api/V8/Config/services/factories.php' => 'abedc3b0445f9076cf55fb81f922999b', './Api/V8/Config/services/globals.php' => 'd1bdcccf5150b16b84fc8192f63affdd', './Api/V8/Config/services/helpers.php' => 'd15737677999e4f7307443aa83da8afa', - './Api/V8/Config/services/middlewares.php' => '18dde9147c371f54d571ba15e2ed329c', + './Api/V8/Config/services/middlewares.php' => 'a7e3340374c5bfb6b26b4d99e61a8935', './Api/V8/Config/services/params.php' => '9fd77ca190fbcea45ad11f156d5db9b7', './Api/V8/Config/services/services.php' => '1c6d1cc19c087015430b4b965705e2fb', './Api/V8/Config/services/validators.php' => '884d713ad8ed932500039acaf285fa45', @@ -117,7 +116,7 @@ $md5_string = array ( './ModuleInstall/PackageManager/tpls/PackageManagerLicense.tpl' => 'df5e267d1df5ce08fb9406e42d5b4816', './ModuleInstall/PackageManager/tpls/PackageManagerScripts.tpl' => '98e396c0aa57329731fda19c790fffb2', './ModuleInstall/extensions.php' => '87596ad3f28a39c996a5551cad3b5cab', - './README.md' => '32252d95761ee022e09c9a54ddde4d95', + './README.md' => 'f712500fcbf7b512966c0bf3374807ce', './RoboFile.php' => '045b82c1df69553824d0e4ffcce6e03c', './SugarSecurity.php' => '47e316b2d408e8c5192c8ea4a4f921b3', './TreeData.php' => '32873e20cb5fd33f9d1cdaf18c3cac5c', @@ -505,11 +504,9 @@ $md5_string = array ( './Zend/Validate/Interface.php' => 'e313ef824309253dcfab90ff1d38ac86', './Zend/Validate/Ip.php' => 'e313ef824309253dcfab90ff1d38ac86', './Zend/Version.php' => 'e313ef824309253dcfab90ff1d38ac86', - './build/push_output.sh' => 'cde8cd38e3b0c4e988ec4be7d81faa89', - './build/travis-ci-apache' => 'e1e212c4eaf679b6ec620cd0b12f4571', './campaign_tracker.php' => '321e43ca8b664e6ca57ae5589e8c0667', - './composer.json' => 'ec30e8bd48195ff38dd62cbcde73c2a7', - './composer.lock' => '638558a83673b7e29ef0c5d4bf8189d3', + './composer.json' => 'e1f1995de0b0dd645e55494af19c8b0d', + './composer.lock' => '2319e305117e2685d966bf0fe98fd6ae', './cron.php' => '0b8b6bd839a2232a8da074b31feaa708', './crossdomain.xml' => '24b7711640c652b21aa89c9d83d6ec13', './data/BeanFactory.php' => '84b7c36b6a59ea8c5c4069659cc72950', @@ -523,7 +520,7 @@ $md5_string = array ( './data/Relationships/One2OneRelationship.php' => 'c46d3067d5651fbc928763600d5e1a51', './data/Relationships/RelationshipFactory.php' => '98a46e44186f2d2db23be9b894a4f1e2', './data/Relationships/SugarRelationship.php' => 'a71b96492ee7457826fc91a2356c4ebd', - './data/SugarBean.php' => 'd4f57253f79507904f9eacc6502dde53', + './data/SugarBean.php' => 'd90992bfdf9e5aa73d0324d414d57f3c', './deprecated.php' => 'f5f507fd6314f38d29c97e2cc2c62239', './dictionary.php' => 'b7c1370fb75a2940c04db74627c4462c', './download.php' => 'f2d366039d134ac463ff1e75634ce509', @@ -570,10 +567,10 @@ $md5_string = array ( './include/ErrorMessageException.php' => 'cbb52742f39a2b77a951889b571c6e0f', './include/Exceptions/SugarControllerException.php' => '4fde226677e926620e90e83f0ec761f2', './include/Exceptions/SuiteException.php' => '83c2319718dcdee178eda29b56e375b1', - './include/GoogleSync/GoogleSync.php' => '08230174ff7ea894ef16846c46aeb500', - './include/GoogleSync/GoogleSyncBase.php' => 'e57b11431c5e49f71b2e7d2e5dee1832', + './include/GoogleSync/GoogleSync.php' => '957fdac94e2446dc1b69f6e669a406fe', + './include/GoogleSync/GoogleSyncBase.php' => 'b8a6e57f6e7df10eb22b4460d20e7991', './include/GoogleSync/GoogleSyncExceptions.php' => '34218995be6d57d316d17e97064f994f', - './include/GoogleSync/GoogleSyncHelper.php' => '938e195cf4251ee30b31a4674a72a654', + './include/GoogleSync/GoogleSyncHelper.php' => 'd7519e89f6ecb6143db97e8bd7775e95', './include/GroupedTabs/GroupedTabStructure.php' => 'f6fdcc2242695b666e2a1b67318ddd70', './include/HTMLPurifier/CREDITS' => 'a5f9ddecea015543001404c0a9bfd181', './include/HTMLPurifier/HTMLPurifier.autoload.php' => '4b4731b18fc20cf22a3524093b122278', @@ -2727,7 +2724,7 @@ $md5_string = array ( './install/installSystemCheck.php' => 'd5f666ece13475386610cb7915ae3c93', './install/installType.php' => '7acf23c138ceafad2ce0393bd3c116f0', './install/install_defaults.php' => 'd25503407f0db14fa875b295d0f34ae5', - './install/install_utils.php' => '8fb3fc994c368eecd4c875bf0dee4479', + './install/install_utils.php' => '640b6ec4a434023fbb8422e3d4fcbd83', './install/lang.config.php' => 'cb3e68fdb0600481497dcd60f0746aca', './install/language/en_us.lang.php' => '443a21c6c24b089d6dc085a3ebb588d8', './install/license.js' => '9b5c798584a7ae54703dcfa2d1bb991f', @@ -4958,7 +4955,7 @@ $md5_string = array ( './modules/Emails/include/ComposeView/ComposeView.tpl' => '1ec29fbb9803c24e5ed8a12155668e58', './modules/Emails/include/ComposeView/ComposeViewBlank.tpl' => '22365ce6727ffb560e5ad3fc187f13f2', './modules/Emails/include/ComposeView/ComposeViewToolbar.tpl' => '656b26827857375278124e4610b9ff06', - './modules/Emails/include/ComposeView/EmailsComposeView.js' => '4a493e1cefdac676a0988051d45ca3dd', + './modules/Emails/include/ComposeView/EmailsComposeView.js' => '5c836fdc4d36bb39c6b027f13f9724db', './modules/Emails/include/DetailView/EmailsDetailView.php' => 'f95937f398f37afe3927ecc035b8b743', './modules/Emails/include/DetailView/EmailsDraftDetailView.php' => 'baafca815e89a4c0ec3df8b6192552ea', './modules/Emails/include/DetailView/EmailsNonImportedDetailView.php' => 'ef9ecbcf65fa6f03e32f94b6a9805b2f', @@ -5256,7 +5253,7 @@ $md5_string = array ( './modules/Import/Importer.php' => 'c4bf3967c463f929acff62fa541cc664', './modules/Import/Menu.php' => '776e6242c638410abd3290c9387e134e', './modules/Import/UsersLastImport.php' => 'a1c22f45aa62094045f32acbcba0ba8d', - './modules/Import/controller.php' => '84c58a01c2fcff75550e1f90d3dffe9d', + './modules/Import/controller.php' => '461f26cd01bc94f0fa0f17608183120b', './modules/Import/language/en_us.lang.php' => '41328cd1de165898134141618e777774', './modules/Import/maps/ImportMap.php' => 'f8a79c733d4ec686203476e5930c0670', './modules/Import/maps/ImportMapAct.php' => '15401c409712de8a08e3dfc7f95df8a0', @@ -5291,7 +5288,7 @@ $md5_string = array ( './modules/Import/views/view.last.php' => '7c4d08eb99783e31971a4a641d531a50', './modules/Import/views/view.step1.php' => '7f515a1fc6c2c182c24d6af3c65d73b3', './modules/Import/views/view.step2.php' => '30d49b03050d05f5605dc935ea87fd9a', - './modules/Import/views/view.step3.php' => 'a0e152da156d05178d33052f0780495a', + './modules/Import/views/view.step3.php' => 'cb5d880a6d7268fc416d431ff85983b2', './modules/Import/views/view.step4.php' => '8694e2f07e6ddc9128af648d42aa802d', './modules/Import/views/view.undo.php' => '0f11a824c733c819214ef88f666358b5', './modules/InboundEmail/AOPInboundEmail.php' => '2c74ce41273c1982b221a8862dfcc997', @@ -6013,7 +6010,7 @@ $md5_string = array ( './modules/SecurityGroups/javascript/popup_relate.js' => '7579a87bd17a42988d7cfdb1983061d3', './modules/SecurityGroups/language/en_us.lang.php' => '4b8424175457d63eaefd8d924f86920e', './modules/SecurityGroups/metadata/SearchFields.php' => '96c05b90550f5063a76b0ccf0a27f6bf', - './modules/SecurityGroups/metadata/detailviewdefs.php' => 'c2a93e716003fb32e5e2784ea7983664', + './modules/SecurityGroups/metadata/detailviewdefs.php' => '05a1c1a20a12755f8b1e4b18f0769985', './modules/SecurityGroups/metadata/editviewdefs.php' => '6739d8de7b420f85ff4396f5f7603161', './modules/SecurityGroups/metadata/listviewdefs.php' => 'f2405e3ec285440740c0b030b85a1c66', './modules/SecurityGroups/metadata/metafiles.php' => 'be0931e2262e0459e06038d775203322', @@ -6332,7 +6329,7 @@ $md5_string = array ( './modules/Users/Error.php' => '974cf5b2e9d530a56a38f54295452f12', './modules/Users/Forms.php' => '542f6a00ce7cf53d86c21b24b03c286f', './modules/Users/GeneratePassword.php' => 'c14fa3a7d189159f59c0adf91bf07268', - './modules/Users/GoogleApiKeySaverEntryPoint.php' => '4bcae8920af13ae01cc376f763d673fb', + './modules/Users/GoogleApiKeySaverEntryPoint.php' => '31a2b6771f4b67065a06bf5aa4133ecb', './modules/Users/ListRoles.php' => '470204e9dd0e9421f06f7304cf72e063', './modules/Users/LoggedOut.php' => '651bfc7c6c36f3791c2693bda075beb9', './modules/Users/LoggedOut.tpl' => '9537c8e192b86a038ba3177d9e2fdff1', @@ -6351,7 +6348,7 @@ $md5_string = array ( './modules/Users/SetTimezone.tpl' => 'f0fb5ed64fae81a5657ebc8f167967c9', './modules/Users/UpdateTourStatus.php' => 'cc111e28e6df1d96b98678661dd42490', './modules/Users/User.js' => '430d6a4d4b14300ea4c6c3592601fa6c', - './modules/Users/User.php' => '8b3e002479e07d08376dc8063538e5aa', + './modules/Users/User.php' => '6b94a6e99c975c19d2bd3fdbdec31c94', './modules/Users/UserEditView.js' => 'a5d33c708bf0e30356dfe2945df13704', './modules/Users/UserEmailOptions.tpl' => '96b848efbf7f6d4fee7b6bf13a1a1aee', './modules/Users/UserEmailSettings.tpl' => '5d9ff3379f63dcf7c5efbbcc3e88d8ed', @@ -6379,7 +6376,7 @@ $md5_string = array ( './modules/Users/authentication/SugarAuthenticate/SugarAuthenticate.php' => '4b1d9b735022ae8cf9a1d5ea3515617b', './modules/Users/authentication/SugarAuthenticate/SugarAuthenticateUser.php' => 'c59bb9e71e48bed5bb9322ec9416e7b9', './modules/Users/controller.php' => '65021ed13247766dc3353cdbf8e8dada', - './modules/Users/entryPointSaveGoogleApiKey.php' => '93594685ab60cfa9a47606f9b00f7c9a', + './modules/Users/entryPointSaveGoogleApiKey.php' => 'af2395a659fbe4c935ac555e4f3b592b', './modules/Users/field_arrays.php' => 'b2de6918c313caef59c28582475ab3a6', './modules/Users/googleApiKeySaverEntryPointError.tpl' => '5b45b5ce045c459e3e6c571d43eef873', './modules/Users/language/en_us.lang.php' => '7cf5849bd441e65eb84ae48067c4a47b', @@ -6638,7 +6635,7 @@ $md5_string = array ( './soap.php' => 'e28988c2e0b8e2c484587b537a710525', './sugar_version.json' => 'bdfbcefae2f9af559bef6a36367df7bb', './sugar_version.php' => 'db7b6c8d51f87879fce1e6172eedfbed', - './suitecrm_version.php' => '000d6549775d93be1199f3d48df797e6', + './suitecrm_version.php' => '75f00d095bff2cbfc0640daa1fab3c4c', './themes/SuiteP/css/Dawn/color-palette.scss' => 'f85621a6c8b0cd015a8c4703e83e519b', './themes/SuiteP/css/Dawn/icons.scss' => 'd59f8c5855e7a8df09542a663835a196', './themes/SuiteP/css/Dawn/style.css' => 'a6fcb84280b719f1c3dabe74385553bd', @@ -8043,7 +8040,7 @@ $md5_string = array ( './themes/SuiteP/include/DetailView/header.tpl' => 'ba7fbc5faa2a0e336373aae9b1e52a8e', './themes/SuiteP/include/DetailView/tab_panel_content.tpl' => '38b33c06fc8e5c4d55b5276b457d0330', './themes/SuiteP/include/DetailView/test.tpl' => 'fcf838f4139733066cc727fc7f3818ae', - './themes/SuiteP/include/EditView/EditView.tpl' => '875af492db7774ac8c18b11c3946f634', + './themes/SuiteP/include/EditView/EditView.tpl' => '5e279ffcc56b22f3c2d6032247fa288e', './themes/SuiteP/include/EditView/QuickCreate.tpl' => '3acca81ef6a983731de021c1943f4c0b', './themes/SuiteP/include/EditView/SugarVCR.tpl' => 'eed25c746ed4a7ffeeaaae25a36f49c4', './themes/SuiteP/include/EditView/actions_buttons.tpl' => '3ccfac667a36f3f67706deb6de2f9a77', diff --git a/include/GoogleSync/GoogleSync.php b/include/GoogleSync/GoogleSync.php index c6794e441..63f24a512 100644 --- a/include/GoogleSync/GoogleSync.php +++ b/include/GoogleSync/GoogleSync.php @@ -63,11 +63,11 @@ class GoogleSync extends GoogleSyncBase * Gets the combined titles of a Meeting/Event pair for Logging * * @param Meeting $meeting The CRM Meeting - * @param \Google_Service_Calendar_Event $event The Google Event + * @param \Google\Service\Calendar\Event $event The Google Event * * @return string The combined title */ - protected function getTitle(Meeting $meeting = null, Google_Service_Calendar_Event $event = null) + protected function getTitle(Meeting $meeting = null, Google\Service\Calendar\Event $event = null) { $meetingTitle = isset($meeting) ? $meeting->name : null; $eventTitle = isset($event) ? $event->getSummary() : null; @@ -89,13 +89,13 @@ class GoogleSync extends GoogleSyncBase * * @param string $action The action to take with the two events * @param Meeting $meeting The CRM Meeting - * @param \Google_Service_Calendar_Event $event The Google Event + * @param \Google\Service\Calendar\Event $event The Google Event * * @return bool Success/Failure * @throws GoogleSyncException if $action is invalid. * @throws GoogleSyncException if something else fails. */ - protected function doAction($action, Meeting $meeting = null, Google_Service_Calendar_Event $event = null) + protected function doAction($action, Meeting $meeting = null, Google\Service\Calendar\Event $event = null) { $title = $this->getTitle($meeting, $event); @@ -194,12 +194,12 @@ class GoogleSync extends GoogleSyncBase * Used when an event w/ a matching ID is on both ends of the sync. * At least one of the params is required. * - * @param Meeting|null $meeting (optional) Meeting Bean or Google_Service_Calendar_Event Object - * @param \Google_Service_Calendar_Event|null $event (optional) Google_Service_Calendar_Event Object + * @param Meeting|null $meeting (optional) Meeting Bean or Google\Service\Calendar\Event Object + * @param \Google\Service\Calendar\Event|null $event (optional) Google\Service\Calendar\Event Object * * @return string|bool 'push(_delete)', 'pull(_delete)', 'skip', false (on error) */ - protected function pushPullSkip(Meeting $meeting = null, Google_Service_Calendar_Event $event = null) + protected function pushPullSkip(Meeting $meeting = null, Google\Service\Calendar\Event $event = null) { if (empty($meeting) && empty($event)) { throw new GoogleSyncException('Missing Parameter, You must pass at least one event'); diff --git a/include/GoogleSync/GoogleSyncBase.php b/include/GoogleSync/GoogleSyncBase.php index 0b7900ebb..e95459cb0 100755 --- a/include/GoogleSync/GoogleSyncBase.php +++ b/include/GoogleSync/GoogleSyncBase.php @@ -61,10 +61,10 @@ class GoogleSyncBase /** @var User The SuiteCRM User Bean we're currently working with */ protected $workingUser; - /** @var \Google_Client The Google client object for the current sync job */ + /** @var \Google\Client The Google client object for the current sync job */ protected $gClient; - /** @var \Google_Service_Calendar The Google Calendar Service Object */ + /** @var \Google\Service\Calendar The Google Calendar Service Object */ protected $gService; /** @var array The Google AuthcConfig json */ @@ -158,7 +158,7 @@ class GoogleSyncBase * * @param string $id : the SuiteCRM user id * - * @return \Google_Client|false Google_Client on success. False on failure. + * @return \Google\Client|false Google\Client on success. False on failure. * @throws GoogleSyncException if user invalid, unable to retrive the user, or json error */ protected function getClient($id) @@ -201,9 +201,9 @@ class GoogleSyncBase * New Google Client and refresh the token if needed * * @param array $accessToken - * @return \Google_Client or false on Exception + * @return \Google\Client or false on Exception * @throws GoogleSyncException If the refresh token is missing - * @throws Exception rethrows if caught from Google_Client::fetchAccessTokenWithRefreshToken + * @throws Exception rethrows if caught from Google\Client::fetchAccessTokenWithRefreshToken */ protected function getGoogleClient($accessToken) { @@ -212,9 +212,9 @@ class GoogleSyncBase } // New Google Client - $client = new Google_Client(); + $client = new \Google\Client(); $client->setApplicationName('SuiteCRM'); - $client->setScopes(Google_Service_Calendar::CALENDAR); + $client->setScopes(Google\Service\Calendar::CALENDAR); $client->setAccessType('offline'); $client->setAuthConfig($this->authJson); $client->setAccessToken($accessToken); @@ -349,7 +349,7 @@ class GoogleSyncBase $this->logger->info(__FILE__ . ':' . __LINE__ . ' ' . __METHOD__ . ' - ' . 'Unable to find the SuiteCRM Google Calendar, wiping current sync data & creating it!'); $helper = new GoogleSyncHelper; $helper->wipeLocalSyncData($this->workingUser->id); - $calendar = new Google_Service_Calendar_Calendar(); + $calendar = new \Google\Service\Calendar\Calendar(); $calendar->setSummary('SuiteCRM'); $calendar->setTimeZone($this->timezone); @@ -367,11 +367,11 @@ class GoogleSyncBase /** * find the id of the 'SuiteCRM' calendar ... in the future, this will return the calendar of the users choosing. * - * @param Google_Service_Calendar_CalendarList $calendarList + * @param \Google\Service\Calendar\CalendarList $calendarList * * @return string|null Matching Google Calendar ID or null. */ - protected function getSuiteCRMCalendar(Google_Service_Calendar_CalendarList $calendarList) + protected function getSuiteCRMCalendar(Google\Service\Calendar\CalendarList $calendarList) { foreach ($calendarList->getItems() as $calendarListEntry) { if ($calendarListEntry->getSummary() == 'SuiteCRM') { @@ -386,7 +386,7 @@ class GoogleSyncBase * Get events in users google calendar * * - * @return bool|array Array of Google_Service_Calendar_Event Objects + * @return bool|array Array of Google\Service\Calendar\Event Objects */ protected function getUserGoogleEvents() { @@ -453,7 +453,7 @@ class GoogleSyncBase * * @param string $event_id Google Event ID * - * @return \Google_Service_Calendar_Event|null Google_Service_Calendar_Event if found, null if not found + * @return \Google\Service\Calendar\Event|null Google\Service\Calendar\Event if found, null if not found * @throws GoogleSyncException if $event_id is empty * @throws GoogleSyncException if Google Service not set up */ @@ -528,7 +528,7 @@ class GoogleSyncBase } // create new calendar service - $this->gService = new Google_Service_Calendar($this->gClient); + $this->gService = new \Google\Service\Calendar($this->gClient); if ($this->isServiceExists()) { return true; } @@ -543,20 +543,20 @@ class GoogleSyncBase * be updated. * * @param Meeting $event_local : SuiteCRM Meeting Bean - * @param \Google_Service_Calendar_Event $event_remote (optional) \Google_Service_Calendar_Event Object + * @param \Google\Service\Calendar\Event $event_remote (optional) \Google\Service\Calendar\Event Object * * @return string|bool Meeting Id on success, false on failure */ - protected function pushEvent(Meeting $event_local = null, Google_Service_Calendar_Event $event_remote = null) + protected function pushEvent(Meeting $event_local = null, Google\Service\Calendar\Event $event_remote = null) { if (!$event_local instanceof Meeting) { throw new InvalidArgumentException('Argument 1 passed to GoogleSyncBase::pushEvent() must be an instance of Meeting, ' . getType($event_local) . ' given.'); } - if (!$this->gService instanceof Google_Service_Calendar) { - throw new GoogleSyncException('GooleSyncBase is trying to push event but Google_Service_Calendar_Resource_Events is not set.', GoogleSyncException::NO_GSERVICE_SET); + if (!$this->gService instanceof Google\Service\Calendar) { + throw new GoogleSyncException('GooleSyncBase is trying to push event but Google\Service\Calendar\Resource\Events is not set.', GoogleSyncException::NO_GSERVICE_SET); } - if (!$this->gService->events instanceof Google_Service_Calendar_Resource_Events) { - throw new GoogleSyncException('GooleSyncBase is trying to push event but Google_Service_Calendar_Resource_Events is not set.', GoogleSyncException::NO_GRESOURCE_SET); + if (!$this->gService->events instanceof Google\Service\Calendar\Resource\Events) { + throw new GoogleSyncException('GooleSyncBase is trying to push event but Google\Service\Calendar\Resource\Events is not set.', GoogleSyncException::NO_GRESOURCE_SET); } if (!isset($event_remote) || empty($event_remote)) { @@ -568,7 +568,7 @@ class GoogleSyncBase } /* We don't get a status code back showing success. Instead, the return of the - * create or update is the Google_Service_Calendar_Event object after saving. + * create or update is the Google\Service\Calendar\Event object after saving. * So we check to make sure it has an ID to determine Success/Failure. */ if (!isset($return->id)) { @@ -580,16 +580,16 @@ class GoogleSyncBase } /** - * Helper method to get a Google_Service_Calendar_EventExtendedProperties object for the Google event + * Helper method to get a Google\Service\Calendar\EventExtendedProperties object for the Google event * - * Takes the local and remote events, and returns a Google_Service_Calendar_EventExtendedProperties + * Takes the local and remote events, and returns a Google\Service\Calendar\EventExtendedProperties * - * @param \Google_Service_Calendar_Event $event_remote \Google_Service_Calendar_Event Object + * @param \Google\Service\Calendar\Event $event_remote \Google\Service\Calendar\Event Object * @param Meeting $event_local Meeting (optional) \Meeting Bean * - * @return Google_Service_Calendar_EventExtendedProperties object + * @return Google\Service\Calendar\EventExtendedProperties object */ - protected function returnExtendedProperties(Google_Service_Calendar_Event $event_remote, Meeting $event_local) + protected function returnExtendedProperties(Google\Service\Calendar\Event $event_remote, Meeting $event_local) { // We pull the existing extendedProperties, and change our values // That way we don't mess with anything else that's using other values. @@ -598,7 +598,7 @@ class GoogleSyncBase if (!empty($extendedProperties)) { $private = $extendedProperties->getPrivate(); } elseif (empty($extendedProperties)) { - $extendedProperties = new Google_Service_Calendar_EventExtendedProperties; + $extendedProperties = new Google\Service\Calendar\EventExtendedProperties; $private = array(); } @@ -616,16 +616,16 @@ class GoogleSyncBase * If the SuiteCRM Meeting is not provided, a new one will be created * and inserted. If one is provided, the existing meeting will be updated. * - * @param \Google_Service_Calendar_Event $event_remote \Google_Service_Calendar_Event Object + * @param \Google\Service\Calendar\Event $event_remote \Google\Service\Calendar\Event Object * @param Meeting $event_local Meeting (optional) \Meeting Bean * * @return bool Success/Failure of setLastSync, since that's what saves the record * @throws GoogleSyncException if returned event invalid */ - protected function pullEvent(Google_Service_Calendar_Event $event_remote = null, Meeting $event_local = null) + protected function pullEvent(Google\Service\Calendar\Event $event_remote = null, Meeting $event_local = null) { - if (!$event_remote instanceof Google_Service_Calendar_Event) { - throw new InvalidArgumentException('Argument 1 passed to GoogleSyncBase::pullEvent() must be an instance of Google_Service_Calendar_Event, ' . getType($event_local) . ' given.'); + if (!$event_remote instanceof Google\Service\Calendar\Event) { + throw new InvalidArgumentException('Argument 1 passed to GoogleSyncBase::pullEvent() must be an instance of Google\Service\Calendar\Event, ' . getType($event_local) . ' given.'); } if (!isset($event_local) || empty($event_local)) { @@ -646,7 +646,7 @@ class GoogleSyncBase $greturn = $this->gService->events->update($this->calendarId, $event_remote->getId(), $event_remote); /* We don't get a status code back showing success. Instead, the return of the - * create or update is the Google_Service_Calendar_Event object after saving. + * create or update is the Google\Service\Calendar\Event object after saving. * So we check to make sure it has an ID to determine Success/Failure. */ if (isset($greturn->id)) { @@ -676,7 +676,7 @@ class GoogleSyncBase /** * Delete Google Event * - * @param \Google_Service_Calendar_Event $event \Google_Service_Calendar_Event Object + * @param \Google\Service\Calendar\Event $event \Google\Service\Calendar\Event Object * @param String $meeting_id SuiteCRM Meeting Id * * @return string Meeting Id on success @@ -685,10 +685,10 @@ class GoogleSyncBase * @throws GoogleSyncException If Meeting ID fails validation * @throws GoogleSyncException If delete fails */ - protected function delEvent(Google_Service_Calendar_Event $event = null, $meeting_id = null) + protected function delEvent(Google\Service\Calendar\Event $event = null, $meeting_id = null) { - if (!$event instanceof Google_Service_Calendar_Event) { - throw new InvalidArgumentException('Argument 1 passed to GoogleSyncBase::delEvent() must be an instance of Google_Service_Calendar_Event, ' . gettype($event) . ' given'); + if (!$event instanceof Google\Service\Calendar\Event) { + throw new InvalidArgumentException('Argument 1 passed to GoogleSyncBase::delEvent() must be an instance of Google\Service\Calendar\Event, ' . gettype($event) . ' given'); } // Make sure the calendar service is set up @@ -754,12 +754,12 @@ class GoogleSyncBase * Update SuiteCRM Meeting from Google Calendar Event * * @param Meeting $event_local SuiteCRM Meeting Bean - * @param \Google_Service_Calendar_Event $event_remote Google_Service_Calendar_Event Object + * @param \Google\Service\Calendar\Event $event_remote Google\Service\Calendar\Event Object * * @return Meeting|bool SuiteCRM Meeting Bean or false on failure * @throws GoogleSyncException if the Google Event is missing required data */ - protected function updateSuitecrmMeetingEvent(Meeting $event_local, Google_Service_Calendar_Event $event_remote) + protected function updateSuitecrmMeetingEvent(Meeting $event_local, Google\Service\Calendar\Event $event_remote) { $event_local->name = (string) $event_remote->getSummary(); @@ -775,13 +775,13 @@ class GoogleSyncBase $start = $event_remote->getStart(); if (!$start) { throw new GoogleSyncException( - 'GoogleSyncBase is trying to get "start" as Google_Service_Calendar_EventDateTime but it is not set', + 'GoogleSyncBase is trying to get "start" as Google\Service\Calendar\EventDateTime but it is not set', GoogleSyncException::NO_REMOVE_EVENT_START_IS_NOT_SET ); } - if (!$start instanceof Google_Service_Calendar_EventDateTime) { + if (!$start instanceof Google\Service\Calendar\EventDateTime) { throw new GoogleSyncException( - 'GoogleSyncBase is trying to get "start" as Google_Service_Calendar_EventDateTime but it is incorrect, ' . + 'GoogleSyncBase is trying to get "start" as Google\Service\Calendar\EventDateTime but it is incorrect, ' . gettype($start) . ' given.', GoogleSyncException::NO_REMOVE_EVENT_START_IS_INCORRECT ); @@ -834,12 +834,12 @@ class GoogleSyncBase /** * Create SuiteCRM Meeting event * - * @param \Google_Service_Calendar_Event $event_remote The Google_Service_Calendar_Event we're creating a SuiteCRM Meeting for + * @param \Google\Service\Calendar\Event $event_remote The Google\Service\Calendar\Event we're creating a SuiteCRM Meeting for * * @return Meeting|bool SuiteCRM Meeting Bean or false on failure * @throws GoogleSyncException if fails to retrive meeting */ - protected function createSuitecrmMeetingEvent(Google_Service_Calendar_Event $event_remote) + protected function createSuitecrmMeetingEvent(Google\Service\Calendar\Event $event_remote) { $this->logger->debug(__FILE__ . ':' . __LINE__ . ' ' . __METHOD__ . ' - ' . 'Creating New SuiteCRM Meeting'); $meeting = BeanFactory::getBean('Meetings'); @@ -856,11 +856,11 @@ class GoogleSyncBase * Update Google Calendar Event from SuiteCRM Meeting * * @param Meeting $event_local SuiteCRM Meeting Bean - * @param \Google_Service_Calendar_Event $event_remote Google Event Object + * @param \Google\Service\Calendar\Event $event_remote Google Event Object * - * @return \Google_Service_Calendar_Event + * @return \Google\Service\Calendar\Event */ - protected function updateGoogleCalendarEvent(Meeting $event_local, Google_Service_Calendar_Event $event_remote) + protected function updateGoogleCalendarEvent(Meeting $event_local, Google\Service\Calendar\Event $event_remote) { $event_remote->setSummary($event_local->name); $event_remote->setDescription($event_local->description); @@ -870,12 +870,12 @@ class GoogleSyncBase $localStart = $timedate->to_db($event_local->date_start, false); $localEnd = $timedate->to_db($event_local->date_end, false); - $startDateTime = new Google_Service_Calendar_EventDateTime; + $startDateTime = new \Google\Service\Calendar\EventDateTime; $startDateTime->setDateTime(date(DATE_ATOM, strtotime($localStart . ' UTC'))); $startDateTime->setTimeZone($this->timezone); $event_remote->setStart($startDateTime); - $endDateTime = new Google_Service_Calendar_EventDateTime; + $endDateTime = new \Google\Service\Calendar\EventDateTime; $endDateTime->setDateTime(date(DATE_ATOM, strtotime($localEnd . ' UTC'))); $endDateTime->setTimeZone($this->timezone); $event_remote->setEnd($endDateTime); @@ -893,11 +893,11 @@ class GoogleSyncBase ); if ($reminders_local) { - $reminders_remote = new Google_Service_Calendar_EventReminders; + $reminders_remote = new Google\Service\Calendar\EventReminders; $reminders_remote->setUseDefault(false); $reminders_array = array(); foreach ($reminders_local as $reminder_local) { - $reminder_remote = new Google_Service_Calendar_EventReminder; + $reminder_remote = new Google\Service\Calendar\EventReminder; $reminder_remote->setMethod('popup'); $reminder_remote->setMinutes($reminder_local->timer_popup / 60); $reminders_array[] = $reminder_remote; @@ -913,15 +913,15 @@ class GoogleSyncBase * * @param Meeting $event_local SuiteCRM Meeting Bean * - * @return \Google_Service_Calendar_Event Google_Service_Calendar_Event Object + * @return \Google\Service\Calendar\Event Google\Service\Calendar\Event Object */ protected function createGoogleCalendarEvent(Meeting $event_local) { //We're creating a new event - $event_remote_empty = new Google_Service_Calendar_Event; + $event_remote_empty = new Google\Service\Calendar\Event; - $extendedProperties = new Google_Service_Calendar_EventExtendedProperties; + $extendedProperties = new Google\Service\Calendar\EventExtendedProperties; $extendedProperties->setPrivate(array()); $event_remote_empty->setExtendedProperties($extendedProperties); diff --git a/include/GoogleSync/GoogleSyncHelper.php b/include/GoogleSync/GoogleSyncHelper.php index c124c6af0..bb2404671 100644 --- a/include/GoogleSync/GoogleSyncHelper.php +++ b/include/GoogleSync/GoogleSyncHelper.php @@ -61,11 +61,11 @@ class GoogleSyncHelper * At least one of the params is required. * * @param Meeting $meeting (optional) Meeting Bean - * @param \Google_Service_Calendar_Event $event (optional) Google_Service_Calendar_Event Object + * @param \Google\Service\Calendar\Event $event (optional) Google\Service\Calendar\Event Object * * @return string push, pull, skip, or false on error */ - public function singleEventAction(Meeting $meeting = null, Google_Service_Calendar_Event $event = null) + public function singleEventAction(Meeting $meeting = null, Google\Service\Calendar\Event $event = null) { if (empty($meeting) && empty($event)) { return false; @@ -84,11 +84,11 @@ class GoogleSyncHelper * Takes two calendar events, and extracts their last modified and sync times. * * @param Meeting $meeting Meeting Bean - * @param \Google_Service_Calendar_Event $event Google_Service_Calendar_Event Object + * @param \Google\Service\Calendar\Event $event Google\Service\Calendar\Event Object * * @return array key/value array with [sModified, $gModified, lastsync] keys */ - public function getTimeStrings(Meeting $meeting, Google_Service_Calendar_Event $event) + public function getTimeStrings(Meeting $meeting, Google\Service\Calendar\Event $event) { $timeArray = array(); @@ -114,12 +114,12 @@ class GoogleSyncHelper * Takes two calendar events and the timeArray from getTimeStrings, and returns a push/pull[_delete] string. * * @param Meeting $meeting Meeting Bean - * @param \Google_Service_Calendar_Event $event Google_Service_Calendar_Event Object + * @param \Google\Service\Calendar\Event $event Google\Service\Calendar\Event Object * @param array timeArray from getTimeStrings * * @return string 'push(_delete)', 'pull(_delete)' */ - public function getNewestMeetingResponse(Meeting $meeting, Google_Service_Calendar_Event $event, array $timeArray) + public function getNewestMeetingResponse(Meeting $meeting, Google\Service\Calendar\Event $event, array $timeArray) { if ($timeArray['gModified'] > $timeArray['sModified']) { if ($event->status == 'cancelled') { @@ -140,13 +140,13 @@ class GoogleSyncHelper * Takes two calendar events and the timeArray from getTimeStrings, and returns bool (should we skip this record). * * @param Meeting $meeting Meeting Bean - * @param \Google_Service_Calendar_Event $event Google_Service_Calendar_Event Object + * @param \Google\Service\Calendar\Event $event Google\Service\Calendar\Event Object * @param array $timeArray from getTimeStrings * @param array $syncedList from GoogleSyncBase Class * * @return bool should we skip this record */ - public function isSkippable(Meeting $meeting, Google_Service_Calendar_Event $event, array $timeArray, array $syncedList) + public function isSkippable(Meeting $meeting, Google\Service\Calendar\Event $event, array $timeArray, array $syncedList) { $ret = false; @@ -173,7 +173,7 @@ class GoogleSyncHelper * * Creates reminders for event from google event reminders * - * @param array $overrides Google Calendar Event Reminders (See Class Google_Service_Calendar_EventReminders) + * @param array $overrides Google Calendar Event Reminders (See Class Google\Service\Calendar\EventReminders) * @param string $meeting Meeting Bean * * @return array|bool Nested array of unsaved reminders and reminder_invitees, false on Failure diff --git a/install/install_utils.php b/install/install_utils.php index 1ec340a03..98b38d685 100755 --- a/install/install_utils.php +++ b/install/install_utils.php @@ -1288,17 +1288,15 @@ function create_table_if_not_exist(&$focus) } - function create_default_users() { - $db = DBManagerFactory::getInstance(); global $setup_site_admin_password; global $setup_site_admin_user_name; global $create_default_user; global $sugar_config; require_once('install/UserDemoData.php'); - + //Create default admin user $user = BeanFactory::newBean('Users'); $user->id = 1; @@ -1310,11 +1308,9 @@ function create_default_users() $user->is_admin = true; $user->employee_status = 'Active'; $user->user_hash = User::getPasswordHash($setup_site_admin_password); - $user->save(); - //Bug#53793: Keep default current user in the global variable in order to store 'created_by' info as default user - // while installation is proceed. - $GLOBALS['current_user'] = $user; + $GLOBALS['current_user'] = $user; + $GLOBALS['current_user']->save(); if ($create_default_user) { $default_user = BeanFactory::newBean('Users'); diff --git a/lib/PDF/PDFConfigurator.php b/lib/PDF/PDFConfigurator.php new file mode 100644 index 000000000..b5c5ba031 --- /dev/null +++ b/lib/PDF/PDFConfigurator.php @@ -0,0 +1,109 @@ +configurator = $configurator; + } + + /** + * Factory method for nice fluent syntax. + * + * @return PDFConfigurator + */ + public static function make(): PDFConfigurator + { + return new self(); + } + + /** + * @param string $engine + * @return $this + */ + public function setEngine(string $engine): PDFConfigurator + { + if (empty($engine)) { + throw new InvalidArgumentException('PDF Engine cannot be empty'); + } + + $this->configurator->config['pdf']['defaultEngine'] = $engine; + + return $this; + } + + /** + * Saves the current configuration. + * + * @return PDFConfigurator + */ + public function save(): PDFConfigurator + { + $this->configurator->saveConfig(); + + return $this; + } +} diff --git a/lib/PDF/PDFWrapper.php b/lib/PDF/PDFWrapper.php index d0f0d96a0..ec503f952 100644 --- a/lib/PDF/PDFWrapper.php +++ b/lib/PDF/PDFWrapper.php @@ -172,4 +172,12 @@ class PDFWrapper return $sugar_config['pdf'][$key] ?? null; } + + /** + * @return string|null + */ + public static function getController(): ?string + { + return self::getPDFConfig('controller'); + } } diff --git a/lib/Search/SearchConfigurator.php b/lib/Search/SearchConfigurator.php index cb30f7ac7..4e21745e8 100644 --- a/lib/Search/SearchConfigurator.php +++ b/lib/Search/SearchConfigurator.php @@ -94,7 +94,7 @@ class SearchConfigurator public function setEngine(string $engine): SearchConfigurator { if (empty($engine)) { - throw new InvalidArgumentException('$engine cannot be empty'); + throw new InvalidArgumentException('Search Engine cannot be empty'); } $searchController = 'UnifiedSearch'; diff --git a/modules/Administration/PDF/Controller.php b/modules/Administration/PDF/Controller.php new file mode 100644 index 000000000..df58241b0 --- /dev/null +++ b/modules/Administration/PDF/Controller.php @@ -0,0 +1,81 @@ +setEngine($PDFEngine) + ->save(); + + if ($this->isAjax()) { + $this->yieldJson(['status' => 'success']); + } + + $this->redirect('index.php?module=Administration&action=index'); + } +} diff --git a/modules/Administration/PDF/MVC/Controller.php b/modules/Administration/PDF/MVC/Controller.php new file mode 100644 index 000000000..020c16c3c --- /dev/null +++ b/modules/Administration/PDF/MVC/Controller.php @@ -0,0 +1,53 @@ +smarty->assign('MOD', $mod_strings); + $this->smarty->assign('APP', $app_strings); + $this->smarty->assign('APP_LIST', $app_list_strings); + $this->smarty->assign('LANGUAGES', get_languages()); + $this->smarty->assign('JAVASCRIPT', get_set_focus_js()); + $this->smarty->assign('error', $errors); + $this->smarty->assign('BUTTONS', $this->getButtons()); + + if (empty($sugar_config['pdf'])) { + LoggerManager::getLogger()->warn('Configuration does not contains default PDF settings.'); + } + + $pdfSettings = $sugar_config['pdf'] ?? null; + $this->smarty->assign('config', $pdfSettings); + } + + /** + * Returns the cancel and save button. + * + * @return string + */ + protected function getButtons(): string + { + global $mod_strings; + global $app_strings; + + $this->smarty->assign('MOD', $mod_strings); + $this->smarty->assign('APP', $app_strings); + + return $this->smarty->fetch('modules/Administration/PDF/buttons.tpl'); + } + + /** + * Returns an associative array with their class name and translated label + * + * @return array + */ + protected function getEngines(): array + { + $engines = []; + + foreach (PDFWrapper::getEngines() as $engine) { + $engines[$engine] = StringUtils::camelToTranslation($engine); + } + + return $engines; + } +} diff --git a/modules/Administration/PDF/PDFView.php b/modules/Administration/PDF/PDFView.php new file mode 100644 index 000000000..f93794567 --- /dev/null +++ b/modules/Administration/PDF/PDFView.php @@ -0,0 +1,89 @@ +smarty->assign('selectedController', PDFWrapper::getController()); + $this->smarty->assign('selectedEngine', PDFWrapper::getDefaultEngine()); + $engines = $this->getEngines(); + + $this->smarty->assign('engines', [ + translate('LBL_PDF_WRAPPER_ENGINES') => $engines + ]); + } + + /** + * @see SugarView::display() + */ + public function display(): void + { + global $mod_strings, $app_strings; + + $this->smarty->assign('APP', $app_strings); + $this->smarty->assign('MOD', $mod_strings); + + $template = $this->templateFile; + if (file_exists('custom/' . $this->templateFile)) { + $template = 'custom/' . $this->templateFile; + } + + $this->smarty->display($template); + } +} diff --git a/modules/Administration/PDF/buttons.tpl b/modules/Administration/PDF/buttons.tpl new file mode 100644 index 000000000..d9e1fc45d --- /dev/null +++ b/modules/Administration/PDF/buttons.tpl @@ -0,0 +1,50 @@ +{* + * SugarCRM Community Edition is a customer relationship management program developed by + * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc. + * + * SuiteCRM is an extension to SugarCRM Community Edition developed by SalesAgility Ltd. + * Copyright (C) 2011 - 2021 SalesAgility Ltd. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License version 3 as published by the + * Free Software Foundation with the addition of the following permission added + * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK + * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY + * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. + * + * 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 Affero General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License along with + * this program; if not, see http://www.gnu.org/licenses or write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + * + * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road, + * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU Affero General Public License version 3. + * + * In accordance with Section 7(b) of the GNU Affero General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "Powered by + * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not + * reasonably feasible for technical reasons, the Appropriate Legal Notices must + * display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM". + *} + + diff --git a/modules/Administration/PDF/view.tpl b/modules/Administration/PDF/view.tpl new file mode 100644 index 000000000..be1c3e821 --- /dev/null +++ b/modules/Administration/PDF/view.tpl @@ -0,0 +1,84 @@ +{* + * SugarCRM Community Edition is a customer relationship management program developed by + * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc. + * + * SuiteCRM is an extension to SugarCRM Community Edition developed by SalesAgility Ltd. + * Copyright (C) 2011 - 2021 SalesAgility Ltd. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License version 3 as published by the + * Free Software Foundation with the addition of the following permission added + * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK + * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY + * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. + * + * 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 Affero General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License along with + * this program; if not, see http://www.gnu.org/licenses or write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + * + * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road, + * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU Affero General Public License version 3. + * + * In accordance with Section 7(b) of the GNU Affero General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "Powered by + * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not + * reasonably feasible for technical reasons, the Appropriate Legal Notices must + * display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM". + *} +