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,206 @@
<div class="pagedUsersList" ng-class="{loading: $ctrl.isLoadingUsers}">
<div class="userListFilters row">
<div class="col s12 m12 l6">
<div class="input-field col s12 m4 l4">
<a
class='dropdown-trigger btn bulk-actions'
href=''
data-activates='user-list-bulk-actions'
piwik-dropdown-menu
ng-class="{ disabled: $ctrl.isBulkActionsDisabled }"
>
{{:: 'UsersManager_BulkActions'|translate }}
</a>
<ul id='user-list-bulk-actions' class='dropdown-content'>
<li>
<a
class='dropdown-trigger'
data-activates="bulk-set-access"
piwik-dropdown-menu
>
{{:: 'UsersManager_SetPermission'|translate }}
</a>
<ul id="bulk-set-access" class="dropdown-content">
<li ng-repeat="access in $ctrl.bulkActionAccessLevels">
<a
href=""
ng-click="$ctrl.userToChange = null; $ctrl.roleToChangeTo = access.key; $ctrl.showAccessChangeConfirm();"
>
{{ access.value }}
</a>
</li>
</ul>
</li>
<li>
<a
href=""
ng-click="$ctrl.userToChange = null; $ctrl.roleToChangeTo = 'noaccess'; $ctrl.showAccessChangeConfirm();"
>
{{:: 'UsersManager_RemovePermissions'|translate }}
</a>
</li>
<li ng-if="$ctrl.currentUserRole == 'superuser'">
<a href="" ng-click="$ctrl.showDeleteConfirm()">{{:: 'UsersManager_DeleteUsers'|translate }}</a>
</li>
</ul>
</div>
<div class="input-field col s12 m4 l4">
<div
piwik-field
name="user-text-filter"
class="permissions-for-selector"
uicontrol="text"
ng-model="$ctrl.userTextFilter"
ng-model-options="{debounce: 300}"
placeholder="{{:: 'UsersManager_UserSearch'|translate }}"
full-width="true"
ng-change="$ctrl.changeSearch({ filter_search: $ctrl.userTextFilter })"
></div>
</div>
<div class="input-field col s12 m4 l4">
<div
piwik-field
name="access-level-filter"
uicontrol="select"
ng-model="$ctrl.accessLevelFilter"
placeholder="{{:: 'UsersManager_FilterByAccess'|translate }}"
options="$ctrl.filterAccessLevels"
full-width="true"
ng-change="$ctrl.changeSearch({ filter_access: $ctrl.accessLevelFilter })"
></div>
</div>
</div>
<div class="input-field col s12 m12 l6 users-list-pagination-container" ng-if="$ctrl.totalEntries > $ctrl.searchParams.limit">
<div class="usersListPagination">
<a class="btn prev" ng-class="{ disabled: $ctrl.searchParams.offset <= 0 }" ng-click="$ctrl.gotoPreviousPage()">
<span class="pointer">« {{:: 'General_Previous'|translate }}</span>
</a>
<div class="counter">
<span>
{{ 'General_Pagination'|translate:$ctrl.getPaginationLowerBound():$ctrl.getPaginationUpperBound():$ctrl.totalEntries }}
</span>
<div piwik-activity-indicator ng-if="$ctrl.isLoadingUsers" loading="$ctrl.isLoadingUsers"></div>
</div>
<a class="btn next" ng-class="{ disabled: $ctrl.searchParams.offset + $ctrl.searchParams.limit >= $ctrl.totalEntries }" ng-click="$ctrl.gotoNextPage()">
<span class="pointer">{{:: 'General_Next'|translate }} »</span>
</a>
</div>
</div>
</div>
<div piwik-notification context="info" type="persistent" noclear="true" ng-if="$ctrl.isRoleHelpToggled" class="roles-help-notification">
<span piwik-translate="UsersManager_RolesHelp"><a href='https://matomo.org/faq/general/faq_70/' target='_blank' rel='noreferrer noopener'>::</a>::<a href='https://matomo.org/faq/general/faq_69/' target='_blank' rel='noreferrer noopener'>::</a></span>
</div>
<div piwik-content-block>
<table piwik-content-table id="manageUsersTable" ng-class="{ loading: $ctrl.isLoadingUsers }">
<thead>
<tr>
<th class="select-cell">
<span class="checkbox-container">
<input type="checkbox" id="paged_users_select_all" checked="checked" ng-model="$ctrl.isAllCheckboxSelected" ng-change="$ctrl.onAllCheckboxChange()" />
<label for="paged_users_select_all"></label>
</span>
</th>
<th class='first'>{{:: 'UsersManager_Username'|translate }}</th>
<th class="role_header">
<span>{{:: 'UsersManager_RoleFor'|translate }}</span>
<a href="" class="helpIcon" ng-click="$ctrl.isRoleHelpToggled = !$ctrl.isRoleHelpToggled" ng-class="{ sticky: $ctrl.isRoleHelpToggled }">
<span class="icon-help"></span>
</a>
<div
piwik-field
class="permissions-for-selector"
uicontrol="site"
ng-model="$ctrl.permissionsForSite"
ng-change="$ctrl.changeSearch({ idSite: $ctrl.permissionsForSite.id })"
ui-control-attributes="{ onlySitesWithAdminAccess: $ctrl.currentUserRole !== 'superuser' }"
></div>
</th>
<th ng-if="$ctrl.currentUserRole == 'superuser'">{{:: 'UsersManager_Email'|translate }}</th>
<th ng-if="$ctrl.currentUserRole == 'superuser'" title="{{'UsersManager_UsesTwoFactorAuthentication'|translate}}">{{:: 'UsersManager_2FA'|translate }}</th>
<th ng-if="$ctrl.currentUserRole == 'superuser'">{{:: 'UsersManager_LastSeen'|translate }}</th>
<th class="actions-cell-header"><div>{{:: 'General_Actions'|translate }}</div></th>
</tr>
</thead>
<tbody>
<tr class="select-all-row" ng-if="$ctrl.isAllCheckboxSelected && $ctrl.users.length && $ctrl.users.length < $ctrl.totalEntries">
<td colspan="8">
<div ng-if="!$ctrl.areAllResultsSelected">
<span piwik-translate="UsersManager_TheDisplayedUsersAreSelected"><strong>{{ $ctrl.users.length }}</strong></span>
<a class="toggle-select-all-in-search" href="#" ng-click="$ctrl.areAllResultsSelected = !$ctrl.areAllResultsSelected" piwik-translate="UsersManager_ClickToSelectAll"><strong>{{ $ctrl.totalEntries }}</strong></a>
</div>
<div ng-if="$ctrl.areAllResultsSelected">
<span piwik-translate="UsersManager_AllUsersAreSelected"><strong>{{ $ctrl.totalEntries }}</strong></span>
<a class="toggle-select-all-in-search" href="#" ng-click="$ctrl.areAllResultsSelected = !$ctrl.areAllResultsSelected" piwik-translate="UsersManager_ClickToSelectDisplayedUsers"><strong>{{ $ctrl.users.length }}</strong></a>
</div>
</td>
</tr>
<tr ng-repeat="user in $ctrl.users" ng-attr-id="row{{ $index }}">
<td class="select-cell">
<span class="checkbox-container">
<input type="checkbox" ng-attr-id="paged_users_select_row{{ $index }}" checked="checked" ng-model="$ctrl.selectedRows[$index]" ng-click="$ctrl.onRowSelected()" />
<label ng-attr-for="paged_users_select_row{{ $index }}"></label>
</span>
</td>
<td id="userLogin">{{ user.login }}</td>
<td class="access-cell">
<div
piwik-field
uicontrol="select"
ng-model="user.role"
options="user.login != 'anonymous' ? $ctrl.accessLevels : $ctrl.anonymousAccessLevels"
ng-change="$ctrl.userToChange = user; $ctrl.roleToChangeTo = user.role; $ctrl.previousRole = '{{ user.role }}'; $ctrl.showAccessChangeConfirm();"
data-disabled="user.role == 'superuser'"
></div>
</td>
<td id="email" ng-if="$ctrl.currentUserRole == 'superuser'">{{ user.email }}</td>
<td id="twofa" ng-if="$ctrl.currentUserRole == 'superuser'">
{{ user.uses_2fa ? ('✓') : '☓' }}
</td>
<td id="last_seen" ng-if="$ctrl.currentUserRole == 'superuser'">
{{ user.last_seen ? (user.last_seen + ' ago') : '-' }}
</td>
<td class="center actions-cell">
<button class="edituser table-action" title="Edit" ng-click="$ctrl.onEditUser({ user: user })" ng-if="user.login != 'anonymous'">
<span class="icon-edit"></span>
</button>
<button class="deleteuser table-action" title="Delete" ng-click="$ctrl.userToChange = user; $ctrl.showDeleteConfirm()" ng-if="$ctrl.currentUserRole == 'superuser' && user.login != 'anonymous'">
<span class="icon-delete"></span>
</button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="delete-user-confirm-modal modal">
<div class="modal-content">
<h3 ng-if="$ctrl.userToChange" piwik-translate="UsersManager_DeleteUserConfirmSingle"><strong>{{ $ctrl.userToChange.login }}</strong></h3>
<p ng-if="!$ctrl.userToChange" piwik-translate="UsersManager_DeleteUserConfirmMultiple"><strong>{{ $ctrl.getAffectedUsersCount() }}</strong></p>
</div>
<div class="modal-footer">
<a href="" class="modal-action modal-close btn" ng-click="$ctrl.deleteRequestedUsers()">{{:: 'General_Yes'|translate }}</a>
<a href="" class="modal-action modal-close modal-no" ng-click="$ctrl.userToChange = null; $ctrl.roleToChangeTo = null;">{{:: 'General_No'|translate }}</a>
</div>
</div>
<div class="change-user-role-confirm-modal modal">
<div class="modal-content">
<h3 ng-if="$ctrl.userToChange" piwik-translate="UsersManager_DeleteUserPermConfirmSingle"><strong>{{ $ctrl.userToChange.login }}</strong>::<strong>{{ $ctrl.getRoleDisplay($ctrl.roleToChangeTo) }}</strong>::<strong>{{ $ctrl.permissionsForSite.name }}</strong></h3>
<h3 ng-if="$ctrl.userToChange && $ctrl.userToChange.login == 'anonymous' && $ctrl.roleToChangeTo == 'view'"><em>{{ 'General_Note'|translate }}: <span piwik-translate="UsersManager_AnonymousUserRoleChangeWarning">anonymous::{{ $ctrl.getRoleDisplay($ctrl.roleToChangeTo) }}</span></em></h3>
<p ng-if="!$ctrl.userToChange" piwik-translate="UsersManager_DeleteUserPermConfirmMultiple"><strong>{{ $ctrl.getAffectedUsersCount() }}</strong>::<strong>{{ $ctrl.getRoleDisplay($ctrl.roleToChangeTo) }}</strong>::<strong>{{ $ctrl.permissionsForSite.name }}</strong></p>
</div>
<div class="modal-footer">
<a href="" class="modal-action modal-close btn" ng-click="$ctrl.changeUserRole()">{{:: 'General_Yes'|translate }}</a>
<a href="" class="modal-action modal-close modal-no" ng-click="$ctrl.userToChange.role = $ctrl.previousRole; $ctrl.userToChange = null; $ctrl.roleToChangeTo = null;">{{:: 'General_No'|translate }}</a>
</div>
</div>
</div>

