diff --git a/src/app.js b/src/app.js index 75586bbd3..c37837590 100644 --- a/src/app.js +++ b/src/app.js @@ -41,6 +41,7 @@ const modules = [ 'graphdb.framework.core.directives.uppercased', 'graphdb.framework.core.directives.prop-indeterminate', 'graphdb.framework.guides.services', + 'graphdb.framework.core.services.licenseService', 'graphdb.framework.core.directives.operationsstatusesmonitor', 'graphdb.framework.core.directives.autocomplete', 'ngCustomElement' @@ -193,8 +194,8 @@ const moduleDefinition = function (productInfo, translations) { workbench.constant('productInfo', productInfo); // we need to inject $jwtAuth here in order to init the service before everything else - workbench.run(['$rootScope', '$route', 'toastr', '$sce', '$translate', 'ThemeService', 'WorkbenchSettingsStorageService', 'LSKeys', 'GuidesService', - function ($rootScope, $route, toastr, $sce, $translate, ThemeService, WorkbenchSettingsStorageService, LSKeys, GuidesService) { + workbench.run(['$rootScope', '$route', 'toastr', '$sce', '$translate', 'ThemeService', 'WorkbenchSettingsStorageService', 'LSKeys', 'GuidesService', '$licenseService', + function ($rootScope, $route, toastr, $sce, $translate, ThemeService, WorkbenchSettingsStorageService, LSKeys, GuidesService, $licenseService) { $rootScope.$on('$routeChangeSuccess', function () { updateTitleAndHelpInfo(); @@ -222,6 +223,9 @@ const moduleDefinition = function (productInfo, translations) { ThemeService.applyDarkThemeMode(); GuidesService.init(); + + // Checks license status and adds tracking code when free/evaluation license + $licenseService.checkLicenseStatus(); }]); workbench.filter('titlecase', function () { diff --git a/src/js/angular/controllers.js b/src/js/angular/controllers.js index bc2dc6f39..73cfffa44 100644 --- a/src/js/angular/controllers.js +++ b/src/js/angular/controllers.js @@ -92,7 +92,7 @@ function homeCtrl($scope, $rootScope, $http, $repositories, $jwtAuth, $licenseSe } } - $scope.$on('autocompleteStatus', function() { + $scope.$on('autocompleteStatus', function () { checkAutocompleteStatus(); }); @@ -202,7 +202,7 @@ function mainCtrl($scope, $menuItems, $jwtAuth, $http, toastr, $location, $repos $scope.initTutorial(); }); - $scope.checkMenu = debounce(function() { + $scope.checkMenu = debounce(function () { return $('.main-menu').hasClass('collapsed'); }, 250, {trailing: false}); @@ -324,7 +324,7 @@ function mainCtrl($scope, $menuItems, $jwtAuth, $http, toastr, $location, $repos $scope.isFreeAccessEnabled = function () { return $jwtAuth.isFreeAccessEnabled(); }; - $scope.hasExternalAuthUser = function() { + $scope.hasExternalAuthUser = function () { return $jwtAuth.hasExternalAuthUser(); }; $scope.isDefaultAuthEnabled = function () { @@ -401,7 +401,7 @@ function mainCtrl($scope, $menuItems, $jwtAuth, $http, toastr, $location, $repos return $repositories.isActiveRepoFedXType(); }; - $scope.isLicenseValid = function() { + $scope.isLicenseValid = function () { return $licenseService.isLicenseValid(); }; @@ -409,7 +409,7 @@ function mainCtrl($scope, $menuItems, $jwtAuth, $http, toastr, $location, $repos * Sets attrs property in the directive * @param attrs */ - $scope.setAttrs = function(attrs) { + $scope.setAttrs = function (attrs) { $scope.attrs = attrs; }; @@ -424,7 +424,7 @@ function mainCtrl($scope, $menuItems, $jwtAuth, $http, toastr, $location, $repos if ($scope.attrs) { $scope.isRestricted = $scope.attrs.hasOwnProperty('license') && !$licenseService.isLicenseValid() || - $scope.attrs.hasOwnProperty('write') && $scope.isSecurityEnabled() && !$scope.canWriteActiveRepo()|| + $scope.attrs.hasOwnProperty('write') && $scope.isSecurityEnabled() && !$scope.canWriteActiveRepo() || $scope.attrs.hasOwnProperty('ontop') && $scope.isActiveRepoOntopType() || $scope.attrs.hasOwnProperty('fedx') && $scope.isActiveRepoFedXType(); } @@ -611,7 +611,7 @@ function mainCtrl($scope, $menuItems, $jwtAuth, $http, toastr, $location, $repos }, { "title": $translate.instant('main.info.title.create.repo.page'), - "info": decodeHTML($translate.instant('main.info.create.repo.page', {link:""})) + "info": decodeHTML($translate.instant('main.info.create.repo.page', {link: ""})) }, { "title": $translate.instant('main.info.title.load.sample.dataset'), @@ -637,7 +637,7 @@ function mainCtrl($scope, $menuItems, $jwtAuth, $http, toastr, $location, $repos }, 50); }; - $scope.getTutorialPageHtml = function(page) { + $scope.getTutorialPageHtml = function (page) { return $sce.trustAsHtml(page.info); }; @@ -811,6 +811,9 @@ function mainCtrl($scope, $menuItems, $jwtAuth, $http, toastr, $location, $repos } }); + $scope.getProductType = function () { + return $licenseService.productType(); + }; $scope.isEnterprise = function () { return $scope.getProductType() === "enterprise"; @@ -827,24 +830,21 @@ function mainCtrl($scope, $menuItems, $jwtAuth, $http, toastr, $location, $repos return _.indexOf(editions, $scope.getProductType()) >= 0; }; - $scope.showLicense = function() { - return $licenseService.showLicense; + $scope.showLicense = function () { + return $licenseService.showLicense(); }; - $scope.getLicense = function() { - return $licenseService.license; + $scope.getLicense = function () { + return $licenseService.license(); }; - $scope.isLicenseHardcoded = function() { - return $licenseService.isLicenseHardcoded; + $scope.isLicenseHardcoded = function () { + return $licenseService.isLicenseHardcoded(); }; - $scope.getProductType = function() { - return $licenseService.productType; - }; - $scope.getProductTypeHuman = function() { - return $licenseService.productTypeHuman; + $scope.getProductTypeHuman = function () { + return $licenseService.productTypeHuman(); }; @@ -977,17 +977,17 @@ function uxTestCtrl($scope, $repositories, toastr, ModalService) { }); }; - $scope.demoToast = function(alertType, secondArg=true) { + $scope.demoToast = function (alertType, secondArg = true) { toastr[alertType]('Consectetur adipiscing elit. Sic transit gloria mundi.', secondArg ? 'Lorem ipsum dolor sit amet' : undefined, {timeOut: 300000, extendedTimeOut: 300000}); }; - $scope.clearToasts = function() { + $scope.clearToasts = function () { toastr.clear(); }; - $scope.clearRepo = function() { + $scope.clearRepo = function () { $repositories.setRepository(''); }; } diff --git a/src/js/angular/core/services/license.service.js b/src/js/angular/core/services/license.service.js index a03a28351..259c7d05f 100644 --- a/src/js/angular/core/services/license.service.js +++ b/src/js/angular/core/services/license.service.js @@ -1,47 +1,202 @@ +const EVALUATION_TYPE_1 = "this is an evaluation license"; +const EVALUATION_TYPE_2 = "evaluation"; + angular.module('graphdb.framework.core.services.licenseService', []) - .service('$licenseService', ['$rootScope', 'LicenseRestService', '$translate', licenseService]); - -function licenseService($rootScope, LicenseRestService, $translate) { - - const that = this; - - this.checkLicenseStatus = function () { - that.loadingLicense = true; - LicenseRestService.getHardcodedLicense().success(function (res) { - that.isLicenseHardcoded = (res === 'true'); - }).error(function () { - that.isLicenseHardcoded = true; - }).then(function () { - LicenseRestService.getLicenseInfo().then(function (res) { - that.license = res.data; - that.showLicense = true; - that.loadingLicense = false; - updateProductType(that.license); - }, function () { - that.license = {message: $translate.instant('no.license.set.msg'), valid: false}; - that.showLicense = true; - that.loadingLicense = false; - updateProductType(that.license); + .service('$licenseService', ['$window', '$document', 'LicenseRestService', '$translate', licenseService]); + +/** + * Service to manage and check license status, types, and tracking permissions. + * @param {object} $window - Angular service to interact with the window object. + * @param {object} $document - Angular service to interact with the document object. + * @param {object} LicenseRestService - Service for fetching license information from the backend. + * @param {object} $translate - Angular translate service for internationalization. + * @return {object} An object exposing methods for checking and managing license information: + * - `checkLicenseStatus`: Fetches license status and updates local variables. + * - `isLicenseValid`: Checks if the current license is valid. + * - `license`: Returns the current license object. + * - `loadingLicense`: Returns whether the license is being loaded. + * - `isLicenseHardcoded`: Checks if the license is hardcoded. + * - `showLicense`: Determines if the license should be shown in the UI. + * - `productType`: Returns the current product type associated with the license. + * - `productTypeHuman`: Returns a human-readable version of the product type. + */ + +function licenseService($window, $document, LicenseRestService, $translate) { + let _license = undefined; + let _loadingLicense = false; + let _isLicenseHardcoded = false; + let _showLicense = false; + let _productType = undefined; + let _productTypeHuman = undefined; + + /** + * Fetches license status and updates local variables related to license and product type. + * @return {Promise} A promise that resolves once the license status is fetched and processed. + */ + const checkLicenseStatus = () => { + _loadingLicense = true; + return LicenseRestService.getHardcodedLicense() + .then((res) => { + _isLicenseHardcoded = (res === 'true'); + return LicenseRestService.getLicenseInfo(); + }) + .then((res) => { + _license = res.data; + }) + .catch(() => { + _isLicenseHardcoded = true; + _license = { + message: $translate.instant('no.license.set.msg'), + valid: false + }; + }) + .finally(() => { + updateProductType(_license); + updateTracking(_license); + _showLicense = true; + _loadingLicense = false; }); - }); - }; - - const updateProductType = function (license) { - that.productType = license.productType; - if (that.productType === "standard") { - that.productTypeHuman = "Standard"; - } else if (that.productType === "enterprise") { - that.productTypeHuman = "Enterprise"; - } else if (that.productType === "free") { - that.productTypeHuman = "Free"; - } else if (that.productType === "graphdb") { - that.productTypeHuman = "GraphDB"; + }; + + const updateProductType = (license) => { + _productType = license.productType; + if (_productType === "standard") { + _productTypeHuman = "Standard"; + } else if (_productType === "enterprise") { + _productTypeHuman = "Enterprise"; + } else if (_productType === "free") { + _productTypeHuman = "Free"; + } else if (_productType === "graphdb") { + _productTypeHuman = "GraphDB"; } }; - this.checkLicenseStatus(); + /** + * Updates the tracking behavior based on the user's tracking consent or permission. + * @private + */ + const updateTracking = () => { + if (isTrackingAllowed()) { + addGoogleTagManagerScript(); + } else { + removeGoogleTagManagerScripts(); + } + }; + + /** + * Determines if tracking is allowed based on license status and product type. + * @return {boolean} A promise that resolves to a boolean indicating if tracking is allowed. + */ + const isTrackingAllowed = () => { + const licenseTypeOfUse = _license && _license.typeOfUse.toLowerCase(); + return _productType === "free" || + licenseTypeOfUse === EVALUATION_TYPE_1 || + licenseTypeOfUse === EVALUATION_TYPE_2; + }; + + /** + * Adds the Google Tag Manager (GTM) script to the document's head if it is not already present. + * This function creates a new script element for GTM and appends it to the document's head. + * @private + */ + const addGoogleTagManagerScript = () => { + if (!gtmScriptExists()) { + const dataLayerScript = $document[0].createElement('script'); + dataLayerScript.text = "(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'}); var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:''; j.async=true; j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl; f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer','GTM-WBP6C6Z4');"; + $document[0].getElementsByTagName('head')[0].appendChild(dataLayerScript); + } + }; + + /** + * Checks if a Google Tag Manager (GTM) script is already present in the document. + * + * When the GTM script is injected, it dynamically adds additional scripts depending on how many tags + * are configured within your GTM container. These scripts are usually inserted into the `` section, + * but it’s better to search through the entire document to ensure all related GTM scripts are found. + * + * This function searches through all `