This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository pollen. See https://gitlab.nuiton.org/chorem/pollen.git commit 85886fb91608659cd362b2991d1d531fe5479a3e Author: Sylvain Bavencoff <bavencoff@codelutin.com> Date: Mon Mar 27 18:02:29 2017 +0200 écrans d'administration des sondages et des utilisateurs --- .../pollen/services/service/PollenUserService.java | 18 +- pollen-ui-riot-js/src/main/web/i18n.json | 56 ++-- pollen-ui-riot-js/src/main/web/js/UserService.js | 4 + pollen-ui-riot-js/src/main/web/tag/Pollen.tag.html | 10 +- .../src/main/web/tag/PollenHeader.tag.html | 5 + .../src/main/web/tag/UserCard.tag.html | 168 +++++++++++ pollen-ui-riot-js/src/main/web/tag/Users.tag.html | 336 ++++----------------- 7 files changed, 277 insertions(+), 320 deletions(-) diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/PollenUserService.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/PollenUserService.java index cdb6fde..861a09d 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/service/PollenUserService.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/PollenUserService.java @@ -353,22 +353,8 @@ public class PollenUserService extends PollenServiceSupport implements PollenSer } - boolean passwordNotBlank = checkNotBlank(errors, "password", user.getPassword(), l(getLocale(), "pollen.error.user.passwordEmpty")); - - if (userExists && passwordNotBlank) { - - // check current password - - try { - - getSecurityService().checkUserPassword(persisted, user.getPassword()); - - } catch (PollenInvalidPasswordException e) { - - check(errors, "password", false, l(getLocale(), "pollen.error.user.passwordInvalid")); - - } - + if (!userExists) { + checkNotBlank(errors, "password", user.getPassword(), l(getLocale(), "pollen.error.user.passwordEmpty")); } return errors; diff --git a/pollen-ui-riot-js/src/main/web/i18n.json b/pollen-ui-riot-js/src/main/web/i18n.json index 5f55e30..ad31bbc 100644 --- a/pollen-ui-riot-js/src/main/web/i18n.json +++ b/pollen-ui-riot-js/src/main/web/i18n.json @@ -235,21 +235,23 @@ "poll_created_vote": "Pour voter, veuillez utiliser le lien suivant", "poll_created_editUrl": "Gérer le sondage", "poll_created_voteUrl": "Voter", + "users_title": "Les utilisateurs", + "users_sort": "Trier par", "users_name": "Nom", "users_email": "Courriel", - "users_administrator": "Administrateur", - "users_status": "Statut", - "users_title": "Les utilisateurs", - "users_actions": "Actions", - "users_makeAdmin": "Rendre administrateur", - "users_unmakeAdmin": "Ne plus être administrateur", - "users_banUser": "Désactiver l'utilisateur", - "users_deleteUser": "Supprimer l'utilisateur", - "users_deleteUserMessage": "Supprimer l'utilisateur ?", - "users_CREATED": "En cours d'utilisation", - "users_VALIDATION": "En cours de validation", - "users_DISABLED": "Désactivé", - "users_BANNED": "Banni", + "users_noUser": "Aucun utilisateur", + "user_banned": "Banni", + "user_emailValidate": "En cours de validation", + "user_administrator": "Administrateur", + "user_edit": "Modifier l'utilisateur", + "user_banUser": "Désactiver l'utilisateur", + "user_banUserMessage": "Désactiver l'utilisateur ?", + "user_deleteUser": "Supprimer l'utilisateur", + "user_deleteUserMessage": "Supprimer l'utilisateur ?", + "user_name": "Nom", + "user_email": "Courriel", + "user_cancel": "Annuler", + "user_save": "Enregistrer", "choice_description_placeholder": "Vous pouvez saisir une description", "date-picker_today": "Aujourd'hui", "choice_text": "Renseignez un text", @@ -487,21 +489,23 @@ "poll_created_vote": "To vote on poll, please use following link", "poll_created_editUrl": "Manage poll", "poll_created_voteUrl": "Vote on poll", + "users_title": "All users", + "users_sort": "Sort by", "users_name": "Name", "users_email": "Email", - "users_administrator": "Administrator", - "users_status": "Status", - "users_title": "All users", - "users_actions": "Actions", - "users_makeAdmin": "Make administrator", - "users_unmakeAdmin": "Remove administrator", - "users_banUser": "Ban user", - "users_deleteUser": "Delete user", - "users_deleteUserMessage": "Delete user?", - "users_CREATED": "Account in use", - "users_VALIDATION": "Account validation", - "users_DISABLED": "Account disabled", - "users_BANNED": "Account banned", + "users_noUser": "No user", + "user_banned": "banned", + "user_emailValidate": "email validation waiting", + "user_administrator": "Administrator", + "user_edit": "Edit user", + "user_banUser": "Ban user", + "user_banUserMessage": "Ban user ?", + "user_deleteUser": "Delete user", + "user_deleteUserMessage": "Delete user ?", + "user_name": "Name", + "user_email": "Email", + "user_cancel": "Cancel", + "user_save": "Save", "choice_description_placeholder": "You can enter a description", "date-picker_today": "Today", "choice_text": "Set a text", diff --git a/pollen-ui-riot-js/src/main/web/js/UserService.js b/pollen-ui-riot-js/src/main/web/js/UserService.js index 4fdf53d..a3b0f20 100644 --- a/pollen-ui-riot-js/src/main/web/js/UserService.js +++ b/pollen-ui-riot-js/src/main/web/js/UserService.js @@ -55,6 +55,10 @@ class UserService extends FetchService { return this.doDelete(url); } + saveUser(user) { + return this.form("/v1/users/edit", {user: user}); + } + } module.exports = singleton(UserService); diff --git a/pollen-ui-riot-js/src/main/web/tag/Pollen.tag.html b/pollen-ui-riot-js/src/main/web/tag/Pollen.tag.html index 5df1484..14a6de4 100644 --- a/pollen-ui-riot-js/src/main/web/tag/Pollen.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/Pollen.tag.html @@ -87,19 +87,17 @@ require("./Users.tag.html"); }); route("/poll", () => { - if (!session.isConnected()) { + if (!session.isConnected() && !session.getUser.administrator) { route("/signin?url=/poll"); } else { - riot.mount(this.refs.content, "polls", {method: "polls", session: session}); } }); route("/user", () => { - if (!session.isConnected()) { + if (!session.isConnected() && !session.getUser.administrator) { route("/signin?url=/user"); } else { - riot.mount(this.refs.content, "users", {session: session}); } }); @@ -159,5 +157,9 @@ require("./Users.tag.html"); riot.mount(this.refs.content, "home"); }); + this.bus.on("unauthorize", () => { + route("signin?url=" + window.location.hash.substring(1)); + }); + </script> </Pollen> diff --git a/pollen-ui-riot-js/src/main/web/tag/PollenHeader.tag.html b/pollen-ui-riot-js/src/main/web/tag/PollenHeader.tag.html index 9d1f942..8f33ccb 100644 --- a/pollen-ui-riot-js/src/main/web/tag/PollenHeader.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/PollenHeader.tag.html @@ -57,6 +57,11 @@ require("./HeaderI18n.tag.html"); <a href="#user/favoriteLists">{__.myFavoriteLists}</a> <span role="separator" class="divider"></span> <a onclick="{signOut}">{__.signout}</a> + <virtual if="{user.administrator}"> + <span role="separator" class="divider"></span> + <a href="#user">{__.users}</a> + <a href="#poll">{__.polls}</a> + </virtual> </virtual> </div> </div> diff --git a/pollen-ui-riot-js/src/main/web/tag/UserCard.tag.html b/pollen-ui-riot-js/src/main/web/tag/UserCard.tag.html new file mode 100644 index 0000000..6931865 --- /dev/null +++ b/pollen-ui-riot-js/src/main/web/tag/UserCard.tag.html @@ -0,0 +1,168 @@ +<UserCard> + <div class="user-card-view" if={!editing}> + <div class="login"> + <span> + <i class="fa fa-ban" if={user.isBanned} title={__.banned}></i> + <i class="fa fa-cog" if={user.administrator} title={__.administrator}></i> + {opts.user.name} - {opts.user.email} + <i class="fa fa-refresh" if={!user.emailIsValidate} title={__.emailValidate}></i> + </span> + </div> + + <div class="flags"> + + </div> + + <div class="actions"> + <i class="fa fa-pencil-square-o" + title={__.edit} + onclick={startEdition}></i> + <i class="fa fa-ban" + if={!user.isBanned} + title={__.banUser} + onclick={banUser}></i> + <i class="fa fa-times" + title={__.deleteUser} + onclick={deleteUser}></i> + </div> + + </div> + + <form class="user-card-edit" if={editing} onsubmit={saveUser}> + <div class="o-form-element"> + <label class="c-label" for="name">{__.name}</label> + <input type="text" + id="name" + ref="name" + class="c-field" + value={opts.user.name}> + </div> + <div class="o-form-element"> + <label class="c-label" for="name">{__.email}</label> + <input type="email" + id="email" + ref="email" + required + class="c-field {c-field--error: errors.email}" + value={opts.user.email}> + <div class="c-hint c-hint--static c-hint--error" each={error in errors.email}>{error}</div> + </div> + <div class="o-form-element"> + <label class="c-toggle c-toggle--info"> + {__.administrator} + <input type="checkbox" + id="administrator" + ref="administrator" + checked={opts.user.administrator}> + <div class="c-toggle__track"> + <div class="c-toggle__handle"></div> + </div> + </label> + </div> + <div class="actions"> + <div class="actions-left"> + <button type="button" + class="c-button c-button--ghost-info" + onclick={cancelEdition}> + <i class="fa fa-undo" aria-hidden="true"></i> + {__.cancel} + </button> + </div> + <div class="actions-right"> + <button type="submit" + class="c-button c-button--info"> + <i class="fa fa-check" aria-hidden="true"></i> + {__.save} + </button> + </div> + </div> + + </form> + + <script type="es6"> + let session = require("../js/Session"); + this.installBundle(session, "user"); + let userService = require("../js/UserService"); + this.editing = false; + this.errors = {}; + + this.startEdition = () => { + this.editing = true; + }; + + this.cancelEdition = () => { + this.editing = false; + }; + + this.banUser = () => { + if (confirm(this.__.banUserMessage)) { + userService.banUser(this.opts.user.id).then(() => { + this.opts.onUserChange(); + }); + } + }; + + this.deleteUser = () => { + if (confirm(this.__.deleteUserMessage)) { + userService.deleteUser(this.opts.user.id).then(() => { + this.opts.onUserChange(); + }); + } + }; + + this.saveUser = e => { + e.preventDefault(); + e.stopPropagation(); + this.user.name = this.refs.name.value; + this.user.email = this.refs.email.value; + this.user.administrator = this.refs.administrator.checked; + + userService.saveUser(this.user).then(() => { + this.errors = {}; + this.cancelEdition(); + this.opts.onUserChange(); + }, errors => { + this.errors = errors; + this.update(); + }); + }; + </script> + + <style> + + usercard { + display: block; + margin: 5px auto; + border-radius: 4px; + border: 1px solid #13a2ff; + box-shadow: 0 0 1px hsla(0,0%,7%,.6); + padding: 0.5em; + color: #13a2ff; + } + + .user-card-view { + display: flex; + justify-content: space-between; + } + + .user-card-edit .o-form-element .c-label:first-child { + width: 25%; + display: inline-block; + text-align: right; + float: left; + padding-top: 0.5em; + padding-right: 5px; + } + + .user-card-edit .o-form-element .c-field { + width: 75%; + display: inline-block; + } + + .user-card-edit .o-form-element .c-toggle, + .user-card-edit .o-form-element .c-hint { + margin-left: 25%; + } + + </style> +</UserCard> diff --git a/pollen-ui-riot-js/src/main/web/tag/Users.tag.html b/pollen-ui-riot-js/src/main/web/tag/Users.tag.html index 5196209..61bfb85 100644 --- a/pollen-ui-riot-js/src/main/web/tag/Users.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/Users.tag.html @@ -1,307 +1,95 @@ require("./Pagination.tag.html"); +require("./UserCard.tag.html"); <Users> - <div class="body-container"> - - <div class="container"> - - <div class="legend">{__title}</div> - <table> - <thead> - <tr> - <th onclick="{toggleSort}"> - <div> - {__.name} <i ref="sortName" class="fa fa-sort-amount-asc"></i> - </div> - </th> - <th onclick="{toggleSort}"> - <div> - {__.email} <i ref="sortEmail" class="disabled fa fa-sort"></i> - </div> - </th> - <th> - <div> - {__.administrator} + <div class="container" > + <div show="{loaded}"> + <h1 class="c-heading">{__.title}</h1> + + <div show={users.length > 0} > + <div class="o-form-element sort"> + <label class="c-label" for="sort">{__.sort}</label> + <div class="c-input-group"> + <div class="o-field"> + <select class="c-field" + onchange={selectSort}> + <option value="name">{__.name}</option> + <option value="email">{__.email}</option> + </select> </div> - </th> - <th> - <div>{__.status}</div> - </th> - <th> - <div>{__.actions}</div> - </th> - </tr> - </thead> - <tbody> - <tr each="{user in users}"> - <td> - <a href="#user/{user.id}">{user.name}</a> - </td> - <td>{user.email}</td> - <td> - <input type="checkbox" disabled="disabled" checked="{user.administrator?'checked':''}"> - </td> - <td>{user.status}</td> - <td> - <div if="{user.id!=connectedUser.id}" class="dropdown"> - <a class="header-link"><i class="fa fa-navicon fa-15x"/></a> - <div class="dropdown-content" id="{user.id}"> - <a if="{!user.administrator && !user.isBanned}" onclick="{makeAdmin}">{parent.__.makeAdmin}</a> - <a if="{user.administrator && !user.isBanned}" onclick="{unmakeAdmin}">{parent.__.unmakeAdmin}</a> - <a if="{!user.isBanned}" onclick="{banUser}">{parent.__.banUser}</a> - <a onclick="{deleteUser}">{parent.__.deleteUser}</a> - </div> - </div> - </td> - </tr> - </tbody> - <tfoot> - <tr> - <th colspan="5"> - <div> - <Pagination ref="pagination" sortName='{sortName}' sortValue='false' callback="{callback}"/> - </div> - </th> - </tr> - </tfoot> - </table> + <button class="c-button c-button--brand" + onclick={toggleSort}> + <i class="fa {pagination.desc ? 'fa-sort-amount-desc' : 'fa-sort-amount-asc'}"></i> + </button> + </div> + </div> + + <Pagination pagination={pagination} onchange={refresh}/> + + <UserCard each={user in users} user={user} on-user-change={parent.refresh}/> + + <Pagination pagination={pagination} onchange={refresh}/> + </div> + <div show={users.length === 0} class="c-alert c-alert--info"> + {__.noUser} + </div> </div> - </div> <script type="es6"> - this.session = require("../js/Session"); - let i18nCallback = (locale) => { - this.moment.locale(locale); - this.refreshUsers(); + this.loaded = false; + let session = require("../js/Session"); + this.installBundle(session, "users"); + + this.pagination = { + order: "name", + desc: false, + pageSize: 5, + pageNumber: 0 }; - this.installBundle(this.session, "users", i18nCallback); - - this.moment = require("moment"); - this.moment.locale(this.session.locale); - - this.connectedUser = this.session.getUser(); - - this.sortName = "name"; - this.sortValue = false; // means asc this.users = []; let userService = require("../js/UserService"); - this.reloadPagination = ()=> { - this.pagination.onSortChange(this.sortOwner.getAttribute("sortName"), this.sortValue); - }; - - this.makeAdmin = e => { - let userId = e.target.parentNode.id; - // console.info("user id: " + userId); - userService.makeAdmin(userId).then(()=> { - this.reloadPagination(); - }); - }; - this.unmakeAdmin = e => { - let userId = e.target.parentNode.id; - // console.info("user id: " + userId); - userService.unmakeAdmin(userId).then(()=> { - this.reloadPagination(); - }); - }; - this.banUser = e => { - let userId = e.target.parentNode.id; - // console.info("user id: " + userId); - userService.banUser(userId).then(()=> { - this.reloadPagination(); - }); - }; - this.deleteUser = e => { - let userId = e.target.parentNode.id; - // console.info("user id: " + userId); - if (confirm(this.__deleteUserMessage)) { - userService.deleteUser(userId).then(()=> { - this.reloadPagination(); - }); - } - }; - - this.callback = (pagination) => { - // console.info("Request data with pagination::"); - if (!pagination.order) { - pagination.order = this.sortName; - pagination.desc = this.sortValue; - } - // console.info(pagination); - return userService.users(pagination).then((result) => { + this.refresh = () => { + return userService.users(this.pagination).then((result) => { this.users = result.elements; - // console.info("Users::"); - // console.info(this.users); - this.refreshUsers(); + Object.assign(this.pagination, result.pagination); + this.loaded = true; this.update(); return result; }); }; - this.refreshUsers = () => { - this.users.forEach(u => { - let status = "CREATED"; - if (!u.emailIsValidate) { - status = "VALIDATION"; - } - if (u.isDisabled) { - status = "DISABLE"; - } - if (u.isBanned) { - status = "BANNED"; - } - u.status = this.__[status]; - }); - }; - this.on("mount", () => { - this.pagination = this.tags.pagination; - this.sortOwner = this.refs.sortName; - this.refs.sortName.setAttribute("sortName", "name"); - this.refs.sortEmail.setAttribute("sortName", "email"); - }); - - this.toggleSort = (e) => { - // console.info(this.refs.sortName); - if (this.sortOwner === e.target) { - this.sortOwner.classList.toggle("fa-sort-amount-asc"); - this.sortOwner.classList.toggle("fa-sort-amount-desc"); - this.sortValue = !this.sortValue; - } else { - - this.sortOwner.classList.add("fa-sort"); - this.sortOwner.classList.add("disabled"); - this.sortOwner.classList.remove("fa-sort-amount-desc"); - this.sortOwner.classList.remove("fa-sort-amount-asc"); + this.refresh(); - this.sortOwner = e.target; - - this.sortOwner.classList.remove("disabled"); - this.sortOwner.classList.remove("fa-sort"); - this.sortOwner.classList.remove("fa-sort-amount-desc"); - this.sortOwner.classList.add("fa-sort-amount-asc"); - this.sortValue = true; - } - - this.reloadPagination(); + this.toggleSort = () => { + this.pagination.desc = !this.pagination.desc; + this.refresh(); }; - i18nCallback(); + this.selectSort = (e) => { + this.pagination.order = e.target.value; + this.refresh(); + }; </script> <style> - pagination { - width: 100%; - } - - .fa-15x { - font-size: 1.5em; - } - - .disabled { - color: gray; + .c-heading { + text-align: center; } - .body-container { - max-width: 90%; - } - - .container { - display: flex; - justify-content: space-around; - align-items: center; - flex-direction: column; - background: white; - border: solid 2px #c8ccca; - border-radius: 10px; - padding: 15px 0; - width: 100%; - } - - table { - padding-top: 10px; - } - - th { - text-align: left; - } - - th > div { - justify-content: flex-start; - display: flex; - flex-direction: row; - } - - td:first-child { - width: 300px; - } - - td:nth-child(2) { - width: 300px; - } - - td:nth-child(3) { - width: 120px; - } - - td:nth-child(4) { - width: 170px; - } - - td:nth-child(5) { - width: 170px; - } - - .legend { - width: 100%; - height: 30px; - border-bottom: 1px solid #b2c7d3; - display: flex; - justify-content: center; - align-items: center; - padding-bottom: 10px; - font-size: 20px; - } - - input[type=checkbox] { - width: 20px; - height: 20px; - margin: 2px; - } - - /* The container <div> - needed to position the dropdown content */ - .dropdown { - position: relative; + .o-form-element.sort .c-label:first-child { + width: 70px; display: inline-block; + text-align: right; + float: left; + padding-top: 0.5em; + padding-right: 5px; } - /* Dropdown Content (Hidden by Default) */ - .dropdown-content { - display: none; - position: absolute; - background-color: #f9f9f9; - min-width: 228px; - box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2); - } - - /* Links inside the dropdown */ - .dropdown-content a { - color: black; - padding: 12px 16px; - text-decoration: none; - display: block; - } - - /* Change color of dropdown links on hover */ - .dropdown-content a:hover { - background-color: #f1f1f1; - } - - /* Show the dropdown menu on hover */ - .dropdown:hover .dropdown-content { - display: block; - z-index: 1; + .o-form-element.sort .c-input-group { + width: 200px; } </style> -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.