Merge remote-tracking branch 'origin/dev-v7.8' into temp-U4-10803

# Conflicts:
#	src/Umbraco.Web.UI.Client/bower.json
This commit is contained in:
Shannon
2018-01-12 14:32:54 +11:00
72 changed files with 1619 additions and 935 deletions

View File

@@ -33,56 +33,44 @@
"clipboard": "1.7.1",
"font-awesome": "~4.2"
},
"install": {
"path": "lib-bower",
"ignore": [
"font-awesome",
"angular",
"bootstrap",
"codemirror"
],
"sources": {
"moment": "bower_components/moment/min/moment-with-locales.js",
"underscore": [
"bower_components/underscore/underscore-min.js",
"bower_components/underscore/underscore-min.map"
],
"jquery": [
"bower_components/jquery/dist/jquery.min.js",
"bower_components/jquery/dist/jquery.min.map"
],
"angular-dynamic-locale": [
"bower_components/angular-dynamic-locale/tmhDynamicLocale.min.js",
"bower_components/angular-dynamic-locale/tmhDynamicLocale.min.js.map"
],
"angular-local-storage": [
"bower_components/angular-local-storage/dist/angular-local-storage.min.js",
"bower_components/angular-local-storage/dist/angular-local-storage.min.js.map"
],
"tinymce": [
"bower_components/tinymce/tinymce.min.js"
],
"typeahead.js": "bower_components/typeahead.js/dist/typeahead.bundle.min.js",
"rgrove-lazyload":"bower_components/rgrove-lazyload/lazyload.js",
"ng-file-upload":"bower_components/ng-file-upload/ng-file-upload.min.js",
"jquery-ui":"bower_components/jquery-ui/jquery-ui.min.js",
"jquery-migrate":"bower_components/jquery-migrate/jquery-migrate.min.js",
"rgrove-lazyload": "bower_components/rgrove-lazyload/lazyload.js",
"ng-file-upload": "bower_components/ng-file-upload/ng-file-upload.min.js",
"jquery-ui": "bower_components/jquery-ui/jquery-ui.min.js",
"jquery-migrate": "bower_components/jquery-migrate/jquery-migrate.min.js",
"clipboard": "bower_components/clipboard/dist/clipboard.min.js"
}
},
"resolutions": {
"moment": ">=2.19.3 <3.0.0"
}
}

View File

