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,3 @@
<div class="field">
{{ field.myProperty }}
</div>

View File

@ -0,0 +1,170 @@
/*!
* 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-field>
*
* eg <div piwik-field uicontrol="select"
* title="{{ 'SitesManager_Timezone'|translate }}"
* value="site.timezone"
* options="timezones"
* inline-help="test"
* description=""
* introduction=""
* name=""
* placeholder=""
* rows="3"
* autocomplete="off"
* data-disabled="true"
* full-width="true"
* templateFile=""></div>
*
* templateFile allows to render a custom template
* We do not use type= attribute here as it would match some CSS from input type=radio etc
*/
(function () {
angular.module('piwikApp').directive('piwikField', piwikField);
piwikField.$inject = ['piwik', '$compile'];
function piwikField(piwik, $compile){
return {
restrict: 'A',
require: '?ngModel',
scope: {
uicontrol: '@',
name: '@',
value: '@',
default: '@',
options: '=',
description: '@',
introduction: '@',
title: '@',
inlineHelp: '@',
disabled: '=',
uiControlAttributes: '=',
autocomplete: '@',
condition: '@',
varType: '@',
autofocus: '@',
tabindex: '@',
templateFile: '@',
fullWidth: '@',
maxlength: '@',
required: '@',
placeholder: '@',
rows: '@',
min: '@',
max: '@'
},
template: '<div piwik-form-field="field"></div>',
link: function(scope, elm, attrs, ctrl) {
if (!ctrl) {
return;
}
// load init value
if (scope.field.value !== undefined && scope.field.value !== null) {
ctrl.$setViewValue(scope.field.value);
} else if (ctrl.$viewValue) {
scope.field.value = ctrl.$viewValue;
}
// view -> model
scope.$watch('field.value', function (val, oldVal) {
if (val !== oldVal && val !== ctrl.$viewValue) {
ctrl.$setViewValue(val);
}
});
// model -> view
ctrl.$render = function() {
scope.field.value = ctrl.$viewValue;
};
},
controller: function ($scope) {
var field = {};
field.uiControl = $scope.uicontrol;
if ($scope.varType) {
field.type = $scope.varType;
} else if (field.uiControl === 'multiselect') {
field.type = 'array';
} else if (field.uiControl === 'checkbox') {
field.type = 'boolean';
} else if (field.uiControl === 'site') {
field.type = 'object';
} else if (field.uiControl === 'number') {
field.type = 'integer';
} else {
field.type = 'string';
}
field.name = $scope.name;
field.value = $scope.value;
field.defaultValue = $scope.default;
field.availableValues = $scope.options;
field.description = $scope.description;
field.introduction = $scope.introduction;
field.inlineHelp = $scope.inlineHelp;
field.templateFile = $scope.templateFile;
field.title = $scope.title;
field.uiControlAttributes = $scope.uiControlAttributes || {};
field.fullWidth = !!$scope.fullWidth;
if (field.type === 'array' && angular.isString(field.value) && field.value) {
field.value = JSON.parse(field.value);
}
var i = 0, attribute;
var attributes = ['disabled', 'autocomplete', 'tabindex', 'autofocus', 'rows', 'required', 'maxlength', 'placeholder', 'min', 'max'];
for (i; i < attributes.length; i++) {
attribute = attributes[i];
if (!!$scope[attribute]) {
field.uiControlAttributes[attribute] = $scope[attribute];
}
}
$scope.field = field;
$scope.$watch('options', function (val, oldVal) {
if (val !== oldVal) {
$scope.field.availableValues = val;
}
});
$scope.$watch('title', function (val, oldVal) {
if (val !== oldVal) {
$scope.field.title = val;
}
});
$scope.$watch('inlineHelp', function (val, oldVal) {
if (val !== oldVal) {
$scope.field.inlineHelp = val;
}
});
if ('undefined' !== typeof $scope.placeholder && $scope.placeholder !== null) {
$scope.$watch('placeholder', function (val, oldVal) {
if (val !== oldVal) {
$scope.field.uiControlAttributes.placeholder = val;
}
});
}
$scope.$watch('disabled', function (val, oldVal) {
if (val !== oldVal) {
$scope.field.uiControlAttributes.disabled = val;
}
});
}
};
}
})();

View File

