This is an automated email from the git hooks/post-receive script. New commit to branch feature/pollen-riot-js in repository pollen. See https://gitlab.nuiton.org/chorem/pollen.git commit 371c5fc99a4e4f0f563a5c28052c98dcee118fcf Author: Tony CHEMIT <dev@tchemit.fr> Date: Thu Feb 2 08:05:28 2017 +0100 Ajout de la gestion des utilisateurs --- .../chorem/pollen/rest/api/v1/PollenUserApi.java | 10 + pollen-rest-api/src/main/resources/mapping | 2 + .../pollen/services/service/PollenUserService.java | 24 ++ pollen-ui-riot-js/src/main/web/i18n.json | 28 ++ pollen-ui-riot-js/src/main/web/js/UserService.js | 60 ++++ pollen-ui-riot-js/src/main/web/tag/Pollen.tag | 10 + pollen-ui-riot-js/src/main/web/tag/Users.tag | 304 +++++++++++++++++++++ 7 files changed, 438 insertions(+) diff --git a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/PollenUserApi.java b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/PollenUserApi.java index 86dbe9e..8931c4a 100644 --- a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/PollenUserApi.java +++ b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/PollenUserApi.java @@ -95,6 +95,16 @@ public class PollenUserApi extends WebMotionController { pollenUserService.banUser(userId.getEntityId(), anonymize); } + public void makeAdmin(PollenUserService pollenUserService, PollenEntityId<PollenUser> userId) { + + pollenUserService.makeAdmin(userId.getEntityId()); + } + + public void unmakeAdmin(PollenUserService pollenUserService, PollenEntityId<PollenUser> userId) { + + pollenUserService.unmakeAdmin(userId.getEntityId()); + } + public void validateUserEmail(PollenUserService pollenUserService, PollenEntityId<PollenUser> userId, String token) throws PollenInvalidEmailActivationTokenException { diff --git a/pollen-rest-api/src/main/resources/mapping b/pollen-rest-api/src/main/resources/mapping index d64c8e2..e838a4b 100644 --- a/pollen-rest-api/src/main/resources/mapping +++ b/pollen-rest-api/src/main/resources/mapping @@ -141,6 +141,8 @@ PUT,POST /v1/users/{userId}/password PollenUserApi.changePassword POST /v1/users/{userId}/admin PollenUserApi.adminUser DELETE /v1/users/{userId} PollenUserApi.deleteUser DELETE /v1/users/{userId}/ban PollenUserApi.banUser +POST /v1/users/{userId}/makeAdmin PollenUserApi.makeAdmin +POST /v1/users/{userId}/unmakeAdmin PollenUserApi.unmakeAdmin PUT /v1/users/{userId}?token={} PollenUserApi.validateUserEmail # VoteCountingApi 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 422302f..9f8c7f0 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 @@ -175,6 +175,30 @@ public class PollenUserService extends PollenServiceSupport implements PollenSer getNotificationService().onUserEdited(user); } + public void makeAdmin(String userId) { + + checkNotNull(userId); + checkIsAdmin(); + + PollenUser user = getUser0(userId); + user.setAdministrator(true); + + commit(); + + } + + public void unmakeAdmin(String userId) { + + checkNotNull(userId); + checkIsAdmin(); + + PollenUser user = getUser0(userId); + user.setAdministrator(false); + + commit(); + + } + public void changePassword(String userId, String oldPassword, String newPassword) throws InvalidFormException { diff --git a/pollen-ui-riot-js/src/main/web/i18n.json b/pollen-ui-riot-js/src/main/web/i18n.json index 957e890..78b3af8 100644 --- a/pollen-ui-riot-js/src/main/web/i18n.json +++ b/pollen-ui-riot-js/src/main/web/i18n.json @@ -193,6 +193,20 @@ "poll_created_vote": "Pour voter, veuillez utiliser le lien suivant", "poll_created_editUrl": "Gérer le sondage", "poll_created_voteUrl": "Voter", + "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é", "": "" }, "en": { @@ -391,6 +405,20 @@ "poll_created_vote": "To vote on poll, please use following link", "poll_created_editUrl": "Manage poll", "poll_created_voteUrl": "Vote on poll", + "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", "": "" } } \ No newline at end of file diff --git a/pollen-ui-riot-js/src/main/web/js/UserService.js b/pollen-ui-riot-js/src/main/web/js/UserService.js new file mode 100644 index 0000000..4fdf53d --- /dev/null +++ b/pollen-ui-riot-js/src/main/web/js/UserService.js @@ -0,0 +1,60 @@ +/*- + * #%L + * Pollen :: UI (Riot Js) + * %% + * Copyright (C) 2009 - 2017 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +let singleton = require("./Singleton"); +let FetchService = require("./FetchService"); + +class UserService extends FetchService { + + users(pagination) { + return this.getWithParams("/v1/users", {paginationParameter: pagination}); + } + + user(userId, permission) { + let url = "/v1/users/" + userId; + if (permission) { + url += "?permission=" + permission; + } + return this.get(url); + } + + makeAdmin(userId) { + let url = "/v1/users/" + userId + "/makeAdmin"; + return this.post(url); + } + + unmakeAdmin(userId) { + let url = "/v1/users/" + userId + "/unmakeAdmin"; + return this.post(url); + } + + deleteUser(userId) { + let url = "/v1/users/" + userId + "?anonymize=true"; + return this.doDelete(url); + } + + banUser(userId) { + let url = "/v1/users/" + userId + "/ban?anonymize=true"; + return this.doDelete(url); + } + +} + +module.exports = singleton(UserService); diff --git a/pollen-ui-riot-js/src/main/web/tag/Pollen.tag b/pollen-ui-riot-js/src/main/web/tag/Pollen.tag index 32756e6..1ce83da 100644 --- a/pollen-ui-riot-js/src/main/web/tag/Pollen.tag +++ b/pollen-ui-riot-js/src/main/web/tag/Pollen.tag @@ -27,6 +27,7 @@ require("./Home.tag"); require("./poll/CreatePoll.tag"); require("./poll/Poll.tag"); require("./poll/Polls.tag"); +require("./Users.tag"); <Pollen class="body-wrapper"> <Header></Header> <div class="body-content" ref="content"></div> @@ -81,6 +82,15 @@ require("./poll/Polls.tag"); } }); + route("/user", () => { + if (!session.isConnected()) { + route("/signin?url=/user"); + } else { + + riot.mount(this.refs.content, "users", {session: session}); + } + }); + route("/poll/*/choice", (pollId) => { riot.mount(this.refs.content, "poll", {pollId: pollId, tabName: 'choices'}); }); diff --git a/pollen-ui-riot-js/src/main/web/tag/Users.tag b/pollen-ui-riot-js/src/main/web/tag/Users.tag new file mode 100644 index 0000000..493faf0 --- /dev/null +++ b/pollen-ui-riot-js/src/main/web/tag/Users.tag @@ -0,0 +1,304 @@ +require('./Pagination.tag'); +<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> + </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}" onclick="{makeAdmin}">{parent.__.makeAdmin}</a> + <a if="{user.administrator}" onclick="{unmakeAdmin}">{parent.__.unmakeAdmin}</a> + <a 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> + </div> + + </div> + + <script> + this.session = require("../js/Session"); + let i18nCallback = (locale) => { + this.moment.locale(locale); + this.refreshUsers(); + }; + this.installBundle(this.session, "users", i18nCallback); + + this.moment = require('moment'); + this.moment.locale(this.session.locale); + + this.session.getUser().then(user=> { + this.connectedUser = user; + }); + + this.sortName = 'name'; + this.sortValue = false; // means asc + this.users = []; + + let userService = require('../js/UserService'); + + + this.makeAdmin = e => { + let userId = e.target.parentNode.id; + console.info('user id: ' + userId); + userService.makeAdmin(userId).then(result=> { + this.pagination.onSortChange(this.sortOwner.getAttribute('sortName'), this.sortValue); + }); + }; + this.unmakeAdmin = e => { + let userId = e.target.parentNode.id; + console.info('user id: ' + userId); + userService.unmakeAdmin(userId).then(result=> { + this.pagination.onSortChange(this.sortOwner.getAttribute('sortName'), this.sortValue); + }); + }; + this.banUser = e => { + let userId = e.target.parentNode.id; + console.info('user id: ' + userId); + userService.banUser(userId).then(result=> { + this.pagination.refresh(); + }); + }; + this.deleteUser = e => { + let userId = e.target.parentNode.id; + console.info('user id: ' + userId); + if (confirm(this.__deleteUserMessage)) { + userService.deleteUser(userId).then(result=> { + this.pagination.refresh(); + }); + } + }; + + 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.users = result.elements; + console.info('Users::'); + console.info(this.users); + this.refreshUsers(); + this.update(); + return result; + }); + }; + + this.refreshUsers = () => { + this.users.forEach(u => { + let status = 'CREATED'; + if (!u.emailIsValidate) { + status = 'VALIDATION'; + } + if (u.isDisabled) { + status = 'DISABLE'; + } + 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.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.pagination.onSortChange(this.sortOwner.getAttribute('sortName'), this.sortValue); + }; + + i18nCallback(); + + </script> + <style> + + pagination { + width: 100%; + } + + .fa-15x { + font-size: 1.5em; + } + + .disabled { + color: gray; + } + + .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; + display: inline-block; + } + + /* 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; + } + + </style> +</Users> \ No newline at end of file -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.