@@ -16,52 +16,16 @@ function eventsService($q, $rootScope) {
return {
/** raise an event with a given name, returns an array of promises for each listener */
emit: function (name, args) {
/** raise an event with a given name */
emit: function (name, args) {
//there are no listeners
if (!$rootScope.$$listeners[name]) {
return;
//return [];
}
//send the event
$rootScope.$emit(name, args);
//PP: I've commented out the below, since we currently dont
// expose the eventsService as a documented api
// and think we need to figure out our usecases for this
// since the below modifies the return value of the then on() method
/*
//setup a deferred promise for each listener
var deferred = [];
for (var i = 0; i < $rootScope.$$listeners[name].length; i++) {
deferred.push($q.defer());
}*/
//create a new event args object to pass to the
// $emit containing methods that will allow listeners
// to return data in an async if required
/*
var eventArgs = {
args: args,
reject: function (a) {
deferred.pop().reject(a);
},
resolve: function (a) {
deferred.pop().resolve(a);
}
};*/
/*
//return an array of promises
var promises = _.map(deferred, function(p) {
return p.promise;
});
return promises;*/
},
/** subscribe to a method, or use scope.$on = same thing */

View File

@@ -17,6 +17,7 @@
* Registers all tours from the server and returns a promise
*/
function registerAllTours() {
tours = [];
return tourResource.getTours().then(function(tourFiles) {
angular.forEach(tourFiles, function (tourFile) {
angular.forEach(tourFile.tours, function(newTour) {
@@ -218,7 +219,38 @@
var deferred = $q.defer();
var tours = getTours();
setTourStatuses(tours).then(function() {
var groupedTours = _.groupBy(tours, "group");
var groupedTours = [];
tours.forEach(function (item) {
var groupExists = false;
var newGroup = {
"group": "",
"tours": []
};
groupedTours.forEach(function(group){
// extend existing group if it is already added
if(group.group === item.group) {
if(item.groupOrder) {
group.groupOrder = item.groupOrder
}
groupExists = true;
group.tours.push(item)
}
});
// push new group to array if it doesn't exist
if(!groupExists) {
newGroup.group = item.group;
if(item.groupOrder) {
newGroup.groupOrder = item.groupOrder
}
newGroup.tours.push(item);
groupedTours.push(newGroup);
}
});
deferred.resolve(groupedTours);
});
return deferred.promise;

View File

@@ -124,7 +124,7 @@
function showTourButton(index, tourGroup) {
if(index !== 0) {
var prevTour = tourGroup[index - 1];
var prevTour = tourGroup.tours[index - 1];
if(prevTour.completed) {
return true;
}
@@ -147,12 +147,12 @@
// Finding out, how many tours are completed for the progress circle
angular.forEach(vm.tours, function(group){
var completedTours = 0;
angular.forEach(group, function(tour){
angular.forEach(group.tours, function(tour){
if(tour.completed) {
completedTours++;
}
});
group.completedPercentage = Math.round((completedTours/group.length)*100);
group.completedPercentage = Math.round((completedTours/group.tours.length)*100);
});
}

View File

@@ -12,23 +12,23 @@
<h5 style="margin-bottom: 10px; margin-top: 0;">Tours</h5>
<div ng-repeat="(key,value) in vm.tours" style="margin-bottom: 5px;">
<div ng-repeat="tourGroup in vm.tours | orderBy:'groupOrder'" style="margin-bottom: 5px;">
<div class="umb-help-list">
<a href="" class="umb-help-list-item umb-help-list-item__content flex items-center justify-between" style="text-decoration: none;" ng-click="value.open = !value.open">
<h5 class="umb-help-list-item__group-title"><i style="margin-right: 2px;text-decoration: none;" ng-class="{'icon-navigation-right': !value.open, 'icon-navigation-down': value.open}"></i>
<span ng-if="key !== 'undefined'">{{key}}</span>
<span ng-if="key === 'undefined'">Other</span>
<a href="" class="umb-help-list-item umb-help-list-item__content flex items-center justify-between" style="text-decoration: none;" ng-click="tourGroup.open = !tourGroup.open">
<h5 class="umb-help-list-item__group-title"><i style="margin-right: 2px;text-decoration: none;" ng-class="{'icon-navigation-right': !tourGroup.open, 'icon-navigation-down': tourGroup.open}"></i>
<span ng-if="tourGroup.group !== 'undefined'">{{tourGroup.group}}</span>
<span ng-if="tourGroup.group === 'undefined'">Other</span>
</h5>
<umb-progress-circle
percentage="{{value.completedPercentage}}"
percentage="{{tourGroup.completedPercentage}}"
size="40">
</umb-progress-circle>
</a>
<div ng-if="value.open">
<div data-element="tour-{{tour.alias}}" class="umb-help-list-item" ng-repeat="tour in value">
<div ng-if="tourGroup.open">
<div data-element="tour-{{tour.alias}}" class="umb-help-list-item" ng-repeat="tour in tourGroup.tours">
<div class="umb-help-list-item__content justify-between">
<div class="flex items-center">
<div ng-if="!tour.completed" class="umb-number-badge umb-number-badge--xs umb-help-list-item__icon">{{ $index + 1 }}</div>
@@ -36,7 +36,7 @@
<span ng-class="{'strike': tour.completed}" class="umb-help-list-item__title">{{ tour.name }}</span>
</div>
<div>
<umb-button ng-if="!tour.completed && vm.showTourButton($index, value)" button-style="primary" size="xxs" type="button" label="Start" action="vm.startTour(tour)"></umb-button>
<umb-button ng-if="!tour.completed && vm.showTourButton($index, tourGroup)" button-style="primary" size="xxs" type="button" label="Start" action="vm.startTour(tour)"></umb-button>
<umb-button ng-if="tour.completed" size="xxs" type="button" label="Rerun" action="vm.startTour(tour)"></umb-button>
</div>
</div>

View File

@@ -1,7 +1,7 @@
(function () {
"use strict";
function UserEditController($scope, $timeout, $location, $routeParams, formHelper, usersResource, userService, contentEditingHelper, localizationService, notificationsService, mediaHelper, Upload, umbRequestHelper, usersHelper, authResource, dateHelper) {
function UserEditController($scope, eventsService, $q, $timeout, $location, $routeParams, formHelper, usersResource, userService, contentEditingHelper, localizationService, notificationsService, mediaHelper, Upload, umbRequestHelper, usersHelper, authResource, dateHelper) {
var vm = this;
@@ -16,7 +16,7 @@
vm.maxFileSize = Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB";
vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes);
vm.usernameIsEmail = Umbraco.Sys.ServerVariables.umbracoSettings.usernameIsEmail;
//create the initial model for change password
vm.changePasswordModel = {
config: {},
@@ -33,6 +33,7 @@
vm.unlockUser = unlockUser;
vm.clearAvatar = clearAvatar;
vm.save = save;
vm.toggleChangePassword = toggleChangePassword;
function init() {
@@ -117,38 +118,83 @@
function save() {
vm.page.saveButtonState = "busy";
vm.user.resetPasswordValue = null;
if (formHelper.submitForm({ scope: $scope, statusMessage: vm.labels.saving })) {
//anytime a user is changing another user's password, we are in effect resetting it so we need to set that flag here
if(vm.user.changePassword) {
vm.user.changePassword.reset = !vm.user.changePassword.oldPassword && !vm.user.isCurrentUser;
//anytime a user is changing another user's password, we are in effect resetting it so we need to set that flag here
if (vm.user.changePassword) {
vm.user.changePassword.reset = !vm.user.changePassword.oldPassword && !vm.user.isCurrentUser;
}
vm.page.saveButtonState = "busy";
vm.user.resetPasswordValue = null;
//save current nav to be restored later so that the tabs dont change
var currentNav = vm.user.navigation;
usersResource.saveUser(vm.user)
.then(function (saved) {
//if the user saved, then try to execute all extended save options
extendedSave(saved).then(function(result) {
//if all is good, then reset the form
formHelper.resetForm({ scope: $scope, notifications: saved.notifications });
}, function(err) {
//otherwise show the notifications for the user being saved
formHelper.showNotifications(saved);
});
vm.user = _.omit(saved, "navigation");
//restore
vm.user.navigation = currentNav;
setUserDisplayState();
formatDatesToLocal(vm.user);
vm.changePasswordModel.isChanging = false;
//the user has a password if they are not states: Invited, NoCredentials
vm.changePasswordModel.config.hasPassword = vm.user.userState !== 3 && vm.user.userState !== 4;
vm.page.saveButtonState = "success";
}, function (err) {
contentEditingHelper.handleSaveError({
redirectOnFailure: false,
err: err
});
//show any notifications
if (err.data) {
formHelper.showNotifications(err.data);
}
vm.page.saveButtonState = "error";
});
}
}
contentEditingHelper.contentEditorPerformSave({
statusMessage: vm.labels.saving,
saveMethod: usersResource.saveUser,
scope: $scope,
content: vm.user,
// We do not redirect on failure for users - this is because it is not possible to actually save a user
// when server side validation fails - as opposed to content where we are capable of saving the content
// item if server side validation fails
redirectOnFailure: false,
rebindCallback: function (orignal, saved) { }
}).then(function (saved) {
/**
* Used to emit the save event and await any async operations being performed by editor extensions
* @param {any} savedUser
*/
function extendedSave(savedUser) {
vm.user = saved;
setUserDisplayState();
formatDatesToLocal(vm.user);
//used to track any promises added by the event handlers to be awaited
var promises = [];
var args = {
//getPromise: getPromise,
user: savedUser,
//a promise can be added by the event handler if the handler needs an async operation to be awaited
addPromise: function (p) {
promises.push(p);
}
};
vm.changePasswordModel.isChanging = false;
vm.page.saveButtonState = "success";
//the user has a password if they are not states: Invited, NoCredentials
vm.changePasswordModel.config.hasPassword = vm.user.userState !== 3 && vm.user.userState !== 4;
}, function (err) {
vm.page.saveButtonState = "error";
});
//emit the event
eventsService.emit("editors.user.editController.save", args);
//await all promises to complete
var resultPromise = $q.all(promises);
return resultPromise;
}
function goToPage(ancestor) {

View File

@@ -1,406 +1,27 @@
<div ng-controller="Umbraco.Editors.Users.UserController as vm" class="clearfix">
<umb-load-indicator ng-if="vm.loading"></umb-load-indicator>
<form name="editUserForm" novalidate val-form-manager>
<umb-editor-view>
<umb-editor-header
name="vm.user.name"
hide-icon="true"
hide-description="true"
hide-alias="true">
<umb-editor-header name="vm.user.name"
hide-icon="true"
hide-description="true"
hide-alias="true"
navigation="vm.user.navigation">
</umb-editor-header>
<umb-editor-container>
<umb-load-indicator
ng-if="vm.loading">
</umb-load-indicator>
<div ng-if="!vm.loading" class="umb-packages-view-wrapper" style="padding: 0;">
<div class="umb-package-details">
<div ng-if="!vm.loading" class="umb-packages-view-wrapper" style="padding: 0;">
<div class="umb-package-details__main-content">
<umb-box>
<umb-box-header title-key="user_profile"></umb-box-header>
<umb-box-content class="block-form">
<umb-control-group label="@general_email" required="true">
<input
type="email"
localize="placeholder"
placeholder="@placeholders_enteremail"
class="input-block-level"
ng-model="vm.user.email"
umb-auto-focus name="email"
val-email
required
val-server-field="Email" />
<span class="help-inline" val-msg-for="email" val-toggle-msg="required"><localize key="general_required">Required</localize></span>
<span class="help-inline" val-msg-for="email" val-toggle-msg="valServerField"></span>
</umb-control-group>
<umb-control-group label="@general_username" ng-if="!vm.usernameIsEmail" required="true">
<input
type="text"
localize="placeholder"
placeholder="@placeholders_enterusername"
class="input-block-level"
ng-model="vm.user.username"
umb-auto-focus name="username"
required
val-server-field="Username" />
<span class="help-inline" val-msg-for="username" val-toggle-msg="required"><localize key="general_required">Required</localize></span>
<span class="help-inline" val-msg-for="username" val-toggle-msg="valServerField"></span>
</umb-control-group>
<umb-control-group label="@user_language" description="@user_languageHelp">
<select
class="input-block-level"
ng-model="vm.user.culture"
ng-options="key as value for (key, value) in vm.user.availableCultures"
name="culture"
required
val-server-field="Culture">
</select>
<span class="help-inline" val-msg-for="culture" val-toggle-msg="required"><localize key="general_required">Required</localize></span>
<span class="help-inline" val-msg-for="culture" val-toggle-msg="valServerField"></span>
</umb-control-group>
</umb-box-content>
</umb-box>
<umb-box>
<umb-box-header title-key="user_assignAccess"></umb-box-header>
<umb-box-content class="block-form">
<umb-control-group style="margin-bottom: 25px;" label="@general_groups" description="@user_groupsHelp" required="true">
<umb-user-group-preview
ng-repeat="userGroup in vm.user.userGroups"
icon="userGroup.icon"
name="userGroup.name"
sections="userGroup.sections"
content-start-node="userGroup.contentStartNode"
media-start-node="userGroup.mediaStartNode"
allow-remove="!vm.user.isCurrentUser"
on-remove="vm.removeSelectedItem($index, vm.user.userGroups)">
</umb-user-group-preview>
<a href=""
ng-if="!vm.user.isCurrentUser"
style="max-width: 100%;"
class="umb-node-preview-add"
ng-click="vm.openUserGroupPicker()"
prevent-default>
<localize key="general_add">Add</localize>
</a>
</umb-control-group>
<umb-control-group style="margin-bottom: 25px;" label="@user_startnodes" description="@user_startnodeshelp">
<umb-node-preview
style="max-width: 100%;"
ng-repeat="node in vm.user.startContentIds"
icon="node.icon"
name="node.name"
allow-remove="!vm.user.isCurrentUser"
on-remove="vm.removeSelectedItem($index, vm.user.startContentIds)">
</umb-node-preview>
<umb-node-preview
ng-if="vm.user.startContentIds.length === 0 && vm.user.isCurrentUser"
style="max-width: 100%;"
name="vm.labels.noStartNodes">
</umb-node-preview>
<a href=""
ng-if="!vm.user.isCurrentUser"
style="max-width: 100%;"
class="umb-node-preview-add"
ng-click="vm.openContentPicker()"
prevent-default>
<localize key="general_add">Add</localize>
</a>
</umb-control-group>
<umb-control-group label="@user_mediastartnodes" description="@user_mediastartnodeshelp">
<umb-node-preview
style="max-width: 100%;"
ng-repeat="node in vm.user.startMediaIds"
icon="node.icon"
name="node.name"
allow-remove="!vm.user.isCurrentUser"
on-remove="vm.removeSelectedItem($index, vm.user.startMediaIds)">
</umb-node-preview>
<umb-node-preview
ng-if="vm.user.startMediaIds.length === 0 && vm.user.isCurrentUser"
style="max-width: 100%;"
name="vm.labels.noStartNodes">
</umb-node-preview>
<a href=""
ng-if="!vm.user.isCurrentUser"
style="max-width: 100%;"
class="umb-node-preview-add"
ng-click="vm.openMediaPicker()"
prevent-default>
<localize key="general_add">Add</localize>
</a>
</umb-control-group>
</umb-box-content>
</umb-box>
<umb-box>
<umb-box-header title-key="user_access" description-key="user_accessHelp"></umb-box-header>
<umb-box-content class="block-form" style="padding-bottom: 0;">
<umb-control-group label="@sections_content">
<umb-node-preview
style="max-width: 100%;"
ng-repeat="node in vm.user.calculatedStartContentIds"
icon="node.icon"
name="node.name">
</umb-node-preview>
<umb-node-preview
ng-if="vm.user.calculatedStartContentIds.length === 0"
style="max-width: 100%;"
name="vm.labels.noStartNodes">
</umb-node-preview>
</umb-control-group>
<umb-control-group label="@sections_media">
<umb-node-preview
style="max-width: 100%;"
ng-repeat="node in vm.user.calculatedStartMediaIds"
icon="node.icon"
name="node.name">
</umb-node-preview>
<umb-node-preview
ng-if="vm.user.calculatedStartMediaIds.length === 0"
style="max-width: 100%;"
name="vm.labels.noStartNodes">
</umb-node-preview>
</umb-control-group>
</umb-box-content>
</umb-box>
</div>
<div class="umb-package-details__sidebar">
<div class="umb-package-details__section">
<!-- Avatar -->
<div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #d8d7d9;">
<ng-form name="avatarForm" class="flex flex-column justify-center items-center">
<umb-avatar
style="margin-bottom: 15px;"
color="secondary"
size="xxl"
name="{{vm.user.name}}"
img-src="{{vm.user.avatars[3]}}"
img-srcset="{{vm.user.avatars[4]}} 2x, {{vm.user.avatars[4]}} 3x">
</umb-avatar>
<umb-progress-bar
style="max-width: 120px;"
ng-if="vm.avatarFile.uploadStatus === 'uploading'"
progress="{{ vm.avatarFile.uploadProgress }}"
size="s">
</umb-progress-bar>
<div class="flex items-center" ng-if="vm.avatarFile.uploadStatus !== 'uploading'">
<a href=""
class="umb-user-group-preview__action"
ngf-select ng-model="filesHolder"
ngf-change="changeAvatar($files, $event)"
ngf-multiple="false"
ngf-pattern="{{vm.acceptedFileTypes}}"
ngf-max-size="{{ vm.maxFileSize }}">
<localize key="user_changePhoto">Change photo</localize>
</a>
<a href=""
ng-if="vm.user.avatars"
class="umb-user-group-preview__action umb-user-group-preview__action--red"
ng-click="vm.clearAvatar()"
prevent-default>
<localize key="user_removePhoto">Remove photo</localize>
</a>
</div>
</ng-form>
</div>
<!-- Actions -->
<div style="margin-bottom: 20px;">
<div style="margin-bottom: 10px;">
<umb-button
ng-if="vm.user.userDisplayState.key === 'Disabled' && !vm.user.isCurrentUser"
type="button"
button-style="[success,block]"
state="vm.enableUserButtonState"
action="vm.enableUser()"
label="Enable"
label-key="actions_enable"
size="s">
</umb-button>
</div>
<div style="margin-bottom: 10px;">
<umb-button
ng-if="vm.user.userDisplayState.key === 'LockedOut' && !vm.user.isCurrentUser"
type="button"
button-style="[success,block]"
state="vm.unlockUserButtonState"
action="vm.unlockUser()"
label="Unlock"
label-key="actions_unlock"
size="s">
</umb-button>
</div>
<div style="margin-bottom: 10px;">
<umb-button
ng-if="vm.user.userDisplayState.key !== 'Disabled' && !vm.user.isCurrentUser"
type="button"
button-style="[info,block]"
action="vm.disableUser()"
state="vm.disableUserButtonState"
label="Disable"
label-key="actions_disable"
size="s">
</umb-button>
</div>
<umb-button
type="button"
button-style="[info,block]"
action="vm.toggleChangePassword()"
label="Change password"
label-key="general_changePassword"
state="changePasswordButtonState"
ng-if="vm.changePasswordModel.isChanging === false"
size="s">
</umb-button>
<ng-form ng-if="vm.changePasswordModel.isChanging" name="passwordForm" class="block-form" val-form-manager>
<change-password
password-values="vm.user.changePassword"
config="vm.changePasswordModel.config">
</change-password>
<umb-button
type="button"
action="vm.toggleChangePassword()"
label="Cancel"
label-key="general_cancel"
button-style="cancel">
</umb-button>
</ng-form>
<div ng-if="vm.user.resetPasswordValue">
<p><br />Password reset to value: <strong>{{vm.user.resetPasswordValue}}</strong></p>
</div>
</div>
<!-- User stats -->
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<localize key="general_status">Status</localize>:
</div>
<div class="umb-package-details__information-item-content">
<umb-badge style="margin-top: 4px;" size="s" color="{{vm.user.userDisplayState.color}}">
{{vm.user.userDisplayState.name}}
</umb-badge>
</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<localize key="user_lastLogin">Last login</localize>:
</div>
<div class="umb-package-details__information-item-content">
<span ng-if="vm.user.lastLoginDate">{{ vm.user.formattedLastLogin }}</span>
<span ng-if="!vm.user.lastLoginDate">{{ vm.user.name | umbWordLimit:1 }} <localize key="user_noLogin">has not logged in yet</localize></span>
</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<localize key="user_failedPasswordAttempts">Failed login attempts</localize>:
</div>
<div class="umb-package-details__information-item-content">
{{ vm.user.failedPasswordAttempts }}
</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<localize key="user_lastLockoutDate">Last lockout date</localize>:
</div>
<div class="umb-package-details__information-item-content">
<span ng-if="vm.user.lastLockoutDate === '0001-01-01T00:00:00'">
{{ vm.user.name | umbWordLimit:1 }} <localize key="user_noLockouts">hasn't been locked out</localize>
</span>
<span ng-if="vm.user.lastLockoutDate !== '0001-01-01T00:00:00'">{{ vm.user.formattedLastLockoutDate }}</span>
</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<localize key="user_lastPasswordChangeDate">Password is last changed</localize>:
</div>
<div class="umb-package-details__information-item-content">
<span ng-if="vm.user.lastPasswordChangeDate === '0001-01-01T00:00:00'">
<localize key="user_noPasswordChange">The password hasn't been changed</localize>
</span>
<span ng-if="vm.user.lastPasswordChangeDate !== '0001-01-01T00:00:00'">{{ vm.user.formattedLastPasswordChangeDate }}</span>
</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<localize key="user_createDate">User is created</localize>:
</div>
<div class="umb-package-details__information-item-content">
{{ vm.user.formattedCreateDate }}
</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<localize key="user_updateDate">User is last updated</localize>:
</div>
<div class="umb-package-details__information-item-content">
{{ vm.user.formattedUpdateDate }}
</div>
</div>
</div>
</div>
</div>
<umb-editor-sub-views
ng-if="!vm.loading"
sub-views="vm.user.navigation"
model="vm">
</umb-editor-sub-views>
</div>
@@ -410,33 +31,30 @@
<umb-editor-footer-content-left>
<umb-breadcrumbs
ancestors="vm.breadcrumbs"
allow-on-open="true"
on-open="vm.goToPage(ancestor)">
<umb-breadcrumbs ancestors="vm.breadcrumbs"
allow-on-open="true"
on-open="vm.goToPage(ancestor)">
</umb-breadcrumbs>
</umb-editor-footer-content-left>
<umb-editor-footer-content-right>
<umb-button
type="button"
action="vm.goToPage(vm.breadcrumbs[0])"
label="Return to list"
label-key="buttons_returnToList"
disabled="vm.loading">
<umb-button type="button"
action="vm.goToPage(vm.breadcrumbs[0])"
label="Return to list"
label-key="buttons_returnToList"
disabled="vm.loading">
</umb-button>
<umb-button
type="button"
action="vm.save()"
state="vm.page.saveButtonState"
button-style="success"
shortcut="ctrl+s"
label="Save"
label-key="buttons_save"
disabled="vm.loading">
<umb-button type="button"
action="vm.save()"
state="vm.page.saveButtonState"
button-style="success"
shortcut="ctrl+s"
label="Save"
label-key="buttons_save"
disabled="vm.loading">
</umb-button>
</umb-editor-footer-content-right>
@@ -447,25 +65,22 @@
</form>
<umb-overlay
ng-if="vm.userGroupPicker.show"
model="vm.userGroupPicker"
view="vm.userGroupPicker.view"
position="right">
<umb-overlay ng-if="vm.userGroupPicker.show"
model="vm.userGroupPicker"
view="vm.userGroupPicker.view"
position="right">
</umb-overlay>
<umb-overlay
ng-if="vm.contentPicker.show"
model="vm.contentPicker"
view="vm.contentPicker.view"
position="right">
<umb-overlay ng-if="vm.contentPicker.show"
model="vm.contentPicker"
view="vm.contentPicker.view"
position="right">
</umb-overlay>
<umb-overlay
ng-if="vm.mediaPicker.show"
model="vm.mediaPicker"
view="vm.mediaPicker.view"
position="right">
<umb-overlay ng-if="vm.mediaPicker.show"
model="vm.mediaPicker"
view="vm.mediaPicker.view"
position="right">
</umb-overlay>
</div>

View File

@@ -0,0 +1,361 @@
<div class="umb-package-details">
<div class="umb-package-details__main-content">
<umb-box>
<umb-box-header title-key="user_profile"></umb-box-header>
<umb-box-content class="block-form">
<umb-control-group label="@general_email" required="true">
<input type="email"
localize="placeholder"
placeholder="@placeholders_enteremail"
class="input-block-level"
ng-model="model.user.email"
umb-auto-focus name="email"
val-email
required
val-server-field="Email" />
<span class="help-inline" val-msg-for="email" val-toggle-msg="required"><localize key="general_required">Required</localize></span>
<span class="help-inline" val-msg-for="email" val-toggle-msg="valServerField"></span>
</umb-control-group>
<umb-control-group label="@general_username" ng-if="!model.usernameIsEmail" required="true">
<input type="text"
localize="placeholder"
placeholder="@placeholders_enterusername"
class="input-block-level"
ng-model="model.user.username"
umb-auto-focus name="username"
required
val-server-field="Username" />
<span class="help-inline" val-msg-for="username" val-toggle-msg="required"><localize key="general_required">Required</localize></span>
<span class="help-inline" val-msg-for="username" val-toggle-msg="valServerField"></span>
</umb-control-group>
<umb-control-group label="@user_language" description="@user_languageHelp">
<select class="input-block-level"
ng-model="model.user.culture"
ng-options="key as value for (key, value) in model.user.availableCultures"
name="culture"
required
val-server-field="Culture"></select>
<span class="help-inline" val-msg-for="culture" val-toggle-msg="required"><localize key="general_required">Required</localize></span>
<span class="help-inline" val-msg-for="culture" val-toggle-msg="valServerField"></span>
</umb-control-group>
</umb-box-content>
</umb-box>
<umb-box>
<umb-box-header title-key="user_assignAccess"></umb-box-header>
<umb-box-content class="block-form">
<umb-control-group style="margin-bottom: 25px;" label="@general_groups" description="@user_groupsHelp" required="true">
<umb-user-group-preview ng-repeat="userGroup in model.user.userGroups"
icon="userGroup.icon"
name="userGroup.name"
sections="userGroup.sections"
content-start-node="userGroup.contentStartNode"
media-start-node="userGroup.mediaStartNode"
allow-remove="!model.user.isCurrentUser"
on-remove="model.removeSelectedItem($index, model.user.userGroups)">
</umb-user-group-preview>
<a href=""
ng-if="!model.user.isCurrentUser"
style="max-width: 100%;"
class="umb-node-preview-add"
ng-click="model.openUserGroupPicker()"
prevent-default>
<localize key="general_add">Add</localize>
</a>
</umb-control-group>
<umb-control-group style="margin-bottom: 25px;" label="@user_startnodes" description="@user_startnodeshelp">
<umb-node-preview style="max-width: 100%;"
ng-repeat="node in model.user.startContentIds"
icon="node.icon"
name="node.name"
allow-remove="!model.user.isCurrentUser"
on-remove="model.removeSelectedItem($index, model.user.startContentIds)">
</umb-node-preview>
<umb-node-preview ng-if="model.user.startContentIds.length === 0 && model.user.isCurrentUser"
style="max-width: 100%;"
name="model.labels.noStartNodes">
</umb-node-preview>
<a href=""
ng-if="!model.user.isCurrentUser"
style="max-width: 100%;"
class="umb-node-preview-add"
ng-click="model.openContentPicker()"
prevent-default>
<localize key="general_add">Add</localize>
</a>
</umb-control-group>
<umb-control-group label="@user_mediastartnodes" description="@user_mediastartnodeshelp">
<umb-node-preview style="max-width: 100%;"
ng-repeat="node in model.user.startMediaIds"
icon="node.icon"
name="node.name"
allow-remove="!model.user.isCurrentUser"
on-remove="model.removeSelectedItem($index, model.user.startMediaIds)">
</umb-node-preview>
<umb-node-preview ng-if="model.user.startMediaIds.length === 0 && model.user.isCurrentUser"
style="max-width: 100%;"
name="model.labels.noStartNodes">
</umb-node-preview>
<a href=""
ng-if="!model.user.isCurrentUser"
style="max-width: 100%;"
class="umb-node-preview-add"
ng-click="model.openMediaPicker()"
prevent-default>
<localize key="general_add">Add</localize>
</a>
</umb-control-group>
</umb-box-content>
</umb-box>
<umb-box>
<umb-box-header title-key="user_access" description-key="user_accessHelp"></umb-box-header>
<umb-box-content class="block-form" style="padding-bottom: 0;">
<umb-control-group label="@sections_content">
<umb-node-preview style="max-width: 100%;"
ng-repeat="node in model.user.calculatedStartContentIds"
icon="node.icon"
name="node.name">
</umb-node-preview>
<umb-node-preview ng-if="model.user.calculatedStartContentIds.length === 0"
style="max-width: 100%;"
name="model.labels.noStartNodes">
</umb-node-preview>
</umb-control-group>
<umb-control-group label="@sections_media">
<umb-node-preview style="max-width: 100%;"
ng-repeat="node in model.user.calculatedStartMediaIds"
icon="node.icon"
name="node.name">
</umb-node-preview>
<umb-node-preview ng-if="model.user.calculatedStartMediaIds.length === 0"
style="max-width: 100%;"
name="model.labels.noStartNodes">
</umb-node-preview>
</umb-control-group>
</umb-box-content>
</umb-box>
</div>
<div class="umb-package-details__sidebar">
<div class="umb-package-details__section">
<!-- Avatar -->
<div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #d8d7d9;">
<ng-form name="avatarForm" class="flex flex-column justify-center items-center">
<umb-avatar style="margin-bottom: 15px;"
color="secondary"
size="xxl"
name="{{model.user.name}}"
img-src="{{model.user.avatars[3]}}"
img-srcset="{{model.user.avatars[4]}} 2x, {{model.user.avatars[4]}} 3x">
</umb-avatar>
<umb-progress-bar style="max-width: 120px;"
ng-if="model.avatarFile.uploadStatus === 'uploading'"
progress="{{ model.avatarFile.uploadProgress }}"
size="s">
</umb-progress-bar>
<div class="flex items-center" ng-if="model.avatarFile.uploadStatus !== 'uploading'">
<a href=""
class="umb-user-group-preview__action"
ngf-select ng-model="filesHolder"
ngf-change="changeAvatar($files, $event)"
ngf-multiple="false"
ngf-pattern="{{model.acceptedFileTypes}}"
ngf-max-size="{{ model.maxFileSize }}">
<localize key="user_changePhoto">Change photo</localize>
</a>
<a href=""
ng-if="model.user.avatars"
class="umb-user-group-preview__action umb-user-group-preview__action--red"
ng-click="model.clearAvatar()"
prevent-default>
<localize key="user_removePhoto">Remove photo</localize>
</a>
</div>
</ng-form>
</div>
<!-- Actions -->
<div style="margin-bottom: 20px;">
<div style="margin-bottom: 10px;">
<umb-button ng-if="model.user.userDisplayState.key === 'Disabled' && !model.user.isCurrentUser"
type="button"
button-style="[success,block]"
state="model.enableUserButtonState"
action="model.enableUser()"
label="Enable"
label-key="actions_enable"
size="s">
</umb-button>
</div>
<div style="margin-bottom: 10px;">
<umb-button ng-if="model.user.userDisplayState.key === 'LockedOut' && !model.user.isCurrentUser"
type="button"
button-style="[success,block]"
state="model.unlockUserButtonState"
action="model.unlockUser()"
label="Unlock"
label-key="actions_unlock"
size="s">
</umb-button>
</div>
<div style="margin-bottom: 10px;">
<umb-button ng-if="model.user.userDisplayState.key !== 'Disabled' && !model.user.isCurrentUser"
type="button"
button-style="[info,block]"
action="model.disableUser()"
state="model.disableUserButtonState"
label="Disable"
label-key="actions_disable"
size="s">
</umb-button>
</div>
<umb-button type="button"
button-style="[info,block]"
action="model.toggleChangePassword()"
label="Change password"
label-key="general_changePassword"
state="changePasswordButtonState"
ng-if="model.changePasswordModel.isChanging === false"
size="s">
</umb-button>
<ng-form ng-if="model.changePasswordModel.isChanging" name="passwordForm" class="block-form" val-form-manager>
<change-password password-values="model.user.changePassword"
config="model.changePasswordModel.config">
</change-password>
<umb-button type="button"
action="model.toggleChangePassword()"
label="Cancel"
label-key="general_cancel"
button-style="cancel">
</umb-button>
</ng-form>
<div ng-if="model.user.resetPasswordValue">
<p><br />Password reset to value: <strong>{{model.user.resetPasswordValue}}</strong></p>
</div>
</div>
<!-- User stats -->
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<localize key="general_status">Status</localize>:
</div>
<div class="umb-package-details__information-item-content">
<umb-badge style="margin-top: 4px;" size="s" color="{{model.user.userDisplayState.color}}">
{{model.user.userDisplayState.name}}
</umb-badge>
</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<localize key="user_lastLogin">Last login</localize>:
</div>
<div class="umb-package-details__information-item-content">
<span ng-if="model.user.lastLoginDate">{{ model.user.formattedLastLogin }}</span>
<span ng-if="!model.user.lastLoginDate">{{ model.user.name | umbWordLimit:1 }} <localize key="user_noLogin">has not logged in yet</localize></span>
</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<localize key="user_failedPasswordAttempts">Failed login attempts</localize>:
</div>
<div class="umb-package-details__information-item-content">
{{ model.user.failedPasswordAttempts }}
</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<localize key="user_lastLockoutDate">Last lockout date</localize>:
</div>
<div class="umb-package-details__information-item-content">
<span ng-if="model.user.lastLockoutDate === '0001-01-01T00:00:00'">
{{ model.user.name | umbWordLimit:1 }} <localize key="user_noLockouts">hasn't been locked out</localize>
</span>
<span ng-if="model.user.lastLockoutDate !== '0001-01-01T00:00:00'">{{ model.user.formattedLastLockoutDate }}</span>
</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<localize key="user_lastPasswordChangeDate">Password is last changed</localize>:
</div>
<div class="umb-package-details__information-item-content">
<span ng-if="model.user.lastPasswordChangeDate === '0001-01-01T00:00:00'">
<localize key="user_noPasswordChange">The password hasn't been changed</localize>
</span>
<span ng-if="model.user.lastPasswordChangeDate !== '0001-01-01T00:00:00'">{{ model.user.formattedLastPasswordChangeDate }}</span>
</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<localize key="user_createDate">User is created</localize>:
</div>
<div class="umb-package-details__information-item-content">
{{ model.user.formattedCreateDate }}
</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<localize key="user_updateDate">User is last updated</localize>:
</div>
<div class="umb-package-details__information-item-content">
{{ model.user.formattedUpdateDate }}
</div>
</div>
</div>
</div>
</div>