View File

@ -0,0 +1,223 @@
/*!
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
/**
* Usage:
* <piwik-paged-users-list>
*/
(function () {
angular.module('piwikApp').component('piwikPagedUsersList', {
templateUrl: 'plugins/UsersManager/angularjs/paged-users-list/paged-users-list.component.html?cb=' + piwik.cacheBuster,
bindings: {
onEditUser: '&',
onChangeUserRole: '&',
onDeleteUser: '&',
onSearchChange: '&',
initialSiteId: '<',
initialSiteName: '<',
currentUserRole: '<',
isLoadingUsers: '<',
accessLevels: '<',
filterAccessLevels: '<',
totalEntries: '<',
users: '<',
searchParams: '<'
},
controller: PagedUsersListController
});
PagedUsersListController.$inject = ['$element'];
function PagedUsersListController($element) {
var vm = this;
// options for selects
vm.bulkActionAccessLevels = null;
// selection state
vm.areAllResultsSelected = false;
vm.selectedRows = {};
vm.isAllCheckboxSelected = false;
// intermediate state
vm.isBulkActionsDisabled = true;
vm.userToChange = null;
vm.roleToChangeTo = null;
vm.previousRole = null;
vm.accessLevelFilter = null;
// other state
vm.isRoleHelpToggled = false;
vm.$onInit = $onInit;
vm.$onChanges = $onChanges;
vm.onAllCheckboxChange = onAllCheckboxChange;
vm.changeUserRole = changeUserRole;
vm.onRowSelected = onRowSelected;
vm.deleteRequestedUsers = deleteRequestedUsers;
vm.getPaginationLowerBound = getPaginationLowerBound;
vm.getPaginationUpperBound = getPaginationUpperBound;
vm.showDeleteConfirm = showDeleteConfirm;
vm.getAffectedUsersCount = getAffectedUsersCount;
vm.showAccessChangeConfirm = showAccessChangeConfirm;
vm.getRoleDisplay = getRoleDisplay;
vm.changeSearch = changeSearch;
vm.gotoPreviousPage = gotoPreviousPage;
vm.gotoNextPage = gotoNextPage;
function changeSearch(changes) {
var newParams = $.extend({}, vm.searchParams, changes);
vm.onSearchChange({ params: newParams });
}
function $onInit() {
vm.permissionsForSite = {
id: vm.initialSiteId,
name: vm.initialSiteName
};
vm.bulkActionAccessLevels = [];
vm.anonymousAccessLevels = [];
vm.accessLevels.forEach(function (entry) {
if (entry.key !== 'noaccess' && entry.key !== 'superuser') {
vm.bulkActionAccessLevels.push(entry);
}
if (entry.key === 'noaccess' || entry.key === 'view') {
vm.anonymousAccessLevels.push(entry);
}
});
}
function $onChanges(changes) {
if (changes.users) {
clearSelection();
}
}
function onAllCheckboxChange() {
if (!vm.isAllCheckboxSelected) {
clearSelection();
} else {
for (var i = 0; i !== vm.users.length; ++i) {
vm.selectedRows[i] = true;
}
vm.isBulkActionsDisabled = false;
}
}
function clearSelection() {
vm.selectedRows = {};
vm.areAllResultsSelected = false;
vm.isBulkActionsDisabled = true;
vm.isAllCheckboxSelected = false;
vm.userToChange = null;
}
function changeUserRole() {
vm.onChangeUserRole({
users: getUserOperationSubject(),
role: vm.roleToChangeTo
});
}
function deleteRequestedUsers() {
vm.onDeleteUser({
users: getUserOperationSubject(),
});
}
function getUserOperationSubject() {
if (vm.userToChange) {
return [vm.userToChange];
} else if (vm.areAllResultsSelected) {
return 'all';
} else {
return getSelectedUsers();
}
}
function showAccessChangeConfirm() {
$element.find('.change-user-role-confirm-modal').openModal({ dismissible: false });
}
function getAffectedUsersCount() {
if (vm.areAllResultsSelected) {
return vm.totalEntries;
}
return getSelectedCount();
}
function onRowSelected() {
var selectedRowKeyCount = getSelectedCount();
vm.isBulkActionsDisabled = selectedRowKeyCount === 0;
vm.isAllCheckboxSelected = selectedRowKeyCount === vm.users.length;
}
function getSelectedCount() {
var selectedRowKeyCount = 0;
Object.keys(vm.selectedRows).forEach(function (key) {
if (vm.selectedRows[key]) {
++selectedRowKeyCount;
}
});
return selectedRowKeyCount;
}
function getSelectedUsers() {
var result = [];
Object.keys(vm.selectedRows).forEach(function (index) {
if (vm.selectedRows[index]
&& vm.users[index] // sanity check
) {
result.push(vm.users[index]);
}
});
return result;
}
function getPaginationLowerBound() {
return vm.searchParams.offset + 1;
}
function getPaginationUpperBound() {
return Math.min(vm.searchParams.offset + vm.searchParams.limit, vm.totalEntries);
}
function showDeleteConfirm() {
$element.find('.delete-user-confirm-modal').openModal({ dismissible: false });
}
function getRoleDisplay(role) {
var result = null;
vm.accessLevels.forEach(function (entry) {
if (entry.key === role) {
result = entry.value;
}
});
return result;
}
function gotoPreviousPage() {
changeSearch({
offset: Math.max(0, vm.searchParams.offset - vm.searchParams.limit)
});
}
function gotoNextPage() {
var newOffset = vm.searchParams.offset + vm.searchParams.limit;
if (newOffset >= vm.totalEntries) {
return;
}
changeSearch({
offset: newOffset,
});
}
}
})();

