v9: Implement telemetry levels (#12267)

* Add initial classes

* Add TelemetryProviders

* Add new NodeCountService.cs and NodeTelemetryProvider

* Add data contract attribute to UsageInformation

Otherwise it wont serialize correctly

* Implement more providers

* Fix builders and propertyEditorTelemetry

* Add MediaTelemetryProvider

* Add MediaTelemetryProvider

* Fix doubling of media telemetry

* Move contentCount from NodeCountTelemetryProvider and move to ContentTelemetryProvider

* Revert ContentTelemetryProvider changes

* Add detailed information to TelemetryService

* Add integration tests

* Add more tests and todos for tests

* Fix stylecop warnings

* Use yield return instead of instantiating local list

* Implement Macro test

* Inject interface instead of implementation in TelemetryService

* Fix TelemetryServiceTests.cs

* Implement media tests

* Implement propertyTypeTests

* Implement constants instead of hardcoded strings

* Add SystemInformationTelemetryProvider

* Use SystemInformationTableDataProvider in UserDataService

* Implement more properties

* Add UsageInformation

* Replace UserDataService with SystemInformationTelemetryProvider

* Undo changes to UserDataService and obsolete it

* Remove ISystemInformationTableDataProvider

* Register SystemInformationTelemetryProvider as telemetry provider

* Use constants for telemetry names

* Make UserDataServiceTests test SystemInformationTelemetryProvider instead

* Update UserDataServiceTests to cover new data

* Add unit tests

* Add integration test testing expected data is returned

* Implement Analytics dashboard

* Improve assertion message

* Add text and styling to analyticspage

* Rename consent to analytic

* implement save button for consent level

* Implement save button

* Fix system information test

* Add TelemetryResource

* Move telemetry providers to infrastructure

* Add database provider to system information

* Set startvalue for slider

* Fix unit tests

* Implement MetricsConsentService using KeyValueService

* Return void hen setting the telemetry level

* fix startposition when not reloading

* Add a couple tests

* Update src/Umbraco.Core/Services/MetricsConsentService.cs

* Rename ConsentLevel.cs

* Use direct Enum instead of parsing

* rename consent resource

* add lazy database

* refactor slider

* Implement ng-if and propers pips

* Make classes internal

* Fix slider not loading when navigating to tab

* Add telemetry level check to TelemetryService.cs

* Add Consent for analytics text

* Fix build errors for unit tests

* Fix TelemetryServiceTests

* revert package-lock.json

* Fix integration test

* Update slider

* Update TelemetryService.cs

* Apply suggestions from code review

Co-authored-by: Mole <nikolajlauridsen@protonmail.ch>

Co-authored-by: Nikolaj Geisle <niko737@edu.ucl.dk>
Co-authored-by: nikolajlauridsen <nikolajlauridsen@protonmail.ch>
This commit is contained in:
Nikolaj Geisle
2022-04-19 15:06:10 +02:00
committed by GitHub
parent 2d31913837
commit c07ffb68fc
50 changed files with 1597 additions and 26 deletions

View File

@@ -13,7 +13,7 @@ For extra details about options and events take a look here: https://refreshless
<pre>
<div ng-controller="My.Controller as vm">
<umb-range-slider
<umb-range-slider
ng-model="vm.value"
on-end="vm.slideEnd(values)">
</umb-range-slider>
@@ -229,11 +229,13 @@ For extra details about options and events take a look here: https://refreshless
var origins = slider.noUiSlider.getOrigins();
// Move tooltips into the origin element. The default stylesheet handles this.
if(tooltips && tooltips.length !== 0){
tooltips.forEach(function (tooltip, index) {
if (tooltip) {
origins[index].appendChild(tooltip);
}
if (tooltip) {
origins[index].appendChild(tooltip);
}
});
}
slider.noUiSlider.on('update', function (values, handle, unencoded, tap, positions) {
@@ -283,7 +285,7 @@ For extra details about options and events take a look here: https://refreshless
offset = (textIsRtl && !isVertical ? 100 : 0) + (offset / handlesInPool) - lastOffset;
// Filter to unique values
var tooltipValues = poolValues[poolIndex].filter((v, i, a) => a.indexOf(v) === i);
var tooltipValues = poolValues[poolIndex].filter((v, i, a) => a.indexOf(v) === i);
// Center this tooltip over the affected handles
tooltips[handleNumber].innerHTML = tooltipValues.join(separator);

View File

@@ -20,7 +20,8 @@ Umbraco.Sys.ServerVariables = {
"updateCheckApiBaseUrl": "/umbraco/Api/UpdateCheck/",
"relationApiBaseUrl": "/umbraco/UmbracoApi/Relation/",
"rteApiBaseUrl": "/umbraco/UmbracoApi/RichTextPreValue/",
"iconApiBaseUrl": "/umbraco/UmbracoApi/Icon/"
"iconApiBaseUrl": "/umbraco/UmbracoApi/Icon/",
"analyticsApiBaseUrl": "/umbraco/UmbracoApi/Consent/"
},
umbracoSettings: {
"umbracoPath": "/umbraco",

View File

@@ -0,0 +1,57 @@
/**
* @ngdoc service
* @name umbraco.resources.consentResource
* @function
*
* @description
* Used by the health check dashboard to get checks and send requests to fix checks.
*/
(function () {
'use strict';
function analyticResource($http, umbRequestHelper) {
function getConsentLevel () {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"analyticsApiBaseUrl",
"GetConsentLevel")),
'Server call failed for getting current consent level');
}
function getAllConsentLevels () {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"analyticsApiBaseUrl",
"GetAllLevels")),
'Server call failed for getting current consent level');
}
function saveConsentLevel (value) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"analyticsApiBaseUrl",
"SetConsentLevel"),
{ telemetryLevel : value }
),
'Server call failed for getting current consent level');
}
var resource = {
getConsentLevel: getConsentLevel,
getAllConsentLevels : getAllConsentLevels,
saveConsentLevel : saveConsentLevel
};
return resource;
}
angular.module('umbraco.resources').factory('analyticResource', analyticResource);
})();