@ -0,0 +1,16 @@
<div>
<label class="fieldRadioTitle" ng-show="formField.title">{{ formField.title }}</label>
<p ng-repeat="checkboxModel in formField.availableOptions"
class="checkbox">
<input ng-model="formField.checkboxkeys[$index.toString()]"
value="{{ checkboxModel.key }}"
ng-change="formField.updateCheckboxArrayValue()"
piwik-attributes="{{formField.uiControlAttributes}}"
type="checkbox"
id="{{ formField.name + checkboxModel.key }}"
name="{{ checkboxModel.name }}">
<label for="{{ formField.name + checkboxModel.key }}">{{ checkboxModel.value }}
<span class="form-description" ng-show="checkboxModel.description">{{ checkboxModel.description }}</span></label>
</p>
</div>

View File

@ -0,0 +1,10 @@
<div class="checkbox">
<input ng-model="formField.value"
piwik-attributes="{{formField.uiControlAttributes}}"
ng-value="1"
type="checkbox"
id="{{ formField.name }}"
name="{{ formField.name }}">
<label for="{{ formField.name }}" ng-bind-html="formField.title"></label>
</div>

View File

@ -0,0 +1,24 @@
<div class="expandableSelector" piwik-focus-anywhere-but-here="formField.showSelect = false">
<div ng-click="formField.showSelect = !formField.showSelect" class="select-wrapper"
><span class="caret"></span><input type="text" class="select-dropdown"
readonly="true" value="{{ formField.title }}">
</div>
<div ng-show="formField.showSelect" class="expandableList z-depth-2">
<div class="searchContainer">
<input type="text" placeholder="Search"
ng-model="formField.searchTerm" class="expandableSearch browser-default"
piwik-focus-if="formField.showSelect">
</div>
<ul class="collection firstLevel">
<li ng-repeat="options in formField.availableOptions" class="collection-item" ng-show="options.values|filter:formField.searchTerm|length">
<h4 class="expandableListCategory" ng-click="formField.showCategory == options.group ? formField.showCategory = '' : formField.showCategory = options.group">{{ options.group }} <span class="secondary-content" ng-class='{"icon-arrow-right": formField.showCategory != options.group, "icon-arrow-bottom": formField.showCategory == options.group}'></span></h4>
<ul ng-show="formField.showCategory == options.group || formField.searchTerm" class="collection secondLevel">
<li class="expandableListItem collection-item valign-wrapper" ng-repeat="children in options.values|filter:formField.searchTerm" ng-click="formField.value = children.key;formField.showSelect = false;"><span class="primary-content">{{ children.value }}</span> <span ng-show="children.tooltip" title="{{ children.tooltip }}" class="secondary-content icon-help"></span></li>
</ul>
</li>
</ul>
</div>
</div>

View File

@ -0,0 +1,88 @@
.expandableSelector {
position: relative;
.secondary-content {
font-size: 16px;
margin-top: -3px;
color: @theme-color-link;
cursor: help;
}
ul {
min-width: 250px;
&.collection.firstLevel {
border-top: 0;
margin-top: 0;
margin-bottom: 0;
font-size: 12px;
> li {
padding: 0 !important;
}
}
.expandableListCategory {
padding: 10px 20px;
color: @theme-color-link;
}
li {
&:hover {
background: #f2f2f2 !important;
}
&.collection-item {
cursor: pointer;
}
}
ul {
margin-top: 0;
margin-bottom: 0;
.primary-content {
width: 100%;
}
.secondary-content {
margin-top: 3px;
}
li {
padding-top: 6px !important;
padding-bottom: 6px !important;
padding-left: 30px !important;
min-width: 200px;
&:hover {
background: #f2f2f2 !important;
}
}
}
}
.searchContainer {
padding: 5px;
border-left: 1px solid #e0e0e0;
border-right: 1px solid #e0e0e0;
border-top: 1px solid #e0e0e0;
}
.expandableSearch {
vertical-align: top;
padding: 7px 6px !important;
border: 1px solid #d0d0d0 !important;
background: #fff !important;
font-size: 11px !important;
color: #454545 !important;
width: 100% !important;
}
.expandableList {
position: absolute;
z-index: 9999;
margin-top: -48px;
background: #fff;
}
}

View File

@ -0,0 +1,8 @@
<div>
<div matomo-field-array
name="{{ formField.name }}"
ng-model="formField.value"
field="formField.uiControlAttributes.field">
</div>
<label for="{{ formField.name }}" ng-bind-html="formField.title"></label>
</div>

View File

@ -0,0 +1,10 @@
<div>
<div class="btn">
<span for="{{ formField.name }}" ng-bind-html="formField.title"></span>
<input name="{{ formField.name }}" type="file" id="{{ formField.name }}">
</div>
<div class="file-path-wrapper">
<input class="file-path validate" ng-model="formField.value" type="text">
</div>
</div>