View File

@ -0,0 +1,203 @@
piwik-paged-users-list {
display: block;
position: relative;
[piwik-siteselector] {
display: inline-block;
margin-left: .3rem;
}
.access-display-control {
position: absolute;
margin-left: .1rem;
label {
color: @theme-color-text;
}
}
.card {
margin-top: 0;
margin-bottom: 20px;
}
.card .card-content {
padding-top: 0;
padding-bottom: 0;
}
table.entityTable tbody tr td {
vertical-align: middle !important;
}
table.entityTable tbody tr td.actions-cell {
width: 130px;
padding: 0;
text-align: center;
}
table.entityTable th.actions-cell-header > div {
text-align: center;
}
table#manageUsersTable {
[piwik-field] {
.form-group {
margin: 0;
}
.input-field {
margin-top: 0;
padding: 0;
}
}
.select-wrapper {
transform: scale(.8);
width: 100px;
input {
margin-bottom: 0;
padding-bottom: .3em;
height: 1em;
line-height: 1em;
}
span.caret {
top: 0;
}
}
th.role_header {
.helpIcon {
color: #9e9e9e;
font-size: .8rem;
margin-left: .1rem;
text-decoration: none;
&:hover,&.sticky {
opacity: 1;
}
}
}
}
tbody span.checkbox-container {
label {
transform: scale(.8);
height: 1em;
line-height: 1em;
}
}
.select-cell {
width: 32px;
}
table.entityTable tbody tr.select-all-row > td {
padding: 6px;
text-align: center;
}
.sites_autocomplete {
display: block;
margin-left: 0;
}
.userListFilters {
> .col > .input-field {
display: inline-block;
vertical-align: top;
padding: 0;
}
.form-group, [piwik-form-field] .input-field {
margin: 0;
}
[piwik-form-field] input {
margin-bottom: 0;
}
.input-field > .btn {
margin-top: .7rem;
}
&.row {
margin-bottom: 0;
margin-left: -0.75rem;
margin-right: -0.75rem;
}
}
.usersListPagination {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin-top: .7rem;
float: right;
.pointer {
cursor: pointer;
}
div.counter {
display: inline-block;
line-height: 36px;
vertical-align: bottom;
flex: 1;
text-align: center;
margin-left: 10px;
margin-right: 10px;
}
}
.delete-user-confirm-modal,.change-user-role-confirm-modal {
.modal-no {
float: right;
margin-right: 1em;
margin-top: 1em;
}
}
.pagedUsersList.loading {
table {
opacity: 0.5;
}
a, input, select, button, label {
pointer-events: none;
}
div.counter {
position: relative;
> span {
opacity: 0;
}
}
div[piwik-activity-indicator] {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
span {
display: none;
}
}
}
.roles-help-notification {
margin-top: 1rem;
}
}
#root piwik-paged-users-list .siteSelector.borderedControl {
background-color: white;
width: 150px;
}
#content piwik-paged-users-list .sites_autocomplete > .siteSelector {
position: static;
}