PDF rausgenommen

This commit is contained in:
aschwarz
2023-01-23 11:03:31 +01:00
parent 82d562a322
commit a6523903eb
28078 changed files with 4247552 additions and 2 deletions

View File

@ -0,0 +1,78 @@
/*!
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
(function () {
angular.module('piwikApp').factory('segmentGeneratorModel', segmentGeneratorModel);
segmentGeneratorModel.$inject = ['piwikApi', 'piwik'];
function segmentGeneratorModel(piwikApi, piwik) {
var initialSegments = null;
var limitPromise = null;
var fetchedSiteId = null;
var model = {
isLoading: false,
segments : [],
loadSegments: loadSegments
};
return model;
function loadSegments(siteId, visitSegmentsOnly) {
if (model.isLoading) {
if (limitPromise) {
limitPromise.abort();
limitPromise = null;
}
}
model.isLoading = true;
// we need to clear last limit result because we now fetch different data
if (fetchedSiteId != siteId) {
limitPromise = null;
fetchedSiteId = siteId;
}
if (!limitPromise) {
var params = {method: 'API.getSegmentsMetadata',filter_limit: '-1', '_hideImplementationData': 0};
if (siteId === 'all' || !siteId) {
params.idSites = 'all';
params.idSite = 'all';
} else if (siteId) {
params.idSites = siteId;
params.idSite = siteId;
}
limitPromise = piwikApi.fetch(params);
}
return limitPromise.then(function (response) {
model.isLoading = false;
if (angular.isDefined(response)) {
if (visitSegmentsOnly) {
model.segments = [];
angular.forEach(response, function (segment) {
if (segment.sqlSegment && segment.sqlSegment.match(/log_visit\./)) {
model.segments.push(segment);
}
});
} else {
model.segments = response;
}
}
return model.segments;
}).finally(function () {
model.isLoading = false;
});
}
}
})();

View File

@ -0,0 +1,320 @@
/*!
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
(function () {
angular.module('piwikApp').controller('SegmentGeneratorController', SegmentGeneratorController);
SegmentGeneratorController.$inject = ['$scope', 'piwik', 'piwikApi', 'segmentGeneratorModel', '$filter', '$timeout'];
var findAndExplodeByMatch = function(metric){
var matches = ["==" , "!=" , "<=", ">=", "=@" , "!@","<",">", "=^", "=$"];
var newMetric = {};
var minPos = metric.length;
var match, index;
var singleChar = false;
for (var key=0; key < matches.length; key++) {
match = matches[key];
index = metric.indexOf(match);
if(index !== -1){
if(index < minPos){
minPos = index;
if(match.length === 1){
singleChar = true;
}
}
}
}
if (minPos < metric.length) {
// sth found - explode
if(singleChar == true){
newMetric.segment = metric.substr(0,minPos);
newMetric.matches = metric.substr(minPos,1);
newMetric.value = decodeURIComponent(metric.substr(minPos+1));
} else {
newMetric.segment = metric.substr(0,minPos);
newMetric.matches = metric.substr(minPos,2);
newMetric.value = decodeURIComponent(metric.substr(minPos+2));
}
// if value is only "" -> change to empty string
if(newMetric.value === '""')
{
newMetric.value = "";
}
}
newMetric.value = decodeURIComponent(newMetric.value);
return newMetric;
};
function generateUniqueId() {
var id = '';
var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
for (var i = 1; i <= 10; i++) {
id += chars.charAt(Math.floor(Math.random() * chars.length));
}
return id;
}
function stripTags(text) {
if (text) {
text = ('' + text).replace(/(<([^>]+)>)/ig,"");
}
return text;
}
function SegmentGeneratorController($scope, piwik, piwikApi, segmentGeneratorModel, $filter, $timeout) {
var translate = $filter('translate');
var self = this;
var firstSegment = '';
var firstMatch = '';
this.conditions = [];
this.model = segmentGeneratorModel;
this.segments = {};
this.matches = {
metric: [
{key: '==', value: translate('General_OperationEquals')},
{key: '!=', value: translate('General_OperationNotEquals')},
{key: '<=', value: translate('General_OperationAtMost')},
{key: '>=', value: translate('General_OperationAtLeast')},
{key: '<', value: translate('General_OperationLessThan')},
{key: '>', value: translate('General_OperationGreaterThan')}
],
dimension: [
{key: '==', value: translate('General_OperationIs')},
{key: '!=', value: translate('General_OperationIsNot')},
{key: '=@', value: translate('General_OperationContains')},
{key: '!@', value: translate('General_OperationDoesNotContain')},
{key: '=^', value: translate('General_OperationStartsWith')},
{key: '=$', value: translate('General_OperationEndsWith')}
],
};
this.matches[''] = this.matches.dimension;
this.andConditionLabel = '';
this.addNewAndCondition = function () {
var condition = {orConditions: []};
this.addAndCondition(condition);
this.addNewOrCondition(condition);
return condition;
};
this.addAndCondition = function (condition) {
this.andConditionLabel = translate('SegmentEditor_OperatorAND');
this.conditions.push(condition);
this.updateSegmentDefinition();
}
this.addNewOrCondition = function (condition) {
var orCondition = {
segment: firstSegment,
matches: firstMatch,
value: ''
};
this.addOrCondition(condition, orCondition);
};
this.addOrCondition = function (condition, orCondition) {
orCondition.isLoading = false;
orCondition.id = generateUniqueId();
condition.orConditions.push(orCondition);
this.updateSegmentDefinition();
$timeout(function () {
self.updateAutocomplete(orCondition);
});
};
this.updateAutocomplete = function (orCondition) {
orCondition.isLoading = true;
this.updateSegmentDefinition();
var resolved = false;
var promise = piwikApi.fetch({
module: 'API',
format: 'json',
method: 'API.getSuggestedValuesForSegment',
segmentName: orCondition.segment
}, {createErrorNotification: false});
promise.then(function(response) {
orCondition.isLoading = false;
resolved = true;
var inputElement = $('.orCondId' + orCondition.id + " .metricValueBlock input");
if (response && response.result != 'error') {
inputElement.autocomplete({
source: response,
minLength: 0,
select: function(event, ui){
event.preventDefault();
orCondition.value = ui.item.value;
self.updateSegmentDefinition();
$timeout(function () {
$scope.$apply();
});
}
});
}
inputElement.off('click');
inputElement.click(function (e) {
$(inputElement).autocomplete('search', orCondition.value);
});
}, function(response) {
resolved = true;
orCondition.isLoading = false;
var inputElement = $('.orCondId' + orCondition.id + " .metricValueBlock input");
inputElement.autocomplete({
source: [],
minLength: 0
});
$(inputElement).autocomplete('search', orCondition.value);
});
$timeout(function () {
if (!resolved) {
promise.abort();
}
}, 20000);
};
this.removeOrCondition = function (condition, orCondition) {
var index = condition.orConditions.indexOf(orCondition);
if (index > -1) {
condition.orConditions.splice(index, 1);
}
if (condition.orConditions.length === 0) {
var index = self.conditions.indexOf(condition);
if (index > -1) {
self.conditions.splice(index, 1);
}
if (self.conditions.length === 0) {
self.andConditionLabel = '';
}
}
this.updateSegmentDefinition();
};
this.getSegmentString = function () {
var segmentStr = '';
angular.forEach(this.conditions, function (conditions) {
var subSegmentStr = '';
angular.forEach(conditions.orConditions, function (orCondition){
if (subSegmentStr !== ''){
subSegmentStr += ","; // OR operator
}
// one encode for urldecode on value, one encode for urldecode on condition
subSegmentStr += orCondition.segment + orCondition.matches + encodeURIComponent(encodeURIComponent(orCondition.value));
});
if (segmentStr !== '') {
segmentStr += ";"; // add AND operator between segment blocks
}
segmentStr += subSegmentStr;
});
return segmentStr;
};
this.setSegmentString = function (segmentStr) {
var orCondition, condition;
this.conditions = [];
if (!segmentStr) {
return;
}
var blocks = segmentStr.split(';');
for (var key = 0; key < blocks.length; key++) {
condition = {orConditions: []};
this.addAndCondition(condition);
blocks[key] = blocks[key].split(',');
for (var innerkey = 0; innerkey < blocks[key].length; innerkey++) {
orCondition = findAndExplodeByMatch(blocks[key][innerkey]);
this.addOrCondition(condition, orCondition);
}
}
};
this.updateSegmentDefinition = function () {
$scope.segmentDefinition = this.getSegmentString();
};
if ($scope.segmentDefinition) {
this.setSegmentString($scope.segmentDefinition);
}
$scope.$watch('idsite', function (newValue, oldValue) {
if (newValue != oldValue) {
reloadSegments(newValue, $scope.visitSegmentsOnly);
}
});
reloadSegments($scope.idsite, $scope.visitSegmentsOnly);
function reloadSegments(idsite, visitSegmentsOnly) {
segmentGeneratorModel.loadSegments(idsite, visitSegmentsOnly).then(function (segments) {
self.segmentList = [];
var groups = {};
angular.forEach(segments, function (segment) {
if (!segment.category) {
segment.category = 'Others';
}
if (!firstSegment) {
firstSegment = segment.segment;
if (segment.type && self.matches[segment.type]) {
firstMatch = self.matches[segment.type][0].key;
} else {
firstMatch = self.matches[''][0].key
}
}
self.segments[segment.segment] = segment;
var segmentData = {group: segment.category, key: segment.segment, value: segment.name};
if ('acceptedValues' in segment && segment.acceptedValues) {
segmentData.tooltip = stripTags(segment.acceptedValues);
}
self.segmentList.push(segmentData);
});
if ($scope.addInitialCondition && self.conditions.length === 0) {
self.addNewAndCondition();
}
});
}
}
})();

View File

@ -0,0 +1,61 @@
<div class="segment-generator">
<div piwik-activity-indicator loading="segmentGenerator.model.isLoading"></div>
<div ng-repeat="(conditionIndex,condition) in segmentGenerator.conditions" class="segmentRow{{conditionIndex}}">
<div class="segment-rows">
<div ng-repeat="orCondition in condition.orConditions" class="orCondId{{ orCondition.id }}">
<div class="segment-row">
<a ng-click="segmentGenerator.removeOrCondition(condition, orCondition)" class="segment-close"></a>
<a href="#" class="segment-loading" ng-show="orCondition.isLoading"></a>
<div class="segment-row-inputs valign-wrapper">
<div class="segment-input metricListBlock valign-wrapper">
<div piwik-field uicontrol="expandable-select" name="segments"
title="{{ segmentGenerator.segments[orCondition.segment].name }}"
full-width="true"
style="width: 100%;"
ng-change="segmentGenerator.updateAutocomplete(orCondition)"
ng-model="orCondition.segment"
options="segmentGenerator.segmentList">
</div>
</div>
<div class="segment-input metricMatchBlock valign-wrapper">
<div piwik-field uicontrol="select" name="matches"
style="display: inline-block"
full-width="true"
ng-change="segmentGenerator.updateSegmentDefinition()"
ng-model="orCondition.matches"
options="segmentGenerator.matches[segmentGenerator.segments[orCondition.segment].type]">
</div>
</div>
<div class="segment-input metricValueBlock valign-wrapper">
<div class="form-group row" style="width: 100%;">
<div class="input-field col s12">
<span role="status" aria-live="polite" class="ui-helper-hidden-accessible"></span>
<input ng-model="orCondition.value" placeholder="Value"
ng-change="segmentGenerator.updateSegmentDefinition()"
type="text" class="autocomplete" title="Value" autocomplete="off">
</div>
</div>
</div>
<div class="clear"></div>
</div>
</div>
<div class="segment-or">{{ 'SegmentEditor_OperatorOR'|translate }}</div>
</div>
<div class="segment-add-or" ng-click="segmentGenerator.addNewOrCondition(condition)">
<div>
<a ng-bind-html="'+' + ('SegmentEditor_AddANDorORCondition'|translate:'&lt;span>' + ('SegmentEditor_OperatorOR'|translate) + '&lt;/span>')"></a>
</div>
</div>
</div>
<div class="segment-and">{{ 'SegmentEditor_OperatorAND'|translate }}</div>
</div>
<div class="segment-add-row initial" ng-click="segmentGenerator.addNewAndCondition()">
<div>
<a ng-bind-html="'+' + ('SegmentEditor_AddANDorORCondition'|translate:'&lt;span>' + segmentGenerator.andConditionLabel + '&lt;/span>')"></a>
</div>
</div>
</div>

View File

@ -0,0 +1,66 @@
/*!
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
/**
* Usage:
* <div piwik-segment-generator>
*/
(function () {
angular.module('piwikApp').directive('piwikSegmentGenerator', piwikSegmentGenerator);
piwikSegmentGenerator.$inject = ['$document', 'piwik', '$filter', '$timeout'];
function piwikSegmentGenerator($document, piwik, $filter, $timeout){
var defaults = {
segmentDefinition: '',
addInitialCondition: false,
visitSegmentsOnly: false,
idsite: piwik.idSite
};
return {
restrict: 'A',
scope: {
segmentDefinition: '@',
addInitialCondition: '=',
visitSegmentsOnly: '=',
idsite: '='
},
require: "?ngModel",
templateUrl: 'plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.directive.html?cb=' + piwik.cacheBuster,
controller: 'SegmentGeneratorController',
controllerAs: 'segmentGenerator',
compile: function (element, attrs) {
for (var index in defaults) {
if (attrs[index] === undefined) {
attrs[index] = defaults[index];
}
}
return function (scope, element, attrs, ngModel) {
if (ngModel) {
ngModel.$render = function() {
scope.segmentDefinition = ngModel.$viewValue;
if (scope.segmentDefinition) {
scope.segmentGenerator.setSegmentString(scope.segmentDefinition);
} else {
scope.segmentGenerator.setSegmentString('');
}
};
}
scope.$watch('segmentDefinition', function (newValue) {
if (ngModel) {
ngModel.$setViewValue(newValue);
}
});
};
}
};
}
})();

