diff --git a/domain/user/endpoint.go b/domain/user/endpoint.go index 2d966eaf..d5b877ec 100644 --- a/domain/user/endpoint.go +++ b/domain/user/endpoint.go @@ -13,6 +13,7 @@ package user import ( "database/sql" + "encoding/csv" "encoding/json" "fmt" "io/ioutil" @@ -207,7 +208,6 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) { go mailer.InviteNewUser(userModel.Email, inviter.Fullname(), url, userModel.Email, requestedPassword) h.Runtime.Log.Info(fmt.Sprintf("%s invited by %s on %s", userModel.Email, inviter.Email, ctx.AppURL)) - } else { mailer := mail.Mailer{Runtime: h.Runtime, Store: h.Store, Context: ctx} go mailer.InviteExistingUser(userModel.Email, inviter.Fullname(), ctx.GetAppURL("")) @@ -594,7 +594,7 @@ func (h *Handler) ForgotPassword(w http.ResponseWriter, r *http.Request) { // ResetPassword stores the newly chosen password for the user. func (h *Handler) ResetPassword(w http.ResponseWriter, r *http.Request) { - method := "user.ForgotUserPassword" + method := "user.ResetPassword" ctx := domain.GetRequestContext(r) ctx.Subdomain = organization.GetSubdomainFromHost(r) @@ -669,3 +669,166 @@ func (h *Handler) MatchUsers(w http.ResponseWriter, r *http.Request) { response.WriteJSON(w, u) } + +// BulkImport imports comma-delimited list of users: +// firstname, lastname, email +func (h *Handler) BulkImport(w http.ResponseWriter, r *http.Request) { + method := "user.BulkImport" + ctx := domain.GetRequestContext(r) + + if !ctx.Administrator { + response.WriteForbiddenError(w) + return + } + + defer streamutil.Close(r.Body) + body, err := ioutil.ReadAll(r.Body) + if err != nil { + response.WriteBadRequestError(w, method, "text") + h.Runtime.Log.Error(method, err) + return + } + usersList := string(body) + + cr := csv.NewReader(strings.NewReader(usersList)) + cr.TrimLeadingSpace = true + cr.FieldsPerRecord = 3 + + records, err := cr.ReadAll() + if err != nil { + response.WriteServerError(w, method, err) + h.Runtime.Log.Error(method, err) + return + } + + ctx.Transaction, err = h.Runtime.Db.Beginx() + if err != nil { + response.WriteServerError(w, method, err) + h.Runtime.Log.Error(method, err) + return + } + + inviter, err := h.Store.User.Get(ctx, ctx.UserID) + if err != nil { + response.WriteServerError(w, method, err) + h.Runtime.Log.Error(method, err) + return + } + + for _, v := range records { + userModel := user.User{} + userModel.Firstname = strings.TrimSpace(v[0]) + userModel.Lastname = strings.TrimSpace(v[1]) + userModel.Email = strings.ToLower(strings.TrimSpace(v[2])) + + if len(userModel.Email) == 0 || len(userModel.Firstname) == 0 || len(userModel.Lastname) == 0 { + h.Runtime.Log.Info(method + " missing firstname, lastname, or email") + continue + } + + userModel.Initials = stringutil.MakeInitials(userModel.Firstname, userModel.Lastname) + requestedPassword := secrets.GenerateRandomPassword() + userModel.Salt = secrets.GenerateSalt() + userModel.Password = secrets.GeneratePassword(requestedPassword, userModel.Salt) + + // only create account if not dupe + addUser := true + addAccount := true + var userID string + + userDupe, err := h.Store.User.GetByEmail(ctx, userModel.Email) + if err != nil && err != sql.ErrNoRows { + h.Runtime.Log.Error(method, err) + continue + } + + if userModel.Email == userDupe.Email { + addUser = false + userID = userDupe.RefID + h.Runtime.Log.Info("Dupe user found, will not add " + userModel.Email) + } + + if addUser { + userID = uniqueid.Generate() + userModel.RefID = userID + + err = h.Store.User.Add(ctx, userModel) + if err != nil { + ctx.Transaction.Rollback() + response.WriteServerError(w, method, err) + h.Runtime.Log.Error(method, err) + return + } + + h.Runtime.Log.Info("Adding user") + } else { + AttachUserAccounts(ctx, *h.Store, ctx.OrgID, &userDupe) + + for _, a := range userDupe.Accounts { + if a.OrgID == ctx.OrgID { + addAccount = false + h.Runtime.Log.Info("Dupe account found, will not add") + break + } + } + } + + // set up user account for the org + if addAccount { + var a account.Account + a.RefID = uniqueid.Generate() + a.UserID = userID + a.OrgID = ctx.OrgID + a.Editor = true + a.Admin = false + a.Active = true + + err = h.Store.Account.Add(ctx, a) + if err != nil { + ctx.Transaction.Rollback() + response.WriteServerError(w, method, err) + h.Runtime.Log.Error(method, err) + return + } + } + + if addUser { + event.Handler().Publish(string(event.TypeAddUser)) + h.Store.Audit.Record(ctx, audit.EventTypeUserAdd) + } + + if addAccount { + event.Handler().Publish(string(event.TypeAddAccount)) + h.Store.Audit.Record(ctx, audit.EventTypeAccountAdd) + } + + // If we did not add user or give them access (account) then we error back + if !addUser && !addAccount { + h.Runtime.Log.Info(method + " duplicate user not added") + continue + } + + // Invite new user and prepare invitation email (that contains SSO link) + if addUser && addAccount { + size := len(requestedPassword) + + auth := fmt.Sprintf("%s:%s:%s", ctx.AppURL, userModel.Email, requestedPassword[:size]) + encrypted := secrets.EncodeBase64([]byte(auth)) + + url := fmt.Sprintf("%s/%s", ctx.GetAppURL("auth/sso"), url.QueryEscape(string(encrypted))) + mailer := mail.Mailer{Runtime: h.Runtime, Store: h.Store, Context: ctx} + go mailer.InviteNewUser(userModel.Email, inviter.Fullname(), url, userModel.Email, requestedPassword) + + h.Runtime.Log.Info(fmt.Sprintf("%s invited by %s on %s", userModel.Email, inviter.Email, ctx.AppURL)) + } else { + mailer := mail.Mailer{Runtime: h.Runtime, Store: h.Store, Context: ctx} + go mailer.InviteExistingUser(userModel.Email, inviter.Fullname(), ctx.GetAppURL("")) + + h.Runtime.Log.Info(fmt.Sprintf("%s is giving access to an existing user %s", inviter.Email, userModel.Email)) + } + } + + ctx.Transaction.Commit() + + response.WriteEmpty(w) +} diff --git a/gui/app/components/customize/user-admin.js b/gui/app/components/customize/user-admin.js index e1fcd8d4..2017a60c 100644 --- a/gui/app/components/customize/user-admin.js +++ b/gui/app/components/customize/user-admin.js @@ -10,180 +10,64 @@ // https://documize.com import $ from 'jquery'; -import Component from '@ember/component'; -import { schedule, debounce } from '@ember/runloop'; import AuthProvider from '../../mixins/auth'; import ModalMixin from '../../mixins/modal'; +import Component from '@ember/component'; export default Component.extend(AuthProvider, ModalMixin, { - editUser: null, - deleteUser: null, - filter: '', - hasSelectedUsers: false, - showDeleteDialog: false, + bulkUsers: '', + newUser: null, init() { this._super(...arguments); - this.password = {}; - this.filteredUsers = []; - this.selectedUsers = []; + this.set('newUser', { firstname: '', lastname: '', email: '', active: true }); }, - - didReceiveAttrs() { - this._super(...arguments); - - let users = this.get('users'); - - users.forEach(user => { - user.set('me', user.get('id') === this.get('session.session.authenticated.user.id')); - user.set('selected', false); - }); - - this.set('users', users); - this.set('filteredUsers', users); - }, - - onKeywordChange: function () { - debounce(this, this.filterUsers, 350); - }.observes('filter'), - - filterUsers() { - let users = this.get('users'); - let filteredUsers = []; - let filter = this.get('filter').toLowerCase(); - - users.forEach(user => { - if (user.get('fullname').toLowerCase().includes(filter) || user.get('email').toLowerCase().includes(filter)) { - filteredUsers.pushObject(user); - } - }); - - this.set('filteredUsers', filteredUsers); - }, - + actions: { - toggleSelect(user) { - user.set('selected', !user.get('selected')); - - let su = this.get('selectedUsers'); - if (user.get('selected')) { - su.push(user.get('id')); - } else { - su = _.reject(su, function(id){ return id === user.get('id') }); - } - - this.set('selectedUsers', su); - this.set('hasSelectedUsers', su.length > 0); + onOpenUserModal() { + this.modalOpen("#add-user-modal", {"show": true}, '#newUserFirstname'); }, - toggleActive(id) { - let user = this.users.findBy("id", id); - user.set('active', !user.get('active')); - let cb = this.get('onSave'); - cb(user); - }, - - toggleEditor(id) { - let user = this.users.findBy("id", id); - user.set('editor', !user.get('editor')); - let cb = this.get('onSave'); - cb(user); - }, - - toggleAdmin(id) { - let user = this.users.findBy("id", id); - user.set('admin', !user.get('admin')); - let cb = this.get('onSave'); - cb(user); - }, - - toggleUsers(id) { - let user = this.users.findBy("id", id); - user.set('viewUsers', !user.get('viewUsers')); - let cb = this.get('onSave'); - cb(user); - }, - - onShowEdit(id) { - let user = this.users.findBy("id", id); - let userCopy = user.getProperties('id', 'created', 'revised', 'firstname', 'lastname', 'email', 'initials', 'active', 'editor', 'admin', 'viewUsers', 'accounts'); - - this.set('editUser', userCopy); - this.set('password', { - password: "", - confirmation: "" - }); - - $('#edit-user-modal').on('show.bs.modal', function(event) { // eslint-disable-line no-unused-vars - schedule('afterRender', () => { - $("#edit-firstname").focus(); - }); - }); - - $('#edit-user-modal').modal('dispose'); - $('#edit-user-modal').modal({show: true}); - }, - - onUpdate() { - let user = this.get('editUser'); - let password = this.get('password'); - - if (is.empty(user.firstname)) { - $("#edit-firstname").addClass("is-invalid").focus(); + onAddUser() { + if (is.empty(this.get('newUser.firstname'))) { + $("#newUserFirstname").addClass('is-invalid').focus(); return; } - if (is.empty(user.lastname)) { - $("#edit-lastname").addClass("is-invalid").focus(); + $("#newUserFirstname").removeClass('is-invalid'); + + if (is.empty(this.get('newUser.lastname'))) { + $("#newUserLastname").addClass('is-invalid').focus(); return; } - if (is.empty(user.email) || is.not.email(user.email)) { - $("#edit-email").addClass("is-invalid").focus(); + $("#newUserLastname").removeClass('is-invalid'); + + if (is.empty(this.get('newUser.email')) || is.not.email(this.get('newUser.email'))) { + $("#newUserEmail").addClass('is-invalid').focus(); return; } + $("#newUserEmail").removeClass('is-invalid'); - $('#edit-user-modal').modal('hide'); - $('#edit-user-modal').modal('dispose'); + let user = this.get('newUser'); - let cb = this.get('onSave'); - cb(user); - - if (is.not.empty(password.password) && is.not.empty(password.confirmation) && - password.password === password.confirmation) { - - let pw = this.get('onPassword'); - pw(user, password.password); - } - }, - - onShowDelete(id) { - this.set('deleteUser', this.users.findBy("id", id)); - this.set('showDeleteDialog', true); - }, - - onDelete() { - this.set('showDeleteDialog', false); - - this.set('selectedUsers', []); - this.set('hasSelectedUsers', false); - - let cb = this.get('onDelete'); - cb(this.get('deleteUser.id')); - - return true; - }, - - onBulkDelete() { - let su = this.get('selectedUsers'); - - su.forEach(userId => { - let cb = this.get('onDelete'); - cb(userId); + this.get('onAddUser')(user).then(() => { + this.set('newUser', { firstname: '', lastname: '', email: '', active: true }); }); - this.set('selectedUsers', []); - this.set('hasSelectedUsers', false); + this.modalClose("#add-user-modal"); + }, - this.modalClose('#admin-user-delete-modal'); + onAddUsers() { + if (is.empty(this.get('bulkUsers'))) { + $("#bulkUsers").addClass('is-invalid').focus(); + return; + } + $("#bulkUsers").removeClass('is-invalid'); + + this.get('onAddUsers')(this.get('bulkUsers')).then(() => { + this.set('bulkUsers', ''); + }); + + this.modalClose("#add-user-modal"); } } }); diff --git a/gui/app/components/customize/user-list.js b/gui/app/components/customize/user-list.js new file mode 100644 index 00000000..e1fcd8d4 --- /dev/null +++ b/gui/app/components/customize/user-list.js @@ -0,0 +1,189 @@ +// Copyright 2016 Documize Inc. . All rights reserved. +// +// This software (Documize Community Edition) is licensed under +// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html +// +// You can operate outside the AGPL restrictions by purchasing +// Documize Enterprise Edition and obtaining a commercial license +// by contacting . +// +// https://documize.com + +import $ from 'jquery'; +import Component from '@ember/component'; +import { schedule, debounce } from '@ember/runloop'; +import AuthProvider from '../../mixins/auth'; +import ModalMixin from '../../mixins/modal'; + +export default Component.extend(AuthProvider, ModalMixin, { + editUser: null, + deleteUser: null, + filter: '', + hasSelectedUsers: false, + showDeleteDialog: false, + + init() { + this._super(...arguments); + this.password = {}; + this.filteredUsers = []; + this.selectedUsers = []; + }, + + didReceiveAttrs() { + this._super(...arguments); + + let users = this.get('users'); + + users.forEach(user => { + user.set('me', user.get('id') === this.get('session.session.authenticated.user.id')); + user.set('selected', false); + }); + + this.set('users', users); + this.set('filteredUsers', users); + }, + + onKeywordChange: function () { + debounce(this, this.filterUsers, 350); + }.observes('filter'), + + filterUsers() { + let users = this.get('users'); + let filteredUsers = []; + let filter = this.get('filter').toLowerCase(); + + users.forEach(user => { + if (user.get('fullname').toLowerCase().includes(filter) || user.get('email').toLowerCase().includes(filter)) { + filteredUsers.pushObject(user); + } + }); + + this.set('filteredUsers', filteredUsers); + }, + + actions: { + toggleSelect(user) { + user.set('selected', !user.get('selected')); + + let su = this.get('selectedUsers'); + if (user.get('selected')) { + su.push(user.get('id')); + } else { + su = _.reject(su, function(id){ return id === user.get('id') }); + } + + this.set('selectedUsers', su); + this.set('hasSelectedUsers', su.length > 0); + }, + + toggleActive(id) { + let user = this.users.findBy("id", id); + user.set('active', !user.get('active')); + let cb = this.get('onSave'); + cb(user); + }, + + toggleEditor(id) { + let user = this.users.findBy("id", id); + user.set('editor', !user.get('editor')); + let cb = this.get('onSave'); + cb(user); + }, + + toggleAdmin(id) { + let user = this.users.findBy("id", id); + user.set('admin', !user.get('admin')); + let cb = this.get('onSave'); + cb(user); + }, + + toggleUsers(id) { + let user = this.users.findBy("id", id); + user.set('viewUsers', !user.get('viewUsers')); + let cb = this.get('onSave'); + cb(user); + }, + + onShowEdit(id) { + let user = this.users.findBy("id", id); + let userCopy = user.getProperties('id', 'created', 'revised', 'firstname', 'lastname', 'email', 'initials', 'active', 'editor', 'admin', 'viewUsers', 'accounts'); + + this.set('editUser', userCopy); + this.set('password', { + password: "", + confirmation: "" + }); + + $('#edit-user-modal').on('show.bs.modal', function(event) { // eslint-disable-line no-unused-vars + schedule('afterRender', () => { + $("#edit-firstname").focus(); + }); + }); + + $('#edit-user-modal').modal('dispose'); + $('#edit-user-modal').modal({show: true}); + }, + + onUpdate() { + let user = this.get('editUser'); + let password = this.get('password'); + + if (is.empty(user.firstname)) { + $("#edit-firstname").addClass("is-invalid").focus(); + return; + } + if (is.empty(user.lastname)) { + $("#edit-lastname").addClass("is-invalid").focus(); + return; + } + if (is.empty(user.email) || is.not.email(user.email)) { + $("#edit-email").addClass("is-invalid").focus(); + return; + } + + $('#edit-user-modal').modal('hide'); + $('#edit-user-modal').modal('dispose'); + + let cb = this.get('onSave'); + cb(user); + + if (is.not.empty(password.password) && is.not.empty(password.confirmation) && + password.password === password.confirmation) { + + let pw = this.get('onPassword'); + pw(user, password.password); + } + }, + + onShowDelete(id) { + this.set('deleteUser', this.users.findBy("id", id)); + this.set('showDeleteDialog', true); + }, + + onDelete() { + this.set('showDeleteDialog', false); + + this.set('selectedUsers', []); + this.set('hasSelectedUsers', false); + + let cb = this.get('onDelete'); + cb(this.get('deleteUser.id')); + + return true; + }, + + onBulkDelete() { + let su = this.get('selectedUsers'); + + su.forEach(userId => { + let cb = this.get('onDelete'); + cb(userId); + }); + + this.set('selectedUsers', []); + this.set('hasSelectedUsers', false); + + this.modalClose('#admin-user-delete-modal'); + } + } +}); diff --git a/gui/app/components/customize/user-settings.js b/gui/app/components/customize/user-settings.js deleted file mode 100644 index ff0de148..00000000 --- a/gui/app/components/customize/user-settings.js +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2016 Documize Inc. . All rights reserved. -// -// This software (Documize Community Edition) is licensed under -// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html -// -// You can operate outside the AGPL restrictions by purchasing -// Documize Enterprise Edition and obtaining a commercial license -// by contacting . -// -// https://documize.com - -import $ from 'jquery'; -import { empty, and } from '@ember/object/computed'; -import Component from '@ember/component'; -import { isEmpty } from '@ember/utils'; -import { get, set } from '@ember/object'; -import AuthProvider from '../../mixins/auth'; - -export default Component.extend(AuthProvider, { - firstnameEmpty: empty('newUser.firstname'), - lastnameEmpty: empty('newUser.lastname'), - emailEmpty: empty('newUser.email'), - hasFirstnameEmptyError: and('firstnameEmpty', 'firstnameError'), - hasLastnameEmptyError: and('lastnameEmpty', 'lastnameError'), - hasEmailEmptyError: and('emailEmpty', 'emailError'), - - init() { - this._super(...arguments); - this.newUser = { firstname: "", lastname: "", email: "", active: true }; - }, - - actions: { - add() { - if (isEmpty(this.get('newUser.firstname'))) { - set(this, 'firstnameError', true); - return $("#newUserFirstname").focus(); - } - if (isEmpty(this.get('newUser.lastname'))) { - set(this, 'lastnameError', true); - return $("#newUserLastname").focus(); - } - if (isEmpty(this.get('newUser.email')) || is.not.email(this.get('newUser.email'))) { - set(this, 'emailError', true); - return $("#newUserEmail").focus(); - } - - let user = get(this, 'newUser'); - - get(this, 'add')(user).then(() => { - this.set('newUser', { firstname: "", lastname: "", email: "", active: true }); - set(this, 'firstnameError', false); - set(this, 'lastnameError', false); - set(this, 'emailError', false); - $("#newUserFirstname").focus(); - }); - } - } -}); diff --git a/gui/app/pods/customize/users/controller.js b/gui/app/pods/customize/users/controller.js index 7c81f6c6..f599f3b1 100644 --- a/gui/app/pods/customize/users/controller.js +++ b/gui/app/pods/customize/users/controller.js @@ -9,48 +9,41 @@ // // https://documize.com -import { set } from '@ember/object'; import { inject as service } from '@ember/service'; import Controller from '@ember/controller'; export default Controller.extend({ userService: service('user'), - init() { - this._super(...arguments); - this.newUser = { firstname: "", lastname: "", email: "", active: true }; + loadUsers() { + this.get('userService').getComplete().then((users) => { + this.set('model', users); + }); }, actions: { - add(user) { - set(this, 'newUser', user); - - return this.get('userService') - .add(this.get('newUser')) - .then((user) => { - this.get('model').pushObject(user); - }) - .catch(function (error) { - let msg = error.status === 409 ? 'Unable to add duplicate user' : 'Unable to add user'; - this.showNotification(msg); - }); + onAddUser(user) { + return this.get('userService').add(user).then((user) => { + this.get('model').pushObject(user); + }); + }, + + onAddUsers(list) { + return this.get('userService').addBulk(list).then(() => { + this.loadUsers(); + }); }, onDelete(userId) { - let self = this; - this.get('userService').remove(userId).then(function () { - self.get('userService').getComplete().then(function (users) { - self.set('model', users); - }); + this.get('userService').remove(userId).then( () => { + this.loadUsers(); }); }, onSave(user) { - let self = this; - this.get('userService').save(user).then(function () { - - self.get('userService').getComplete().then(function (users) { - self.set('model', users); + this.get('userService').save(user).then(() => { + this.get('userService').getComplete().then((users) => { + this.set('model', users); }); }); }, diff --git a/gui/app/pods/customize/users/template.hbs b/gui/app/pods/customize/users/template.hbs index f5ff22c4..875ef778 100644 --- a/gui/app/pods/customize/users/template.hbs +++ b/gui/app/pods/customize/users/template.hbs @@ -1,12 +1,8 @@ -
-
-
-

Users

-

Set basic information, passwords and permissions for {{model.length}} users

-
-
-
+{{customize/user-admin + onAddUser=(action 'onAddUser') + onAddUsers=(action 'onAddUsers')}} -{{customize/user-settings add=(action 'add')}} - -{{customize/user-admin users=model onDelete=(action "onDelete") onSave=(action "onSave") onPassword=(action "onPassword")}} +{{customize/user-list users=model + onDelete=(action "onDelete") + onSave=(action "onSave") + onPassword=(action "onPassword")}} diff --git a/gui/app/services/user.js b/gui/app/services/user.js index ef300023..e3283bf1 100644 --- a/gui/app/services/user.js +++ b/gui/app/services/user.js @@ -31,6 +31,16 @@ export default Service.extend({ }); }, + // Adds comma-delim list of users (firstname, lastname, email). + addBulk(list) { + return this.get('ajax').request(`users/import`, { + type: 'POST', + data: list, + contentType: 'text' + }).then(() => { + }); + }, + // Returns user model for specified user id. getUser(userId) { let url = `users/${userId}`; diff --git a/gui/app/templates/components/customize/user-admin.hbs b/gui/app/templates/components/customize/user-admin.hbs index 2fb2022e..a8cb4078 100644 --- a/gui/app/templates/components/customize/user-admin.hbs +++ b/gui/app/templates/components/customize/user-admin.hbs @@ -1,153 +1,56 @@ -
-

Users

- - - - - - - - - - - - - {{#each filteredUsers key="id" as |user|}} - - - - - - - - - {{/each}} - -
{{input type="text" class="form-control" placeholder="filter users" value=filter}}Add SpaceView UsersAdminActive - {{#if hasSelectedUsers}} - +
+
+
+

Users

+

Set basic information, passwords and permissions for {{model.length}} users

-
-
- {{#if user.me}} - check_box_outline_blank - {{else if user.selected}} - check_box - {{else}} - check_box_outline_blank - {{/if}} -
-
-
{{ user.fullname }}
- -
-
- {{#if user.editor}} - check_box - {{else}} - check_box_outline_blank - {{/if}} - - {{#if user.viewUsers}} - check_box - {{else}} - check_box_outline_blank - {{/if}} - - {{#if user.me}} - check_box - {{else if user.admin}} - check_box - {{else}} - check_box_outline_blank - {{/if}} - - {{#if user.me}} - check_box - {{else if user.active}} - check_box - {{else}} - check_box_outline_blank - {{/if}} - - {{#if user.me}} -
- edit -
- {{else}} -
- edit -
-
-
- delete -
- {{/if}} -
-
+ + + {{/if}} - - -{{#ui/ui-dialog title="Delete User" confirmCaption="Delete" buttonType="btn-danger" show=showDeleteDialog onAction=(action 'onDelete')}} -

Are you sure you want to delete {{deleteUser.fullname}}?

-{{/ui/ui-dialog}} - diff --git a/gui/app/templates/components/customize/user-list.hbs b/gui/app/templates/components/customize/user-list.hbs new file mode 100644 index 00000000..2fb2022e --- /dev/null +++ b/gui/app/templates/components/customize/user-list.hbs @@ -0,0 +1,153 @@ +
+

Users

+ + + + + + + + + + + + + {{#each filteredUsers key="id" as |user|}} + + + + + + + + + {{/each}} + +
{{input type="text" class="form-control" placeholder="filter users" value=filter}}Add SpaceView UsersAdminActive + {{#if hasSelectedUsers}} + + + + {{/if}} +
+
+ {{#if user.me}} + check_box_outline_blank + {{else if user.selected}} + check_box + {{else}} + check_box_outline_blank + {{/if}} +
+
+
{{ user.fullname }}
+ +
+
+ {{#if user.editor}} + check_box + {{else}} + check_box_outline_blank + {{/if}} + + {{#if user.viewUsers}} + check_box + {{else}} + check_box_outline_blank + {{/if}} + + {{#if user.me}} + check_box + {{else if user.admin}} + check_box + {{else}} + check_box_outline_blank + {{/if}} + + {{#if user.me}} + check_box + {{else if user.active}} + check_box + {{else}} + check_box_outline_blank + {{/if}} + + {{#if user.me}} +
+ edit +
+ {{else}} +
+ edit +
+
+
+ delete +
+ {{/if}} +
+
+ + + +{{#ui/ui-dialog title="Delete User" confirmCaption="Delete" buttonType="btn-danger" show=showDeleteDialog onAction=(action 'onDelete')}} +

Are you sure you want to delete {{deleteUser.fullname}}?

+{{/ui/ui-dialog}} + diff --git a/gui/app/templates/components/customize/user-settings.hbs b/gui/app/templates/components/customize/user-settings.hbs deleted file mode 100644 index 8b20a6db..00000000 --- a/gui/app/templates/components/customize/user-settings.hbs +++ /dev/null @@ -1,17 +0,0 @@ -{{#if isAuthProviderDocumize}} -
-

Add User

-
- - {{focus-input id="newUserFirstname" type="text" value=newUser.firstname placeholder="Firstname" class=(if hasFirstnameEmptyError 'form-control mb-2 mr-sm-4 mb-sm-0 is-invalid' 'form-control mb-2 mr-sm-4 mb-sm-0')}} - - - {{input id="newUserLastname" type="text" value=newUser.lastname placeholder="Lastname" class=(if hasLastnameEmptyError 'form-control mb-2 mr-sm-4 mb-sm-0 is-invalid' 'form-control mb-2 mr-sm-4 mb-sm-0')}} - - - {{input id="newEmail" type="email" value=newUser.email placeholder="Email" class=(if hasEmailEmptyError 'form-control mb-2 mr-sm-4 mb-sm-0 is-invalid' 'form-control mb-2 mr-sm-4 mb-sm-0')}} - - -
-
-{{/if}} \ No newline at end of file diff --git a/server/routing/routes.go b/server/routing/routes.go index b4cfdd0f..d6e7ef96 100644 --- a/server/routing/routes.go +++ b/server/routing/routes.go @@ -151,6 +151,7 @@ func RegisterEndpoints(rt *env.Runtime, s *domain.Store) { Add(rt, RoutePrefixPrivate, "users/{userID}", []string{"DELETE", "OPTIONS"}, nil, user.Delete) Add(rt, RoutePrefixPrivate, "users/sync", []string{"GET", "OPTIONS"}, nil, keycloak.Sync) Add(rt, RoutePrefixPrivate, "users/match", []string{"POST", "OPTIONS"}, nil, user.MatchUsers) + Add(rt, RoutePrefixPrivate, "users/import", []string{"POST", "OPTIONS"}, nil, user.BulkImport) Add(rt, RoutePrefixPrivate, "search", []string{"POST", "OPTIONS"}, nil, document.SearchDocuments)