View File

@ -0,0 +1,6 @@
<div>
<input type="{{ formField.uiControl }}"
name="{{ formField.name }}"
ng-model="formField.value"
>
</div>

View File

@ -0,0 +1,9 @@
<div>
<select multiple
name="{{ formField.name }}"
ng-model="formField.value"
ng-options="t.key as t.value group by t.group for t in formField.availableOptions"
piwik-attributes="{{formField.uiControlAttributes}}">
</select>
<label for="{{ formField.name }}" ng-bind-html="formField.title"></label>
</div>

View File

@ -0,0 +1,9 @@
<div>
<div matomo-multi-pair-field
name="{{ formField.name }}"
ng-model="formField.value"
field1="formField.uiControlAttributes.field1"
field2="formField.uiControlAttributes.field2">
</div>
<label for="{{ formField.name }}" ng-bind-html="formField.title"></label>
</div>

View File

@ -0,0 +1,13 @@
<div>
<input
class="control_{{ formField.uiControl }}"
type="{{ formField.uiControl }}"
id="{{ formField.name }}"
name="{{ formField.name }}"
ng-model="formField.value"
string-to-number
ng-value="formField.value"
piwik-attributes="{{formField.uiControlAttributes}}"
>
<label for="{{ formField.name }}" ng-bind-html="formField.title"></label>
</div>

View File

@ -0,0 +1,17 @@
<div>
<label class="fieldRadioTitle" ng-show="formField.title">{{ formField.title }}</label>
<p ng-repeat="radioModel in formField.availableOptions"
class="radio">
<input ng-model="formField.value"
ng-value="radioModel.key"
type="radio"
id="{{ formField.name + radioModel.key }}"
name="{{ formField.name }}"
ng-disabled="radioModel.disabled || formField.disabled"
piwik-attributes="{{formField.uiControlAttributes}}"
>
<label for="{{ formField.name + radioModel.key }}">{{ radioModel.value }}
<span class="form-description" ng-show="radioModel.description">{{ radioModel.description }}</span></label>
</p>
</div>

View File

@ -0,0 +1,10 @@
<div>
<select name="{{ formField.name }}"
ng-model="formField.value"
ng-options="t.key as t.value group by t.group disable when t.disabled for t in formField.availableOptions"
piwik-attributes="{{formField.uiControlAttributes}}"
ng-click="onShowSelect()"
>
</select>
<label for="{{ formField.name }}" ng-bind-html="formField.title"></label>
</div>

View File

@ -0,0 +1,14 @@
<div>
<label for="{{ formField.name }}" class="siteSelectorLabel" ng-bind-html="formField.title"></label>
<div piwik-siteselector
class="sites_autocomplete"
ng-model="formField.value"
id="{{ formField.name }}"
show-all-sites-item="formField.uiControlAttributes.showAllSitesItem || false"
switch-site-on-select="false"
show-selected-site="true"
only-sites-with-admin-access="formField.uiControlAttributes.onlySitesWithAdminAccess || false"
placeholder="{{ formField.uiControlAttributes.placeholder }}"
piwik-attributes="{{formField.uiControlAttributes}}"
></div>
</div>

View File

@ -0,0 +1,10 @@
<div>
<input class="control_{{ formField.uiControl }}"
type="{{ formField.uiControl }}"
name="{{ formField.name }}"
ng-list
ng-model="formField.value"
piwik-attributes="{{formField.uiControlAttributes}}"
>
<label for="{{ formField.name }}" ng-bind-html="formField.title"></label>
</div>

View File

@ -0,0 +1,13 @@
<div>
<input
class="control_{{ formField.uiControl }}"
type="{{ formField.uiControl }}"
id="{{ formField.name }}"
name="{{ formField.name }}"
ng-model="formField.value"
ng-value="formField.value"
ng-trim="false"
piwik-attributes="{{formField.uiControlAttributes}}"
>
<label for="{{ formField.name }}" ng-bind-html="formField.title"></label>
</div>

View File

@ -0,0 +1,9 @@
<div>
<textarea name="{{ formField.name }}"
ng-list="&#10;" ng-trim="false"
piwik-attributes="{{formField.uiControlAttributes}}"
ng-model="formField.value"
class="materialize-textarea"
></textarea>
<label for="{{ formField.name }}" ng-bind-html="formField.title"></label>
</div>

View File

@ -0,0 +1,9 @@
<div>
<textarea name="{{ formField.name }}"
piwik-attributes="{{formField.uiControlAttributes}}"
id="{{ formField.name }}"
ng-model="formField.value"
class="materialize-textarea"
></textarea>
<label for="{{ formField.name }}" ng-bind-html="formField.title"></label>
</div>

