PDF rausgenommen
This commit is contained in:
@ -0,0 +1,48 @@
|
||||
<div class="capabilitiesEdit" ng-class="{ busy: $ctrl.isBusy }">
|
||||
<div
|
||||
class="chip"
|
||||
ng-repeat="capability in $ctrl.availableCapabilities"
|
||||
ng-if="$ctrl.capabilitiesSet[capability.id]"
|
||||
>
|
||||
<span
|
||||
class="capability-name"
|
||||
title="{{ capability.description }} {{ ($ctrl.isIncludedInRole(capability) ? ('<br/><br/>' + ('UsersManager_IncludedInUsersRole'|translate)): '') }}"
|
||||
>
|
||||
{{ capability.category }}: {{ capability.name }}
|
||||
</span>
|
||||
<span
|
||||
class="icon-close"
|
||||
ng-if="!$ctrl.isIncludedInRole(capability)"
|
||||
ng-click="$ctrl.capabilityToAddOrRemove = capability; $ctrl.onToggleCapability(fakse)"
|
||||
></span>
|
||||
</div>
|
||||
|
||||
<div piwik-field uicontrol="expandable-select"
|
||||
name="add_capability"
|
||||
full-width="true"
|
||||
ng-change="$ctrl.onToggleCapability(true)"
|
||||
ng-model="$ctrl.capabilityToAddOrRemoveId"
|
||||
options="$ctrl.availableCapabilitiesGrouped"
|
||||
data-disabled="$ctrl.isBusy"
|
||||
ng-if="$ctrl.availableCapabilitiesGrouped.length"
|
||||
class="addCapability"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="ui-confirm confirmCapabilityToggle modal">
|
||||
<div class="modal-content">
|
||||
<h2 ng-if="$ctrl.isAddingCapability" piwik-translate="UsersManager_AreYouSureAddCapability">
|
||||
<strong>{{ $ctrl.userLogin }}</strong>::<strong>{{ $ctrl.capabilityToAddOrRemove.name }}</strong>::<strong>{{ $ctrl.siteName }}</strong>
|
||||
</h2>
|
||||
<h2 ng-if="!$ctrl.isAddingCapability" piwik-translate="UsersManager_AreYouSureRemoveCapability">
|
||||
<strong>{{ $ctrl.capabilityToAddOrRemove.name }}</strong>::<strong>{{ $ctrl.userLogin }}</strong>::<strong>{{ $ctrl.siteName }}</strong>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="" class="modal-action modal-close btn" ng-click="$ctrl.toggleCapability()">{{:: 'General_Yes'|translate }}</a>
|
||||
<a href="" class="modal-action modal-close modal-no" ng-click="$ctrl.capabilityToAddOrRemove = null; $ctrl.capabilityToAddOrRemoveId = null">
|
||||
{{:: 'General_No'|translate }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,213 @@
|
||||
/*!
|
||||
* 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-capabilities-edit>
|
||||
*/
|
||||
(function () {
|
||||
angular.module('piwikApp').component('piwikCapabilitiesEdit', {
|
||||
templateUrl: 'plugins/UsersManager/angularjs/capabilities-edit/capabilities-edit.component.html?cb=' + piwik.cacheBuster,
|
||||
bindings: {
|
||||
idsite: '<',
|
||||
siteName: '<',
|
||||
userLogin: '<',
|
||||
userRole: '<',
|
||||
capabilities: '<',
|
||||
onCapabilitiesChange: '&',
|
||||
},
|
||||
controller: CapabilitiesEditController
|
||||
});
|
||||
|
||||
CapabilitiesEditController.$inject = ['piwikApi', 'permissionsMetadataService', 'piwik', '$element'];
|
||||
|
||||
function CapabilitiesEditController(piwikApi, permissionsMetadataService, piwik, $element) {
|
||||
var vm = this;
|
||||
|
||||
vm.isBusy = false;
|
||||
vm.availableCapabilities = [];
|
||||
vm.availableCapabilitiesGrouped = [];
|
||||
vm.capabilitiesSet = {};
|
||||
|
||||
// intermediate state
|
||||
vm.isAddingCapability = false;
|
||||
vm.capabilityToAddOrRemoveId = null;
|
||||
vm.capabilityToAddOrRemove = null;
|
||||
|
||||
vm.$onInit = $onInit;
|
||||
vm.$onChanges = $onChanges;
|
||||
vm.onToggleCapability = onToggleCapability;
|
||||
vm.toggleCapability = toggleCapability;
|
||||
vm.isIncludedInRole = isIncludedInRole;
|
||||
|
||||
function $onInit() {
|
||||
fetchAvailableCapabilities();
|
||||
|
||||
if (typeof vm.capabilities === 'undefined') {
|
||||
fetchCapabilities();
|
||||
}
|
||||
}
|
||||
|
||||
function $onChanges() {
|
||||
setCapabilitiesSet();
|
||||
}
|
||||
|
||||
function isIncludedInRole(capability) {
|
||||
return capability.includedInRoles.indexOf(vm.userRole) !== -1;
|
||||
}
|
||||
|
||||
function fetchAvailableCapabilities() {
|
||||
permissionsMetadataService.getAllCapabilities()
|
||||
.then(function (capabilities) {
|
||||
vm.availableCapabilities = capabilities;
|
||||
setCapabilitiesSet();
|
||||
setAvailableCapabilitiesDropdown();
|
||||
});
|
||||
}
|
||||
|
||||
function fetchCapabilities() {
|
||||
vm.isBusy = true;
|
||||
piwikApi.fetch({
|
||||
method: 'UsersManager.getUsersPlusRole',
|
||||
limit: '1',
|
||||
filter_search: vm.userLogin,
|
||||
}).then(function (user) {
|
||||
if (!user || !user.capabilities) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return user.capabilities;
|
||||
}).then(function (capabilities) {
|
||||
vm.capabilities = capabilities;
|
||||
setCapabilitiesSet();
|
||||
setAvailableCapabilitiesDropdown();
|
||||
})['finally'](function () {
|
||||
vm.isBusy = false;
|
||||
});
|
||||
}
|
||||
|
||||
function setCapabilitiesSet() {
|
||||
vm.capabilitiesSet = {};
|
||||
(vm.capabilities || []).forEach(function (capability) {
|
||||
vm.capabilitiesSet[capability] = true;
|
||||
});
|
||||
(vm.availableCapabilities || []).forEach(function (capability) {
|
||||
if (vm.isIncludedInRole(capability)) {
|
||||
vm.capabilitiesSet[capability.id] = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setAvailableCapabilitiesDropdown() {
|
||||
var availableCapabilitiesGrouped = [];
|
||||
vm.availableCapabilities.forEach(function (capability) {
|
||||
if (vm.capabilitiesSet[capability.id]) {
|
||||
return;
|
||||
}
|
||||
|
||||
availableCapabilitiesGrouped.push({
|
||||
group: capability.category,
|
||||
key: capability.id,
|
||||
value: capability.name,
|
||||
tooltip: capability.description,
|
||||
});
|
||||
});
|
||||
vm.availableCapabilitiesGrouped = availableCapabilitiesGrouped;
|
||||
vm.availableCapabilitiesGrouped.sort(function (lhs, rhs) {
|
||||
if (lhs.group === rhs.group) {
|
||||
if (lhs.value === rhs.value) {
|
||||
return 0;
|
||||
}
|
||||
return lhs.value < rhs.value ? -1 : 1;
|
||||
}
|
||||
return lhs.group < rhs.group ? -1 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
function onToggleCapability(isAdd) {
|
||||
vm.isAddingCapability = isAdd;
|
||||
|
||||
vm.availableCapabilities.forEach(function (capability) {
|
||||
if (capability.id === vm.capabilityToAddOrRemoveId) {
|
||||
vm.capabilityToAddOrRemove = capability;
|
||||
}
|
||||
});
|
||||
|
||||
$element.find('.confirmCapabilityToggle').openModal({
|
||||
dismissible: false,
|
||||
yes: function () {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function toggleCapability() {
|
||||
if (vm.isAddingCapability) {
|
||||
addCapability(vm.capabilityToAddOrRemove);
|
||||
} else {
|
||||
removeCapability(vm.capabilityToAddOrRemove);
|
||||
}
|
||||
}
|
||||
|
||||
function addCapability(capability) {
|
||||
vm.isBusy = true;
|
||||
piwikApi.post({
|
||||
method: 'UsersManager.addCapabilities',
|
||||
}, {
|
||||
userLogin: vm.userLogin,
|
||||
capabilities: capability.id,
|
||||
idSites: vm.idsite
|
||||
}).then(function () {
|
||||
vm.onCapabilitiesChange.call({
|
||||
capabilities: getCapabilitiesList(),
|
||||
});
|
||||
|
||||
setCapabilitiesSet();
|
||||
setAvailableCapabilitiesDropdown();
|
||||
})['finally'](function () {
|
||||
vm.isBusy = false;
|
||||
vm.capabilityToAddOrRemove = null;
|
||||
vm.capabilityToAddOrRemoveId = null;
|
||||
});
|
||||
}
|
||||
|
||||
function removeCapability(capability) {
|
||||
vm.isBusy = true;
|
||||
piwikApi.post({
|
||||
method: 'UsersManager.removeCapabilities',
|
||||
}, {
|
||||
userLogin: vm.userLogin,
|
||||
capabilities: capability.id,
|
||||
idSites: vm.idsite
|
||||
}).then(function () {
|
||||
vm.onCapabilitiesChange.call({
|
||||
capabilities: getCapabilitiesList(),
|
||||
});
|
||||
|
||||
setCapabilitiesSet();
|
||||
setAvailableCapabilitiesDropdown();
|
||||
})['finally'](function () {
|
||||
vm.isBusy = false;
|
||||
vm.capabilityToAddOrRemove = null;
|
||||
vm.capabilityToAddOrRemoveId = null;
|
||||
});
|
||||
}
|
||||
|
||||
function getCapabilitiesList() {
|
||||
var result = [];
|
||||
vm.availableCapabilities.forEach(function (capability) {
|
||||
if (vm.isIncludedInRole(capability)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (vm.capabilitiesSet[capability.id]) {
|
||||
result.push(capability.id);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
})();
|
@ -0,0 +1,76 @@
|
||||
.capabilitiesEdit {
|
||||
display: inline-block;
|
||||
margin-bottom: -8px;
|
||||
|
||||
> div.chip {
|
||||
margin-right: 8px;
|
||||
display: inline-block;
|
||||
margin-bottom: 8px;
|
||||
|
||||
span.capability-name {
|
||||
padding-right: .4rem;
|
||||
}
|
||||
|
||||
> span.icon-close {
|
||||
font-size: .6rem;
|
||||
float: right;
|
||||
margin-top: 1.4em;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.addCapability {
|
||||
display: inline-block;
|
||||
margin-bottom: 8px;
|
||||
vertical-align: bottom;
|
||||
|
||||
.input-field {
|
||||
margin-top: 0;
|
||||
.caret {
|
||||
right: -25px;
|
||||
top: 16px;
|
||||
z-index: 9;
|
||||
cursor: pointer;
|
||||
}
|
||||
.select-dropdown {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 0;
|
||||
border: 0;
|
||||
background: #e4e4e4;
|
||||
display: inline-block;
|
||||
height: 32px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: rgba(0,0,0,0.6);
|
||||
line-height: 32px;
|
||||
border-radius: 3em;
|
||||
padding-left: 12px;
|
||||
padding-right: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.select-wrapper {
|
||||
transform: scale(.89) translate(-.6rem);
|
||||
margin-top: -0.55rem;
|
||||
max-width: 160px;
|
||||
|
||||
input {
|
||||
margin-bottom: 0;
|
||||
height: 2rem;
|
||||
line-height: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.busy {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.confirmCapabilityToggle {
|
||||
.modal-no {
|
||||
float: right;
|
||||
margin-right: 1em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
<div class="capabilityEdit">
|
||||
{{ capabilityEdit.myProperty }}
|
||||
</div>
|
@ -0,0 +1,3 @@
|
||||
.capabilityEdit {
|
||||
// ...
|
||||
}
|
@ -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>
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
@ -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;
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*!
|
||||
* 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-capabilities-edit>
|
||||
*/
|
||||
(function () {
|
||||
angular.module('piwikApp').factory('permissionsMetadataService', PermissionsMetadataService);
|
||||
|
||||
PermissionsMetadataService.$inject = ['piwikApi', '$q'];
|
||||
|
||||
function PermissionsMetadataService(piwikApi, $q) {
|
||||
var allCapabilities;
|
||||
|
||||
return {
|
||||
getAllCapabilities: function () {
|
||||
if (allCapabilities) {
|
||||
return $q.when(allCapabilities);
|
||||
}
|
||||
|
||||
return piwikApi.fetch({
|
||||
method: 'UsersManager.getAvailableCapabilities',
|
||||
}).then(function (capabilities) {
|
||||
allCapabilities = capabilities;
|
||||
return allCapabilities;
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
})();
|
@ -0,0 +1,47 @@
|
||||
/*!
|
||||
* 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('AnonymousSettingsController', AnonymousSettingsController);
|
||||
|
||||
AnonymousSettingsController.$inject = ['piwikApi'];
|
||||
|
||||
function AnonymousSettingsController(piwikApi) {
|
||||
// remember to keep controller very simple. Create a service/factory (model) if needed
|
||||
|
||||
var self = this;
|
||||
|
||||
function updateSettings(postParams)
|
||||
{
|
||||
self.loading = true;
|
||||
|
||||
piwikApi.withTokenInUrl();
|
||||
piwikApi.post({
|
||||
module: 'UsersManager', action: 'recordAnonymousUserSettings', format: 'json'
|
||||
}, postParams).then(function (success) {
|
||||
var UI = require('piwik/UI');
|
||||
var notification = new UI.Notification();
|
||||
notification.show(_pk_translate('CoreAdminHome_SettingsSaveSuccess'), {
|
||||
id: 'anonymousUserSettings', context: 'success'});
|
||||
notification.scrollToNotification();
|
||||
|
||||
self.loading = false;
|
||||
}, function (errorMessage) {
|
||||
self.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
this.save = function () {
|
||||
|
||||
var postParams = {
|
||||
anonymousDefaultReport: this.defaultReport == '1' ? this.defaultReportWebsite : this.defaultReport,
|
||||
anonymousDefaultDate: this.defaultDate
|
||||
};
|
||||
|
||||
updateSettings(postParams);
|
||||
};
|
||||
}
|
||||
})();
|
@ -0,0 +1,103 @@
|
||||
/*!
|
||||
* 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('PersonalSettingsController', PersonalSettingsController);
|
||||
|
||||
PersonalSettingsController.$inject = ['piwikApi', '$window', 'piwik'];
|
||||
|
||||
function PersonalSettingsController(piwikApi, $window, piwik) {
|
||||
// remember to keep controller very simple. Create a service/factory (model) if needed
|
||||
|
||||
var self = this;
|
||||
|
||||
this.doesRequirePasswordConfirmation = false;
|
||||
|
||||
function updateSettings(postParams)
|
||||
{
|
||||
self.loading = true;
|
||||
|
||||
piwikApi.withTokenInUrl();
|
||||
piwikApi.post({
|
||||
module: 'UsersManager', action: 'recordUserSettings', format: 'json'
|
||||
}, postParams).then(function (success) {
|
||||
var UI = require('piwik/UI');
|
||||
var notification = new UI.Notification();
|
||||
notification.show(_pk_translate('CoreAdminHome_SettingsSaveSuccess'), {
|
||||
id: 'PersonalSettingsSuccess', context: 'success'});
|
||||
notification.scrollToNotification();
|
||||
|
||||
self.doesRequirePasswordConfirmation = !!self.password;
|
||||
self.passwordCurrent = '';
|
||||
self.loading = false;
|
||||
}, function (errorMessage) {
|
||||
self.loading = false;
|
||||
self.passwordCurrent = '';
|
||||
});
|
||||
}
|
||||
|
||||
this.requirePasswordConfirmation = function () {
|
||||
this.doesRequirePasswordConfirmation = true;
|
||||
};
|
||||
|
||||
this.regenerateTokenAuth = function () {
|
||||
var parameters = { userLogin: piwik.userLogin };
|
||||
|
||||
self.loading = true;
|
||||
|
||||
piwikHelper.modalConfirm('#confirmTokenRegenerate', {yes: function () {
|
||||
piwikApi.withTokenInUrl();
|
||||
piwikApi.post({
|
||||
module: 'API',
|
||||
method: 'UsersManager.regenerateTokenAuth'
|
||||
}, parameters).then(function (success) {
|
||||
$window.location.reload();
|
||||
self.loading = false;
|
||||
}, function (errorMessage) {
|
||||
self.loading = false;
|
||||
});
|
||||
}});
|
||||
};
|
||||
|
||||
this.cancelSave = function () {
|
||||
this.passwordCurrent = '';
|
||||
};
|
||||
|
||||
this.save = function () {
|
||||
|
||||
if (this.doesRequirePasswordConfirmation && !this.passwordCurrent) {
|
||||
angular.element('#confirmChangesWithPassword').openModal({ dismissible: false, ready: function () {
|
||||
$('.modal.open #currentPassword').focus();
|
||||
}});
|
||||
return;
|
||||
}
|
||||
|
||||
angular.element('#confirmChangesWithPassword').closeModal();
|
||||
|
||||
var postParams = {
|
||||
email: this.email,
|
||||
defaultReport: this.defaultReport == 'MultiSites' ? this.defaultReport : this.site.id,
|
||||
defaultDate: this.defaultDate,
|
||||
language: this.language,
|
||||
timeformat: this.timeformat,
|
||||
};
|
||||
|
||||
if (this.password) {
|
||||
postParams.password = this.password;
|
||||
}
|
||||
|
||||
if (this.passwordBis) {
|
||||
postParams.passwordBis = this.passwordBis;
|
||||
}
|
||||
|
||||
if (this.passwordCurrent) {
|
||||
postParams.passwordConfirmation = this.passwordCurrent;
|
||||
}
|
||||
|
||||
updateSettings(postParams);
|
||||
};
|
||||
}
|
||||
})();
|
@ -0,0 +1,188 @@
|
||||
<div
|
||||
piwik-content-block
|
||||
content-title="{{ $ctrl.getFormTitle() }}"
|
||||
class="userEditForm"
|
||||
ng-class="{ loading: $ctrl.isSavingUserInfo }"
|
||||
>
|
||||
<div class="row" piwik-form>
|
||||
<div class="col m2 entityList" ng-if="!$ctrl.isAdd">
|
||||
<ul class="listCircle">
|
||||
<li ng-class="{active: $ctrl.activeTab === 'basic'}" class="menuBasicInfo">
|
||||
<a href="" ng-click="$ctrl.activeTab = 'basic'">{{:: 'UsersManager_BasicInformation'|translate }}</a>
|
||||
</li>
|
||||
|
||||
<li ng-class="{active: $ctrl.activeTab === 'permissions'}" class="menuPermissions">
|
||||
<a href="" ng-click="$ctrl.activeTab = 'permissions'">
|
||||
{{:: 'UsersManager_Permissions'|translate }}
|
||||
</a>
|
||||
<span class="icon-warning" ng-if="!$ctrl.userHasAccess && !$ctrl.user.superuser_access"></span>
|
||||
</li>
|
||||
|
||||
<li ng-class="{active: $ctrl.activeTab === 'superuser'}" class="menuSuperuser" ng-if="$ctrl.currentUserRole == 'superuser'">
|
||||
<a href="" ng-click="$ctrl.activeTab = 'superuser'">{{:: 'UsersManager_SuperUserAccess'|translate }}</a>
|
||||
</li>
|
||||
|
||||
<li ng-class="{active: $ctrl.activeTab === '2fa'}" class="menuUserTwoFa" ng-if="$ctrl.currentUserRole == 'superuser' && $ctrl.user.uses_2fa && !$ctrl.isAdd">
|
||||
<a href="" ng-click="$ctrl.activeTab = '2fa'">{{:: 'UsersManager_TwoFactorAuthentication'|translate }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="save-button-spacer hide-on-small-only">
|
||||
</div>
|
||||
|
||||
<div class='entityCancel' ng-click="$ctrl.onDoneEditing({ isUserModified: $ctrl.isUserModified })">
|
||||
<a href="" class="entityCancelLink">{{:: 'Mobile_NavigationBack'|translate }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="visibleTab col m10">
|
||||
<div ng-if="$ctrl.activeTab === 'basic'" class="basic-info-tab">
|
||||
<div
|
||||
piwik-field
|
||||
uicontrol="text"
|
||||
name="user_login"
|
||||
ng-model="$ctrl.user.login"
|
||||
title="Username"
|
||||
maxlength="100"
|
||||
data-disabled="$ctrl.isSavingUserInfo || !$ctrl.isAdd"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div
|
||||
piwik-field
|
||||
uicontrol="password"
|
||||
name="user_password"
|
||||
ng-model="$ctrl.user.password"
|
||||
title="Password"
|
||||
data-disabled="$ctrl.isSavingUserInfo || ($ctrl.currentUserRole != 'superuser' && !$ctrl.isAdd)"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div
|
||||
piwik-field
|
||||
uicontrol="text"
|
||||
name="user_email"
|
||||
ng-model="$ctrl.user.email"
|
||||
title="Email"
|
||||
maxlength="100"
|
||||
data-disabled="$ctrl.isSavingUserInfo || ($ctrl.currentUserRole != 'superuser' && !$ctrl.isAdd)"
|
||||
ng-if="$ctrl.currentUserRole == 'superuser' || $ctrl.isAdd"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div
|
||||
piwik-field
|
||||
uicontrol="site"
|
||||
name="user_site"
|
||||
ng-model="$ctrl.firstSiteAccess"
|
||||
title="First website permission"
|
||||
data-disabled="$ctrl.isSavingUserInfo"
|
||||
ng-if="$ctrl.isAdd"
|
||||
ui-control-attributes="{ onlySitesWithAdminAccess: true }"
|
||||
ng-attr-inline-help="{{:: 'UsersManager_FirstSiteInlineHelp'|translate }}"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div piwik-save-button
|
||||
saving="$ctrl.isSavingUserInfo"
|
||||
onconfirm="$ctrl.saveUserInfo()"
|
||||
ng-value="$ctrl.getSaveButtonLabel()"
|
||||
data-disabled="$ctrl.isAdd && (!$ctrl.firstSiteAccess || !$ctrl.firstSiteAccess.id)"
|
||||
ng-if="$ctrl.currentUserRole == 'superuser' || $ctrl.isAdd"
|
||||
></div>
|
||||
|
||||
<div class='entityCancel' ng-if="$ctrl.isAdd">
|
||||
<a href="" class="entityCancelLink" ng-click="$ctrl.onDoneEditing({ isUserModified: $ctrl.isUserModified })">{{:: 'General_Cancel'|translate }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="!$ctrl.isAdd" ng-show="$ctrl.activeTab === 'permissions'" class="user-permissions">
|
||||
<piwik-user-permissions-edit
|
||||
user-login="$ctrl.user.login"
|
||||
ng-if="!$ctrl.user.superuser_access"
|
||||
on-user-has-access-detected="$ctrl.userHasAccess = hasAccess"
|
||||
on-access-change="$ctrl.isUserModified = true"
|
||||
access-levels="$ctrl.accessLevels"
|
||||
filter-access-levels="$ctrl.filterAccessLevels"
|
||||
>
|
||||
</piwik-user-permissions-edit>
|
||||
<div ng-if="$ctrl.user.superuser_access" class="alert alert-info">
|
||||
{{:: 'UsersManager_SuperUsersPermissionsNotice'|translate }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="$ctrl.activeTab === 'superuser' && $ctrl.currentUserRole == 'superuser' && !$ctrl.isAdd" class="superuser-access">
|
||||
<p>{{:: 'UsersManager_SuperUserIntro1'|translate }}</p>
|
||||
|
||||
<p><strong>{{:: 'UsersManager_SuperUserIntro2'|translate }}</strong></p>
|
||||
|
||||
<div
|
||||
piwik-field
|
||||
uicontrol="checkbox"
|
||||
name="superuser_access"
|
||||
ng-model="$ctrl.user.superuser_access"
|
||||
ng-attr-title="{{:: 'UsersManager_HasSuperUserAccess'|translate }}"
|
||||
ng-click="$ctrl.confirmSuperUserChange()"
|
||||
data-disabled="$ctrl.isSavingUserInfo"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="superuser-confirm-modal modal">
|
||||
<div class="modal-content">
|
||||
<h2>{{:: 'UsersManager_AreYouSure'|translate }}</h2>
|
||||
<p ng-if="!$ctrl.user.superuser_access">
|
||||
{{:: 'UsersManager_RemoveSuperuserAccessConfirm'|translate }}
|
||||
</p>
|
||||
<p ng-if="$ctrl.user.superuser_access">
|
||||
{{:: 'UsersManager_AddSuperuserAccessConfirm'|translate }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="" class="modal-action modal-close btn" ng-click="$ctrl.toggleSuperuserAccess()">{{:: 'General_Yes'|translate }}</a>
|
||||
<a href="" class="modal-action modal-close modal-no" ng-click="$ctrl.user.superuser_access = !$ctrl.user.superuser_access">{{:: 'General_No'|translate }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="$ctrl.activeTab === '2fa' && $ctrl.currentUserRole == 'superuser' && !$ctrl.isAdd" class="twofa-reset">
|
||||
<p>{{:: 'UsersManager_ResetTwoFactorAuthenticationInfo'|translate }}</p>
|
||||
|
||||
<div piwik-save-button
|
||||
class="resetTwoFa"
|
||||
saving="$ctrl.isResetting2FA"
|
||||
onconfirm="$ctrl.confirmReset2FA()"
|
||||
value="{{ 'UsersManager_ResetTwoFactorAuthentication'|translate }}"
|
||||
></div>
|
||||
|
||||
<div class="twofa-confirm-modal modal">
|
||||
<div class="modal-content">
|
||||
<h2>{{:: 'UsersManager_AreYouSure'|translate }}</h2>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="" class="modal-action modal-close btn" ng-click="$ctrl.reset2FA()">{{:: 'General_Yes'|translate }}</a>
|
||||
<a href="" class="modal-action modal-close modal-no">{{:: 'General_No'|translate }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="change-password-modal modal">
|
||||
<div class="modal-content">
|
||||
<h2 piwik-translate="UsersManager_AreYouSureChangeDetails"><strong>{{ $ctrl.user.login }}</strong></h2>
|
||||
<p>{{:: 'UsersManager_ConfirmWithPassword'|translate }}</p>
|
||||
|
||||
<div piwik-field uicontrol="password" name="currentUserPassword" autocomplete="off"
|
||||
ng-model="$ctrl.passwordConfirmation"
|
||||
full-width="true"
|
||||
title="{{ 'UsersManager_YourCurrentPassword'|translate }}"
|
||||
value="">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="" class="modal-action modal-close btn" ng-click="$ctrl.updateUser()">{{:: 'General_Yes'|translate }}</a>
|
||||
<a href="" class="modal-action modal-close modal-no">{{:: 'General_No'|translate }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,195 @@
|
||||
/*!
|
||||
* 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-user-edit-form>
|
||||
*/
|
||||
(function () {
|
||||
angular.module('piwikApp').component('piwikUserEditForm', {
|
||||
templateUrl: 'plugins/UsersManager/angularjs/user-edit-form/user-edit-form.component.html?cb=' + piwik.cacheBuster,
|
||||
bindings: {
|
||||
user: '<',
|
||||
onDoneEditing: '&',
|
||||
currentUserRole: '<',
|
||||
accessLevels: '<',
|
||||
filterAccessLevels: '<',
|
||||
initialSiteId: '<',
|
||||
initialSiteName: '<'
|
||||
},
|
||||
controller: UserEditFormController
|
||||
});
|
||||
|
||||
UserEditFormController.$inject = ['$element', 'piwikApi', '$q'];
|
||||
|
||||
function UserEditFormController($element, piwikApi, $q) {
|
||||
var vm = this;
|
||||
vm.activeTab = 'basic';
|
||||
vm.permissionsForIdSite = 1;
|
||||
vm.isSavingUserInfo = false;
|
||||
vm.userHasAccess = true;
|
||||
vm.firstSiteAccess = null;
|
||||
vm.isUserModified = false;
|
||||
vm.passwordConfirmation = '';
|
||||
|
||||
vm.$onInit = $onInit;
|
||||
vm.$onChanges = $onChanges;
|
||||
vm.confirmSuperUserChange = confirmSuperUserChange;
|
||||
vm.confirmReset2FA = confirmReset2FA;
|
||||
vm.getFormTitle = getFormTitle;
|
||||
vm.getSaveButtonLabel = getSaveButtonLabel;
|
||||
vm.toggleSuperuserAccess = toggleSuperuserAccess;
|
||||
vm.saveUserInfo = saveUserInfo;
|
||||
vm.reset2FA = reset2FA;
|
||||
vm.updateUser = updateUser;
|
||||
|
||||
function $onInit() {
|
||||
vm.firstSiteAccess = {
|
||||
id: vm.initialSiteId,
|
||||
name: vm.initialSiteName
|
||||
};
|
||||
}
|
||||
|
||||
function $onChanges() {
|
||||
if (vm.user) {
|
||||
vm.isAdd = false;
|
||||
} else {
|
||||
vm.isAdd = true;
|
||||
vm.user = {};
|
||||
}
|
||||
|
||||
if (!vm.isAdd) {
|
||||
vm.user.password = 'XXXXXXXX'; // make sure password is not stored in the client after update/save
|
||||
}
|
||||
}
|
||||
|
||||
function getFormTitle() {
|
||||
return vm.isAdd ? _pk_translate('UsersManager_AddNewUser') : _pk_translate('UsersManager_EditUser');
|
||||
}
|
||||
|
||||
function getSaveButtonLabel() {
|
||||
return vm.isAdd ? _pk_translate('UsersManager_CreateUser') : _pk_translate('UsersManager_SaveBasicInfo');
|
||||
}
|
||||
|
||||
function confirmSuperUserChange() {
|
||||
$element.find('.superuser-confirm-modal').openModal({ dismissible: false });
|
||||
}
|
||||
|
||||
function confirmReset2FA() {
|
||||
$element.find('.twofa-confirm-modal').openModal({ dismissible: false });
|
||||
}
|
||||
|
||||
function confirmUserChange() {
|
||||
vm.passwordConfirmation = '';
|
||||
function onEnter(event){
|
||||
var keycode = (event.keyCode ? event.keyCode : event.which);
|
||||
if (keycode == '13'){
|
||||
$element.find('.change-password-modal').closeModal();
|
||||
vm.updateUser();
|
||||
}
|
||||
}
|
||||
|
||||
$element.find('.change-password-modal').openModal({ dismissible: false, ready: function () {
|
||||
$('.modal.open #currentUserPassword').focus();
|
||||
$('.modal.open #currentUserPassword').off('keypress').keypress(onEnter);
|
||||
}});
|
||||
}
|
||||
|
||||
function toggleSuperuserAccess() {
|
||||
vm.isSavingUserInfo = true;
|
||||
piwikApi.post({
|
||||
method: 'UsersManager.setSuperUserAccess'
|
||||
}, {
|
||||
userLogin: vm.user.login,
|
||||
hasSuperUserAccess: vm.user.superuser_access ? '1' : '0'
|
||||
}).catch(function () {
|
||||
// ignore error (still displayed to user)
|
||||
}).then(function () {
|
||||
vm.isSavingUserInfo = false;
|
||||
vm.isUserModified = true;
|
||||
});
|
||||
}
|
||||
|
||||
function saveUserInfo() {
|
||||
if (vm.isAdd) {
|
||||
createUser();
|
||||
} else {
|
||||
confirmUserChange();
|
||||
}
|
||||
}
|
||||
|
||||
function reset2FA() {
|
||||
vm.isResetting2FA = true;
|
||||
return piwikApi.post({
|
||||
method: 'TwoFactorAuth.resetTwoFactorAuth',
|
||||
userLogin: vm.user.login
|
||||
}).catch(function (e) {
|
||||
vm.isResetting2FA = false;
|
||||
throw e;
|
||||
}).then(function () {
|
||||
vm.isResetting2FA = false;
|
||||
vm.user.uses_2fa = false;
|
||||
vm.activeTab = 'basic';
|
||||
|
||||
showUserSavedNotification();
|
||||
});
|
||||
}
|
||||
|
||||
function showUserSavedNotification() {
|
||||
var UI = require('piwik/UI');
|
||||
var notification = new UI.Notification();
|
||||
notification.show(_pk_translate('General_YourChangesHaveBeenSaved'), { context: 'success', type: 'toast' });
|
||||
}
|
||||
|
||||
function createUser() {
|
||||
vm.isSavingUserInfo = true;
|
||||
return piwikApi.post({
|
||||
method: 'UsersManager.addUser'
|
||||
}, {
|
||||
userLogin: vm.user.login,
|
||||
password: vm.user.password,
|
||||
email: vm.user.email,
|
||||
alias: vm.user.alias,
|
||||
initialIdSite: vm.firstSiteAccess ? vm.firstSiteAccess.id : undefined
|
||||
}).catch(function (e) {
|
||||
vm.isSavingUserInfo = false;
|
||||
throw e;
|
||||
}).then(function () {
|
||||
vm.firstSiteAccess = null;
|
||||
vm.isSavingUserInfo = false;
|
||||
vm.isAdd = false;
|
||||
vm.isEmailChanged = false;
|
||||
vm.isUserModified = true;
|
||||
|
||||
showUserSavedNotification();
|
||||
});
|
||||
}
|
||||
|
||||
function updateUser() {
|
||||
vm.isSavingUserInfo = true;
|
||||
return piwikApi.post({
|
||||
method: 'UsersManager.updateUser'
|
||||
}, {
|
||||
userLogin: vm.user.login,
|
||||
password: vm.user.password ? vm.user.password : undefined,
|
||||
passwordConfirmation: vm.passwordConfirmation ? vm.passwordConfirmation : undefined,
|
||||
email: vm.user.email,
|
||||
alias: vm.user.alias
|
||||
}).catch(function (e) {
|
||||
vm.isSavingUserInfo = false;
|
||||
vm.passwordConfirmation = false;
|
||||
throw e;
|
||||
}).then(function () {
|
||||
vm.isSavingUserInfo = false;
|
||||
vm.passwordConfirmation = false;
|
||||
vm.isUserModified = true;
|
||||
|
||||
showUserSavedNotification();
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
@ -0,0 +1,45 @@
|
||||
.userEditForm {
|
||||
.entityList ul li.active a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.entityList ul {
|
||||
.icon-warning {
|
||||
.alert-warning;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-permissions,.superuser-access {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.save-button-spacer {
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.twofa-confirm-modal, .superuser-confirm-modal,.change-password-modal {
|
||||
.modal-no {
|
||||
float: right;
|
||||
margin-right: 1em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.basic-info-tab {
|
||||
.siteSelector {
|
||||
width: calc(~'100% - 25px');
|
||||
}
|
||||
[piwik-siteselector] {
|
||||
margin-bottom: 1rem;
|
||||
|
||||
.title {
|
||||
&,span {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,218 @@
|
||||
<div class="userPermissionsEdit" ng-class="{ loading: $ctrl.isLoadingAccess }">
|
||||
<div class="row" ng-if="!$ctrl.hasAccessToAtLeastOneSite">
|
||||
<div piwik-notification context="warning" type="persistent" noclear="true">
|
||||
<strong>{{:: 'General_Warning'|translate }}:</strong>
|
||||
|
||||
{{:: 'UsersManager_NoAccessWarning'|translate }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row to-all-websites">
|
||||
<div class="col s12">
|
||||
<div>
|
||||
<span>{{ 'UsersManager_GiveAccessToAll'|translate }}:</span>
|
||||
|
||||
<div
|
||||
id="all-sites-access-select"
|
||||
piwik-field
|
||||
uicontrol="select"
|
||||
ng-model="$ctrl.allWebsitesAccssLevelSet"
|
||||
options="$ctrl.accessLevels"
|
||||
full-width="true"
|
||||
></div>
|
||||
|
||||
<a href="" class="btn" ng-class="{ disabled: $ctrl.isGivingAccessToAllSites }" ng-click="$ctrl.showChangeAccessAllSitesModal()">
|
||||
{{:: 'General_Apply'|translate }}
|
||||
</a>
|
||||
</div>
|
||||
<p> </p>
|
||||
<p>{{ 'UsersManager_OrManageIndividually'|translate }}:</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filters row">
|
||||
<div class="col s12 m12 l8">
|
||||
<div class="input-field bulk-actions">
|
||||
<a
|
||||
class='dropdown-trigger btn'
|
||||
href=''
|
||||
data-activates='user-permissions-edit-bulk-actions'
|
||||
piwik-dropdown-menu
|
||||
ng-class="{ disabled: $ctrl.isBulkActionsDisabled }"
|
||||
>
|
||||
{{:: 'UsersManager_BulkActions'|translate }}
|
||||
</a>
|
||||
<ul id='user-permissions-edit-bulk-actions' class='dropdown-content'>
|
||||
<li>
|
||||
<a class='dropdown-trigger' data-activates="user-permissions-bulk-set-access" piwik-dropdown-menu>{{:: 'UsersManager_SetPermission'|translate }}</a>
|
||||
<ul id="user-permissions-bulk-set-access" class="dropdown-content">
|
||||
<li ng-repeat="access in $ctrl.accessLevels">
|
||||
<a href="" ng-click="$ctrl.siteAccessToChange = null; $ctrl.roleToChangeTo = access.key; $ctrl.showChangeAccessConfirm();">{{ access.value }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="" ng-click="$ctrl.siteAccessToChange = null; $ctrl.roleToChangeTo = 'noaccess'; $ctrl.showRemoveAccessConfirm();">{{:: 'UsersManager_RemovePermissions'|translate }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="input-field site-filter">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="{{:: 'UsersManager_FilterByWebsite'|translate }}"
|
||||
ng-model="$ctrl.siteNameFilter"
|
||||
ng-model-options="{debounce: 300}"
|
||||
ng-change="$ctrl.offset = 0; $ctrl.fetchAccess()"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="input-field access-filter">
|
||||
<div
|
||||
piwik-field
|
||||
uicontrol="select"
|
||||
ng-model="$ctrl.accessLevelFilter"
|
||||
options="$ctrl.filterAccessLevels"
|
||||
full-width="true"
|
||||
ng-change="$ctrl.offset = 0; $ctrl.fetchAccess()"
|
||||
placeholder="{{ 'UsersManager_FilterByAccess'|translate }}"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m12 l4 sites-for-permission-pagination-container" ng-if="$ctrl.totalEntries > $ctrl.limit">
|
||||
<div class="sites-for-permission-pagination">
|
||||
<a class="prev" ng-class="{ disabled: $ctrl.offset <= 0 }">
|
||||
<span class="pointer" ng-click="$ctrl.gotoPreviousPage()">« {{:: 'General_Previous'|translate }}</span>
|
||||
</a>
|
||||
|
||||
<span class="counter">
|
||||
<span>
|
||||
{{ 'General_Pagination'|translate:$ctrl.getPaginationLowerBound():$ctrl.getPaginationUpperBound():$ctrl.totalEntries }}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<a class="next" ng-class="{ disabled: $ctrl.offset + $ctrl.limit >= $ctrl.totalEntries }">
|
||||
<span class="pointer" ng-click="$ctrl.gotoNextPage()">{{:: '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-notification context="info" type="persistent" noclear="true" ng-if="$ctrl.isCapabilitiesHelpToggled" class="capabilities-help-notification">
|
||||
<span piwik-translate="UsersManager_CapabilitiesHelp">
|
||||
TODO
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<table piwik-content-table id="sitesForPermission">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="select-cell">
|
||||
<span class="checkbox-container">
|
||||
<input type="checkbox" id="perm_edit_select_all" ng-model="$ctrl.isAllCheckboxSelected" ng-change="$ctrl.onAllCheckboxChange()" />
|
||||
<label for="perm_edit_select_all"></label>
|
||||
</span>
|
||||
</th>
|
||||
<th>{{:: 'General_Name'|translate }}</th>
|
||||
<th class="role_header">
|
||||
<span>{{:: 'UsersManager_Role'|translate }}</span>
|
||||
<a href="" class="helpIcon" ng-click="$ctrl.isRoleHelpToggled = !$ctrl.isRoleHelpToggled" ng-class="{ sticky: $ctrl.isRoleHelpToggled }">
|
||||
<span class="icon-help"></span>
|
||||
</a>
|
||||
</th>
|
||||
<th class="capabilities_header">
|
||||
<span>{{:: 'UsersManager_Capabilities'|translate }}</span>
|
||||
<a href="" class="helpIcon" ng-click="$ctrl.isCapabilitiesHelpToggled = !$ctrl.isCapabilitiesHelpToggled" ng-class="{ sticky: $ctrl.isCapabilitiesHelpToggled }">
|
||||
<span class="icon-help"></span>
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="select-all-row" ng-if="$ctrl.isAllCheckboxSelected && $ctrl.siteAccess.length < $ctrl.totalEntries">
|
||||
<td colspan="4">
|
||||
<div ng-if="!$ctrl.areAllResultsSelected">
|
||||
<span piwik-translate="UsersManager_TheDisplayedWebsitesAreSelected"><strong>{{ $ctrl.siteAccess.length }}</strong></span>
|
||||
<a 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_AllWebsitesAreSelected"><strong>{{ $ctrl.totalEntries }}</strong></span>
|
||||
<a href="#" ng-click="$ctrl.areAllResultsSelected = !$ctrl.areAllResultsSelected" piwik-translate="UsersManager_ClickToSelectDisplayedWebsites"><strong>{{ $ctrl.siteAccess.length }}</strong></a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-repeat="entry in $ctrl.siteAccess">
|
||||
<td class="select-cell">
|
||||
<span class="checkbox-container">
|
||||
<input type="checkbox" ng-attr-id="perm_edit_select_row{{ $index }}" ng-model="$ctrl.selectedRows[$index]" ng-click="$ctrl.onRowSelected()" />
|
||||
<label ng-attr-for="perm_edit_select_row{{ $index }}"></label>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span>{{ entry.site_name }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div
|
||||
piwik-field
|
||||
uicontrol="select"
|
||||
ng-model="entry.role"
|
||||
options="$ctrl.accessLevels"
|
||||
ng-change="$ctrl.previousRole = '{{ entry.role }}'; $ctrl.roleToChangeTo = entry.role; $ctrl.siteAccessToChange = entry; $ctrl.showChangeAccessConfirm();"
|
||||
full-width="true"
|
||||
class="role-select"
|
||||
></div>
|
||||
</td>
|
||||
<td>
|
||||
<piwik-capabilities-edit
|
||||
idsite="entry.idsite"
|
||||
site-name="entry.site_name"
|
||||
user-login="$ctrl.userLogin"
|
||||
user-role="entry.role"
|
||||
capabilities="entry.capabilities"
|
||||
on-capabilities-change="$ctrl.fetchAccess()"
|
||||
>
|
||||
</piwik-capabilities-edit>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="delete-access-confirm-modal modal">
|
||||
<div class="modal-content">
|
||||
<h3 ng-if="$ctrl.siteAccessToChange" piwik-translate="UsersManager_DeletePermConfirmSingle"><strong>{{ $ctrl.userLogin }}</strong>::<strong>{{ $ctrl.siteAccessToChange.site_name }}</strong></h3>
|
||||
<p ng-if="!$ctrl.siteAccessToChange" piwik-translate="UsersManager_DeletePermConfirmMultiple"><strong>{{ $ctrl.userLogin }}</strong>::<strong>{{ $ctrl.getAffectedSitesCount() }}</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.siteAccessToChange = null; $ctrl.roleToChangeTo = null;">{{:: 'General_No'|translate }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="change-access-confirm-modal modal">
|
||||
<div class="modal-content">
|
||||
<h3 ng-if="$ctrl.siteAccessToChange" piwik-translate="UsersManager_ChangePermToSiteConfirmSingle"><strong>{{ $ctrl.userLogin }}</strong>::<strong>{{ $ctrl.siteAccessToChange.site_name }}</strong>::<strong>{{ $ctrl.getRoleDisplay($ctrl.roleToChangeTo) }}</strong></h3>
|
||||
<p ng-if="!$ctrl.siteAccessToChange" piwik-translate="UsersManager_ChangePermToSiteConfirmMultiple"><strong>{{ $ctrl.userLogin }}</strong>::<strong>{{ $ctrl.getAffectedSitesCount() }}</strong>::<strong>{{ $ctrl.getRoleDisplay($ctrl.roleToChangeTo) }}</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.siteAccessToChange.role = $ctrl.previousRole; $ctrl.siteAccessToChange = null; $ctrl.roleToChangeTo = null;">{{:: 'General_No'|translate }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="confirm-give-access-all-sites modal">
|
||||
<div class="modal-content">
|
||||
<h3 piwik-translate="UsersManager_ChangePermToAllSitesConfirm"><strong>{{ $ctrl.userLogin }}</strong>::<strong>{{ $ctrl.getRoleDisplay($ctrl.allWebsitesAccssLevelSet) }}</strong></h3>
|
||||
<p>{{ 'UsersManager_ChangePermToAllSitesConfirm2'|translate }}</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="" class="modal-action modal-close btn" ng-click="$ctrl.giveAccessToAllSites()">{{:: 'General_Yes'|translate }}</a>
|
||||
<a href="" class="modal-action modal-close modal-no">{{:: 'General_No'|translate }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,298 @@
|
||||
/*!
|
||||
* 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-user-permissions-edit>
|
||||
*/
|
||||
(function () {
|
||||
angular.module('piwikApp').component('piwikUserPermissionsEdit', {
|
||||
templateUrl: 'plugins/UsersManager/angularjs/user-permissions-edit/user-permissions-edit.component.html?cb=' + piwik.cacheBuster,
|
||||
bindings: {
|
||||
userLogin: '<',
|
||||
limit: '<',
|
||||
onUserHasAccessDetected: '&',
|
||||
onAccessChange: '&',
|
||||
accessLevels: '<',
|
||||
filterAccessLevels: '<'
|
||||
},
|
||||
controller: UserPermissionsEditController
|
||||
});
|
||||
|
||||
UserPermissionsEditController.$inject = ['piwikApi', '$element', '$q'];
|
||||
|
||||
function UserPermissionsEditController(piwikApi, $element, $q) {
|
||||
var vm = this;
|
||||
|
||||
// search/pagination state
|
||||
vm.siteAccess = [];
|
||||
vm.offset = 0;
|
||||
vm.totalEntries = null;
|
||||
vm.accessLevelFilter = '';
|
||||
vm.siteNameFilter = '';
|
||||
vm.isLoadingAccess = false;
|
||||
vm.allWebsitesAccssLevelSet = 'view';
|
||||
|
||||
// row selection state
|
||||
vm.isAllCheckboxSelected = false;
|
||||
vm.selectedRows = {};
|
||||
vm.isBulkActionsDisabled = true;
|
||||
vm.areAllResultsSelected = false;
|
||||
vm.previousRole = null;
|
||||
|
||||
// other state
|
||||
vm.hasAccessToAtLeastOneSite = true;
|
||||
vm.isRoleHelpToggled = false;
|
||||
vm.isCapabilitiesHelpToggled = false;
|
||||
vm.isGivingAccessToAllSites = false;
|
||||
|
||||
// intermediate state
|
||||
vm.roleToChangeTo = null;
|
||||
vm.siteAccessToChange = null;
|
||||
|
||||
vm.$onInit = $onInit;
|
||||
vm.$onChanges = $onChanges;
|
||||
vm.onAllCheckboxChange = onAllCheckboxChange;
|
||||
vm.onRowSelected = onRowSelected;
|
||||
vm.getPaginationLowerBound = getPaginationLowerBound;
|
||||
vm.getPaginationUpperBound = getPaginationUpperBound;
|
||||
vm.fetchAccess = fetchAccess;
|
||||
vm.gotoPreviousPage = gotoPreviousPage;
|
||||
vm.gotoNextPage = gotoNextPage;
|
||||
vm.showRemoveAccessConfirm = showRemoveAccessConfirm;
|
||||
vm.getSelectedRowsCount = getSelectedRowsCount;
|
||||
vm.getAffectedSitesCount = getAffectedSitesCount;
|
||||
vm.changeUserRole = changeUserRole;
|
||||
vm.showChangeAccessConfirm = showChangeAccessConfirm;
|
||||
vm.getRoleDisplay = getRoleDisplay;
|
||||
vm.showAddExistingUserModal = showAddExistingUserModal;
|
||||
vm.giveAccessToAllSites = giveAccessToAllSites;
|
||||
vm.showChangeAccessAllSitesModal = showChangeAccessAllSitesModal;
|
||||
|
||||
function giveAccessToAllSites() {
|
||||
vm.isGivingAccessToAllSites = true;
|
||||
piwikApi.fetch({
|
||||
method: 'SitesManager.getSitesWithAdminAccess',
|
||||
}).then(function (allSites) {
|
||||
var idSites = allSites.map(function (s) { return s.idsite; });
|
||||
return piwikApi.post({
|
||||
method: 'UsersManager.setUserAccess'
|
||||
}, {
|
||||
userLogin: vm.userLogin,
|
||||
access: vm.allWebsitesAccssLevelSet,
|
||||
'idSites[]': idSites,
|
||||
});
|
||||
}).then(function () {
|
||||
return vm.fetchAccess();
|
||||
})['finally'](function () {
|
||||
vm.isGivingAccessToAllSites = false;
|
||||
});
|
||||
}
|
||||
|
||||
function showChangeAccessAllSitesModal() {
|
||||
$element.find('.confirm-give-access-all-sites').openModal({ dismissible: false });
|
||||
}
|
||||
|
||||
function $onInit() {
|
||||
vm.limit = vm.limit || 10;
|
||||
|
||||
resetSiteToAdd();
|
||||
fetchAccess();
|
||||
}
|
||||
|
||||
function $onChanges() {
|
||||
vm.accessLevels = vm.accessLevels.filter(shouldShowAccessLevel);
|
||||
vm.filterAccessLevels = vm.filterAccessLevels.filter(shouldShowAccessLevel);
|
||||
|
||||
if (vm.limit) {
|
||||
fetchAccess();
|
||||
}
|
||||
|
||||
function shouldShowAccessLevel(entry) {
|
||||
return entry.key !== 'superuser';
|
||||
}
|
||||
}
|
||||
|
||||
function fetchAccess() {
|
||||
vm.isLoadingAccess = true;
|
||||
piwikApi.fetch({
|
||||
method: 'UsersManager.getSitesAccessForUser',
|
||||
limit: vm.limit,
|
||||
offset: vm.offset,
|
||||
filter_search: vm.siteNameFilter,
|
||||
filter_access: vm.accessLevelFilter,
|
||||
userLogin: vm.userLogin
|
||||
}, { includeHeaders: true }).then(function (result) {
|
||||
vm.isLoadingAccess = false;
|
||||
vm.siteAccess = result.response;
|
||||
vm.totalEntries = parseInt(result.headers('x-matomo-total-results')) || 0;
|
||||
vm.hasAccessToAtLeastOneSite = !! result.headers('x-matomo-has-some');
|
||||
|
||||
if (vm.onUserHasAccessDetected) {
|
||||
vm.onUserHasAccessDetected({ hasAccess: vm.hasAccessToAtLeastOneSite });
|
||||
}
|
||||
|
||||
clearSelection();
|
||||
}).catch(function () {
|
||||
vm.isLoadingAccess = false;
|
||||
|
||||
clearSelection();
|
||||
});
|
||||
}
|
||||
|
||||
function getAllSitesInSearch() {
|
||||
return piwikApi.fetch({
|
||||
method: 'UsersManager.getSitesAccessForUser',
|
||||
filter_search: vm.siteNameFilter,
|
||||
filter_access: vm.accessLevelFilter,
|
||||
userLogin: vm.userLogin,
|
||||
filter_limit: '-1'
|
||||
}).then(function (access) {
|
||||
return access.map(function (a) { return a.idsite; });
|
||||
});
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
vm.selectedRows = {};
|
||||
vm.areAllResultsSelected = false;
|
||||
vm.isBulkActionsDisabled = true;
|
||||
vm.isAllCheckboxSelected = false;
|
||||
vm.siteAccessToChange = null;
|
||||
}
|
||||
|
||||
function onAllCheckboxChange() {
|
||||
if (!vm.isAllCheckboxSelected) {
|
||||
clearSelection();
|
||||
} else {
|
||||
for (var i = 0; i !== vm.siteAccess.length; ++i) {
|
||||
vm.selectedRows[i] = true;
|
||||
}
|
||||
vm.isBulkActionsDisabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onRowSelected() {
|
||||
var selectedRowKeyCount = getSelectedRowsCount();
|
||||
vm.isBulkActionsDisabled = selectedRowKeyCount === 0;
|
||||
vm.isAllCheckboxSelected = selectedRowKeyCount === vm.siteAccess.length;
|
||||
}
|
||||
|
||||
function getPaginationLowerBound() {
|
||||
return vm.offset + 1;
|
||||
}
|
||||
|
||||
function getPaginationUpperBound() {
|
||||
return Math.min(vm.offset + vm.limit, vm.totalEntries);
|
||||
}
|
||||
|
||||
function resetSiteToAdd() {
|
||||
vm.siteToAdd = {
|
||||
id: null,
|
||||
name: ''
|
||||
};
|
||||
}
|
||||
|
||||
function changeUserRole() {
|
||||
vm.isLoadingAccess = true;
|
||||
|
||||
return $q.resolve().then(function () {
|
||||
if (vm.siteAccessToChange) {
|
||||
return [vm.siteAccessToChange.idsite];
|
||||
}
|
||||
|
||||
if (vm.areAllResultsSelected) {
|
||||
return getAllSitesInSearch();
|
||||
}
|
||||
|
||||
return getSelectedSites();
|
||||
}).then(function (idSites) {
|
||||
return piwikApi.post({
|
||||
method: 'UsersManager.setUserAccess'
|
||||
}, {
|
||||
userLogin: vm.userLogin,
|
||||
access: vm.roleToChangeTo,
|
||||
'idSites[]': idSites
|
||||
});
|
||||
}).catch(function () {
|
||||
// ignore (errors will still be displayed to the user)
|
||||
}).then(function () {
|
||||
vm.onAccessChange();
|
||||
|
||||
return fetchAccess();
|
||||
});
|
||||
}
|
||||
|
||||
function getSelectedSites() {
|
||||
var result = [];
|
||||
Object.keys(vm.selectedRows).forEach(function (index) {
|
||||
if (vm.selectedRows[index]
|
||||
&& vm.siteAccess[index] // safety check
|
||||
) {
|
||||
result.push(vm.siteAccess[index].idsite);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function gotoPreviousPage() {
|
||||
vm.offset = Math.max(0, vm.offset - vm.limit);
|
||||
|
||||
fetchAccess();
|
||||
}
|
||||
|
||||
function gotoNextPage() {
|
||||
var newOffset = vm.offset + vm.limit;
|
||||
if (newOffset >= vm.totalEntries) {
|
||||
return;
|
||||
}
|
||||
|
||||
vm.offset = newOffset;
|
||||
fetchAccess();
|
||||
}
|
||||
|
||||
function showRemoveAccessConfirm() {
|
||||
$element.find('.delete-access-confirm-modal').openModal({ dismissible: false });
|
||||
}
|
||||
|
||||
function showChangeAccessConfirm() {
|
||||
$element.find('.change-access-confirm-modal').openModal({ dismissible: false });
|
||||
}
|
||||
|
||||
function showAddExistingUserModal() {
|
||||
$element.find('.add-existing-user-modal').openModal({ dismissible: false });
|
||||
}
|
||||
|
||||
function getSelectedRowsCount() {
|
||||
var selectedRowKeyCount = 0;
|
||||
Object.keys(vm.selectedRows).forEach(function (key) {
|
||||
if (vm.selectedRows[key]) {
|
||||
++selectedRowKeyCount;
|
||||
}
|
||||
});
|
||||
return selectedRowKeyCount;
|
||||
}
|
||||
|
||||
function getAffectedSitesCount() {
|
||||
if (vm.areAllResultsSelected) {
|
||||
return vm.totalEntries;
|
||||
}
|
||||
|
||||
return getSelectedRowsCount();
|
||||
}
|
||||
|
||||
function getRoleDisplay(role) {
|
||||
var result = null;
|
||||
vm.accessLevels.forEach(function (entry) {
|
||||
if (entry.key === role) {
|
||||
result = entry.value;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,232 @@
|
||||
.userPermissionsEdit {
|
||||
&.loading {
|
||||
.sites-for-permission-pagination, table {
|
||||
opacity: .5;
|
||||
}
|
||||
}
|
||||
|
||||
.permission-select .select-wrapper {
|
||||
display: inline-block;
|
||||
transform: scale(.8);
|
||||
margin-right: -10px;
|
||||
margin-left: -10px;
|
||||
z-index: 999;
|
||||
|
||||
input {
|
||||
margin-bottom: 0;
|
||||
height: 1.1em;
|
||||
line-height: 1.1em;
|
||||
}
|
||||
|
||||
.caret {
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.add-site {
|
||||
float: right;
|
||||
|
||||
[piwik-field] {
|
||||
display:inline-block;
|
||||
|
||||
.input-field {
|
||||
width: 180px;
|
||||
}
|
||||
}
|
||||
|
||||
[piwik-siteselector] {
|
||||
display: inline-block;
|
||||
|
||||
a.title {
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.siteSelector {
|
||||
position: static !important;
|
||||
margin-top: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-flat:hover {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
.filters {
|
||||
margin-left: -0.75rem;
|
||||
margin-right: -0.75rem;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
> div:first-child {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
> div > .input-field {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.sites-for-permission-pagination {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
min-height: 2.5rem;
|
||||
}
|
||||
|
||||
.form-group, .input-field, input {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.add-site {
|
||||
> div {
|
||||
vertical-align: bottom;
|
||||
margin-top: .8rem;
|
||||
}
|
||||
|
||||
> a {
|
||||
padding: 0 1rem 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bulk-actions > a.dropdown-trigger {
|
||||
margin-top: .8rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
#sitesForPermission {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
width: calc(100%);
|
||||
font-size: 100%;
|
||||
|
||||
td > span {
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
.select-cell {
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
span.checkbox-container {
|
||||
transform: scale(.8);
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
.role-select .select-wrapper {
|
||||
transform: scale(.8) translate(-1.3rem);
|
||||
margin-top: -0.5rem;
|
||||
max-width: 160px;
|
||||
|
||||
span.caret {
|
||||
top: 8px;
|
||||
}
|
||||
|
||||
input {
|
||||
margin-bottom: 0;
|
||||
height: 2rem;
|
||||
line-height: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
tr.select-all-row > td {
|
||||
padding: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.row.form-group {
|
||||
margin: 0;
|
||||
.col {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
tr .input-field {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
table.entityTable tbody tr td {
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
|
||||
.add-permission {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.sites-for-permission-pagination-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sites-for-permission-pagination {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: calc(100%);
|
||||
text-align: center;
|
||||
|
||||
a.disabled {
|
||||
pointer-events: none;
|
||||
color: #9e9e9e;
|
||||
}
|
||||
|
||||
.counter {
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.delete-site-permission {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.delete-access-confirm-modal, .change-access-confirm-modal, .confirm-give-access-all-sites {
|
||||
.modal-no {
|
||||
float: right;
|
||||
margin-right: 1em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
th.role_header, th.capabilities_header {
|
||||
.helpIcon {
|
||||
color: #9e9e9e;
|
||||
font-size: .8rem;
|
||||
margin-left: .1rem;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover,&.sticky {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.to-all-websites {
|
||||
margin-left: -0.75em;
|
||||
margin-right: -0.75em;
|
||||
}
|
||||
#all-sites-access-select {
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
width: 150px;
|
||||
.form-group {
|
||||
margin: 0;
|
||||
}
|
||||
.input-field {
|
||||
margin-top: 0;
|
||||
}
|
||||
.select-dropdown {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-permission-toast .notification {
|
||||
padding-left: 20px;
|
||||
&::before {
|
||||
display:none;
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
<div class="usersManager">
|
||||
<div ng-show="!$ctrl.isEditing">
|
||||
<div piwik-content-intro>
|
||||
<h2
|
||||
piwik-enriched-headline
|
||||
help-url="https://matomo.org/docs/manage-users/"
|
||||
feature-name="Users Management"
|
||||
>
|
||||
{{:: 'UsersManager_ManageUsers'|translate }}
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
{{:: 'UsersManager_ManageUsersDesc'|translate }}
|
||||
</p>
|
||||
|
||||
<div class="row add-user-container">
|
||||
<div class="col s12">
|
||||
<div class="input-field">
|
||||
<a class="btn add-new-user" ng-click="$ctrl.isEditing = true; $ctrl.userBeingEdited = null;">
|
||||
{{:: 'UsersManager_AddUser'|translate }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="input-field" ng-if="$ctrl.currentUserRole !== 'superuser'">
|
||||
<a class="btn add-existing-user" ng-click="$ctrl.showAddExistingUserModal();">
|
||||
{{:: 'UsersManager_AddExistingUser'|translate }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<piwik-paged-users-list
|
||||
on-edit-user="$ctrl.onEditUser(user)"
|
||||
on-change-user-role="$ctrl.onChangeUserRole(users, role)"
|
||||
on-delete-user="$ctrl.onDeleteUser(users)"
|
||||
on-search-change="$ctrl.searchParams = params; $ctrl.fetchUsers();"
|
||||
initial-site-id="$ctrl.initialSiteId"
|
||||
initial-site-name="$ctrl.initialSiteName"
|
||||
is-loading-users="$ctrl.isLoadingUsers"
|
||||
current-user-role="$ctrl.currentUserRole"
|
||||
access-levels="$ctrl.accessLevels"
|
||||
filter-access-levels="$ctrl.filterAccessLevels"
|
||||
search-params="$ctrl.searchParams"
|
||||
users="$ctrl.users"
|
||||
total-entries="$ctrl.totalEntries"
|
||||
></piwik-paged-users-list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TODO: whether a user is being edited should be part of the URL -->
|
||||
<div ng-if="$ctrl.isEditing">
|
||||
<piwik-user-edit-form
|
||||
on-done-editing="$ctrl.onDoneEditing(isUserModified);"
|
||||
user="$ctrl.userBeingEdited"
|
||||
current-user-role="$ctrl.currentUserRole"
|
||||
allow-superuser-edit="$ctrl.isCurrentUserSuperUser"
|
||||
access-levels="$ctrl.accessLevels"
|
||||
filter-access-levels="$ctrl.filterAccessLevels"
|
||||
initial-site-id="$ctrl.initialSiteId"
|
||||
initial-site-name="$ctrl.initialSiteName"
|
||||
></piwik-user-edit-form>
|
||||
</div>
|
||||
|
||||
<div class="add-existing-user-modal modal">
|
||||
<div class="modal-content">
|
||||
<h3>{{:: 'UsersManager_AddExistingUser'|translate }}</h3>
|
||||
<p>{{:: 'UsersManager_EnterUsernameOrEmail'|translate }}:</p>
|
||||
<div
|
||||
piwik-field
|
||||
name="add-existing-user-email"
|
||||
uicontrol="text"
|
||||
ng-model="$ctrl.addNewUserLoginEmail"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="" class="modal-action modal-close btn" ng-click="$ctrl.addExistingUser()">{{:: 'General_Add'|translate }}</a>
|
||||
<a href="" class="modal-action modal-close modal-no" ng-click="$ctrl.addNewUserLoginEmail = null;">{{:: 'General_Cancel'|translate }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,220 @@
|
||||
/*!
|
||||
* 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-users-manager>
|
||||
*/
|
||||
(function () {
|
||||
angular.module('piwikApp').component('piwikUsersManager', {
|
||||
templateUrl: 'plugins/UsersManager/angularjs/users-manager/users-manager.component.html?cb=' + piwik.cacheBuster,
|
||||
bindings: {
|
||||
currentUserRole: '<',
|
||||
initialSiteName: '@',
|
||||
initialSiteId: '@',
|
||||
accessLevels: '<',
|
||||
filterAccessLevels: '<'
|
||||
},
|
||||
controller: UsersManagerController
|
||||
});
|
||||
|
||||
UsersManagerController.$inject = ['$element', 'piwik', 'piwikApi', '$q', '$timeout'];
|
||||
|
||||
function UsersManagerController($element, piwik, piwikApi, $q, $timeout) {
|
||||
var vm = this;
|
||||
vm.isEditing = false;
|
||||
vm.isCurrentUserSuperUser = true;
|
||||
|
||||
// search state
|
||||
vm.users = [];
|
||||
vm.totalEntries = null;
|
||||
vm.searchParams = {};
|
||||
vm.isLoadingUsers = false;
|
||||
|
||||
vm.$onInit = $onInit;
|
||||
vm.$onChanges = $onChanges;
|
||||
vm.$onDestroy = $onDestroy;
|
||||
vm.onEditUser = onEditUser;
|
||||
vm.onDoneEditing = onDoneEditing;
|
||||
vm.showAddExistingUserModal = showAddExistingUserModal;
|
||||
vm.onChangeUserRole = onChangeUserRole;
|
||||
vm.onDeleteUser = onDeleteUser;
|
||||
vm.fetchUsers = fetchUsers;
|
||||
vm.addExistingUser = addExistingUser;
|
||||
|
||||
function onChangeUserRole(users, role) {
|
||||
vm.isLoadingUsers = true;
|
||||
|
||||
$q.resolve().then(function () {
|
||||
if (users === 'all') {
|
||||
return getAllUsersInSearch();
|
||||
}
|
||||
return users;
|
||||
}).then(function (users) {
|
||||
return users.filter(function (user) {
|
||||
return user.role !== 'superuser';
|
||||
}).map(function (user) {
|
||||
return user.login;
|
||||
});
|
||||
}).then(function (userLogins) {
|
||||
var requests = userLogins.map(function (login) {
|
||||
return {
|
||||
method: 'UsersManager.setUserAccess',
|
||||
userLogin: login,
|
||||
access: role,
|
||||
idSites: vm.searchParams.idSite,
|
||||
ignoreSuperusers: 1
|
||||
};
|
||||
});
|
||||
return piwikApi.bulkFetch(requests, { createErrorNotification: true });
|
||||
}).catch(function (e) {
|
||||
// ignore (errors will still be displayed to the user)
|
||||
}).then(function () {
|
||||
return fetchUsers();
|
||||
});
|
||||
}
|
||||
|
||||
function onDeleteUser(users) {
|
||||
vm.isLoadingUsers = true;
|
||||
|
||||
$q.resolve().then(function () {
|
||||
if (users === 'all') {
|
||||
return getAllUsersInSearch();
|
||||
}
|
||||
return users;
|
||||
}).then(function (users) {
|
||||
return users.map(function (user) { return user.login; });
|
||||
}).then(function (userLogins) {
|
||||
var requests = userLogins.map(function (login) {
|
||||
return {
|
||||
method: 'UsersManager.deleteUser',
|
||||
userLogin: login
|
||||
};
|
||||
});
|
||||
return piwikApi.bulkFetch(requests, { createErrorNotification: true });
|
||||
}).catch(function () {
|
||||
// ignore (errors will still be displayed to the user)
|
||||
}).then(function () {
|
||||
return fetchUsers();
|
||||
});
|
||||
}
|
||||
|
||||
function $onInit() {
|
||||
// TODO: maybe this should go in another directive...
|
||||
$element.tooltip({
|
||||
track: true,
|
||||
content: function() {
|
||||
var title = $(this).attr('title');
|
||||
return piwikHelper.escape(title.replace(/\n/g, '<br />'));
|
||||
},
|
||||
show: false,
|
||||
hide: false
|
||||
});
|
||||
|
||||
if (vm.currentUserRole === 'superuser') {
|
||||
vm.filterAccessLevels.push({ key: 'superuser', value: 'Superuser' });
|
||||
}
|
||||
|
||||
vm.searchParams = {
|
||||
offset: 0,
|
||||
limit: 20,
|
||||
filter_search: '',
|
||||
filter_access: '',
|
||||
idSite: vm.initialSiteId
|
||||
};
|
||||
|
||||
fetchUsers();
|
||||
}
|
||||
|
||||
function $onChanges(changes) {
|
||||
if (changes.limit) {
|
||||
fetchUsers();
|
||||
}
|
||||
}
|
||||
|
||||
function $onDestroy() {
|
||||
try {
|
||||
$element.tooltip('destroy');
|
||||
} catch (e) {
|
||||
// empty
|
||||
}
|
||||
}
|
||||
|
||||
function fetchUsers() {
|
||||
vm.isLoadingUsers = true;
|
||||
return piwikApi.fetch($.extend({}, vm.searchParams, {
|
||||
method: 'UsersManager.getUsersPlusRole'
|
||||
}), { includeHeaders: true }).then(function (result) {
|
||||
vm.totalEntries = parseInt(result.headers('x-matomo-total-results')) || 0;
|
||||
vm.users = result.response;
|
||||
|
||||
vm.isLoadingUsers = false;
|
||||
}).catch(function () {
|
||||
vm.isLoadingUsers = false;
|
||||
});
|
||||
}
|
||||
|
||||
function getAllUsersInSearch() {
|
||||
return piwikApi.fetch({
|
||||
method: 'UsersManager.getUsersPlusRole',
|
||||
filter_search: vm.searchParams.filter_search,
|
||||
filter_access: vm.searchParams.filter_access,
|
||||
idSite: vm.searchParams.idSite,
|
||||
filter_limit: '-1'
|
||||
});
|
||||
}
|
||||
|
||||
function onEditUser(user) {
|
||||
piwik.helper.lazyScrollToContent();
|
||||
vm.isEditing = true;
|
||||
vm.userBeingEdited = user;
|
||||
}
|
||||
|
||||
function onDoneEditing(isUserModified) {
|
||||
vm.isEditing = false;
|
||||
if (isUserModified) { // if a user was modified, we must reload the users list
|
||||
fetchUsers();
|
||||
}
|
||||
}
|
||||
|
||||
function showAddExistingUserModal() {
|
||||
$element.find('.add-existing-user-modal').openModal({ dismissible: false });
|
||||
}
|
||||
|
||||
function addExistingUser() {
|
||||
vm.isLoadingUsers = true;
|
||||
return piwikApi.fetch({
|
||||
method: 'UsersManager.userExists',
|
||||
userLogin: vm.addNewUserLoginEmail
|
||||
}).then(function (response) {
|
||||
if (response && response.value) {
|
||||
return vm.addNewUserLoginEmail;
|
||||
}
|
||||
|
||||
return piwikApi.fetch({
|
||||
method: 'UsersManager.getUserLoginFromUserEmail',
|
||||
userEmail: vm.addNewUserLoginEmail
|
||||
}).then(function (response) {
|
||||
return response.value;
|
||||
});
|
||||
}).then(function (login) {
|
||||
return piwikApi.post({
|
||||
method: 'UsersManager.setUserAccess'
|
||||
}, {
|
||||
userLogin: login,
|
||||
access: 'view',
|
||||
idSites: vm.searchParams.idSite
|
||||
});
|
||||
}).catch(function (error) {
|
||||
vm.isLoadingUsers = false;
|
||||
throw error;
|
||||
}).then(function () {
|
||||
return fetchUsers();
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
@ -0,0 +1,28 @@
|
||||
.usersManager {
|
||||
.card .card-content .card-title {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.add-user-container {
|
||||
&.row {
|
||||
margin-left: -0.75rem;
|
||||
margin-right: -0.75rem;
|
||||
}
|
||||
|
||||
> .col > .input-field {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.add-existing-user-modal {
|
||||
.form-group,.input-field,input.control_text {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.modal-no {
|
||||
float: right;
|
||||
margin-right: 1em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user