View File

@ -0,0 +1,204 @@
.segment-generator {
width: 900px;
.segment-row-inputs {
.form-group {
margin-top: 0;
margin-bottom: 0;
.input-field {
margin-top: 0;
}
}
}
.segment-input input {
display: block;
width: 96%;
padding: 8px 2%;
}
.segment-input label {
display: block;
margin: 0 0 5px 0;
font-size: 11px;
color: #505050;
}
.segment-input {
float: left;
padding: 6px 0 5px 3px;
border: 2px dashed #EFEFEB;
margin-right: 3px;
}
.segment-rows {
padding: 4px;
margin: 0 3px 0 0;
border: 1px solid #a9a399;
border-radius: 3px 3px 3px 3px;
position: relative;
box-shadow: 0 12px 6px -10px rgba(0, 0, 0, 0.42);
}
.segment-add-row,
.segment-add-or {
cursor: pointer;
font-size: 14px;
font-weight: bold;
background: @theme-color-background-contrast;
color: #b9b9b9;
text-align: center;
position: relative;
.border-radius(5px);
}
.segment-add-row > div,
.segment-add-or > div {
border-radius: 4px;
border: 2px dashed #fff;
padding: 10px 0;
}
.segment-add-row > div a,
.segment-add-or > div a {
color: #b9b9b9;
text-decoration: none;
}
.segment-input {
select, input {
.font-default(12px, 14px);
color: @theme-color-text;
font-weight: 600;
margin: 0;
height: 32px;
}
}
.segment-add-row > div a span,
.segment-add-or > div a span {
color: @theme-color-brand;
text-shadow: none;
}
.segment-add-row {
margin: 0 3px 0 0;
padding: 0 12px;
border: 1px solid #a9a399;
border-radius: 3px 3px 3px 3px;
box-shadow: 0 12px 6px -10px rgba(0, 0, 0, 0.42);
}
.segment-add-or {
text-shadow: 0 1px 0 #fff;
display: inline-block;
width: 100%;
padding: 0 1%;
background: #efefeb;
border-radius: 3px 3px 3px 3px;
}
.segment-add-or > div {
border: 2px dashed #EFEFEB;
background-color: #efefeb;
}
.segment-row {
border-radius: 3px;
display: inline-block;
position: relative;
width: 887px;
padding: 12px 1%;
background: #efefeb;
padding: 0 5px 0 5px;
}
.segment-row .segment-close {
top: 15px;
right: 6px;
position: absolute;
width: 15px;
height: 15px;
background: url(plugins/SegmentEditor/images/segment-close.png) 0 0 no-repeat;
z-index: 9999;
}
.segment-row .segment-loading {
top: 25px;
right: 30px;
position: absolute;
width: 15px;
height: 15px;
background: url(plugins/Morpheus/images/loading-blue.gif) 0 0 no-repeat;
}
.segment-or {
display: inline-block;
margin: 0 0 0 6%;
position: relative;
background: #efefeb;
padding: 5px 28px;
color: #4f4f4f;
font-weight: bold;
font-size: 14px;
text-shadow: 0 1px 0 #fff;
}
.segment-or:before,
.segment-or:after {
content: '';
position: absolute;
background: @theme-color-background-base;
border: 1px solid #efefeb;
width: 10px;
top: -1px;
bottom: -1px;
}
.segment-or:before {
border-left: none;
left: 0;
border-radius: 0 5px 5px 0;
}
.segment-or:after {
border-right: none;
right: 0;
border-radius: 5px 0 0 5px;
}
.segment-and {
display: inline-block;
margin: -1px 0 -1px 6%;
z-index: 1;
position: relative;
background: @theme-color-background-contrast;
padding: 5px 35px;
color: #4f4f4f;
font-size: 14px;
font-weight: bold;
text-shadow: 0 1px 0 #fff;
}
.segment-and:before,
.segment-and:after {
content: '';
position: absolute;
background: url(plugins/SegmentEditor/images/bg-inverted-corners.png);
border: 1px solid #a9a399;
width: 10px;
top: 0;
bottom: 0;
}
.segment-and:before {
border-left: none;
left: 0;
border-radius: 0 5px 5px 0;
}
.segment-and:after {
border-right: none;
right: 0;
border-radius: 5px 0 0 5px;
}
}