View File

@ -0,0 +1,28 @@
<div class="form-group row" ng-show="formField.showField">
<h3 ng-if="formField.introduction" class="col s12">{{ formField.introduction }}</h3>
<div class="col s12"
ng-class="{'input-field': formField.uiControl != 'checkbox' && formField.uiControl != 'radio', 'file-field': formField.uiControl == 'file', 'm6': !formField.fullWidth}"
ng-include="formField.templateFile" onload="templateLoaded()"
>
</div>
<div class="col s12"
ng-class="{'m6': !formField.fullWidth}">
<div ng-if="formField.description || formField.inlineHelp || (formField.defaultValue && formField.uiControl != 'checkbox' && formField.uiControl != 'radio')"
class="form-help">
<div ng-show="formField.description"
class='form-description'>{{ formField.description }}</div>
<span class="inline-help" ng-bind-html="formField.inlineHelp"></span>
<span ng-show="formField.defaultValuePretty && formField.uiControl != 'checkbox' && formField.uiControl != 'radio'">
<br />
{{ 'General_Default'|translate }}:
<span>{{formField.defaultValuePretty|limitTo:50}}</span>
</span>
</div>
</div>
</div>

View File

@ -0,0 +1,451 @@
/*!
* 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-form-field="{...}">
*/
(function () {
angular.module('piwikApp').directive('piwikFormField', piwikFormField);
piwikFormField.$inject = ['piwik', '$timeout'];
function piwikFormField(piwik, $timeout){
function initMaterialSelect($select, placeholder) {
$select.material_select();
// to prevent overlapping selects, when a select is opened, we set the z-index to a high value on focus & remove z-index for all others
// NOTE: we can't remove it directly blur since the blur causes the select to overlap, aborting the select click. (a timeout is used
// to make sure the z-index is removed however, in case a non-select dropdown is displayed over it)
$select.closest('.select-wrapper').find('input.select-dropdown')
.focus(function () {
$('.select-wrapper').css('z-index', '');
$(this).closest('.select-wrapper').css('z-index', 999);
}).blur(function () {
var self = this;
setTimeout(function () {
$(self).closest('.select-wrapper').css('z-index', '');
}, 250);
});
// add placeholder to input
if (placeholder) {
var $materialInput = $select.closest('.select-wrapper').find('input');
$materialInput.attr('placeholder', placeholder);
}
}
function syncMultiCheckboxKeysWithFieldValue(field)
{
angular.forEach(field.availableOptions, function (option, index) {
if (option && field.value.indexOf(option.key) !== -1) {
field.checkboxkeys[index] = true;
} else {
field.checkboxkeys[index] = false;
}
});
}
function hasUiControl(field, uiControlType)
{
return field.uiControl === uiControlType;
}
function isSelectControl(field)
{
return hasUiControl(field, 'select') || hasUiControl(field, 'multiselect');
}
function isArrayCheckboxControl(field)
{
return field.type === 'array' && hasUiControl(field, 'checkbox');
}
function hasGroupedValues(availableValues)
{
if (!angular.isObject(availableValues)
|| angular.isArray(availableValues)) {
return false;
}
var key;
for (key in availableValues) {
if (Object.prototype.hasOwnProperty.call(availableValues, key)) {
if (angular.isObject(availableValues[key])) {
return true;
} else {
return false;
}
}
}
return false;
}
function whenRendered(scope, element, inlineHelpNode) {
return function () {
var field = scope.formField;
if (inlineHelpNode) {
angular.element(inlineHelpNode).appendTo(element.find('.inline-help'));
}
if (isSelectControl(field)) {
var $select = element.find('select');
initMaterialSelect($select, field.uiControlAttributes.placeholder);
scope.$watch('formField.value', function (val, oldVal) {
if (val !== oldVal) {
$timeout(function () {
initMaterialSelect($select, field.uiControlAttributes.placeholder);
});
}
});
scope.$watch('formField.uiControlAttributes.disabled', function (val, oldVal) {
if (val !== oldVal) {
$timeout(function () {
initMaterialSelect($select, field.uiControlAttributes.placeholder);
});
}
});
} else if (hasUiControl(field, 'textarea')) {
element.find('textarea').trigger('autoresize');
scope.$watch('formField.value', function (val, oldVal) {
if (val !== oldVal) {
$timeout(function () {
element.find('textarea').trigger('autoresize');
});
}
});
} else if (hasUiControl(field, 'file')) {
// angular doesn't support file input type with ngModel. We implement our own "two way binding"
var $file = element.find('[type=file]');
$file.on('change', function () {
scope.formField.value = $(this).val();
});
scope.$watch('formField.value', function (val, oldVal) {
if (val !== oldVal && val === '') {
$file.val('');
}
});
} else if (isArrayCheckboxControl(field)) {
Materialize.updateTextFields();
scope.$watch('formField.value', function (val, oldVal) {
if (val !== oldVal && val && !oldVal && angular.isArray(val)) {
// we do this only on initial check
syncMultiCheckboxKeysWithFieldValue(field);
}
});
} else if (hasUiControl(field, 'text')
|| hasUiControl(field, 'textarea')
|| hasUiControl(field, 'password')
|| hasUiControl(field, 'email')
|| hasUiControl(field, 'number')
|| hasUiControl(field, 'url')
|| hasUiControl(field, 'search')) {
Materialize.updateTextFields();
scope.$watch('formField.value', function (val, oldVal) {
if (val !== oldVal) {
$timeout(function () {
Materialize.updateTextFields();
});
}
});
}
}
}
function getTemplate(field) {
var control = field.uiControl;
if (control === 'password' || control === 'url' || control === 'search' || control === 'email') {
control = 'text'; // we use same template for text and password both
}
var file = 'field-' + control;
var fieldsSupportingArrays = ['textarea', 'checkbox', 'text'];
if (field.type === 'array' && fieldsSupportingArrays.indexOf(control) !== -1) {
file += '-array';
}
return 'plugins/CorePluginsAdmin/angularjs/form-field/' + file + '.html?cb=' + piwik.cacheBuster;
};
return {
restrict: 'A',
scope: {
piwikFormField: '=',
allSettings: '='
},
templateUrl: 'plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.html?cb=' + piwik.cacheBuster,
compile: function (element, attrs) {
function evaluateConditionalExpression(scope, field)
{
if (!field.condition) {
return;
}
var values = {};
angular.forEach(scope.allSettings, function (setting) {
if (setting.value === '0') {
values[setting.name] = 0;
} else {
values[setting.name] = setting.value;
}
});
field.showField = scope.$eval(field.condition, values);
}
function formatAvailableValues(field)
{
if (!field.availableValues) {
return;
}
var flatValues = [];
if (hasUiControl(field, 'radio') || hasUiControl(field, 'checkbox')) {
angular.forEach(field.availableValues, function (value, key) {
if (angular.isObject(value) && typeof value.key !== 'undefined'){
flatValues.push(value);
return;
}
if (field.type === 'integer' && angular.isString(key)) {
key = parseInt(key, 10);
}
flatValues.push({key: key, value: value});
});
return flatValues;
}
if (hasUiControl(field, 'expandable-select')) {
var availableValues = field.availableValues;
var flatValues = [];
var groups = {};
angular.forEach(availableValues, function (value) {
if (!value.group) {
value.group = '';
}
if (!(value.group in groups) || !groups[value.group]) {
groups[value.group] = {values: [], group: value.group}
}
var formatted = {key: value.key, value: value.value};
if ('tooltip' in value && value.tooltip) {
formatted.tooltip = value.tooltip;
}
groups[value.group].values.push(formatted);
});
angular.forEach(groups, function (group) {
if (group.values.length) {
flatValues.push(group);
}
});
return flatValues;
}
if (isSelectControl(field)) {
var availableValues = field.availableValues;
if (!hasGroupedValues(availableValues)) {
availableValues = {'': availableValues};
}
var flatValues = [];
angular.forEach(availableValues, function (values, group) {
angular.forEach(values, function (value, key) {
if (angular.isObject(value) && typeof value.key !== 'undefined'){
flatValues.push(value);
return;
}
if (field.type === 'integer' && angular.isString(key)) {
key = parseInt(key, 10);
}
flatValues.push({group: group, key: key, value: value});
});
});
return flatValues;
}
return field.availableValues;
}
function formatPrettyDefaultValue(defaultValue, availableOptions) {
if (angular.isString(defaultValue) && defaultValue) {
// eg default value for multi tuple
var defaultParsed = null;
try {
defaultParsed = JSON.parse(defaultValue);
} catch (e) {
// invalid JSON
}
if (angular.isObject(defaultParsed)) {
return null;
}
}
if (!angular.isArray(availableOptions)) {
if (angular.isArray(defaultValue)) {
return null;
}
return defaultValue;
}
var prettyValues = [];
if (!angular.isArray(defaultValue)) {
defaultValue = [defaultValue];
}
angular.forEach(availableOptions, function (value, key) {
if (defaultValue.indexOf(value.key) !== -1 && typeof value.value !== 'undefined') {
prettyValues.push(value.value);
}
});
return prettyValues.join(', ');
}
return function (scope, element, attrs) {
var field = scope.piwikFormField;
var defaultValue = field.defaultValue;
if (angular.isArray(field.defaultValue)) {
field.defaultValue = defaultValue.join(',');
}
// convert boolean values since angular 1.6 uses strict equals when determining if a model value
// matches the ng-value of an input.
if (field.type === 'boolean') {
var valueIsTruthy = field.value && field.value > 0 && field.value !== '0';
// for checkboxes, the value MUST be either true or faluse
if (field.uiControl === 'checkbox') {
field.value = valueIsTruthy;
} else if (field.uiControl === 'radio') {
field.value = valueIsTruthy ? '1' : '0';
}
}
// we are setting availableOptions and not availableValues again. Otherwise when watching the scope
// availableValues and in the watch change availableValues could trigger lots of more watch events
field.availableOptions = formatAvailableValues(field);
// for selects w/ a placeholder, add an option to unset the select
if (field.uiControl === 'select'
&& field.uiControlAttributes.placeholder
&& !hasOption('')
) {
field.availableOptions.splice(0, 0, { key: '', value: '' });
}
field.defaultValuePretty = formatPrettyDefaultValue(defaultValue, field.availableOptions);
field.showField = true;
var inlineHelpNode;
if (field.inlineHelp && field.inlineHelp.indexOf('#') === 0) {
inlineHelpNode = field.inlineHelp;
field.inlineHelp = ' '; // we make sure inline help will be shown
}
if (isArrayCheckboxControl(field)) {
field.updateCheckboxArrayValue = function () {
var values = [];
for (var x in field.checkboxkeys) {
if (field.checkboxkeys[x]) {
values.push(field.availableOptions[x].key);
}
}
field.value = values;
}
field.checkboxkeys = new Array(field.availableOptions.length);
if (field.value && angular.isArray(field.value)) {
syncMultiCheckboxKeysWithFieldValue(field);
}
}
if (field.condition && scope.allSettings) {
evaluateConditionalExpression(scope, field);
for (var key in scope.allSettings) {
if(scope.allSettings.hasOwnProperty(key)) {
scope.$watchCollection('allSettings[' + key + '].value', function (val, oldVal) {
if (val !== oldVal) {
evaluateConditionalExpression(scope, field);
}
});
}
}
}
if (!field.templateFile) {
field.templateFile = getTemplate(field);
}
scope.formField = field;
scope.$watch('formField.availableValues', function (val, oldVal) {
if (val !== oldVal) {
scope.formField.availableOptions = formatAvailableValues(scope.formField);
if (isSelectControl(scope.formField)) {
$timeout(function () {
initMaterialSelect(element.find('select'), field.uiControlAttributes.placeholder);
});
}
}
});
scope.templateLoaded = function () {
$timeout(whenRendered(scope, element, inlineHelpNode));
};
function hasOption(key) {
for (var i = 0; i !== field.availableOptions.length; ++i) {
if (field.availableOptions[i].key === key) {
return true;
}
}
return false;
}
};
}
};
}
})();