View File

@@ -200,6 +200,10 @@ angular.module('umbraco.services')
* localizationService.localizeMany(["speechBubbles_templateErrorHeader", "speechBubbles_templateErrorText"]).then(function(data){
* var header = data[0];
* var message = data[1];
*
*
*
*
* notificationService.error(header, message);
* });
* </pre>

View File

@@ -0,0 +1,98 @@
(function () {
"use strict";
function AnalyticsController($q, analyticResource, localizationService, notificationsService) {
let sliderRef = null;
var vm = this;
vm.getConsentLevel = getConsentLevel;
vm.getAllConsentLevels = getAllConsentLevels;
vm.saveConsentLevel = saveConsentLevel;
vm.sliderChange = sliderChange;
vm.setup = setup;
vm.loading = true;
vm.consentLevel = '';
vm.consentLevels = [];
vm.val = 1;
vm.sliderOptions =
{
"start": 1,
"step": 1,
"tooltips": [false],
"range": {
"min": 1,
"max": 3
},
pips: {
mode: 'values',
density: 50,
values: [1, 2, 3],
format: {
to: function (value) {
return vm.consentLevels[value - 1];
},
from: function (value) {
return Number(value);
}
}
}
};
$q.all(
[getConsentLevel(),
getAllConsentLevels()
]).then( () => {
vm.startPos = calculateStartPositionForSlider();
vm.sliderVal = vm.consentLevels[vm.startPos - 1];
vm.sliderOptions.start = vm.startPos;
vm.val = vm.startPos;
vm.sliderOptions.pips.format = {
to: function (value) {
return vm.consentLevels[value - 1];
},
from: function (value) {
return Number(value);
}
}
vm.loading = false;
if (sliderRef) {
sliderRef.noUiSlider.set(vm.startPos);
}
});
function setup(slider) {
sliderRef = slider;
}
function getConsentLevel() {
return analyticResource.getConsentLevel().then(function (response) {
vm.consentLevel = response;
})
}
function getAllConsentLevels(){
return analyticResource.getAllConsentLevels().then(function (response) {
vm.consentLevels = response;
})
}
function saveConsentLevel(){
analyticResource.saveConsentLevel(vm.sliderVal);
localizationService.localize("analytics_analyticsLevelSavedSuccess").then(function(value) {
notificationsService.success(value);
});
}
function sliderChange(values) {
const result = Number(values[0]);
vm.sliderVal = vm.consentLevels[result - 1];
}
function calculateStartPositionForSlider(){
let startPosition = vm.consentLevels.indexOf(vm.consentLevel) + 1;
if(startPosition === 0){
return 2;// Default start value
}
return startPosition;
}
}
angular.module("umbraco").controller("Umbraco.Dashboard.AnalyticsController", AnalyticsController);
})();

View File

@@ -0,0 +1,59 @@
<div ng-controller="Umbraco.Dashboard.AnalyticsController as vm">
<umb-box>
<umb-box-content>
<h3 class="bold">
<localize key="analytics_consentForAnalytics">Consent for analytics</localize>
</h3>
<div class="umb-healthcheck-help-text">
<p>In order to improve Umbraco and add new functionality based on as relevant information as possible,
<br>we would like to collect system- and usage information from your installation.
<br>We will NOT collect any personal data like content, code or users, and all data will be fully anonymous.
<br>
<br>We will on a regular basis share some of the overall learnings from these metrics.
Hopefully, you'll help us collect some valuable data.</p>
<div ng-if="!vm.loading" style="padding-left: 12px;padding-top: 50px; padding-bottom: 50px; width: 25%">
<umb-range-slider
ng-model="vm.val"
on-setup="vm.setup(slider)"
options="vm.sliderOptions"
on-update="vm.sliderChange(values)">
</umb-range-slider>
</div>
<p>
<div ng-if="vm.sliderVal === 'Minimal'">
<b>{{vm.sliderVal}}</b>
<br>We'll only send an anonymous site ID to let us know that the site exists.
</div>
<div ng-if="vm.sliderVal === 'Basic'">
<b>{{vm.sliderVal}}</b>
<br>We'll send site ID, umbraco version and packages installed
</div>
<div ng-if="vm.sliderVal === 'Detailed'">
<b>{{vm.sliderVal}}</b>
<br> We'll send:
<br>- Site ID, umbraco version and packages installed
<br>- System information like Server OS and Webserver
<br>- Statistics, like number of content nodes and number of media items
<br>- Configuration settings, like modelsbuilder mode and used languages
<br>
<br>We might change/extend what we send on the detailed level in the future, but if so, it will be listed in
this view.
By choosing "detailed" I accept these future changes
</div>
</p>
</div>
<div class="umb-panel-group__details-status-actions">
<umb-button type="button"
button-style="success"
label="Save"
action="vm.saveConsentLevel()"
ng-model="vm.sliderVal" ng-if="vm.consentLevel">
</umb-button>
</div>
</umb-box-content>
</umb-box>
</div>