Merge remote-tracking branch 'origin/temp-U4-9776' into temp-U4-8632
# Conflicts: # src/Umbraco.Web.UI/config/trees.config
This commit is contained in:
@@ -118,6 +118,8 @@
|
||||
|
||||
public const string Scripts = "scripts";
|
||||
|
||||
public const string Users = "users";
|
||||
|
||||
//TODO: Fill in the rest!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,15 +52,38 @@ Use this directive to render an avatar.
|
||||
|
||||
function AvatarDirective() {
|
||||
|
||||
function link(scope, element, attrs, ctrl) {
|
||||
|
||||
scope.initials = "";
|
||||
|
||||
function onInit() {
|
||||
scope.initials = getNameInitials(scope.name);
|
||||
}
|
||||
|
||||
function getNameInitials(name) {
|
||||
if(name) {
|
||||
var initials = name.match(/\b\w/g) || [];
|
||||
initials = ((initials.shift() || '') + (initials.pop() || '')).toUpperCase();
|
||||
return initials;
|
||||
}
|
||||
}
|
||||
|
||||
onInit();
|
||||
|
||||
}
|
||||
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
templateUrl: 'views/components/umb-avatar.html',
|
||||
scope: {
|
||||
size: "@",
|
||||
name: "@",
|
||||
color: "@",
|
||||
imgSrc: "@",
|
||||
imgSrcset: "@"
|
||||
}
|
||||
},
|
||||
link: link
|
||||
};
|
||||
|
||||
return directive;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
function BadgeDirective() {
|
||||
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
transclude: true,
|
||||
templateUrl: 'views/components/umb-badge.html',
|
||||
scope: {
|
||||
size: "@?",
|
||||
color: "@?"
|
||||
}
|
||||
};
|
||||
|
||||
return directive;
|
||||
|
||||
}
|
||||
|
||||
angular.module('umbraco.directives').directive('umbBadge', BadgeDirective);
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,23 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
function CheckmarkDirective() {
|
||||
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
transclude: true,
|
||||
templateUrl: 'views/components/umb-checkmark.html',
|
||||
scope: {
|
||||
size: "@?",
|
||||
checked: "="
|
||||
}
|
||||
};
|
||||
|
||||
return directive;
|
||||
|
||||
}
|
||||
|
||||
angular.module('umbraco.directives').directive('umbCheckmark', CheckmarkDirective);
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @ngdoc filter
|
||||
* @name umbraco.filters.filter:umbWordLimit
|
||||
* @namespace umbWordLimitFilter
|
||||
*
|
||||
* @description
|
||||
* Limits the number of words in a string to the passed in value
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function umbWordLimitFilter() {
|
||||
return function (collection, property) {
|
||||
|
||||
if (!angular.isString(collection)) {
|
||||
return collection;
|
||||
}
|
||||
|
||||
if (angular.isUndefined(property)) {
|
||||
return collection;
|
||||
}
|
||||
|
||||
var newString = "";
|
||||
var array = [];
|
||||
|
||||
array = collection.split(" ", property);
|
||||
array.length = property;
|
||||
newString = array.join(" ");
|
||||
|
||||
return newString;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('umbraco.filters').filter('umbWordLimit', umbWordLimitFilter);
|
||||
|
||||
})();
|
||||
288
src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js
Normal file
288
src/Umbraco.Web.UI.Client/src/common/resources/users.resource.js
Normal file
@@ -0,0 +1,288 @@
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.resources.usersResource
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Used by the users section to get users and send requests to create, invite, delete, etc. users.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function usersResource($http, umbRequestHelper, $q) {
|
||||
|
||||
function getUser() {
|
||||
var deferred = $q.defer();
|
||||
var user = {
|
||||
"name": "Tammy Contreras",
|
||||
"email": "tammy@contreras.com"
|
||||
};
|
||||
deferred.resolve(user);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function getUsers() {
|
||||
var deferred = $q.defer();
|
||||
var users = [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Tammy Contreras",
|
||||
"userGroups": [
|
||||
{
|
||||
"name": "Admin"
|
||||
}
|
||||
],
|
||||
"avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/adellecharles/128.jpg",
|
||||
"state": "active",
|
||||
"lastLogin": "2014-04-25T01:32:21.196Z"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Edward Flores",
|
||||
"userGroups": [
|
||||
{
|
||||
"name": "Admin"
|
||||
}
|
||||
],
|
||||
"avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/marcosmoralez/128.jpg",
|
||||
"state": "active",
|
||||
"lastLogin": "2014-04-25T01:32:21.196Z"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "Benjamin Mills",
|
||||
"userGroups": [
|
||||
{
|
||||
"name": "Writer"
|
||||
}
|
||||
],
|
||||
"avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/dancounsell/128.jpg",
|
||||
"state": "disabled",
|
||||
"lastLogin": "2014-04-25T01:32:21.196Z"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "Samantha Martinez",
|
||||
"userGroups": [
|
||||
{
|
||||
"name": "Editor"
|
||||
}
|
||||
],
|
||||
"avatar": "",
|
||||
"state": "pending",
|
||||
"lastLogin": ""
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"name": "Angela Stone",
|
||||
"userGroups": [
|
||||
{
|
||||
"name": "Editor"
|
||||
}
|
||||
],
|
||||
"avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/jina/128.jpg",
|
||||
"state": "active",
|
||||
"lastLogin": "2014-04-25T01:32:21.196Z"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"name": "Beverly Silva",
|
||||
"userGroups": [
|
||||
{
|
||||
"name": "Editor"
|
||||
}
|
||||
],
|
||||
"avatar": "",
|
||||
"state": "active",
|
||||
"lastLogin": "2014-04-25T01:32:21.196Z"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"name": "Arthur Welch",
|
||||
"userGroups": [
|
||||
{
|
||||
"name": "Editor"
|
||||
}
|
||||
],
|
||||
"avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/ashleyford/128.jpg",
|
||||
"state": "active",
|
||||
"lastLogin": "2014-04-25T01:32:21.196Z"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"name": "Ruth Turner",
|
||||
"userGroups": [
|
||||
{
|
||||
"name": "Translator"
|
||||
},
|
||||
{
|
||||
"name": "Editor"
|
||||
}
|
||||
],
|
||||
"avatar": "",
|
||||
"state": "pending",
|
||||
"lastLogin": ""
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"name": "Tammy Contreras",
|
||||
"userGroups": [
|
||||
{
|
||||
"name": "Translator"
|
||||
}
|
||||
],
|
||||
"avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/adellecharles/128.jpg",
|
||||
"state": "active",
|
||||
"lastLogin": "2014-04-25T01:32:21.196Z"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"name": "Edward Flores",
|
||||
"userGroups": [
|
||||
{
|
||||
"name": "Admin"
|
||||
}
|
||||
],
|
||||
"avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/marcosmoralez/128.jpg",
|
||||
"state": "active",
|
||||
"lastLogin": "2014-04-25T01:32:21.196Z"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"name": "Benjamin Mills",
|
||||
"userGroups": [
|
||||
{
|
||||
"name": "Writer"
|
||||
},
|
||||
{
|
||||
"name": "Translator"
|
||||
}
|
||||
],
|
||||
"avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/dancounsell/128.jpg",
|
||||
"state": "disabled",
|
||||
"lastLogin": "2014-04-25T01:32:21.196Z"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"name": "Samantha Martinez",
|
||||
"userGroupName": "Editor",
|
||||
"userGroups": [
|
||||
{
|
||||
"name": "Editor"
|
||||
}
|
||||
],
|
||||
"avatar": "",
|
||||
"state": "pending",
|
||||
"lastLogin": ""
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"name": "Angela Stone",
|
||||
"userGroups": [
|
||||
{
|
||||
"name": "Editor"
|
||||
}
|
||||
],
|
||||
"avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/jina/128.jpg",
|
||||
"state": "active",
|
||||
"lastLogin": "2014-04-25T01:32:21.196Z"
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"name": "Beverly Silva",
|
||||
"userGroups": [
|
||||
{
|
||||
"name": "Editor"
|
||||
}
|
||||
],
|
||||
"avatar": "",
|
||||
"state": "active",
|
||||
"lastLogin": "2014-04-25T01:32:21.196Z"
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"name": "Arthur Welch",
|
||||
"userGroups": [
|
||||
{
|
||||
"name": "Editor"
|
||||
}
|
||||
],
|
||||
"avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/ashleyford/128.jpg",
|
||||
"state": "active",
|
||||
"lastLogin": "2014-04-25T01:32:21.196Z"
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"name": "Ruth Turner",
|
||||
"userGroups": [
|
||||
{
|
||||
"name": "Translator"
|
||||
}
|
||||
],
|
||||
"avatar": "",
|
||||
"state": "pending",
|
||||
"lastLogin": ""
|
||||
}
|
||||
];
|
||||
deferred.resolve(users);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function getUserRole() {
|
||||
var deferred = $q.defer();
|
||||
var user = {
|
||||
"name": "Admin",
|
||||
"alias": "admin",
|
||||
"id": 1,
|
||||
"icon": "icon-medal"
|
||||
};
|
||||
deferred.resolve(user);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function getUserGroups() {
|
||||
var deferred = $q.defer();
|
||||
var userGroups = [
|
||||
{
|
||||
"name": "Admin",
|
||||
"alias": "admin",
|
||||
"id": 1,
|
||||
"icon": "icon-medal"
|
||||
},
|
||||
{
|
||||
"name": "Writer",
|
||||
"alias": "writer",
|
||||
"id": 2,
|
||||
"icon": "icon-edit"
|
||||
},
|
||||
{
|
||||
"name": "Editor",
|
||||
"alias": "editor",
|
||||
"id": 3,
|
||||
"icon": "icon-tools"
|
||||
},
|
||||
{
|
||||
"name": "Translator",
|
||||
"alias": "translator",
|
||||
"id": 4,
|
||||
"icon": "icon-globe"
|
||||
}
|
||||
];
|
||||
deferred.resolve(userGroups);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
var resource = {
|
||||
getUser: getUser,
|
||||
getUsers: getUsers,
|
||||
getUserRole: getUserRole,
|
||||
getUserGroups: getUserGroups
|
||||
};
|
||||
|
||||
return resource;
|
||||
|
||||
}
|
||||
|
||||
angular.module('umbraco.resources').factory('usersResource', usersResource);
|
||||
|
||||
})();
|
||||
@@ -120,6 +120,8 @@
|
||||
@import "components/umb-querybuilder.less";
|
||||
@import "components/umb-pagination.less";
|
||||
@import "components/umb-mini-list-view.less";
|
||||
@import "components/umb-badge.less";
|
||||
@import "components/umb-checkmark.less";
|
||||
|
||||
@import "components/buttons/umb-button.less";
|
||||
@import "components/buttons/umb-button-group.less";
|
||||
@@ -130,6 +132,8 @@
|
||||
@import "components/umb-node-preview.less";
|
||||
@import "components/umb-mini-editor.less";
|
||||
|
||||
@import "components/users/umb-users.less";
|
||||
|
||||
// Utilities
|
||||
@import "utilities/_flexbox.less";
|
||||
@import "utilities/_spacing.less";
|
||||
|
||||
@@ -18,10 +18,12 @@
|
||||
transition: opacity 0.25s ease;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.umb-button__icon {
|
||||
margin-right: 5px;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
.umb-button__content.-hidden {
|
||||
|
||||
@@ -2,29 +2,87 @@
|
||||
border-radius: 50%;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background-color: transparent;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: @black;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.umb-avatar.-xs {
|
||||
/* Sizes */
|
||||
|
||||
.umb-avatar--xxs {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.umb-avatar--xs {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.umb-avatar.-s {
|
||||
.umb-avatar--s {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.umb-avatar.-m {
|
||||
.umb-avatar--m {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.umb-avatar.-l {
|
||||
.umb-avatar--l {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.umb-avatar.-xl {
|
||||
.umb-avatar--xl {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
/* Colors */
|
||||
|
||||
.umb-avatar--white {
|
||||
background-color: @white;
|
||||
color: @black;
|
||||
}
|
||||
|
||||
.umb-avatar--gray {
|
||||
background-color: @gray-10;
|
||||
color: @black;
|
||||
}
|
||||
|
||||
.umb-avatar--primary {
|
||||
background-color: @turquoise-l1;
|
||||
color: @white;
|
||||
}
|
||||
|
||||
.umb-avatar--secondary {
|
||||
background-color: @purple-l1;
|
||||
color: @white;
|
||||
}
|
||||
|
||||
.umb-avatar--success {
|
||||
background-color: @green-l1;
|
||||
color: @white;
|
||||
}
|
||||
|
||||
.umb-avatar--warning {
|
||||
background-color: @yellow-l1;
|
||||
color: @white;
|
||||
}
|
||||
|
||||
.umb-avatar--danger {
|
||||
background-color: @red-l1;
|
||||
color: @white;
|
||||
}
|
||||
|
||||
65
src/Umbraco.Web.UI.Client/src/less/components/umb-badge.less
Normal file
65
src/Umbraco.Web.UI.Client/src/less/components/umb-badge.less
Normal file
@@ -0,0 +1,65 @@
|
||||
.umb-badge {
|
||||
padding: 6px 8px;
|
||||
margin: 0 5px 0 0;
|
||||
font-weight: 600;
|
||||
color: @black;
|
||||
background-color: @turquoise-washed;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: @turquoise;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
// Colors
|
||||
.umb-badge--primary {
|
||||
background-color: @turquoise-washed;
|
||||
border-color: @turquoise;
|
||||
}
|
||||
|
||||
.umb-badge--seconday {
|
||||
background-color: @purple-washed;
|
||||
border-color: @purple;
|
||||
}
|
||||
|
||||
.umb-badge--danger {
|
||||
background-color: @red-washed;
|
||||
border-color: @red;
|
||||
}
|
||||
|
||||
.umb-badge--warning {
|
||||
background-color: @yellow-washed;
|
||||
border-color: @yellow;
|
||||
}
|
||||
|
||||
.umb-badge--success {
|
||||
background-color: @green-washed;
|
||||
border-color: @green;
|
||||
}
|
||||
|
||||
// Size
|
||||
.umb-badge--xs {
|
||||
font-size: 13px;
|
||||
padding: 1px 6px;
|
||||
}
|
||||
|
||||
.umb-badge--s {
|
||||
font-size: 14px;
|
||||
padding: 3px 6px;
|
||||
}
|
||||
|
||||
.umb-badge--m {
|
||||
font-size: 16px;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.umb-badge--l {
|
||||
font-size: 18px;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.umb-badge--xl {
|
||||
font-size: 20px;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
.umb-checkmark {
|
||||
border: 2px solid @white;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
background: @gray-7;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: @white;
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.umb-checkmark--checked {
|
||||
background: @green;
|
||||
}
|
||||
|
||||
.umb-checkmark--xs {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.umb-checkmark--s {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.umb-checkmark--m {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.umb-checkmark--l {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.umb-checkmark--xl {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
font-size: 20px;
|
||||
}
|
||||
@@ -76,7 +76,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px dashed @gray-8;
|
||||
border: 1px dashed @gray-7;
|
||||
color: @turquoise-d1;
|
||||
font-weight: bold;
|
||||
padding: 5px 15px;
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
.umb-users {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin: -10px;
|
||||
}
|
||||
|
||||
.umb-user {
|
||||
flex: 1 1 200px;
|
||||
background-color: @gray-10;
|
||||
border: 1px solid @gray-9;
|
||||
border-radius: 3px;
|
||||
padding: 15px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 10px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.umb-user--selected {
|
||||
border-color: @turquoise;
|
||||
}
|
||||
|
||||
.umb-user:hover {
|
||||
border-color: @turquoise;
|
||||
}
|
||||
|
||||
.umb-user__avatar {
|
||||
margin-bottom: 10px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.umb-user__badge {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.umb-user__name {
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.umb-user__checkmark {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.umb-user__group {
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.umb-user__divider {
|
||||
display: block;
|
||||
width: 20px;
|
||||
border-bottom: 1px solid @gray-7;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 10px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.umb-user__last-login {
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -113,6 +113,17 @@ h5.-black {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* BLOCK MODE */
|
||||
.block-form .umb-control-group {
|
||||
border-bottom: none;
|
||||
margin-bottom: 10px !important;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.block-form .umb-control-group label .help-block,
|
||||
.block-form .umb-control-group label small {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/*COMPACT MODE */
|
||||
.compact .umb-pane{margin: 0px 0px 15px 0px;}
|
||||
|
||||
@@ -13,6 +13,14 @@ table {
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
table thead {
|
||||
background-color: @gray-10;
|
||||
}
|
||||
|
||||
table tr:hover {
|
||||
background-color: @gray-10;
|
||||
}
|
||||
|
||||
|
||||
// BASELINE STYLES
|
||||
// ---------------
|
||||
@@ -20,13 +28,13 @@ table {
|
||||
.table {
|
||||
width: 100%;
|
||||
margin-bottom: @baseLineHeight;
|
||||
border: 1px solid @gray-8;
|
||||
// Cells
|
||||
th,
|
||||
td {
|
||||
padding: 8px;
|
||||
padding: 10px 8px;
|
||||
line-height: @baseLineHeight;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
border-top: 1px solid @tableBorder;
|
||||
}
|
||||
th {
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
angular.module("umbraco").controller("Umbraco.Dialogs.LoginController",
|
||||
function ($scope, $cookies, localizationService, userService, externalLoginInfo, resetPasswordCodeInfo, $timeout, authResource, dialogService) {
|
||||
function ($scope, $cookies, $location, localizationService, userService, externalLoginInfo, resetPasswordCodeInfo, $timeout, authResource, dialogService) {
|
||||
|
||||
$scope.isInvite = false;
|
||||
|
||||
function init() {
|
||||
// Check if it is a new user
|
||||
if ($location.search().invite) {
|
||||
$scope.isInvite = true;
|
||||
$scope.inviteSetPassword = true;
|
||||
}
|
||||
}
|
||||
|
||||
$scope.inviteSavePassword = function() {
|
||||
$scope.inviteSetPassword = false;
|
||||
$scope.inviteSetAvatar = true;
|
||||
};
|
||||
|
||||
var setFieldFocus = function(form, field) {
|
||||
$timeout(function() {
|
||||
@@ -234,4 +249,6 @@
|
||||
$scope.showLogin();
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div ng-controller="Umbraco.Dialogs.LoginController">
|
||||
|
||||
|
||||
<div id="login" class="umb-modalcolumn umb-dialog" ng-class="{'show-validation': loginForm.$invalid}" ng-cloak konami-code="activateKonamiMode()">
|
||||
|
||||
<div class="login-overlay__background-image" ng-if="backgroundImage" ng-style="{'background-image':'url(' + backgroundImage + ')'}"></div>
|
||||
@@ -8,7 +8,61 @@
|
||||
<img ng-src="assets/img/application/logo.png" ng-srcset="assets/img/application/logo@2x.png 2x, assets/img/application/logo@3x.png 3x">
|
||||
</div>
|
||||
|
||||
<div class="umb-login-container">
|
||||
<div ng-show="isInvite" class="umb-login-container">
|
||||
|
||||
<div class="form" ng-if="inviteSetPassword">
|
||||
<h1 style="margin-bottom: 10px; text-align: left;">Hi, User</h1>
|
||||
<p style="line-height: 1.6; margin-bottom: 25px;">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non libero vel turpis ultrices pharetra.</p>
|
||||
|
||||
<div class="control-group" ng-class="{error: setPasswordForm.password.$invalid}">
|
||||
<label>
|
||||
<localize key="user_newPassword">New password</localize>
|
||||
<small style="font-size: 13px;">Some hint here about the new password</small>
|
||||
</label>
|
||||
<input type="password" ng-model="password" name="password" class="-full-width-input" umb-auto-focus />
|
||||
</div>
|
||||
|
||||
<div class="control-group" ng-class="{error: setPasswordForm.confirmPassword.$invalid}">
|
||||
<label><localize key="user_confirmNewPassword">Confirm new password</localize></label>
|
||||
<input type="password" ng-model="confirmPassword" name="confirmPassword" class="-full-width-input" />
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center">
|
||||
<umb-button
|
||||
type="button"
|
||||
button-style="success"
|
||||
action="inviteSavePassword()"
|
||||
label="Save password">
|
||||
</umb-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form" ng-if="inviteSetAvatar">
|
||||
|
||||
<div class="flex justify-center items-center">
|
||||
<div style="border-radius: 50%; width: 100px; height: 100px; font-size: 50px; text-align: center; display: flex; align-items: center; justify-content: center; background-color: #F3F3F5; border: 2px dashed #A2A1A6; color: #A2A1A6;">+</div>
|
||||
</div>
|
||||
|
||||
<h1 style="margin-bottom: 10px;">Upload a photo</h1>
|
||||
<p style="text-align: center; margin-bottom: 25px; line-height: 1.6em;">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non libero vel turpis ultrices pharetra.</p>
|
||||
<div class="flex justify-center items-center">
|
||||
<umb-button
|
||||
type="button"
|
||||
button-style="success"
|
||||
label="Get started">
|
||||
</umb-button>
|
||||
<umb-button
|
||||
type="button"
|
||||
button-style="link"
|
||||
label="Skip">
|
||||
</umb-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div ng-show="!isInvite" class="umb-login-container">
|
||||
|
||||
<div class="form">
|
||||
<h1>{{greeting}}</h1>
|
||||
|
||||
@@ -3,11 +3,9 @@
|
||||
<div class="umb-el-wrap">
|
||||
<label ng-if="hideLabel!=='true'" class="control-label" for="{{alias}}">
|
||||
{{labelstring}}
|
||||
<small>{{descriptionstring}}</small>
|
||||
<small ng-if="descriptionstring">{{descriptionstring}}</small>
|
||||
</label>
|
||||
|
||||
<div class="controls controls-row" ng-transclude>
|
||||
</div>
|
||||
<div class="controls controls-row" ng-transclude></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1 +1,7 @@
|
||||
<img class="umb-avatar -{{size}}" ng-src="{{imgSrc}}" ng-srcset="{{imgSrcset}}" />
|
||||
<div>
|
||||
<img class="umb-avatar umb-avatar--{{size}}" ng-if="imgSrc || imgSrcset" ng-src="{{imgSrc}}" ng-srcset="{{imgSrcset}}" />
|
||||
<div class="umb-avatar umb-avatar--{{size}} umb-avatar--{{color}}" ng-if="!imgSrc && !imgSrcset">
|
||||
<span ng-if="name">{{ initials }}</span>
|
||||
<span ng-if="!name">?</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1 @@
|
||||
<span class="umb-badge umb-badge--{{color}} umb-badge--{{size}}" ng-transclude></span>
|
||||
@@ -0,0 +1 @@
|
||||
<i class="icon-check umb-checkmark umb-checkmark--{{size}}" ng-class="{'umb-checkmark--checked': checked}"></i>
|
||||
@@ -0,0 +1,34 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function UsersOverviewController($scope) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
vm.page = {};
|
||||
vm.page.name = "User Management";
|
||||
vm.page.navigation = [
|
||||
{
|
||||
"name": "Users",
|
||||
"icon": "icon-user",
|
||||
"view": "views/users/views/users/users.html",
|
||||
"active": true
|
||||
},
|
||||
{
|
||||
"name": "Roles",
|
||||
"icon": "icon-users",
|
||||
"view": "views/users/views/roles/roles.html"
|
||||
}
|
||||
];
|
||||
|
||||
function init() {
|
||||
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.Users.OverviewController", UsersOverviewController);
|
||||
|
||||
})();
|
||||
28
src/Umbraco.Web.UI.Client/src/views/users/overview.html
Normal file
28
src/Umbraco.Web.UI.Client/src/views/users/overview.html
Normal file
@@ -0,0 +1,28 @@
|
||||
<div ng-controller="Umbraco.Editors.Users.OverviewController as vm" class="clearfix">
|
||||
|
||||
<form name="usersForm" novalidate val-form-manager>
|
||||
|
||||
<umb-editor-view footer="false">
|
||||
|
||||
<umb-editor-header
|
||||
name="vm.page.name"
|
||||
name-locked="true"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
navigation="vm.page.navigation"
|
||||
hide-alias="true">
|
||||
</umb-editor-header>
|
||||
|
||||
<umb-editor-container>
|
||||
|
||||
<umb-editor-sub-views
|
||||
sub-views="vm.page.navigation">
|
||||
</umb-editor-sub-views>
|
||||
|
||||
</umb-editor-container>
|
||||
|
||||
</umb-editor-view>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
40
src/Umbraco.Web.UI.Client/src/views/users/role.controller.js
Normal file
40
src/Umbraco.Web.UI.Client/src/views/users/role.controller.js
Normal file
@@ -0,0 +1,40 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function UserRoleEditController($scope, $timeout, $location, usersResource) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
vm.loading = false;
|
||||
vm.page = {};
|
||||
vm.userRole = {};
|
||||
|
||||
vm.goBack = goBack;
|
||||
|
||||
function init() {
|
||||
|
||||
vm.loading = true;
|
||||
|
||||
// get user
|
||||
usersResource.getUserRole().then(function (userRole) {
|
||||
vm.userRole = userRole;
|
||||
});
|
||||
|
||||
// fake loading
|
||||
$timeout(function () {
|
||||
vm.loading = false;
|
||||
}, 500);
|
||||
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
$location.path("users/users/overview");
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.Users.RoleController", UserRoleEditController);
|
||||
|
||||
})();
|
||||
48
src/Umbraco.Web.UI.Client/src/views/users/role.html
Normal file
48
src/Umbraco.Web.UI.Client/src/views/users/role.html
Normal file
@@ -0,0 +1,48 @@
|
||||
<div ng-controller="Umbraco.Editors.Users.RoleController as vm" class="clearfix">
|
||||
|
||||
<umb-load-indicator ng-show="vm.loading"></umb-load-indicator>
|
||||
|
||||
<form name="editUserForm" novalidate val-form-manager>
|
||||
|
||||
<umb-editor-view>
|
||||
|
||||
<umb-editor-header
|
||||
name="vm.userRole.name"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
hide-alias="true">
|
||||
</umb-editor-header>
|
||||
|
||||
<umb-editor-container>
|
||||
|
||||
<umb-editor-sub-header>
|
||||
<umb-editor-sub-header-content-left>
|
||||
<a class="umb-package-details__back-link" href="" ng-click="vm.goBack()">← Back to roles</a>
|
||||
</umb-editor-sub-header-content-left>
|
||||
</umb-editor-sub-header>
|
||||
|
||||
</umb-editor-container>
|
||||
|
||||
<umb-editor-footer>
|
||||
|
||||
<umb-editor-footer-content-right>
|
||||
|
||||
<umb-button
|
||||
type="button"
|
||||
action="vm.save()"
|
||||
state="vm.page.saveButtonState"
|
||||
button-style="success"
|
||||
shortcut="ctrl+s"
|
||||
label="Save"
|
||||
label-key="buttons_save">
|
||||
</umb-button>
|
||||
|
||||
</umb-editor-footer-content-right>
|
||||
|
||||
</umb-editor-footer>
|
||||
|
||||
</umb-editor-view>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
40
src/Umbraco.Web.UI.Client/src/views/users/user.controller.js
Normal file
40
src/Umbraco.Web.UI.Client/src/views/users/user.controller.js
Normal file
@@ -0,0 +1,40 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function UserEditController($scope, $timeout, $location, usersResource) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
vm.loading = false;
|
||||
vm.page = {};
|
||||
vm.user = {};
|
||||
|
||||
vm.goBack = goBack;
|
||||
|
||||
function init() {
|
||||
|
||||
vm.loading = true;
|
||||
|
||||
// get user
|
||||
usersResource.getUser().then(function (user) {
|
||||
vm.user = user;
|
||||
});
|
||||
|
||||
// fake loading
|
||||
$timeout(function () {
|
||||
vm.loading = false;
|
||||
}, 500);
|
||||
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
$location.path("users/users/overview");
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.Users.UserController", UserEditController);
|
||||
|
||||
})();
|
||||
48
src/Umbraco.Web.UI.Client/src/views/users/user.html
Normal file
48
src/Umbraco.Web.UI.Client/src/views/users/user.html
Normal file
@@ -0,0 +1,48 @@
|
||||
<div ng-controller="Umbraco.Editors.Users.UserController as vm" class="clearfix">
|
||||
|
||||
<umb-load-indicator ng-show="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>
|
||||
|
||||
<umb-editor-container>
|
||||
|
||||
<umb-editor-sub-header>
|
||||
<umb-editor-sub-header-content-left>
|
||||
<a class="umb-package-details__back-link" href="" ng-click="vm.goBack()">← Back to users</a>
|
||||
</umb-editor-sub-header-content-left>
|
||||
</umb-editor-sub-header>
|
||||
|
||||
</umb-editor-container>
|
||||
|
||||
<umb-editor-footer>
|
||||
|
||||
<umb-editor-footer-content-right>
|
||||
|
||||
<umb-button
|
||||
type="button"
|
||||
action="vm.save()"
|
||||
state="vm.page.saveButtonState"
|
||||
button-style="success"
|
||||
shortcut="ctrl+s"
|
||||
label="Save"
|
||||
label-key="buttons_save">
|
||||
</umb-button>
|
||||
|
||||
</umb-editor-footer-content-right>
|
||||
|
||||
</umb-editor-footer>
|
||||
|
||||
</umb-editor-view>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,94 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function UserRolesController($scope, $timeout, $location, usersResource) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
vm.userRoles = [];
|
||||
vm.selection = [];
|
||||
vm.viewState = 'overview';
|
||||
|
||||
vm.pagination = {
|
||||
"pageNumber": 1,
|
||||
"totalPages": 5
|
||||
};
|
||||
|
||||
vm.setViewState = setViewState;
|
||||
vm.goToUserRole = goToUserRole;
|
||||
vm.clearSelection = clearSelection;
|
||||
vm.selectUserRole = selectUserRole;
|
||||
vm.selectAll = selectAll;
|
||||
vm.areAllSelected = areAllSelected;
|
||||
|
||||
function init() {
|
||||
|
||||
vm.loading = true;
|
||||
|
||||
// Get users
|
||||
usersResource.getUserGroups().then(function (userRoles) {
|
||||
vm.userRoles = userRoles;
|
||||
});
|
||||
|
||||
// fake loading
|
||||
$timeout(function () {
|
||||
vm.loading = false;
|
||||
}, 500);
|
||||
|
||||
}
|
||||
|
||||
function setViewState(state) {
|
||||
vm.viewState = state;
|
||||
}
|
||||
|
||||
function selectUserRole(userRole, selection) {
|
||||
if(userRole.selected) {
|
||||
var index = selection.indexOf(userRole.id);
|
||||
selection.splice(index, 1);
|
||||
userRole.selected = false;
|
||||
} else {
|
||||
userRole.selected = true;
|
||||
vm.selection.push(userRole.id);
|
||||
}
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
angular.forEach(vm.userRoles, function(userRole){
|
||||
userRole.selected = false;
|
||||
});
|
||||
vm.selection = [];
|
||||
}
|
||||
|
||||
function goToUserRole(userRole) {
|
||||
$location.path('users/users/role/' + userRole.id);
|
||||
}
|
||||
function selectAll() {
|
||||
if(areAllSelected()) {
|
||||
vm.selection = [];
|
||||
angular.forEach(vm.userRoles, function(userRole){
|
||||
userRole.selected = false;
|
||||
});
|
||||
} else {
|
||||
// clear selection so we don't add the same user twice
|
||||
vm.selection = [];
|
||||
// select all users
|
||||
angular.forEach(vm.userRoles, function(userRole){
|
||||
userRole.selected = true;
|
||||
vm.selection.push(userRole.id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function areAllSelected() {
|
||||
if(vm.selection.length === vm.userRoles.length) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.Users.RolesController", UserRolesController);
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,71 @@
|
||||
<div ng-controller="Umbraco.Editors.Users.RolesController as vm" class="clearfix">
|
||||
|
||||
<umb-editor-sub-header>
|
||||
|
||||
<!-- No selection -->
|
||||
<umb-editor-sub-header-content-left ng-if="vm.selection.length === 0">
|
||||
<umb-button
|
||||
type="button"
|
||||
button-style="success"
|
||||
action="vm.createUserRole()"
|
||||
label="Create Role">
|
||||
</umb-button>
|
||||
</umb-editor-sub-header-content-left>
|
||||
|
||||
<umb-editor-sub-header-content-right ng-if="vm.selection.length === 0">
|
||||
|
||||
<umb-editor-sub-header-section>
|
||||
<ng-form class="form-search -no-margin-bottom pull-right" novalidate>
|
||||
<div class="inner-addon left-addon">
|
||||
<i class="icon icon-search" ng-click="enterSearch($event)"></i>
|
||||
<input
|
||||
class="form-control search-input"
|
||||
type="text"
|
||||
localize="placeholder"
|
||||
placeholder="@general_typeToSearch"
|
||||
ng-model="options.filter"
|
||||
ng-change="enterSearch()"
|
||||
ng-keydown="forceSearch($event)"
|
||||
prevent-enter-submit
|
||||
no-dirty-check>
|
||||
</div>
|
||||
</ng-form>
|
||||
</umb-editor-sub-header-section>
|
||||
|
||||
</umb-editor-sub-header-content-right>
|
||||
|
||||
<!-- With selection -->
|
||||
<umb-editor-sub-header-content-left ng-if="vm.selection.length > 0">
|
||||
<umb-editor-sub-header-section>
|
||||
<umb-button
|
||||
type="button"
|
||||
label="Clear selection"
|
||||
label-key="buttons_clearSelection"
|
||||
action="vm.clearSelection()"
|
||||
disabled="actionInProgress">
|
||||
</umb-button>
|
||||
</umb-editor-sub-header-section>
|
||||
<umb-editor-sub-header-section>
|
||||
<strong>{{ vm.selection.length }} <localize key="general_of">of</localize> {{ vm.userRoles.length }} <localize key="general_selected">selected</localize></strong>
|
||||
</umb-editor-sub-header-section>
|
||||
</umb-editor-sub-header-content-left>
|
||||
|
||||
<umb-editor-sub-header-content-right ng-if="vm.selection.length > 0">
|
||||
<umb-button
|
||||
type="button"
|
||||
button-style="link"
|
||||
label="Delete"
|
||||
icon="icon-trash"
|
||||
action="vm.deleteUserRole()">
|
||||
</umb-button>
|
||||
</umb-editor-sub-header-content-right>
|
||||
|
||||
</umb-editor-sub-header>
|
||||
|
||||
<div ng-repeat="userRole in vm.userRoles" class="flex items-center" ng-click="vm.selectUserRole(userRole, vm.selection)">
|
||||
<i class="{{userRole.icon}}"></i>
|
||||
<div class="bold" style="margin-right: 5px;">{{userRole.name}}</div>
|
||||
<a href="" ng-click="vm.goToUserRole(userRole)">Edit</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,268 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function UsersController($scope, $timeout, $location, usersResource) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
vm.users = [];
|
||||
vm.userGroups = [];
|
||||
vm.userStates = [];
|
||||
vm.selection = [];
|
||||
vm.newUser = {};
|
||||
vm.newUser.userGroups = [];
|
||||
vm.usersViewState = 'overview';
|
||||
|
||||
vm.allowDisableUser = true;
|
||||
vm.allowEnableUser = true;
|
||||
vm.allowSetUserRole = true;
|
||||
|
||||
vm.usersPagination = {
|
||||
"pageNumber": 1,
|
||||
"totalPages": 5
|
||||
}
|
||||
|
||||
vm.layouts = [
|
||||
{
|
||||
"icon": "icon-thumbnails-small",
|
||||
"path": "1",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"icon": "icon-list",
|
||||
"path": "2",
|
||||
"selected": true
|
||||
}
|
||||
];
|
||||
|
||||
vm.activeLayout = {
|
||||
"icon": "icon-thumbnails-small",
|
||||
"path": "1",
|
||||
"selected": true
|
||||
};
|
||||
|
||||
vm.defaultButton = {
|
||||
labelKey:"users_inviteUser",
|
||||
handler: function() {
|
||||
vm.setUsersViewState('inviteUser');
|
||||
}
|
||||
};
|
||||
|
||||
vm.subButtons = [
|
||||
{
|
||||
labelKey: "users_createUser",
|
||||
handler: function () {
|
||||
vm.setUsersViewState('createUser');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
vm.setUsersViewState = setUsersViewState;
|
||||
vm.getUserStateType = getUserStateType;
|
||||
vm.selectLayout = selectLayout;
|
||||
vm.selectUser = selectUser;
|
||||
vm.clearSelection = clearSelection;
|
||||
vm.goToUser = goToUser;
|
||||
vm.disableUser = disableUser;
|
||||
vm.openUserGroupPicker = openUserGroupPicker;
|
||||
vm.removeSelectedUserGroup = removeSelectedUserGroup;
|
||||
vm.selectAll = selectAll;
|
||||
vm.areAllSelected = areAllSelected;
|
||||
|
||||
function init() {
|
||||
|
||||
vm.loading = true;
|
||||
|
||||
// Get users
|
||||
usersResource.getUsers().then(function (users) {
|
||||
vm.users = users;
|
||||
vm.userStates = getUserStates(vm.users);
|
||||
formatDates(vm.users);
|
||||
});
|
||||
|
||||
// Get user groups
|
||||
usersResource.getUserGroups().then(function (userGroups) {
|
||||
vm.userGroups = userGroups;
|
||||
});
|
||||
|
||||
// fake loading
|
||||
$timeout(function () {
|
||||
vm.loading = false;
|
||||
}, 500);
|
||||
|
||||
}
|
||||
|
||||
function setUsersViewState(state) {
|
||||
vm.usersViewState = state;
|
||||
}
|
||||
|
||||
function getUserStateType(state) {
|
||||
switch (state) {
|
||||
case "disabled" || "umbracoDisabled":
|
||||
return "danger";
|
||||
case "pending":
|
||||
return "warning";
|
||||
default:
|
||||
return "success";
|
||||
}
|
||||
}
|
||||
|
||||
function selectLayout(selectedLayout) {
|
||||
|
||||
angular.forEach(vm.layouts, function(layout){
|
||||
layout.active = false;
|
||||
});
|
||||
|
||||
selectedLayout.active = true;
|
||||
vm.activeLayout = selectedLayout;
|
||||
}
|
||||
|
||||
function selectUser(user, selection) {
|
||||
if(user.selected) {
|
||||
var index = selection.indexOf(user.id);
|
||||
selection.splice(index, 1);
|
||||
user.selected = false;
|
||||
} else {
|
||||
user.selected = true;
|
||||
vm.selection.push(user.id);
|
||||
}
|
||||
|
||||
setBulkActions(vm.users);
|
||||
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
angular.forEach(vm.users, function(user){
|
||||
user.selected = false;
|
||||
});
|
||||
vm.selection = [];
|
||||
}
|
||||
|
||||
function goToUser(user, event) {
|
||||
$location.path('users/users/user/' + user.id);
|
||||
}
|
||||
|
||||
function disableUser() {
|
||||
alert("disable users");
|
||||
}
|
||||
|
||||
function openUserGroupPicker(event) {
|
||||
vm.userGroupPicker = {
|
||||
title: "Select user groups",
|
||||
view: "itempicker",
|
||||
event: event,
|
||||
availableItems: vm.userGroups,
|
||||
selectedItems: vm.newUser.userGroups,
|
||||
show: true,
|
||||
submit: function(model) {
|
||||
|
||||
if(model.selectedItem) {
|
||||
vm.newUser.userGroups.push(model.selectedItem);
|
||||
}
|
||||
|
||||
vm.userGroupPicker.show = false;
|
||||
vm.userGroupPicker = null;
|
||||
},
|
||||
close: function(oldModel) {
|
||||
vm.userGroupPicker.show = false;
|
||||
vm.userGroupPicker = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function removeSelectedUserGroup(index, selection) {
|
||||
selection.splice(index, 1);
|
||||
}
|
||||
|
||||
function selectAll() {
|
||||
if(areAllSelected()) {
|
||||
vm.selection = [];
|
||||
angular.forEach(vm.users, function(user){
|
||||
user.selected = false;
|
||||
});
|
||||
} else {
|
||||
// clear selection so we don't add the same user twice
|
||||
vm.selection = [];
|
||||
// select all users
|
||||
angular.forEach(vm.users, function(user){
|
||||
user.selected = true;
|
||||
vm.selection.push(user.id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function areAllSelected() {
|
||||
if(vm.selection.length === vm.users.length) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// helpers
|
||||
function getUserStates(users) {
|
||||
var userStates = [];
|
||||
|
||||
angular.forEach(users, function(user) {
|
||||
|
||||
var newUserState = {"name": user.state, "count": 1};
|
||||
var userStateExists = false;
|
||||
|
||||
angular.forEach(userStates, function(userState){
|
||||
if(newUserState.name === userState.name) {
|
||||
userState.count = userState.count + 1;
|
||||
userStateExists = true;
|
||||
}
|
||||
});
|
||||
|
||||
if(userStateExists === false) {
|
||||
userStates.push(newUserState);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return userStates;
|
||||
}
|
||||
|
||||
function formatDates(users) {
|
||||
angular.forEach(users, function(user){
|
||||
if(user.lastLogin) {
|
||||
user.formattedLastLogin = moment(user.lastLogin).format("MMMM Do YYYY, HH:mm");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setBulkActions(users) {
|
||||
|
||||
// reset all states
|
||||
vm.allowDisableUser = true;
|
||||
vm.allowEnableUser = true;
|
||||
vm.allowSetUserGroup = true;
|
||||
|
||||
angular.forEach(users, function(user){
|
||||
|
||||
if(!user.selected) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(user.state === "disabled") {
|
||||
vm.allowDisableUser = false;
|
||||
}
|
||||
|
||||
if(user.state === "active") {
|
||||
vm.allowEnableUser = false;
|
||||
}
|
||||
|
||||
if(user.state === "pending") {
|
||||
vm.allowEnableUser = false;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
init();
|
||||
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.Users.UsersController", UsersController);
|
||||
|
||||
})();
|
||||
338
src/Umbraco.Web.UI.Client/src/views/users/views/users/users.html
Normal file
338
src/Umbraco.Web.UI.Client/src/views/users/views/users/users.html
Normal file
@@ -0,0 +1,338 @@
|
||||
<div ng-controller="Umbraco.Editors.Users.UsersController as vm" class="clearfix">
|
||||
|
||||
<umb-load-indicator ng-show="vm.loading"></umb-load-indicator>
|
||||
|
||||
<!-- Users Overview -->
|
||||
<div ng-if="vm.usersViewState === 'overview'">
|
||||
|
||||
<umb-editor-sub-header>
|
||||
|
||||
<!-- No selection -->
|
||||
<umb-editor-sub-header-content-left ng-if="vm.selection.length === 0">
|
||||
<umb-button-group
|
||||
ng-if="vm.defaultButton"
|
||||
default-button="vm.defaultButton"
|
||||
sub-buttons="vm.subButtons">
|
||||
</umb-button-group>
|
||||
</umb-editor-sub-header-content-left>
|
||||
|
||||
<umb-editor-sub-header-content-right ng-if="vm.selection.length === 0">
|
||||
<umb-editor-sub-header-section>
|
||||
<umb-layout-selector
|
||||
ng-if="vm.layouts"
|
||||
layouts="vm.layouts"
|
||||
active-layout="vm.activeLayout"
|
||||
on-layout-select="vm.selectLayout">
|
||||
</umb-layout-selector>
|
||||
</umb-editor-sub-header-section>
|
||||
<umb-editor-sub-header-section>
|
||||
<ng-form class="form-search -no-margin-bottom pull-right" novalidate>
|
||||
<div class="inner-addon left-addon">
|
||||
<i class="icon icon-search" ng-click="enterSearch($event)"></i>
|
||||
<input
|
||||
class="form-control search-input"
|
||||
type="text"
|
||||
localize="placeholder"
|
||||
placeholder="@general_typeToSearch"
|
||||
ng-model="options.filter"
|
||||
ng-change="enterSearch()"
|
||||
ng-keydown="forceSearch($event)"
|
||||
prevent-enter-submit
|
||||
no-dirty-check>
|
||||
</div>
|
||||
</ng-form>
|
||||
</umb-editor-sub-header-section>
|
||||
</umb-editor-sub-header-content-right>
|
||||
|
||||
<!-- With selection -->
|
||||
<umb-editor-sub-header-content-left ng-if="vm.selection.length > 0">
|
||||
<umb-editor-sub-header-section>
|
||||
<umb-button
|
||||
type="button"
|
||||
label="Clear selection"
|
||||
label-key="buttons_clearSelection"
|
||||
action="vm.clearSelection()"
|
||||
disabled="actionInProgress">
|
||||
</umb-button>
|
||||
</umb-editor-sub-header-section>
|
||||
<umb-editor-sub-header-section>
|
||||
<strong>{{ vm.selection.length }} <localize key="general_of">of</localize> {{ vm.users.length }} <localize key="general_selected">selected</localize></strong>
|
||||
</umb-editor-sub-header-section>
|
||||
</umb-editor-sub-header-content-left>
|
||||
|
||||
<umb-editor-sub-header-content-right ng-if="vm.selection.length > 0">
|
||||
<umb-button
|
||||
ng-if="vm.allowSetUserRole"
|
||||
type="button"
|
||||
button-style="link"
|
||||
label="Set role"
|
||||
icon="icon-users"
|
||||
action="vm.setUserRoleUser()">
|
||||
</umb-button>
|
||||
<umb-button
|
||||
ng-if="vm.allowEnableUser"
|
||||
type="button"
|
||||
button-style="link"
|
||||
label="Enable"
|
||||
icon="icon-check"
|
||||
action="vm.enableUser()">
|
||||
</umb-button>
|
||||
<umb-button
|
||||
ng-if="vm.allowDisableUser"
|
||||
type="button"
|
||||
button-style="link"
|
||||
label="Disable"
|
||||
icon="icon-block"
|
||||
action="vm.disableUser()">
|
||||
</umb-button>
|
||||
</umb-editor-sub-header-content-right>
|
||||
|
||||
</umb-editor-sub-header>
|
||||
|
||||
<div style="margin-bottom: 20px;" class="flex items-center">
|
||||
|
||||
<div style="font-size: 16px;">
|
||||
<span class="bold">Users</span> <span>({{vm.users.length}})</span>
|
||||
</div>
|
||||
|
||||
<div class="flex" style="margin-left: auto;">
|
||||
|
||||
<div class="dropdown pull-right">
|
||||
<a style="text-decoration: none;" class="btn btn-link dropdown-toggle" href="" ng-click="vm.showFilter = !vm.showFilter">
|
||||
Show:
|
||||
<span class="bold" style="text-decoration: underline; margin-left: 2px;">All</span>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<ul ng-if="vm.showFilter" on-outside-click="vm.showFilter = false;" style="display: block;" class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu">
|
||||
<li style="padding: 5px 10px;">
|
||||
<div class="flex items-center">
|
||||
<input style="margin-right: 7px; margin-top: 2px;" type="checkbox" />All
|
||||
</div>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
<li ng-repeat="userGroup in vm.userGroups" style="padding: 5px 10px;">
|
||||
<div class="flex items-center">
|
||||
<input style="margin-right: 7px; margin-top: 2px;" type="checkbox" />
|
||||
{{ userGroup.name }}
|
||||
</div>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
<li ng-repeat="userState in vm.userStates" style="padding: 5px 10px;">
|
||||
<div class="flex items-center">
|
||||
<input style="margin-right: 7px; margin-top: 2px;" type="checkbox" />
|
||||
{{ userState.name }} ({{userState.count}})
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="dropdown pull-right">
|
||||
<a style="text-decoration: none;" class="btn btn-link dropdown-toggle" href="" data-toggle="dropdown">
|
||||
Order by:
|
||||
<span class="bold" style="text-decoration: underline; margin-left: 2px;">Last login</span>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu">
|
||||
<li><a tabindex="-1" href="#" prevent-default>Last login</a></li>
|
||||
<li><a tabindex="-1" href="#" prevent-default>Name</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Layout: Cards -->
|
||||
<div class="umb-users" ng-if="vm.activeLayout.path === '1'">
|
||||
<div class="umb-user" ng-repeat="user in vm.users" ng-class="{'umb-user--selected': user.selected}" ng-click="vm.selectUser(user, vm.selection)">
|
||||
<umb-badge
|
||||
class="umb-user__badge"
|
||||
size="xs"
|
||||
ng-if="user.state !== 'active'"
|
||||
color="{{vm.getUserStateType(user.state)}}">
|
||||
{{ user.state }}
|
||||
</umb-badge>
|
||||
<div class="umb-user__avatar">
|
||||
<umb-avatar
|
||||
size="l"
|
||||
color="secondary"
|
||||
name="{{user.name}}"
|
||||
img-src="{{user.avatar}}">
|
||||
</umb-avatar>
|
||||
</div>
|
||||
<div class="umb-user__checkmark" ng-if="user.selected"><umb-checkmark checked="user.selected" size="s"></umb-checkmark></div>
|
||||
<a class="umb-user__name" href="" ng-click="vm.goToUser(user)">{{user.name}}</a>
|
||||
<div class="umb-user__group">
|
||||
<span ng-repeat="group in user.userGroups">{{ group.name }}<span ng-if="!$last">, </span></span>
|
||||
</div>
|
||||
<div class="umb-user__divider"></div>
|
||||
<div class="umb-user__last-login">
|
||||
<div ng-if="user.formattedLastLogin">
|
||||
<div>Last login on</div>
|
||||
{{ user.formattedLastLogin }}
|
||||
</div>
|
||||
<div ng-if="!user.formattedLastLogin">
|
||||
<div>{{ user.name | umbWordLimit:1 }} has</div>
|
||||
not logged in yet
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Layout: Table -->
|
||||
<div ng-if="vm.activeLayout.path === '2'">
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="padding-left: 20px; width: 10px;">
|
||||
<a href="" style="text-decoration: none;" ng-click="vm.selectAll()"><umb-checkmark checked="vm.areAllSelected()" size="xs"></umb-checkmark></a>
|
||||
</th>
|
||||
<th style="width: 70px;"></th>
|
||||
<th>Name</th>
|
||||
<th>User group</th>
|
||||
<th>Last Login</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="user in vm.users" ng-click="vm.selectUser(user, vm.selection)" style="cursor: pointer;">
|
||||
<td style="padding-left: 20px;">
|
||||
<umb-checkmark
|
||||
checked="user.selected"
|
||||
size="xs">
|
||||
</umb-checkmark>
|
||||
</td>
|
||||
<td scope="row" style="padding-left: 20px;">
|
||||
<umb-avatar
|
||||
size="xs"
|
||||
color="secondary"
|
||||
name="{{user.name}}"
|
||||
img-src="{{user.avatar}}">
|
||||
</umb-avatar>
|
||||
</td>
|
||||
<td class="bold"><a href="" ng-click="vm.goToUser(user)">{{user.name}}</a></td>
|
||||
<td><span ng-repeat="group in user.userGroups">{{group.name}}<span ng-if="!$last">, </span></span></td>
|
||||
<td>{{ user.formattedLastLogin }}</td>
|
||||
<td style="text-transform: capitalize;">
|
||||
<umb-badge
|
||||
size="xs"
|
||||
ng-if="user.state !== 'active'"
|
||||
color="{{vm.getUserStateType(user.state)}}">
|
||||
{{ user.state }}
|
||||
</umb-badge>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="flex justify-center">
|
||||
<umb-pagination
|
||||
ng-if="vm.usersPagination.totalPages"
|
||||
page-number="vm.usersPagination.pageNumber"
|
||||
total-pages="vm.usersPagination.totalPages">
|
||||
</umb-pagination>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Invite user -->
|
||||
<div ng-if="vm.usersViewState === 'inviteUser' || vm.usersViewState === 'createUser'">
|
||||
|
||||
<umb-editor-sub-header>
|
||||
<umb-editor-sub-header-content-left>
|
||||
<a class="umb-package-details__back-link" href="" ng-click="vm.setUsersViewState('overview');">← Take me back</a>
|
||||
</umb-editor-sub-header-content-left>
|
||||
</umb-editor-sub-header>
|
||||
|
||||
<div class="flex justify-center">
|
||||
<ng-form style="max-width: 500px;" class="block-form">
|
||||
<div>
|
||||
<div ng-if="vm.usersViewState === 'inviteUser'">
|
||||
<h3 class="bold" style="margin-bottom: 0;">Invite user</h3>
|
||||
<p style="line-height: 1.6em; margin-bottom: 15px;">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non libero vel turpis ultrices pharetra.</p>
|
||||
</div>
|
||||
<div ng-if="vm.usersViewState === 'createUser'">
|
||||
<h3 class="bold" style="margin-bottom: 0;">Create user</h3>
|
||||
<p style="line-height: 1.6em; margin-bottom: 15px;">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non libero vel turpis ultrices pharetra.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<umb-control-group label="Name">
|
||||
<input type="text"
|
||||
localize="placeholder"
|
||||
placeholder="@placeholders_entername"
|
||||
class="input-block-level"
|
||||
ng-model="vm.newUser.name"
|
||||
umb-auto-focus />
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group label="Email">
|
||||
<input type="email"
|
||||
localize="placeholder"
|
||||
placeholder="@placeholders_entername"
|
||||
class="input-block-level"
|
||||
ng-model="vm.newUser.email" />
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group label="User group">
|
||||
|
||||
<umb-node-preview
|
||||
style="max-width: 100%;"
|
||||
ng-repeat="group in vm.newUser.userGroups"
|
||||
icon="group.icon"
|
||||
name="group.name"
|
||||
allow-remove="true"
|
||||
on-remove="vm.removeSelectedUserGroup($index, vm.newUser.userGroups)">
|
||||
</umb-node-preview>
|
||||
|
||||
<a href=""
|
||||
style="max-width: 100%;"
|
||||
class="umb-node-preview-add"
|
||||
ng-click="vm.openUserGroupPicker($event)"
|
||||
prevent-default>
|
||||
<localize key="general_add">Add</localize>
|
||||
</a>
|
||||
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group label="Message" ng-if="vm.usersViewState === 'inviteUser'">
|
||||
<textarea type="text"
|
||||
class="input-block-level"
|
||||
localize="placeholder"
|
||||
placeholder="@placeholders_entername"
|
||||
ng-model="vm.newUser.message"></textarea>
|
||||
</umb-control-group>
|
||||
|
||||
<umb-button
|
||||
ng-if="vm.usersViewState === 'inviteUser'"
|
||||
button-style="success"
|
||||
type="button"
|
||||
action=""
|
||||
label="Send invite">
|
||||
</umb-button>
|
||||
|
||||
<umb-button
|
||||
ng-if="vm.usersViewState === 'createUser'"
|
||||
button-style="success"
|
||||
type="button"
|
||||
action=""
|
||||
label="Create user">
|
||||
</umb-button>
|
||||
|
||||
</ng-form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<umb-overlay
|
||||
ng-if="vm.userGroupPicker.show"
|
||||
model="vm.userGroupPicker"
|
||||
view="vm.userGroupPicker.view"
|
||||
position="target">
|
||||
</umb-overlay>
|
||||
|
||||
</div>
|
||||
@@ -24,9 +24,10 @@
|
||||
<add application="developer" alias="xslt" title="XSLT Files" type="umbraco.loadXslt, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="5" />
|
||||
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTreeController, umbraco" silent="false" initialize="true" sortOrder="6" title="Partial View Macro Files" iconClosed="icon-folder" iconOpen="icon-folder" />
|
||||
<!--Users-->
|
||||
<add application="users" alias="users" title="Users" type="umbraco.loadUsers, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="0" />
|
||||
<add application="users" alias="userGroups" title="User Groups" type="umbraco.cms.presentation.Trees.UserGroups, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="1" />
|
||||
<add application="users" alias="UserGroupPermissions" title="User Group Permissions" type="umbraco.cms.presentation.Trees.UserGroupPermissions, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="2" />
|
||||
<add initialize="true" sortOrder="3" alias="users" application="users" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.UserTreeController, umbraco" />
|
||||
<add initialize="true" sortOrder="0" alias="users_old" application="users" title="Users (Legacy)" iconClosed=".sprTreeFolder" iconOpen=".sprTreeFolder_o" type="umbraco.loadUsers, umbraco" />
|
||||
<!--Members-->
|
||||
<add initialize="true" sortOrder="0" alias="member" application="member" title="Members" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.MemberTreeController, umbraco" />
|
||||
<add initialize="true" sortOrder="1" alias="memberTypes" application="member" title="Member Types" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.MemberTypeTreeController, umbraco" />
|
||||
@@ -40,6 +41,6 @@
|
||||
<add initialize="true" sortOrder="2" alias="datasource" application="forms" title="Datasources" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Forms.Web.Trees.DataSourceTreeController, Umbraco.Forms.Web" />
|
||||
<add initialize="true" sortOrder="0" alias="form" application="forms" title="Forms" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Forms.Web.Trees.FormTreeController, Umbraco.Forms.Web" />
|
||||
<add initialize="true" sortOrder="3" alias="prevaluesource" application="forms" title="Prevalue sources" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Forms.Web.Trees.PreValueSourceTreeController, Umbraco.Forms.Web" />
|
||||
<add initialize="true" sortOrder="3" alias="formsecurity" application="users" title="Forms Security" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Forms.Web.Trees.FormSecurityTreeController, Umbraco.Forms.Web" />
|
||||
<add initialize="true" sortOrder="3" alias="formsecurity" application="users" title="Forms Security" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Forms.Web.Trees.FormSecurityTreeController, Umbraco.Forms.Web" />
|
||||
<add initialize="true" sortOrder="2" alias="userPermissions" application="users" title="User Permissions" iconClosed=".sprTreeFolder" iconOpen=".sprTreeFolder_o" type="umbraco.cms.presentation.Trees.UserPermissions, umbraco" />
|
||||
</trees>
|
||||
@@ -1371,6 +1371,7 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="templates">Templates</key>
|
||||
<key alias="xslt">XSLT Files</key>
|
||||
<key alias="analytics">Analytics</key>
|
||||
<key alias="users">Users</key>
|
||||
</area>
|
||||
<area alias="update">
|
||||
<key alias="updateAvailable">New update ready</key>
|
||||
@@ -1426,6 +1427,10 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="yourHistory" version="7.0">Your recent history</key>
|
||||
<key alias="sessionExpires" version="7.0">Session expires in</key>
|
||||
</area>
|
||||
<area alias="users">
|
||||
<key alias="inviteUser">Invite user</key>
|
||||
<key alias="createUser">Create user</key>
|
||||
</area>
|
||||
<area alias="validation">
|
||||
<key alias="validation">Validation</key>
|
||||
<key alias="validateAsEmail">Validate as email</key>
|
||||
|
||||
@@ -1368,6 +1368,7 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="templates">Templates</key>
|
||||
<key alias="xslt">XSLT Files</key>
|
||||
<key alias="analytics">Analytics</key>
|
||||
<key alias="users">Users</key>
|
||||
</area>
|
||||
<area alias="update">
|
||||
<key alias="updateAvailable">New update ready</key>
|
||||
@@ -1423,6 +1424,10 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="yourHistory" version="7.0">Your recent history</key>
|
||||
<key alias="sessionExpires" version="7.0">Session expires in</key>
|
||||
</area>
|
||||
<area alias="users">
|
||||
<key alias="inviteUser">Invite user</key>
|
||||
<key alias="createUser">Create user</key>
|
||||
</area>
|
||||
<area alias="validation">
|
||||
<key alias="validation">Validation</key>
|
||||
<key alias="validateAsEmail">Validate as email</key>
|
||||
|
||||
@@ -23,6 +23,7 @@ using System.Text;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The API controller used for editing data types
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Web.Http;
|
||||
using AutoMapper;
|
||||
using ClientDependency.Core;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
using Constants = Umbraco.Core.Constants;
|
||||
@@ -15,7 +26,7 @@ namespace Umbraco.Web.Editors
|
||||
/// </summary>
|
||||
public UserController()
|
||||
: this(UmbracoContext.Current)
|
||||
{
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -24,7 +35,87 @@ namespace Umbraco.Web.Editors
|
||||
/// <param name="umbracoContext"></param>
|
||||
public UserController(UmbracoContext umbracoContext)
|
||||
: base(umbracoContext)
|
||||
{
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a user by Id
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
public UserDisplay GetById(int id)
|
||||
{
|
||||
var user = Services.UserService.GetUserById(id);
|
||||
if (user == null)
|
||||
{
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
return Mapper.Map<IUser, UserDisplay>(user);
|
||||
}
|
||||
|
||||
//TODO: This will probably not be UserDisplay objects since there's probably too much data in the display object for a grid
|
||||
public PagedResult<UserDisplay> GetPagedUsers(
|
||||
int id,
|
||||
int pageNumber = 1,
|
||||
int pageSize = 0,
|
||||
string orderBy = "SortOrder",
|
||||
Direction orderDirection = Direction.Ascending,
|
||||
string filter = "")
|
||||
{
|
||||
|
||||
//TODO: Make this real, for now this is mock data
|
||||
|
||||
var startId = 100 + ((pageNumber -1) * pageSize);
|
||||
var numUsers = pageSize;
|
||||
var users = new List<UserDisplay>();
|
||||
var userTypes = Services.UserService.GetAllUserTypes().ToDictionary(x => x.Alias, x => x.Name);
|
||||
var cultures = Services.TextService.GetSupportedCultures().ToDictionary(x => x.Name, x => x.DisplayName);
|
||||
for (int i = 0; i < numUsers; i++)
|
||||
{
|
||||
var display = new UserDisplay
|
||||
{
|
||||
Id = startId,
|
||||
UserType = "writer",
|
||||
AllowedSections = new[] {"content", "media"},
|
||||
AvailableUserTypes = userTypes,
|
||||
Email = "test" + startId + "@test.com",
|
||||
Name = "User " + startId,
|
||||
Culture = "en-US",
|
||||
AvailableCultures = cultures,
|
||||
Path = "-1," + startId,
|
||||
ParentId = -1,
|
||||
StartContentId = -1,
|
||||
StartMediaId = -1
|
||||
};
|
||||
users.Add(display);
|
||||
startId++;
|
||||
}
|
||||
|
||||
return new PagedResult<UserDisplay>(100, pageNumber, pageSize)
|
||||
{
|
||||
Items = users
|
||||
};
|
||||
}
|
||||
|
||||
public UserDisplay PostSaveUser(UserSave userSave)
|
||||
{
|
||||
if (userSave == null) throw new ArgumentNullException("userSave");
|
||||
|
||||
if (ModelState.IsValid == false)
|
||||
{
|
||||
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
|
||||
}
|
||||
|
||||
var intId = userSave.Id.TryConvertTo<int>();
|
||||
if (intId.Success == false)
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
|
||||
var found = Services.UserService.GetUserById(intId.Result);
|
||||
if (found == null)
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
|
||||
//TODO: More validation, password changing logic, persisting
|
||||
return Mapper.Map<IUser, UserDisplay>(found);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
/// <summary>
|
||||
/// A basic structure the represents a user
|
||||
/// A bare minimum structure that represents a user, usually attached to other objects
|
||||
/// </summary>
|
||||
[DataContract(Name = "user", Namespace = "")]
|
||||
public class UserBasic : System.IComparable
|
||||
public class UserBasic : IComparable
|
||||
{
|
||||
[DataMember(Name = "id", IsRequired = true)]
|
||||
[Required]
|
||||
@@ -19,9 +20,9 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
public string Name { get; set; }
|
||||
|
||||
|
||||
int System.IComparable.CompareTo(object obj)
|
||||
int IComparable.CompareTo(object obj)
|
||||
{
|
||||
return Name.CompareTo(((UserBasic)obj).Name);
|
||||
return String.Compare(Name, ((UserBasic)obj).Name, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,10 @@ using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents information for the current user
|
||||
/// </summary>
|
||||
[DataContract(Name = "user", Namespace = "")]
|
||||
public class UserDetail : UserBasic
|
||||
{
|
||||
|
||||
59
src/Umbraco.Web/Models/ContentEditing/UserDisplay.cs
Normal file
59
src/Umbraco.Web/Models/ContentEditing/UserDisplay.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a user that is being edited
|
||||
/// </summary>
|
||||
[DataContract(Name = "user", Namespace = "")]
|
||||
public class UserDisplay : EntityBasic, INotificationModel
|
||||
{
|
||||
public UserDisplay()
|
||||
{
|
||||
Notifications = new List<Notification>();
|
||||
}
|
||||
|
||||
[DataMember(Name = "culture", IsRequired = true)]
|
||||
public string Culture { get; set; }
|
||||
|
||||
[DataMember(Name = "email", IsRequired = true)]
|
||||
public string Email { get; set; }
|
||||
|
||||
[DataMember(Name = "userType")]
|
||||
public string UserType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the available user types (i.e. to populate a drop down)
|
||||
/// The key is the Alias the value is the Name - the Alias is what is used in the UserType property and for persistence
|
||||
/// </summary>
|
||||
[DataMember(Name = "availableUserTypes")]
|
||||
public IDictionary<string, string> AvailableUserTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the available cultures (i.e. to populate a drop down)
|
||||
/// The key is the culture stored in the database, the value is the Name
|
||||
/// </summary>
|
||||
[DataMember(Name = "availableCultures")]
|
||||
public IDictionary<string, string> AvailableCultures { get; set; }
|
||||
|
||||
[DataMember(Name = "startContentId")]
|
||||
public int StartContentId { get; set; }
|
||||
|
||||
[DataMember(Name = "startMediaId")]
|
||||
public int StartMediaId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of sections the user is allowed to view.
|
||||
/// </summary>
|
||||
[DataMember(Name = "allowedSections")]
|
||||
public IEnumerable<string> AllowedSections { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes.
|
||||
/// </summary>
|
||||
[DataMember(Name = "notifications")]
|
||||
public List<Notification> Notifications { get; private set; }
|
||||
}
|
||||
}
|
||||
51
src/Umbraco.Web/Models/ContentEditing/UserSave.cs
Normal file
51
src/Umbraco.Web/Models/ContentEditing/UserSave.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the data used to persist a user
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This will be different from the model used to display a user and we don't want to "Overpost" data back to the server,
|
||||
/// and there will most likely be different bits of data required for updating passwords which will be different from the
|
||||
/// data used to display vs save
|
||||
/// </remarks>
|
||||
[DataContract(Name = "user", Namespace = "")]
|
||||
public class UserSave : EntityBasic, IValidatableObject
|
||||
{
|
||||
//TODO: There will be more information to save along with the structure for changing passwords
|
||||
|
||||
[DataMember(Name = "locale", IsRequired = true)]
|
||||
[Required]
|
||||
public string Culture { get; set; }
|
||||
|
||||
[DataMember(Name = "email", IsRequired = true)]
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
public string Email { get; set; }
|
||||
|
||||
[DataMember(Name = "userType")]
|
||||
[Required]
|
||||
public string UserType { get; set; }
|
||||
|
||||
[DataMember(Name = "startContentId")]
|
||||
public int StartContentId { get; set; }
|
||||
|
||||
[DataMember(Name = "startMediaId")]
|
||||
public int StartMediaId { get; set; }
|
||||
|
||||
[DataMember(Name = "allowedSections")]
|
||||
public IEnumerable<string> AllowedSections { get; set; }
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
//TODO: Add other server side validation
|
||||
//if (CultureInfo.GetCultureInfo(Culture))
|
||||
// yield return new ValidationResult("The culture is invalid", new[] { "Culture" });
|
||||
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,16 @@ namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
config.CreateMap<IUser, UserDisplay>()
|
||||
.ForMember(detail => detail.Id, opt => opt.MapFrom(user => user.Id))
|
||||
.ForMember(detail => detail.UserType, opt => opt.MapFrom(user => user.UserType.Alias))
|
||||
.ForMember(detail => detail.StartContentId, opt => opt.MapFrom(user => user.StartContentId))
|
||||
.ForMember(detail => detail.StartMediaId, opt => opt.MapFrom(user => user.StartMediaId))
|
||||
.ForMember(detail => detail.Culture, opt => opt.MapFrom(user => user.GetUserCulture(applicationContext.Services.TextService)))
|
||||
.ForMember(
|
||||
detail => detail.AvailableUserTypes,
|
||||
opt => opt.MapFrom(user => applicationContext.Services.SectionService.GetSections().ToDictionary(x => x.Alias, x => x.Name)));
|
||||
|
||||
config.CreateMap<IUser, UserDetail>()
|
||||
.ForMember(detail => detail.UserId, opt => opt.MapFrom(user => GetIntId(user.Id)))
|
||||
.ForMember(detail => detail.StartContentId, opt => opt.MapFrom(user => user.StartContentId))
|
||||
|
||||
57
src/Umbraco.Web/Trees/UserTreeController.cs
Normal file
57
src/Umbraco.Web/Trees/UserTreeController.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System.Net.Http.Formatting;
|
||||
using Umbraco.Web.Models.Trees;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
using Constants = Umbraco.Core.Constants;
|
||||
|
||||
namespace Umbraco.Web.Trees
|
||||
{
|
||||
[UmbracoTreeAuthorize(Constants.Trees.Users)]
|
||||
[Tree(Constants.Applications.Users, Constants.Trees.Users, null, sortOrder: 3)]
|
||||
[PluginController("UmbracoTrees")]
|
||||
[CoreTree]
|
||||
public class UserTreeController : TreeController
|
||||
{
|
||||
public UserTreeController()
|
||||
{
|
||||
}
|
||||
|
||||
public UserTreeController(UmbracoContext umbracoContext) : base(umbracoContext)
|
||||
{
|
||||
}
|
||||
|
||||
public UserTreeController(UmbracoContext umbracoContext, UmbracoHelper umbracoHelper) : base(umbracoContext, umbracoHelper)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create a root model for a tree
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override TreeNode CreateRootNode(FormDataCollection queryStrings)
|
||||
{
|
||||
var root = base.CreateRootNode(queryStrings);
|
||||
|
||||
//this will load in a custom UI instead of the dashboard for the root node
|
||||
root.RoutePath = string.Format("{0}/{1}/{2}", Constants.Applications.Users, Constants.Trees.Users, "overview");
|
||||
root.Icon = "icon-users";
|
||||
|
||||
root.HasChildren = false;
|
||||
return root;
|
||||
}
|
||||
|
||||
protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
|
||||
{
|
||||
var baseUrl = Constants.Applications.Users + "/users/";
|
||||
|
||||
var nodes = new TreeNodeCollection();
|
||||
return nodes;
|
||||
}
|
||||
|
||||
protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings)
|
||||
{
|
||||
var menu = new MenuItemCollection();
|
||||
return menu;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -367,6 +367,8 @@
|
||||
<Compile Include="Models\ContentEditing\SimpleNotificationModel.cs" />
|
||||
<Compile Include="Models\ContentEditing\SnippetDisplay.cs" />
|
||||
<Compile Include="Models\ContentEditing\TemplateDisplay.cs" />
|
||||
<Compile Include="Models\ContentEditing\UserDisplay.cs" />
|
||||
<Compile Include="Models\ContentEditing\UserSave.cs" />
|
||||
<Compile Include="Models\LocalPackageInstallModel.cs" />
|
||||
<Compile Include="Models\Mapping\CodeFileDisplayMapper.cs" />
|
||||
<Compile Include="Models\Mapping\ContentTypeModelMapperExtensions.cs" />
|
||||
@@ -434,6 +436,7 @@
|
||||
<Compile Include="Models\Mapping\PropertyTypeGroupResolver.cs" />
|
||||
<Compile Include="Security\Identity\PreviewAuthenticationMiddleware.cs" />
|
||||
<Compile Include="SingletonHttpContextAccessor.cs" />
|
||||
<Compile Include="Trees\UserTreeController.cs" />
|
||||
<Compile Include="Trees\ContentTypeTreeController.cs" />
|
||||
<Compile Include="Trees\PackagesTreeController.cs" />
|
||||
<Compile Include="Trees\MediaTypeTreeController.cs" />
|
||||
|
||||
@@ -34,7 +34,8 @@ namespace umbraco
|
||||
/// <summary>
|
||||
/// Handles loading of all umbraco users into the users application tree
|
||||
/// </summary>
|
||||
[Tree(Constants.Applications.Users, "users", "Users")]
|
||||
[Tree(Constants.Applications.Users, "users_old", "Users (Legacy)")]
|
||||
//TODO: Remove this tree when ready
|
||||
public class loadUsers : BaseTree
|
||||
{
|
||||
public loadUsers(string application) : base(application) { }
|
||||
|
||||
Reference in New Issue
Block a user