View File

@ -0,0 +1,39 @@
/*!
* 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-form>
*/
(function () {
angular.module('piwikApp').directive('piwikForm', piwikForm);
piwikForm.$inject = ['piwik', '$timeout'];
function piwikForm(piwik, $timeout){
return {
restrict: 'A',
priority: '10',
compile: function (element, attrs) {
return function (scope, element, attrs) {
$timeout(function () {
element.find('input[type=text]').keypress(function (e) {
var key = e.keyCode || e.which;
if (key == 13) {
element.find('[piwik-save-button] input').triggerHandler('click');
}
});
});
};
}
};
}
})();

View File

@ -0,0 +1,74 @@
/*!
* 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('PluginSettingsController', PluginSettingsController);
PluginSettingsController.$inject = ['$scope', 'piwikApi'];
function PluginSettingsController($scope, piwikApi) {
// remember to keep controller very simple. Create a service/factory (model) if needed
var self = this;
this.isLoading = true;
this.isSaving = {};
var apiMethod = 'CorePluginsAdmin.getUserSettings';
if ($scope.mode === 'admin') {
apiMethod = 'CorePluginsAdmin.getSystemSettings';
}
piwikApi.fetch({method: apiMethod}).then(function (settings) {
self.isLoading = false;
self.settingsPerPlugin = settings;
}, function () {
self.isLoading = false;
});
this.save = function (settings) {
var apiMethod = 'CorePluginsAdmin.setUserSettings';
if ($scope.mode === 'admin') {
apiMethod = 'CorePluginsAdmin.setSystemSettings';
}
this.isSaving[settings.pluginName] = true;
var values = {};
if (!values[settings.pluginName]) {
values[settings.pluginName] = [];
}
angular.forEach(settings.settings, function (setting) {
var value = setting.value;
if (value === false) {
value = '0';
} else if (value === true) {
value = '1';
}
values[settings.pluginName].push({
name: setting.name,
value: value
});
});
piwikApi.post({method: apiMethod}, {settingValues: values}).then(function (success) {
self.isSaving[settings.pluginName] = false;
var UI = require('piwik/UI');
var notification = new UI.Notification();
notification.show(_pk_translate('CoreAdminHome_PluginSettingsSaveSuccess'), {
id: 'generalSettings', context: 'success'
});
notification.scrollToNotification();
}, function () {
self.isSaving[settings.pluginName] = false;
});
};
}
})();

View File

@ -0,0 +1,25 @@
<div class="pluginSettings">
<div ng-repeat="settings in pluginSettings.settingsPerPlugin"
class="card"
id="{{ settings.pluginName }}PluginSettings">
<div class="card-content">
<h2 id="{{ settings.pluginName }}" class="card-title">{{ settings.title }}</h2>
<div ng-repeat="setting in settings.settings">
<div piwik-form-field="setting" all-settings="settings.settings"></div>
</div>
<input type="button" ng-click="pluginSettings.save(settings)"
ng-disabled="pluginSettings.isLoading"
value="{{ 'General_Save'|translate }}"
class="pluginsSettingsSubmit btn"/>
<div piwik-activity-indicator loading="pluginSettings.isLoading || pluginSettings.isSaving[settings.pluginName]"></div>
</div>
</div>
</div>

View File

@ -0,0 +1,44 @@
/*!
* 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-plugin-settings>
*/
(function () {
angular.module('piwikApp').directive('piwikPluginSettings', piwikPluginSettings);
piwikPluginSettings.$inject = ['piwik'];
function piwikPluginSettings(piwik){
var defaults = {
mode: ''
};
return {
restrict: 'A',
scope: {
mode: '@'
},
templateUrl: 'plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.html?cb=' + piwik.cacheBuster,
controller: 'PluginSettingsController',
controllerAs: 'pluginSettings',
compile: function (element, attrs) {
for (var index in defaults) {
if (defaults.hasOwnProperty(index) && attrs[index] === undefined) {
attrs[index] = defaults[index];
}
}
return function (scope, element, attrs) {
};
}
};
}
})();

View File

@ -0,0 +1,9 @@
.pluginSettings {
textarea {
width: 376px;
height: 250px;
}
}
.pluginsSettingsSubmit {
margin-top: 30px;
}

View File

@ -0,0 +1,113 @@
/*!
* 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-plugin-filter>
*/
(function () {
angular.module('piwikApp').directive('piwikPluginFilter', piwikPluginFilter);
piwikPluginFilter.$inject = ['piwik'];
function piwikPluginFilter(piwik){
return {
restrict: 'A',
compile: function (element, attrs) {
return function (scope, element, attrs) {
updateAllNumbersOfMatchingPluginsInFilter();
function filterPlugins()
{
var filterOrigin = getCurrentFilterOrigin();
var filterStatus = getCurrentFilterStatus();
var $nodesToEnable = getMatchingNodes(filterOrigin, filterStatus);
$('#plugins tr[data-filter-origin][data-filter-status]').css('display', 'none');
$nodesToEnable.css('display', 'table-row');
updateAllNumbersOfMatchingPluginsInFilter();
}
function updateAllNumbersOfMatchingPluginsInFilter()
{
var filterOrigin = getCurrentFilterOrigin();
var filterStatus = getCurrentFilterStatus();
updateNumberOfMatchingPluginsInFilter('[data-filter-status="all"]', filterOrigin, 'all');
updateNumberOfMatchingPluginsInFilter('[data-filter-status="active"]', filterOrigin, 'active');
updateNumberOfMatchingPluginsInFilter('[data-filter-status="inactive"]', filterOrigin, 'inactive');
updateNumberOfMatchingPluginsInFilter('[data-filter-origin="all"]', 'all', filterStatus);
updateNumberOfMatchingPluginsInFilter('[data-filter-origin="core"]', 'core', filterStatus);
updateNumberOfMatchingPluginsInFilter('[data-filter-origin="official"]', 'official', filterStatus);
updateNumberOfMatchingPluginsInFilter('[data-filter-origin="thirdparty"]', 'thirdparty', filterStatus);
}
function updateNumberOfMatchingPluginsInFilter(selectorFilterToUpdate, filterOrigin, filterStatus)
{
var numMatchingNodes = getMatchingNodes(filterOrigin, filterStatus).length;
var updatedCounterText = ' (' + numMatchingNodes + ')';
element.find(selectorFilterToUpdate + ' .counter').text(updatedCounterText);
}
function getCurrentFilterOrigin()
{
return element.find('.origin a.active').data('filter-origin');
}
function getCurrentFilterStatus()
{
return element.find('.status a.active').data('filter-status');
}
function getMatchingNodes(filterOrigin, filterStatus)
{
var query = '#plugins tr';
if ('all' == filterOrigin) {
query += '[data-filter-origin]';
} else {
query += '[data-filter-origin=' + filterOrigin + ']';
}
if ('all' == filterStatus) {
query += '[data-filter-status]';
} else {
query += '[data-filter-status=' + filterStatus + ']';
}
return $(query);
}
element.find('.status').on('click', 'a', function (event) {
event.preventDefault();
$(this).siblings().removeClass('active');
$(this).addClass('active');
filterPlugins();
});
element.find('.origin').on('click', 'a', function (event) {
event.preventDefault();
$(this).siblings().removeClass('active');
$(this).addClass('active');
filterPlugins();
});
};
}
};
}
})();

View File

@ -0,0 +1,65 @@
/*!
* 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-plugin-management>
*/
(function () {
angular.module('piwikApp').directive('piwikPluginManagement', piwikPluginManagement);
piwikPluginManagement.$inject = ['piwik'];
function piwikPluginManagement(piwik){
return {
restrict: 'A',
compile: function (element, attrs) {
return function (scope, element, attrs) {
var uninstallConfirmMessage = '';
element.find('.uninstall').click(function (event) {
event.preventDefault();
var link = $(this).attr('href');
var pluginName = $(this).attr('data-plugin-name');
if (!link || !pluginName) {
return;
}
if (!uninstallConfirmMessage) {
uninstallConfirmMessage = $('#uninstallPluginConfirm').text();
}
var messageToDisplay = uninstallConfirmMessage.replace('%s', pluginName);
$('#uninstallPluginConfirm').text(messageToDisplay);
piwikHelper.modalConfirm('#confirmUninstallPlugin', {
yes: function () {
window.location = link;
}
});
});
element.find('.plugin-donation-link').click(function (event) {
event.preventDefault();
var overlayId = $(this).data('overlay-id');
piwikHelper.modalConfirm('#'+overlayId, {});
});
};
}
};
}
})();

View File

@ -0,0 +1,46 @@
/*!
* 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-plugin-upload>
*/
(function () {
angular.module('piwikApp').directive('piwikPluginUpload', piwikPluginUpload);
piwikPluginUpload.$inject = ['piwik'];
function piwikPluginUpload(piwik){
return {
restrict: 'A',
compile: function (element, attrs) {
return function (scope, element, attrs) {
$('.uploadPlugin').click(function (event) {
event.preventDefault();
piwikHelper.modalConfirm('#installPluginByUpload', {});
});
$('#uploadPluginForm').submit(function (event) {
var $zipFile = $('[name=pluginZip]');
var fileName = $zipFile.val();
if (!fileName || '.zip' != fileName.slice(-4)) {
event.preventDefault();
alert(_pk_translate('CorePluginsAdmin_NoZipFileSelected'));
}
});
};
}
};
}
})();

View File

@ -0,0 +1,8 @@
<div style="display:inline-block;">
<input type="button"
ng-click="onconfirm()"
value="{{ value ? value : ('General_Save'|translate) }}"
ng-disabled="saving || disabled"
class="btn"/>
<div piwik-activity-indicator loading="saving"></div>
</div>

View File

@ -0,0 +1,31 @@
/*!
* 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-save-button>
*/
(function () {
angular.module('piwikApp').directive('piwikSaveButton', piwikSaveButton);
piwikSaveButton.$inject = ['piwik'];
function piwikSaveButton(piwik){
return {
restrict: 'A',
replace: true,
scope: {
saving: '=?',
value: '@?',
disabled: '=?',
onconfirm: '&?'
},
templateUrl: 'plugins/CorePluginsAdmin/angularjs/save-button/save-button.directive.html?cb=' + piwik.cacheBuster
};
}
})();