From 054a301a6b9fd88602bb05eb831c006ac3f1d3c4 Mon Sep 17 00:00:00 2001 From: Luis Faria Date: Wed, 11 Feb 2026 12:37:18 +0000 Subject: [PATCH 1/7] Fix #3621, #3622 #3623 --- .../roda/core/data/common/RodaConstants.java | 3 + .../roda/core/common/CryptographyUtils.java | 40 ++ .../roda/core/model/DefaultModelService.java | 117 ++-- .../DefaultTransactionalModelService.java | 7 + .../org/roda/core/model/ModelService.java | 3 + .../roda/core/model/utils/LdapUtility.java | 7 + .../resources/config/roda-roles.properties | 6 + .../config/i18n/client/ClientMessages.java | 71 +++ .../api/v2/controller/MembersController.java | 140 ++++- .../wui/api/v2/services/IndexService.java | 1 - .../wui/api/v2/services/MembersService.java | 102 +++- .../org/roda/wui/client/browse/BrowseAIP.java | 1 - .../client/browse/tabs/RODAMemberTabs.java | 58 ++ .../org/roda/wui/client/browse/tabs/Tabs.java | 21 + .../common/RODAMemberActionsToolbar.java | 40 ++ .../common/actions/RODAMemberAction.java | 32 + .../common/actions/RODAMemberActions.java | 107 ++-- .../actions/RODAMemberToolbarActions.java | 406 +++++++++++++ .../callbacks/ActionNoAsyncCallback.java | 1 - .../common/dialogs/AccessKeyDialogs.java | 59 +- .../wui/client/common/dialogs/Dialogs.java | 54 -- .../common/dialogs/MemberSelectDialog.java | 2 +- .../common/dialogs/RODAMembersDialogs.java | 445 ++++++++++++++ .../client/common/lists/RodaMemberList.java | 10 +- .../common/lists/utils/ActionMenuCell.java | 54 ++ .../common/lists/utils/BasicTablePanel.java | 29 + .../common/lists/utils/SimpleActionCell.java | 58 ++ .../client/common/panels/PermissionPanel.java | 80 +++ .../common/panels/PermissionsPanel.java | 39 ++ .../common/panels/PermissionsPanel.ui.xml | 16 + .../roda/wui/client/common/resources/main.gss | 174 +++++- .../slider/DisseminationsSliderHelper.java | 251 -------- .../common/slider/InfoSliderHelper.java | 380 +----------- .../common/slider/OptionsSliderHelper.java | 76 --- .../wui/client/common/slider/SliderPanel.java | 161 ----- .../wui/client/common/slider/Sliders.java | 62 -- .../client/common/utils/FormUtilities.java | 38 ++ .../roda/wui/client/main/BreadcrumbUtils.java | 18 + .../roda/wui/client/main/ContentPanel.java | 17 +- .../client/main/ExpiredSessionDetector.java | 4 +- .../java/org/roda/wui/client/main/Header.java | 2 +- .../java/org/roda/wui/client/main/Login.java | 4 +- .../org/roda/wui/client/main/UserMenu.java | 4 +- .../wui/client/management/CreateGroup.java | 133 ----- .../wui/client/management/CreateGroup.ui.xml | 32 - .../wui/client/management/CreateUser.java | 138 ----- .../wui/client/management/CreateUser.ui.xml | 32 - .../client/management/CreateUserPanel.ui.xml | 62 -- .../roda/wui/client/management/EditGroup.java | 161 ----- .../wui/client/management/EditGroup.ui.xml | 35 -- .../roda/wui/client/management/EditUser.java | 241 -------- .../wui/client/management/EditUser.ui.xml | 40 -- .../wui/client/management/GroupDataPanel.java | 268 --------- .../wui/client/management/Management.java | 1 + .../roda/wui/client/management/ShowGroup.java | 178 ------ .../wui/client/management/ShowGroup.ui.xml | 61 -- .../wui/client/management/ShowLogEntry.java | 11 +- .../client/management/ShowNotification.java | 2 +- .../roda/wui/client/management/ShowUser.java | 192 ------ .../wui/client/management/ShowUser.ui.xml | 81 --- .../management/access/AccessKeyDataPanel.java | 192 ------ .../access/AccessKeyDataPanel.ui.xml | 47 -- .../access/AccessKeyTablePanel.java | 278 +++++++-- .../access/AccessKeyTablePanel.ui.xml | 3 - .../management/access/CreateAccessKey.java | 141 ----- .../management/access/CreateAccessKey.ui.xml | 34 -- .../management/access/ShowAccessKey.java | 284 --------- .../management/access/ShowAccessKey.ui.xml | 80 --- .../{ => members}/CreateUserPanel.java | 317 ++-------- .../management/members/CreateUserPanel.ui.xml | 49 ++ .../management/members/GroupDataPanel.java | 194 ++++++ .../{ => members}/GroupDataPanel.ui.xml | 21 +- .../management/{ => members}/GroupSelect.java | 2 +- .../{ => members}/MemberManagement.java | 77 +-- .../{ => members}/MemberManagement.ui.xml | 0 .../{ => members}/PasswordPanel.java | 2 +- .../{ => members}/PermissionsPanel.java | 2 +- .../management/{ => members}/Profile.java | 4 +- .../management/{ => members}/Profile.ui.xml | 2 +- .../{ => members}/RecoverLogin.java | 2 +- .../{ => members}/RecoverLogin.ui.xml | 0 .../management/{ => members}/Register.java | 4 +- .../management/{ => members}/Register.ui.xml | 2 +- .../{ => members}/ResetPassword.java | 22 +- .../{ => members}/ResetPassword.ui.xml | 2 +- .../management/{ => members}/SetPassword.java | 2 +- .../{ => members}/SetPassword.ui.xml | 2 +- .../client/management/members/ShowMember.java | 167 ++++++ .../management/members/ShowMember.ui.xml | 25 + .../{ => members}/UpdatePasswordPanel.java | 2 +- .../{ => members}/UpdatePasswordPanel.ui.xml | 0 .../UserAndGroupKeyDownHandler.java | 2 +- .../{ => members}/UserDataPanel.java | 255 ++------ .../{ => members}/UserDataPanel.ui.xml | 22 +- .../management/members/UserDetailsPanel.java | 8 + .../members/tabs/AccessKeysTab.java | 83 +++ .../members/tabs/AccessKeysTab.ui.xml | 17 + .../members/tabs/PermissionsPanel.java | 558 ++++++++++++++++++ .../members/tabs/RODAMemberDetailsPanel.java | 149 +++++ .../tabs/RODAMemberDetailsPanel.ui.xml | 16 + .../members/tabs/RODAMemberGroupsTab.java | 330 +++++++++++ .../members/tabs/RODAMemberGroupsTab.ui.xml | 17 + .../tabs/RODAMemberPermissionsTab.java | 67 +++ .../tabs/RODAMemberPermissionsTab.ui.xml | 16 + .../CreateRepresentationInformation.java | 3 +- .../roda/wui/client/planning/CreateRisk.java | 9 +- .../EditRepresentationInformation.java | 3 +- .../roda/wui/client/planning/EditRisk.java | 3 +- .../client/planning/EditRiskIncidence.java | 26 +- .../roda/wui/client/planning/RiskHistory.java | 3 +- .../ShowRepresentationInformation.java | 3 +- .../roda/wui/client/planning/ShowRisk.java | 5 +- .../client/planning/ShowRiskIncidence.java | 3 +- .../client/services/MembersRestService.java | 65 +- .../wui/common/client/tools/HistoryUtils.java | 5 +- .../i18n/client/ClientMessages.properties | 70 ++- .../client/ClientMessages_pt_PT.properties | 39 ++ .../main/resources/config/roda-wui.properties | 28 +- 118 files changed, 4339 insertions(+), 4293 deletions(-) create mode 100644 roda-core/roda-core/src/main/java/org/roda/core/common/CryptographyUtils.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/RODAMemberTabs.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/RODAMemberActionsToolbar.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RODAMemberAction.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RODAMemberToolbarActions.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/RODAMembersDialogs.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/ActionMenuCell.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/SimpleActionCell.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/panels/PermissionPanel.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/panels/PermissionsPanel.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/panels/PermissionsPanel.ui.xml delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/DisseminationsSliderHelper.java delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/OptionsSliderHelper.java delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/SliderPanel.java delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/Sliders.java delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateGroup.java delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateGroup.ui.xml delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateUser.java delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateUser.ui.xml delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateUserPanel.ui.xml delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/EditGroup.java delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/EditGroup.ui.xml delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/EditUser.java delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/EditUser.ui.xml delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/GroupDataPanel.java delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowGroup.java delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowGroup.ui.xml delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowUser.java delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowUser.ui.xml delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/AccessKeyDataPanel.java delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/AccessKeyDataPanel.ui.xml delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/CreateAccessKey.java delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/CreateAccessKey.ui.xml delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/ShowAccessKey.java delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/ShowAccessKey.ui.xml rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/CreateUserPanel.java (53%) create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/CreateUserPanel.ui.xml create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/GroupDataPanel.java rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/GroupDataPanel.ui.xml (51%) rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/GroupSelect.java (99%) rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/MemberManagement.java (68%) rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/MemberManagement.ui.xml (100%) rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/PasswordPanel.java (99%) rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/PermissionsPanel.java (99%) rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/Profile.java (97%) rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/Profile.ui.xml (91%) rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/RecoverLogin.java (99%) rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/RecoverLogin.ui.xml (100%) rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/Register.java (98%) rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/Register.ui.xml (92%) rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/ResetPassword.java (72%) rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/ResetPassword.ui.xml (83%) rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/SetPassword.java (98%) rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/SetPassword.ui.xml (83%) create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/ShowMember.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/ShowMember.ui.xml rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/UpdatePasswordPanel.java (99%) rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/UpdatePasswordPanel.ui.xml (100%) rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/UserAndGroupKeyDownHandler.java (96%) rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/UserDataPanel.java (61%) rename roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/{ => members}/UserDataPanel.ui.xml (67%) create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UserDetailsPanel.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/AccessKeysTab.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/AccessKeysTab.ui.xml create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/PermissionsPanel.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberDetailsPanel.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberDetailsPanel.ui.xml create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberGroupsTab.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberGroupsTab.ui.xml create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberPermissionsTab.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberPermissionsTab.ui.xml diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/common/RodaConstants.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/common/RodaConstants.java index 64715f8c18..7f90cd73d4 100644 --- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/common/RodaConstants.java +++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/common/RodaConstants.java @@ -2161,6 +2161,9 @@ public enum RODA_TYPE { public static final String PERMISSION_METHOD_CREATE_GROUP = "org.roda.wui.api.v2.controller.MembersController.createGroup"; public static final String PERMISSION_METHOD_UPDATE_USER = "org.roda.wui.api.v2.controller.MembersController.updateUser"; public static final String PERMISSION_METHOD_DELETE_USER = "org.roda.wui.api.v2.controller.MembersController.deleteUser"; + public static final String PERMISSION_METHOD_REVOKE_ACCESS_TOKEN = "org.roda.wui.api.v2.controller.MembersController.regenerateAccessKey"; + public static final String PERMISSION_METHOD_DELETE_ACCESS_TOKEN = "org.roda.wui.api.v2.controller.MembersController.deleteAccessKey"; + public static final String PERMISSION_METHOD_REGENERATE_ACCESS_TOKEN = "org.roda.wui.api.v2.controller.MembersController.regenerateAccessKey"; public static final String PERMISSION_METHOD_CREATE_ACCESS_KEY = "org.roda.wui.api.v2.controller.MembersController.createAccessKey"; diff --git a/roda-core/roda-core/src/main/java/org/roda/core/common/CryptographyUtils.java b/roda-core/roda-core/src/main/java/org/roda/core/common/CryptographyUtils.java new file mode 100644 index 0000000000..bcd994f3ba --- /dev/null +++ b/roda-core/roda-core/src/main/java/org/roda/core/common/CryptographyUtils.java @@ -0,0 +1,40 @@ +package org.roda.core.common; + +import org.roda.core.data.exceptions.GenericException; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * + * @author Miguel Guimarães + */ +public class CryptographyUtils { + + private CryptographyUtils() { + // do nothing + } + + public static String hashTokenSHA256(String token) throws GenericException { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] encodedHash = digest.digest(token.getBytes(StandardCharsets.UTF_8)); + + // Convert the byte array into a hex string for easy storage + StringBuilder hexString = new StringBuilder(2 * encodedHash.length); + for (byte hash : encodedHash) { + String hex = Integer.toHexString(0xff & hash); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + + } catch (NoSuchAlgorithmException e) { + // Wrap and throw using your application's exception handling + throw new GenericException("Error initializing SHA-256 hashing algorithm", e); + } + } +} diff --git a/roda-core/roda-core/src/main/java/org/roda/core/model/DefaultModelService.java b/roda-core/roda-core/src/main/java/org/roda/core/model/DefaultModelService.java index 7da4af2b04..44b29806e4 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/model/DefaultModelService.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/model/DefaultModelService.java @@ -52,6 +52,7 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.roda.core.RodaCoreFactory; +import org.roda.core.common.CryptographyUtils; import org.roda.core.common.JwtUtils; import org.roda.core.common.PremisV3Utils; import org.roda.core.common.ProvidesInputStream; @@ -214,6 +215,32 @@ public class DefaultModelService implements ModelService { private JobRepository jobRepository; private ReportRepository reportRepository; + public DefaultModelService(StorageService storage, EventsManager eventsManager, NodeType nodeType, + String instanceId) { + this.storage = storage; + this.eventsManager = eventsManager; + this.nodeType = nodeType; + this.instanceId = instanceId; + this.observers = new ArrayList<>(); + + if (RodaCoreFactory.checkIfWriteIsAllowed(nodeType)) { + ensureAllContainersExist(); + ensureAllDirectoriesExist(); + } + } + + private static void clearSpecificIndexes(IndexService index, Class objectClass, + IsModelObject rodaObject) throws AuthorizationDeniedException { + if (AIP.class.equals(objectClass)) { + List ids = Arrays.asList(rodaObject.getId()); + index.delete(IndexedRepresentation.class, + new Filter(new OneOfManyFilterParameter(RodaConstants.REPRESENTATION_AIP_ID, ids))); + index.delete(IndexedFile.class, new Filter(new OneOfManyFilterParameter(RodaConstants.FILE_AIP_ID, ids))); + index.delete(IndexedPreservationEvent.class, + new Filter(new OneOfManyFilterParameter(RodaConstants.PRESERVATION_EVENT_AIP_ID, ids))); + } + } + /** * Lazily retrieves the JobRepository bean from Spring context. */ @@ -241,32 +268,6 @@ private boolean isJpaAvailable() { return SpringContext.isContextInitialized(); } - public DefaultModelService(StorageService storage, EventsManager eventsManager, NodeType nodeType, - String instanceId) { - this.storage = storage; - this.eventsManager = eventsManager; - this.nodeType = nodeType; - this.instanceId = instanceId; - this.observers = new ArrayList<>(); - - if (RodaCoreFactory.checkIfWriteIsAllowed(nodeType)) { - ensureAllContainersExist(); - ensureAllDirectoriesExist(); - } - } - - private static void clearSpecificIndexes(IndexService index, Class objectClass, - IsModelObject rodaObject) throws AuthorizationDeniedException { - if (AIP.class.equals(objectClass)) { - List ids = Arrays.asList(rodaObject.getId()); - index.delete(IndexedRepresentation.class, - new Filter(new OneOfManyFilterParameter(RodaConstants.REPRESENTATION_AIP_ID, ids))); - index.delete(IndexedFile.class, new Filter(new OneOfManyFilterParameter(RodaConstants.FILE_AIP_ID, ids))); - index.delete(IndexedPreservationEvent.class, - new Filter(new OneOfManyFilterParameter(RodaConstants.PRESERVATION_EVENT_AIP_ID, ids))); - } - } - private void ensureAllContainersExist() { try { createContainerIfNotExists(RodaConstants.STORAGE_CONTAINER_AIP); @@ -2805,7 +2806,40 @@ public Group updateGroup(final Group group, boolean notify, boolean isHandlingEv } catch (IllegalOperationException e) { throw new AuthorizationDeniedException("Illegal operation", e); } + } + + @Override + public Group updateGroupMembers(String id, Set members, boolean notify, boolean isHandlingEvent) + throws AuthorizationDeniedException, NotFoundException, GenericException { + boolean writeIsAllowed = RodaCoreFactory.checkIfWriteIsAllowed(nodeType); + + if (!writeIsAllowed && !isHandlingEvent) { + RodaCoreFactory.throwExceptionIfWriteIsNotAllowed(); + } + Group group = UserUtility.getLdapUtility().getGroup(id); + group.getUsers().addAll(members); + + for (String member: members) { + User user = UserUtility.getLdapUtility().getUser(member); + user.getGroups().add(id); + notifyUserUpdated(user).failOnError(); + } + + try { + Group updatedGroup = UserUtility.getLdapUtility().modifyGroupMembers(group); + if (notify && writeIsAllowed) { + notifyGroupUpdated(updatedGroup).failOnError(); + } + if (!isHandlingEvent) { + // FIXME 20180813 hsilva: group is not the previous state of the group + eventsManager.notifyGroupUpdated(this, group, updatedGroup); + } + + return updatedGroup; + } catch (IllegalOperationException e) { + throw new AuthorizationDeniedException("Illegal operation", e); + } } @Override @@ -2942,8 +2976,8 @@ public void createOrUpdateJob(Job job) } /** - * Flushes a job and its reports from the database to the file storage. - * This method is called when a job reaches a final state. + * Flushes a job and its reports from the database to the file storage. This + * method is called when a job reaches a final state. */ private void flushJobToStorage(Job job) throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException { @@ -3008,8 +3042,7 @@ public CloseableIterable> listJobReports(String jobId) if (jobRepo != null && reportRepo != null && jobRepo.existsById(jobId)) { // Return reports from database List dbReports = reportRepo.findByJobId(jobId); - List> wrappedReports = dbReports.stream() - .map(OptionalWithCause::of) + List> wrappedReports = dbReports.stream().map(OptionalWithCause::of) .collect(Collectors.toList()); return CloseableIterables.fromList(wrappedReports); } @@ -4081,13 +4114,13 @@ public CloseableIterable> list(Cla } else if (DescriptiveMetadata.class.equals(objectClass)) { ret = listDescriptiveMetadata(); } else if (Report.class.equals(objectClass)) { - // Include both DB reports (for running jobs) and storage reports (for completed jobs) + // Include both DB reports (for running jobs) and storage reports (for completed + // jobs) CloseableIterable> storageReports = ResourceParseUtils.convert(getStorage(), listReportResources(), Report.class); if (isJpaAvailable() && getReportRepository() != null) { List dbReports = getReportRepository().findAll(); - List> wrappedDbReports = dbReports.stream() - .map(OptionalWithCause::of) + List> wrappedDbReports = dbReports.stream().map(OptionalWithCause::of) .collect(Collectors.toList()); CloseableIterable> dbIterable = CloseableIterables.fromList(wrappedDbReports); ret = CloseableIterables.concat(dbIterable, storageReports); @@ -4102,8 +4135,7 @@ public CloseableIterable> list(Cla resourcesIterable, Job.class); if (isJpaAvailable() && getJobRepository() != null) { List dbJobs = getJobRepository().findAll(); - List> wrappedDbJobs = dbJobs.stream() - .map(OptionalWithCause::of) + List> wrappedDbJobs = dbJobs.stream().map(OptionalWithCause::of) .collect(Collectors.toList()); CloseableIterable> dbIterable = CloseableIterables.fromList(wrappedDbJobs); ret = CloseableIterables.concat(dbIterable, storageJobs); @@ -4144,13 +4176,13 @@ public CloseableIterable> storageReports = ResourceParseUtils.convertLite(getStorage(), listReportResources(), objectClass); if (isJpaAvailable() && getReportRepository() != null) { List dbReports = getReportRepository().findAll(); - List> wrappedDbReports = dbReports.stream() - .map(OptionalWithCause::of) + List> wrappedDbReports = dbReports.stream().map(OptionalWithCause::of) .collect(Collectors.toList()); CloseableIterable> dbIterable = LiteRODAObjectFactory .transformIntoLite(CloseableIterables.fromList(wrappedDbReports)); @@ -4171,8 +4203,7 @@ public CloseableIterable dbJobs = getJobRepository().findAll(); - List> wrappedDbJobs = dbJobs.stream() - .map(OptionalWithCause::of) + List> wrappedDbJobs = dbJobs.stream().map(OptionalWithCause::of) .collect(Collectors.toList()); CloseableIterable> dbIterable = LiteRODAObjectFactory .transformIntoLite(CloseableIterables.fromList(wrappedDbJobs)); @@ -5266,9 +5297,10 @@ public AccessKey createAccessKey(AccessKey accessKey, String createdBy) throws G accessKey.setExpirationDate(expirationDate); } - String token = JwtUtils.generateToken(accessKey.getUserName(), accessKey.getExpirationDate()); + String plainTextToken = JwtUtils.generateToken(accessKey.getUserName(), accessKey.getExpirationDate()); - accessKey.setKey(token); + String hashedToken = CryptographyUtils.hashTokenSHA256(plainTextToken); + accessKey.setKey(hashedToken); accessKey.setCreatedOn(new Date()); accessKey.setCreatedBy(createdBy); @@ -5279,6 +5311,7 @@ public AccessKey createAccessKey(AccessKey accessKey, String createdBy) throws G StoragePath accessKeyPath = ModelUtils.getAccessKeysStoragePath(accessKey.getId()); storage.createBinary(accessKeyPath, new StringContentPayload(accessKeyAsJson), false); + accessKey.setKey(plainTextToken); return accessKey; } diff --git a/roda-core/roda-core/src/main/java/org/roda/core/model/DefaultTransactionalModelService.java b/roda-core/roda-core/src/main/java/org/roda/core/model/DefaultTransactionalModelService.java index 14c4e31a3d..3b82df283a 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/model/DefaultTransactionalModelService.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/model/DefaultTransactionalModelService.java @@ -15,6 +15,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import org.roda.core.common.ReturnWithExceptionsWrapper; import org.roda.core.common.iterables.CloseableIterable; @@ -2038,6 +2039,12 @@ public Group updateGroup(Group group, boolean notify, boolean isHandlingEvent) return mainModelService.updateGroup(group, notify, isHandlingEvent); } + @Override + public Group updateGroupMembers(String id, Set members, boolean notify, boolean isHandlingEvent) + throws GenericException, NotFoundException, AuthorizationDeniedException { + return mainModelService.updateGroupMembers(id, members, notify, isHandlingEvent); + } + @Override public void deleteGroup(String id, boolean notify) throws GenericException, AuthorizationDeniedException { mainModelService.deleteGroup(id, notify); diff --git a/roda-core/roda-core/src/main/java/org/roda/core/model/ModelService.java b/roda-core/roda-core/src/main/java/org/roda/core/model/ModelService.java index 02c1ae5ce0..0879aec09b 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/model/ModelService.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/model/ModelService.java @@ -14,6 +14,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import org.roda.core.common.iterables.CloseableIterable; import org.roda.core.common.notifications.NotificationProcessor; @@ -547,6 +548,8 @@ Group updateGroup(Group group, boolean notify) Group updateGroup(Group group, boolean notify, boolean isHandlingEvent) throws GenericException, NotFoundException, AuthorizationDeniedException; + Group updateGroupMembers(String id, Set members, boolean notify,boolean isHandlingEvent) throws AuthorizationDeniedException, NotFoundException, GenericException; + void deleteGroup(String id, boolean notify) throws GenericException, AuthorizationDeniedException; void deleteGroup(String id, boolean notify, boolean isHandlingEvent) diff --git a/roda-core/roda-core/src/main/java/org/roda/core/model/utils/LdapUtility.java b/roda-core/roda-core/src/main/java/org/roda/core/model/utils/LdapUtility.java index 980fd4b228..cc94a2c875 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/model/utils/LdapUtility.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/model/utils/LdapUtility.java @@ -709,6 +709,10 @@ public Group modifyGroup(final Group modifiedGroup) return modifyGroup(modifiedGroup, false); } + public Group modifyGroupMembers(final Group modifiedGroup) throws NotFoundException, IllegalOperationException, GenericException { + return modifyGroup(modifiedGroup, true); + } + /** * Removes a group. * @@ -1809,6 +1813,9 @@ private Name removeBaseDN(Name dn) { return LdapUtils.removeFirst(dn, LdapUtils.newLdapName(ldapRootDN)); } + public boolean isProtectedUser(String username) { + return this.ldapProtectedUsers.contains(username); + } public String transformExtra(Set values) { Handlebars handlebars = new Handlebars(); diff --git a/roda-core/roda-core/src/main/resources/config/roda-roles.properties b/roda-core/roda-core/src/main/resources/config/roda-roles.properties index 40ee50fc25..e54f2efb76 100644 --- a/roda-core/roda-core/src/main/resources/config/roda-roles.properties +++ b/roda-core/roda-core/src/main/resources/config/roda-roles.properties @@ -113,6 +113,12 @@ core.roles.org.roda.wui.api.v2.controller.MembersController.updateAccessKey = ac core.roles.org.roda.wui.api.v2.controller.MembersController.getAccessKeysByUser = access_key.read core.roles.org.roda.wui.api.v2.controller.MembersController.createAccessKey = access_key.manage core.roles.org.roda.wui.api.v2.controller.MembersController.changeActive = member.manage +core.roles.org.roda.wui.api.v2.controller.MembersController.addGroupsToUser = member.manage +core.roles.org.roda.wui.api.v2.controller.MembersController.removeGroupsFromUser = member.manage +core.roles.org.roda.wui.api.v2.controller.MembersController.getUserGroups = member.read +core.roles.org.roda.wui.api.v2.controller.MembersController.getGroupMembers = member.read +core.roles.org.roda.wui.api.v2.controller.MembersController.addMembersToGroup = member.manage +core.roles.org.roda.wui.api.v2.controller.MembersController.removeMembersFromGroup = member.manage core.roles.org.roda.wui.api.v2.services.MembersService.retrieveOwnUserExtra = member.read core.roles.org.roda.wui.api.v2.services.MembersService.retrieveUserExtra = member.read diff --git a/roda-ui/roda-wui/src/main/java/config/i18n/client/ClientMessages.java b/roda-ui/roda-wui/src/main/java/config/i18n/client/ClientMessages.java index 7847fb8bb5..135c05fdf1 100644 --- a/roda-ui/roda-wui/src/main/java/config/i18n/client/ClientMessages.java +++ b/roda-ui/roda-wui/src/main/java/config/i18n/client/ClientMessages.java @@ -417,6 +417,8 @@ public interface ClientMessages extends Messages { String cancelButton(); + String updateButton(); + String revertButton(); String removeButton(); @@ -1100,6 +1102,14 @@ SafeHtml searchPreFilterLongRangeFilterParameterGreaterThan(String searchPreFilt String userRemoveConfirmDialogMessage(); + String singleUserRemoveConfirmDialogTitle(); + + String singleGroupRemoveConfirmDialogTitle(); + + String singleUserRemoveConfirmDialogMessage(String username); + + String singleGroupRemoveConfirmDialogMessage(String username); + // Member management String addUserButton(); @@ -2499,6 +2509,8 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String String createAccessKeyTitle(); + String regenerateAccessKeyTitle(); + String showAccessKeyTitle(); String editAccessKeyTitle(); @@ -2537,6 +2549,8 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String String accessKeySuccessfullyRegenerated(); + String accessKeySuccessfullyDeleted(); + String accessKeySuccessfullyRevoked(); String accessKeyDeleteConfirmationMessage(); @@ -2545,6 +2559,8 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String String accessKeyRegenerateConfirmationMessage(); + String accessKeyExpirationDateInThePast(); + /** Market **/ String marketPluginsActionsTabLabel(); @@ -2640,6 +2656,61 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String String reasonCantActOnGroup(); + // RODA Members - toolbar actions + String deactivateUserTitle(); + + String deactivateUserConfirmationMessage(); + + String activateUserTitle(); + + String activateUserConfirmationMessage(); + + // RODA Members - Members + String membersTabTitle(); + + String addNewGroupModalTitle(); + + String addToGroupButton(); + + String addNewMemberToGroupButton(); + + String groupSuccessfullyAdded(); + + String memberSuccessfullyAdded(); + + String addNewMemberToGroupTitle(); + + String addNewMemberAction(); + + String removeGroupConfirmationTitle(); + + String removeGroupConfirmationMessage(String group); + + String groupSuccessfullyRemoved(); + + String removeMemberConfirmationMessage(String member); + + String removeMemberConfirmationTitle(); + + String memberSuccessfullyRemoved(); + + // RODA Members - Permissions + String catalogueAndSearchGroupLabel(); + + String ingestPreservationActionsInternalActionsGroupLabel(); + + String administrationGroupLabel(); + + String planningGroupLabel(); + + String disposalGroupLabel(); + + String editPermissionsReadOnlyPermissionsText(); + + String permissionsUpdateWithSuccess(); + + String editPermissionsModalTitle(); + // ── Email viewer ────────────────────────────────────────────────────────── String emailViewerSubject(); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/MembersController.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/MembersController.java index 7b20c7f465..5c81bcfc11 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/MembersController.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/MembersController.java @@ -19,6 +19,7 @@ import javax.crypto.SecretKey; +import io.swagger.v3.oas.annotations.Parameter; import org.apache.commons.lang3.StringUtils; import org.apereo.cas.client.authentication.AttributePrincipal; import org.roda.core.RodaCoreFactory; @@ -78,6 +79,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.ldap.NamingException; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -272,7 +274,7 @@ private static Set mapCasGroupstoRODAGroups(Set casGroups) { } @Override - public User getUser(String name) { + public RODAMember getUser(String name) { RequestContext requestContext = RequestUtils.parseHTTPRequest(request); ControllerAssistant controllerAssistant = new ControllerAssistant() {}; LogEntryState state = LogEntryState.SUCCESS; @@ -281,7 +283,11 @@ public User getUser(String name) { // check user permissions controllerAssistant.checkRoles(requestContext.getUser()); - return membersService.retrieveUser(name); + if (name.startsWith("user-")) { + return membersService.retrieveUser(name.substring("user-".length())); + } else { + return membersService.retrieveGroup(name.substring("group-".length())); + } } catch (RODAException e) { state = LogEntryState.FAILURE; @@ -631,6 +637,136 @@ public Void updateGroup(@RequestBody Group modifiedGroup) { return null; } + @Override + public User addGroupsToUser(String id, @RequestBody SelectedItemsRequest groups) { + ControllerAssistant controllerAssistant = new ControllerAssistant() {}; + RequestContext requestContext = RequestUtils.parseHTTPRequest(request); + + LogEntryState state = LogEntryState.SUCCESS; + + try { + // check user permissions + controllerAssistant.checkRoles(requestContext.getUser()); + // delegate + return membersService.addGroupsToUser(id, groups); + } catch (RODAException e) { + state = LogEntryState.FAILURE; + throw new RESTException(e); + } finally { + // register action + controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_USER_PARAM, id, + RodaConstants.CONTROLLER_SELECTED_ITEMS_PARAM, groups); + } + } + + @Override + public Group addMembersToGroup(String id, @RequestBody SelectedItemsRequest members) { + ControllerAssistant controllerAssistant = new ControllerAssistant() {}; + RequestContext requestContext = RequestUtils.parseHTTPRequest(request); + + LogEntryState state = LogEntryState.SUCCESS; + + try { + // check user permissions + controllerAssistant.checkRoles(requestContext.getUser()); + // delegate + return membersService.addMembersToGroup(id, members); + } catch (RODAException e) { + state = LogEntryState.FAILURE; + throw new RESTException(e); + } finally { + // register action + controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_GROUP_PARAM, id, + RodaConstants.CONTROLLER_SELECTED_ITEMS_PARAM, members); + } + } + + @Override + public User removeGroupsFromUser(String id, String groupID) { + ControllerAssistant controllerAssistant = new ControllerAssistant() {}; + RequestContext requestContext = RequestUtils.parseHTTPRequest(request); + + LogEntryState state = LogEntryState.SUCCESS; + + try { + // check user permissions + controllerAssistant.checkRoles(requestContext.getUser()); + // delegate + return membersService.removeGroupFromUser(id, groupID); + } catch (RODAException e) { + state = LogEntryState.FAILURE; + throw new RESTException(e); + } finally { + // register action + controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_USER_PARAM, id, + RodaConstants.CONTROLLER_GROUP_PARAM, groupID); + } + } + + @Override + public Group removeMembersFromGroup(String id, String userId) { + ControllerAssistant controllerAssistant = new ControllerAssistant() {}; + RequestContext requestContext = RequestUtils.parseHTTPRequest(request); + + LogEntryState state = LogEntryState.SUCCESS; + + try { + // check user permissions + controllerAssistant.checkRoles(requestContext.getUser()); + // delegate + return membersService.removeMemberFromGroup(id, userId); + } catch (RODAException e) { + state = LogEntryState.FAILURE; + throw new RESTException(e); + } finally { + // register action + controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_GROUP_PARAM, id, + RodaConstants.CONTROLLER_USER_PARAM, userId); + } + } + + @Override + public Set getUserGroups(String id) { + ControllerAssistant controllerAssistant = new ControllerAssistant() {}; + RequestContext requestContext = RequestUtils.parseHTTPRequest(request); + + LogEntryState state = LogEntryState.SUCCESS; + + try { + // check user permissions + controllerAssistant.checkRoles(requestContext.getUser()); + // delegate + return membersService.getGroupFromUser(id); + } catch (RODAException e) { + state = LogEntryState.FAILURE; + throw new RESTException(e); + } finally { + // register action + controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_USER_PARAM, id); + } + } + + @Override + public Set getGroupMembers(String id) { + ControllerAssistant controllerAssistant = new ControllerAssistant() {}; + RequestContext requestContext = RequestUtils.parseHTTPRequest(request); + + LogEntryState state = LogEntryState.SUCCESS; + + try { + // check user permissions + controllerAssistant.checkRoles(requestContext.getUser()); + // delegate + return membersService.getGroupMembers(id); + } catch (RODAException e) { + state = LogEntryState.FAILURE; + throw new RESTException(e); + } finally { + // register action + controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_GROUP_PARAM, id); + } + } + @Override public User updateUser(@RequestBody UpdateUserRequest userRequest) { // TODO: Change request usage diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/IndexService.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/IndexService.java index 898514443e..cfef55b3b4 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/IndexService.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/IndexService.java @@ -23,7 +23,6 @@ import org.roda.core.data.v2.index.SuggestRequest; import org.roda.core.data.v2.index.sort.Sorter; import org.roda.core.data.v2.index.sublist.Sublist; -import org.roda.core.data.v2.user.User; import org.roda.wui.api.v2.controller.RequestHandler; import org.roda.wui.api.v2.exceptions.RESTException; import org.roda.wui.api.v2.stream.FacetsCSVOutputStream; diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/MembersService.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/MembersService.java index 437feef11d..b81eaf2149 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/MembersService.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/MembersService.java @@ -15,11 +15,14 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import org.apache.commons.io.IOUtils; +import org.apache.solr.client.solrj.util.ClientUtils; import org.joda.time.DateTime; import org.roda.core.RodaCoreFactory; import org.roda.core.common.Messages; @@ -41,6 +44,8 @@ import org.roda.core.data.exceptions.RequestNotValidException; import org.roda.core.data.exceptions.UserAlreadyExistsException; import org.roda.core.data.v2.generics.MetadataValue; +import org.roda.core.data.v2.generics.select.SelectedItemsListRequest; +import org.roda.core.data.v2.generics.select.SelectedItemsRequest; import org.roda.core.data.v2.index.filter.Filter; import org.roda.core.data.v2.index.select.SelectedItems; import org.roda.core.data.v2.index.select.SelectedItemsFilter; @@ -57,8 +62,11 @@ import org.roda.core.data.v2.validation.ValidationException; import org.roda.core.index.IndexService; import org.roda.core.model.ModelService; +import org.roda.core.model.utils.LdapGroup; +import org.roda.core.model.utils.LdapUtility; import org.roda.core.model.utils.UserUtility; import org.roda.core.plugins.base.maintenance.ChangeRodaMemberStatusPlugin; +import org.roda.core.repository.ldap.LdapGroupRepository; import org.roda.core.util.IdUtils; import org.roda.wui.api.v2.exceptions.RESTException; import org.roda.wui.api.v2.utils.CommonServicesUtils; @@ -69,6 +77,8 @@ import org.roda.wui.common.server.ServerTools; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import jakarta.servlet.http.HttpServletRequest; @@ -82,6 +92,9 @@ public class MembersService { private static final Logger LOGGER = LoggerFactory.getLogger(MembersService.class); private static final String RECAPTCHA_CODE_SECRET_PROPERTY = "ui.google.recaptcha.code.secret"; + @Autowired + LdapGroupRepository ldapGroupRepository; + private static List getMemberUuidFromSelectedItems(SelectedItems members) throws GenericException, RequestNotValidException { List uuids = new ArrayList<>(); @@ -202,8 +215,8 @@ public void deleteUser(String username) throws GenericException, AuthorizationDe RodaCoreFactory.getIndexService().commit(true, RODAMember.class); } - public Group retrieveGroup(String groupname) throws GenericException, NotFoundException { - return RodaCoreFactory.getModelService().retrieveGroup(groupname); + public Group retrieveGroup(String groupName) throws GenericException, NotFoundException { + return RodaCoreFactory.getModelService().retrieveGroup(groupName); } public Job changeActiveMembers(SelectedItems selectedItems, boolean active, User user) @@ -297,6 +310,91 @@ public User confirmUserEmail(String username, String email, String emailConfirma return RodaCoreFactory.getModelService().confirmUserEmail(username, email, emailConfirmationToken, true, true); } + public User addGroupsToUser(String id, SelectedItemsRequest groups) + throws GenericException, AuthorizationDeniedException, AlreadyExistsException, NotFoundException { + if (groups instanceof SelectedItemsListRequest listRequest) { + User user = RodaCoreFactory.getModelService().retrieveUser(id); + Set collect = listRequest.getIds().stream().map(m -> m.replace("group-", "")).collect(Collectors.toSet()); + user.getGroups().addAll(collect); + RodaCoreFactory.getModelService().updateUser(user, null, true); + RodaCoreFactory.getIndexService().commit(true, RODAMember.class); + return user; + } + + return new User(); + } + + public Group addMembersToGroup(String id, SelectedItemsRequest members) + throws GenericException, AuthorizationDeniedException, AlreadyExistsException, NotFoundException, IllegalOperationException { + if (members instanceof SelectedItemsListRequest listRequest) { + Set collect = listRequest.getIds().stream().map(m -> m.replace("user-", "")).collect(Collectors.toSet()); + + collect.removeIf(p -> UserUtility.getLdapUtility().isProtectedUser(p)); + + if (collect.isEmpty()) { + throw new IllegalOperationException("All selected users are protected and cannot be added to the group"); + } + + Group group = RodaCoreFactory.getModelService().updateGroupMembers(id, collect, true, false); + RodaCoreFactory.getIndexService().commit(true, RODAMember.class); + return group; + } + + return new Group(); + } + + public Set getGroupFromUser(String id) throws NotFoundException, GenericException { + Set groups = new HashSet<>(); + RodaCoreFactory.getModelService().retrieveUser(id).getGroups().forEach(groupName -> { + try { + Group group = RodaCoreFactory.getModelService().retrieveGroup(groupName); + group.getUsers().clear(); + groups.add(group); + } catch (GenericException | NotFoundException e) { + LOGGER.error("Error retrieving group {}", groupName, e); + } + }); + + return groups; + } + + public Set getGroupMembers(String id) throws NotFoundException, GenericException { + Set members = new HashSet<>(); + RodaCoreFactory.getModelService().retrieveGroup(id).getUsers().forEach(userId -> { + try { + User user = RodaCoreFactory.getModelService().retrieveUser(userId); + user.getGroups().clear(); + user.setResetPasswordToken(null); + user.setResetPasswordTokenExpirationDate(null); + user.setEmailConfirmationToken(null); + user.setEmailConfirmationTokenExpirationDate(null); + members.add(user); + } catch (GenericException e) { + LOGGER.error("Error retrieving user {}", userId, e); + } + }); + + return members; + } + + public User removeGroupFromUser(String id, String groupID) + throws GenericException, AuthorizationDeniedException, AlreadyExistsException, NotFoundException { + User user = RodaCoreFactory.getModelService().retrieveUser(id); + user.getGroups().remove(groupID); + RodaCoreFactory.getModelService().updateUser(user, null, true); + RodaCoreFactory.getIndexService().commit(true, RODAMember.class); + return user; + } + + public Group removeMemberFromGroup(String id, String userId) + throws GenericException, AuthorizationDeniedException, AlreadyExistsException, NotFoundException { + User user = RodaCoreFactory.getModelService().retrieveUser(userId); + user.getGroups().remove(id); + RodaCoreFactory.getModelService().updateUser(user, null, true); + RodaCoreFactory.getIndexService().commit(true, RODAMember.class); + return RodaCoreFactory.getModelService().retrieveGroup(id); + } + public User updateUser(UpdateUserRequest request) throws GenericException, AlreadyExistsException, NotFoundException, AuthorizationDeniedException, ValidationException, RequestNotValidException { ModelService model = RodaCoreFactory.getModelService(); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseAIP.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseAIP.java index b23c196d24..08e8fc3ef1 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseAIP.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseAIP.java @@ -346,7 +346,6 @@ private static void refresh(String id, AsyncCallback callback) { }); } }); - } private void updateSectionIdentification(BrowseAIPResponse response) { diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/RODAMemberTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/RODAMemberTabs.java new file mode 100644 index 0000000000..53a51775df --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/RODAMemberTabs.java @@ -0,0 +1,58 @@ +package org.roda.wui.client.browse.tabs; + +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Widget; +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.v2.user.RODAMember; +import org.roda.wui.client.common.actions.Actionable; +import org.roda.wui.client.common.utils.PermissionClientUtils; +import org.roda.wui.client.management.members.tabs.AccessKeysTab; +import org.roda.wui.client.management.members.tabs.RODAMemberDetailsPanel; +import org.roda.wui.client.management.members.tabs.RODAMemberGroupsTab; +import org.roda.wui.client.management.members.tabs.RODAMemberPermissionsTab; + +public class RODAMemberTabs extends Tabs { + + public void init(RODAMember member, AsyncCallback actionCallback) { + + int activeIndex = this.getSelectedTabIndex(); + + this.clear(); + + // 1. Clear any existing tabs before building new ones! + createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.detailsTab()), new TabContentBuilder() { + @Override + public Widget buildTabWidget() { + return new RODAMemberDetailsPanel(member, actionCallback); + } + }); + + createAndAddTab(SafeHtmlUtils.fromSafeConstant(member.isUser() ? messages.groups() : messages.membersTabTitle()), + new TabContentBuilder() { + @Override + public Widget buildTabWidget() { + return new RODAMemberGroupsTab(member, actionCallback); + } + }); + + createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.permissionsTab()), new TabContentBuilder() { + @Override + public Widget buildTabWidget() { + return new RODAMemberPermissionsTab(member, actionCallback); + } + }); + + if (member.isUser() && PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_REVOKE_ACCESS_TOKEN, + RodaConstants.PERMISSION_METHOD_REGENERATE_ACCESS_TOKEN, RodaConstants.PERMISSION_METHOD_DELETE_ACCESS_TOKEN)) { + createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.showAccessKeyTitle()), new TabContentBuilder() { + @Override + public Widget buildTabWidget() { + return new AccessKeysTab(member, actionCallback); + } + }); + } + + this.selectTabByIndex(activeIndex); + } +} \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/Tabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/Tabs.java index 75f04f1dfe..7e3af0215c 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/Tabs.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/Tabs.java @@ -120,6 +120,27 @@ protected void selectTab(Widget tabButtonContainer) { } } + public void clear() { + tabButtons.clear(); + tabContentWrapper.clear(); + tabs.clear(); + selectedTab = null; + } + + public int getSelectedTabIndex() { + if (selectedTab == null) { + return 0; // Default to first tab + } + return tabButtons.getWidgetIndex(selectedTab); + } + + public void selectTabByIndex(int index) { + if (index >= 0 && index < tabButtons.getWidgetCount()) { + Widget tabToSelect = tabButtons.getWidget(index); + selectTab(tabToSelect); + } + } + public interface TabContentBuilder { public Widget buildTabWidget(); } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/RODAMemberActionsToolbar.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/RODAMemberActionsToolbar.java new file mode 100644 index 0000000000..ac42198e5f --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/RODAMemberActionsToolbar.java @@ -0,0 +1,40 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE file at the root of the source + * tree and available online at + * + * https://github.com/keeps/roda + */ +package org.roda.wui.client.common; + +import java.util.List; + +import org.roda.core.data.v2.user.RODAMember; +import org.roda.wui.client.common.actions.RODAMemberAction; +import org.roda.wui.client.common.actions.RODAMemberToolbarActions; +import org.roda.wui.client.common.actions.model.ActionableObject; +import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; + +/** + * + * @author Miguel Guimarães + */ +public class RODAMemberActionsToolbar extends BrowseObjectActionsToolbar { + public void buildIcon() { + // do nothing + } + + public void buildTags() { + // do nothing + } + + public void buildActions() { + this.actions.clear(); + + RODAMemberToolbarActions rodaMemberActions = RODAMemberToolbarActions.get(); + this.actions.add(new ActionableWidgetBuilder(rodaMemberActions).withActionCallback(actionCallback) + .buildGroupedListWithObjects(new ActionableObject<>(object), + List.of(RODAMemberAction.ACTIVATE, RODAMemberAction.DEACTIVATE, RODAMemberAction.REMOVE), + List.of())); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RODAMemberAction.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RODAMemberAction.java new file mode 100644 index 0000000000..1ed5b7418b --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RODAMemberAction.java @@ -0,0 +1,32 @@ +package org.roda.wui.client.common.actions; + +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.v2.user.RODAMember; + +import java.util.Arrays; +import java.util.List; + +/** + * @author Miguel Guimarães + */ + +public enum RODAMemberAction implements Actionable.Action { + NEW_USER(RodaConstants.PERMISSION_METHOD_CREATE_USER), NEW_GROUP(RodaConstants.PERMISSION_METHOD_CREATE_GROUP), + ACTIVATE(RodaConstants.PERMISSION_METHOD_UPDATE_USER), DEACTIVATE(RodaConstants.PERMISSION_METHOD_UPDATE_USER), + EDIT(RodaConstants.PERMISSION_METHOD_UPDATE_USER), REMOVE(RodaConstants.PERMISSION_METHOD_DELETE_USER), + ADD_NEW_GROUP(RodaConstants.PERMISSION_METHOD_UPDATE_USER), + ADD_NEW_MEMBER(RodaConstants.PERMISSION_METHOD_UPDATE_USER), + EDIT_PERMISSIONS(RodaConstants.PERMISSION_METHOD_UPDATE_USER), + NEW_ACCESS_KEY(RodaConstants.PERMISSION_METHOD_CREATE_ACCESS_KEY); + + private final List methods; + + RODAMemberAction(String... methods) { + this.methods = Arrays.asList(methods); + } + + @Override + public List getMethods() { + return this.methods; + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RODAMemberActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RODAMemberActions.java index 88c67146bf..f0f50550aa 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RODAMemberActions.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RODAMemberActions.java @@ -9,24 +9,22 @@ import java.util.Arrays; import java.util.HashSet; -import java.util.List; import java.util.Set; -import org.roda.core.data.common.RodaConstants; +import com.google.gwt.i18n.client.LocaleInfo; import org.roda.core.data.utils.SelectedItemsUtils; import org.roda.core.data.v2.index.select.SelectedItems; import org.roda.core.data.v2.user.RODAMember; import org.roda.core.data.v2.user.requests.ChangeUserStatusRequest; +import org.roda.core.data.v2.user.requests.CreateGroupRequest; +import org.roda.core.data.v2.user.requests.CreateUserRequest; +import org.roda.wui.client.common.actions.callbacks.ActionAsyncCallback; import org.roda.wui.client.common.actions.model.ActionableBundle; import org.roda.wui.client.common.actions.model.ActionableGroup; import org.roda.wui.client.common.dialogs.Dialogs; +import org.roda.wui.client.common.dialogs.RODAMembersDialogs; import org.roda.wui.client.ingest.process.ShowJob; -import org.roda.wui.client.management.CreateGroup; -import org.roda.wui.client.management.CreateUser; -import org.roda.wui.client.management.EditGroup; -import org.roda.wui.client.management.EditUser; -import org.roda.wui.client.management.MemberManagement; -import org.roda.wui.client.management.access.CreateAccessKey; +import org.roda.wui.client.management.members.MemberManagement; import org.roda.wui.client.services.Services; import org.roda.wui.common.client.tools.HistoryUtils; import org.roda.wui.common.client.widgets.Toast; @@ -50,7 +48,7 @@ public class RODAMemberActions extends AbstractActionable { Arrays.asList(RODAMemberAction.ACTIVATE, RODAMemberAction.DEACTIVATE, RODAMemberAction.REMOVE)); private static final Set POSSIBLE_ACTIONS_ON_GROUP = new HashSet<>( - Arrays.asList(RODAMemberAction.EDIT, RODAMemberAction.REMOVE)); + Arrays.asList(RODAMemberAction.REMOVE)); private static final Set POSSIBLE_ACTIONS_ON_MEMBERS = new HashSet<>( Arrays.asList(RODAMemberAction.ACTIVATE, RODAMemberAction.DEACTIVATE, RODAMemberAction.REMOVE)); @@ -92,11 +90,8 @@ public CanActResult userCanAct(Action action, RODAMember object) { @Override public CanActResult contextCanAct(Action action, RODAMember object) { if (object.isUser()) { - return new CanActResult( - (action.equals(RODAMemberAction.DEACTIVATE) && object.isActive()) - || (action.equals(RODAMemberAction.ACTIVATE) && !object.isActive()) - || (action.equals(RODAMemberAction.REMOVE) || (action.equals(RODAMemberAction.EDIT) - || (action.equals(RODAMemberAction.NEW_ACCESS_KEY) && object.isUser()))), + return new CanActResult((action.equals(RODAMemberAction.DEACTIVATE) && object.isActive()) + || (action.equals(RODAMemberAction.ACTIVATE) && !object.isActive()) || (action.equals(RODAMemberAction.REMOVE)), CanActResult.Reason.CONTEXT, messages.reasonCantActOnUser()); } else { return new CanActResult(POSSIBLE_ACTIONS_ON_GROUP.contains(action), CanActResult.Reason.CONTEXT, @@ -132,12 +127,8 @@ public void act(Action action, RODAMember object, AsyncCallback callback) { - callback.onSuccess(ActionImpact.NONE); - if (object.isUser()) { - HistoryUtils.newHistory(EditUser.RESOLVER, object.getId()); - } else { - HistoryUtils.newHistory(EditGroup.RESOLVER, object.getId()); - } - } - private void deactivate(SelectedItems objects, AsyncCallback callback) { Services services = new Services("Deactivate RODA member", "deactivate"); ChangeUserStatusRequest request = new ChangeUserStatusRequest(SelectedItemsUtils.convertToRESTRequest(objects), @@ -240,18 +222,51 @@ public void onFailure(Throwable caught) { } private void createUser(AsyncCallback callback) { - callback.onSuccess(ActionImpact.NONE); - HistoryUtils.newHistory(CreateUser.RESOLVER.getHistoryPath()); + RODAMembersDialogs.createUser(messages.addUserButton(), messages.cancelButton(), messages.saveButton(), + new ActionAsyncCallback(callback) { + @Override + public void onSuccess(CreateUserRequest result) { + Services services = new Services("Create User", "create"); + services.membersResource(s -> s.createUser(result, LocaleInfo.getCurrentLocale().getLocaleName())) + .whenComplete((res, error) -> { + if (error == null) { + Toast.showInfo(messages.users(), "User successfully created"); + doActionCallbackUpdated(); + } else { + doActionCallbackNone(); + } + }); + } + + @Override + public void onFailure(Throwable caught) { + doActionCallbackNone(); + } + }); } private void createGroup(AsyncCallback callback) { - callback.onSuccess(ActionImpact.NONE); - HistoryUtils.newHistory(CreateGroup.RESOLVER.getHistoryPath()); - } + RODAMembersDialogs.createGroup(messages.addGroupButton(), messages.cancelButton(), messages.saveButton(), + new ActionAsyncCallback(callback) { + + @Override + public void onSuccess(CreateGroupRequest createGroupRequest) { + Services services = new Services("Create Group", "create"); + services.membersResource(s -> s.createGroup(createGroupRequest)).whenComplete((res, error) -> { + if (error == null) { + Toast.showInfo(messages.groups(), "Group successfully created"); + doActionCallbackUpdated(); + } else { + doActionCallbackNone(); + } + }); + } - private void createNewAccessKey(AsyncCallback callback, RODAMember user) { - callback.onSuccess(ActionImpact.NONE); - HistoryUtils.newHistory(CreateAccessKey.RESOLVER, user.getName()); + @Override + public void onFailure(Throwable caught) { + doActionCallbackNone(); + } + }); } @Override @@ -264,35 +279,13 @@ public ActionableBundle createActionsBundle() { "btn-plus-circle"); actionableGroup.addButton(messages.addGroupButton(), RODAMemberAction.NEW_GROUP, ActionImpact.UPDATED, "btn-plus-circle"); - actionableGroup.addButton(messages.editUserAction(), RODAMemberAction.EDIT, ActionImpact.UPDATED, "btn-edit"); - actionableGroup.addButton(messages.editUserActivate(), RODAMemberAction.ACTIVATE, ActionImpact.UPDATED, "btn-enable-user"); actionableGroup.addButton(messages.editUserDeactivate(), RODAMemberAction.DEACTIVATE, ActionImpact.UPDATED, "btn-disable-user"); - actionableGroup.addButton(messages.addAccessKeyButton(), RODAMemberAction.NEW_ACCESS_KEY, ActionImpact.UPDATED, - "btn-key"); actionableGroup.addButton(messages.editUserRemove(), RODAMemberAction.REMOVE, ActionImpact.DESTROYED, "btn-ban"); transferredResourcesActionableBundle.addGroup(actionableGroup); return transferredResourcesActionableBundle; } - - public enum RODAMemberAction implements Action { - NEW_USER(RodaConstants.PERMISSION_METHOD_CREATE_USER), NEW_GROUP(RodaConstants.PERMISSION_METHOD_CREATE_GROUP), - ACTIVATE(RodaConstants.PERMISSION_METHOD_UPDATE_USER), DEACTIVATE(RodaConstants.PERMISSION_METHOD_UPDATE_USER), - EDIT(RodaConstants.PERMISSION_METHOD_UPDATE_USER), REMOVE(RodaConstants.PERMISSION_METHOD_DELETE_USER), - NEW_ACCESS_KEY(RodaConstants.PERMISSION_METHOD_CREATE_ACCESS_KEY); - - private List methods; - - RODAMemberAction(String... methods) { - this.methods = Arrays.asList(methods); - } - - @Override - public List getMethods() { - return this.methods; - } - } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RODAMemberToolbarActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RODAMemberToolbarActions.java new file mode 100644 index 0000000000..baf4b8e973 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RODAMemberToolbarActions.java @@ -0,0 +1,406 @@ +package org.roda.wui.client.common.actions; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.user.client.rpc.AsyncCallback; +import config.i18n.client.ClientMessages; + +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.utils.SelectedItemsUtils; +import org.roda.core.data.v2.accessKey.CreateAccessKeyRequest; +import org.roda.core.data.v2.index.filter.BasicSearchFilterParameter; +import org.roda.core.data.v2.index.filter.Filter; +import org.roda.core.data.v2.index.filter.NotSimpleFilterParameter; +import org.roda.core.data.v2.index.select.SelectedItems; +import org.roda.core.data.v2.user.Group; +import org.roda.core.data.v2.user.RODAMember; +import org.roda.core.data.v2.user.User; +import org.roda.core.data.v2.user.requests.ChangeUserStatusRequest; +import org.roda.core.data.v2.user.requests.UpdateUserRequest; +import org.roda.wui.client.common.NoAsyncCallback; +import org.roda.wui.client.common.actions.callbacks.ActionAsyncCallback; +import org.roda.wui.client.common.actions.callbacks.ActionNoAsyncCallback; +import org.roda.wui.client.common.actions.model.ActionableBundle; +import org.roda.wui.client.common.actions.model.ActionableGroup; +import org.roda.wui.client.common.dialogs.AccessKeyDialogs; +import org.roda.wui.client.common.dialogs.Dialogs; +import org.roda.wui.client.common.dialogs.RODAMembersDialogs; +import org.roda.wui.client.ingest.process.ShowJob; +import org.roda.wui.client.management.members.MemberManagement; +import org.roda.wui.client.management.members.tabs.PermissionsPanel; +import org.roda.wui.client.services.Services; +import org.roda.wui.common.client.tools.HistoryUtils; +import org.roda.wui.common.client.widgets.Toast; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Miguel Guimarães + */ + +public class RODAMemberToolbarActions extends AbstractActionable { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + + private static final Set POSSIBLE_ACTIONS_ON_USER = new HashSet<>( + Arrays.asList(RODAMemberAction.ACTIVATE, RODAMemberAction.DEACTIVATE, RODAMemberAction.REMOVE)); + + private static final Set POSSIBLE_ACTIONS_ON_GROUP = new HashSet<>( + Arrays.asList(RODAMemberAction.EDIT, RODAMemberAction.EDIT_PERMISSIONS, RODAMemberAction.REMOVE, + RODAMemberAction.ADD_NEW_MEMBER)); + + private static final RODAMemberToolbarActions GENERAL_INSTANCE = new RODAMemberToolbarActions(); + + private RODAMemberToolbarActions() { + } + + public static RODAMemberToolbarActions get() { + return GENERAL_INSTANCE; + } + + @Override + public RODAMemberAction[] getActions() { + return RODAMemberAction.values(); + } + + @Override + public RODAMemberAction actionForName(String name) { + return RODAMemberAction.valueOf(name); + } + + @Override + public CanActResult userCanAct(Action action, RODAMember object) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult contextCanAct(Action action, RODAMember object) { + if (object.isUser()) { + return new CanActResult( + (action.equals(RODAMemberAction.DEACTIVATE) && object.isActive()) + || (action.equals(RODAMemberAction.ACTIVATE) && !object.isActive()) + || (action.equals(RODAMemberAction.REMOVE) + || (action.equals(RODAMemberAction.EDIT) || action.equals(RODAMemberAction.EDIT_PERMISSIONS) + || (action.equals(RODAMemberAction.ADD_NEW_GROUP) && object.isUser()) + || (action.equals(RODAMemberAction.NEW_ACCESS_KEY) && object.isUser()))), + CanActResult.Reason.CONTEXT, messages.reasonCantActOnUser()); + } else { + return new CanActResult(POSSIBLE_ACTIONS_ON_GROUP.contains(action), CanActResult.Reason.CONTEXT, + messages.reasonCantActOnGroup()); + } + } + + @Override + public void act(Action action, RODAMember object, AsyncCallback callback) { + if (action.equals(RODAMemberAction.ACTIVATE)) { + activate(object, callback); + } else if (action.equals(RODAMemberAction.DEACTIVATE)) { + deactivate(object, callback); + } else if (action.equals(RODAMemberAction.EDIT)) { + edit(object, callback); + } else if (action.equals(RODAMemberAction.REMOVE)) { + remove(object, callback); + } else if (action.equals(RODAMemberAction.NEW_ACCESS_KEY)) { + createNewAccessKey(callback, object); + } else if (action.equals(RODAMemberAction.ADD_NEW_GROUP)) { + addNewGroup(object, callback); + } else if (action.equals(RODAMemberAction.ADD_NEW_MEMBER)) { + addNewMember(object, callback); + } else if (action.equals(RODAMemberAction.EDIT_PERMISSIONS)) { + editPermissions(object, callback); + } else { + unsupportedAction(action, callback); + } + } + + private void editPermissions(RODAMember object, AsyncCallback callback) { + PermissionsPanel permissionsPanel = new PermissionsPanel(object, false, true); + + RODAMembersDialogs.showEditRODAMemberPermissionsPanel(messages.editPermissionsModalTitle(), messages.cancelButton(), + messages.updateButton(), permissionsPanel, new ActionAsyncCallback>(callback) { + @Override + public void onSuccess(List result) { + Services services = new Services("Edit RODA member permissions", "update"); + if (object.isUser()) { + UpdateUserRequest request = new UpdateUserRequest(); + User user = (User) object; + user.setDirectRoles(new HashSet<>(result)); + request.setPassword(null); + request.setUser(user); + services.membersResource(s -> s.updateUser(request)).whenComplete((res, error) -> { + if (error != null) { + doActionCallbackUpdated(); + Toast.showError(error.getMessage()); + } else { + doActionCallbackUpdated(); + Toast.showInfo(messages.userPermissions(), messages.permissionsUpdateWithSuccess()); + } + }); + } else { + Group group = (Group) object; + group.setDirectRoles(new HashSet<>(result)); + services.membersResource(s -> s.updateGroup(group)).whenComplete((res, error) -> { + if (error != null) { + doActionCallbackUpdated(); + Toast.showError(error.getMessage()); + } else { + doActionCallbackUpdated(); + Toast.showInfo(messages.groupPermissions(), messages.permissionsUpdateWithSuccess()); + } + }); + } + } + }); + } + + private void addNewMember(RODAMember object, AsyncCallback callback) { + Filter filter = new Filter(new BasicSearchFilterParameter(RodaConstants.MEMBERS_IS_USER, "true")); + ((Group) object).getUsers() + .forEach(user -> filter.add(new NotSimpleFilterParameter(RodaConstants.MEMBERS_NAME, user))); + + RODAMembersDialogs.showAddGroupsToRODAMember(SafeHtmlUtils.fromSafeConstant(messages.addNewMemberToGroupTitle()), + messages.cancelButton(), messages.confirmButton(), filter, + new ActionAsyncCallback>(callback) { + @Override + public void onSuccess(SelectedItems result) { + Services services = new Services("Add member to RODA group", "update"); + services + .membersResource(s -> s.addMembersToGroup(object.getId(), SelectedItemsUtils.convertToRESTRequest(result))) + .whenComplete((group, error) -> { + if (error != null) { + doActionCallbackUpdated(); + Toast.showError(error.getMessage()); + } else { + doActionCallbackUpdated(); + Toast.showInfo(messages.membersTabTitle(), messages.memberSuccessfullyAdded()); + } + }); + } + }); + } + + private void addNewGroup(RODAMember object, AsyncCallback callback) { + Filter filter = new Filter(new BasicSearchFilterParameter(RodaConstants.MEMBERS_IS_USER, "false")); + ((User) object).getGroups() + .forEach(group -> filter.add(new NotSimpleFilterParameter(RodaConstants.MEMBERS_NAME, group))); + + RODAMembersDialogs.showAddGroupsToRODAMember(SafeHtmlUtils.fromSafeConstant(messages.addNewGroupModalTitle()), + messages.cancelButton(), messages.confirmButton(), filter, + new ActionAsyncCallback>(callback) { + @Override + public void onSuccess(SelectedItems result) { + Services services = new Services("Add groups to RODA member", "update"); + services + .membersResource(s -> s.addGroupsToUser(object.getId(), SelectedItemsUtils.convertToRESTRequest(result))) + .whenComplete((res, error) -> { + if (error != null) { + doActionCallbackUpdated(); + Toast.showError(error.getMessage()); + } else { + doActionCallbackUpdated(); + Toast.showInfo(messages.groups(), messages.groupSuccessfullyAdded()); + } + }); + } + }); + } + + private void activate(RODAMember object, AsyncCallback callback) { + Dialogs.showConfirmDialog(messages.activateUserTitle(), messages.activateUserConfirmationMessage(), + messages.dialogNo(), messages.dialogYes(), new ActionAsyncCallback(callback) { + @Override + public void onSuccess(Boolean confirmed) { + if (confirmed) { + Services services = new Services("Activate RODA member", "activate"); + ChangeUserStatusRequest request = new ChangeUserStatusRequest( + SelectedItemsUtils.convertToRESTRequest(objectToSelectedItems(object, RODAMember.class)), true); + services.membersResource(s -> s.changeActive(request)).whenComplete((res, error) -> { + if (error == null) { + Toast.showInfo(messages.runningInBackgroundTitle(), messages.runningInBackgroundDescription()); + Dialogs.showJobRedirectDialog(messages.jobCreatedMessage(), new AsyncCallback() { + + @Override + public void onFailure(Throwable caught) { + doActionCallbackUpdated(); + } + + @Override + public void onSuccess(final Void nothing) { + callback.onSuccess(ActionImpact.NONE); + HistoryUtils.newHistory(ShowJob.RESOLVER, res.getId()); + } + }); + } + }); + } else { + doActionCallbackNone(); + } + } + + @Override + public void onFailure(Throwable caught) { + callback.onFailure(caught); + } + }); + } + + private void edit(RODAMember object, AsyncCallback callback) { + if (object.isUser()) { + RODAMembersDialogs.editUserInformation(messages.editUserTitle(), messages.cancelButton(), messages.updateButton(), + (User) object, new ActionAsyncCallback(callback) { + @Override + public void onSuccess(UpdateUserRequest result) { + Services services = new Services("Update User", "update"); + services.membersResource(s -> s.updateUser(result)).whenComplete((res, error) -> { + if (error == null) { + Toast.showInfo(messages.users(), "User successfully updated"); + doActionCallbackUpdated(); + } else { + doActionCallbackNone(); + } + }); + } + }); + } else { + RODAMembersDialogs.editGroupInformation(messages.editGroupTitle(), messages.cancelButton(), + messages.updateButton(), (Group) object, new ActionAsyncCallback(callback) { + @Override + public void onSuccess(Group result) { + Services services = new Services("Update group", "update"); + services.membersResource(s -> s.updateGroup(result)).whenComplete((res, error) -> { + if (error == null) { + Toast.showInfo(messages.groups(), "Group successfully updated"); + doActionCallbackUpdated(); + } else { + doActionCallbackNone(); + } + }); + } + }); + } + } + + private void deactivate(RODAMember object, AsyncCallback callback) { + Dialogs.showConfirmDialog(messages.deactivateUserTitle(), messages.deactivateUserConfirmationMessage(), + messages.dialogNo(), messages.dialogYes(), new ActionAsyncCallback(callback) { + @Override + public void onSuccess(Boolean confirmed) { + if (confirmed) { + Services services = new Services("Deactivate RODA member", "deactivate"); + ChangeUserStatusRequest request = new ChangeUserStatusRequest( + SelectedItemsUtils.convertToRESTRequest(objectToSelectedItems(object, RODAMember.class)), false); + services.membersResource(s -> s.changeActive(request)).whenComplete((res, error) -> { + if (error == null) { + Toast.showInfo(messages.runningInBackgroundTitle(), messages.runningInBackgroundDescription()); + Dialogs.showJobRedirectDialog(messages.jobCreatedMessage(), new AsyncCallback() { + + @Override + public void onFailure(Throwable caught) { + doActionCallbackUpdated(); + } + + @Override + public void onSuccess(final Void nothing) { + callback.onSuccess(ActionImpact.NONE); + HistoryUtils.newHistory(ShowJob.RESOLVER, res.getId()); + } + }); + } + }); + } else { + doActionCallbackNone(); + } + } + + @Override + public void onFailure(Throwable caught) { + doActionCallbackNone(); + callback.onFailure(caught); + } + }); + } + + private void remove(RODAMember object, AsyncCallback callback) { + Dialogs.showConfirmDialog(object.isUser() ? messages.singleUserRemoveConfirmDialogTitle() : messages.singleGroupRemoveConfirmDialogTitle(), + object.isUser() ? messages.singleUserRemoveConfirmDialogMessage(object.getId()) : messages.singleGroupRemoveConfirmDialogMessage(object.getId()), messages.dialogNo(), messages.dialogYes(), + new ActionAsyncCallback(callback) { + @Override + public void onSuccess(Boolean confirmed) { + if (confirmed) { + Services services = new Services("Remove RODA members", "remove"); + services + .membersResource(s -> s.deleteMultipleMembers( + SelectedItemsUtils.convertToRESTRequest(objectToSelectedItems(object, RODAMember.class)))) + .whenComplete((res, error) -> { + if (error == null) { + callback.onSuccess(Actionable.ActionImpact.DESTROYED); + } else { + Toast.showError(error.getMessage()); + callback.onSuccess(Actionable.ActionImpact.NONE); + } + }); + } else { + doActionCallbackNone(); + } + } + + @Override + public void onFailure(Throwable caught) { + callback.onFailure(caught); + } + }); + } + + private void createNewAccessKey(AsyncCallback callback, RODAMember user) { + callback.onSuccess(ActionImpact.NONE); + AccessKeyDialogs.createAccessKeyDialog(messages.createAccessKeyTitle(), null, true, + new ActionNoAsyncCallback(callback) { + @Override + public void onSuccess(CreateAccessKeyRequest keyRequest) { + Services services = new Services("Create access key", "create"); + CreateAccessKeyRequest createAccessKeyRequest = new CreateAccessKeyRequest(keyRequest.getName(), + keyRequest.getExpirationDate()); + services.membersResource(s -> s.createAccessKey(user.getId(), createAccessKeyRequest)) + .whenComplete((response, error) -> { + if (response != null) { + AccessKeyDialogs.showAccessKeyDialog(messages.accessKeyLabel(), response, + new NoAsyncCallback() { + @Override + public void onSuccess(Boolean result) { + callback.onSuccess(ActionImpact.UPDATED); + } + }); + } + }); + } + }); + } + + @Override + public ActionableBundle createActionsBundle() { + ActionableBundle actionableBundle = new ActionableBundle<>(); + + // ACTIONS + ActionableGroup actionableGroup = new ActionableGroup<>(messages.manage(), "btn-edit"); + actionableGroup.addButton(messages.editUserAction(), RODAMemberAction.EDIT, ActionImpact.UPDATED, "btn-edit"); + + actionableGroup.addButton(messages.editUserActivate(), RODAMemberAction.ACTIVATE, ActionImpact.UPDATED, + "btn-enable-user"); + actionableGroup.addButton(messages.editUserDeactivate(), RODAMemberAction.DEACTIVATE, ActionImpact.UPDATED, + "btn-disable-user"); + actionableGroup.addButton(messages.addAccessKeyButton(), RODAMemberAction.NEW_ACCESS_KEY, ActionImpact.UPDATED, + "btn-key"); + actionableGroup.addButton(messages.addToGroupButton(), RODAMemberAction.ADD_NEW_GROUP, ActionImpact.UPDATED, + "btn-group"); + actionableGroup.addButton(messages.addNewMemberToGroupButton(), RODAMemberAction.ADD_NEW_MEMBER, + ActionImpact.UPDATED, "btn-user"); + actionableGroup.addButton(messages.editButton(), RODAMemberAction.EDIT_PERMISSIONS, ActionImpact.UPDATED, + "btn-edit"); + actionableGroup.addButton(messages.editUserRemove(), RODAMemberAction.REMOVE, ActionImpact.DESTROYED, "btn-ban"); + + actionableBundle.addGroup(actionableGroup); + return actionableBundle; + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/callbacks/ActionNoAsyncCallback.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/callbacks/ActionNoAsyncCallback.java index 803cd149a5..20e9a62bae 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/callbacks/ActionNoAsyncCallback.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/callbacks/ActionNoAsyncCallback.java @@ -11,7 +11,6 @@ import org.roda.wui.client.common.actions.Actionable; import com.google.gwt.user.client.rpc.AsyncCallback; -import org.roda.wui.client.services.Services; /** * @author Bruno Ferreira diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/AccessKeyDialogs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/AccessKeyDialogs.java index f3f40d37ef..1fafe165fe 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/AccessKeyDialogs.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/AccessKeyDialogs.java @@ -8,10 +8,12 @@ package org.roda.wui.client.common.dialogs; import com.google.gwt.i18n.client.DateTimeFormat; -import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.datepicker.client.DateBox; import org.roda.core.data.v2.accessKey.AccessKey; +import org.roda.core.data.v2.accessKey.CreateAccessKeyRequest; import org.roda.wui.client.common.utils.JavascriptUtils; +import org.roda.wui.common.client.tools.StringUtils; import org.roda.wui.common.client.widgets.Toast; import com.google.gwt.core.client.GWT; @@ -100,17 +102,30 @@ public void onClick(ClickEvent clickEvent) { dialogBox.show(); } - public static void showRegenerateAccessKeyDialog(String title, final AsyncCallback callback) { + public static void createAccessKeyDialog(String title, String tokenName, boolean create, + final AsyncCallback callback) { final DialogBox dialogBox = new DialogBox(false, true); final Button cancelButton = new Button(messages.cancelButton()); final Button confirmButton = new Button(messages.confirmButton()); final FlowPanel layout = new FlowPanel(); final FlowPanel header = new FlowPanel(); final FlowPanel footer = new FlowPanel(); - final Label expirationDateLabel = new Label("Expiration date"); + + final Label tokenNameLabel = new Label(messages.accessKeyNameLabel()); + final TextBox tokenNameTextBox = new TextBox(); + final Label tokenNameTextBoxErrorLabel = new Label(messages.mandatoryField()); + + if (!create) { + tokenNameTextBox.setText(tokenName); + tokenNameTextBox.setEnabled(false); + } + + final Label expirationDateLabel = new Label(messages.accessKeyExpirationDateLabel()); final DateBox expirationDateBox = new DateBox(); - final Label expirationDateErrorLabel = new Label("Invalid Date"); + final Label expirationDateErrorLabel = new Label(); + tokenNameTextBoxErrorLabel.addStyleName("form-label-error"); + tokenNameTextBoxErrorLabel.setVisible(false); expirationDateErrorLabel.addStyleName("form-label-error"); expirationDateErrorLabel.setVisible(false); @@ -124,21 +139,51 @@ public static void showRegenerateAccessKeyDialog(String title, final AsyncCallba dialogBox.setText(title); layout.add(header); layout.add(footer); + + header.add(tokenNameLabel); + header.add(tokenNameTextBox); + header.add(tokenNameTextBoxErrorLabel); + header.add(expirationDateLabel); header.add(expirationDateBox); header.add(expirationDateErrorLabel); + footer.add(cancelButton); footer.add(confirmButton); confirmButton.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent clickEvent) { + boolean errors = false; + String name = tokenNameTextBox.getText(); + if (create && (name == null || StringUtils.isBlank(name))) { + tokenNameTextBoxErrorLabel.setVisible(true); + errors = true; + } else { + tokenNameTextBoxErrorLabel.setVisible(false); + } + Date selectedDate = expirationDateBox.getValue(); - if (selectedDate == null || selectedDate.before(new Date())) { + if (selectedDate == null) { expirationDateErrorLabel.setVisible(true); + expirationDateErrorLabel.setText(messages.mandatoryField()); + errors = true; + } else if (selectedDate.before(new Date())) { + expirationDateErrorLabel.setVisible(true); + expirationDateErrorLabel.setText(messages.accessKeyExpirationDateInThePast()); + errors = true; } else { + expirationDateErrorLabel.setVisible(false); + } + + if (!errors) { dialogBox.hide(); - callback.onSuccess(selectedDate); + CreateAccessKeyRequest request = new CreateAccessKeyRequest(); + if (create) { + request.setName(tokenNameTextBox.getText()); + } + request.setExpirationDate(selectedDate); + callback.onSuccess(request); } } }); @@ -160,6 +205,8 @@ public void onClick(ClickEvent clickEvent) { header.addStyleName("wui-dialog-message"); footer.addStyleName("wui-dialog-layout-footer"); + tokenNameLabel.addStyleName("form-label"); + tokenNameTextBox.addStyleName("form-textbox form-textbox-small"); expirationDateLabel.addStyleName("form-label"); expirationDateBox.addStyleName("form-textbox form-textbox-small"); confirmButton.addStyleName("btn btn-play"); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/Dialogs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/Dialogs.java index 0f9fd7525f..56f803cb60 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/Dialogs.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/Dialogs.java @@ -7,11 +7,9 @@ */ package org.roda.wui.client.common.dialogs; -import org.roda.core.data.v2.ip.IndexedFile; import org.roda.wui.client.common.NoAsyncCallback; import org.roda.wui.client.common.search.SearchSuggestBox; import org.roda.wui.client.common.utils.JavascriptUtils; -import org.roda.wui.common.client.tools.RestUtils; import org.roda.wui.common.client.tools.StringUtils; import org.roda.wui.common.client.widgets.Toast; @@ -22,8 +20,6 @@ import com.google.gwt.regexp.shared.RegExp; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlUtils; -import com.google.gwt.safehtml.shared.SafeUri; -import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.DialogBox; @@ -140,56 +136,6 @@ public static void showInformationDialog(String title, final String message, Str showInformationDialog(title, message, continueButtonText, canCopyMessage, new NoAsyncCallback<>()); } - public static void showTechnicalMetadataInformation(String title, String downloadText, String closeText, - IndexedFile file, String html) { - final DialogBox dialogBox = new ClosableDialog(true, true); - FlowPanel main = new FlowPanel(); - ScrollPanel layout = new ScrollPanel(); - FlowPanel footer = new FlowPanel(); - final Button downloadButton = new Button(downloadText); - final Button closeButton = new Button(closeText); - layout.setSize("70vw", "60vh"); - - VerticalPanel verticalPanel = new VerticalPanel(); - verticalPanel.setWidth("100%"); - if (html == null) { - html = "

No technical metadata found. Please contact system administrator

"; - } - HTML keyHtml = new HTML(html); - keyHtml.setStyleName("value-overflow"); - verticalPanel.add(keyHtml); - - layout.add(verticalPanel); - layout.addStyleName("wui-dialog-message"); - - footer.add(closeButton); - footer.add(downloadButton); - footer.addStyleName("wui-dialog-layout-footer"); - - downloadButton.addStyleName("btn btn-download"); - closeButton.addStyleName("btn btn-link"); - main.addStyleName("wui-dialog-layout"); - main.add(layout); - main.add(footer); - - closeButton.addClickHandler(event -> { - dialogBox.hide(); - }); - - downloadButton.addClickHandler(event -> { - SafeUri downloadUri = RestUtils.createTechnicalMetadataDownloadUri(file.getUUID()); - Window.Location.assign(downloadUri.asString()); - }); - - dialogBox.setWidget(main); - dialogBox.setText(title); - dialogBox.addStyleName("wui-dialog-information"); - dialogBox.setGlassEnabled(true); - dialogBox.setAnimationEnabled(false); - dialogBox.center(); - dialogBox.show(); - } - public static void showInformationDialog(String title, final String message, String continueButtonText, boolean canCopyMessage, final AsyncCallback callback) { final DialogBox dialogBox = new DialogBox(false, true); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/MemberSelectDialog.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/MemberSelectDialog.java index 8287ff6df0..afe832809e 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/MemberSelectDialog.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/MemberSelectDialog.java @@ -18,6 +18,6 @@ public MemberSelectDialog(String title, Filter filter) { super(title, new ListBuilder<>(() -> new RodaMemberList(), new AsyncTableCellOptions<>(RODAMember.class, "MemberSelectDialog_rodaMembers").withFilter(filter) - .withSummary(title))); + .withCsvDownloadButtonVisibility(false).withSummary(title))); } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/RODAMembersDialogs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/RODAMembersDialogs.java new file mode 100644 index 0000000000..470f484abc --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/RODAMembersDialogs.java @@ -0,0 +1,445 @@ +package org.roda.wui.client.common.dialogs; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.safehtml.shared.SafeHtml; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.DialogBox; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.Widget; +import config.i18n.client.ClientMessages; +import org.roda.core.data.common.SecureString; +import org.roda.core.data.v2.index.filter.Filter; +import org.roda.core.data.v2.index.select.SelectedItems; +import org.roda.core.data.v2.index.select.SelectedItemsList; +import org.roda.core.data.v2.index.select.SelectedItemsNone; +import org.roda.core.data.v2.user.Group; +import org.roda.core.data.v2.user.RODAMember; +import org.roda.core.data.v2.user.User; +import org.roda.core.data.v2.user.requests.CreateGroupRequest; +import org.roda.core.data.v2.user.requests.CreateUserRequest; +import org.roda.core.data.v2.user.requests.UpdateUserRequest; +import org.roda.wui.client.common.lists.RodaMemberList; +import org.roda.wui.client.common.lists.utils.AsyncTableCell; +import org.roda.wui.client.common.lists.utils.AsyncTableCellOptions; +import org.roda.wui.client.common.lists.utils.ListBuilder; +import org.roda.wui.client.common.search.SearchWrapper; +import org.roda.wui.client.management.members.CreateUserPanel; +import org.roda.wui.client.management.members.GroupDataPanel; +import org.roda.wui.client.management.members.UserDataPanel; +import org.roda.wui.client.management.members.tabs.PermissionsPanel; + +import java.util.HashSet; +import java.util.List; + +/** + * @author Miguel Guimarães + */ + +public class RODAMembersDialogs { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + + private RODAMembersDialogs() { + // private method + } + + public static void createGroup(final String title, final String cancelButtonText, final String saveButtonText, + final AsyncCallback callback) { + final DialogBox dialogBox = new DialogBox(false, true); + dialogBox.setHTML(title); + dialogBox.addStyleName("create-group-dialog"); + + final FlowPanel layout = new FlowPanel(); + + dialogBox.addStyleName("wui-dialog-prompt"); + layout.addStyleName("wui-dialog-layout"); + + final FlowPanel buttonPanel = new FlowPanel(); + final Button cancelButton = new Button(cancelButtonText); + final Button saveButton = new Button(saveButtonText); + buttonPanel.add(cancelButton); + buttonPanel.add(saveButton); + + final FlowPanel content = new FlowPanel(); + GroupDataPanel groupDataPanel = new GroupDataPanel(false); + + content.add(groupDataPanel); + layout.add(content); + layout.add(buttonPanel); + dialogBox.setWidget(layout); + + dialogBox.setGlassEnabled(true); + dialogBox.setAnimationEnabled(false); + + cancelButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + dialogBox.hide(); + callback.onFailure(null); + } + }); + + saveButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + if (!groupDataPanel.isChanged()) { + dialogBox.hide(); + callback.onFailure(null); + } else { + if (groupDataPanel.isValid()) { + dialogBox.hide(); + Group group = groupDataPanel.getGroup(); + CreateGroupRequest createGroupRequest = new CreateGroupRequest(group.getName(), group.getFullName(), + new HashSet<>()); + callback.onSuccess(createGroupRequest); + } + } + } + }); + + cancelButton.addStyleName("btn btn-link"); + saveButton.addStyleName("pull-right btn btn-play"); + + dialogBox.center(); + dialogBox.show(); + } + + public static void createUser(final String title, final String cancelButtonText, final String saveButtonText, + final AsyncCallback callback) { + final DialogBox dialogBox = new DialogBox(false, true); + dialogBox.setHTML(title); + dialogBox.addStyleName("create-user-dialog"); + + final FlowPanel layout = new FlowPanel(); + + dialogBox.addStyleName("wui-dialog-prompt"); + layout.addStyleName("wui-dialog-layout"); + + final FlowPanel buttonPanel = new FlowPanel(); + final Button cancelButton = new Button(cancelButtonText); + final Button saveButton = new Button(saveButtonText); + buttonPanel.add(cancelButton); + buttonPanel.add(saveButton); + + final FlowPanel content = new FlowPanel(); + CreateUserPanel userPanel = new CreateUserPanel(); + + userPanel.setOnFormReadyCallback(new Runnable() { + @Override + public void run() { + dialogBox.center(); + } + }); + + content.add(userPanel); + layout.add(content); + layout.add(buttonPanel); + dialogBox.setWidget(layout); + + dialogBox.setGlassEnabled(true); + dialogBox.setAnimationEnabled(false); + + cancelButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + dialogBox.hide(); + callback.onFailure(null); + } + }); + + saveButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + if (!userPanel.isChanged()) { + dialogBox.hide(); + callback.onFailure(null); + } else { + if (userPanel.isValid()) { + dialogBox.hide(); + User user = userPanel.getUser(); + CreateUserRequest request = new CreateUserRequest(user.getEmail(), user.getName(), user.getFullName(), + new HashSet<>(), new HashSet<>(), user.isGuest(), null, userPanel.getUserExtra()); + callback.onSuccess(request); + } + } + } + }); + + cancelButton.addStyleName("btn btn-link"); + saveButton.addStyleName("pull-right btn btn-play"); + + dialogBox.center(); + dialogBox.show(); + } + + public static void editGroupInformation(final String title, final String cancelButtonText, final String saveButtonText, + Group group, final AsyncCallback callback) { + final DialogBox dialogBox = new DialogBox(false, true); + dialogBox.setHTML(title); + dialogBox.addStyleName("edit-group-information-dialog"); + + final FlowPanel layout = new FlowPanel(); + + dialogBox.addStyleName("wui-dialog-prompt"); + layout.addStyleName("wui-dialog-layout"); + + final FlowPanel buttonPanel = new FlowPanel(); + final Button cancelButton = new Button(cancelButtonText); + final Button saveButton = new Button(saveButtonText); + buttonPanel.add(cancelButton); + buttonPanel.add(saveButton); + + final FlowPanel content = new FlowPanel(); + GroupDataPanel groupPanel = new GroupDataPanel(true); + groupPanel.setGroupNameReadOnly(true); + groupPanel.setGroup(group); + + content.add(groupPanel); + layout.add(content); + layout.add(buttonPanel); + dialogBox.setWidget(layout); + + dialogBox.setGlassEnabled(true); + dialogBox.setAnimationEnabled(false); + + cancelButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + dialogBox.hide(); + callback.onFailure(null); + } + }); + + saveButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + if (!groupPanel.isChanged()) { + dialogBox.hide(); + callback.onFailure(null); + } else { + if (groupPanel.isValid()) { + dialogBox.hide(); + callback.onSuccess(groupPanel.getGroup()); + } + } + } + }); + + cancelButton.addStyleName("btn btn-link"); + saveButton.addStyleName("pull-right btn btn-play"); + + dialogBox.center(); + dialogBox.show(); + } + + public static void editUserInformation(final String title, final String cancelButtonText, final String saveButtonText, + User user, final AsyncCallback callback) { + final DialogBox dialogBox = new DialogBox(false, true); + dialogBox.setHTML(title); + dialogBox.addStyleName("edit-user-information-dialog"); + + final FlowPanel layout = new FlowPanel(); + + dialogBox.addStyleName("wui-dialog-prompt"); + layout.addStyleName("wui-dialog-layout"); + + final FlowPanel buttonPanel = new FlowPanel(); + final Button cancelButton = new Button(cancelButtonText); + final Button saveButton = new Button(saveButtonText); + buttonPanel.add(cancelButton); + buttonPanel.add(saveButton); + + final FlowPanel content = new FlowPanel(); + UserDataPanel userPanel = new UserDataPanel(true, true); + userPanel.setUsernameReadOnly(true); + userPanel.setUser(user); + userPanel.setUserExtra(user.getExtra()); + + content.add(userPanel); + layout.add(content); + layout.add(buttonPanel); + dialogBox.setWidget(layout); + + dialogBox.setGlassEnabled(true); + dialogBox.setAnimationEnabled(false); + + cancelButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + dialogBox.hide(); + callback.onFailure(null); + } + }); + + saveButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + if (!userPanel.isChanged()) { + dialogBox.hide(); + callback.onFailure(null); + } else { + if (userPanel.isValid()) { + dialogBox.hide(); + UpdateUserRequest request = new UpdateUserRequest(); + request.setUser(userPanel.getUser()); + request.getUser().setDirectRoles(user.getDirectRoles()); + request.getUser().setGroups(user.getGroups()); + if (userPanel.isPasswordChanged()) { + SecureString securePassword = new SecureString(userPanel.getPassword().toCharArray()); + request.setPassword(securePassword); + } else { + request.setPassword(null); + } + request.setValues(userPanel.getUserExtra()); + callback.onSuccess(request); + } + } + } + }); + + cancelButton.addStyleName("btn btn-link"); + saveButton.addStyleName("pull-right btn btn-play"); + + dialogBox.center(); + dialogBox.show(); + } + + public static void showEditRODAMemberPermissionsPanel(final String title, final String cancelButtonText, + final String saveButtonText, Widget panel, final AsyncCallback> callback) { + final DialogBox dialogBox = new DialogBox(false, true); + dialogBox.setHTML(title); + dialogBox.addStyleName("edit-permissions-dialog"); + + if (panel instanceof PermissionsPanel) { + ((PermissionsPanel) panel).setOnDataLoadedCallback(new Runnable() { + @Override + public void run() { + dialogBox.center(); + } + }); + } + + final FlowPanel layout = new FlowPanel(); + + dialogBox.addStyleName("wui-dialog-prompt"); + layout.addStyleName("wui-dialog-layout"); + + final FlowPanel buttonPanel = new FlowPanel(); + buttonPanel.addStyleName("dialog-button-panel"); + final Button cancelButton = new Button(cancelButtonText); + final Button saveButton = new Button(saveButtonText); + buttonPanel.add(cancelButton); + buttonPanel.add(saveButton); + + final FlowPanel content = new FlowPanel(); + content.addStyleName("content"); + content.add(panel); + layout.add(content); + layout.add(buttonPanel); + dialogBox.setWidget(layout); + + dialogBox.setGlassEnabled(true); + dialogBox.setAnimationEnabled(false); + + cancelButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + dialogBox.hide(); + callback.onFailure(null); + } + }); + + saveButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + dialogBox.hide(); + callback.onSuccess(((PermissionsPanel) panel).getUserSelections()); + } + }); + + cancelButton.addStyleName("btn btn-link"); + saveButton.addStyleName("pull-right btn btn-play"); + + dialogBox.center(); + dialogBox.show(); + } + + public static void showAddGroupsToRODAMember(SafeHtml title, final String cancelButtonText, + final String confirmButtonText, Filter filter, final AsyncCallback> callback) { + + final DialogBox dialogBox = new DialogBox(false, true); + + dialogBox.addStyleName("ri-dialog add-groups-to-user-dialog"); + dialogBox.setHTML(title); + final FlowPanel layout = new FlowPanel(); + + dialogBox.addStyleName("wui-dialog-prompt"); + layout.addStyleName("wui-dialog-layout"); + + final FlowPanel buttonPanel = new FlowPanel(); + final Button cancelButton = new Button(cancelButtonText); + final Button confirmButton = new Button(confirmButtonText); + confirmButton.setEnabled(false); + buttonPanel.add(cancelButton); + buttonPanel.add(confirmButton); + + final FlowPanel content = new FlowPanel(); + content.addStyleName("row skip_padding full_width content"); + content.add(createInnerAddGroupList(dialogBox, confirmButton, filter, callback)); + layout.add(content); + layout.add(buttonPanel); + dialogBox.setWidget(layout); + + dialogBox.setGlassEnabled(true); + dialogBox.setAnimationEnabled(false); + + cancelButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + dialogBox.hide(); + callback.onFailure(null); + } + }); + + cancelButton.addStyleName("btn btn-link"); + confirmButton.addStyleName("pull-right btn btn-play"); + + dialogBox.center(); + dialogBox.show(); + } + + public static FlowPanel createInnerAddGroupList(final DialogBox dialogBox, final Button addGroupButton, + final Filter filter, final AsyncCallback> callback) { + FlowPanel container = new FlowPanel(); + container.addStyleName("wui-dialog-message"); + + // create search box and results list + + ListBuilder rodaMemberListBuilder = new ListBuilder<>(() -> new RodaMemberList(), + new AsyncTableCellOptions<>(RODAMember.class, "RepresentationInformationDialogs_RI") + .withSummary(messages.representationInformationTitle()).withInitialPageSize(10).withPageSizeIncrement(10) + .withCsvDownloadButtonVisibility(false).withRecenteringOfParentDialog(dialogBox).withForceSelectable(true) + .withFilter(filter).addCheckboxSelectionListener(new AsyncTableCell.CheckboxSelectionListener() { + @Override + public void onSelectionChange(SelectedItems selected) { + addGroupButton.setEnabled(!(selected instanceof SelectedItemsNone) + && (!(selected instanceof SelectedItemsList) || !((SelectedItemsList) selected).getIds().isEmpty())); + } + })); + + SearchWrapper searchWrapper = new SearchWrapper(false).withListsInsideScrollPanel("ri-dialog-list-scroll") + .createListAndSearchPanel(rodaMemberListBuilder); + + container.add(searchWrapper); + + addGroupButton.addClickHandler(event -> { + dialogBox.hide(); + callback.onSuccess(searchWrapper.getSelectedItems(RODAMember.class)); + }); + + return container; + } + +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/RodaMemberList.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/RodaMemberList.java index cff9cdec48..c97eac9bbb 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/RodaMemberList.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/RodaMemberList.java @@ -34,20 +34,18 @@ public class RodaMemberList extends AsyncTableCell { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static final List fieldsToReturn = Arrays.asList(RodaConstants.INDEX_UUID, RodaConstants.MEMBERS_ID, + RodaConstants.MEMBERS_IS_USER, RodaConstants.MEMBERS_NAME, RodaConstants.MEMBERS_FULLNAME, + RodaConstants.MEMBERS_GROUPS, RodaConstants.MEMBERS_IS_ACTIVE); @SuppressWarnings("unused") private final ClientLogger logger = new ClientLogger(getClass().getName()); - private static final ClientMessages messages = GWT.create(ClientMessages.class); - private Column activeColumn; private Column typeColumn; private TextColumn nameColumn; private TextColumn fullNameColumn; private TextColumn groupsColumn; - private static final List fieldsToReturn = Arrays.asList(RodaConstants.INDEX_UUID, RodaConstants.MEMBERS_ID, - RodaConstants.MEMBERS_IS_USER, RodaConstants.MEMBERS_NAME, RodaConstants.MEMBERS_FULLNAME, - RodaConstants.MEMBERS_GROUPS, RodaConstants.MEMBERS_IS_ACTIVE); - @Override protected void adjustOptions(AsyncTableCellOptions options) { options.withFieldsToReturn(fieldsToReturn); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/ActionMenuCell.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/ActionMenuCell.java new file mode 100644 index 0000000000..2bd5d8361d --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/ActionMenuCell.java @@ -0,0 +1,54 @@ +package org.roda.wui.client.common.lists.utils; + +import com.google.gwt.cell.client.AbstractCell; +import com.google.gwt.cell.client.ValueUpdater; +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.NativeEvent; +import com.google.gwt.safehtml.shared.SafeHtmlBuilder; +import config.i18n.client.ClientMessages; + +/** + * + * @author Miguel Guimarães + */ +public class ActionMenuCell extends AbstractCell { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private final Delegate delegate; + + public ActionMenuCell(Delegate delegate) { + super("click"); + this.delegate = delegate; + } + + @Override + public void render(Context context, T value, SafeHtmlBuilder sb) { + // Render a button that looks like a dropdown trigger + sb.appendHtmlConstant("
"); + sb.appendHtmlConstant("
"); + sb.appendHtmlConstant( + ""); + sb.appendHtmlConstant("
"); + sb.appendHtmlConstant("
"); + } + + @Override + public void onBrowserEvent(Context context, Element parent, T value, NativeEvent event, + ValueUpdater valueUpdater) { + if ("click".equals(event.getType())) { + // Get position of the clicked element + int left = parent.getAbsoluteLeft(); + int top = parent.getAbsoluteTop() + parent.getOffsetHeight(); + + if (delegate != null) { + delegate.onShowMenu(value, left, top); + } + } + } + + public interface Delegate { + void onShowMenu(T value, int left, int top); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/BasicTablePanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/BasicTablePanel.java index b5b667cbd8..bd183413ab 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/BasicTablePanel.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/BasicTablePanel.java @@ -148,6 +148,35 @@ public void setVisible(boolean visible) { handleScrollChanges(); } + public void removeSelectionModel() { + if (selectionModel != null) { + display.setSelectionModel(null); + } + } + + /** + * Updates the table with new rows, clearing the previous data. + * * @param newRowItems An Iterator containing the new data to display. + */ + public void updateData(Iterator newRowItems) { + if (dataProvider != null) { + // Get the list managed by the provider + List list = dataProvider.getList(); + + // Clear existing data + list.clear(); + + // Add new data + while (newRowItems.hasNext()) { + list.add(newRowItems.next()); + } + + // Note: You do not need to call refresh(). + // Modifying the list returned by dataProvider.getList() + // automatically pushes the changes to the CellTable. + } + } + public void handleScrollChanges() { if (displayScroll.getMaximumHorizontalScrollPosition() > 0) { double percent = displayScroll.getHorizontalScrollPosition() * 100F diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/SimpleActionCell.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/SimpleActionCell.java new file mode 100644 index 0000000000..4b1d114416 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/SimpleActionCell.java @@ -0,0 +1,58 @@ +package org.roda.wui.client.common.lists.utils; + +import com.google.gwt.cell.client.AbstractCell; +import com.google.gwt.cell.client.ValueUpdater; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.NativeEvent; +import com.google.gwt.safehtml.shared.SafeHtmlBuilder; + +/** + * @author Miguel Guimarães + */ +public class SimpleActionCell extends AbstractCell { + + private final Delegate delegate; + private final String buttonText; + private final String cssClass; + + // Constructor allows you to pass custom text and CSS classes for the button + public SimpleActionCell(String buttonText, String cssClass, Delegate delegate) { + super("click", "keydown"); // Listen for both click and Enter key + this.buttonText = buttonText; + this.cssClass = cssClass; + this.delegate = delegate; + } + + @Override + public void render(Context context, T value, SafeHtmlBuilder sb) { + // Render the button with your custom classes + + sb.appendHtmlConstant(""); + } + + @Override + public void onBrowserEvent(Context context, Element parent, T value, NativeEvent event, + ValueUpdater valueUpdater) { + + super.onBrowserEvent(context, parent, value, event, valueUpdater); + + String eventType = event.getType(); + if ("click".equals(eventType) || ("keydown".equals(eventType) && event.getKeyCode() == 13)) { + + // Stop the event from propagating (useful if the cell is in a selectable + // DataGrid/CellTable) + event.preventDefault(); + event.stopPropagation(); + + if (delegate != null) { + delegate.execute(value); + } + } + } + + public interface Delegate { + void execute(T value); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/panels/PermissionPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/panels/PermissionPanel.java new file mode 100644 index 0000000000..8a730df406 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/panels/PermissionPanel.java @@ -0,0 +1,80 @@ +package org.roda.wui.client.common.panels; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.Label; +import config.i18n.client.ClientMessages; +import org.roda.core.data.v2.user.Group; +import org.roda.core.data.v2.user.RODAMember; +import org.roda.core.data.v2.user.User; + +import java.util.Set; + +/** + * @author Miguel Guimarães + */ +public class PermissionPanel extends Composite { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private final Set list; + private final boolean isUser; + + // Private constructor forces the use of the Builder + private PermissionPanel(Builder builder) { + this.list = builder.list; + this.isUser = builder.isUser; + + FlowPanel superPanel = new FlowPanel(); + initWidget(superPanel); + + for (String str : list) { + FlowPanel panel = new FlowPanel(); + + FlowPanel panelBody = new FlowPanel(); + HTML type = new HTML( + SafeHtmlUtils.fromSafeConstant(isUser ? "" : "")); + panelBody.add(type); + type.addStyleName("permission-type"); + Label label = new Label(str); + panelBody.add(label); + panel.add(panelBody); + panelBody.addStyleName("panel-body"); + label.addStyleName("permission-name"); + + panel.addStyleName("panel permission"); + panel.addStyleName(isUser ? "permission-group" : "permission-user"); + + superPanel.add(panel); + } + } + + public Set getList() { + return list; + } + + public boolean isUser() { + return isUser; + } + + // --- BUILDER IMPLEMENTATION --- + + public static class Builder { + private final Set list; + private final boolean isUser; + + public Builder(RODAMember member) { + this.isUser = member.isUser(); + if (this.isUser) { + list = ((User) member).getGroups(); + } else { + list = ((Group) member).getUsers(); + } + } + + public PermissionPanel build() { + return new PermissionPanel(this); + } + } +} \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/panels/PermissionsPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/panels/PermissionsPanel.java new file mode 100644 index 0000000000..1348089fba --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/panels/PermissionsPanel.java @@ -0,0 +1,39 @@ +package org.roda.wui.client.common.panels; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.Widget; +import config.i18n.client.ClientMessages; +import org.roda.wui.client.common.labels.Header; + +import java.util.concurrent.Flow; + +/** + * @author Miguel Guimarães + */ + +public class PermissionsPanel extends Composite { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + + @UiField + FlowPanel permissionsDescription; + + @UiField + Header permissionsTitle; + + @UiField + Label permissionsEmpty; + + @UiField + FlowPanel permissionsPanel; + + + + interface MyUiBinder extends UiBinder { + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/panels/PermissionsPanel.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/panels/PermissionsPanel.ui.xml new file mode 100644 index 0000000000..43a294cca1 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/panels/PermissionsPanel.ui.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/resources/main.gss b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/resources/main.gss index 28269b2c62..2b9bf9f549 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/resources/main.gss +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/resources/main.gss @@ -234,11 +234,12 @@ table .label-success, table .label-danger, table .label-warning, table .label-in /* font-size: 75%; */ /* font-weight: 700; */ line-height: 1; - color: #fff; + color: #047857; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; + font-weight: bold; } .label-danger { @@ -248,11 +249,12 @@ table .label-success, table .label-danger, table .label-warning, table .label-in /* font-size: 75%; */ /* font-weight: 700; */ line-height: 1; - color: #fff; + color: #B91C3B; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; + font-weight: bold; } .label-warning { @@ -262,11 +264,12 @@ table .label-success, table .label-danger, table .label-warning, table .label-in /*font-size: 75%; font-weight: 700;*/ line-height: 1; - color: #fff; + color: #9b6100; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; + font-weight: bold; } .label-info { @@ -276,11 +279,12 @@ table .label-success, table .label-danger, table .label-warning, table .label-in /* font-size: 75%; */ /* font-weight: 700; */ line-height: 1; - color: #fff; + color: #036d97; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; + font-weight: bold; } .label-default { @@ -290,11 +294,12 @@ table .label-success, table .label-danger, table .label-warning, table .label-in /* font-size: 75%; */ /* font-weight: 700; */ line-height: 1; - color: #fff; + color: #666; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; + font-weight: bold; } .label-priority-urgent { @@ -1191,6 +1196,14 @@ button:after { content: "\e14b"; } +.btn-group:after { + content: "\f0c0"; +} + +.btn-user:after { + content: "\e7fe"; +} + .btn-preview:after { content: "\e8f4"; } @@ -7215,7 +7228,8 @@ td.datePickerMonth, td.datePickerYear { .ri-dialog .ri-dialog-list-scroll { width: 800px; height: 400px; - overflow-x: scroll; + overflow-x: hidden !important; /* Hides the horizontal scrollbar entirely */ + overflow-y: auto !important; /* Adds a vertical scrollbar ONLY when content exceeds 400px */ margin-top: 20px; margin-bottom: 20px; } @@ -9183,11 +9197,11 @@ wui-disposal-hold-data .form-textbox { ******************************/ .badge-panel { position: relative; - background-color: #e0e0e0; + background-color: #f5f5f5; display: inline-flex; padding: 0.7rem; line-height: 1; - color: #7f7f7f; + color: #666; text-align: center; white-space: nowrap; vertical-align: baseline; @@ -9201,6 +9215,7 @@ wui-disposal-hold-data .form-textbox { text-align: left; text-transform: uppercase; text-decoration: none; + font-weight: bold; } .badge-panel-dark { @@ -9713,3 +9728,146 @@ wui-disposal-hold-data .form-textbox { font-size: 13px; } + + +/* Container and Search */ +.permissions-dashboard { + width: 100%; +} +.permissions-search-box { + width: 100%; + padding: 8px 12px; + margin-bottom: 15px; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; +} + +/* Category Accordions */ +.permission-category { + border: 1px solid #e0e0e0; + border-radius: 4px; + margin-bottom: 10px; + background: #fff; +} + +.permission-category-header { + padding: 12px 15px; + background: #f5f5f5; + cursor: pointer; + border-bottom: 1px solid #e0e0e0; + display: flex; + justify-content: space-between; /* Pushes title left, icon right */ + align-items: center; /* Vertically centers them */ + transition: background 0.2s ease; +} + +.permission-category-header.collapsed { + border-bottom: none; +} +.permission-category-header:hover { + background: #eaeaea; +} + +/* Items container */ +.permission-category-items { + padding: 15px; + + /* Creates responsive columns. + It will fit as many 350px columns as possible. + If the screen is small, it gracefully collapses to 1 column. */ + column-width: 400px; + column-gap: 40px; /* The horizontal space between columns */ +} + +/* Select All styling */ +.select-all-wrapper { + font-weight: bold; + padding-bottom: 8px; + margin-bottom: 8px; + border-bottom: 1px dashed #e0e0e0; +} + +/* Individual permissions (Flexbox replaces HorizontalPanel) */ +.permission-category-item { + display: flex; + align-items: center; + margin-bottom: 12px; /* Adds vertical space since we removed flex gap */ + + /* CRITICAL: Prevents a checkbox and its label from + being visually ripped apart across two different columns */ + break-inside: avoid; + page-break-inside: avoid; +} + +.permission-checkbox { + margin-right: 8px; +} +.permission-description { + cursor: pointer; + user-select: none; +} +.permission-description-off { + color: #999; +} + +/* Right side wrapper containing Select All and Arrow */ +.permission-category-header-right { + display: flex; + align-items: center; + gap: 16px; /* Controls the small gap between the Select All checkbox and the arrow */ +} + +/* Header Checkbox */ +.permission-category-checkbox { + margin: 0; + cursor: pointer; + font-weight: normal; + color: #555; +} + +.permission-category-title { + font-weight: bold; + color: #333; + margin: 0; + user-select: none; +} + +/* Arrow Icon */ +.permission-category-icon { + font-size: 12px; + color: #888; + margin: 0; + user-select: none; +} + +/* Add max-height so the dialog knows when to stop growing and start scrolling */ +.edit-permissions-dialog .wui-dialog-layout, +.edit-user-information-dialog .wui-dialog-layout, +.create-user-dialog .wui-dialog-layout { + width: 80vw; + max-width: 80vw !important; + max-height: 85vh; /* ADD THIS: Keeps the dialog within the monitor height */ + + /* Use flexbox to ensure internal scrolling works correctly */ + display: flex; + flex-direction: column; +} + +/* This will now work because we added content.addStyleName("content") in Java */ +.edit-permissions-dialog .wui-dialog-layout > .content, +.edit-user-information-dialog .wui-dialog-layout > .content, +.create-user-dialog .wui-dialog-layout > .content { + flex: 1; + overflow-y: auto; + min-height: 0; + padding-bottom: 20px; +} + +/* Update this selector to target the class we added, instead of GWT's FlowPanel object name */ +.edit-permissions-dialog .wui-dialog-layout > .dialog-button-panel, +.edit-user-information-dialog .wui-dialog-layout > .dialog-button-panel, +.create-user-dialog .wui-dialog-layout > .dialog-button-panel { + flex-shrink: 0; + margin-top: 15px; +} \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/DisseminationsSliderHelper.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/DisseminationsSliderHelper.java deleted file mode 100644 index 4513a050c5..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/DisseminationsSliderHelper.java +++ /dev/null @@ -1,251 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE file at the root of the source - * tree and available online at - * - * https://github.com/keeps/roda - */ -package org.roda.wui.client.common.slider; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.roda.core.data.common.RodaConstants; -import org.roda.core.data.v2.index.FindRequest; -import org.roda.core.data.v2.index.IsIndexed; -import org.roda.core.data.v2.index.facet.Facets; -import org.roda.core.data.v2.index.filter.Filter; -import org.roda.core.data.v2.index.filter.SimpleFilterParameter; -import org.roda.core.data.v2.index.sort.SortParameter; -import org.roda.core.data.v2.index.sort.Sorter; -import org.roda.core.data.v2.index.sublist.Sublist; -import org.roda.core.data.v2.ip.IndexedAIP; -import org.roda.core.data.v2.ip.IndexedDIP; -import org.roda.core.data.v2.ip.IndexedFile; -import org.roda.core.data.v2.ip.IndexedRepresentation; -import org.roda.wui.client.common.actions.Actionable; -import org.roda.wui.client.common.actions.DisseminationActions; -import org.roda.wui.client.common.actions.model.ActionableObject; -import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; -import org.roda.wui.client.common.popup.CalloutPopup; -import org.roda.wui.client.common.popup.CalloutPopup.CalloutPosition; -import org.roda.wui.client.common.utils.AsyncCallbackUtils; -import org.roda.wui.client.services.Services; -import org.roda.wui.common.client.tools.HistoryUtils; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.event.dom.client.ClickHandler; -import com.google.gwt.i18n.client.LocaleInfo; -import com.google.gwt.safehtml.shared.SafeHtmlUtils; -import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.FocusPanel; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.UIObject; - -import config.i18n.client.ClientMessages; - -public class DisseminationsSliderHelper { - - private static final ClientMessages messages = GWT.create(ClientMessages.class); - - private DisseminationsSliderHelper() { - // do nothing - } - - private static void updateDisseminationsSliderPanel(final IndexedAIP aip, - final SliderPanel disseminationsSliderPanel) { - Filter filter = new Filter(new SimpleFilterParameter(RodaConstants.DIP_AIP_UUIDS, aip.getUUID())); - updateDisseminations(filter, disseminationsSliderPanel); - } - - private static void updateDisseminationsSliderPanel(IndexedRepresentation representation, - final SliderPanel disseminationsSliderPanel) { - Filter filter = new Filter( - new SimpleFilterParameter(RodaConstants.DIP_REPRESENTATION_UUIDS, representation.getUUID())); - updateDisseminations(filter, disseminationsSliderPanel); - } - - private static void updateDisseminationsSliderPanel(IndexedFile file, final SliderPanel disseminationsSliderPanel) { - Filter filter = new Filter(new SimpleFilterParameter(RodaConstants.DIP_FILE_UUIDS, file.getUUID())); - updateDisseminations(filter, disseminationsSliderPanel); - } - - private static void updateDisseminationsSliderPanel(IndexedFile file, final SliderPanel disseminationsSliderPanel, - Services services) { - Filter filter = new Filter(new SimpleFilterParameter(RodaConstants.DIP_FILE_UUIDS, file.getUUID())); - updateDisseminations(filter, disseminationsSliderPanel, services); - } - - private static void updateDisseminations(Filter filter, final SliderPanel disseminationsSliderPanel, - Services services) { - Sorter sorter = new Sorter(new SortParameter(RodaConstants.DIP_DATE_CREATED, true)); - Sublist sublist = new Sublist(0, 100); - Facets facets = Facets.NONE; - String localeString = LocaleInfo.getCurrentLocale().getLocaleName(); - - List dipFields = new ArrayList<>(RodaConstants.DIP_PERMISSIONS_FIELDS_TO_RETURN); - dipFields.addAll(Arrays.asList(RodaConstants.INDEX_UUID, RodaConstants.DIP_ID, RodaConstants.DIP_TITLE, - RodaConstants.DIP_DESCRIPTION, RodaConstants.DIP_DELETE_EXTERNAL_URL, RodaConstants.DIP_OPEN_EXTERNAL_URL)); - - FindRequest findRequest = FindRequest.getBuilder(filter, true).withSorter(sorter) - .withSublist(sublist).withFacets(facets).build(); - - services.rodaEntityRestService(s -> s.find(findRequest, localeString), IndexedDIP.class) - .whenComplete((indexedDIPIndexResult, throwable) -> { - if (throwable != null) { - AsyncCallbackUtils.defaultFailureTreatment(throwable); - } else { - updateDisseminationsSliderPanel(indexedDIPIndexResult.getResults(), disseminationsSliderPanel); - } - }); - } - - private static void updateDisseminations(Filter filter, final SliderPanel disseminationsSliderPanel) { - Sorter sorter = new Sorter(new SortParameter(RodaConstants.DIP_DATE_CREATED, true)); - Sublist sublist = new Sublist(0, 100); - Facets facets = Facets.NONE; - String localeString = LocaleInfo.getCurrentLocale().getLocaleName(); - - List dipFields = new ArrayList<>(RodaConstants.DIP_PERMISSIONS_FIELDS_TO_RETURN); - dipFields.addAll(Arrays.asList(RodaConstants.INDEX_UUID, RodaConstants.DIP_ID, RodaConstants.DIP_TITLE, - RodaConstants.DIP_DESCRIPTION, RodaConstants.DIP_DELETE_EXTERNAL_URL, RodaConstants.DIP_OPEN_EXTERNAL_URL)); - - FindRequest request = FindRequest.getBuilder(filter, true).withFieldsToReturn(dipFields).withSublist(sublist).withSorter(sorter).withFacets(facets).build(); - - Services services = new Services("Find Indexed DIP", "get"); - services.dipResource(s -> s.find(request, localeString)).whenComplete((indexedDIPIndexResult, throwable) -> { - if (throwable != null) { - AsyncCallbackUtils.defaultFailureTreatment(throwable.getCause()); - } else { - updateDisseminationsSliderPanel(indexedDIPIndexResult.getResults(), disseminationsSliderPanel); - } - }); - } - - private static void updateDisseminationsSliderPanel(List dips, SliderPanel disseminationsSliderPanel) { - disseminationsSliderPanel.clear(); - disseminationsSliderPanel.addTitle(new Label(messages.viewRepresentationFileDisseminationTitle())); - - if (dips.isEmpty()) { - Label dipEmpty = new Label(messages.browseFileDipEmpty()); - disseminationsSliderPanel.addContent(dipEmpty); - dipEmpty.addStyleName("dip-empty"); - } else { - for (final IndexedDIP dip : dips) { - disseminationsSliderPanel.addContent(createDisseminationPanel(dip)); - } - } - } - - private static FlowPanel createDisseminationPanel(final IndexedDIP dip) { - FlowPanel layout = new FlowPanel(); - - // open layout - FlowPanel leftLayout = new FlowPanel(); - Label titleLabel = new Label(dip.getTitle()); - Label descriptionLabel = new Label(dip.getDescription()); - - leftLayout.add(titleLabel); - leftLayout.add(descriptionLabel); - - FocusPanel openFocus = new FocusPanel(leftLayout); - layout.add(openFocus); - - // options - HTML optionsIcon = new HTML(SafeHtmlUtils.fromSafeConstant("")); - final FocusPanel optionsButton = new FocusPanel(optionsIcon); - - optionsButton.addStyleName("lightbtn"); - optionsIcon.addStyleName("lightbtn-icon"); - optionsButton.setTitle(messages.browseFileDipDelete()); - - optionsButton.addClickHandler(new ClickHandler() { - - @Override - public void onClick(ClickEvent event) { - showActions(dip, optionsButton); - } - }); - - layout.add(optionsButton); - - titleLabel.addStyleName("dipTitle"); - descriptionLabel.addStyleName("dipDescription"); - layout.addStyleName("dip"); - leftLayout.addStyleName("dip-left"); - openFocus.addStyleName("dip-focus"); - optionsButton.addStyleName("dip-options"); - - openFocus.addClickHandler(new ClickHandler() { - - @Override - public void onClick(ClickEvent event) { - HistoryUtils.openBrowse(dip); - } - - }); - - return layout; - } - - protected static void updateDisseminationsObjectSliderPanel(final T object, - final SliderPanel disseminationsSliderPanel) { - if (object instanceof IndexedAIP) { - updateDisseminationsSliderPanel((IndexedAIP) object, disseminationsSliderPanel); - } else if (object instanceof IndexedRepresentation) { - updateDisseminationsSliderPanel((IndexedRepresentation) object, disseminationsSliderPanel); - } else if (object instanceof IndexedFile) { - updateDisseminationsSliderPanel((IndexedFile) object, disseminationsSliderPanel); - } else { - // do nothing - } - } - - protected static void updateDisseminationsObjectSliderPanel(final T object, - final SliderPanel disseminationsSliderPanel, Services services) { - if (object instanceof IndexedAIP) { - updateDisseminationsSliderPanel((IndexedAIP) object, disseminationsSliderPanel); - } else if (object instanceof IndexedRepresentation) { - updateDisseminationsSliderPanel((IndexedRepresentation) object, disseminationsSliderPanel); - } else if (object instanceof IndexedFile) { - updateDisseminationsSliderPanel((IndexedFile) object, disseminationsSliderPanel, services); - } else { - // do nothing - } - } - - protected static void showActions(final IndexedDIP dip, final UIObject actionsButton) { - final CalloutPopup actionsPopup = new CalloutPopup(); - actionsPopup.addStyleName("ActionableStyleMenu"); - - if (actionsPopup.isShowing()) { - actionsPopup.hide(); - } else { - AsyncCallback callback = new AsyncCallback() { - - @Override - public void onFailure(Throwable caught) { - AsyncCallbackUtils.defaultFailureTreatment(caught); - } - - @Override - public void onSuccess(Actionable.ActionImpact impact) { - if (!Actionable.ActionImpact.NONE.equals(impact)) { - // update - } - actionsPopup.hide(); - } - }; - - actionsPopup.setWidget(new ActionableWidgetBuilder<>(DisseminationActions.get(dip.getPermissions())) - .withActionCallback(callback).buildListWithObjects(new ActionableObject<>(dip))); - actionsPopup.showRelativeTo(actionsButton, CalloutPosition.TOP_RIGHT); - } - - } - -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/InfoSliderHelper.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/InfoSliderHelper.java index 00b15d19e2..7eff192b3c 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/InfoSliderHelper.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/InfoSliderHelper.java @@ -7,67 +7,39 @@ */ package org.roda.wui.client.common.slider; -import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.TreeSet; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import org.roda.core.data.common.RodaConstants; import org.roda.core.data.utils.RepresentationInformationUtils; -import org.roda.core.data.v2.index.IsIndexed; import org.roda.core.data.v2.ip.IndexedAIP; import org.roda.core.data.v2.ip.IndexedFile; import org.roda.core.data.v2.ip.IndexedRepresentation; -import org.roda.core.data.v2.ip.Permissions; import org.roda.core.data.v2.ip.metadata.FileFormat; import org.roda.core.data.v2.jobs.Job; -import org.roda.wui.client.browse.PreservationEvents; import org.roda.wui.client.browse.RepresentationInformationHelper; -import org.roda.wui.client.common.actions.AipActions; -import org.roda.wui.client.common.dialogs.Dialogs; import org.roda.wui.client.common.model.BrowseAIPResponse; -import org.roda.wui.client.common.model.BrowseFileResponse; import org.roda.wui.client.common.model.BrowseRepresentationResponse; import org.roda.wui.client.ingest.process.ShowJob; -import org.roda.wui.client.management.distributed.ShowDistributedInstance; -import org.roda.wui.client.planning.RiskIncidenceRegister; import org.roda.wui.client.services.Services; -import org.roda.wui.common.client.tools.ConfigurationManager; import org.roda.wui.common.client.tools.DescriptionLevelUtils; import org.roda.wui.common.client.tools.HistoryUtils; import org.roda.wui.common.client.tools.Humanize; -import org.roda.wui.common.client.tools.RestUtils; import org.roda.wui.common.client.tools.StringUtils; import org.roda.wui.common.client.widgets.Toast; -import com.google.gwt.cell.client.SafeHtmlCell; import com.google.gwt.core.client.GWT; -import com.google.gwt.dom.client.Style; -import com.google.gwt.http.client.Request; -import com.google.gwt.http.client.RequestBuilder; -import com.google.gwt.http.client.RequestCallback; -import com.google.gwt.http.client.RequestException; -import com.google.gwt.http.client.Response; -import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.safehtml.shared.SafeHtmlUtils; -import com.google.gwt.safehtml.shared.SafeUri; -import com.google.gwt.user.cellview.client.CellTable; -import com.google.gwt.user.cellview.client.Column; import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HTMLPanel; import com.google.gwt.user.client.ui.InlineHTML; -import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Widget; -import com.google.gwt.view.client.ListDataProvider; -import com.google.gwt.view.client.SingleSelectionModel; import config.i18n.client.ClientMessages; @@ -78,72 +50,8 @@ private InfoSliderHelper() { // do nothing } - protected static void updateInfoObjectSliderPanel(T object, SliderPanel slider) { - if (object instanceof IndexedAIP) { - updateInfoSliderPanel((IndexedAIP) object, slider); - } else if (object instanceof IndexedRepresentation) { - updateInfoSliderPanel((IndexedRepresentation) object, slider); - } else { - // do nothing - } - } - - private static void updateInfoSliderPanel(IndexedAIP aip, SliderPanel infoSliderPanel) { - HashMap values = new HashMap<>(); - - infoSliderPanel.clear(); - infoSliderPanel.addTitle(new Label(messages.oneOfAObject(IndexedAIP.class.getName()))); - - if (aip != null) { - if (StringUtils.isNotBlank(aip.getLevel())) { - values.put(messages.aipLevel(), - new InlineHTML(DescriptionLevelUtils.getElementLevelIconSafeHtml(aip.getLevel(), true))); - } - - if (StringUtils.isNotBlank(aip.getTitle())) { - values.put(messages.aipGenericTitle(), new InlineHTML(SafeHtmlUtils.fromString(aip.getTitle()))); - } - - if (aip.getDateInitial() != null || aip.getDateFinal() != null) { - values.put(messages.aipDates(), new InlineHTML( - SafeHtmlUtils.fromString(Humanize.getDatesText(aip.getDateInitial(), aip.getDateFinal(), true)))); - } - } - - populate(infoSliderPanel, values); - } - - private static void updateInfoSliderPanel(IndexedRepresentation representation, SliderPanel infoSliderPanel) { - HashMap values = new HashMap<>(); - - infoSliderPanel.clear(); - infoSliderPanel.addTitle(new Label(messages.oneOfAObject(IndexedRepresentation.class.getName()))); - - if (representation != null) { - if (StringUtils.isNotBlank(messages.representationType())) { - values.put(messages.representationType(), - new InlineHTML(DescriptionLevelUtils.getRepresentationTypeIcon(representation.getType(), true))); - } - - if (StringUtils.isNotBlank(messages.representationFiles())) { - values.put(messages.representationFiles(), new InlineHTML(SafeHtmlUtils.fromString( - messages.numberOfFiles(representation.getNumberOfDataFiles(), representation.getNumberOfDataFolders())))); - } - - if (representation.getNumberOfDataFiles() + representation.getNumberOfDataFolders() > 0) { - values.put(messages.representationFiles(), new InlineHTML(SafeHtmlUtils.fromString( - messages.numberOfFiles(representation.getNumberOfDataFiles(), representation.getNumberOfDataFolders())))); - } - - values.put(messages.representationOriginal(), new InlineHTML(SafeHtmlUtils.fromString( - representation.isOriginal() ? messages.originalRepresentation() : messages.alternativeRepresentation()))); - } - - populate(infoSliderPanel, values); - } - - public static HashMap getRepresentationInfoDetailsMap(BrowseRepresentationResponse response) { - HashMap values = new HashMap<>(); + public static Map getRepresentationInfoDetailsMap(BrowseRepresentationResponse response) { + Map values = new HashMap<>(); IndexedRepresentation representation = response.getIndexedRepresentation(); values.put(messages.representationId(), createIdHTML(response)); @@ -165,21 +73,8 @@ public static HashMap getRepresentationInfoDetailsMap(BrowseRepr return values; } - public static void updateInfoSliderPanel(BrowseRepresentationResponse response, SliderPanel infoSliderPanel) { - IndexedRepresentation representation = response.getIndexedRepresentation(); - - HashMap values = getRepresentationInfoDetailsMap(response); - infoSliderPanel.clear(); - infoSliderPanel.addTitle(new Label(messages.oneOfAObject(IndexedRepresentation.class.getName()))); - - addLinkIfCentralInstance(values, representation.getInstanceName(), representation.isLocalInstance(), - representation.getInstanceId()); - - populate(infoSliderPanel, values); - } - - public static HashMap getAipInfoDetailsMap(BrowseAIPResponse response) { - HashMap values = new HashMap<>(); + public static Map getAipInfoDetailsMap(BrowseAIPResponse response) { + Map values = new HashMap<>(); IndexedAIP aip = response.getIndexedAIP(); values.put(messages.itemId(), createIdHTML(response)); @@ -210,7 +105,6 @@ public static HashMap getAipInfoDetailsMap(BrowseAIPResponse res values.put(messages.sipId(), sipIds); } - if (response.getIndexedAIP().getIngestJobId() != null && !response.getIndexedAIP().getIngestJobId().isEmpty()) { FlowPanel jobIdsList = new FlowPanel(); jobIdsList.addStyleName("slider-info-entry-value-aip-ingest-jobs"); @@ -257,8 +151,7 @@ public static HashMap getAipInfoDetailsMap(BrowseAIPResponse res if (throwable != null) { Toast.showError("Error fetching AIP jobs information"); - } - else { + } else { jobIdsList.clear(); jobIdsList.add(value); } @@ -269,156 +162,8 @@ public static HashMap getAipInfoDetailsMap(BrowseAIPResponse res return values; } - public static void updateInfoSliderPanel(BrowseAIPResponse response, SliderPanel infoSliderPanel) { - IndexedAIP aip = response.getIndexedAIP(); - - HashMap values = getAipInfoDetailsMap(response); - infoSliderPanel.clear(); - infoSliderPanel.addTitle(new Label(messages.oneOfAObject(IndexedAIP.class.getName()))); - - addLinkIfCentralInstance(values, response.getIndexedAIP().getInstanceName(), - response.getIndexedAIP().isLocalInstance(), aip.getInstanceId()); - - if (!response.getIndexedAIP().getPermissions().getUsers().equals(new Permissions().getUsers()) - || !response.getIndexedAIP().getPermissions().getGroups().equals(new Permissions().getGroups())) { - values.put(messages.aipPermissionDetails(), createAipPermissionDetailsHTML(response.getIndexedAIP())); - } - populate(infoSliderPanel, values); - } - - private static Widget createAipPermissionDetailsHTML(IndexedAIP aip) { - Permissions permissions = aip.getPermissions(); - - final String CSS_HAS_PERMISSION = ""; - final String CSS_NO_PERMISSION = " slider-aip-permissions-table-icon-fade"; - - List>> entryList = new ArrayList<>(); - for (String username : new TreeSet<>(permissions.getUsernames())) { - entryList.add(new AbstractMap.SimpleEntry<>("u-" + username, permissions.getUserPermissions(username))); - } - for (String groupname : new TreeSet<>(permissions.getGroupnames())) { - entryList.add(new AbstractMap.SimpleEntry<>("g-" + groupname, permissions.getGroupPermissions(groupname))); - } - - CellTable>> table = new CellTable<>(); - table.addStyleName("slider-aip-permissions-table"); - - Column>, SafeHtml> userGroupIconColumn = new Column>, SafeHtml>( - new SafeHtmlCell()) { - @Override - public SafeHtml getValue(Entry> object) { - if (object.getKey().startsWith("u-")) { - return SafeHtmlUtils.fromSafeConstant(""); - } else { - return SafeHtmlUtils.fromSafeConstant(""); - } - } - }; - - Column>, SafeHtml> nameColumn = new Column>, SafeHtml>( - new SafeHtmlCell()) { - @Override - public SafeHtml getValue(Entry> object) { - String name = object.getKey().substring(2); - return SafeHtmlUtils.fromSafeConstant( - "" + SafeHtmlUtils.htmlEscape(name) + ""); - } - }; - - Column>, SafeHtml> iconReadColumn = new Column>, SafeHtml>( - new SafeHtmlCell()) { - @Override - public SafeHtml getValue(Entry> object) { - String extraIconCss = object.getValue().contains(Permissions.PermissionType.READ) ? CSS_HAS_PERMISSION - : CSS_NO_PERMISSION; - return SafeHtmlUtils - .fromSafeConstant(""); - } - }; - - Column>, SafeHtml> iconCreateColumn = new Column>, SafeHtml>( - new SafeHtmlCell()) { - @Override - public SafeHtml getValue(Entry> object) { - String extraIconCss = object.getValue().contains(Permissions.PermissionType.CREATE) ? CSS_HAS_PERMISSION - : CSS_NO_PERMISSION; - return SafeHtmlUtils - .fromSafeConstant(""); - } - }; - - Column>, SafeHtml> iconEditColumn = new Column>, SafeHtml>( - new SafeHtmlCell()) { - @Override - public SafeHtml getValue(Entry> object) { - String extraIconCss = object.getValue().contains(Permissions.PermissionType.UPDATE) ? CSS_HAS_PERMISSION - : CSS_NO_PERMISSION; - return SafeHtmlUtils - .fromSafeConstant(""); - } - }; - - Column>, SafeHtml> iconDeleteColumn = new Column>, SafeHtml>( - new SafeHtmlCell()) { - @Override - public SafeHtml getValue(Entry> object) { - String extraIconCss = object.getValue().contains(Permissions.PermissionType.DELETE) ? CSS_HAS_PERMISSION - : CSS_NO_PERMISSION; - return SafeHtmlUtils - .fromSafeConstant(""); - } - }; - - Column>, SafeHtml> iconGrantColumn = new Column>, SafeHtml>( - new SafeHtmlCell()) { - @Override - public SafeHtml getValue(Entry> object) { - String extraIconCss = object.getValue().contains(Permissions.PermissionType.GRANT) ? CSS_HAS_PERMISSION - : CSS_NO_PERMISSION; - return SafeHtmlUtils - .fromSafeConstant(""); - } - }; - - table.addColumn(userGroupIconColumn); - table.addColumn(nameColumn); - table.addColumn(iconReadColumn); - table.addColumn(iconCreateColumn); - table.addColumn(iconEditColumn); - table.addColumn(iconDeleteColumn); - table.addColumn(iconGrantColumn); - - table.setColumnWidth(userGroupIconColumn, 23, Style.Unit.PX); - table.setColumnWidth(iconReadColumn, 23, Style.Unit.PX); - table.setColumnWidth(iconCreateColumn, 23, Style.Unit.PX); - table.setColumnWidth(iconEditColumn, 23, Style.Unit.PX); - table.setColumnWidth(iconDeleteColumn, 23, Style.Unit.PX); - table.setColumnWidth(iconGrantColumn, 23, Style.Unit.PX); - - nameColumn.setCellStyleNames("nowrap slider-aip-permissions-table-name"); - - AipActions aipActions = AipActions.get(); - if (aipActions.canAct(AipActions.AipAction.UPDATE_PERMISSIONS, aip).canAct()) { - table.addStyleName("slider-aip-permissions-table-with-grant"); - SingleSelectionModel>> selectionModel = new SingleSelectionModel<>( - item -> item.getKey().substring(2)); - selectionModel.addSelectionChangeHandler(event -> aipActions.act(AipActions.AipAction.UPDATE_PERMISSIONS, aip)); - table.setSelectionModel(selectionModel); - } - - ListDataProvider>> dataProvider = new ListDataProvider<>(entryList); - dataProvider.addDataDisplay(table); - - return table; - } - - public static HashMap getFileInfoDetailsMap(IndexedFile file, List riRules) { - HashMap values = new HashMap<>(); + public static Map getFileInfoDetailsMap(IndexedFile file, List riRules) { + Map values = new HashMap<>(); if (file != null) { String fileName = file.getOriginalName() != null ? file.getOriginalName() : file.getId(); @@ -501,93 +246,6 @@ public static HashMap getFileInfoDetailsMap(IndexedFile file, Li return values; } - public static void createFileInfoSliderPanel(IndexedFile file, BrowseFileResponse response, - SliderPanel infoSliderPanel) { - HashMap values = getFileInfoDetailsMap(file, response.getRepresentationInformationFields()); - infoSliderPanel.clear(); - infoSliderPanel.addTitle(new Label(messages.oneOfAObject(IndexedFile.class.getName()))); - - Long risksCounter = response.getRiskCounterResponse().getResult(); - Long preservationEventsCounter = response.getPreservationCounterResponse().getResult(); - - if (file != null) { - addLinkIfCentralInstance(values, file.getInstanceName(), file.isLocalInstance(), file.getInstanceId()); - - List history = new ArrayList<>(); - history.add(file.getAipId()); - history.add(file.getRepresentationId()); - history.addAll(file.getPath()); - history.add(file.getId()); - - if (risksCounter >= 0) { - Anchor risksLink = new Anchor(messages.aipRiskIncidences(risksCounter), - HistoryUtils.createHistoryHashLink(RiskIncidenceRegister.RESOLVER, history)); - values.put(messages.preservationRisks(), risksLink); - } - - if (preservationEventsCounter >= 0) { - Anchor eventsLink = new Anchor(messages.aipEvents(preservationEventsCounter), - HistoryUtils.createHistoryHashLink(PreservationEvents.BROWSE_RESOLVER, file.getAipId(), - file.getRepresentationUUID(), file.getUUID())); - values.put(messages.preservationEvents(), eventsLink); - } - - SafeUri uri = RestUtils.createTechnicalMetadataHTMLUri(file.getUUID()); - RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, uri.asString()); - - Anchor technicalInformationAnchor = new Anchor(); - technicalInformationAnchor.setStyleName("clickable"); - technicalInformationAnchor.setText(messages.showTechnicalMetadata()); - - // technicalInformation - try { - requestBuilder.sendRequest(null, new RequestCallback() { - @Override - public void onResponseReceived(Request request, Response response) { - if (response.getStatusCode() == 200) { - if (!response.getText().isEmpty()) { - values.put(messages.viewTechnicalInformation(), technicalInformationAnchor); - technicalInformationAnchor - .addClickHandler(e -> Dialogs.showTechnicalMetadataInformation(messages.viewTechnicalMetadata(), - messages.downloadButton(), messages.closeButton(), file, response.getText())); - } - } else { - values.put(messages.viewTechnicalInformation(), technicalInformationAnchor); - technicalInformationAnchor - .addClickHandler(e -> Dialogs.showTechnicalMetadataInformation(messages.viewTechnicalMetadata(), - messages.downloadButton(), messages.closeButton(), file, null)); - } - populate(infoSliderPanel, values); - } - - @Override - public void onError(Request request, Throwable throwable) { - populate(infoSliderPanel, values); - } - }); - } catch (RequestException e) { - throw new RuntimeException(e); - } - } - } - - private static void populate(SliderPanel infoSliderPanel, HashMap values) { - for (Entry entry : values.entrySet()) { - FlowPanel entryPanel = new FlowPanel(); - - Label keyLabel = new Label(entry.getKey()); - Widget valueLabel = entry.getValue(); - - entryPanel.add(keyLabel); - entryPanel.add(valueLabel); - infoSliderPanel.addContent(entryPanel); - - keyLabel.addStyleName("slider-info-entry-key"); - valueLabel.addStyleName("slider-info-entry-value"); - entryPanel.addStyleName("slider-info-entry"); - } - } - private static FlowPanel createExtensionHTML(List representationInformationFields, String extension) { FlowPanel panel = new FlowPanel(); final String riFilter = RepresentationInformationUtils @@ -722,28 +380,4 @@ private static FlowPanel createRepresentationTypeHTML(BrowseRepresentationRespon return panel; } - - public static void addLinkIfCentralInstance(Map values, String instanceName, boolean localToInstance, - String instanceId) { - if (StringUtils.isNotBlank(instanceId)) { - String distributedMode = ConfigurationManager.getStringWithDefault( - RodaConstants.DEFAULT_DISTRIBUTED_MODE_TYPE.name(), RodaConstants.DISTRIBUTED_MODE_TYPE_PROPERTY); - if (RodaConstants.DistributedModeType.CENTRAL.name().equals(distributedMode)) { - if (localToInstance) { - values.put(messages.distributedInstanceLabel(), new Label(instanceName)); - } else { - Anchor anchor = new Anchor(); - if (StringUtils.isNotBlank(instanceName)) { - anchor.setText(instanceName); - } else { - anchor.setText(instanceId); - } - anchor.setHref(HistoryUtils.createHistoryHashLink(ShowDistributedInstance.RESOLVER, instanceId)); - values.put(messages.distributedInstanceLabel(), anchor); - } - } else { - values.put(messages.itemInstanceId(), new Label(instanceId)); - } - } - } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/OptionsSliderHelper.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/OptionsSliderHelper.java deleted file mode 100644 index a49ef14887..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/OptionsSliderHelper.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE file at the root of the source - * tree and available online at - * - * https://github.com/keeps/roda - */ -package org.roda.wui.client.common.slider; - -import org.roda.core.data.v2.index.IsIndexed; -import org.roda.core.data.v2.ip.DIPFile; -import org.roda.core.data.v2.ip.IndexedAIP; -import org.roda.core.data.v2.ip.IndexedDIP; -import org.roda.core.data.v2.ip.IndexedFile; -import org.roda.core.data.v2.ip.IndexedRepresentation; -import org.roda.wui.client.common.actions.AipActions; -import org.roda.wui.client.common.actions.DisseminationActions; -import org.roda.wui.client.common.actions.DisseminationFileActions; -import org.roda.wui.client.common.actions.FileActions; -import org.roda.wui.client.common.actions.RepresentationActions; -import org.roda.wui.client.common.actions.model.ActionableObject; -import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; - -public class OptionsSliderHelper { - - private OptionsSliderHelper() { - // do nothing - } - - static void updateOptionsObjectSliderPanel(T object, SliderPanel slider) { - if (object instanceof IndexedFile) { - updateOptionsSliderPanel((IndexedFile) object, slider); - } else if (object instanceof IndexedRepresentation) { - updateOptionsSliderPanel((IndexedRepresentation) object, slider); - } else if (object instanceof IndexedAIP) { - updateOptionsSliderPanel((IndexedAIP) object, slider); - } else if (object instanceof IndexedDIP) { - updateOptionsSliderPanel((IndexedDIP) object, slider); - } else if (object instanceof DIPFile) { - updateOptionsSliderPanel((DIPFile) object, slider); - } else { - // do nothing - } - } - - private static void updateOptionsSliderPanel(IndexedAIP aip, SliderPanel slider) { - slider.clear(); - slider - .addContent(new ActionableWidgetBuilder<>(AipActions.get()).buildListWithObjects(new ActionableObject<>(aip))); - } - - private static void updateOptionsSliderPanel(IndexedRepresentation representation, SliderPanel slider) { - slider.clear(); - slider.addContent(new ActionableWidgetBuilder<>(RepresentationActions.get()) - .buildListWithObjects(new ActionableObject<>(representation))); - } - - private static void updateOptionsSliderPanel(final IndexedFile file, final SliderPanel slider) { - slider.clear(); - slider.addContent( - new ActionableWidgetBuilder<>(FileActions.get()).buildListWithObjects(new ActionableObject<>((file)))); - } - - private static void updateOptionsSliderPanel(final IndexedDIP dip, final SliderPanel slider) { - slider.clear(); - slider.addContent(new ActionableWidgetBuilder<>(DisseminationActions.get(dip.getPermissions())) - .buildListWithObjects(new ActionableObject<>((dip)))); - } - - private static void updateOptionsSliderPanel(final DIPFile file, final SliderPanel slider) { - slider.clear(); - slider.addContent(new ActionableWidgetBuilder<>(DisseminationFileActions.get()) - .buildListWithObjects(new ActionableObject<>((file)))); - } - -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/SliderPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/SliderPanel.java deleted file mode 100644 index f959285583..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/SliderPanel.java +++ /dev/null @@ -1,161 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE file at the root of the source - * tree and available online at - * - * https://github.com/keeps/roda - */ -package org.roda.wui.client.common.slider; - -import org.roda.wui.client.common.utils.JavascriptUtils; - -import com.google.gwt.dom.client.Document; -import com.google.gwt.event.dom.client.ChangeEvent; -import com.google.gwt.event.dom.client.ChangeHandler; -import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.event.dom.client.ClickHandler; -import com.google.gwt.event.dom.client.HasChangeHandlers; -import com.google.gwt.event.shared.HandlerRegistration; -import com.google.gwt.uibinder.client.UiChild; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.FocusPanel; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.ScrollPanel; -import com.google.gwt.user.client.ui.SimplePanel; -import com.google.gwt.user.client.ui.Widget; - -public class SliderPanel extends Composite implements HasChangeHandlers { - - private static final String CSS_CLASS_ACTIVE = "active"; - private static SliderPanel OPEN_SLIDER = null; - - private final SimplePanel wrapper; - private final ScrollPanel layout; - private FlowPanel contentLayout; - - private FocusPanel toggleButton = null; - private HandlerRegistration toggleButtonHandlerRegistration = null; - private boolean disposed = false; - - public SliderPanel() { - super(); - this.contentLayout = new FlowPanel(); - this.layout = new ScrollPanel(); - this.layout.add(contentLayout); - - this.wrapper = new SimplePanel(this.layout); - initWidget(wrapper); - - layout.addStyleName("slider-layout"); - this.addStyleName("slider"); - } - - /** - * Destroys this slider, making it unusable. Trying to use the slider after this - * will cause errors. - */ - public void dispose() { - if (!disposed) { - closeAll(); - if (toggleButtonHandlerRegistration != null) { - toggleButtonHandlerRegistration.removeHandler(); - } - clear(); - getElement().removeFromParent(); - disposed = true; - } - } - - public void setToggleButton(FocusPanel toggleButton) { - this.toggleButton = toggleButton; - - // bind toggle button events - toggleButtonHandlerRegistration = toggleButton.addClickHandler(new ClickHandler() { - - @Override - public void onClick(ClickEvent event) { - if (isOpen(SliderPanel.this)) { - closeAll(); - } else { - open(); - } - } - }); - } - - public static void closeAll() { - if (OPEN_SLIDER != null) { - // remove CSS classes - OPEN_SLIDER.setActive(false); - - // hide animation - JavascriptUtils.toggle(OPEN_SLIDER.getElement()); - - // update state - OPEN_SLIDER = null; - } - } - - private static boolean isOpen(SliderPanel slider) { - return slider == OPEN_SLIDER; - } - - public static void open(SliderPanel slider) { - closeAll(); - - // update state - OPEN_SLIDER = slider; - - // Show animation - JavascriptUtils.toggle(slider.getElement()); - - // Add CSS classes - OPEN_SLIDER.setActive(true); - } - - @UiChild(tagname = "title") - public void addTitle(Label title) { - contentLayout.add(title); - title.addStyleName("slider-title"); - } - - @UiChild(tagname = "content") - public void addContent(Widget widget) { - contentLayout.add(widget); - } - - public void clear() { - contentLayout.clear(); - } - - private void setActive(boolean active) { - if (active) { - if (toggleButton != null) { - toggleButton.addStyleName(CSS_CLASS_ACTIVE); - } - this.addStyleDependentName(CSS_CLASS_ACTIVE); - } else { - if (toggleButton != null) { - toggleButton.removeStyleName(CSS_CLASS_ACTIVE); - } - this.removeStyleDependentName(CSS_CLASS_ACTIVE); - } - - ChangeEvent.fireNativeEvent(Document.get().createChangeEvent(), this); - } - - public void open() { - open(this); - } - - public boolean isOpen() { - return isOpen(this); - } - - @Override - public HandlerRegistration addChangeHandler(ChangeHandler handler) { - return addHandler(handler, ChangeEvent.getType()); - } - -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/Sliders.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/Sliders.java deleted file mode 100644 index 6b62ba92ad..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/slider/Sliders.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE file at the root of the source - * tree and available online at - * - * https://github.com/keeps/roda - */ -package org.roda.wui.client.common.slider; - -import org.roda.core.data.v2.index.IsIndexed; -import org.roda.core.data.v2.ip.IndexedFile; -import org.roda.wui.client.common.model.BrowseAIPResponse; -import org.roda.wui.client.common.model.BrowseFileResponse; -import org.roda.wui.client.common.model.BrowseRepresentationResponse; -import org.roda.wui.client.services.Services; - -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.FocusPanel; - -public class Sliders { - - private Sliders() { - - } - - public static SliderPanel createSlider(FlowPanel container, FocusPanel toggleButton) { - SliderPanel slider = new SliderPanel(); - container.add(slider); - container.addStyleName("slider-container"); - slider.setToggleButton(toggleButton); - return slider; - } - - public static SliderPanel createDisseminationSlider(FlowPanel container, - FocusPanel toggleButton, T object, Services services) { - SliderPanel slider = createSlider(container, toggleButton); - DisseminationsSliderHelper.updateDisseminationsObjectSliderPanel(object, slider, services); - return slider; - } - - public static SliderPanel createAipInfoSlider(FlowPanel container, FocusPanel toggleButton, - BrowseAIPResponse response) { - SliderPanel slider = createSlider(container, toggleButton); - InfoSliderHelper.updateInfoSliderPanel(response, slider); - return slider; - } - - public static SliderPanel createFileInfoSlider(FlowPanel container, FocusPanel toggleButton, IndexedFile file, - BrowseFileResponse response) { - SliderPanel slider = createSlider(container, toggleButton); - InfoSliderHelper.createFileInfoSliderPanel(file, response, slider); - return slider; - } - - public static SliderPanel createRepresentationInfoSlider(FlowPanel container, FocusPanel toggleButton, - BrowseRepresentationResponse response) { - SliderPanel slider = createSlider(container, toggleButton); - InfoSliderHelper.updateInfoSliderPanel(response, slider); - return slider; - } - -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/utils/FormUtilities.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/utils/FormUtilities.java index faa9d9fe02..5266fa556f 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/utils/FormUtilities.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/utils/FormUtilities.java @@ -13,6 +13,7 @@ import java.util.Set; import java.util.concurrent.Callable; +import com.google.gwt.user.client.ui.InlineHTML; import org.roda.core.data.v2.generics.MetadataValue; import org.roda.wui.client.common.RichTextToolbar; @@ -41,6 +42,7 @@ import com.google.gwt.user.datepicker.client.DateBox; import config.i18n.client.ClientMessages; +import org.roda.wui.common.client.tools.StringUtils; /** * Created by adrapereira on 13-06-2016. @@ -569,4 +571,40 @@ public static void callOnChange(final Callable onChange) { // do nothing } } + + public static FlowPanel buildField(String label, InlineHTML html) { + FlowPanel topPanel = new FlowPanel(); + topPanel.addStyleName("descriptiveMetadata"); + FlowPanel fieldPanel = new FlowPanel(); + fieldPanel.setStyleName("field"); + + Label fieldLabel = new Label(label); + fieldLabel.setStyleName("label"); + + FlowPanel fieldValuePanel = new FlowPanel(); + fieldValuePanel.setStyleName("value"); + fieldValuePanel.add(html); + + fieldPanel.add(fieldLabel); + fieldPanel.add(fieldValuePanel); + + topPanel.add(fieldPanel); + return topPanel; + } + + public static void addIfNotBlank(FlowPanel panel, String label, String value) { + if (StringUtils.isNotBlank(value)) { + panel.add(buildField(label, new InlineHTML(SafeHtmlUtils.htmlEscape(value)))); + } + } + + public static FlowPanel buildSeparator(String text) { + FlowPanel separator = new FlowPanel(); + separator.addStyleName("form-separator"); + Label separatorLabel = new Label(messages.groups()); + separatorLabel.addStyleName("separator-label"); + separator.add(separatorLabel); + + return separator; + } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java index 37d160281f..d62435b148 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java @@ -19,11 +19,14 @@ import org.roda.core.data.v2.ip.IndexedFile; import org.roda.core.data.v2.ip.IndexedRepresentation; import org.roda.core.data.v2.ip.TransferredResource; +import org.roda.core.data.v2.user.RODAMember; import org.roda.wui.client.browse.BrowseTop; import org.roda.wui.client.browse.PreservationEvents; import org.roda.wui.client.disposal.DisposalDestroyedRecords; import org.roda.wui.client.ingest.appraisal.IngestAppraisal; import org.roda.wui.client.ingest.transfer.IngestTransfer; +import org.roda.wui.client.management.members.MemberManagement; +import org.roda.wui.client.management.members.ShowMember; import org.roda.wui.common.client.tools.DescriptionLevelUtils; import org.roda.wui.common.client.tools.HistoryUtils; import org.roda.wui.common.client.tools.ListUtils; @@ -436,6 +439,21 @@ private static SafeHtml getBreadcrumbLabel(IndexedAIP aip) { return breadcrumbLabel; } + public static List getRODAMemberBreadcrumbs(RODAMember user) { + List ret = new ArrayList<>(); + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(messages.usersAndGroupsTitle()), messages.usersAndGroupsTitle(), + MemberManagement.RESOLVER.getHistoryPath())); + + if (user != null) { + List path = new ArrayList<>(ShowMember.RESOLVER.getHistoryPath()); + path.add(user.getUUID()); + String label = user.getId(); + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromString(label), label, path)); + } + + return ret; + } + private static String getBreadcrumbTitle(IndexedAIP aip) { String title; if (aip.getGhost()) { diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/ContentPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/ContentPanel.java index ec213cfc30..8276d9a553 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/ContentPanel.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/ContentPanel.java @@ -26,11 +26,11 @@ import org.roda.wui.client.ingest.Ingest; import org.roda.wui.client.management.AcknowledgeNotification; import org.roda.wui.client.management.Management; -import org.roda.wui.client.management.Profile; -import org.roda.wui.client.management.RecoverLogin; -import org.roda.wui.client.management.Register; -import org.roda.wui.client.management.ResetPassword; -import org.roda.wui.client.management.SetPassword; +import org.roda.wui.client.management.members.Profile; +import org.roda.wui.client.management.members.RecoverLogin; +import org.roda.wui.client.management.members.Register; +import org.roda.wui.client.management.members.ResetPassword; +import org.roda.wui.client.management.members.SetPassword; import org.roda.wui.client.management.VerifyEmail; import org.roda.wui.client.planning.Planning; import org.roda.wui.client.process.Process; @@ -54,13 +54,10 @@ */ public class ContentPanel extends SimplePanel { - private static ContentPanel instance = null; - - private static ClientLogger logger = new ClientLogger(ContentPanel.class.getName()); - private static final Set resolvers = new HashSet<>(); private static final ClientMessages messages = GWT.create(ClientMessages.class); - + private static ContentPanel instance = null; + private static ClientLogger logger = new ClientLogger(ContentPanel.class.getName()); private Widget currWidget; private List lastHistoryTokens = null; private HistoryResolver lastResolver = null; diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/ExpiredSessionDetector.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/ExpiredSessionDetector.java index 3559fac751..2d8712b1cb 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/ExpiredSessionDetector.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/ExpiredSessionDetector.java @@ -11,8 +11,8 @@ import org.roda.core.data.v2.user.User; import org.roda.wui.client.common.UserLogin; import com.google.gwt.user.client.Timer; -import org.roda.wui.client.management.RecoverLogin; -import org.roda.wui.client.management.Register; +import org.roda.wui.client.management.members.RecoverLogin; +import org.roda.wui.client.management.members.Register; import org.roda.wui.client.welcome.Welcome; import org.roda.wui.common.client.tools.HistoryUtils; diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/Header.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/Header.java index eaafd0602b..f00c72c604 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/Header.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/Header.java @@ -29,7 +29,7 @@ import org.roda.wui.client.ingest.preingest.PreIngest; import org.roda.wui.client.ingest.transfer.IngestTransfer; import org.roda.wui.client.management.Management; -import org.roda.wui.client.management.MemberManagement; +import org.roda.wui.client.management.members.MemberManagement; import org.roda.wui.client.management.NotificationRegister; import org.roda.wui.client.management.Statistics; import org.roda.wui.client.management.UserLog; diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/Login.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/Login.java index 309028ea53..12002c7cf0 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/Login.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/Login.java @@ -24,8 +24,8 @@ import org.roda.wui.client.common.UserLogin; import org.roda.wui.client.common.dialogs.Dialogs; import org.roda.wui.client.common.utils.JavascriptUtils; -import org.roda.wui.client.management.RecoverLogin; -import org.roda.wui.client.management.Register; +import org.roda.wui.client.management.members.RecoverLogin; +import org.roda.wui.client.management.members.Register; import org.roda.wui.client.services.Services; import org.roda.wui.client.welcome.Welcome; import org.roda.wui.common.client.ClientLogger; diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/UserMenu.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/UserMenu.java index 1a15fd8a12..b9f7111be2 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/UserMenu.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/UserMenu.java @@ -19,8 +19,8 @@ import org.roda.core.data.v2.user.User; import org.roda.wui.client.common.UserLogin; import org.roda.wui.client.common.utils.JavascriptUtils; -import org.roda.wui.client.management.Profile; -import org.roda.wui.client.management.Register; +import org.roda.wui.client.management.members.Profile; +import org.roda.wui.client.management.members.Register; import org.roda.wui.common.client.ClientLogger; import org.roda.wui.common.client.tools.HistoryUtils; import org.roda.wui.common.client.widgets.wcag.AcessibleMenuBar; diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateGroup.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateGroup.java deleted file mode 100644 index ec1a5052fc..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateGroup.java +++ /dev/null @@ -1,133 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE file at the root of the source - * tree and available online at - * - * https://github.com/keeps/roda - */ -/** - * - */ -package org.roda.wui.client.management; - -import java.util.List; - -import org.roda.core.data.exceptions.AlreadyExistsException; -import org.roda.core.data.v2.user.Group; -import org.roda.core.data.v2.user.requests.CreateGroupRequest; -import org.roda.wui.client.common.UserLogin; -import org.roda.wui.client.services.Services; -import org.roda.wui.common.client.HistoryResolver; -import org.roda.wui.common.client.tools.HistoryUtils; -import org.roda.wui.common.client.tools.ListUtils; -import org.roda.wui.common.client.widgets.Toast; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.uibinder.client.UiBinder; -import com.google.gwt.uibinder.client.UiField; -import com.google.gwt.uibinder.client.UiHandler; -import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.Button; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.Widget; - -import config.i18n.client.ClientMessages; - -/** - * @author Luis Faria - * - */ -public class CreateGroup extends Composite { - - public static final HistoryResolver RESOLVER = new HistoryResolver() { - - @Override - public void resolve(List historyTokens, final AsyncCallback callback) { - CreateGroup createGroup = new CreateGroup(new Group()); - callback.onSuccess(createGroup); - } - - @Override - public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); - } - - @Override - public List getHistoryPath() { - return ListUtils.concat(MemberManagement.RESOLVER.getHistoryPath(), getHistoryToken()); - } - - @Override - public String getHistoryToken() { - return "create_group"; - } - }; - - interface MyUiBinder extends UiBinder { - } - - private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - - private Group group; - - private static final ClientMessages messages = GWT.create(ClientMessages.class); - - @UiField - Button buttonApply; - - @UiField - Button buttonCancel; - - @UiField(provided = true) - GroupDataPanel groupDataPanel; - - /** - * Create a new panel to create a group - * - * @param group - * the group to create - */ - public CreateGroup(Group group) { - this.group = group; - - this.groupDataPanel = new GroupDataPanel(true, false); - this.groupDataPanel.setGroup(group); - - initWidget(uiBinder.createAndBindUi(this)); - } - - @UiHandler("buttonApply") - void buttonApplyHandler(ClickEvent e) { - if (groupDataPanel.isValid()) { - group = groupDataPanel.getGroup(); - Services services = new Services("Create group", "create"); - CreateGroupRequest createGroupRequest = new CreateGroupRequest(group.getName(), group.getFullName(), - group.getDirectRoles()); - services.membersResource(s -> s.createGroup(createGroupRequest)).whenComplete((newGroup, error) -> { - if (newGroup != null) { - HistoryUtils.newHistory(MemberManagement.RESOLVER); - } else if (error != null) { - errorMessage(error); - } - }); - } - } - - @UiHandler("buttonCancel") - void buttonCancelHandler(ClickEvent e) { - cancel(); - } - - private void cancel() { - HistoryUtils.newHistory(MemberManagement.RESOLVER); - } - - private void errorMessage(Throwable caught) { - if (caught instanceof AlreadyExistsException) { - Toast.showError(messages.createGroupAlreadyExists(group.getName())); - } else { - Toast.showError(messages.createGroupFailure(caught.getMessage())); - } - } -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateGroup.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateGroup.ui.xml deleted file mode 100644 index 0c21d1d09f..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateGroup.ui.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateUser.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateUser.java deleted file mode 100644 index 4062f2232b..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateUser.java +++ /dev/null @@ -1,138 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE file at the root of the source - * tree and available online at - * - * https://github.com/keeps/roda - */ -/** - * - */ -package org.roda.wui.client.management; - -import java.util.List; - -import org.roda.core.data.exceptions.EmailAlreadyExistsException; -import org.roda.core.data.exceptions.UserAlreadyExistsException; -import org.roda.core.data.v2.user.User; -import org.roda.core.data.v2.user.requests.CreateUserRequest; -import org.roda.wui.client.common.UserLogin; -import org.roda.wui.client.services.Services; -import org.roda.wui.common.client.HistoryResolver; -import org.roda.wui.common.client.tools.HistoryUtils; -import org.roda.wui.common.client.tools.ListUtils; -import org.roda.wui.common.client.widgets.Toast; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.i18n.client.LocaleInfo; -import com.google.gwt.uibinder.client.UiBinder; -import com.google.gwt.uibinder.client.UiField; -import com.google.gwt.uibinder.client.UiHandler; -import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.Button; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.Widget; - -import config.i18n.client.ClientMessages; - -/** - * @author Luis Faria - * - */ -public class CreateUser extends Composite { - - public static final HistoryResolver RESOLVER = new HistoryResolver() { - - @Override - public void resolve(List historyTokens, final AsyncCallback callback) { - CreateUser createUser = new CreateUser(new User()); - callback.onSuccess(createUser); - } - - @Override - public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); - } - - @Override - public List getHistoryPath() { - return ListUtils.concat(MemberManagement.RESOLVER.getHistoryPath(), getHistoryToken()); - } - - @Override - public String getHistoryToken() { - return "create_user"; - } - }; - - interface MyUiBinder extends UiBinder { - } - - private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - - private User user; - - private static final ClientMessages messages = GWT.create(ClientMessages.class); - - @UiField - Button buttonApply; - - @UiField - Button buttonCancel; - - @UiField(provided = true) - CreateUserPanel userDataPanel; - - /** - * Create a new panel to create a user - * - * @param user - * the user to create - */ - public CreateUser(User user) { - this.user = user; - - this.userDataPanel = new CreateUserPanel(true, false, true); - this.userDataPanel.setUser(user); - initWidget(uiBinder.createAndBindUi(this)); - } - - @UiHandler("buttonApply") - void buttonApplyHandler(ClickEvent e) { - if (userDataPanel.isValid()) { - user = userDataPanel.getUser(); - Services services = new Services("Create RODA user", "create"); - CreateUserRequest userOperations = new CreateUserRequest(user.getEmail(), user.getName(), user.getFullName(), - user.getDirectRoles(), user.getGroups(), user.isGuest(), null, userDataPanel.getUserExtra()); - services.membersResource(s -> s.createUser(userOperations, LocaleInfo.getCurrentLocale().getLocaleName())) - .whenComplete((createdUser, error) -> { - if (createdUser != null) { - HistoryUtils.newHistory(MemberManagement.RESOLVER); - } else if (error != null) { - errorMessage(error); - } - }); - } - } - - @UiHandler("buttonCancel") - void buttonCancelHandler(ClickEvent e) { - cancel(); - } - - private void cancel() { - HistoryUtils.newHistory(MemberManagement.RESOLVER); - } - - private void errorMessage(Throwable caught) { - if (caught instanceof EmailAlreadyExistsException) { - Toast.showError(messages.createUserEmailAlreadyExists(user.getEmail())); - } else if (caught instanceof UserAlreadyExistsException) { - Toast.showError(messages.createUserAlreadyExists(user.getId())); - } else { - Toast.showError(messages.createUserFailure(caught.getMessage())); - } - } - -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateUser.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateUser.ui.xml deleted file mode 100644 index 7d1073363e..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateUser.ui.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateUserPanel.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateUserPanel.ui.xml deleted file mode 100644 index 5afd8d520c..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateUserPanel.ui.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - .section { - margin-top: 20px; - } - - - - - - - - - - - * - - - - - - * - - - - - - * - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/EditGroup.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/EditGroup.java deleted file mode 100644 index 0ab478ebb7..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/EditGroup.java +++ /dev/null @@ -1,161 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE file at the root of the source - * tree and available online at - * - * https://github.com/keeps/roda - */ -/** - * - */ -package org.roda.wui.client.management; - -import java.util.List; - -import org.roda.core.data.exceptions.NotFoundException; -import org.roda.core.data.v2.user.Group; -import org.roda.wui.client.common.UserLogin; -import org.roda.wui.client.services.Services; -import org.roda.wui.common.client.HistoryResolver; -import org.roda.wui.common.client.tools.HistoryUtils; -import org.roda.wui.common.client.tools.ListUtils; -import org.roda.wui.common.client.widgets.Toast; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.uibinder.client.UiBinder; -import com.google.gwt.uibinder.client.UiField; -import com.google.gwt.uibinder.client.UiHandler; -import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.Button; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.Widget; - -import config.i18n.client.ClientMessages; - -/** - * @author Luis Faria - * - */ -public class EditGroup extends Composite { - - public static final HistoryResolver RESOLVER = new HistoryResolver() { - - @Override - public void resolve(List historyTokens, final AsyncCallback callback) { - if (historyTokens.size() == 1) { - String groupname = historyTokens.get(0); - Services services = new Services("Get Group", "get"); - services.membersResource(s -> s.getGroup(groupname)).whenComplete((group, error) -> { - if (group != null) { - EditGroup editGroup = new EditGroup(group); - callback.onSuccess(editGroup); - } else if (error != null) { - callback.onFailure(error); - } - }); - } else { - HistoryUtils.newHistory(MemberManagement.RESOLVER); - callback.onSuccess(null); - } - - } - - @Override - public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); - } - - @Override - public List getHistoryPath() { - return ListUtils.concat(MemberManagement.RESOLVER.getHistoryPath(), getHistoryToken()); - } - - @Override - public String getHistoryToken() { - return "edit_group"; - } - }; - - interface MyUiBinder extends UiBinder { - } - - private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - - private Group group; - - private static final ClientMessages messages = GWT.create(ClientMessages.class); - - @UiField - Button buttonApply; - - @UiField - Button buttonCancel; - - @UiField(provided = true) - GroupDataPanel groupDataPanel; - - /** - * Create a new panel to edit a group - * - * @param group - * the group to edit - */ - public EditGroup(Group group) { - this.group = group; - - this.groupDataPanel = new GroupDataPanel(true, true); - this.groupDataPanel.setGroup(group); - - initWidget(uiBinder.createAndBindUi(this)); - } - - @UiHandler("buttonApply") - void buttonApplyHandler(ClickEvent e) { - if (groupDataPanel.isChanged()) { - if (groupDataPanel.isValid()) { - group = groupDataPanel.getGroup(); - Services services = new Services("Update group", "update"); - services.membersResource(s -> s.updateGroup(group)).whenComplete((res, error) -> { - if (error == null) { - HistoryUtils.newHistory(MemberManagement.RESOLVER); - } else { - errorMessage(error); - } - }); - } - } else { - HistoryUtils.newHistory(MemberManagement.RESOLVER); - } - } - - @UiHandler("buttonRemove") - void buttonRemoveHandler(ClickEvent e) { - Services services = new Services("Delete group", "delete"); - services.membersResource(s -> s.deleteGroup(group.getId())).whenComplete((res, error) -> { - if (error == null) { - HistoryUtils.newHistory(MemberManagement.RESOLVER); - } else { - errorMessage(error); - } - }); - } - - @UiHandler("buttonCancel") - void buttonCancelHandler(ClickEvent e) { - cancel(); - } - - private void cancel() { - HistoryUtils.newHistory(ShowGroup.RESOLVER, group.getId()); - } - - private void errorMessage(Throwable caught) { - if (caught instanceof NotFoundException) { - Toast.showError(messages.editGroupNotFound(group.getName())); - cancel(); - } else { - Toast.showError(messages.editGroupFailure(EditGroup.this.group.getName(), caught.getMessage())); - } - } -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/EditGroup.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/EditGroup.ui.xml deleted file mode 100644 index e51a43cbfe..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/EditGroup.ui.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/EditUser.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/EditUser.java deleted file mode 100644 index 550e663c4e..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/EditUser.java +++ /dev/null @@ -1,241 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE file at the root of the source - * tree and available online at - * - * https://github.com/keeps/roda - */ -/** - * - */ -package org.roda.wui.client.management; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; - -import org.roda.core.data.common.SecureString; -import org.roda.core.data.exceptions.AlreadyExistsException; -import org.roda.core.data.exceptions.NotFoundException; -import org.roda.core.data.v2.generics.select.SelectedItemsListRequest; -import org.roda.core.data.v2.user.User; -import org.roda.core.data.v2.user.requests.ChangeUserStatusRequest; -import org.roda.core.data.v2.user.requests.UpdateUserRequest; -import org.roda.wui.client.common.UserLogin; -import org.roda.wui.client.common.dialogs.Dialogs; -import org.roda.wui.client.ingest.process.ShowJob; -import org.roda.wui.client.services.Services; -import org.roda.wui.common.client.HistoryResolver; -import org.roda.wui.common.client.tools.HistoryUtils; -import org.roda.wui.common.client.tools.ListUtils; -import org.roda.wui.common.client.widgets.Toast; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.uibinder.client.UiBinder; -import com.google.gwt.uibinder.client.UiField; -import com.google.gwt.uibinder.client.UiHandler; -import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.Button; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.Widget; - -import config.i18n.client.ClientMessages; - -/** - * @author Luis Faria - * - */ -public class EditUser extends Composite { - - public static final HistoryResolver RESOLVER = new HistoryResolver() { - - @Override - public void resolve(List historyTokens, final AsyncCallback callback) { - if (historyTokens.size() == 1) { - String username = historyTokens.get(0); - Services services = new Services("Get User", "get"); - services.membersResource(s -> s.getUser(username)).whenComplete((user, error) -> { - if (user != null) { - EditUser editUser = new EditUser(user); - callback.onSuccess(editUser); - } else if (error != null) { - callback.onFailure(error); - } - }); - } else { - HistoryUtils.newHistory(MemberManagement.RESOLVER); - callback.onSuccess(null); - } - } - - @Override - public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); - } - - @Override - public List getHistoryPath() { - return ListUtils.concat(MemberManagement.RESOLVER.getHistoryPath(), getHistoryToken()); - } - - @Override - public String getHistoryToken() { - return "edit_user"; - } - }; - - interface MyUiBinder extends UiBinder { - } - - private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - - private final User user; - - private static final ClientMessages messages = GWT.create(ClientMessages.class); - - @UiField - Button buttonApply; - - @UiField - Button buttonDeActivate; - - @UiField - Button buttonRemove; - - @UiField - Button buttonCancel; - - @UiField(provided = true) - UserDataPanel userDataPanel; - - /** - * Create a new panel to edit a user - * - * @param user - * the user to edit - */ - public EditUser(User user) { - this.user = user; - this.userDataPanel = new UserDataPanel(true, true, true); - this.userDataPanel.setUser(user); - if (user.getExtra() != null) { - this.userDataPanel.setUserExtra(user.getExtra()); - } else { - this.userDataPanel.setUserExtra(new HashSet<>()); - } - - initWidget(uiBinder.createAndBindUi(this)); - - userDataPanel.setUsernameReadOnly(true); - - buttonDeActivate.setEnabled(true); - if (user.isActive()) { - buttonDeActivate.setText(messages.editUserDeactivate()); - } - } - - private SecureString getPassword() { - if (userDataPanel.getPassword() != null) { - return new SecureString(userDataPanel.getPassword().toCharArray()); - } else { - return null; - } - } - - @UiHandler("buttonApply") - void buttonApplyHandler(ClickEvent e) { - if (userDataPanel.isChanged()) { - if (userDataPanel.isValid()) { - final User updatedUser = userDataPanel.getUser(); - try (SecureString password = getPassword()) { - Services services = new Services("Update User", "update"); - UpdateUserRequest userOperations = new UpdateUserRequest(updatedUser, password, userDataPanel.getUserExtra()); - services.membersResource(s -> s.updateUser(userOperations)).whenComplete((res, error) -> { - if (error == null) { - HistoryUtils.newHistory(ShowUser.RESOLVER, res.getId()); - } else { - errorMessage(error, updatedUser); - } - }); - } - } else { - HistoryUtils.newHistory(MemberManagement.RESOLVER); - } - } - } - - @UiHandler("buttonDeActivate") - void buttonDeActivateHandler(ClickEvent e) { - user.setActive(!user.isActive()); - Services services = new Services("Update User", "update"); - - ChangeUserStatusRequest request = new ChangeUserStatusRequest( - new SelectedItemsListRequest(Arrays.asList(user.getUUID())), user.isActive()); - services.membersResource(s -> s.changeActive(request)).whenComplete((res, error) -> { - if (error == null) { - Toast.showInfo(messages.runningInBackgroundTitle(), messages.runningInBackgroundDescription()); - Dialogs.showJobRedirectDialog(messages.jobCreatedMessage(), new AsyncCallback() { - - @Override - public void onFailure(Throwable caught) { - HistoryUtils.newHistory(MemberManagement.RESOLVER); - } - - @Override - public void onSuccess(final Void nothing) { - HistoryUtils.newHistory(ShowJob.RESOLVER, res.getId()); - } - }); - - HistoryUtils.newHistory(MemberManagement.RESOLVER); - } - }); - } - - @UiHandler("buttonRemove") - void buttonRemoveHandler(ClickEvent e) { - Dialogs.showConfirmDialog(messages.userRemoveConfirmDialogTitle(), messages.userRemoveConfirmDialogMessage(), - messages.dialogNo(), messages.dialogYes(), new AsyncCallback() { - @Override - public void onSuccess(Boolean confirmed) { - if (confirmed) { - Services services = new Services("Delete user", "delete"); - services.membersResource(s -> s.deleteUser(user.getId())).whenComplete((res, error) -> { - if (error == null) { - HistoryUtils.newHistory(MemberManagement.RESOLVER); - } else { - errorMessage(error, null); - } - }); - } - } - - @Override - public void onFailure(Throwable caught) { - errorMessage(caught, null); - } - }); - } - - @UiHandler("buttonCancel") - void buttonCancelHandler(ClickEvent e) { - cancel(); - } - - private void cancel() { - HistoryUtils.newHistory(ShowUser.RESOLVER, user.getId()); - } - - private void errorMessage(Throwable caught, User modifiedUser) { - if (caught instanceof NotFoundException) { - Toast.showError(messages.editUserNotFound(user.getName())); - cancel(); - } else if (caught instanceof AlreadyExistsException) { - String email = (modifiedUser != null) ? modifiedUser.getEmail() : user.getEmail(); - Toast.showError(messages.editUserEmailAlreadyExists(email)); - } else { - Toast.showError(messages.editUserFailure(EditUser.this.user.getName(), caught.getMessage())); - } - } -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/EditUser.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/EditUser.ui.xml deleted file mode 100644 index 86a9a8cb13..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/EditUser.ui.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/GroupDataPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/GroupDataPanel.java deleted file mode 100644 index 9a1aa7fe3e..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/GroupDataPanel.java +++ /dev/null @@ -1,268 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE file at the root of the source - * tree and available online at - * - * https://github.com/keeps/roda - */ -/** - * - */ -package org.roda.wui.client.management; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.roda.core.data.common.RodaConstants; -import org.roda.core.data.v2.user.Group; -import org.roda.wui.client.common.utils.AsyncCallbackUtils; -import org.roda.wui.common.client.ClientLogger; -import org.roda.wui.common.client.tools.HistoryUtils; -import org.roda.wui.common.client.tools.StringUtils; -import org.roda.wui.common.client.widgets.wcag.WCAGUtilities; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.event.dom.client.ChangeEvent; -import com.google.gwt.event.dom.client.ChangeHandler; -import com.google.gwt.event.dom.client.KeyUpEvent; -import com.google.gwt.event.dom.client.KeyUpHandler; -import com.google.gwt.event.logical.shared.HasValueChangeHandlers; -import com.google.gwt.event.logical.shared.ValueChangeEvent; -import com.google.gwt.event.logical.shared.ValueChangeHandler; -import com.google.gwt.event.shared.HandlerRegistration; -import com.google.gwt.uibinder.client.UiBinder; -import com.google.gwt.uibinder.client.UiField; -import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.TextBox; -import com.google.gwt.user.client.ui.Widget; - -/** - * @author Luis Faria - * - */ -public class GroupDataPanel extends Composite implements HasValueChangeHandlers { - - interface MyUiBinder extends UiBinder { - } - - private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - - private Group group = new Group(); - - @UiField - TextBox groupname; - - @UiField - TextBox fullname; - - @UiField - Label usersLabel, usersValue; - - @UiField - FlowPanel permissionsSelectPanel; - - @UiField - PermissionsPanel permissionsPanel; - - @SuppressWarnings("unused") - private ClientLogger logger = new ClientLogger(getClass().getName()); - - private boolean editmode; - - private boolean changed = false; - private boolean checked = false; - - /** - * Create a new group data panel - * - * @param editmode - * if group name should be editable - */ - public GroupDataPanel(boolean editmode) { - this(true, editmode); - } - - /** - * - * @param visible - * @param editmode - */ - public GroupDataPanel(boolean visible, boolean editmode) { - - initWidget(uiBinder.createAndBindUi(this)); - - this.editmode = editmode; - super.setVisible(visible); - - ChangeHandler changeHandler = new ChangeHandler() { - - @Override - public void onChange(ChangeEvent event) { - GroupDataPanel.this.onChange(); - } - }; - - KeyUpHandler keyUpHandler = new KeyUpHandler() { - - @Override - public void onKeyUp(KeyUpEvent event) { - onChange(); - } - }; - - groupname.setEnabled(!isEditmode()); - groupname.addKeyDownHandler(new UserAndGroupKeyDownHandler()); - groupname.addChangeHandler(changeHandler); - groupname.addKeyUpHandler(keyUpHandler); - fullname.addChangeHandler(changeHandler); - fullname.addKeyUpHandler(keyUpHandler); - - permissionsPanel.addValueChangeHandler(new ValueChangeHandler>() { - - @Override - public void onValueChange(ValueChangeEvent> event) { - onChange(); - } - }); - } - - /** - * Set group information of group - * - * @param group - */ - public void setGroup(Group group) { - this.group = group; - this.groupname.setText(group.getName()); - this.fullname.setText(group.getFullName()); - this.usersValue.setText(StringUtils.prettyPrint(group.getUsers())); - - this.setPermissions(group.getName(), group.getDirectRoles(), group.getAllRoles()); - - // update visibility - this.usersLabel.setVisible(!group.getUsers().isEmpty()); - this.usersValue.setVisible(!group.getUsers().isEmpty()); - - } - - private void setPermissions(final String name, final Set directRoles, final Set allRoles) { - permissionsPanel.init(new AsyncCallback() { - - @Override - public void onSuccess(Boolean result) { - Set indirectRoles = new HashSet<>(allRoles); - indirectRoles.removeAll(directRoles); - permissionsPanel.checkPermissions(directRoles, RodaConstants.ADMINISTRATORS.equals(name)); - permissionsPanel.checkPermissions(indirectRoles, true); - WCAGUtilities.getInstance().makeAccessible(permissionsSelectPanel.getElement()); - } - - @Override - public void onFailure(Throwable caught) { - AsyncCallbackUtils.defaultFailureTreatment(caught); - HistoryUtils.newHistory(MemberManagement.RESOLVER); - } - }); - } - - /** - * Get group defined by this panel. This panel defines: name, fullname - * - * @return the group modified by this panel - */ - public Group getGroup() { - group.setId(groupname.getText()); - group.setName(groupname.getText()); - group.setFullName(fullname.getText()); - group.setDirectRoles(permissionsPanel.getDirectRoles()); - return group; - } - - /** - * Is group data panel valid - * - * @return true if valid - */ - public boolean isValid() { - boolean valid = true; - - if (groupname.getText().length() == 0) { - valid = false; - groupname.addStyleName("isWrong"); - } else { - groupname.removeStyleName("isWrong"); - } - - if (fullname.getText().length() == 0) { - valid = false; - fullname.addStyleName("isWrong"); - } else { - fullname.removeStyleName("isWrong"); - } - - checked = true; - return valid; - } - - /** - * Is group name read only - * - * @return true if read only - */ - public boolean isGroupnameReadOnly() { - return groupname.isReadOnly(); - } - - /** - * Set group name read only - * - * @param readonly - */ - public void setGroupnameReadOnly(boolean readonly) { - groupname.setReadOnly(readonly); - } - - public void clear() { - groupname.setText(""); - fullname.setText(""); - } - - /** - * Is group data panel editable, i.e. on create group mode - * - * @return true if editable - */ - public boolean isEditmode() { - return editmode; - } - - /** - * Is group data panel has been changed - * - * @return changed - */ - public boolean isChanged() { - return changed; - } - - @Override - public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) { - return addHandler(handler, ValueChangeEvent.getType()); - } - - protected void onChange() { - changed = true; - if (checked) { - isValid(); - } - ValueChangeEvent.fire(this, getValue()); - } - - public Group getValue() { - return getGroup(); - } -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/Management.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/Management.java index 3d93c3ff7d..94814d218d 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/Management.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/Management.java @@ -16,6 +16,7 @@ import org.roda.wui.client.common.UserLogin; import org.roda.wui.client.management.distributed.DistributedInstancesManagement; import org.roda.wui.client.management.distributed.LocalInstanceManagement; +import org.roda.wui.client.management.members.MemberManagement; import org.roda.wui.client.process.ActionProcess; import org.roda.wui.client.process.CreateSelectedJob; import org.roda.wui.client.process.InternalProcess; diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowGroup.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowGroup.java deleted file mode 100644 index decb3e1dc1..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowGroup.java +++ /dev/null @@ -1,178 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE file at the root of the source - * tree and available online at - * - * https://github.com/keeps/roda - */ -package org.roda.wui.client.management; - -import java.util.List; -import java.util.MissingResourceException; -import java.util.Set; - -import org.roda.core.data.v2.user.Group; -import org.roda.core.data.v2.user.RODAMember; -import org.roda.wui.client.common.NoAsyncCallback; -import org.roda.wui.client.common.UserLogin; -import org.roda.wui.client.common.actions.Actionable; -import org.roda.wui.client.common.actions.RODAMemberActions; -import org.roda.wui.client.common.actions.model.ActionableObject; -import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; -import org.roda.wui.client.common.utils.SidebarUtils; -import org.roda.wui.client.services.Services; -import org.roda.wui.common.client.HistoryResolver; -import org.roda.wui.common.client.tools.ConfigurationManager; -import org.roda.wui.common.client.tools.HistoryUtils; -import org.roda.wui.common.client.tools.ListUtils; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.uibinder.client.UiBinder; -import com.google.gwt.uibinder.client.UiField; -import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.InlineHTML; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.SimplePanel; -import com.google.gwt.user.client.ui.Widget; - -import config.i18n.client.ClientMessages; - -/** - * @author Gabriel Barros - */ -public class ShowGroup extends Composite { - public static final HistoryResolver RESOLVER = new HistoryResolver() { - - @Override - public void resolve(List historyTokens, final AsyncCallback callback) { - if (historyTokens.size() == 1) { - String groupname = historyTokens.get(0); - Services services = new Services("Get Group", "get"); - services.membersResource(s -> s.getGroup(groupname)).whenComplete((group, error) -> { - if (group != null) { - ShowGroup showGroup = new ShowGroup(group); - callback.onSuccess(showGroup); - } else if (error != null) { - callback.onFailure(error); - } - }); - } else { - HistoryUtils.newHistory(MemberManagement.RESOLVER); - callback.onSuccess(null); - } - - } - - @Override - public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); - } - - @Override - public List getHistoryPath() { - return ListUtils.concat(MemberManagement.RESOLVER.getHistoryPath(), getHistoryToken()); - } - - @Override - public String getHistoryToken() { - return "show_group"; - } - }; - - interface MyUiBinder extends UiBinder { - } - - private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - - private ActionableWidgetBuilder actionableWidgetBuilder; - private Group group; - - private static final ClientMessages messages = GWT.create(ClientMessages.class); - - @UiField - Label fullnameValue, groupNameValue; - - @UiField - FlowPanel userList, permissionList; - @UiField - SimplePanel actionsSidebar; - - @UiField - FlowPanel contentFlowPanel; - - @UiField - FlowPanel sidebarFlowPanel; - - public ShowGroup(Group group) { - this.group = group; - initWidget(uiBinder.createAndBindUi(this)); - initElements(); - } - - private void initElements() { - groupNameValue.setText(group.getName()); - fullnameValue.setText(group.getFullName()); - - if (group.getUsers().isEmpty()) { - userList.add(new Label(messages.showGroupEmptyUserList())); - } else { - for (String user : group.getUsers()) { - userList.add(createListItem(user)); - } - } - - // Permissions - buildPermissionList(); - - // Sidebar - RODAMemberActions rodaMemberActions = RODAMemberActions.get(); - actionableWidgetBuilder = new ActionableWidgetBuilder<>(rodaMemberActions).withBackButton() - .withActionCallback(new NoAsyncCallback() { - @Override - public void onSuccess(Actionable.ActionImpact result) { - if (result.equals(Actionable.ActionImpact.DESTROYED)) { - HistoryUtils.newHistory(MemberManagement.RESOLVER); - } - } - }); - - SidebarUtils.toggleSidebar(contentFlowPanel, sidebarFlowPanel, rodaMemberActions.hasAnyRoles()); - actionsSidebar.setWidget(actionableWidgetBuilder.buildListWithObjects(new ActionableObject<>(this.group))); - } - - private void buildPermissionList() { - Set allGroupRoles = group.getAllRoles(); - - if (allGroupRoles.isEmpty()) { - permissionList.add(new Label(messages.showGroupEmptyPermissions())); - } else { - List roles = ConfigurationManager.getStringList("ui.role"); - for (String role : roles) { - String description; - try { - description = messages.role(role); - } catch (MissingResourceException e) { - description = role + " (needs translation)"; - } - if (allGroupRoles.contains(role)) { - permissionList.add(createListItem(description)); - } - } - } - } - - private FlowPanel createListItem(String item) { - FlowPanel panel = new FlowPanel(); - InlineHTML bullet = new InlineHTML("•"); - InlineHTML value = new InlineHTML(item); - - bullet.addStyleName("bullet"); - panel.add(bullet); - panel.add(value); - - return panel; - } - -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowGroup.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowGroup.ui.xml deleted file mode 100644 index ac1e565ea5..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowGroup.ui.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowLogEntry.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowLogEntry.java index cf86182336..bd2ac3a59b 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowLogEntry.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowLogEntry.java @@ -74,7 +74,7 @@ public void resolve(List historyTokens, final AsyncCallback call @Override public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); + UserLogin.getInstance().checkRoles(new HistoryResolver[] {Management.RESOLVER}, false, callback); } @Override @@ -87,13 +87,8 @@ public String getHistoryToken() { return "logentry"; } }; - - interface MyUiBinder extends UiBinder { - } - private static final MyUiBinder uiBinder = GWT.create(MyUiBinder.class); private static final ClientMessages messages = GWT.create(ClientMessages.class); - @UiField Label logIdLabel; @UiField @@ -144,7 +139,6 @@ interface MyUiBinder extends UiBinder { SimplePanel expandedAuditLogs; @UiField SimplePanel expandedAuditLogsList; - /** * Create a new panel to view a log entry * @@ -230,4 +224,7 @@ public ShowLogEntry(LogEntry logEntry) { } } + interface MyUiBinder extends UiBinder { + } + } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowNotification.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowNotification.java index e134bc00ff..12a9679b3d 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowNotification.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowNotification.java @@ -69,7 +69,7 @@ public void resolve(List historyTokens, final AsyncCallback call @Override public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); + UserLogin.getInstance().checkRole(this, callback); } @Override diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowUser.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowUser.java deleted file mode 100644 index b242ed820d..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowUser.java +++ /dev/null @@ -1,192 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE file at the root of the source - * tree and available online at - * - * https://github.com/keeps/roda - */ -package org.roda.wui.client.management; - -import java.util.List; -import java.util.MissingResourceException; -import java.util.Set; - -import org.roda.core.data.v2.user.RODAMember; -import org.roda.core.data.v2.user.User; -import org.roda.wui.client.common.NoAsyncCallback; -import org.roda.wui.client.common.UserLogin; -import org.roda.wui.client.common.actions.Actionable; -import org.roda.wui.client.common.actions.RODAMemberActions; -import org.roda.wui.client.common.actions.model.ActionableObject; -import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; -import org.roda.wui.client.common.utils.HtmlSnippetUtils; -import org.roda.wui.client.common.utils.SidebarUtils; -import org.roda.wui.client.management.access.AccessKeyTablePanel; -import org.roda.wui.client.services.Services; -import org.roda.wui.common.client.HistoryResolver; -import org.roda.wui.common.client.tools.ConfigurationManager; -import org.roda.wui.common.client.tools.HistoryUtils; -import org.roda.wui.common.client.tools.ListUtils; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.uibinder.client.UiBinder; -import com.google.gwt.uibinder.client.UiField; -import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.InlineHTML; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.SimplePanel; -import com.google.gwt.user.client.ui.Widget; - -import config.i18n.client.ClientMessages; - -/** - * @author Gabriel Barros - */ -public class ShowUser extends Composite { - public static final HistoryResolver RESOLVER = new HistoryResolver() { - - @Override - public void resolve(List historyTokens, final AsyncCallback callback) { - if (historyTokens.size() == 1) { - String username = historyTokens.get(0); - Services services = new Services("Get User", "get"); - services.membersResource(s -> s.getUser(username)).whenComplete((user, error) -> { - if (user != null) { - ShowUser showUser = new ShowUser(user); - callback.onSuccess(showUser); - } else if (error != null) { - callback.onFailure(error); - } - }); - } else { - HistoryUtils.newHistory(MemberManagement.RESOLVER); - callback.onSuccess(null); - } - } - - @Override - public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); - } - - @Override - public List getHistoryPath() { - return ListUtils.concat(MemberManagement.RESOLVER.getHistoryPath(), getHistoryToken()); - } - - @Override - public String getHistoryToken() { - return "show_user"; - } - }; - private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - private final User user; - @UiField - Label userNameValue; - @UiField - Label fullnameValue; - @UiField - Label emailValue; - @UiField - HTML stateValue; - @UiField - FlowPanel extraValue; - @UiField - FlowPanel permissionList; - @UiField - FlowPanel groupList; - @UiField - FlowPanel accessKeyTablePanel; - @UiField - SimplePanel actionsSidebar; - @UiField - FlowPanel contentFlowPanel; - @UiField - FlowPanel sidebarFlowPanel; - - public ShowUser(User user) { - this.user = user; - initWidget(uiBinder.createAndBindUi(this)); - initElements(); - } - - private void initElements() { - userNameValue.setText(user.getName()); - fullnameValue.setText(user.getFullName()); - emailValue.setText(user.getEmail()); - stateValue.setHTML(HtmlSnippetUtils.getUserStateHtml(user)); - - if (!user.getExtra().isEmpty()) { - HtmlSnippetUtils.createExtraShow(extraValue, user.getExtra(), false); - } - - // Groups - if (user.getGroups().isEmpty()) { - groupList.add(new Label(messages.showUserEmptyGroupList())); - } else { - for (String group : user.getGroups()) { - groupList.add(createListItem(group)); - } - } - - // Permissions - buildPermissionList(); - - accessKeyTablePanel.add(new AccessKeyTablePanel(user.getId())); - - // Sidebar - RODAMemberActions rodaMemberActions = RODAMemberActions.get(); - ActionableWidgetBuilder actionableWidgetBuilder = new ActionableWidgetBuilder<>(rodaMemberActions) - .withBackButton().withActionCallback(new NoAsyncCallback() { - @Override - public void onSuccess(Actionable.ActionImpact result) { - if (result.equals(Actionable.ActionImpact.DESTROYED)) { - HistoryUtils.newHistory(MemberManagement.RESOLVER); - } - } - }); - - SidebarUtils.toggleSidebar(contentFlowPanel, sidebarFlowPanel, rodaMemberActions.hasAnyRoles()); - actionsSidebar.setWidget(actionableWidgetBuilder.buildListWithObjects(new ActionableObject<>(this.user))); - } - - private void buildPermissionList() { - Set allUserRoles = user.getAllRoles(); - - if (allUserRoles.isEmpty()) { - permissionList.add(new Label(messages.showUserEmptyPermissions())); - } else { - List roles = ConfigurationManager.getStringList("ui.role"); - for (String role : roles) { - String description; - try { - description = messages.role(role); - } catch (MissingResourceException e) { - description = role + " (needs translation)"; - } - if (allUserRoles.contains(role)) { - permissionList.add(createListItem(description)); - } - } - } - } - - private FlowPanel createListItem(String item) { - FlowPanel panel = new FlowPanel(); - InlineHTML bullet = new InlineHTML("•"); - InlineHTML value = new InlineHTML(item); - - bullet.addStyleName("bullet"); - panel.add(bullet); - panel.add(value); - - return panel; - } - - interface MyUiBinder extends UiBinder { - } -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowUser.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowUser.ui.xml deleted file mode 100644 index 91b25185e6..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowUser.ui.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/AccessKeyDataPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/AccessKeyDataPanel.java deleted file mode 100644 index b8276c495b..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/AccessKeyDataPanel.java +++ /dev/null @@ -1,192 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE file at the root of the source - * tree and available online at - * - * https://github.com/keeps/roda - */ -package org.roda.wui.client.management.access; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import org.roda.core.data.v2.accessKey.AccessKey; -import org.roda.wui.common.client.tools.StringUtils; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.event.dom.client.ChangeEvent; -import com.google.gwt.event.dom.client.ChangeHandler; -import com.google.gwt.event.dom.client.KeyUpEvent; -import com.google.gwt.event.dom.client.KeyUpHandler; -import com.google.gwt.event.logical.shared.HasValueChangeHandlers; -import com.google.gwt.event.logical.shared.ValueChangeEvent; -import com.google.gwt.event.logical.shared.ValueChangeHandler; -import com.google.gwt.event.shared.HandlerRegistration; -import com.google.gwt.i18n.client.DateTimeFormat; -import com.google.gwt.uibinder.client.UiBinder; -import com.google.gwt.uibinder.client.UiField; -import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.TextBox; -import com.google.gwt.user.client.ui.Widget; -import com.google.gwt.user.datepicker.client.DateBox; - -import config.i18n.client.ClientMessages; - -/** - * @author Gabriel Barros - */ -public class AccessKeyDataPanel extends Composite implements HasValueChangeHandlers { - public static final String IS_WRONG = "isWrong"; - - interface MyUiBinder extends UiBinder { - } - - private static AccessKeyDataPanel.MyUiBinder uiBinder = GWT.create(AccessKeyDataPanel.MyUiBinder.class); - - private static final ClientMessages messages = GWT.create(ClientMessages.class); - - @UiField - TextBox name; - - @UiField - Label nameError; - - @UiField - DateBox expirationDate; - - @UiField - Label expirationDateError; - - @UiField - HTML errors; - - private final boolean editMode; - - private boolean changed = false; - private boolean checked = false; - - public AccessKeyDataPanel(AccessKey accessKey, boolean editMode) { - initWidget(uiBinder.createAndBindUi(this)); - - this.editMode = editMode; - - setInitialState(accessKey); - initHandlers(); - - if (editMode) { - setAccessKey(accessKey); - } - } - - private void initHandlers() { - ChangeHandler changeHandler = new ChangeHandler() { - @Override - public void onChange(ChangeEvent changeEvent) { - AccessKeyDataPanel.this.onChange(); - } - }; - - KeyUpHandler keyUpHandler = new KeyUpHandler() { - @Override - public void onKeyUp(KeyUpEvent keyUpEvent) { - AccessKeyDataPanel.this.onChange(); - } - }; - } - - private void setInitialState(AccessKey accessKey) { - errors.setVisible(false); - - DateBox.DefaultFormat dateFormat = new DateBox.DefaultFormat(DateTimeFormat.getFormat("yyyy-MM-dd")); - expirationDate.setFormat(dateFormat); - expirationDate.getDatePicker().setYearArrowsVisible(true); - expirationDate.setFireNullValues(true); - expirationDate.setValue(new Date()); - - expirationDate.addValueChangeHandler(new ValueChangeHandler() { - @Override - public void onValueChange(ValueChangeEvent valueChangeEvent) { - onChange(); - } - }); - } - - public void setAccessKey(AccessKey accessKey) { - this.name.setText(accessKey.getName()); - this.expirationDate.setValue(accessKey.getExpirationDate()); - } - - public AccessKey getAccessKey() { - AccessKey accessKey = new AccessKey(); - accessKey.setName(name.getText()); - accessKey.setExpirationDate(expirationDate.getValue()); - return accessKey; - } - - public boolean isValid() { - List errorList = new ArrayList<>(); - if (StringUtils.isBlank(name.getText())) { - name.addStyleName(IS_WRONG); - nameError.setText(messages.mandatoryField()); - nameError.setVisible(true); - Window.scrollTo(name.getAbsoluteLeft(), name.getAbsoluteTop()); - errorList.add(messages.isAMandatoryField(messages.distributedInstanceNameLabel())); - } else { - name.removeStyleName(IS_WRONG); - nameError.setVisible(false); - } - - if (expirationDate.getValue() == null || expirationDate.getValue().before(new Date())) { - expirationDate.addStyleName("isWrong"); - expirationDateError.setVisible(true); - expirationDateError.setText(messages.mandatoryField()); - errorList.add(messages.isAMandatoryField(messages.accessKeyExpirationDateLabel())); - } else { - expirationDate.removeStyleName("isWrong"); - expirationDateError.setVisible(false); - } - - return errorList.isEmpty(); - } - - @Override - public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) { - return addHandler(handler, ValueChangeEvent.getType()); - } - - // @UiHandler("buttonAddMember") - // void buttonAddMemberHandler(ClickEvent e) { - // Filter filter = new Filter(); - // filter.add(new SimpleFilterParameter(RodaConstants.MEMBERS_IS_USER, "true")); - // - // MemberSelectDialog selectDialog = new - // MemberSelectDialog(messages.selectUserOrGroupToAdd(), filter); - // selectDialog.showAndCenter(); - // selectDialog.addValueChangeHandler(new ValueChangeHandler() { - // - // @Override - // public void onValueChange(ValueChangeEvent event) { - // RODAMember selected = event.getValue(); - // if (selected != null) { - // user.setText(selected.getName()); - // } - // } - // }); - // } - - protected void onChange() { - changed = true; - if (checked) { - isValid(); - } - ValueChangeEvent.fire(this, getValue()); - } - - public AccessKey getValue() { - return getAccessKey(); - } -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/AccessKeyDataPanel.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/AccessKeyDataPanel.ui.xml deleted file mode 100644 index c72a34989e..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/AccessKeyDataPanel.ui.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - .section { - margin-top: 20px; - } - - .group { - display: flex; - } - - - - - - - - - - - - * - - - - - - - * - - - - - - - - - - - diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/AccessKeyTablePanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/AccessKeyTablePanel.java index 33038e997f..a57838a1c2 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/AccessKeyTablePanel.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/AccessKeyTablePanel.java @@ -7,14 +7,20 @@ */ package org.roda.wui.client.management.access; -import java.util.List; - +import com.google.gwt.user.client.rpc.AsyncCallback; +import org.roda.core.data.common.RodaConstants; import org.roda.core.data.v2.accessKey.AccessKey; +import org.roda.core.data.v2.accessKey.AccessKeyStatus; import org.roda.core.data.v2.accessKey.AccessKeys; +import org.roda.core.data.v2.accessKey.CreateAccessKeyRequest; +import org.roda.wui.client.common.NoAsyncCallback; +import org.roda.wui.client.common.dialogs.AccessKeyDialogs; +import org.roda.wui.client.common.dialogs.Dialogs; +import org.roda.wui.client.common.lists.utils.ActionMenuCell; import org.roda.wui.client.common.lists.utils.BasicTablePanel; import org.roda.wui.client.common.utils.HtmlSnippetUtils; +import org.roda.wui.client.common.utils.PermissionClientUtils; import org.roda.wui.client.services.Services; -import org.roda.wui.common.client.tools.HistoryUtils; import org.roda.wui.common.client.tools.Humanize; import com.google.gwt.cell.client.SafeHtmlCell; @@ -25,44 +31,53 @@ import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.cellview.client.Column; import com.google.gwt.user.cellview.client.TextColumn; +import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.Widget; import config.i18n.client.ClientMessages; +import org.roda.wui.common.client.widgets.Toast; /** * @author Gabriel Barros */ public class AccessKeyTablePanel extends Composite { private static final ClientMessages messages = GWT.create(ClientMessages.class); - - interface MyUiBinder extends UiBinder { - } - private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - @UiField FlowPanel contentFlowPanel; + private BasicTablePanel table; + private String username; + public AccessKeyTablePanel(String username) { initWidget(uiBinder.createAndBindUi(this)); + this.username = username; + + // Call the new unified refresh method + refresh(); + } + + public void refresh() { Services services = new Services("Get user access keys", "get"); services.membersResource(s -> s.getAccessKeysByUser(username)).whenComplete((accessKeys, error) -> { if (accessKeys != null) { + // Clear the panel and rebuild the table/empty state from scratch contentFlowPanel.clear(); contentFlowPanel.add(createTable(accessKeys)); + } else if (error != null) { + Toast.showError(error.getMessage()); } }); } public ScrollPanel createTable(AccessKeys accessKeys) { ScrollPanel scrollPanel = new ScrollPanel(); - scrollPanel.addStyleName("basicTable-border"); - scrollPanel.addStyleName("basicTable"); if (accessKeys.getObjects().isEmpty()) { String someOfAObject = messages.someOfAObject(accessKeys.getClass().getName()); @@ -71,15 +86,8 @@ public ScrollPanel createTable(AccessKeys accessKeys) { scrollPanel.add(label); } else { FlowPanel accessKeyPanel = new FlowPanel(); - BasicTablePanel table = getBasicTableForAccessKey(accessKeys); - table.getSelectionModel().addSelectionChangeHandler(event -> { - AccessKey selectedObject = table.getSelectionModel().getSelectedObject(); - if (selectedObject != null) { - table.getSelectionModel().clear(); - List path = HistoryUtils.getHistory(ShowAccessKey.RESOLVER.getHistoryPath(), selectedObject.getId()); - HistoryUtils.newHistory(path); - } - }); + table = getBasicTableForAccessKey(accessKeys); + table.removeSelectionModel(); accessKeyPanel.add(table); scrollPanel.add(accessKeyPanel); @@ -88,39 +96,215 @@ public ScrollPanel createTable(AccessKeys accessKeys) { return scrollPanel; } + private void showActionsMenu(AccessKey key, int left, int top) { + // 1. Create the Popup + PopupPanel popup = new PopupPanel(true); // true = auto-hide when clicking away + + // 2. Create your FlowPanel and add your action items + FlowPanel menuPanel = new FlowPanel(); + menuPanel.addStyleName("groupedActionableDropdown"); + + Button revokeBtn = new Button(messages.accessKeyRevokeButton()); + revokeBtn.addStyleName("actionable-button actionable-button-updated actionable-button-label btn-edit"); + revokeBtn.addClickHandler(e -> { + popup.hide(); + Dialogs.showConfirmDialog(messages.accessKeyLabel(), messages.accessKeyRevokeConfirmationMessage(), + messages.cancelButton(), messages.confirmButton(), new NoAsyncCallback() { + @Override + public void onSuccess(Boolean confirm) { + if (confirm) { + Services services = new Services("Revoke access key", "revoke"); + services.membersResource(s -> s.revokeAccessKey(key.getId())).whenComplete((response, error) -> { + if (response != null) { + Toast.showInfo(messages.accessKeyLabel(), messages.accessKeySuccessfullyRevoked()); + refresh(); + } else { + Toast.showError(error.getMessage()); + } + }); + } + } + }); + }); + + Button regenBtn = new Button(messages.accessKeyRegenerateButton()); + regenBtn.addStyleName("actionable-button actionable-button-updated actionable-button-label btn-edit"); + regenBtn.addClickHandler(e -> { + popup.hide(); + Dialogs.showConfirmDialog(messages.accessKeyLabel(), messages.accessKeyRegenerateConfirmationMessage(), + messages.cancelButton(), messages.confirmButton(), new NoAsyncCallback() { + @Override + public void onSuccess(Boolean confirm) { + if (confirm) { + AccessKeyDialogs.createAccessKeyDialog(messages.regenerateAccessKeyTitle(), key.getName(), false, + new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + // do nothing + } + + @Override + public void onSuccess(CreateAccessKeyRequest request) { + Services services = new Services("Regenerate access key", "regenerate"); + CreateAccessKeyRequest regenerateAccessKeyRequest = new CreateAccessKeyRequest(); + regenerateAccessKeyRequest.setExpirationDate(request.getExpirationDate()); + services.membersResource(s -> s.regenerateAccessKey(key.getId(), regenerateAccessKeyRequest)) + .whenComplete((response, error) -> { + if (response != null) { + AccessKeyDialogs.showAccessKeyDialog(messages.accessKeyLabel(), response, + new NoAsyncCallback() { + @Override + public void onSuccess(Boolean result) { + Toast.showInfo(messages.accessKeyLabel(), messages.accessKeySuccessfullyRegenerated()); + refresh(); + } + }); + } else { + Toast.showError(error.getMessage()); + } + }); + } + }); + } + } + }); + }); + + Button deleteKeyBtn = new Button(messages.accessKeyDeleteButton()); + deleteKeyBtn.addStyleName("actionable-button actionable-button-updated actionable-button-label btn-edit"); + deleteKeyBtn.addClickHandler(e -> { + popup.hide(); + Dialogs.showConfirmDialog(messages.accessKeyLabel(), messages.accessKeyDeleteConfirmationMessage(), + messages.cancelButton(), messages.confirmButton(), new NoAsyncCallback() { + @Override + public void onSuccess(Boolean confirm) { + if (confirm) { + Services services = new Services("Delete access key", "delete"); + services.membersResource(s -> s.deleteAccessKey(key.getId())).whenComplete((accessKey, error) -> { + if (error != null) { + Toast.showError(error.getMessage()); + } else { + Toast.showInfo(messages.accessKeyLabel(), messages.accessKeySuccessfullyDeleted()); + refresh(); + } + }); + } + } + }); + }); + + if (showRevokeAccessKey(key)) { + menuPanel.add(revokeBtn); + } + + if (showRegenerateAccessKey(key)) { + menuPanel.add(regenBtn); + } + + if (showDeleteAccessKey(key)) { + menuPanel.add(deleteKeyBtn); + } + + // 3. Show the popup at the calculated coordinates + popup.setWidget(menuPanel); + popup.setPopupPosition(left, top); + popup.show(); + } + + private TextColumn getLastUsageDateColumn() { + return new TextColumn() { + @Override + public String getValue(AccessKey accessKey) { + return accessKey.getLastUsageDate() != null ? Humanize.formatDate(accessKey.getLastUsageDate()) + : messages.accessKeyNeverUsedLabel(); + } + }; + } + + private TextColumn getNameColumn() { + return new TextColumn() { + @Override + public String getValue(AccessKey accessKey) { + return accessKey.getName(); + } + }; + } + + private TextColumn getExpirationDateColumn() { + return new TextColumn() { + @Override + public String getValue(AccessKey accessKey) { + return accessKey.getExpirationDate() != null ? Humanize.formatDate(accessKey.getExpirationDate()) + : messages.accessKeyNotFoundLabel(); + } + }; + } + + private Column getStatusColumn() { + return new Column(new SafeHtmlCell()) { + @Override + public SafeHtml getValue(AccessKey accessKey) { + return HtmlSnippetUtils.getAccessKeyStateHtml(accessKey); + } + }; + } + + private Column getActionsColumn() { + ActionMenuCell actionCell = new ActionMenuCell(this::showActionsMenu); + + return new Column(actionCell) { + @Override + public AccessKey getValue(AccessKey object) { + return object; + } + }; + } + + private boolean showActionsColumn() { + return PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_REGENERATE_ACCESS_TOKEN, + RodaConstants.PERMISSION_METHOD_DELETE_ACCESS_TOKEN, RodaConstants.PERMISSION_METHOD_REVOKE_ACCESS_TOKEN); + } + private BasicTablePanel getBasicTableForAccessKey(AccessKeys accessKeys) { if (accessKeys.getObjects().isEmpty()) { return new BasicTablePanel<>(messages.noItemsToDisplay(messages.distributedInstancesLabel())); } else { - return new BasicTablePanel(accessKeys.getObjects().iterator(), - new BasicTablePanel.ColumnInfo(messages.accessKeyNameLabel(), 15, new TextColumn() { - @Override - public String getValue(AccessKey accessKey) { - return accessKey.getName(); - } - }), new BasicTablePanel.ColumnInfo(messages.accessKeyLastUsageDateLabel(), 15, - new TextColumn() { - @Override - public String getValue(AccessKey accessKey) { - return accessKey.getLastUsageDate() != null ? Humanize.formatDate(accessKey.getLastUsageDate()) - : messages.accessKeyNeverUsedLabel(); - } - }), + return new BasicTablePanel<>(accessKeys.getObjects().iterator(), + new BasicTablePanel.ColumnInfo(messages.accessKeyNameLabel(), 15, getNameColumn()), + new BasicTablePanel.ColumnInfo(messages.accessKeyLastUsageDateLabel(), 15, getLastUsageDateColumn()), new BasicTablePanel.ColumnInfo(messages.accessKeyExpirationDateLabel(), 15, - new TextColumn() { - @Override - public String getValue(AccessKey accessKey) { - return accessKey.getExpirationDate() != null ? Humanize.formatDate(accessKey.getExpirationDate()) - : messages.accessKeyNotFoundLabel(); - } - }), - new BasicTablePanel.ColumnInfo(messages.accessKeyStatusLabel(), 15, - new Column(new SafeHtmlCell()) { - @Override - public SafeHtml getValue(AccessKey accessKey) { - return HtmlSnippetUtils.getAccessKeyStateHtml(accessKey); - } - })); + getExpirationDateColumn()), + new BasicTablePanel.ColumnInfo(messages.accessKeyStatusLabel(), 15, getStatusColumn()), + new BasicTablePanel.ColumnInfo(messages.actions(), !showActionsColumn(), 15, getActionsColumn())); + } + } + + private boolean showRevokeAccessKey(AccessKey accessKey) { + switch (accessKey.getStatus()) { + case CREATED: + case ACTIVE: + case INACTIVE: + return true; + default: + return false; } } + + private boolean showRegenerateAccessKey(AccessKey accessKey) { + switch (accessKey.getStatus()) { + case CREATED: + case ACTIVE: + return true; + default: + return false; + } + } + + private boolean showDeleteAccessKey(AccessKey accessKey) { + return AccessKeyStatus.REVOKED.equals(accessKey.getStatus()) + || AccessKeyStatus.EXPIRED.equals(accessKey.getStatus()); + } + + interface MyUiBinder extends UiBinder { + } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/AccessKeyTablePanel.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/AccessKeyTablePanel.ui.xml index 75fe6c754a..c9596fa7e8 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/AccessKeyTablePanel.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/AccessKeyTablePanel.ui.xml @@ -6,9 +6,6 @@ - - - diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/CreateAccessKey.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/CreateAccessKey.java deleted file mode 100644 index af3fa47a58..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/CreateAccessKey.java +++ /dev/null @@ -1,141 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE file at the root of the source - * tree and available online at - * - * https://github.com/keeps/roda - */ -package org.roda.wui.client.management.access; - -import java.util.List; - -import org.roda.core.data.v2.accessKey.AccessKey; -import org.roda.core.data.v2.accessKey.CreateAccessKeyRequest; -import org.roda.core.data.v2.user.User; -import org.roda.wui.client.common.NoAsyncCallback; -import org.roda.wui.client.common.TitlePanel; -import org.roda.wui.client.common.UserLogin; -import org.roda.wui.client.common.dialogs.AccessKeyDialogs; -import org.roda.wui.client.management.EditUser; -import org.roda.wui.client.management.MemberManagement; -import org.roda.wui.client.management.ShowUser; -import org.roda.wui.client.services.Services; -import org.roda.wui.common.client.HistoryResolver; -import org.roda.wui.common.client.tools.HistoryUtils; -import org.roda.wui.common.client.tools.ListUtils; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.uibinder.client.UiBinder; -import com.google.gwt.uibinder.client.UiField; -import com.google.gwt.uibinder.client.UiHandler; -import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.Button; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.Widget; - -import config.i18n.client.ClientMessages; - -/** - * @author Gabriel Barros - */ -public class CreateAccessKey extends Composite { - public static final HistoryResolver RESOLVER = new HistoryResolver() { - - @Override - public void resolve(List historyTokens, AsyncCallback callback) { - if (historyTokens.size() == 1) { - Services services = new Services("Get User", "get"); - services.membersResource(s -> s.getUser(historyTokens.get(0))).whenComplete((user, error) -> { - if (user != null) { - CreateAccessKey createAccessKey = new CreateAccessKey(user); - callback.onSuccess(createAccessKey); - } - }); - } else { - CreateAccessKey createAccessKey = new CreateAccessKey(null); - callback.onSuccess(createAccessKey); - } - } - - @Override - public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); - } - - @Override - public String getHistoryToken() { - return "create_access_key"; - } - - @Override - public List getHistoryPath() { - return ListUtils.concat(MemberManagement.RESOLVER.getHistoryPath(), getHistoryToken()); - } - }; - - interface MyUiBinder extends UiBinder { - } - - private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - - private static final ClientMessages messages = GWT.create(ClientMessages.class); - - private AccessKey accessKey; - private User user; - - @UiField - TitlePanel titlePanel; - - @UiField - Button buttonSave; - - @UiField - Button buttonCancel; - - @UiField(provided = true) - AccessKeyDataPanel accessKeyDataPanel; - - public CreateAccessKey(User user) { - this.user = user; - this.accessKey = new AccessKey(); - this.accessKey.setUserName(user.getName()); - this.accessKeyDataPanel = new AccessKeyDataPanel(accessKey, false); - this.accessKeyDataPanel.setAccessKey(accessKey); - - initWidget(uiBinder.createAndBindUi(this)); - titlePanel.setText(user.getName()); - } - - @UiHandler("buttonSave") - void buttonApplyHandler(ClickEvent e) { - if (accessKeyDataPanel.isValid()) { - AccessKey accessKeyUpdated = accessKeyDataPanel.getAccessKey(); - accessKey.setName(accessKeyUpdated.getName()); - accessKey.setExpirationDate(accessKeyUpdated.getExpirationDate()); - Services services = new Services("Create access key", "create"); - CreateAccessKeyRequest createAccessKeyRequest = new CreateAccessKeyRequest(this.accessKey.getName(), - this.accessKey.getExpirationDate()); - services.membersResource(s -> s.createAccessKey(user.getId(), createAccessKeyRequest)) - .whenComplete((response, error) -> { - if (response != null) { - AccessKeyDialogs.showAccessKeyDialog(messages.accessKeyLabel(), response, new NoAsyncCallback() { - @Override - public void onSuccess(Boolean result) { - HistoryUtils.newHistory(ShowUser.RESOLVER, response.getUserName()); - } - }); - } - }); - } - } - - @UiHandler("buttonCancel") - void buttonCancelHandler(ClickEvent e) { - cancel(); - } - - private void cancel() { - HistoryUtils.newHistory(EditUser.RESOLVER, accessKey.getUserName()); - } -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/CreateAccessKey.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/CreateAccessKey.ui.xml deleted file mode 100644 index 00822f9082..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/CreateAccessKey.ui.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/ShowAccessKey.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/ShowAccessKey.java deleted file mode 100644 index 661b5b6bfb..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/ShowAccessKey.java +++ /dev/null @@ -1,284 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE file at the root of the source - * tree and available online at - * - * https://github.com/keeps/roda - */ -package org.roda.wui.client.management.access; - -import java.util.Date; -import java.util.List; - -import org.roda.core.data.v2.accessKey.AccessKey; -import org.roda.core.data.v2.accessKey.CreateAccessKeyRequest; -import org.roda.wui.client.common.NoAsyncCallback; -import org.roda.wui.client.common.TitlePanel; -import org.roda.wui.client.common.UserLogin; -import org.roda.wui.client.common.dialogs.AccessKeyDialogs; -import org.roda.wui.client.common.dialogs.Dialogs; -import org.roda.wui.client.common.utils.HtmlSnippetUtils; -import org.roda.wui.client.management.MemberManagement; -import org.roda.wui.client.services.Services; -import org.roda.wui.common.client.HistoryResolver; -import org.roda.wui.common.client.tools.Humanize; -import org.roda.wui.common.client.tools.ListUtils; -import org.roda.wui.common.client.tools.StringUtils; -import org.roda.wui.common.client.widgets.Toast; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.safehtml.shared.SafeHtmlUtils; -import com.google.gwt.uibinder.client.UiBinder; -import com.google.gwt.uibinder.client.UiField; -import com.google.gwt.uibinder.client.UiHandler; -import com.google.gwt.user.client.History; -import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.Button; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.Widget; - -import config.i18n.client.ClientMessages; - -/** - * @author Gabriel Barros - */ -public class ShowAccessKey extends Composite { - public static final HistoryResolver RESOLVER = new HistoryResolver() { - - @Override - public void resolve(List historyTokens, AsyncCallback callback) { - if (historyTokens.size() == 1) { - Services services = new Services("Get access key", "get"); - services.membersResource(s -> s.getAccessKey(historyTokens.get(0))).whenComplete((accessKey, error) -> { - if (accessKey != null) { - ShowAccessKey showAccessKey = new ShowAccessKey(accessKey); - callback.onSuccess(showAccessKey); - } - }); - } - } - - @Override - public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); - } - - @Override - public List getHistoryPath() { - return ListUtils.concat(MemberManagement.RESOLVER.getHistoryPath(), getHistoryToken()); - } - - @Override - public String getHistoryToken() { - return "show_access_key"; - } - }; - private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - @UiField - TitlePanel title; - @UiField - Label dateCreated; - @UiField - Label dateUpdated; - @UiField - HTML nameValue; - @UiField - HTML expirationDateValue; - @UiField - HTML lastUsageValue; - @UiField - HTML statusValue; - @UiField - Button buttonRegenerate; - @UiField - Button buttonRevoke; - @UiField - Button buttonDelete; - @UiField - Button buttonCancel; - private AccessKey accessKey; - - public ShowAccessKey(AccessKey accessKey) { - initWidget(uiBinder.createAndBindUi(this)); - initElements(accessKey); - } - - public void refresh() { - reset(); - Services services = new Services("Get access key", "get"); - services.membersResource(s -> s.getAccessKey(accessKey.getId())).whenComplete((accessKey, error) -> { - if (accessKey != null) { - initElements(accessKey); - } - }); - } - - public void reset() { - dateCreated.setText(""); - dateUpdated.setText(""); - expirationDateValue.setText(""); - lastUsageValue.setText(""); - statusValue.setHTML(SafeHtmlUtils.EMPTY_SAFE_HTML); - nameValue.setHTML(SafeHtmlUtils.EMPTY_SAFE_HTML); - } - - private void initElements(AccessKey accessKey) { - this.accessKey = accessKey; - title.setText(accessKey.getName()); - - if (accessKey.getCreatedOn() != null && StringUtils.isNotBlank(accessKey.getCreatedBy())) { - dateCreated - .setText(messages.dateCreated(Humanize.formatDateTime(accessKey.getCreatedOn()), accessKey.getCreatedBy())); - } - - if (accessKey.getUpdatedOn() != null && StringUtils.isNotBlank(accessKey.getUpdatedBy())) { - dateUpdated - .setText(messages.dateUpdated(Humanize.formatDateTime(accessKey.getUpdatedOn()), accessKey.getUpdatedBy())); - } - - if (accessKey.getExpirationDate() != null) { - expirationDateValue.setText(Humanize.formatDateTime(accessKey.getExpirationDate())); - } - - if (accessKey.getLastUsageDate() != null) { - lastUsageValue.setText(Humanize.formatDateTime(accessKey.getLastUsageDate())); - } else { - lastUsageValue.setText("Never"); - } - - if (accessKey.getStatus() != null) { - statusValue.setHTML(HtmlSnippetUtils.getAccessKeyStateHtml(accessKey)); - } - - nameValue.setHTML(accessKey.getName()); - - initSidebarButtons(accessKey); - } - - private void initSidebarButtons(AccessKey accessKey) { - switch (accessKey.getStatus()) { - case CREATED: - case ACTIVE: - enableButtons(buttonRegenerate, buttonRevoke); - disableButtons(buttonDelete); - break; - case EXPIRED: - enableButtons(buttonRevoke); - disableButtons(buttonRegenerate, buttonDelete); - break; - case REVOKED: - enableButtons(buttonDelete); - disableButtons(buttonRegenerate, buttonRevoke); - break; - case INACTIVE: - enableButtons(buttonRevoke, buttonDelete); - disableButtons(buttonDelete, buttonRegenerate); - break; - default: - enableButtons(buttonRegenerate, buttonRevoke, buttonDelete); - break; - } - } - - private void disableButtons(Button... buttons) { - for (Button button : buttons) { - button.setEnabled(false); - } - } - - private void enableButtons(Button... buttons) { - for (Button button : buttons) { - button.setEnabled(true); - } - } - - @UiHandler("buttonDelete") - void buttonDeleteHandler(ClickEvent e) { - Dialogs.showConfirmDialog(messages.accessKeyLabel(), messages.accessKeyDeleteConfirmationMessage(), - messages.cancelButton(), messages.confirmButton(), new NoAsyncCallback() { - @Override - public void onSuccess(Boolean confirm) { - Services services = new Services("Delete access key", "delete"); - services.membersResource(s -> s.deleteAccessKey(accessKey.getId())).whenComplete((accessKey, error) -> { - if (error == null) { - cancel(); - } - }); - } - }); - } - - @UiHandler("buttonRegenerate") - void buttonRegenerateHandler(ClickEvent e) { - Dialogs.showConfirmDialog(messages.accessKeyLabel(), messages.accessKeyRegenerateConfirmationMessage(), - messages.cancelButton(), messages.confirmButton(), new NoAsyncCallback() { - @Override - public void onSuccess(Boolean confirm) { - if (confirm) { - AccessKeyDialogs.showRegenerateAccessKeyDialog("Regenerate access key", new AsyncCallback() { - @Override - public void onFailure(Throwable caught) { - // do nothing - } - - @Override - public void onSuccess(Date date) { - Services services = new Services("Regenerate access key", "regenerate"); - CreateAccessKeyRequest regenerateAccessKeyRequest = new CreateAccessKeyRequest(); - regenerateAccessKeyRequest.setExpirationDate(date); - services.membersResource(s -> s.regenerateAccessKey(accessKey.getId(), regenerateAccessKeyRequest)) - .whenComplete((response, error) -> { - if (response != null) { - AccessKeyDialogs.showAccessKeyDialog(messages.accessKeyLabel(), response, - new NoAsyncCallback() { - @Override - public void onSuccess(Boolean result) { - refresh(); - Toast.showInfo(messages.accessKeyLabel(), messages.accessKeySuccessfullyRegenerated()); - } - }); - } - }); - } - }); - } - } - }); - } - - @UiHandler("buttonRevoke") - void buttonRevokeHandler(ClickEvent e) { - Dialogs.showConfirmDialog(messages.accessKeyLabel(), messages.accessKeyRevokeConfirmationMessage(), - messages.cancelButton(), messages.confirmButton(), new NoAsyncCallback() { - @Override - public void onSuccess(Boolean confirm) { - if (confirm) { - Services services = new Services("Revoke access key", "revoke"); - services.membersResource(s -> s.revokeAccessKey(accessKey.getId())).whenComplete((response, error) -> { - if (response != null) { - refresh(); - Toast.showInfo(messages.accessKeyLabel(), messages.accessKeySuccessfullyRevoked()); - } - }); - } - } - }); - } - - @UiHandler("buttonCancel") - void buttonCancelHandler(ClickEvent e) { - cancel(); - } - - private void cancel() { - History.back(); - } - - interface MyUiBinder extends UiBinder { - } - -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/ShowAccessKey.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/ShowAccessKey.ui.xml deleted file mode 100644 index bd9bdf458c..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/access/ShowAccessKey.ui.xml +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateUserPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/CreateUserPanel.java similarity index 53% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateUserPanel.java rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/CreateUserPanel.java index 78bdb2636b..bdbce4643c 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateUserPanel.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/CreateUserPanel.java @@ -5,7 +5,7 @@ * * https://github.com/keeps/roda */ -package org.roda.wui.client.management; +package org.roda.wui.client.management.members; import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ChangeEvent; @@ -19,7 +19,6 @@ import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HTML; @@ -30,18 +29,14 @@ import config.i18n.client.ClientMessages; import org.roda.core.data.exceptions.AuthorizationDeniedException; import org.roda.core.data.v2.generics.MetadataValue; -import org.roda.core.data.v2.user.Group; import org.roda.core.data.v2.user.User; -import org.roda.wui.client.browse.bundle.UserExtraBundle; import org.roda.wui.client.common.utils.AsyncCallbackUtils; import org.roda.wui.client.common.utils.FormUtilities; +import org.roda.wui.client.services.MembersRestService; import org.roda.wui.client.services.Services; import org.roda.wui.common.client.ClientLogger; -import org.roda.wui.common.client.tools.HistoryUtils; -import org.roda.wui.common.client.widgets.wcag.WCAGUtilities; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.Set; @@ -50,118 +45,42 @@ */ public class CreateUserPanel extends Composite implements HasValueChangeHandlers { - interface MyUiBinder extends UiBinder { - } - - private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - private static final ClientMessages messages = GWT.create(ClientMessages.class); - + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + @SuppressWarnings("unused") + private final ClientLogger logger = new ClientLogger(getClass().getName()); @UiField TextBox username; - @UiField Label usernameError; - @UiField - TextBox fullname; - + TextBox fullName; @UiField - Label fullnameError; - + Label fullNameError; @UiField TextBox email; - @UiField Label emailError; - @UiField FlowPanel extra; - - @UiField - FlowPanel groupSelectPanel; - - @UiField(provided = true) - GroupSelect groupSelect; - - @UiField - FlowPanel permissionsSelectPanel; - @UiField - PermissionsPanel permissionsPanel; - - @SuppressWarnings("unused") - private ClientLogger logger = new ClientLogger(getClass().getName()); - - private boolean enableGroupSelect; - - private boolean editmode; + HTML errors; // has to be true to detected new field changes private boolean changed = true; - private boolean checked = false; private Set userExtra; - - @UiField - HTML errors; + // Add this field + private Runnable onFormReadyCallback; /** * Create a new user data panel * - * @param editmode - * if user name should be editable - * @param enableGroupSelect - * if the list of groups to which the user belong to should be editable - * */ - public CreateUserPanel(boolean editmode, boolean enableGroupSelect) { - this(true, editmode, enableGroupSelect, true); - } - - /** - * Create a new user data panel - * - * @param editmode - * if user name should be editable - * @param enableGroupSelect - * if the list of groups to which the user belong to should be editable - * - */ - public CreateUserPanel(boolean visible, boolean editmode, boolean enableGroupSelect) { - this(visible, editmode, enableGroupSelect, true); - } - - /** - * Create a new user data panel - * - * @param visible - * @param editmode - * @param enableGroupSelect - * @param enablePermissions - */ - public CreateUserPanel(boolean visible, boolean editmode, boolean enableGroupSelect, boolean enablePermissions) { - - groupSelect = new GroupSelect(); - + public CreateUserPanel() { initWidget(uiBinder.createAndBindUi(this)); - - this.editmode = editmode; - super.setVisible(visible); - this.enableGroupSelect = enableGroupSelect; - + super.setVisible(true); errors.setVisible(false); - groupSelectPanel.setVisible(enableGroupSelect); - permissionsSelectPanel.setVisible(enablePermissions); - - ValueChangeHandler valueChangedHandler = new ValueChangeHandler() { - - @Override - public void onValueChange(ValueChangeEvent event) { - onChange(); - } - }; - ChangeHandler changeHandler = new ChangeHandler() { @Override @@ -182,29 +101,13 @@ public void onKeyUp(KeyUpEvent event) { username.addChangeHandler(changeHandler); username.addKeyUpHandler(keyUpHandler); - fullname.addChangeHandler(changeHandler); - fullname.addKeyUpHandler(keyUpHandler); - - permissionsPanel.addValueChangeHandler(new ValueChangeHandler>() { - - @Override - public void onValueChange(ValueChangeEvent> event) { - onChange(); - } - }); - - groupSelect.addValueChangeHandler(new ValueChangeHandler>() { - - @Override - public void onValueChange(ValueChangeEvent> event) { - updatePermissions(event.getValue()); - onChange(); - } - }); + fullName.addChangeHandler(changeHandler); + fullName.addKeyUpHandler(keyUpHandler); usernameError.setVisible(false); - fullnameError.setVisible(false); + fullNameError.setVisible(false); emailError.setVisible(false); + setUser(new User()); } @SuppressWarnings("unused") @@ -231,135 +134,58 @@ private int setSelected(ListBox listbox, String text) { } /** - * Set user information of user + * Get user defined by this panel. This panel defines: name, fullname, title, + * organization name, postal address, postal code, locality, country, email, + * phone number, fax and which groups this user belongs to. * - * @param user + * @return the user modified by this panel */ - public void setUser(User user) { + public User getUser() { + User user = new User(); + user.setId(username.getText()); + user.setName(username.getText()); + user.setFullName(fullName.getText()); + user.setEmail(email.getText()); + + return user; + } + + private void setUser(User user) { this.username.setText(user.getName()); - this.fullname.setText(user.getFullName()); + this.fullName.setText(user.getFullName()); this.email.setText(user.getEmail()); - this.setMemberGroups(user.getGroups()); - this.setPermissions(user.getDirectRoles(), user.getAllRoles()); - Services services = new Services("Get User extra", "get"); - services.membersResource(s -> s.getDefaultUserExtra()).whenComplete((extra, error) -> { + services.membersResource(MembersRestService::getDefaultUserExtra).whenComplete((extra, error) -> { if (extra != null) { CreateUserPanel.this.userExtra = extra.getExtraFormFields(); createForm(userExtra); + + // ADD THIS: Trigger the callback to recenter the dialog + if (onFormReadyCallback != null) { + onFormReadyCallback.run(); + } } else if (error != null) { if (error instanceof AuthorizationDeniedException) { - // TODO inform user he does not have permissions to see to which - // groups he belongs to. GWT.log("No permissions: " + error.getMessage()); } else { AsyncCallbackUtils.defaultFailureTreatment(error); } - } - }); - } - - private void setPermissions(final Set directRoles, final Set allRoles) { - permissionsPanel.init(new AsyncCallback() { - - @Override - public void onSuccess(Boolean result) { - Set indirectRoles = new HashSet<>(allRoles); - indirectRoles.removeAll(directRoles); - - permissionsPanel.checkPermissions(directRoles, false); - permissionsPanel.checkPermissions(indirectRoles, true); - WCAGUtilities.getInstance().makeAccessible(permissionsSelectPanel.getElement()); - } - - @Override - public void onFailure(Throwable caught) { - if (caught instanceof AuthorizationDeniedException) { - // TODO inform user he does not have permissions to see to which - // groups he belongs to. - } else { - AsyncCallbackUtils.defaultFailureTreatment(caught); - HistoryUtils.newHistory(MemberManagement.RESOLVER); + // ADD THIS: It's good practice to also recenter on error + // in case error messages alter the panel's dimensions + if (onFormReadyCallback != null) { + onFormReadyCallback.run(); } } }); } - private void updatePermissions(List groups) { - permissionsPanel.clear(); - permissionsPanel.checkPermissions(new HashSet<>(permissionsPanel.getUserSelections()), false); - for (Group group : groups) { - permissionsPanel.checkPermissions(group.getAllRoles(), true); - } - } - - /** - * Get user defined by this panel. This panel defines: name, fullname, title, - * organization name, postal address, postal code, locality, country, email, - * phone number, fax and which groups this user belongs to. - * - * @return the user modified by this panel - */ - public User getUser() { - User user = new User(); - user.setId(username.getText()); - user.setName(username.getText()); - user.setFullName(fullname.getText()); - user.setEmail(email.getText()); - - if (enableGroupSelect) { - user.setGroups(this.getMemberGroups()); - } - - user.setDirectRoles(permissionsPanel.getDirectRoles()); - return user; - } - public void createForm(Set userExtra) { extra.clear(); FormUtilities.create(extra, userExtra, false); } - /** - * Set the groups of which this user is member of - * - * @param groups - */ - public void setMemberGroups(final Set groups) { - if (enableGroupSelect) { - groupSelect.init(new AsyncCallback() { - - @Override - public void onSuccess(Boolean result) { - groupSelect.setMemberGroups(groups); - WCAGUtilities.getInstance().makeAccessible(groupSelectPanel.getElement()); - } - - @Override - public void onFailure(Throwable caught) { - if (caught instanceof AuthorizationDeniedException) { - // TODO inform user he does not have permissions to see to which - // groups he belongs to. - } else { - AsyncCallbackUtils.defaultFailureTreatment(caught); - } - } - }); - } - } - - /** - * Get the groups of which this user is member of - * - * @return a list of group names - */ - public Set getMemberGroups() { - return enableGroupSelect ? groupSelect.getMemberGroups() : null; - } - - /** * Is user data panel valid * @@ -378,17 +204,17 @@ public boolean isValid() { usernameError.setVisible(false); } - if (fullname.getText().length() == 0) { - fullname.addStyleName("isWrong"); - fullnameError.setText(messages.mandatoryField()); - fullnameError.setVisible(true); + if (fullName.getText().length() == 0) { + fullName.addStyleName("isWrong"); + fullNameError.setText(messages.mandatoryField()); + fullNameError.setVisible(true); if (errorList.isEmpty()) { - Window.scrollTo(fullname.getAbsoluteLeft(), fullname.getAbsoluteTop()); + Window.scrollTo(fullName.getAbsoluteLeft(), fullName.getAbsoluteTop()); } errorList.add(messages.isAMandatoryField(messages.fullname())); } else { - fullname.removeStyleName("isWrong"); - fullnameError.setVisible(false); + fullName.removeStyleName("isWrong"); + fullNameError.setVisible(false); } if (email.getText() == null || "".equals(email.getText().trim())) { @@ -415,7 +241,6 @@ public boolean isValid() { List extraErrors = FormUtilities.validate(userExtra, extra); errorList.addAll(extraErrors); - checked = true; if (!errorList.isEmpty()) { errors.setVisible(true); @@ -431,47 +256,17 @@ public boolean isValid() { return errorList.isEmpty() ? true : false; } - /** - * Is user name read only - * - * @return true if read only - */ - public boolean isUsernameReadOnly() { - return username.isReadOnly(); - } - - /** - * Set user name read only - * - * @param readonly - */ - public void setUsernameReadOnly(boolean readonly) { - username.setReadOnly(readonly); - } - @Override public void setVisible(boolean visible) { super.setVisible(visible); - if (enableGroupSelect) { - groupSelect.setVisible(visible); - } } public void clear() { username.setText(""); - fullname.setText(""); + fullName.setText(""); email.setText(""); } - /** - * Is user data panel editable, i.e. on create user mode - * - * @return true if editable - */ - public boolean isEditmode() { - return editmode; - } - /** * Is user data panel has been changed * @@ -488,12 +283,13 @@ public HandlerRegistration addValueChangeHandler(ValueChangeHandler handle protected void onChange() { changed = true; - if (checked) { - isValid(); - } ValueChangeEvent.fire(this, getValue()); } + public void setOnFormReadyCallback(Runnable callback) { + this.onFormReadyCallback = callback; + } + public User getValue() { return getUser(); } @@ -502,9 +298,6 @@ public Set getUserExtra() { return userExtra; } - public void setUserExtra(Set userExtra) { - this.userExtra = userExtra; - createForm(userExtra); + interface MyUiBinder extends UiBinder { } } - diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/CreateUserPanel.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/CreateUserPanel.ui.xml new file mode 100644 index 0000000000..c2894fe828 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/CreateUserPanel.ui.xml @@ -0,0 +1,49 @@ + + + + + + + + .section { + margin-top: 20px; + } + + + + + + + + + + + + * + + + + + + + * + + + + + + + * + + + + + + + + + + + + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/GroupDataPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/GroupDataPanel.java new file mode 100644 index 0000000000..3e93dc3c79 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/GroupDataPanel.java @@ -0,0 +1,194 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE file at the root of the source + * tree and available online at + * + * https://github.com/keeps/roda + */ +/** + * + */ +package org.roda.wui.client.management.members; + +import org.roda.core.data.v2.user.Group; +import org.roda.wui.common.client.ClientLogger; +import com.google.gwt.core.client.GWT; +import com.google.gwt.event.dom.client.ChangeEvent; +import com.google.gwt.event.dom.client.ChangeHandler; +import com.google.gwt.event.dom.client.KeyUpEvent; +import com.google.gwt.event.dom.client.KeyUpHandler; +import com.google.gwt.event.logical.shared.HasValueChangeHandlers; +import com.google.gwt.event.logical.shared.ValueChangeEvent; +import com.google.gwt.event.logical.shared.ValueChangeHandler; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.TextBox; +import com.google.gwt.user.client.ui.Widget; + +/** + * @author Luis Faria + * + */ +public class GroupDataPanel extends Composite implements HasValueChangeHandlers { + + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + @UiField + TextBox groupName; + @UiField + TextBox fullName; + + private Group group = new Group(); + @SuppressWarnings("unused") + private final ClientLogger logger = new ClientLogger(getClass().getName()); + + private final boolean editMode; + private boolean changed = false; + private boolean checked = false; + /** + * Create a new group data panel + * + * @param editMode + * if group name should be editable + */ + public GroupDataPanel(boolean editMode) { + initWidget(uiBinder.createAndBindUi(this)); + + this.editMode = editMode; + super.setVisible(true); + + ChangeHandler changeHandler = new ChangeHandler() { + + @Override + public void onChange(ChangeEvent event) { + GroupDataPanel.this.onChange(); + } + }; + + KeyUpHandler keyUpHandler = new KeyUpHandler() { + + @Override + public void onKeyUp(KeyUpEvent event) { + onChange(); + } + }; + + groupName.setEnabled(!isEditMode()); + groupName.addKeyDownHandler(new UserAndGroupKeyDownHandler()); + groupName.addChangeHandler(changeHandler); + groupName.addKeyUpHandler(keyUpHandler); + fullName.addChangeHandler(changeHandler); + fullName.addKeyUpHandler(keyUpHandler); + } + + /** + * Get group defined by this panel. This panel defines: name, fullname + * + * @return the group modified by this panel + */ + public Group getGroup() { + group.setId(groupName.getText()); + group.setName(groupName.getText()); + group.setFullName(fullName.getText()); + return group; + } + + /** + * Set group information of group + * + * @param group + */ + public void setGroup(Group group) { + this.group = group; + this.groupName.setText(group.getName()); + this.fullName.setText(group.getFullName()); + } + + /** + * Is group data panel valid + * + * @return true if valid + */ + public boolean isValid() { + boolean valid = true; + + if (groupName.getText().isEmpty()) { + valid = false; + groupName.addStyleName("isWrong"); + } else { + groupName.removeStyleName("isWrong"); + } + + if (fullName.getText().isEmpty()) { + valid = false; + fullName.addStyleName("isWrong"); + } else { + fullName.removeStyleName("isWrong"); + } + + checked = true; + return valid; + } + + /** + * Is group name read only + * + * @return true if read only + */ + public boolean isGroupNameReadOnly() { + return groupName.isReadOnly(); + } + + /** + * Set group name read only + * + * @param readonly + */ + public void setGroupNameReadOnly(boolean readonly) { + groupName.setReadOnly(readonly); + } + + public void clear() { + groupName.setText(""); + fullName.setText(""); + } + + /** + * Is group data panel editable, i.e. on create group mode + * + * @return true if editable + */ + public boolean isEditMode() { + return editMode; + } + + /** + * Is group data panel has been changed + * + * @return changed + */ + public boolean isChanged() { + return changed; + } + + @Override + public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) { + return addHandler(handler, ValueChangeEvent.getType()); + } + + protected void onChange() { + changed = true; + if (checked) { + isValid(); + } + ValueChangeEvent.fire(this, getValue()); + } + + public Group getValue() { + return getGroup(); + } + + interface MyUiBinder extends UiBinder { + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/GroupDataPanel.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/GroupDataPanel.ui.xml similarity index 51% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/GroupDataPanel.ui.xml rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/GroupDataPanel.ui.xml index 759655ad52..707256479c 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/GroupDataPanel.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/GroupDataPanel.ui.xml @@ -1,7 +1,7 @@ + xmlns:u="urn:import:org.roda.wui.client.management.members"> @@ -17,26 +17,11 @@ * - + * - - - - - - - - - - - - - - - - + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/GroupSelect.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/GroupSelect.java similarity index 99% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/GroupSelect.java rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/GroupSelect.java index 2bc1873912..9c1b757a75 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/GroupSelect.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/GroupSelect.java @@ -5,7 +5,7 @@ * * https://github.com/keeps/roda */ -package org.roda.wui.client.management; +package org.roda.wui.client.management.members; import java.util.HashMap; import java.util.HashSet; diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/MemberManagement.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/MemberManagement.java similarity index 68% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/MemberManagement.java rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/MemberManagement.java index 5bf2f5bd40..5d08ebe4b1 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/MemberManagement.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/MemberManagement.java @@ -5,7 +5,7 @@ * * https://github.com/keeps/roda */ -package org.roda.wui.client.management; +package org.roda.wui.client.management.members; import java.util.List; @@ -16,8 +16,7 @@ import org.roda.wui.client.common.lists.utils.AsyncTableCellOptions; import org.roda.wui.client.common.lists.utils.ListBuilder; import org.roda.wui.client.common.search.SearchWrapper; -import org.roda.wui.client.management.access.CreateAccessKey; -import org.roda.wui.client.management.access.ShowAccessKey; +import org.roda.wui.client.management.Management; import org.roda.wui.common.client.HistoryResolver; import org.roda.wui.common.client.tools.HistoryUtils; import org.roda.wui.common.client.tools.ListUtils; @@ -35,8 +34,8 @@ public class MemberManagement extends Composite { private static final ClientMessages messages = GWT.create(ClientMessages.class); - - public static final HistoryResolver RESOLVER = new HistoryResolver() { + private static MemberManagement instance = null; + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); public static final HistoryResolver RESOLVER = new HistoryResolver() { @Override public void resolve(List historyTokens, AsyncCallback callback) { @@ -58,34 +57,10 @@ public String getHistoryToken() { return "user"; } }; - - private static MemberManagement instance = null; - - /** - * Get the singleton instance - * - * @return the instance - */ - public static MemberManagement getInstance() { - if (instance == null) { - instance = new MemberManagement(); - } else { - instance.refresh(); - } - return instance; - } - - interface MyUiBinder extends UiBinder { - } - - private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - @UiField FlowPanel memberManagementDescription; - @UiField(provided = true) SearchWrapper searchWrapper; - public MemberManagement() { ListBuilder rodaMemberListBuilder = new ListBuilder<>(() -> new RodaMemberList(), @@ -100,6 +75,20 @@ public MemberManagement() { memberManagementDescription.add(new HTMLWidgetWrapper("MemberManagementDescription.html")); } + /** + * Get the singleton instance + * + * @return the instance + */ + public static MemberManagement getInstance() { + if (instance == null) { + instance = new MemberManagement(); + } else { + instance.refresh(); + } + return instance; + } + private void refresh() { searchWrapper.refreshCurrentList(); } @@ -108,27 +97,11 @@ public void resolve(List historyTokens, AsyncCallback callback) if (historyTokens.isEmpty()) { callback.onSuccess(this); } else if (historyTokens.size() == 1) { - if (historyTokens.get(0).equals(CreateUser.RESOLVER.getHistoryToken())) { - CreateUser.RESOLVER.resolve(HistoryUtils.tail(historyTokens), callback); - } else if (historyTokens.get(0).equals(CreateGroup.RESOLVER.getHistoryToken())) { - CreateGroup.RESOLVER.resolve(HistoryUtils.tail(historyTokens), callback); - } else { - HistoryUtils.newHistory(RESOLVER); - callback.onSuccess(null); - } + HistoryUtils.newHistory(RESOLVER); + callback.onSuccess(null); } else if (historyTokens.size() == 2) { - if (historyTokens.get(0).equals(ShowUser.RESOLVER.getHistoryToken())) { - ShowUser.RESOLVER.resolve(HistoryUtils.tail(historyTokens), callback); - } else if (historyTokens.get(0).equals(EditUser.RESOLVER.getHistoryToken())) { - EditUser.RESOLVER.resolve(HistoryUtils.tail(historyTokens), callback); - } else if (historyTokens.get(0).equals(ShowGroup.RESOLVER.getHistoryToken())) { - ShowGroup.RESOLVER.resolve(HistoryUtils.tail(historyTokens), callback); - } else if (historyTokens.get(0).equals(EditGroup.RESOLVER.getHistoryToken())) { - EditGroup.RESOLVER.resolve(HistoryUtils.tail(historyTokens), callback); - } else if (historyTokens.get(0).equals(ShowAccessKey.RESOLVER.getHistoryToken())) { - ShowAccessKey.RESOLVER.resolve(HistoryUtils.tail(historyTokens), callback); - } else if (historyTokens.get(0).equals(CreateAccessKey.RESOLVER.getHistoryToken())) { - CreateAccessKey.RESOLVER.resolve(HistoryUtils.tail(historyTokens), callback); + if (historyTokens.get(0).equals(ShowMember.RESOLVER.getHistoryToken())) { + ShowMember.RESOLVER.resolve(HistoryUtils.tail(historyTokens), callback); } else { HistoryUtils.newHistory(RESOLVER); callback.onSuccess(null); @@ -138,4 +111,10 @@ public void resolve(List historyTokens, AsyncCallback callback) callback.onSuccess(null); } } + + interface MyUiBinder extends UiBinder { + } + + + } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/MemberManagement.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/MemberManagement.ui.xml similarity index 100% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/MemberManagement.ui.xml rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/MemberManagement.ui.xml diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/PasswordPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/PasswordPanel.java similarity index 99% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/PasswordPanel.java rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/PasswordPanel.java index 8bef419043..bdcc4117c7 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/PasswordPanel.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/PasswordPanel.java @@ -5,7 +5,7 @@ * * https://github.com/keeps/roda */ -package org.roda.wui.client.management; +package org.roda.wui.client.management.members; import org.roda.wui.common.client.tools.StringUtils; diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/PermissionsPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/PermissionsPanel.java similarity index 99% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/PermissionsPanel.java rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/PermissionsPanel.java index ab7baca98b..4ec6467a41 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/PermissionsPanel.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/PermissionsPanel.java @@ -8,7 +8,7 @@ /** * */ -package org.roda.wui.client.management; +package org.roda.wui.client.management.members; import java.util.ArrayList; import java.util.HashSet; diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/Profile.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/Profile.java similarity index 97% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/Profile.java rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/Profile.java index e1d4f0233c..e2247d65bb 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/Profile.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/Profile.java @@ -8,7 +8,7 @@ /** * */ -package org.roda.wui.client.management; +package org.roda.wui.client.management.members; import java.util.Arrays; import java.util.List; @@ -117,7 +117,7 @@ interface MyUiBinder extends UiBinder { public Profile(User user) { this.user = user; - this.userDataPanel = new UserDataPanel(true, true, false, false); + this.userDataPanel = new UserDataPanel(true, true); this.userDataPanel.setUser(user); initWidget(uiBinder.createAndBindUi(this)); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/Profile.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/Profile.ui.xml similarity index 91% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/Profile.ui.xml rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/Profile.ui.xml index 69dc9227ae..79374a3543 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/Profile.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/Profile.ui.xml @@ -1,7 +1,7 @@ + xmlns:u="urn:import:org.roda.wui.client.management.members" xmlns:common="urn:import:org.roda.wui.client.common"> diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/RecoverLogin.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/RecoverLogin.java similarity index 99% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/RecoverLogin.java rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/RecoverLogin.java index 8bfb0b8936..4407b0898b 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/RecoverLogin.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/RecoverLogin.java @@ -8,7 +8,7 @@ /** * */ -package org.roda.wui.client.management; +package org.roda.wui.client.management.members; import java.util.Arrays; import java.util.List; diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/RecoverLogin.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/RecoverLogin.ui.xml similarity index 100% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/RecoverLogin.ui.xml rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/RecoverLogin.ui.xml diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/Register.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/Register.java similarity index 98% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/Register.java rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/Register.java index c9fd401005..89598b961b 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/Register.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/Register.java @@ -8,7 +8,7 @@ /** * */ -package org.roda.wui.client.management; +package org.roda.wui.client.management.members; import java.util.Arrays; import java.util.List; @@ -123,7 +123,7 @@ interface MyUiBinder extends UiBinder { * the user to edit */ public Register() { - this.userDataPanel = new UserDataPanel(true, false, false, false); + this.userDataPanel = new UserDataPanel(true, false); Services services = new Services("Get User extra", "get"); services.membersResource(s -> s.getDefaultUserExtra()).whenComplete((userExtra, error) -> { if (userExtra != null) { diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/Register.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/Register.ui.xml similarity index 92% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/Register.ui.xml rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/Register.ui.xml index 317b4e9ea0..e09094e672 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/Register.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/Register.ui.xml @@ -1,7 +1,7 @@ + xmlns:u="urn:import:org.roda.wui.client.management.members" xmlns:common="urn:import:org.roda.wui.client.common"> diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ResetPassword.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/ResetPassword.java similarity index 72% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ResetPassword.java rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/ResetPassword.java index a888dacb66..edca3c0435 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ResetPassword.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/ResetPassword.java @@ -8,40 +8,20 @@ /** * */ -package org.roda.wui.client.management; +package org.roda.wui.client.management.members; import com.google.gwt.core.client.GWT; -import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.event.dom.client.ClickHandler; -import com.google.gwt.event.dom.client.KeyCodes; -import com.google.gwt.event.dom.client.KeyPressEvent; -import com.google.gwt.event.dom.client.KeyUpEvent; -import com.google.gwt.event.dom.client.KeyUpHandler; -import com.google.gwt.event.logical.shared.AttachEvent; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; -import com.google.gwt.uibinder.client.UiHandler; import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.Widget; -import config.i18n.client.ClientMessages; -import org.roda.core.data.common.SecureString; -import org.roda.core.data.exceptions.InvalidTokenException; -import org.roda.core.data.exceptions.NotFoundException; import org.roda.core.data.v2.user.User; import org.roda.wui.client.common.UserLogin; -import org.roda.wui.client.common.dialogs.Dialogs; import org.roda.wui.client.main.Login; -import org.roda.wui.client.services.Services; import org.roda.wui.client.welcome.Welcome; -import org.roda.wui.common.client.ClientLogger; import org.roda.wui.common.client.HistoryResolver; import org.roda.wui.common.client.tools.HistoryUtils; -import org.roda.wui.common.client.widgets.Toast; import java.util.Arrays; import java.util.List; diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ResetPassword.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/ResetPassword.ui.xml similarity index 83% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ResetPassword.ui.xml rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/ResetPassword.ui.xml index aea22e21a3..7e780688aa 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ResetPassword.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/ResetPassword.ui.xml @@ -2,7 +2,7 @@ + xmlns:u="urn:import:org.roda.wui.client.management.members"> diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/SetPassword.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/SetPassword.java similarity index 98% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/SetPassword.java rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/SetPassword.java index 12aa7546b5..8b35820bcc 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/SetPassword.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/SetPassword.java @@ -8,7 +8,7 @@ /** * */ -package org.roda.wui.client.management; +package org.roda.wui.client.management.members; import com.google.gwt.core.client.GWT; import com.google.gwt.uibinder.client.UiBinder; diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/SetPassword.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/SetPassword.ui.xml similarity index 83% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/SetPassword.ui.xml rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/SetPassword.ui.xml index a21e3694b7..b411202bf3 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/SetPassword.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/SetPassword.ui.xml @@ -2,7 +2,7 @@ + xmlns:u="urn:import:org.roda.wui.client.management.members"> diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/ShowMember.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/ShowMember.java new file mode 100644 index 0000000000..d286ebec35 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/ShowMember.java @@ -0,0 +1,167 @@ +package org.roda.wui.client.management.members; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.gwt.user.client.ui.FocusPanel; +import org.roda.core.data.v2.user.RODAMember; +import org.roda.wui.client.browse.tabs.RODAMemberTabs; +import org.roda.wui.client.common.NavigationToolbar; +import org.roda.wui.client.common.NoAsyncCallback; +import org.roda.wui.client.common.TitlePanel; +import org.roda.wui.client.common.RODAMemberActionsToolbar; +import org.roda.wui.client.common.UserLogin; +import org.roda.wui.client.common.actions.Actionable; +import org.roda.wui.client.main.BreadcrumbUtils; +import org.roda.wui.client.services.Services; +import org.roda.wui.common.client.HistoryResolver; +import org.roda.wui.common.client.tools.HistoryUtils; +import org.roda.wui.common.client.tools.ListUtils; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.Widget; + +import config.i18n.client.ClientMessages; +import org.roda.wui.common.client.tools.StringUtils; + +/** + * @author Gabriel Barros + */ +public class ShowMember extends Composite { + public static final HistoryResolver RESOLVER = new HistoryResolver() { + + @Override + public void resolve(List historyTokens, final AsyncCallback callback) { + if (historyTokens.size() == 1) { + String username = historyTokens.get(0); + Services services = new Services("Get User", "get"); + services.membersResource(s -> s.getUser(username)).whenComplete((member, error) -> { + if (member != null) { + ShowMember showMember = new ShowMember(member); + callback.onSuccess(showMember); + } else if (error != null) { + callback.onFailure(error); + } + }); + } else { + HistoryUtils.newHistory(MemberManagement.RESOLVER); + callback.onSuccess(null); + } + } + + @Override + public void isCurrentUserPermitted(AsyncCallback callback) { + UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); + } + + @Override + public List getHistoryPath() { + return ListUtils.concat(MemberManagement.RESOLVER.getHistoryPath(), getHistoryToken()); + } + + @Override + public String getHistoryToken() { + return "show_member"; + } + }; + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + + @UiField + FocusPanel keyboardFocus; + + @UiField + NavigationToolbar navigationToolbar; + + @UiField + RODAMemberActionsToolbar actionsToolbar; + + @UiField + TitlePanel title; + + @UiField + RODAMemberTabs browseTab; + + private Map handlers = new HashMap<>(); + private AsyncCallback handler = new NoAsyncCallback() { + @Override + public void onSuccess(Actionable.ActionImpact result) { + if (handlers.containsKey(result)) { + handlers.get(result).run(); + } + } + }; + + public ShowMember(RODAMember member) { + initWidget(uiBinder.createAndBindUi(this)); + + initHandlers(member); + + updateView(member); + + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse"); + } + + private void updateView(RODAMember member) { + navigationToolbar.withoutButtons().build(); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getRODAMemberBreadcrumbs(member)); + + if (member.isUser()) { + actionsToolbar.setLabel(messages.showUserTitle()); + title.setIconClass("User"); + } else { + actionsToolbar.setLabel(messages.showGroupTitle()); + title.setIconClass("Group"); + } + + actionsToolbar.setObjectAndBuild(member, null, handler); + + title.setText(StringUtils.isBlank(member.getFullName()) ? member.getId() : member.getFullName()); + + browseTab.init(member, handler); + } + + private void initHandlers(RODAMember member) { + handlers.put(Actionable.ActionImpact.DESTROYED, + () -> HistoryUtils.newHistory(MemberManagement.RESOLVER.getHistoryPath())); + + // Change this to use the DOM Swap refresh method instead of HistoryUtils + handlers.put(Actionable.ActionImpact.UPDATED, () -> refreshToolbar(member.getUUID(), member.isUser())); + } + + private void refreshToolbar(String id, boolean isUser) { + Services services = new Services("Get Updated Member", "get"); + + if (isUser) { + services.membersResource(s -> s.getUser(id)).whenComplete((updatedUser, error) -> { + if (updatedUser != null) { + updateShowUserUI(updatedUser); + } + }); + } else { + services.membersResource(s -> s.getGroup(id)).whenComplete((updatedGroup, error) -> { + if (updatedGroup != null) { + updateShowUserUI(updatedGroup); + } + }); + } + } + + private void updateShowUserUI(RODAMember updatedMember) { + title + .setText(StringUtils.isBlank(updatedMember.getFullName()) ? updatedMember.getId() : updatedMember.getFullName()); + + actionsToolbar.setObjectAndBuild(updatedMember, null, handler); + + browseTab.init(updatedMember, handler); + } + + interface MyUiBinder extends UiBinder { + } +} \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/ShowMember.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/ShowMember.ui.xml new file mode 100644 index 0000000000..d1d99d32df --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/ShowMember.ui.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/UpdatePasswordPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UpdatePasswordPanel.java similarity index 99% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/UpdatePasswordPanel.java rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UpdatePasswordPanel.java index b5f55ae0ee..cc85d7bf2d 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/UpdatePasswordPanel.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UpdatePasswordPanel.java @@ -5,7 +5,7 @@ * * https://github.com/keeps/roda */ -package org.roda.wui.client.management; +package org.roda.wui.client.management.members; import org.roda.core.data.common.SecureString; import org.roda.core.data.exceptions.InvalidTokenException; diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/UpdatePasswordPanel.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UpdatePasswordPanel.ui.xml similarity index 100% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/UpdatePasswordPanel.ui.xml rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UpdatePasswordPanel.ui.xml diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/UserAndGroupKeyDownHandler.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UserAndGroupKeyDownHandler.java similarity index 96% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/UserAndGroupKeyDownHandler.java rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UserAndGroupKeyDownHandler.java index c474a049ef..6484841e01 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/UserAndGroupKeyDownHandler.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UserAndGroupKeyDownHandler.java @@ -5,7 +5,7 @@ * * https://github.com/keeps/roda */ -package org.roda.wui.client.management; +package org.roda.wui.client.management.members; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyDownEvent; diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/UserDataPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UserDataPanel.java similarity index 61% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/UserDataPanel.java rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UserDataPanel.java index a1afa3bf82..2c4a9bff0c 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/UserDataPanel.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UserDataPanel.java @@ -8,22 +8,16 @@ /** * */ -package org.roda.wui.client.management; +package org.roda.wui.client.management.members; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.Set; -import org.roda.core.data.exceptions.AuthorizationDeniedException; import org.roda.core.data.v2.generics.MetadataValue; -import org.roda.core.data.v2.user.Group; import org.roda.core.data.v2.user.User; -import org.roda.wui.client.common.utils.AsyncCallbackUtils; import org.roda.wui.client.common.utils.FormUtilities; import org.roda.wui.common.client.ClientLogger; -import org.roda.wui.common.client.tools.HistoryUtils; -import org.roda.wui.common.client.widgets.wcag.WCAGUtilities; import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ChangeEvent; @@ -37,7 +31,6 @@ import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HTML; @@ -54,117 +47,52 @@ */ public class UserDataPanel extends Composite implements HasValueChangeHandlers { - interface MyUiBinder extends UiBinder { - } - - private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - private static final ClientMessages messages = GWT.create(ClientMessages.class); - + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + @SuppressWarnings("unused") + private final ClientLogger logger = new ClientLogger(getClass().getName()); + private final boolean editMode; @UiField TextBox username; - @UiField Label usernameError; - @UiField(provided = true) PasswordPanel password; - @UiField Label passwordError; - @UiField - TextBox fullname; - + TextBox fullName; @UiField - Label fullnameError; - + Label fullNameError; @UiField TextBox email; - @UiField Label emailError; - @UiField FlowPanel extra; - - @UiField - FlowPanel groupSelectPanel; - - @UiField(provided = true) - GroupSelect groupSelect; - - @UiField - FlowPanel permissionsSelectPanel; - @UiField - PermissionsPanel permissionsPanel; - - @SuppressWarnings("unused") - private ClientLogger logger = new ClientLogger(getClass().getName()); - - private boolean enableGroupSelect; - - private boolean editmode; - + HTML errors; // has to be true to detected new field changes - private boolean changed = true; + private boolean changed = false; private boolean checked = false; private Set userExtra; - @UiField - HTML errors; - - /** - * Create a new user data panel - * - * @param editmode - * if user name should be editable - * @param enableGroupSelect - * if the list of groups to which the user belong to should be editable - * - */ - public UserDataPanel(boolean editmode, boolean enableGroupSelect) { - this(true, editmode, enableGroupSelect, true); - } - - /** - * Create a new user data panel - * - * @param editmode - * if user name should be editable - * @param enableGroupSelect - * if the list of groups to which the user belong to should be editable - * - */ - public UserDataPanel(boolean visible, boolean editmode, boolean enableGroupSelect) { - this(visible, editmode, enableGroupSelect, true); - } - /** * Create a new user data panel * * @param visible * @param editMode - * @param enableGroupSelect - * @param enablePermissions */ - public UserDataPanel(boolean visible, boolean editMode, boolean enableGroupSelect, boolean enablePermissions) { + public UserDataPanel(boolean visible, boolean editMode) { password = new PasswordPanel(editMode); - groupSelect = new GroupSelect(); - initWidget(uiBinder.createAndBindUi(this)); - this.editmode = editMode; + this.editMode = editMode; super.setVisible(visible); - this.enableGroupSelect = enableGroupSelect; errors.setVisible(false); - groupSelectPanel.setVisible(enableGroupSelect); - permissionsSelectPanel.setVisible(enablePermissions); - ValueChangeHandler valueChangedHandler = new ValueChangeHandler() { @Override @@ -194,29 +122,12 @@ public void onKeyUp(KeyUpEvent event) { username.addChangeHandler(changeHandler); username.addKeyUpHandler(keyUpHandler); password.addValueChangeHandler(valueChangedHandler); - fullname.addChangeHandler(changeHandler); - fullname.addKeyUpHandler(keyUpHandler); - - permissionsPanel.addValueChangeHandler(new ValueChangeHandler>() { - - @Override - public void onValueChange(ValueChangeEvent> event) { - onChange(); - } - }); - - groupSelect.addValueChangeHandler(new ValueChangeHandler>() { - - @Override - public void onValueChange(ValueChangeEvent> event) { - updatePermissions(event.getValue()); - onChange(); - } - }); + fullName.addChangeHandler(changeHandler); + fullName.addKeyUpHandler(keyUpHandler); usernameError.setVisible(false); passwordError.setVisible(false); - fullnameError.setVisible(false); + fullNameError.setVisible(false); emailError.setVisible(false); } @@ -243,59 +154,6 @@ private int setSelected(ListBox listbox, String text) { return index; } - /** - * Set user information of user - * - * @param user - */ - public void setUser(User user) { - this.username.setText(user.getName()); - this.fullname.setText(user.getFullName()); - this.email.setText(user.getEmail()); - this.userExtra = user.getExtra(); - this.setMemberGroups(user.getGroups()); - this.setPermissions(user.getDirectRoles(), user.getAllRoles()); - } - - public void setUserExtra(Set extra) { - UserDataPanel.this.userExtra = extra; - createForm(extra); - } - - private void setPermissions(final Set directRoles, final Set allRoles) { - permissionsPanel.init(new AsyncCallback() { - - @Override - public void onSuccess(Boolean result) { - Set indirectRoles = new HashSet<>(allRoles); - indirectRoles.removeAll(directRoles); - - permissionsPanel.checkPermissions(directRoles, false); - permissionsPanel.checkPermissions(indirectRoles, true); - WCAGUtilities.getInstance().makeAccessible(permissionsSelectPanel.getElement()); - } - - @Override - public void onFailure(Throwable caught) { - if (caught instanceof AuthorizationDeniedException) { - // TODO inform user he does not have permissions to see to which - // groups he belongs to. - } else { - AsyncCallbackUtils.defaultFailureTreatment(caught); - HistoryUtils.newHistory(MemberManagement.RESOLVER); - } - } - }); - } - - private void updatePermissions(List groups) { - permissionsPanel.clear(); - permissionsPanel.checkPermissions(new HashSet(permissionsPanel.getUserSelections()), false); - for (Group group : groups) { - permissionsPanel.checkPermissions(group.getAllRoles(), true); - } - } - /** * Get user defined by this panel. This panel defines: name, fullname, title, * organization name, postal address, postal code, locality, country, email, @@ -307,57 +165,29 @@ public User getUser() { User user = new User(); user.setId(username.getText()); user.setName(username.getText()); - user.setFullName(fullname.getText()); + user.setFullName(fullName.getText()); user.setEmail(email.getText()); - if (enableGroupSelect) { - user.setGroups(this.getMemberGroups()); - } - - user.setDirectRoles(permissionsPanel.getDirectRoles()); return user; } - public void createForm(Set userExtra) { - extra.clear(); - FormUtilities.create(extra, userExtra, false); - } - /** - * Set the groups of which this user is member of + * Set user information of user * - * @param groups + * @param user */ - public void setMemberGroups(final Set groups) { - if (enableGroupSelect) { - groupSelect.init(new AsyncCallback() { - - @Override - public void onSuccess(Boolean result) { - groupSelect.setMemberGroups(groups); - WCAGUtilities.getInstance().makeAccessible(groupSelectPanel.getElement()); - } + public void setUser(User user) { + this.username.setText(user.getName()); + this.fullName.setText(user.getFullName()); + this.email.setText(user.getEmail()); + this.userExtra = user.getExtra(); - @Override - public void onFailure(Throwable caught) { - if (caught instanceof AuthorizationDeniedException) { - // TODO inform user he does not have permissions to see to which - // groups he belongs to. - } else { - AsyncCallbackUtils.defaultFailureTreatment(caught); - } - } - }); - } + this.changed = false; } - /** - * Get the groups of which this user is member of - * - * @return a list of group names - */ - public Set getMemberGroups() { - return enableGroupSelect ? groupSelect.getMemberGroups() : null; + public void createForm(Set userExtra) { + extra.clear(); + FormUtilities.create(extra, userExtra, false); } /** @@ -408,17 +238,17 @@ public boolean isValid() { errorList.add(messages.passwordIsTooSmall()); } - if (fullname.getText().isEmpty()) { - fullname.addStyleName("isWrong"); - fullnameError.setText(messages.mandatoryField()); - fullnameError.setVisible(true); + if (fullName.getText().isEmpty()) { + fullName.addStyleName("isWrong"); + fullNameError.setText(messages.mandatoryField()); + fullNameError.setVisible(true); if (errorList.isEmpty()) { - Window.scrollTo(fullname.getAbsoluteLeft(), fullname.getAbsoluteTop()); + Window.scrollTo(fullName.getAbsoluteLeft(), fullName.getAbsoluteTop()); } errorList.add(messages.isAMandatoryField(messages.fullname())); } else { - fullname.removeStyleName("isWrong"); - fullnameError.setVisible(false); + fullName.removeStyleName("isWrong"); + fullNameError.setVisible(false); } if (email.getText() == null || "".equals(email.getText().trim())) { @@ -483,16 +313,15 @@ public void setUsernameReadOnly(boolean readonly) { @Override public void setVisible(boolean visible) { super.setVisible(visible); - if (enableGroupSelect) { - groupSelect.setVisible(visible); - } } public void clear() { username.setText(""); password.clear(); - fullname.setText(""); + fullName.setText(""); email.setText(""); + + this.changed = false; } /** @@ -500,8 +329,8 @@ public void clear() { * * @return true if editable */ - public boolean isEditmode() { - return editmode; + public boolean isEditMode() { + return editMode; } /** @@ -533,4 +362,12 @@ public User getValue() { public Set getUserExtra() { return userExtra; } + + public void setUserExtra(Set extra) { + UserDataPanel.this.userExtra = extra; + createForm(extra); + } + + interface MyUiBinder extends UiBinder { + } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/UserDataPanel.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UserDataPanel.ui.xml similarity index 67% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/UserDataPanel.ui.xml rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UserDataPanel.ui.xml index 22a2c2ad9a..49c7ec81b1 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/UserDataPanel.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UserDataPanel.ui.xml @@ -1,7 +1,7 @@ + xmlns:u="urn:import:org.roda.wui.client.management.members"> @@ -30,8 +30,8 @@ * - - + + * @@ -45,21 +45,5 @@ - - - - - - - - - - - - - - - - diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UserDetailsPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UserDetailsPanel.java new file mode 100644 index 0000000000..ec4d000d2d --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UserDetailsPanel.java @@ -0,0 +1,8 @@ +package org.roda.wui.client.management.members; + +/** + * @author Miguel Guimarães + */ + +public class UserDetailsPanel { +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/AccessKeysTab.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/AccessKeysTab.java new file mode 100644 index 0000000000..a40d57627b --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/AccessKeysTab.java @@ -0,0 +1,83 @@ +package org.roda.wui.client.management.members.tabs; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.Widget; +import config.i18n.client.ClientMessages; +import org.roda.core.data.v2.user.RODAMember; +import org.roda.core.data.v2.user.User; +import org.roda.wui.client.common.ActionsToolbar; +import org.roda.wui.client.common.actions.Actionable; +import org.roda.wui.client.common.actions.RODAMemberAction; +import org.roda.wui.client.common.actions.RODAMemberToolbarActions; +import org.roda.wui.client.common.actions.model.ActionableObject; +import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; +import org.roda.wui.client.management.access.AccessKeyTablePanel; + +import java.util.List; + +/** + * @author Miguel Guimarães + */ + +public class AccessKeysTab extends Composite { + + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + + @UiField + ActionsToolbar actionsToolbar; + + @UiField + FlowPanel accessKeyTablePanel; + + // Keep a reference to the panel so we can refresh it + private AccessKeyTablePanel tablePanel; + + public AccessKeysTab(RODAMember member, AsyncCallback actionCallback) { + initWidget(uiBinder.createAndBindUi(this)); + + if (member.isUser()) { + User user = (User) member; + tablePanel = new AccessKeyTablePanel(user.getId()); + accessKeyTablePanel.add(tablePanel); + } + + AsyncCallback localCallback = new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + // Pass failures up to the parent handler + actionCallback.onFailure(caught); + } + + @Override + public void onSuccess(Actionable.ActionImpact result) { + if (Actionable.ActionImpact.UPDATED.equals(result)) { + // Refresh the table locally WITHOUT reloading the whole page! + if (tablePanel != null) { + tablePanel.refresh(); + } + } else { + // If it's something else (e.g., DESTROYED), let the parent handle it + actionCallback.onSuccess(result); + } + } + }; + + actionsToolbar.setLabelVisible(false); + actionsToolbar.setTagsVisible(false); + actionsToolbar.setActionableMenu(new ActionableWidgetBuilder(RODAMemberToolbarActions.get()) + .withActionCallback(localCallback).buildGroupedListWithObjects(new ActionableObject<>(member), + List.of(RODAMemberAction.NEW_ACCESS_KEY), List.of(RODAMemberAction.NEW_ACCESS_KEY)), + true); + + } + + interface MyUiBinder extends UiBinder { + Widget createAndBindUi(AccessKeysTab tab); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/AccessKeysTab.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/AccessKeysTab.ui.xml new file mode 100644 index 0000000000..2246174b56 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/AccessKeysTab.ui.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/PermissionsPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/PermissionsPanel.java new file mode 100644 index 0000000000..3a4d0715a1 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/PermissionsPanel.java @@ -0,0 +1,558 @@ +package org.roda.wui.client.management.members.tabs; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.event.logical.shared.HasValueChangeHandlers; +import com.google.gwt.event.logical.shared.ValueChangeEvent; +import com.google.gwt.event.logical.shared.ValueChangeHandler; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.user.client.ui.CheckBox; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.Label; +import config.i18n.client.ClientMessages; +import org.roda.core.data.v2.user.Group; +import org.roda.core.data.v2.user.RODAMember; +import org.roda.wui.client.management.members.MemberManagement; +import org.roda.wui.client.services.Services; +import org.roda.wui.common.client.ClientLogger; +import org.roda.wui.common.client.tools.ConfigurationManager; +import org.roda.wui.common.client.tools.HistoryUtils; +import org.roda.wui.common.client.widgets.LoadingPopup; +import org.roda.wui.common.client.widgets.Toast; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * + * @author Miguel Guimarães + */ +public class PermissionsPanel extends FlowPanel implements HasValueChangeHandlers> { + + private static final ClientMessages messages = GWT.create(ClientMessages.class); + + @SuppressWarnings("unused") + private final ClientLogger logger = new ClientLogger(getClass().getName()); + + private final List allPermissions; + private final Map categoryPanels; + private final LoadingPopup loading; + private final FlowPanel categoriesContainer; + private final boolean viewOnly; + private final boolean expanded; + private List userSelections; + private boolean enabled; + // New instance fields to support refreshing + private RODAMember member; + private Runnable onDataLoadedCallback; + + public PermissionsPanel(RODAMember member, boolean viewOnly, boolean expanded) { + this.allPermissions = new ArrayList<>(); + this.userSelections = new ArrayList<>(); + this.categoryPanels = new LinkedHashMap<>(); + + this.member = member; + this.viewOnly = viewOnly; + this.expanded = expanded; + + loading = new LoadingPopup(this); + + this.enabled = true; + this.addStyleName("permissions-dashboard"); + + // 2. Container for Categories + categoriesContainer = new FlowPanel(); + categoriesContainer.setStyleName("permissions-categories"); + this.add(categoriesContainer); + + refresh(); // Trigger initial load + } + + public void refresh() { + loading.show(); + + // Clear old elements and state before rebuilding + categoriesContainer.clear(); + allPermissions.clear(); + categoryPanels.clear(); + userSelections.clear(); + + // 1. Fetch the absolute latest member data (to get freshly edited roles) + Services userServices = new Services("Get updated member", "get"); + userServices.membersResource(s -> s.getUser(member.getUUID())).whenComplete(this::handleMemberRefresh); + } + + private void handleMemberRefresh(RODAMember updatedMember, Throwable error) { + if (error != null) { + loading.hide(); + Toast.showError("Unable to fetch updated user data"); + HistoryUtils.newHistory(MemberManagement.RESOLVER.getHistoryPath()); + return; + } + + // Update local reference so init() uses the new roles + this.member = updatedMember; + + // 2. Fetch the groups + Services groupServices = new Services("Fetch user's group", "get"); + groupServices.membersResource(s -> s.getUserGroups(member.getId())).whenComplete((groups, throwable) -> { + if (throwable != null) { + loading.hide(); + Toast.showError("Unable to fetch user's groups"); + ClientLogger clientLogger = new ClientLogger(); + clientLogger.error("Error fetching user's groups", throwable); + HistoryUtils.newHistory(MemberManagement.RESOLVER.getHistoryPath()); + } else { + init(member, groups, viewOnly, expanded); + } + }); + } + + public void init(RODAMember member, Set groups, boolean viewOnly, boolean expanded) { + if (!viewOnly) { + userSelections = new ArrayList<>(member.getDirectRoles()); + } + + List roles = ConfigurationManager.getStringList("ui.role"); + + for (String role : roles) { + String description; + try { + description = messages.role(role); + } catch (MissingResourceException e) { + description = role + " (needs translation)"; + } + + Permission permission = new Permission(role, description); + permission.setChecked(member.getAllRoles().contains(role)); + permission.setEnabled(!viewOnly); + permission.setLocked(viewOnly); + allPermissions.add(permission); + + permission.addValueChangeHandler(new ValueChangeHandler() { + @Override + public void onValueChange(ValueChangeEvent event) { + if (userSelections.contains(event.getValue())) { + userSelections.remove(event.getValue()); + } else { + userSelections.add(event.getValue()); + } + onChange(); + } + }); + + // Grouping logic + String categoryName = determineCategory(role); + CategoryPanel categoryPanel = categoryPanels.get(categoryName); + if (categoryPanel == null) { + categoryPanel = new CategoryPanel(categoryName, viewOnly, expanded); + categoryPanels.put(categoryName, categoryPanel); + categoriesContainer.add(categoryPanel); + } + categoryPanel.addPermission(permission); + } + + if (!viewOnly) { + checkPermissions(member.getDirectRoles(), groups, true, "Permission locked because it's inherited from a group"); + } + + loading.hide(); + + if (onDataLoadedCallback != null) { + onDataLoadedCallback.run(); + } + } + + // --- The rest of your existing logic remains untouched --- + + private String determineCategory(String role) { + String lowerRole = role.toLowerCase(); + if (lowerRole.startsWith("aip") || (lowerRole.startsWith("representation")) + || lowerRole.startsWith("descriptive_metadata") || lowerRole.startsWith("preservation_metadata")) { + return messages.catalogueAndSearchGroupLabel(); + } + + if (lowerRole.startsWith("transfer") || lowerRole.startsWith("job")) { + return messages.ingestPreservationActionsInternalActionsGroupLabel(); + } + + if (lowerRole.startsWith("disposal_")) { + return messages.disposalGroupLabel(); + } + + if (lowerRole.startsWith("member") || lowerRole.startsWith("access_key") || lowerRole.startsWith("permission") + || lowerRole.startsWith("notification") || lowerRole.startsWith("log_entry") + || lowerRole.startsWith("distributed_instances") || lowerRole.startsWith("local_instance_configuration")) { + return messages.administrationGroupLabel(); + } + + if (lowerRole.startsWith("ri") || lowerRole.startsWith("risk")) { + return messages.planningGroupLabel(); + } + + return "Undefined"; + } + + public List getUserSelections() { + for (Permission permission : allPermissions) { + if (permission.isChecked() && !permission.isLocked() && permission.isEnabled()) { + if (!userSelections.contains(permission.getRole())) { + userSelections.add(permission.getRole()); + } + } else { + userSelections.remove(permission.getRole()); + } + } + + return userSelections; + } + + public void checkPermissions(Set directRoles, Set groups, boolean lock, String description) { + Set allRoles = groups.stream().flatMap(group -> group.getAllRoles().stream()).collect(Collectors.toSet()); + + if (allRoles.containsAll(directRoles)) { + checkPermissions(allRoles, true, description); + } else { + checkPermissions(directRoles, false); + checkPermissions(allRoles, true, description); + } + } + + public void checkPermissions(Set roles, boolean lock) { + checkPermissions(roles, lock, null); + } + + private void checkPermissions(Set roles, boolean lock, String description) { + for (String role : roles) { + boolean foundIt = false; + for (Iterator j = allPermissions.iterator(); j.hasNext() && !foundIt;) { + Permission p = j.next(); + if (p.getRole().equals(role)) { + foundIt = true; + p.setChecked(true); + if (description != null) { + p.setTooltip(description); + } + p.setLocked(lock); + } + } + } + } + + @Override + public void clear() { + for (Permission p : allPermissions) { + p.setChecked(false); + p.setLocked(false); + } + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + for (Permission p : allPermissions) { + p.setEnabled(enabled); + } + } + + public void updateLockedPermissions(Set memberGroups) { + if (!memberGroups.isEmpty()) { + this.setEnabled(false); + loading.show(); + } + } + + public Set getDirectRoles() { + List checkedPermissions = new ArrayList<>(); + for (Permission p : allPermissions) { + if (p.isChecked() && !p.isLocked()) { + checkedPermissions.add(p); + } + } + + Set specialRoles = new HashSet<>(); + for (Permission checkedPermission : checkedPermissions) { + specialRoles.add(checkedPermission.getRole()); + } + return specialRoles; + } + + @Override + public HandlerRegistration addValueChangeHandler(ValueChangeHandler> handler) { + return addHandler(handler, ValueChangeEvent.getType()); + } + + protected void onChange() { + ValueChangeEvent.fire(this, getValue()); + } + + public List getValue() { + return getUserSelections(); + } + + public void setOnDataLoadedCallback(Runnable callback) { + this.onDataLoadedCallback = callback; + } + + // --- INNER CLASSES --- + private class CategoryPanel extends FlowPanel { + private final FlowPanel itemsContainer; + private final List permissionsInCat = new ArrayList<>(); + private final CheckBox selectAllBox; + private final CheckBox readOnlyPermissions; + private final Label iconLabel; + private boolean isExpanded = false; + + public CategoryPanel(String titleText, boolean viewOnly, boolean expanded) { + this.isExpanded = expanded; + this.setStyleName("permission-category"); + // --- HEADER --- + FlowPanel header = new FlowPanel(); + header.setStyleName("permission-category-header"); + + // Title on the Left + Label title = new Label(titleText); + title.setStyleName("permission-category-title"); + + // Right side wrapper: Select All + Arrow + FlowPanel headerRight = new FlowPanel(); + headerRight.setStyleName("permission-category-header-right"); + + // Select All Checkbox (Passing the label text directly to the checkbox) + selectAllBox = new CheckBox(messages.selectAllButton()); + selectAllBox.setStyleName("permission-category-checkbox"); + + // Prevent header expand/collapse when interacting with the checkbox + selectAllBox.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + event.stopPropagation(); + } + }); + + selectAllBox.addValueChangeHandler(new ValueChangeHandler() { + @Override + public void onValueChange(ValueChangeEvent event) { + readOnlyPermissions.setValue(false); + triggerSelectAll(event.getValue()); + } + }); + + readOnlyPermissions = new CheckBox(messages.editPermissionsReadOnlyPermissionsText()); + readOnlyPermissions.setStyleName("permission-category-checkbox"); + // Prevent header expand/collapse when interacting with the checkbox + readOnlyPermissions.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + event.stopPropagation(); + } + }); + + readOnlyPermissions.addValueChangeHandler(new ValueChangeHandler() { + @Override + public void onValueChange(ValueChangeEvent event) { + selectAllBox.setValue(false); + triggerSelectReadOnly(event.getValue()); + } + }); + + // Arrow Icon + iconLabel = new Label(); + iconLabel.setStyleName("fas fa-chevron-down permission-category-icon"); + + // Add elements to the right wrapper + if (!viewOnly) { + headerRight.add(readOnlyPermissions); + headerRight.add(selectAllBox); + } + headerRight.add(iconLabel); + + // Add left title and right wrapper to the main flex header + header.add(title); + header.add(headerRight); + + // Accordion Expand/Collapse Logic + // --- FIXED ACCORDION TOGGLE LOGIC --- + header.addDomHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + + // FIX: Check exactly what element was clicked. + // If it was the Select All box OR its text label, abort the toggle. + com.google.gwt.dom.client.Element target = com.google.gwt.dom.client.Element + .as(event.getNativeEvent().getEventTarget()); + + if (selectAllBox.getElement().isOrHasChild(target) || readOnlyPermissions.getElement().isOrHasChild(target)) { + return; + } + + // Otherwise, toggle normally + isExpanded = !isExpanded; + itemsContainer.setVisible(isExpanded); + if (isExpanded) { + header.removeStyleName("collapsed"); + iconLabel.setStyleName("fas fa-chevron-up permission-category-icon"); + } else { + header.addStyleName("collapsed"); + iconLabel.setStyleName("fas fa-chevron-down permission-category-icon"); + } + } + }, ClickEvent.getType()); + + // --- ITEMS CONTAINER --- + itemsContainer = new FlowPanel(); + itemsContainer.setStyleName("permission-category-items"); + + if (isExpanded) { + header.removeStyleName("collapsed"); + iconLabel.setStyleName("fas fa-chevron-up permission-category-icon"); + itemsContainer.setVisible(true); + } else { + itemsContainer.setVisible(false); + } + + this.add(header); + this.add(itemsContainer); + } + + public void addPermission(Permission p) { + permissionsInCat.add(p); + itemsContainer.add(p); + + // Uncheck "Select All" in the header if a single item is unchecked + p.addValueChangeHandler(new ValueChangeHandler() { + @Override + public void onValueChange(ValueChangeEvent event) { + if (!p.isChecked()) { + selectAllBox.setValue(false, false); + } + } + }); + } + + private void triggerSelectAll(boolean isChecked) { + for (Permission p : permissionsInCat) { + if (p.isVisible() && p.isEnabled() && !p.isLocked()) { + if (p.isChecked() != isChecked) { + p.setChecked(isChecked); + p.onChange(); + } + } + } + } + + private void triggerSelectReadOnly(boolean isChecked) { + for (Permission p : permissionsInCat) { + if (p.isVisible() && p.isEnabled() && !p.isLocked()) + if (p.getRole().contains("read") || p.getRole().contains("view")) { + p.setChecked(isChecked); + p.onChange(); + } else { + p.setChecked(false); + p.onChange(); + } + } + } + } + + private class Permission extends FlowPanel implements HasValueChangeHandlers { + + private final String role; + private final CheckBox checkbox; + private final Label descriptionLabel; + private boolean locked; + private boolean enabled; + + public Permission(String role, String description) { + this.role = role; + this.checkbox = new CheckBox(); + this.descriptionLabel = new Label(description); + + this.add(checkbox); + this.add(descriptionLabel); + this.locked = false; + this.enabled = true; + + this.descriptionLabel.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + if (isEnabled() && !locked) { + checkbox.setValue(!checkbox.getValue()); + onChange(); + } + } + }); + + this.checkbox.addValueChangeHandler(new ValueChangeHandler() { + @Override + public void onValueChange(ValueChangeEvent event) { + onChange(); + } + }); + + this.addStyleName("permission-category-item"); + checkbox.addStyleName("permission-checkbox"); + descriptionLabel.setStylePrimaryName("permission-description"); + } + + public boolean isLocked() { + return locked; + } + + public void setLocked(boolean locked) { + this.locked = locked; + checkbox.setEnabled(!locked); + } + + public void setTooltip(String description) { + this.setTitle(description); + } + + public boolean isChecked() { + return checkbox.getValue(); + } + + public void setChecked(boolean checked) { + checkbox.setValue(checked); + } + + public String getRole() { + return role; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + if (!locked) { + this.checkbox.setEnabled(enabled); + } + } + + @Override + public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) { + return addHandler(handler, ValueChangeEvent.getType()); + } + + protected void onChange() { + ValueChangeEvent.fire(this, getValue()); + } + + public String getValue() { + return getRole(); + } + } +} \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberDetailsPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberDetailsPanel.java new file mode 100644 index 0000000000..b288f2bd41 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberDetailsPanel.java @@ -0,0 +1,149 @@ +package org.roda.wui.client.management.members.tabs; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; + +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.Widget; +import config.i18n.client.ClientMessages; +import org.roda.core.data.v2.user.Group; +import org.roda.core.data.v2.user.RODAMember; +import org.roda.core.data.v2.user.User; +import org.roda.wui.client.common.ActionsToolbar; +import org.roda.wui.client.common.actions.Actionable; +import org.roda.wui.client.common.actions.RODAMemberAction; +import org.roda.wui.client.common.actions.RODAMemberToolbarActions; +import org.roda.wui.client.common.actions.model.ActionableObject; +import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; +import org.roda.wui.client.common.utils.FormUtilities; +import org.roda.wui.client.common.utils.HtmlSnippetUtils; +import org.roda.wui.client.services.Services; +import org.roda.wui.common.client.widgets.Toast; + +import java.util.List; + +/** + * @author Miguel Guimarães + */ + +public class RODAMemberDetailsPanel extends Composite { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + + @UiField + ActionsToolbar actionsToolbar; + + @UiField + FlowPanel detailsPanel; + + private RODAMember member; + + public RODAMemberDetailsPanel(RODAMember member, AsyncCallback actionCallback) { + initWidget(uiBinder.createAndBindUi(this)); + this.member = member; + + AsyncCallback localCallback = new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + actionCallback.onFailure(caught); + } + + @Override + public void onSuccess(Actionable.ActionImpact result) { + if (Actionable.ActionImpact.UPDATED.equals(result)) { + actionCallback.onSuccess(result); + } + } + }; + + actionsToolbar.setLabelVisible(false); + actionsToolbar.setTagsVisible(false); + actionsToolbar.setActionableMenu(new ActionableWidgetBuilder(RODAMemberToolbarActions.get()) + .withActionCallback(localCallback).buildGroupedListWithObjects(new ActionableObject<>(member), + List.of(RODAMemberAction.EDIT), List.of(RODAMemberAction.EDIT)), + true); + + init(member); + } + + public void refresh() { + Services services = new Services("Get updated member details", "get"); + + if (member.isUser()) { + services.membersResource(s -> s.getUser(member.getUUID())).whenComplete((updatedUser, err) -> { + if (err != null) { + Toast.showError("Unable to fetch updated user details"); + } else if (updatedUser != null) { + this.member = updatedUser; + clear(); + init(updatedUser); + } + }); + } else { + services.membersResource(s -> s.getUser(member.getUUID())).whenComplete((updatedGroup, err) -> { + if (err != null) { + Toast.showError("Unable to fetch updated group details"); + } else if (updatedGroup != null) { + this.member = updatedGroup; + clear(); + init(updatedGroup); + } + }); + } + } + + private void init(RODAMember member) { + if (member.isUser()) { + initUser(member); + } else { + initGroup(member); + } + } + + private void initUser(RODAMember member) { + User user = (User) member; + + FormUtilities.addIfNotBlank(detailsPanel, messages.username(), user.getId()); + FormUtilities.addIfNotBlank(detailsPanel, messages.fullname(), user.getFullName()); + FormUtilities.addIfNotBlank(detailsPanel, messages.email(), user.getEmail()); + + FlowPanel topPanel = new FlowPanel(); + FlowPanel status = new FlowPanel(); + Label statusLabel = new Label(); + statusLabel.addStyleName("label"); + statusLabel.setText(messages.showUserStatusLabel()); + status.add(statusLabel); + HTML statusValue = new HTML(); + statusValue.addStyleName("value"); + statusValue.setHTML(HtmlSnippetUtils.getUserStateHtml(user)); + status.add(statusValue); + status.addStyleName("field"); + topPanel.addStyleName("descriptiveMetadata"); + topPanel.add(status); + detailsPanel.add(topPanel); + + if (!user.getExtra().isEmpty()) { + HtmlSnippetUtils.createExtraShow(detailsPanel, user.getExtra(), false); + } + } + + private void initGroup(RODAMember member) { + Group group = (Group) member; + + FormUtilities.addIfNotBlank(detailsPanel, messages.username(), group.getId()); + FormUtilities.addIfNotBlank(detailsPanel, messages.fullname(), group.getFullName()); + } + + public void clear() { + detailsPanel.clear(); + } + + interface MyUiBinder extends UiBinder { + Widget createAndBindUi(RODAMemberDetailsPanel detailsPanel); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberDetailsPanel.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberDetailsPanel.ui.xml new file mode 100644 index 0000000000..fb3e0cf09d --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberDetailsPanel.ui.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberGroupsTab.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberGroupsTab.java new file mode 100644 index 0000000000..fb3aea69d6 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberGroupsTab.java @@ -0,0 +1,330 @@ +package org.roda.wui.client.management.members.tabs; + +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.user.cellview.client.Column; +import com.google.gwt.user.cellview.client.TextColumn; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.PopupPanel; +import com.google.gwt.user.client.ui.ScrollPanel; +import config.i18n.client.ClientMessages; +import org.roda.core.data.v2.user.Group; +import org.roda.core.data.v2.user.RODAMember; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.Widget; +import org.roda.core.data.v2.user.User; +import org.roda.wui.client.common.ActionsToolbar; +import org.roda.wui.client.common.NoAsyncCallback; +import org.roda.wui.client.common.actions.Actionable; +import org.roda.wui.client.common.actions.RODAMemberAction; +import org.roda.wui.client.common.actions.RODAMemberToolbarActions; +import org.roda.wui.client.common.actions.model.ActionableObject; +import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; +import org.roda.wui.client.common.dialogs.Dialogs; +import org.roda.wui.client.common.lists.utils.ActionMenuCell; +import org.roda.wui.client.common.lists.utils.BasicTablePanel; +import org.roda.wui.client.services.Services; +import org.roda.wui.common.client.widgets.Toast; + +import java.util.List; +import java.util.Set; + +/** + * + * @author Miguel Guimarães + */ +public class RODAMemberGroupsTab extends Composite { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + private final String id; + private final RODAMember member; // Keep a reference to check member.isUser() in refresh() + @UiField + ActionsToolbar actionsToolbar; + @UiField + FlowPanel groupsPanel; + + private AsyncCallback actionCallback; + + public RODAMemberGroupsTab(RODAMember member, AsyncCallback actionCallback) { + initWidget(uiBinder.createAndBindUi(this)); + + this.member = member; + this.id = member.getId(); + this.actionCallback = actionCallback; + + // 1. Create a local callback to intercept the UPDATED event + AsyncCallback localCallback = new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + actionCallback.onFailure(caught); + } + + @Override + public void onSuccess(Actionable.ActionImpact result) { + if (Actionable.ActionImpact.UPDATED.equals(result)) { + // Refresh the table locally + refresh(); + actionCallback.onSuccess(result); + } else { + // Pass other events (like DESTROYED) up the chain + actionCallback.onSuccess(result); + } + } + }; + + // 2. Bind the local callback to the toolbar + actionsToolbar.setLabelVisible(false); + actionsToolbar.setTagsVisible(false); + actionsToolbar.setActionableMenu(new ActionableWidgetBuilder(RODAMemberToolbarActions.get()) + .withActionCallback(localCallback).buildGroupedListWithObjects(new ActionableObject<>(member), + List.of(RODAMemberAction.ADD_NEW_GROUP, RODAMemberAction.ADD_NEW_MEMBER), + List.of(RODAMemberAction.ADD_NEW_GROUP, RODAMemberAction.ADD_NEW_MEMBER)), + true); + + // 3. Call unified refresh + refresh(); + } + + public void refresh() { + if (member.isUser()) { + Services services = new Services("Get user groups", "get"); + services.membersResource(s -> s.getUserGroups(id)).whenComplete((groupsSet, error) -> { + if (groupsSet != null) { + groupsPanel.clear(); + groupsPanel.add(createTable(groupsSet)); + } else if (error != null) { + Toast.showError(error.getMessage()); + } + }); + } else { + Services services = new Services("Get members of group", "get"); + services.membersResource(s -> s.getGroupMembers(id)).whenComplete((members, error) -> { + if (members != null) { + groupsPanel.clear(); + groupsPanel.add(createTableGroup(members)); + } else if (error != null) { + Toast.showError(error.getMessage()); + } + }); + } + } + + public ScrollPanel createTableGroup(Set members) { + BasicTablePanel table; + ScrollPanel scrollPanel; + + if (members.isEmpty()) { + String someOfAObject = messages.someOfAObject(User.class.getName()); + Label label = new HTML(SafeHtmlUtils.fromSafeConstant(messages.noItemsToDisplayPreFilters(someOfAObject))); + label.addStyleName("basicTableEmpty"); + scrollPanel = new ScrollPanel(label); + } else { + FlowPanel panel = new FlowPanel(); + table = getBasicTableForGroups(members); + table.removeSelectionModel(); + + panel.add(table); + scrollPanel = new ScrollPanel(panel); + } + + return scrollPanel; + } + + public ScrollPanel createTable(Set groups) { + BasicTablePanel table; + ScrollPanel scrollPanel; + + if (groups.isEmpty()) { + String someOfAObject = messages.someOfAObject(Group.class.getName()); + Label label = new HTML(SafeHtmlUtils.fromSafeConstant(messages.noItemsToDisplayPreFilters(someOfAObject))); + label.addStyleName("basicTableEmpty"); + scrollPanel = new ScrollPanel(label); + } else { + FlowPanel panel = new FlowPanel(); + table = getBasicTableForUsers(groups); + table.removeSelectionModel(); + + panel.add(table); + scrollPanel = new ScrollPanel(panel); + } + + return scrollPanel; + } + + private void showActionsMenu(Group key, int left, int top) { + // 1. Create the Popup + PopupPanel popup = new PopupPanel(true); // true = auto-hide when clicking away + + // 2. Create your FlowPanel and add your action items + FlowPanel menuPanel = new FlowPanel(); + menuPanel.addStyleName("groupedActionableDropdown"); + + Button removeBtn = new Button(messages.removeButton()); + removeBtn.addStyleName("actionable-button actionable-button-updated actionable-button-label btn-edit"); + removeBtn.addClickHandler(e -> { + popup.hide(); + + Dialogs.showConfirmDialog(messages.groups(), messages.removeGroupConfirmationMessage(key.getFullName()), + messages.cancelButton(), messages.confirmButton(), new NoAsyncCallback() { + @Override + public void onSuccess(Boolean confirm) { + if (confirm) { + Services services = new Services("Remove group from user", "update"); + services.membersResource(s -> s.removeGroupsFromUser(id, key.getId())).whenComplete((response, error) -> { + if (response != null) { + Toast.showInfo(messages.groups(), messages.groupSuccessfullyRemoved()); + + // Simple call to the unified refresh method + refresh(); + actionCallback.onSuccess(Actionable.ActionImpact.UPDATED); + + } else { + Toast.showError("Failed to remove group from user"); + } + }); + } + } + }); + }); + + menuPanel.add(removeBtn); + + // 3. Show the popup at the calculated coordinates + popup.setWidget(menuPanel); + popup.setPopupPosition(left, top); + popup.show(); + } + + private void showUserActionsMenu(User key, int left, int top) { + // 1. Create the Popup + PopupPanel popup = new PopupPanel(true); // true = auto-hide when clicking away + + // 2. Create your FlowPanel and add your action items + FlowPanel menuPanel = new FlowPanel(); + menuPanel.addStyleName("groupedActionableDropdown"); + + Button removeBtn = new Button(messages.removeButton()); + removeBtn.addStyleName("actionable-button actionable-button-updated actionable-button-label btn-edit"); + removeBtn.addClickHandler(e -> { + popup.hide(); + + Dialogs.showConfirmDialog(messages.removeMemberConfirmationTitle(), messages.removeMemberConfirmationMessage(key.getFullName()), + messages.cancelButton(), messages.confirmButton(), new NoAsyncCallback() { + @Override + public void onSuccess(Boolean confirm) { + if (confirm) { + Services services = new Services("Remove member from group", "update"); + services.membersResource(s -> s.removeMembersFromGroup(id, key.getId())).whenComplete((response, error) -> { + if (response != null) { + Toast.showInfo(messages.groups(), messages.groupSuccessfullyRemoved()); + + // Simple call to the unified refresh method + refresh(); + } else { + Toast.showError("Failed to remove group from user"); + } + }); + } + } + }); + }); + + menuPanel.add(removeBtn); + + // 3. Show the popup at the calculated coordinates + popup.setWidget(menuPanel); + popup.setPopupPosition(left, top); + popup.show(); + } + + private TextColumn getNameColumn() { + return new TextColumn() { + @Override + public String getValue(Group group) { + return group.getName(); + } + }; + } + + private TextColumn getFullNameColumn() { + return new TextColumn() { + @Override + public String getValue(Group group) { + return group.getFullName(); + } + }; + } + + private TextColumn getUserNameColumn() { + return new TextColumn() { + @Override + public String getValue(User group) { + return group.getName(); + } + }; + } + + private TextColumn getUserFullNameColumn() { + return new TextColumn() { + @Override + public String getValue(User group) { + return group.getFullName(); + } + }; + } + + private Column getActionsColumn() { + ActionMenuCell actionCell = new ActionMenuCell(this::showActionsMenu); + + return new Column(actionCell) { + @Override + public Group getValue(Group object) { + return object; + } + }; + } + + private Column getUserActionsColumn() { + ActionMenuCell actionCell = new ActionMenuCell(this::showUserActionsMenu); + + return new Column(actionCell) { + @Override + public User getValue(User object) { + return object; + } + }; + } + + private BasicTablePanel getBasicTableForUsers(Set groups) { + if (groups.isEmpty()) { + return new BasicTablePanel<>(messages.noItemsToDisplay(messages.distributedInstanceLabel())); + } else { + return new BasicTablePanel<>(groups.iterator(), + new BasicTablePanel.ColumnInfo(messages.groupFullname(), 15, getFullNameColumn()), + new BasicTablePanel.ColumnInfo(messages.groupName(), 15, getNameColumn()), + new BasicTablePanel.ColumnInfo<>(messages.actions(), 5, getActionsColumn())); + } + } + + private BasicTablePanel getBasicTableForGroups(Set members) { + if (members.isEmpty()) { + return new BasicTablePanel<>(messages.noItemsToDisplay(messages.distributedInstanceLabel())); + } else { + return new BasicTablePanel<>(members.iterator(), + new BasicTablePanel.ColumnInfo(messages.groupFullname(), 15, getUserFullNameColumn()), + new BasicTablePanel.ColumnInfo(messages.groupName(), 15, getUserNameColumn()), + new BasicTablePanel.ColumnInfo<>(messages.actions(), 5, getUserActionsColumn())); + } + } + + interface MyUiBinder extends UiBinder { + Widget createAndBindUi(RODAMemberGroupsTab tab); + } +} \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberGroupsTab.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberGroupsTab.ui.xml new file mode 100644 index 0000000000..9b5dc83176 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberGroupsTab.ui.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberPermissionsTab.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberPermissionsTab.java new file mode 100644 index 0000000000..a51a5b357f --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberPermissionsTab.java @@ -0,0 +1,67 @@ +package org.roda.wui.client.management.members.tabs; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.Widget; + +import org.roda.core.data.v2.user.RODAMember; +import org.roda.wui.client.common.ActionsToolbar; +import org.roda.wui.client.common.actions.Actionable; +import org.roda.wui.client.common.actions.RODAMemberAction; +import org.roda.wui.client.common.actions.RODAMemberToolbarActions; +import org.roda.wui.client.common.actions.model.ActionableObject; +import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; + +import java.util.List; + +/** + * + * @author Miguel Guimarães + */ +public class RODAMemberPermissionsTab extends Composite { + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + + @UiField + ActionsToolbar actionsToolbar; + + // Changed from FlowPanel to PermissionsPanel to expose the refresh() method + @UiField(provided = true) + PermissionsPanel permissionsTablePanel; + + public RODAMemberPermissionsTab(RODAMember member, AsyncCallback actionCallback) { + permissionsTablePanel = new PermissionsPanel(member, true, false); + initWidget(uiBinder.createAndBindUi(this)); + + // 1. Create a local callback to intercept the UPDATED event + AsyncCallback localCallback = new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + actionCallback.onFailure(caught); + } + + @Override + public void onSuccess(Actionable.ActionImpact result) { + if (Actionable.ActionImpact.UPDATED.equals(result)) { + // Refresh the table locally WITHOUT reloading the whole page! + permissionsTablePanel.refresh(); + } else { + actionCallback.onSuccess(result); + } + } + }; + + actionsToolbar.setLabelVisible(false); + actionsToolbar.setTagsVisible(false); + actionsToolbar.setActionableMenu(new ActionableWidgetBuilder(RODAMemberToolbarActions.get()) + .withActionCallback(localCallback).buildGroupedListWithObjects(new ActionableObject<>(member), + List.of(RODAMemberAction.EDIT_PERMISSIONS), List.of(RODAMemberAction.EDIT_PERMISSIONS)), + true); + } + + interface MyUiBinder extends UiBinder { + Widget createAndBindUi(RODAMemberPermissionsTab tab); + } +} \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberPermissionsTab.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberPermissionsTab.ui.xml new file mode 100644 index 0000000000..3cb2a58a4c --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/tabs/RODAMemberPermissionsTab.ui.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/CreateRepresentationInformation.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/CreateRepresentationInformation.java index 82952f8a93..1dcab617d3 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/CreateRepresentationInformation.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/CreateRepresentationInformation.java @@ -12,7 +12,6 @@ import org.roda.core.data.v2.ri.RepresentationInformation; import org.roda.core.data.v2.ri.RepresentationInformationCreateRequest; import org.roda.wui.client.common.UserLogin; -import org.roda.wui.client.management.MemberManagement; import org.roda.wui.client.services.Services; import org.roda.wui.common.client.HistoryResolver; import org.roda.wui.common.client.tools.HistoryUtils; @@ -42,7 +41,7 @@ public void resolve(List historyTokens, final AsyncCallback call @Override public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); + UserLogin.getInstance().checkRole(this, callback); } @Override diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/CreateRisk.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/CreateRisk.java index 04e38a0f66..6a67524e5d 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/CreateRisk.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/CreateRisk.java @@ -12,7 +12,6 @@ import org.roda.core.data.v2.risks.Risk; import org.roda.wui.client.common.UserLogin; import org.roda.wui.client.common.utils.AsyncCallbackUtils; -import org.roda.wui.client.management.MemberManagement; import org.roda.wui.client.services.Services; import org.roda.wui.common.client.HistoryResolver; import org.roda.wui.common.client.tools.HistoryUtils; @@ -45,7 +44,7 @@ public void resolve(List historyTokens, final AsyncCallback call @Override public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); + UserLogin.getInstance().checkRole(this, callback); } @Override @@ -66,12 +65,6 @@ public String getHistoryToken() { @UiField(provided = true) RiskDataPanel riskDataPanel; - /** - * Create a new panel to create a user - * - * @param user - * the user to create - */ public CreateRisk() { this.riskDataPanel = new RiskDataPanel(null, false); initWidget(uiBinder.createAndBindUi(this)); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/EditRepresentationInformation.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/EditRepresentationInformation.java index 2c7cb09864..365d3cf758 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/EditRepresentationInformation.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/EditRepresentationInformation.java @@ -19,7 +19,6 @@ import org.roda.wui.client.common.dialogs.Dialogs; import org.roda.wui.client.common.utils.AsyncCallbackUtils; import org.roda.wui.client.ingest.process.ShowJob; -import org.roda.wui.client.management.MemberManagement; import org.roda.wui.client.process.InternalProcess; import org.roda.wui.client.services.Services; import org.roda.wui.common.client.HistoryResolver; @@ -67,7 +66,7 @@ public void resolve(List historyTokens, final AsyncCallback call @Override public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); + UserLogin.getInstance().checkRole(this, callback); } @Override diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/EditRisk.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/EditRisk.java index 6fb3218045..043858e281 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/EditRisk.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/EditRisk.java @@ -20,7 +20,6 @@ import org.roda.wui.client.common.dialogs.Dialogs; import org.roda.wui.client.common.utils.AsyncCallbackUtils; import org.roda.wui.client.ingest.process.ShowJob; -import org.roda.wui.client.management.MemberManagement; import org.roda.wui.client.process.InternalProcess; import org.roda.wui.client.services.Services; import org.roda.wui.common.client.HistoryResolver; @@ -70,7 +69,7 @@ public void resolve(List historyTokens, final AsyncCallback call @Override public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); + UserLogin.getInstance().checkRole(this, callback); } @Override diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/EditRiskIncidence.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/EditRiskIncidence.java index cef3d874d3..afd9b8b3d8 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/EditRiskIncidence.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/EditRiskIncidence.java @@ -17,7 +17,6 @@ import org.roda.core.data.v2.risks.SeverityLevel; import org.roda.wui.client.common.UserLogin; import org.roda.wui.client.common.utils.HtmlSnippetUtils; -import org.roda.wui.client.management.MemberManagement; import org.roda.wui.client.services.Services; import org.roda.wui.common.client.HistoryResolver; import org.roda.wui.common.client.tools.HistoryUtils; @@ -71,7 +70,7 @@ public void resolve(List historyTokens, final AsyncCallback call @Override public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); + UserLogin.getInstance().checkRole(this, callback); } @Override @@ -84,51 +83,35 @@ public String getHistoryToken() { return "edit_riskincidence"; } }; - - interface MyUiBinder extends UiBinder { - } - - private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - private static final List fieldsToReturn = Arrays.asList(RodaConstants.INDEX_UUID, RodaConstants.RISK_INCIDENCE_ID, RodaConstants.RISK_INCIDENCE_RISK_ID, RodaConstants.RISK_INCIDENCE_DESCRIPTION, RodaConstants.RISK_INCIDENCE_STATUS, RodaConstants.RISK_INCIDENCE_SEVERITY, RodaConstants.RISK_INCIDENCE_DETECTED_BY, RodaConstants.RISK_INCIDENCE_DETECTED_ON, RodaConstants.RISK_INCIDENCE_MITIGATED_ON, RodaConstants.RISK_INCIDENCE_MITIGATED_BY, RodaConstants.RISK_INCIDENCE_MITIGATED_DESCRIPTION); - - private RiskIncidence incidence; private static final ClientMessages messages = GWT.create(ClientMessages.class); - + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); @UiField Button buttonApply; - @UiField Button buttonCancel; - @UiField Label incidenceId; - @UiField Label objectLabel; - @UiField Anchor objectLink; - @UiField Anchor riskLink; - @UiField Label detectedOn, detectedBy; - @UiField TextArea description; - @UiField ListBox status, severity; - @UiField TextArea mitigatedDescription; + private RiskIncidence incidence; /** * Create a new panel to edit incidence @@ -227,4 +210,7 @@ private void errorMessage(Throwable caught) { } } + interface MyUiBinder extends UiBinder { + } + } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskHistory.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskHistory.java index 46f7f4ae29..93f8eba2d9 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskHistory.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskHistory.java @@ -21,7 +21,6 @@ import org.roda.wui.client.common.UserLogin; import org.roda.wui.client.common.dialogs.Dialogs; import org.roda.wui.client.common.utils.AsyncCallbackUtils; -import org.roda.wui.client.management.MemberManagement; import org.roda.wui.client.services.Services; import org.roda.wui.common.client.HistoryResolver; import org.roda.wui.common.client.tools.HistoryUtils; @@ -70,7 +69,7 @@ public void resolve(List historyTokens, final AsyncCallback call @Override public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); + UserLogin.getInstance().checkRole(this, callback); } @Override diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRepresentationInformation.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRepresentationInformation.java index 23ba0e4497..60522efa74 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRepresentationInformation.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRepresentationInformation.java @@ -39,7 +39,6 @@ import org.roda.wui.client.common.utils.AsyncCallbackUtils; import org.roda.wui.client.common.utils.HtmlSnippetUtils; import org.roda.wui.client.common.utils.SidebarUtils; -import org.roda.wui.client.management.MemberManagement; import org.roda.wui.client.search.Search; import org.roda.wui.client.services.Services; import org.roda.wui.common.client.HistoryResolver; @@ -84,7 +83,7 @@ public void resolve(List historyTokens, final AsyncCallback call @Override public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); + UserLogin.getInstance().checkRole(this, callback); } @Override diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRisk.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRisk.java index da12b78da2..c8e59cdcf9 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRisk.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRisk.java @@ -12,9 +12,7 @@ import java.util.List; -import org.roda.core.data.v2.ip.IndexedAIP; import org.roda.core.data.v2.risks.IndexedRisk; -import org.roda.wui.client.browse.EditDescriptiveMetadata; import org.roda.wui.client.common.NoAsyncCallback; import org.roda.wui.client.common.UserLogin; import org.roda.wui.client.common.actions.Actionable; @@ -22,7 +20,6 @@ import org.roda.wui.client.common.actions.model.ActionableObject; import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; import org.roda.wui.client.common.utils.SidebarUtils; -import org.roda.wui.client.management.MemberManagement; import org.roda.wui.client.services.Services; import org.roda.wui.common.client.HistoryResolver; import org.roda.wui.common.client.tools.HistoryUtils; @@ -55,7 +52,7 @@ public void resolve(List historyTokens, final AsyncCallback call @Override public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); + UserLogin.getInstance().checkRole(this, callback); } @Override diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRiskIncidence.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRiskIncidence.java index 3c347709cc..c7afc1e0a8 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRiskIncidence.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRiskIncidence.java @@ -22,7 +22,6 @@ import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; import org.roda.wui.client.common.utils.HtmlSnippetUtils; import org.roda.wui.client.common.utils.SidebarUtils; -import org.roda.wui.client.management.MemberManagement; import org.roda.wui.client.services.Services; import org.roda.wui.common.client.HistoryResolver; import org.roda.wui.common.client.tools.HistoryUtils; @@ -60,7 +59,7 @@ public void resolve(List historyTokens, final AsyncCallback call @Override public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {MemberManagement.RESOLVER}, false, callback); + UserLogin.getInstance().checkRole(this, callback); } @Override diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/services/MembersRestService.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/services/MembersRestService.java index 1fe00bc838..7560b00b4c 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/services/MembersRestService.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/services/MembersRestService.java @@ -57,8 +57,7 @@ public interface MembersRestService extends RODAEntityRestService { @Operation(summary = "Get user", description = "Gets a particular user", responses = { @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = User.class))), @ApiResponse(responseCode = "404", description = "Not found", content = @Content(schema = @Schema(implementation = ErrorResponseMessage.class)))}) - User getUser( - @Parameter(description = "The user identifier") @PathVariable(name = "id") String name); + RODAMember getUser(@Parameter(description = "The user identifier") @PathVariable(name = "id") String name); @RequestMapping(path = "/users/authenticated", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Get authenticated user", description = "Gets the authenticated user", responses = { @@ -70,23 +69,20 @@ User getUser( @Operation(summary = "Delete user", description = "Deletes an existing user", responses = { @ApiResponse(responseCode = "204", description = "No Content"), @ApiResponse(responseCode = "404", description = "Not found", content = @Content(schema = @Schema(implementation = ErrorResponseMessage.class)))}) - Void deleteUser( - @Parameter(description = "The user identifier") @PathVariable(name = "id") String name); + Void deleteUser(@Parameter(description = "The user identifier") @PathVariable(name = "id") String name); @RequestMapping(path = "/groups/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Get group", description = "Gets a particular group", responses = { @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = User.class))), @ApiResponse(responseCode = "404", description = "Not found", content = @Content(schema = @Schema(implementation = ErrorResponseMessage.class)))}) - Group getGroup( - @Parameter(description = "The group identifier") @PathVariable(name = "id") String name); + Group getGroup(@Parameter(description = "The group identifier") @PathVariable(name = "id") String name); @RequestMapping(path = "/groups/{id}", method = RequestMethod.DELETE) @ResponseStatus(HttpStatus.NO_CONTENT) @Operation(summary = "Delete group", description = "Deletes an existing group", responses = { @ApiResponse(responseCode = "204", description = "No Content"), @ApiResponse(responseCode = "404", description = "Not found", content = @Content(schema = @Schema(implementation = ErrorResponseMessage.class)))}) - Void deleteGroup( - @Parameter(description = "The group identifier") @PathVariable(name = "id") String name); + Void deleteGroup(@Parameter(description = "The group identifier") @PathVariable(name = "id") String name); @RequestMapping(path = "/users/status", method = RequestMethod.POST) @Operation(summary = "Activate or deactivate a RODA users via search query", requestBody = @RequestBody(required = true, content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = ChangeUserStatusRequest.class))), description = "Activates or deactivates RODA users", responses = { @@ -136,6 +132,46 @@ Group createGroup( Void updateGroup( @Parameter(name = "group", required = true, content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)) Group modifiedGroup); + @RequestMapping(path = "/users/{id}/addGroup", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Add group to user", requestBody = @RequestBody(required = true, content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = String.class))), description = "Adds a group to a user", responses = { + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = User.class))), + @ApiResponse(responseCode = "404", description = "Not found", content = @Content(schema = @Schema(implementation = ErrorResponseMessage.class)))}) + User addGroupsToUser(@Parameter(description = "User identifier") @PathVariable(name = "id") String id, + @Parameter(name = "selectedItems", required = true, content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)) SelectedItemsRequest request); + + @RequestMapping(path = "/users/groups/{id}/addMembers", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Add group to user", requestBody = @RequestBody(required = true, content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = String.class))), description = "Adds a group to a user", responses = { + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = Group.class))), + @ApiResponse(responseCode = "404", description = "Not found", content = @Content(schema = @Schema(implementation = ErrorResponseMessage.class)))}) + Group addMembersToGroup(@Parameter(description = "User identifier") @PathVariable(name = "id") String id, + @Parameter(name = "selectedItems", required = true, content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)) SelectedItemsRequest request); + + @RequestMapping(path = "/users/{id}/removeGroup/{groupId}", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Remove group from user", requestBody = @RequestBody(required = true, content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = String.class))), description = "Removes a group from a user", responses = { + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = User.class))), + @ApiResponse(responseCode = "404", description = "Not found", content = @Content(schema = @Schema(implementation = ErrorResponseMessage.class)))}) + User removeGroupsFromUser(@Parameter(description = "User identifier") @PathVariable(name = "id") String id, + @Parameter(description = "Group identifier") @PathVariable(name = "groupId") String groupId); + + @RequestMapping(path = "/users/groups/{id}/removeMember/{userId}", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Remove group from user", requestBody = @RequestBody(required = true, content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = String.class))), description = "Removes a group from a user", responses = { + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = User.class))), + @ApiResponse(responseCode = "404", description = "Not found", content = @Content(schema = @Schema(implementation = ErrorResponseMessage.class)))}) + Group removeMembersFromGroup(@Parameter(description = "User identifier") @PathVariable(name = "id") String id, + @Parameter(description = "User identifier") @PathVariable(name = "userId") String userId); + + @RequestMapping(path = "/users/{id}/groups", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Get user groups", description = "Gets the groups of a user", responses = { + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = Group.class))), + @ApiResponse(responseCode = "404", description = "Not found", content = @Content(schema = @Schema(implementation = ErrorResponseMessage.class)))}) + Set getUserGroups(@Parameter(description = "User identifier") @PathVariable(name = "id") String id); + + @RequestMapping(path = "/users/groups/{id}/members", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + @Operation(summary = "Get group members", description = "Gets members of a group", responses = { + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = User.class))), + @ApiResponse(responseCode = "404", description = "Not found", content = @Content(schema = @Schema(implementation = ErrorResponseMessage.class)))}) + Set getGroupMembers(@Parameter(description = "Group identifier") @PathVariable(name = "id") String id); + @RequestMapping(path = "/users", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Update user", requestBody = @RequestBody(required = true, content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = UpdateUserRequest.class))), description = "Updates a user", responses = { @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = User.class))), @@ -163,14 +199,15 @@ Void resetUserPassword(@Parameter(description = "User identifier") @PathVariable @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StringResponse.class))), @ApiResponse(responseCode = "404", description = "Not found", content = @Content(schema = @Schema(implementation = ErrorResponseMessage.class)))}) StringResponse recoverLogin(@Parameter(description = "User email") @RequestParam(name = "email") String email, - @Parameter(description = "The language to be used for internationalization") @RequestParam(name = "lang", defaultValue = "en", required = false) String localeString, - @Parameter(description = "captcha") @RequestParam(required = false, name = "captcha") String captcha); + @Parameter(description = "The language to be used for internationalization") @RequestParam(name = "lang", defaultValue = "en", required = false) String localeString, + @Parameter(description = "captcha") @RequestParam(required = false, name = "captcha") String captcha); @RequestMapping(path = "/users/{id}/confirm", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Confirm user email", description = "Confirms a user email", responses = { @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = StringResponse.class))), @ApiResponse(responseCode = "400", description = "Bad request", content = @Content(schema = @Schema(implementation = ErrorResponseMessage.class)))}) - StringResponse confirmUserEmail(@Parameter(description = "User identifier") @PathVariable(name = "id") String username, + StringResponse confirmUserEmail( + @Parameter(description = "User identifier") @PathVariable(name = "id") String username, @Parameter(description = "token") @RequestParam(required = false, name = "token") String token); @RequestMapping(path = "/token", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) @@ -189,8 +226,7 @@ AccessToken authenticate( @RequestMapping(path = "/users/{id}/resend-verification", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Resend verification email", description = "Resends verification email", responses = { @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = Notification.class)))}) - Notification sendEmailVerification( - @Parameter(description = "User identifier") @PathVariable(name = "id") String id, + Notification sendEmailVerification(@Parameter(description = "User identifier") @PathVariable(name = "id") String id, @Parameter(description = "The language to be used for internationalization") @RequestParam(name = "lang", defaultValue = "en", required = false) String localeString); @RequestMapping(path = "/users/login", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) @@ -207,8 +243,7 @@ User login( @Operation(summary = "Delete access key", description = "Deletes an access key", responses = { @ApiResponse(responseCode = "204", description = "No Content"), @ApiResponse(responseCode = "404", description = "Not found", content = @Content(schema = @Schema(implementation = ErrorResponseMessage.class)))}) - Void deleteAccessKey( - @Parameter(description = "The access key id") @PathVariable(name = "id") String accessKeyId); + Void deleteAccessKey(@Parameter(description = "The access key id") @PathVariable(name = "id") String accessKeyId); @RequestMapping(path = "/users/{id}/access-keys", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @Operation(summary = "Get user access keys list", description = "Gets a particular user access keys", responses = { diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/common/client/tools/HistoryUtils.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/common/client/tools/HistoryUtils.java index baf0c9d3ce..caf4d1b8bb 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/common/client/tools/HistoryUtils.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/common/client/tools/HistoryUtils.java @@ -45,10 +45,9 @@ import org.roda.wui.client.ingest.process.ShowJobReport; import org.roda.wui.client.ingest.transfer.IngestTransfer; import org.roda.wui.client.ingest.transfer.TransferUpload; -import org.roda.wui.client.management.ShowGroup; import org.roda.wui.client.management.ShowLogEntry; import org.roda.wui.client.management.ShowNotification; -import org.roda.wui.client.management.ShowUser; +import org.roda.wui.client.management.members.ShowMember; import org.roda.wui.client.planning.ShowPreservationAgent; import org.roda.wui.client.planning.ShowRepresentationInformation; import org.roda.wui.client.planning.ShowRisk; @@ -412,7 +411,7 @@ public static void resolve(T object, boolean replace) { path = HistoryUtils.getHistory(jobPath, ShowJobReport.RESOLVER.getHistoryToken(), report.getUUID()); } else if (object instanceof RODAMember) { RODAMember member = (RODAMember) object; - HistoryUtils.newHistory(member.isUser() ? ShowUser.RESOLVER : ShowGroup.RESOLVER, member.getId()); + HistoryUtils.newHistory(ShowMember.RESOLVER, member.getUUID()); } else if (object instanceof IndexedPreservationAgent) { IndexedPreservationAgent agent = (IndexedPreservationAgent) object; HistoryUtils.newHistory(ShowPreservationAgent.RESOLVER, agent.getId()); diff --git a/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages.properties b/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages.properties index 90914d9e91..534245e153 100644 --- a/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages.properties +++ b/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages.properties @@ -234,6 +234,8 @@ someOfAObject[org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation]: someOfAObject[org.roda.core.data.v2.disposal.rule.DisposalRules]:disposal rules someOfAObject[org.roda.core.data.v2.synchronization.central.DistributedInstances]:distributed instances someOfAObject[org.roda.core.data.v2.accessKey.AccessKeys]:access tokens +someOfAObject[org.roda.core.data.v2.user.Group]:groups +someOfAObject[org.roda.core.data.v2.user.User]:users oneOfAObject:{0} oneOfAObject[org.roda.core.data.v2.ip.AIP]:intellectual entity oneOfAObject[org.roda.core.data.v2.ip.IndexedAIP]:intellectual entity @@ -348,6 +350,7 @@ moveSelectedConfirmDialogMessage:Are you sure you want to move the selected {0} # Generic Buttons backButton:Back cancelButton:Cancel +updateButton: Update revertButton:Revert removeButton:Remove refreshButton:Refresh @@ -732,6 +735,10 @@ editUserEmailAlreadyExists:The email address {0} is already used in the records. editGroupNotFound:The group {0} no longer exists. userRemoveConfirmDialogTitle:User and group remove process userRemoveConfirmDialogMessage:Do you want to remove the selected users and groups? +singleUserRemoveConfirmDialogTitle:User remove process +singleGroupRemoveConfirmDialogTitle:Group remove process +singleUserRemoveConfirmDialogMessage:Do you want to remove the user "{0}"? +singleGroupRemoveConfirmDialogMessage:Do you want to remove the group "{0}"? userGroups:Groups addUserButton:Add user addGroupButton:Add group @@ -1708,9 +1715,11 @@ synchronizingStatus[INACTIVE]:Inactive synchronizingStatus[APPLYINGIDENTIFIER]:Applying identifier synchronizingStatus[SYNCHRONIZING]:Synchronizing #Access Token -addAccessKeyButton:New access Token -manageAccessKeyTitle:Manage Access Tokens -createAccessKeyTitle:Create Access Token +addAccessKeyButton:New access token +manageAccessKeyTitle:Manage Access tokens +createAccessKeyTitle:Create Access token +regenerateAccessKeyTitle:Regenerate Access token +accessKeyExpirationDateInThePast:Expiration date must be in the future showAccessKeyTitle:Access Token editAccessKeyTitle:Edit Access Token accessKeyLabel:Access Token @@ -1723,20 +1732,23 @@ accessKeyStatusValue:{0} accessKeyStatusValue[CREATED]:Created accessKeyStatusValue[ACTIVE]:Active accessKeyStatusValue[INACTIVE]:Inactive +accessKeyStatusValue[REVOKED]:Revoked +accessKeyStatusValue[EXPIRED]:Expired accessKeyWarningLabel:To keep your access token secure, we`ll permanently hide it after you close this window. So please be sure to save it somewhere secure. In case you lose your access token, you can regenerate a new one at any time. accessKeyInfo:This token and the instance identifier allow the registration of the local instance. In the local instance go to Administration -> Local instance and fill in the fields with the respective values. -accessKeyEditButton:Edit access Token -accessKeyUpdateButton:Update access Token -accessKeyDeleteButton:Delete access Token -accessKeyRegenerateButton:Regenerate access Token -accessKeyRevokeButton:Revoke access Token +accessKeyEditButton:Edit access token +accessKeyUpdateButton:Update access token +accessKeyDeleteButton:Delete access token +accessKeyRegenerateButton:Regenerate access token +accessKeyRevokeButton:Revoke access token accessKeyNeverUsedLabel:Never used accessKeyNotFoundLabel:Not found -accessKeySuccessfullyRegenerated:Access Token was successfully regenerated -accessKeySuccessfullyRevoked:Access Token was successfully revoked -accessKeyDeleteConfirmationMessage:Are you sure you want to permanently delete this access Token? This action can not be undone once executed. -accessKeyRevokeConfirmationMessage:Are you sure you want to revoke this access Token? -accessKeyRegenerateConfirmationMessage:Are you sure you want to regenerate this access Token? +accessKeySuccessfullyRegenerated:Access token was successfully regenerated +accessKeySuccessfullyDeleted:Access token was successfully deleted +accessKeySuccessfullyRevoked:Access token was successfully revoked +accessKeyDeleteConfirmationMessage:Are you sure you want to permanently delete this access token? This action can not be undone once executed. +accessKeyRevokeConfirmationMessage:Are you sure you want to revoke this access token? +accessKeyRegenerateConfirmationMessage:Are you sure you want to regenerate this access token? #Market marketPluginsActionsTabLabel:Actions marketVersionLabel:Version {0} available in the store @@ -1808,6 +1820,38 @@ ingestIdentifier: Jobs sipIdentifier: Identifiers sipDeleted:Deleted +# RODA Members - Toolbar actions +deactivateUserTitle: Deactivate user +deactivateUserConfirmationMessage: Are you sure you want to deactivate this user? This will prevent the user from logging in and performing any action in the system. +activateUserTitle: Activate user +activateUserConfirmationMessage: Are you sure you want to activate this user? This will allow the user to log in and perform actions in the system. + +# RODA Members +membersTabTitle: Members +addNewGroupModalTitle: Add to group +addToGroupButton: Add to group +addNewMemberToGroupButton: Add a member +addNewMemberToGroupTitle: Add a member to group +addNewMemberAction: Add member +groupSuccessfullyAdded: Group(s) successfully added +memberSuccessfullyAdded: Member(s) successfully added +removeGroupConfirmationTitle: Remove group from user +removeGroupConfirmationMessage: Are you sure you want to remove the group {0} from this user? +groupSuccessfullyRemoved: Group successfully removed +removeMemberConfirmationTitle: Remove member from group +removeMemberConfirmationMessage: Are you sure you want to remove the member {0} from this group? +memberSuccessfullyRemoved: Member successfully removed + + +# RODA Members - Permissions +catalogueAndSearchGroupLabel: Catalogue & search +ingestPreservationActionsInternalActionsGroupLabel: Ingest, preservation actions & internal actions +administrationGroupLabel: Administration +planningGroupLabel: Planning +disposalGroupLabel: Disposal +editPermissionsReadOnlyPermissionsText: Read-only +permissionsUpdateWithSuccess: Permissions successfully updated +editPermissionsModalTitle: Edit permissions # Email viewer emailViewerSubject=Subject diff --git a/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_pt_PT.properties b/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_pt_PT.properties index e24a296784..1b1a887aa4 100644 --- a/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_pt_PT.properties +++ b/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_pt_PT.properties @@ -234,6 +234,8 @@ someOfAObject[org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation]: someOfAObject[org.roda.core.data.v2.disposal.rule.DisposalRules]:condições de eliminação someOfAObject[org.roda.core.data.v2.synchronization.central.DistributedInstances]:instâncias distribuídas someOfAObject[org.roda.core.data.v2.accessKey.AccessKeys]:chaves de acesso +someOfAObject[org.roda.core.data.v2.user.Group]:grupos +someOfAObject[org.roda.core.data.v2.user.User]:utilizadores oneOfAObject:{0} oneOfAObject[org.roda.core.data.v2.ip.AIP]:entidade intelectual oneOfAObject[org.roda.core.data.v2.ip.IndexedAIP]:entidade intelectual @@ -348,6 +350,7 @@ moveSelectedConfirmDialogMessage:Tem a certeza que pretende mover o(s) {0} iten( # Generic Buttons backButton:Voltar cancelButton:Cancelar +updateButton: Atualizar revertButton:Reverter removeButton:Eliminar refreshButton:Atualizar @@ -732,6 +735,10 @@ editUserEmailAlreadyExists:O endereço electrónico {0} já se encontra nos regi editGroupNotFound:O grupo {0} já não existe. userRemoveConfirmDialogTitle:Processo de remoção de utilizadores e grupos userRemoveConfirmDialogMessage:Tem a certeza que pretende eliminar os utilizadores e grupos selecionados? +singleUserRemoveConfirmDialogTitle:Processo de remoção de utilizador +singleGroupRemoveConfirmDialogTitle:Processo de remoção de grupo +singleUserRemoveConfirmDialogMessage:Tem a certeza que pretende eliminar o utilizador {0}? +singleGroupRemoveConfirmDialogMessage:Tem a certeza que pretende eliminar o grupo {0}? userGroups:Grupos addUserButton:Novo utilizador addGroupButton:Novo grupo @@ -1801,3 +1808,35 @@ detailsIngest: Ingestão ingestIdentifier: Processos sipIdentifier: Identificadores sipDeleted: Eliminado + +# RODA Members - Toolbar actions +deactivateUserTitle: Deactivar utilizador +deactivateUserConfirmationMessage: Tem a certeza que quer desativar este utilizador? Isso impedirá que o utilizador faça login e execute ações no sistema. +activateUserTitle: Ativar utilizador +activateUserConfirmationMessage: Tem a certeza que quer ativar este utilizador? Isso permitirá que o utilizador faça login e execute ações no sistema. + +# RODA Members/Groups +membersTabTitle: Membros +addNewGroupModalTitle: Adicionar grupo +addToGroupButton: Adicionar grupo +addNewMemberToGroupButton: Adicionar membro +addNewMemberToGroupTitle: Adicionar membro ao grupo +addNewMemberAction: Adicionar membro +groupSuccessfullyAdded: Grupo adicionado com sucesso +memberSuccessfullyAdded: Membro adicionado com sucesso +removeGroupConfirmationTitle: Remover grupo do utilizador +removeGroupConfirmationMessage: Tem a certeza que quer remover o grupo {0} deste utilizador? +groupSuccessfullyRemoved: Grupo removido com sucesso +removeMemberConfirmationTitle: Remover membro do grupo +removeMemberConfirmationMessage: Tem a certeza que quer remover o membro {0} do grupo? +memberSuccessfullyRemoved: Membro removido com sucesso + +# RODA Members - Permissions +catalogueAndSearchGroupLabel: Catálogo & pesquisa +ingestPreservationActionsInternalActionsGroupLabel: Ingestão, ações de preservação & ações internas +administrationGroupLabel: Administração +planningGroupLabel: Planeamento +disposalGroupLabel: Avaliação e seleção +editPermissionsReadOnlyPermissionsText: Leitura +permissionsUpdateWithSuccess: Permissões atualizadas com sucesso +editPermissionsModalTitle: Editar permissões \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/resources/config/roda-wui.properties b/roda-ui/roda-wui/src/main/resources/config/roda-wui.properties index de7f42b8ad..972891acbc 100644 --- a/roda-ui/roda-wui/src/main/resources/config/roda-wui.properties +++ b/roda-ui/roda-wui/src/main/resources/config/roda-wui.properties @@ -284,14 +284,6 @@ ui.role: log_entry.read ui.role: log_entry.create ui.role: log_entry.delete -ui.role: risk.read -ui.role: risk.manage - -ui.role: ri.read -ui.role: ri.manage - -ui.role: permission.read - ui.role: disposal_rule.read ui.role: disposal_rule.manage ui.role: disposal_schedule.read @@ -306,6 +298,14 @@ ui.role: disposal_confirmation.destroy ui.role: disposal_confirmation.restore ui.role: disposal_confirmation.delete_bin +ui.role: risk.read +ui.role: risk.manage + +ui.role: ri.read +ui.role: ri.manage + +ui.role: permission.read + ui.role: distributed_instances.read ui.role: distributed_instances.manage @@ -907,7 +907,7 @@ ui.lists.IngestAppraisal_searchFiles.search.prefilters.visible = true ui.lists.IngestAppraisal_searchRepresentations.search.prefilters.visible = true ui.lists.IngestTransfer_transferredResources.search.prefilters.visible = true ui.lists.MemberManagement_rodaMembers.search.prefilters.visible = true -ui.lists.MemberSelectDialog_rodaMembers.search.prefilters.visible = true +ui.lists.MemberSelectDialog_rodaMembers.search.prefilters.visible = false ui.lists.NotificationRegister_notifications.search.prefilters.visible = true ui.lists.PreservationEvents_events.search.prefilters.visible = true ui.lists.RepresentationInformationAssociations_RI.search.prefilters.visible = true @@ -2613,11 +2613,11 @@ ui.css.color.button.info=#d9534f ui.css.color.button.info.hover=#c9302c -ui.css.color.label.default=#777 -ui.css.color.label.success=#5cb85c -ui.css.color.label.danger=${ui.css.color.button.danger} -ui.css.color.label.warning=#f0ad4e -ui.css.color.label.info=#3e9ce8 +ui.css.color.label.default=#f5f5f5 +ui.css.color.label.success=#D1FAE5 +ui.css.color.label.danger=#FEE2E2 +ui.css.color.label.warning=#f9f2d2 +ui.css.color.label.info=#d2e7f9 ui.css.color.actions.primary=rgba(0,0,0,0.5) ui.css.color.actions.primary.hover=rgba(0,0,0,0.6) From a88bed0dfdd876e39fe30ccef98ba09d91e26a20 Mon Sep 17 00:00:00 2001 From: Eduardo Teixeira <58005905+eduardojst10@users.noreply.github.com> Date: Tue, 21 Apr 2026 10:24:58 +0100 Subject: [PATCH 2/7] audit logs new design. (#3643) correct DetailsPanelLogEntry location package. --- .../browse/tabs/BrowseLogEntryTabs.java | 21 +++ .../wui/client/browse/tabs/DetailsTab.java | 8 ++ .../roda/wui/client/main/BreadcrumbUtils.java | 19 +++ .../management/DetailsPanelLogEntry.java | 94 ++++++++++++ .../management/DetailsPanelLogEntry.ui.xml | 12 ++ .../wui/client/management/ShowLogEntry.java | 134 ++++-------------- .../wui/client/management/ShowLogEntry.ui.xml | 106 +++----------- 7 files changed, 197 insertions(+), 197 deletions(-) create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseLogEntryTabs.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/DetailsPanelLogEntry.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/DetailsPanelLogEntry.ui.xml diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseLogEntryTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseLogEntryTabs.java new file mode 100644 index 0000000000..3ff7cac8db --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseLogEntryTabs.java @@ -0,0 +1,21 @@ +package org.roda.wui.client.browse.tabs; + +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.user.client.ui.Widget; +import org.roda.core.data.v2.log.LogEntry; + +/** + * + * @author Eduardo Teixeira + */ +public class BrowseLogEntryTabs extends Tabs { + public void init(LogEntry logEntry) { + // Details + createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.detailsTab()), new TabContentBuilder() { + @Override + public Widget buildTabWidget() { + return new DetailsTab(logEntry); + } + }); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java index 734718e613..ac18d8a148 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java @@ -11,8 +11,10 @@ import org.roda.core.data.v2.ip.IndexedFile; import org.roda.core.data.v2.ip.TransferredResource; +import org.roda.core.data.v2.log.LogEntry; import org.roda.wui.client.common.model.BrowseAIPResponse; import org.roda.wui.client.common.model.BrowseRepresentationResponse; +import org.roda.wui.client.management.DetailsPanelLogEntry; import org.roda.wui.client.ingest.transfer.DetailsPanelTransferredResource; import org.roda.wui.client.planning.DetailsPanelAIP; import org.roda.wui.client.planning.DetailsPanelFile; @@ -65,6 +67,12 @@ public DetailsTab(TransferredResource resource) { content.add(detailsPanel); } + public DetailsTab(LogEntry logEntry) { + initWidget(uiBinder.createAndBindUi(this)); + DetailsPanelLogEntry detailsPanel = new DetailsPanelLogEntry(logEntry); + content.add(detailsPanel); + } + interface MyUiBinder extends UiBinder { } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java index d62435b148..6a9cf19647 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java @@ -20,7 +20,10 @@ import org.roda.core.data.v2.ip.IndexedRepresentation; import org.roda.core.data.v2.ip.TransferredResource; import org.roda.core.data.v2.user.RODAMember; +import org.roda.core.data.v2.log.LogEntry; import org.roda.wui.client.browse.BrowseTop; +import org.roda.wui.client.management.ShowLogEntry; +import org.roda.wui.client.management.UserLog; import org.roda.wui.client.browse.PreservationEvents; import org.roda.wui.client.disposal.DisposalDestroyedRecords; import org.roda.wui.client.ingest.appraisal.IngestAppraisal; @@ -30,6 +33,7 @@ import org.roda.wui.common.client.tools.DescriptionLevelUtils; import org.roda.wui.common.client.tools.HistoryUtils; import org.roda.wui.common.client.tools.ListUtils; +import org.roda.wui.common.client.tools.StringUtils; import org.roda.wui.common.client.widgets.Toast; import com.google.gwt.core.client.GWT; @@ -355,6 +359,21 @@ public static List getTransferredResourceBreadcrumbs(Transferred return ret; } + public static List getLogEntryBreadcrumbs(LogEntry logEntry) { + List ret = new ArrayList<>(); + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(messages.activityLogTitle()), + messages.activityLogTitle(), UserLog.RESOLVER.getHistoryPath())); + + if (logEntry != null) { + List path = new ArrayList<>(ShowLogEntry.RESOLVER.getHistoryPath()); + path.add(logEntry.getUUID()); + String label = StringUtils.isNotBlank(logEntry.getId()) ? logEntry.getId() : logEntry.getUUID(); + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromString(label), label, path)); + } + + return ret; + } + public static List getDipBreadcrumbs(IndexedDIP dip, DIPFile dipFile, List dipFileAncestors) { List ret = new ArrayList<>(); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/DetailsPanelLogEntry.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/DetailsPanelLogEntry.java new file mode 100644 index 0000000000..b544364ba1 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/DetailsPanelLogEntry.java @@ -0,0 +1,94 @@ +package org.roda.wui.client.management; + +import org.roda.core.data.v2.log.LogEntry; +import org.roda.wui.client.common.utils.HtmlSnippetUtils; +import org.roda.wui.common.client.tools.Humanize; +import org.roda.wui.common.client.tools.StringUtils; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.InlineHTML; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.Widget; + +import config.i18n.client.ClientMessages; + +/** + * + * @author Eduardo Teixeira + */ +public class DetailsPanelLogEntry extends Composite { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static final MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + + @UiField + FlowPanel details; + + public DetailsPanelLogEntry(LogEntry logEntry) { + initWidget(uiBinder.createAndBindUi(this)); + init(logEntry); + } + + private void init(LogEntry logEntry) { + addIfNotBlank(messages.logEntryIdentifier(), logEntry.getId()); + addIfNotBlank(messages.logEntryReason(), + logEntry.getAuditLogRequestHeaders() != null ? logEntry.getAuditLogRequestHeaders().getReason() : null); + addIfNotBlank(messages.logEntryInstanceId(), logEntry.getInstanceId()); + addIfNotBlank(messages.logEntryComponent(), logEntry.getActionComponent()); + addIfNotBlank(messages.logEntryMethod(), logEntry.getActionMethod()); + addIfNotBlank(messages.logEntryAddress(), logEntry.getAddress()); + + if (logEntry.getDatetime() != null) { + details.add(buildField(messages.logEntryDatetime(), + new InlineHTML(SafeHtmlUtils.htmlEscape(Humanize.formatDateTime(logEntry.getDatetime()))))); + } + + details.add(buildField(messages.logEntryDuration(), + new InlineHTML(SafeHtmlUtils.htmlEscape(Humanize.durationMillisToShortDHMS(logEntry.getDuration()))))); + + addIfNotBlank(messages.logEntryRelatedObject(), logEntry.getRelatedObjectID()); + addIfNotBlank(messages.logEntryUsername(), logEntry.getUsername()); + + if (logEntry.getParameters() != null && !logEntry.getParameters().isEmpty()) { + String paramsInline = logEntry.getParameters().stream().map(p -> messages.logParameter(p.getName(), p.getValue())) + .collect(java.util.stream.Collectors.joining(" | ")); + details.add(buildField(messages.logEntryParameters(), new InlineHTML(SafeHtmlUtils.htmlEscape(paramsInline)))); + } + + if (logEntry.getState() != null) { + details.add(buildField(messages.logEntryState(), + new InlineHTML(HtmlSnippetUtils.getLogEntryStateHtml(logEntry.getState())))); + } + } + + private void addIfNotBlank(String label, String value) { + if (StringUtils.isNotBlank(value)) { + details.add(buildField(label, new InlineHTML(SafeHtmlUtils.htmlEscape(value)))); + } + } + + private FlowPanel buildField(String labelText, InlineHTML html) { + FlowPanel field = new FlowPanel(); + field.setStyleName("field"); + + Label label = new Label(labelText); + label.setStyleName("label"); + + FlowPanel value = new FlowPanel(); + value.setStyleName("value"); + value.add(html); + + field.add(label); + field.add(value); + return field; + } + + interface MyUiBinder extends UiBinder { + Widget createAndBindUi(DetailsPanelLogEntry detailsPanelLogEntry); + } + +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/DetailsPanelLogEntry.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/DetailsPanelLogEntry.ui.xml new file mode 100644 index 0000000000..0e9f8d9e72 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/DetailsPanelLogEntry.ui.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowLogEntry.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowLogEntry.java index bd2ac3a59b..a2b6015967 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowLogEntry.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowLogEntry.java @@ -12,35 +12,35 @@ import java.util.List; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.SimplePanel; import org.roda.core.data.common.RodaConstants; import org.roda.core.data.v2.index.filter.Filter; import org.roda.core.data.v2.index.filter.SimpleFilterParameter; import org.roda.core.data.v2.log.LogEntry; -import org.roda.core.data.v2.log.LogEntryParameter; +import org.roda.wui.client.browse.tabs.BrowseLogEntryTabs; +import org.roda.wui.client.common.ActionsToolbar; +import org.roda.wui.client.common.NavigationToolbar; +import org.roda.wui.client.common.TitlePanel; import org.roda.wui.client.common.UserLogin; import org.roda.wui.client.common.lists.InternalLogEntryList; import org.roda.wui.client.common.lists.utils.AsyncTableCellOptions; import org.roda.wui.client.common.lists.utils.ListBuilder; import org.roda.wui.client.common.search.SearchWrapper; -import org.roda.wui.client.common.utils.HtmlSnippetUtils; +import org.roda.wui.client.main.BreadcrumbUtils; import org.roda.wui.client.services.Services; import org.roda.wui.common.client.HistoryResolver; import org.roda.wui.common.client.tools.HistoryUtils; -import org.roda.wui.common.client.tools.Humanize; import org.roda.wui.common.client.tools.ListUtils; import org.roda.wui.common.client.tools.StringUtils; import com.google.gwt.core.client.GWT; import com.google.gwt.i18n.client.LocaleInfo; -import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.FocusPanel; import com.google.gwt.user.client.ui.Widget; import config.i18n.client.ClientMessages; @@ -90,51 +90,15 @@ public String getHistoryToken() { private static final MyUiBinder uiBinder = GWT.create(MyUiBinder.class); private static final ClientMessages messages = GWT.create(ClientMessages.class); @UiField - Label logIdLabel; - @UiField - Label logIdValue; - @UiField - Label logReasonLabel; - @UiField - Label logReasonValue; - @UiField - Label logComponentLabel; - @UiField - Label logComponentValue; - @UiField - Label logMethodLabel; - @UiField - Label logMethodValue; - @UiField - Label logAddressLabel; - @UiField - Label logAddressValue; - @UiField - Label logDatetimeLabel; - @UiField - Label logDatetimeValue; - @UiField - Label logDurationValue; - @UiField - Label logRelatedObjectLabel; - @UiField - Label logRelatedObjectValue; - @UiField - Label logUsernameLabel; + FocusPanel keyboardFocus; @UiField - Label logUsernameValue; + NavigationToolbar navigationToolbar; @UiField - Label logParametersLabel; + ActionsToolbar actionsToolbar; @UiField - FlowPanel logParametersValue; + TitlePanel title; @UiField - Label logStateLabel; - @UiField - HTML logStateValue; - @UiField - Label logInstanceIdLabel; - @UiField - Label logInstanceIdValue; + BrowseLogEntryTabs browseTab; @UiField SimplePanel expandedAuditLogs; @UiField @@ -145,65 +109,14 @@ public String getHistoryToken() { */ public ShowLogEntry(LogEntry logEntry) { initWidget(uiBinder.createAndBindUi(this)); + navigationToolbar.withoutButtons().build(); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getLogEntryBreadcrumbs(logEntry)); - logIdValue.setText(logEntry.getId()); - logIdLabel.setVisible(StringUtils.isNotBlank(logEntry.getId())); - logIdValue.setVisible(StringUtils.isNotBlank(logEntry.getId())); - - logReasonValue.setText(logEntry.getAuditLogRequestHeaders().getReason()); - logReasonLabel.setVisible(StringUtils.isNotBlank(logEntry.getAuditLogRequestHeaders().getReason())); - logReasonValue.setVisible(StringUtils.isNotBlank(logEntry.getAuditLogRequestHeaders().getReason())); - - logInstanceIdValue.setText(logEntry.getInstanceId()); - logInstanceIdLabel.setVisible(StringUtils.isNotBlank(logEntry.getInstanceId())); - logInstanceIdValue.setVisible(StringUtils.isNotBlank(logEntry.getInstanceId())); - - logComponentValue.setText(logEntry.getActionComponent()); - logComponentLabel.setVisible(StringUtils.isNotBlank(logEntry.getActionComponent())); - logComponentValue.setVisible(StringUtils.isNotBlank(logEntry.getActionComponent())); - - logMethodValue.setText(logEntry.getActionMethod()); - logMethodLabel.setVisible(StringUtils.isNotBlank(logEntry.getActionMethod())); - logMethodValue.setVisible(StringUtils.isNotBlank(logEntry.getActionMethod())); - - logAddressValue.setText(logEntry.getAddress()); - logAddressLabel.setVisible(StringUtils.isNotBlank(logEntry.getAddress())); - logAddressValue.setVisible(StringUtils.isNotBlank(logEntry.getAddress())); - - logDatetimeValue.setText(Humanize.formatDateTime(logEntry.getDatetime())); - logDatetimeLabel.setVisible(logEntry.getDatetime() != null); - logDatetimeValue.setVisible(logEntry.getDatetime() != null); - - logDurationValue.setText(Humanize.durationMillisToShortDHMS(logEntry.getDuration())); - - logRelatedObjectValue.setText(logEntry.getRelatedObjectID()); - logRelatedObjectLabel.setVisible(StringUtils.isNotBlank(logEntry.getRelatedObjectID())); - logRelatedObjectValue.setVisible(StringUtils.isNotBlank(logEntry.getRelatedObjectID())); - - logUsernameValue.setText(logEntry.getUsername()); - logUsernameLabel.setVisible(StringUtils.isNotBlank(logEntry.getUsername())); - logUsernameValue.setVisible(StringUtils.isNotBlank(logEntry.getUsername())); - - List parameters = logEntry.getParameters(); - - if (parameters != null && !parameters.isEmpty()) { - for (LogEntryParameter par : parameters) { - HTML parPanel = new HTML(); - parPanel.setHTML(SafeHtmlUtils.fromString(messages.logParameter(par.getName(), par.getValue()))); - logParametersValue.add(parPanel); - } - logParametersLabel.setVisible(true); - logParametersValue.setVisible(true); - } else { - logParametersLabel.setVisible(false); - logParametersValue.setVisible(false); - } - - logStateValue.setHTML(HtmlSnippetUtils.getLogEntryStateHtml(logEntry.getState())); - logStateLabel.setVisible(logEntry.getState() != null); - logStateValue.setVisible(logEntry.getState() != null); - - expandedAuditLogsList.setVisible(false); + actionsToolbar.setLabel(messages.showLogEntryTitle()); + browseTab.init(logEntry); + title.setText(StringUtils.isNotBlank(logEntry.getId()) ? logEntry.getId() : logEntry.getUUID()); + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse browse-file browse_main_panel"); if (logEntry.getAuditLogRequestHeaders() != null) { Label relatedAuditLogs = new Label(); @@ -212,16 +125,17 @@ public ShowLogEntry(LogEntry logEntry) { expandedAuditLogs.add(relatedAuditLogs); Filter filter = new Filter(new SimpleFilterParameter(RodaConstants.LOG_REQUEST_HEADER_UUID, - logEntry.getAuditLogRequestHeaders().getUuid())); + logEntry.getAuditLogRequestHeaders().getUuid())); ListBuilder auditLogListBuilder = new ListBuilder<>(() -> new InternalLogEntryList(), - new AsyncTableCellOptions<>(LogEntry.class, "AuditLogs_triggeredLogs").withFilter(filter) - .withSummary(messages.listOfAIPs()).bindOpener()); + new AsyncTableCellOptions<>(LogEntry.class, "AuditLogs_triggeredLogs").withFilter(filter) + .withSummary(messages.listOfAIPs()).bindOpener()); SearchWrapper aipsSearchWrapper = new SearchWrapper(false).createListAndSearchPanel(auditLogListBuilder); expandedAuditLogsList.setWidget(aipsSearchWrapper); expandedAuditLogsList.setVisible(true); } + } interface MyUiBinder extends UiBinder { diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowLogEntry.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowLogEntry.ui.xml index 04bba1d8e1..188090c562 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowLogEntry.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowLogEntry.ui.xml @@ -1,97 +1,29 @@ + xmlns:common="urn:import:org.roda.wui.client.common" + xmlns:tabs="urn:import:org.roda.wui.client.browse.tabs"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + + \ No newline at end of file From 938691de66733c18ba5d592572838c6c0700917b Mon Sep 17 00:00:00 2001 From: Eduardo Teixeira <58005905+eduardojst10@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:47:23 +0100 Subject: [PATCH 3/7] notification logs UI update. (#3642) --- .../browse/tabs/BrowseNotificationsTabs.java | 20 ++ .../wui/client/browse/tabs/DetailsTab.java | 8 + .../roda/wui/client/common/resources/main.gss | 12 ++ .../client/common/utils/JavascriptUtils.java | 10 +- .../roda/wui/client/main/BreadcrumbUtils.java | 15 ++ .../management/DetailsPanelNotification.java | 180 ++++++++++++++++++ .../DetailsPanelNotification.ui.xml | 12 ++ .../client/management/ShowNotification.java | 101 ++-------- .../client/management/ShowNotification.ui.xml | 94 ++------- 9 files changed, 288 insertions(+), 164 deletions(-) create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseNotificationsTabs.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/DetailsPanelNotification.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/DetailsPanelNotification.ui.xml diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseNotificationsTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseNotificationsTabs.java new file mode 100644 index 0000000000..a9c9a172a7 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseNotificationsTabs.java @@ -0,0 +1,20 @@ +package org.roda.wui.client.browse.tabs; + +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.user.client.ui.Widget; +import org.roda.core.data.v2.notifications.Notification; + +/** + * + * @author Eduardo Teixeira + */ +public class BrowseNotificationsTabs extends Tabs { + public void init(Notification notification) { + createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.detailsTab()), new TabContentBuilder() { + @Override + public Widget buildTabWidget() { + return new DetailsTab(notification); + } + }); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java index ac18d8a148..c6f11dcb8b 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java @@ -11,9 +11,11 @@ import org.roda.core.data.v2.ip.IndexedFile; import org.roda.core.data.v2.ip.TransferredResource; +import org.roda.core.data.v2.notifications.Notification; import org.roda.core.data.v2.log.LogEntry; import org.roda.wui.client.common.model.BrowseAIPResponse; import org.roda.wui.client.common.model.BrowseRepresentationResponse; +import org.roda.wui.client.management.DetailsPanelNotification; import org.roda.wui.client.management.DetailsPanelLogEntry; import org.roda.wui.client.ingest.transfer.DetailsPanelTransferredResource; import org.roda.wui.client.planning.DetailsPanelAIP; @@ -67,6 +69,12 @@ public DetailsTab(TransferredResource resource) { content.add(detailsPanel); } + public DetailsTab(Notification notification) { + initWidget(uiBinder.createAndBindUi(this)); + DetailsPanelNotification detailsPanel = new DetailsPanelNotification(notification); + content.add(detailsPanel); + } + public DetailsTab(LogEntry logEntry) { initWidget(uiBinder.createAndBindUi(this)); DetailsPanelLogEntry detailsPanel = new DetailsPanelLogEntry(logEntry); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/resources/main.gss b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/resources/main.gss index 2b9bf9f549..5597ef3d8f 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/resources/main.gss +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/resources/main.gss @@ -714,6 +714,18 @@ pre code { border-radius: 4px; } +.notification-body-content pre > code { + overflow: visible; +} + +.notification-body-content pre code { + max-height: none; +} + +.notification-body-content { + overflow: visible; +} + .error { color: #D20707; } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/utils/JavascriptUtils.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/utils/JavascriptUtils.java index 80438d1709..095c1c8486 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/utils/JavascriptUtils.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/utils/JavascriptUtils.java @@ -42,10 +42,14 @@ public static native void runHighlighter() /*-{ }-*/; public static native void runHighlighter(JavaScriptObject parent) /*-{ - $wnd.jQuery(parent).find('pre code').each(function(i, block) { + $wnd.jQuery(parent).find('pre code').each(function(i, block) { + if ($wnd.hljs && $wnd.hljs.highlightElement) { + $wnd.hljs.highlightElement(block); + } else if ($wnd.hljs && $wnd.hljs.highlightBlock) { $wnd.hljs.highlightBlock(block); - }); - }-*/; + } + }); +}-*/; public static native void runHighlighterOn(JavaScriptObject parent) /*-{ $wnd.jQuery(parent).each(function(i, block) { diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java index 6a9cf19647..f70a2386c4 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java @@ -374,6 +374,21 @@ public static List getLogEntryBreadcrumbs(LogEntry logEntry) { return ret; } + public static List getNotificationBreadcrumbs(Notification notification) { + List ret = new ArrayList<>(); + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(messages.notificationsTitle()), + messages.notificationsTitle(), NotificationRegister.RESOLVER.getHistoryPath())); + + if (notification != null) { + List path = new ArrayList<>(ShowNotification.RESOLVER.getHistoryPath()); + path.add(notification.getUUID()); + String label = StringUtils.isNotBlank(notification.getId()) ? notification.getId() : notification.getUUID(); + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromString(label), label, path)); + } + + return ret; + } + public static List getDipBreadcrumbs(IndexedDIP dip, DIPFile dipFile, List dipFileAncestors) { List ret = new ArrayList<>(); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/DetailsPanelNotification.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/DetailsPanelNotification.java new file mode 100644 index 0000000000..9ad6300aa9 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/DetailsPanelNotification.java @@ -0,0 +1,180 @@ +package org.roda.wui.client.management; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.google.gwt.json.client.JSONParser; +import com.google.gwt.user.client.ui.HTML; +import org.roda.core.data.v2.notifications.Notification; +import org.roda.wui.client.common.utils.HtmlSnippetUtils; +import org.roda.wui.client.common.utils.JavascriptUtils; +import org.roda.wui.common.client.tools.Humanize; +import org.roda.wui.common.client.tools.StringUtils; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.InlineHTML; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.Widget; + +import config.i18n.client.ClientMessages; + +/** + * + * @author Eduardo Teixeira + */ +public class DetailsPanelNotification extends Composite { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static final MyUiBinder uiBinder = GWT.create(DetailsPanelNotification.MyUiBinder.class); + + @UiField + FlowPanel details; + + public DetailsPanelNotification(Notification resource) { + initWidget(uiBinder.createAndBindUi(this)); + init(resource); + } + + public void init(Notification n) { + addIfNotBlank(messages.notificationIdentifier(), n.getId()); + addIfNotBlank(messages.notificationSubject(), n.getSubject()); + + if (StringUtils.isNotBlank(n.getBody())) { + details.add(buildBodyField(messages.notificationBody(), n.getBody())); + } + + if (n.getSentOn() != null) { + details.add(buildField(messages.notificationSentOn(), + new InlineHTML(SafeHtmlUtils.htmlEscape(Humanize.formatDateTime(n.getSentOn()))))); + } + + addIfNotBlank(messages.notificationFromUser(), n.getFromUser()); + addIfNotBlank(messages.notificationIsAcknowledged(), + messages.isAcknowledged(Boolean.toString(n.isAcknowledged()).toLowerCase())); + + if (n.getState() != null) { + details.add(buildField(messages.notificationState(), + new InlineHTML(HtmlSnippetUtils.getNotificationStateHTML(n.getState())))); + } + + if (n.getAcknowledgedUsers() != null && !n.getAcknowledgedUsers().isEmpty()) { + FlowPanel ack = new FlowPanel(); + for (Map.Entry e : n.getAcknowledgedUsers().entrySet()) { + ack.add(new InlineHTML(SafeHtmlUtils.htmlEscape(e.getKey() + " " + e.getValue()))); + } + details.add(buildField(messages.notificationAcknowledgedUsers(), ack)); + } + + List remaining = new ArrayList<>(); + if (n.getRecipientUsers() != null) { + remaining.addAll(n.getRecipientUsers()); + } + + if (n.getAcknowledgedUsers() != null) { + remaining.removeAll(n.getAcknowledgedUsers().keySet()); + } + if (!remaining.isEmpty()) { + FlowPanel notAck = new FlowPanel(); + for (String user : remaining) { + notAck.add(new InlineHTML(SafeHtmlUtils.htmlEscape(user))); + } + details.add(buildField(messages.notificationNotAcknowledgedUsers(), notAck)); + } + } + + private void addIfNotBlank(String label, String value) { + if (StringUtils.isNotBlank(value)) { + details.add(buildField(label, new InlineHTML(SafeHtmlUtils.htmlEscape(value)))); + } + } + + private FlowPanel buildField(String label, Widget valueWidget) { + FlowPanel fieldPanel = new FlowPanel(); + fieldPanel.setStyleName("field"); + + Label fieldLabel = new Label(label); + fieldLabel.setStyleName("label"); + + FlowPanel fieldValuePanel = new FlowPanel(); + fieldValuePanel.setStyleName("value"); + fieldValuePanel.add(valueWidget); + + fieldPanel.add(fieldLabel); + fieldPanel.add(fieldValuePanel); + + return fieldPanel; + } + + private FlowPanel buildBodyField(String label, String rawBody) { + FlowPanel bodyPanel = new FlowPanel(); + bodyPanel.setStyleName("field"); + + Label fieldLabel = new Label(label); + fieldLabel.setStyleName("label"); + + FlowPanel fieldValuePanel = new FlowPanel(); + fieldValuePanel.setStyleName("value"); + fieldValuePanel.addStyleName("code-pre"); + fieldValuePanel.addStyleName("notification-body-content"); + + fieldValuePanel.add(buildNotificationBody(rawBody)); + + bodyPanel.add(fieldLabel); + bodyPanel.add(fieldValuePanel); + + return bodyPanel; + } + + private Widget buildNotificationBody(String rawBody) { + String body = rawBody == null ? "" : rawBody.trim(); + + if (body.isEmpty()) { + return new InlineHTML(""); + } + + if (isJson(body)) { + return buildHighlightedCodeBlock(body, "json"); + } + + if (isHtml(body)) { + return buildHighlightedCodeBlock(body, "html"); + } + + return new HTML("
" + SafeHtmlUtils.htmlEscape(body) + "
"); + } + + private boolean isJson(String body) { + try { + JSONParser.parseStrict(body); + return true; + } catch (Exception e) { + return false; + } + } + + private boolean isHtml(String body) { + String s = body == null ? "" : body.trim().toLowerCase(); + return s.contains("" + escaped + ""); + codeHtml.addAttachHandler(event -> { + if (event.isAttached()) { + JavascriptUtils.runHighlighter(codeHtml.getElement()); + } + }); + return codeHtml; + } + + interface MyUiBinder extends UiBinder { + Widget createAndBindUi(DetailsPanelNotification detailsPanelNotification); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/DetailsPanelNotification.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/DetailsPanelNotification.ui.xml new file mode 100644 index 0000000000..3f340b0194 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/DetailsPanelNotification.ui.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowNotification.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowNotification.java index 12a9679b3d..c53249a08e 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowNotification.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowNotification.java @@ -10,31 +10,29 @@ */ package org.roda.wui.client.management; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.roda.core.data.common.RodaConstants; import org.roda.core.data.v2.notifications.Notification; +import org.roda.wui.client.browse.tabs.BrowseNotificationsTabs; +import org.roda.wui.client.common.ActionsToolbar; +import org.roda.wui.client.common.NavigationToolbar; +import org.roda.wui.client.common.TitlePanel; import org.roda.wui.client.common.UserLogin; -import org.roda.wui.client.common.utils.HtmlSnippetUtils; +import org.roda.wui.client.main.BreadcrumbUtils; import org.roda.wui.client.services.Services; import org.roda.wui.common.client.HistoryResolver; import org.roda.wui.common.client.tools.HistoryUtils; -import org.roda.wui.common.client.tools.Humanize; import org.roda.wui.common.client.tools.ListUtils; +import org.roda.wui.common.client.tools.StringUtils; import com.google.gwt.core.client.GWT; -import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; -import com.google.gwt.uibinder.client.UiHandler; import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.FocusPanel; import com.google.gwt.user.client.ui.Widget; import config.i18n.client.ClientMessages; @@ -96,89 +94,30 @@ interface MyUiBinder extends UiBinder { RodaConstants.NOTIFICATION_ACKNOWLEDGED_USERS, RodaConstants.NOTIFICATION_STATE); @UiField - Label notificationId; - + TitlePanel title; @UiField - Label notificationSubject; - + ActionsToolbar actionsToolbar; @UiField - HTML notificationBody; - + NavigationToolbar navigationToolbar; @UiField - Label notificationSentOn; - + FocusPanel keyboardFocus; @UiField - Label notificationFromUser; + BrowseNotificationsTabs browseTab; - @UiField - Label notificationIsAcknowledged; - - @UiField - Label acknowledgedUsersKey; - - @UiField - HTML acknowledgedUsersValue; - - @UiField - Label stateLabel; - - @UiField - HTML stateValue; - - @UiField - Label notAcknowledgedUsersKey; - - @UiField - HTML notAcknowledgedUsersValue; - - @UiField - Button buttonCancel; - - /** - * Create a new panel to view a notification - * - */ public ShowNotification(Notification notification) { initWidget(uiBinder.createAndBindUi(this)); + navigationToolbar.withoutButtons().build(); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getNotificationBreadcrumbs(notification)); - notificationId.setText(notification.getId()); - notificationSubject.setText(notification.getSubject()); - notificationBody.setHTML(notification.getBody()); - notificationSentOn.setText(Humanize.formatDateTime(notification.getSentOn())); - notificationFromUser.setText(notification.getFromUser()); - notificationIsAcknowledged - .setText(messages.isAcknowledged(Boolean.toString(notification.isAcknowledged()).toLowerCase())); - acknowledgedUsersKey.setVisible(false); - notAcknowledgedUsersKey.setVisible(false); - - List recipientUsers = new ArrayList<>(notification.getRecipientUsers()); - - for (String user : notification.getAcknowledgedUsers().keySet()) { - String ackDate = notification.getAcknowledgedUsers().get(user); - acknowledgedUsersKey.setVisible(true); - acknowledgedUsersValue.setHTML(SafeHtmlUtils.fromSafeConstant( - acknowledgedUsersValue.getHTML() + "

" + user + " " + ackDate + "

")); - recipientUsers.remove(user); - } - - for (String user : recipientUsers) { - notAcknowledgedUsersKey.setVisible(true); - notAcknowledgedUsersValue - .setHTML(SafeHtmlUtils.fromSafeConstant(notAcknowledgedUsersValue.getHTML() + "

" + user + "

")); - } + actionsToolbar.setLabel(messages.notificationTitle()); + actionsToolbar.setTagsVisible(false); - stateValue.setHTML(HtmlSnippetUtils.getNotificationStateHTML(notification.getState())); - stateLabel.setVisible(notification.getState() != null); - stateValue.setVisible(notification.getState() != null); - } + title.setText(StringUtils.isNotBlank(notification.getSubject()) ? notification.getSubject() : notification.getId()); - @UiHandler("buttonCancel") - void handleButtonCancel(ClickEvent e) { - cancel(); - } + browseTab.init(notification); - private void cancel() { - HistoryUtils.newHistory(NotificationRegister.RESOLVER); + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse browse-file browse_main_panel"); } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowNotification.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowNotification.ui.xml index 301e8dfab4..04157e14d6 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowNotification.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/ShowNotification.ui.xml @@ -1,89 +1,23 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - - + + \ No newline at end of file From 40d93be51bcbf54e8ffb170567b086306664abc9 Mon Sep 17 00:00:00 2001 From: Eduardo Teixeira <58005905+eduardojst10@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:50:07 +0100 Subject: [PATCH 4/7] update ui for representation information. (#3645) --- .../config/i18n/client/ClientMessages.java | 9 + .../BrowseRepresentationInformationTabs.java | 114 ++++++ .../wui/client/browse/tabs/DetailsTab.java | 9 + ...presentationInformationActionsToolbar.java | 33 ++ .../RepresentationInformationActions.java | 4 +- .../roda/wui/client/common/resources/main.gss | 1 + .../roda/wui/client/main/BreadcrumbUtils.java | 20 + ...DetailsPanelRepresentationInformation.java | 189 ++++++++++ ...tailsPanelRepresentationInformation.ui.xml | 9 + .../ShowRepresentationInformation.java | 347 ++---------------- .../ShowRepresentationInformation.ui.xml | 88 +---- .../i18n/client/ClientMessages.properties | 8 +- 12 files changed, 431 insertions(+), 400 deletions(-) create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseRepresentationInformationTabs.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/BrowseRepresentationInformationActionsToolbar.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/DetailsPanelRepresentationInformation.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/DetailsPanelRepresentationInformation.ui.xml diff --git a/roda-ui/roda-wui/src/main/java/config/i18n/client/ClientMessages.java b/roda-ui/roda-wui/src/main/java/config/i18n/client/ClientMessages.java index 135c05fdf1..52eb703443 100644 --- a/roda-ui/roda-wui/src/main/java/config/i18n/client/ClientMessages.java +++ b/roda-ui/roda-wui/src/main/java/config/i18n/client/ClientMessages.java @@ -1018,6 +1018,10 @@ SafeHtml searchPreFilterLongRangeFilterParameterGreaterThan(String searchPreFilt String detailsCreatedBy(); + String detailsUpdatedOn(); + + String detailsUpdatedBy(); + String detailsModifiedOn(); String detailsModifiedBy(); @@ -1322,10 +1326,15 @@ SafeHtml searchPreFilterLongRangeFilterParameterGreaterThan(String searchPreFilt String representationInformationIntellectualEntities(@PluralCount int size, String link); + String representationInformationIntellectualEntitiesAssociations(); + String representationInformationRepresentations(@PluralCount int size, String link); + String representationInformationRepresentationsAssociations(); + String representationInformationFiles(@PluralCount int size, String link); + String representationInformationFilesAssociations(); /****** Descriptive Metadata ****/ String metadataType(); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseRepresentationInformationTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseRepresentationInformationTabs.java new file mode 100644 index 0000000000..5cb7184b55 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseRepresentationInformationTabs.java @@ -0,0 +1,114 @@ +package org.roda.wui.client.browse.tabs; + +import java.util.ArrayList; +import java.util.List; + +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.utils.RepresentationInformationUtils; +import org.roda.core.data.v2.index.IsIndexed; +import org.roda.core.data.v2.index.filter.Filter; +import org.roda.core.data.v2.index.filter.FilterParameter; +import org.roda.core.data.v2.index.filter.OrFiltersParameters; +import org.roda.core.data.v2.index.filter.SimpleFilterParameter; +import org.roda.core.data.v2.ip.IndexedAIP; +import org.roda.core.data.v2.ip.IndexedFile; +import org.roda.core.data.v2.ip.IndexedRepresentation; +import org.roda.core.data.v2.ri.RepresentationInformation; +import org.roda.wui.client.common.lists.utils.AsyncTableCellOptions; +import org.roda.wui.client.common.lists.utils.ConfigurableAsyncTableCell; +import org.roda.wui.client.common.lists.utils.ListBuilder; +import org.roda.wui.client.common.search.SearchWrapper; + +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.Widget; + +/** + * + * @author Eduardo Teixeira + */ +public class BrowseRepresentationInformationTabs extends Tabs { + public void init(RepresentationInformation ri) { + + List aipParams = new ArrayList<>(); + List representationParams = new ArrayList<>(); + List fileParams = new ArrayList<>(); + initEntityFilters(ri, aipParams, representationParams, fileParams); + + createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.detailsTab()), new TabContentBuilder() { + @Override + public Widget buildTabWidget() { + return new DetailsTab(ri); + } + }); + + createAndAddTab(SafeHtmlUtils.fromString(messages.representationInformationIntellectualEntitiesAssociations()), + () -> buildAssociationsTab(IndexedAIP.class, aipParams, "Search_AIPs")); + + createAndAddTab(SafeHtmlUtils.fromString(messages.representationInformationRepresentationsAssociations()), + () -> buildAssociationsTab(IndexedRepresentation.class, representationParams, "Search_representations")); + + createAndAddTab(SafeHtmlUtils.fromString(messages.representationInformationFilesAssociations()), + () -> buildAssociationsTab(IndexedFile.class, fileParams, "Search_files")); + } + + private void initEntityFilters(RepresentationInformation ri, List aipParams, + List representationParams, List fileParams) { + for (String filter : ri.getFilters()) { + String[] parts = filter.split(RepresentationInformationUtils.REPRESENTATION_INFORMATION_FILTER_SEPARATOR); + if (parts.length < 3) { + continue; + } + + if (RodaConstants.INDEX_AIP.equals(parts[0])) { + aipParams.add(new SimpleFilterParameter(parts[1], parts[2])); + } else if (RodaConstants.INDEX_REPRESENTATION.equals(parts[0])) { + representationParams.add(new SimpleFilterParameter(parts[1], parts[2])); + } else if (RodaConstants.INDEX_FILE.equals(parts[0])) { + fileParams.add(new SimpleFilterParameter(parts[1], parts[2])); + } + } + } + + private Widget buildAssociationsTab(Class clazz, List params, + String listId) { + + if (params == null || params.isEmpty()) { + if (IndexedAIP.class.equals(clazz)) { + return buildEmptyAssociationsCardLikeTab(messages.representationInformationIntellectualEntities(0, "")); + } + if (IndexedRepresentation.class.equals(clazz)) { + return buildEmptyAssociationsCardLikeTab(messages.representationInformationRepresentations(0, "")); + } + return buildEmptyAssociationsCardLikeTab(messages.representationInformationFiles(0, "")); + } + Filter filter = new Filter(new OrFiltersParameters(params)); + + ListBuilder listBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), + new AsyncTableCellOptions<>(clazz, listId).withFilter(filter).withJustActive(false).bindOpener()); + + return new SearchWrapper(false).createListAndSearchPanel(listBuilder); + } + + private Widget buildEmptyAssociationsCardLikeTab(String messageHtml) { + FlowPanel card = new FlowPanel(); + card.addStyleName("roda6CardWithHeader"); + card.addStyleName("wrapper"); + card.addStyleName("skip_padding"); + + FlowPanel body = new FlowPanel(); + body.addStyleName("cardBody"); + + SimplePanel info = new SimplePanel(); + info.addStyleName("table-empty-inner"); + HTML label = new HTML(messageHtml); + label.addStyleName("table-empty-inner-label"); + info.setWidget(label); + + body.add(info); + card.add(body); + return card; + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java index c6f11dcb8b..7bf777e351 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java @@ -11,10 +11,12 @@ import org.roda.core.data.v2.ip.IndexedFile; import org.roda.core.data.v2.ip.TransferredResource; +import org.roda.core.data.v2.ri.RepresentationInformation; import org.roda.core.data.v2.notifications.Notification; import org.roda.core.data.v2.log.LogEntry; import org.roda.wui.client.common.model.BrowseAIPResponse; import org.roda.wui.client.common.model.BrowseRepresentationResponse; +import org.roda.wui.client.planning.DetailsPanelRepresentationInformation; import org.roda.wui.client.management.DetailsPanelNotification; import org.roda.wui.client.management.DetailsPanelLogEntry; import org.roda.wui.client.ingest.transfer.DetailsPanelTransferredResource; @@ -62,6 +64,13 @@ public DetailsTab(IndexedFile file, List riRules) { content.add(detailsPanel); } + public DetailsTab(RepresentationInformation ri){ + initWidget(uiBinder.createAndBindUi(this)); + + DetailsPanelRepresentationInformation detailsPanel = new DetailsPanelRepresentationInformation(ri); + content.add(detailsPanel); + } + public DetailsTab(TransferredResource resource) { initWidget(uiBinder.createAndBindUi(this)); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/BrowseRepresentationInformationActionsToolbar.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/BrowseRepresentationInformationActionsToolbar.java new file mode 100644 index 0000000000..08d48a1839 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/BrowseRepresentationInformationActionsToolbar.java @@ -0,0 +1,33 @@ +package org.roda.wui.client.common; + +import java.util.List; + +import org.roda.core.data.v2.ri.RepresentationInformation; +import org.roda.wui.client.common.actions.RepresentationInformationActions; +import org.roda.wui.client.common.actions.model.ActionableObject; +import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; + +/** + * + * @author Eduardo Teixeira + */ +public class BrowseRepresentationInformationActionsToolbar + extends BrowseObjectActionsToolbar { + public void buildIcon() { + setIcon(null); + } + + public void buildTags() { + // do nothing + } + + public void buildActions() { + this.actions.clear(); + RepresentationInformationActions representationInformationActions; + representationInformationActions = RepresentationInformationActions.get(); + this.actions.add(new ActionableWidgetBuilder(representationInformationActions) + .buildGroupedListWithObjects(new ActionableObject<>(object), List.of(), + List.of(RepresentationInformationActions.RepresentationInformationAction.START_PROCESS))); + } + +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RepresentationInformationActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RepresentationInformationActions.java index 1e791fa7a5..76bd9b67bb 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RepresentationInformationActions.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RepresentationInformationActions.java @@ -45,8 +45,6 @@ import config.i18n.client.ClientMessages; -import javax.naming.Context; - public class RepresentationInformationActions extends AbstractActionable { private static final RepresentationInformationActions INSTANCE = new RepresentationInformationActions(); private static final ClientMessages messages = GWT.create(ClientMessages.class); @@ -328,7 +326,7 @@ public ActionableBundle createActionsBundle() { ActionableBundle formatActionableBundle = new ActionableBundle<>(); // MANAGEMENT - ActionableGroup managementGroup = new ActionableGroup<>(messages.sidebarActionsTitle()); + ActionableGroup managementGroup = new ActionableGroup<>(messages.manage(), "btn-edit"); managementGroup.addButton(messages.newButton(), RepresentationInformationAction.NEW, ActionImpact.UPDATED, "btn-plus-circle"); managementGroup.addButton(messages.createNewRepresentationInformation(), diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/resources/main.gss b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/resources/main.gss index 5597ef3d8f..92ef24eced 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/resources/main.gss +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/resources/main.gss @@ -2581,6 +2581,7 @@ td.datePickerMonth, td.datePickerYear { .main .wui-breadcrumbPanel .breadcrumb-root { color: COLOR_PRIMARY; font-weight: bold; + max-width: none !important; } .main .wui-breadcrumbPanel .breadcrumb-last { diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java index f70a2386c4..ef342d269d 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java @@ -21,6 +21,7 @@ import org.roda.core.data.v2.ip.TransferredResource; import org.roda.core.data.v2.user.RODAMember; import org.roda.core.data.v2.log.LogEntry; +import org.roda.core.data.v2.ri.RepresentationInformation; import org.roda.wui.client.browse.BrowseTop; import org.roda.wui.client.management.ShowLogEntry; import org.roda.wui.client.management.UserLog; @@ -30,6 +31,8 @@ import org.roda.wui.client.ingest.transfer.IngestTransfer; import org.roda.wui.client.management.members.MemberManagement; import org.roda.wui.client.management.members.ShowMember; +import org.roda.wui.client.planning.RepresentationInformationNetwork; +import org.roda.wui.client.planning.ShowRepresentationInformation; import org.roda.wui.common.client.tools.DescriptionLevelUtils; import org.roda.wui.common.client.tools.HistoryUtils; import org.roda.wui.common.client.tools.ListUtils; @@ -389,6 +392,23 @@ public static List getNotificationBreadcrumbs(Notification notif return ret; } + public static List getRepresentationInformationBreadCrumbs(RepresentationInformation ri){ + List ret = new ArrayList<>(); + ret.add(new BreadcrumbItem( + SafeHtmlUtils.fromSafeConstant(messages.representationInformationTitle()), + messages.representationInformationTitle(), + RepresentationInformationNetwork.RESOLVER.getHistoryPath())); + + if (ri != null) { + List path = new ArrayList<>(ShowRepresentationInformation.RESOLVER.getHistoryPath()); + path.add(ri.getUUID()); + String label = ri.getName() != null ? ri.getName() : ri.getId(); + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromString(label), label, path)); + } + + return ret; + } + public static List getDipBreadcrumbs(IndexedDIP dip, DIPFile dipFile, List dipFileAncestors) { List ret = new ArrayList<>(); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/DetailsPanelRepresentationInformation.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/DetailsPanelRepresentationInformation.java new file mode 100644 index 0000000000..7a82e7a941 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/DetailsPanelRepresentationInformation.java @@ -0,0 +1,189 @@ +package org.roda.wui.client.planning; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.v2.ri.RelationObjectType; +import org.roda.core.data.v2.ri.RepresentationInformation; +import org.roda.core.data.v2.ri.RepresentationInformationRelation; +import org.roda.wui.client.common.utils.HtmlSnippetUtils; +import org.roda.wui.client.search.Search; +import org.roda.wui.client.services.Services; +import org.roda.wui.common.client.tools.HistoryUtils; +import org.roda.wui.common.client.tools.Humanize; +import org.roda.wui.common.client.tools.StringUtils; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.i18n.client.LocaleInfo; +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.Anchor; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.InlineHTML; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.Widget; + +import config.i18n.client.ClientMessages; + +/** + * + * @author Eduardo Teixeira + */ +public class DetailsPanelRepresentationInformation extends Composite { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static final MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + + @UiField + FlowPanel details; + + public DetailsPanelRepresentationInformation(RepresentationInformation ri) { + initWidget(uiBinder.createAndBindUi(this)); + init(ri); + } + + private void init(RepresentationInformation ri) { + if (ri.getCreatedOn() != null && StringUtils.isNotBlank(ri.getCreatedBy())) { + addIfNotBlank(messages.detailsCreatedOn(), Humanize.formatDateTime(ri.getCreatedOn())); + addIfNotBlank(messages.detailsCreatedBy(), ri.getCreatedBy()); + } + + if (ri.getUpdatedOn() != null && StringUtils.isNotBlank(ri.getUpdatedBy())) { + addIfNotBlank(messages.detailsUpdatedOn(), Humanize.formatDateTime(ri.getUpdatedOn())); + addIfNotBlank(messages.detailsUpdatedBy(), ri.getUpdatedBy()); + } + addIfNotBlank(messages.representationInformationIdentifier(), ri.getId()); + addIfNotBlank(messages.representationInformationName(), ri.getName()); + addIfNotBlank(messages.representationInformationDescription(), ri.getDescription()); + addIfNotBlank(messages.representationInformationFamily(), ri.getFamilyI18n()); + + if (ri.getSupport() != null) { + addIfNotBlank(messages.representationInformationSupport(), + messages.representationInformationSupportValue(ri.getSupport().toString())); + } + + addTagsField(ri); + addExtrasField(ri); + initRelations(ri); + + } + + private void addIfNotBlank(String label, String value) { + if (StringUtils.isNotBlank(value)) { + details.add(buildField(label, new InlineHTML(SafeHtmlUtils.htmlEscape(value)))); + } + } + + private Widget buildField(String label, Widget valueWidget) { + FlowPanel fieldPanel = new FlowPanel(); + fieldPanel.setStyleName("field"); + + Label fieldLabel = new Label(label); + fieldLabel.setStyleName("label"); + + FlowPanel fieldValuePanel = new FlowPanel(); + fieldValuePanel.setStyleName("value"); + fieldValuePanel.add(valueWidget); + + fieldPanel.add(fieldLabel); + fieldPanel.add(fieldValuePanel); + + return fieldPanel; + } + + private void addTagsField(RepresentationInformation ri) { + List tags = ri.getTags(); + if (tags == null || tags.isEmpty()) + return; + + FlowPanel tagsPanel = new FlowPanel(); + tagsPanel.addStyleName("value"); + for (String category : tags) { + InlineHTML tag = new InlineHTML("" + + messages.representationInformationListItems(SafeHtmlUtils.htmlEscape(category)) + ""); + tag.addClickHandler(event -> { + List history = new ArrayList<>(RepresentationInformationNetwork.RESOLVER.getHistoryPath()); + history.add(Search.RESOLVER.getHistoryToken()); + history.add(RodaConstants.REPRESENTATION_INFORMATION_TAGS); + history.add(category); + HistoryUtils.newHistory(history); + }); + tagsPanel.add(tag); + } + + details.add(buildField(messages.representationInformationTags(), tagsPanel)); + } + + private void addExtrasField(RepresentationInformation ri) { + Services services = new Services("Retrieve representation information family metadata", "get"); + services + .representationInformationResource( + s -> s.retrieveRepresentationInformationFamily(ri.getId(), LocaleInfo.getCurrentLocale().getLocaleName())) + .whenComplete((family, throwable) -> { + if (throwable == null && family != null && family.getFamilyValues() != null + && !family.getFamilyValues().isEmpty()) { + + FlowPanel extrasContent = new FlowPanel(); + extrasContent.addStyleName("ri-extras-panel"); + + HtmlSnippetUtils.createExtraShow(extrasContent, family.getFamilyValues(), false); + + if (extrasContent.getWidgetCount() > 0) { + details.add(extrasContent); + } + } + }); + } + + private void initRelations(RepresentationInformation ri) { + if (ri.getRelations() == null) { + return; + } + + ri.getRelations().sort(Comparator.comparingInt(o -> o.getObjectType().getWeight())); + for (RepresentationInformationRelation relation : ri.getRelations()) { + Widget relationValue = createRelationViewer(relation); + if (relationValue != null) { + relationValue.addStyleName("ri-links-panel"); + details.add(buildField(relation.getRelationTypeI18n(), relationValue)); + } + } + } + + private Widget createRelationViewer(RepresentationInformationRelation relation) { + Widget widgetToAdd = null; + String title = StringUtils.isNotBlank(relation.getTitle()) ? relation.getTitle() : relation.getLink(); + + if (relation.getObjectType().equals(RelationObjectType.TEXT)) { + widgetToAdd = new Label(title); + } else { + Anchor anchor = null; + + if (relation.getObjectType().equals(RelationObjectType.AIP)) { + anchor = new Anchor(title, + HistoryUtils.createHistoryHashLink(HistoryUtils.getHistoryBrowse(relation.getLink()))); + } else if (relation.getObjectType().equals(RelationObjectType.REPRESENTATION_INFORMATION)) { + List history = new ArrayList<>(); + history.addAll(ShowRepresentationInformation.RESOLVER.getHistoryPath()); + history.add(relation.getLink()); + anchor = new Anchor(title, HistoryUtils.createHistoryHashLink(history)); + } else if (relation.getObjectType().equals(RelationObjectType.WEB)) { + anchor = new Anchor(title, relation.getLink()); + anchor.getElement().setAttribute("target", "_blank"); + } + + if (anchor != null) { + widgetToAdd = anchor; + } + } + + return widgetToAdd; + } + + interface MyUiBinder extends UiBinder { + Widget createAndBindUi(DetailsPanelRepresentationInformation representationInformationPanel); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/DetailsPanelRepresentationInformation.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/DetailsPanelRepresentationInformation.ui.xml new file mode 100644 index 0000000000..b875eb3ac9 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/DetailsPanelRepresentationInformation.ui.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRepresentationInformation.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRepresentationInformation.java index 60522efa74..7fd0a89407 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRepresentationInformation.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRepresentationInformation.java @@ -10,58 +10,27 @@ */ package org.roda.wui.client.planning; -import java.util.ArrayList; -import java.util.Comparator; import java.util.List; -import org.roda.core.data.common.RodaConstants; -import org.roda.core.data.utils.RepresentationInformationUtils; -import org.roda.core.data.v2.index.CountRequest; -import org.roda.core.data.v2.index.filter.Filter; -import org.roda.core.data.v2.index.filter.FilterParameter; -import org.roda.core.data.v2.index.filter.OrFiltersParameters; -import org.roda.core.data.v2.index.filter.SimpleFilterParameter; -import org.roda.core.data.v2.ip.IndexedAIP; -import org.roda.core.data.v2.ip.IndexedFile; -import org.roda.core.data.v2.ip.IndexedRepresentation; -import org.roda.core.data.v2.ri.RelationObjectType; import org.roda.core.data.v2.ri.RepresentationInformation; -import org.roda.core.data.v2.ri.RepresentationInformationCreateRequest; -import org.roda.core.data.v2.ri.RepresentationInformationRelation; -import org.roda.wui.client.common.NoAsyncCallback; +import org.roda.wui.client.browse.tabs.BrowseRepresentationInformationTabs; +import org.roda.wui.client.common.BrowseRepresentationInformationActionsToolbar; +import org.roda.wui.client.common.NavigationToolbar; import org.roda.wui.client.common.TitlePanel; import org.roda.wui.client.common.UserLogin; -import org.roda.wui.client.common.actions.Actionable; -import org.roda.wui.client.common.actions.RepresentationInformationActions; -import org.roda.wui.client.common.actions.model.ActionableObject; -import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; -import org.roda.wui.client.common.dialogs.RepresentationInformationDialogs; -import org.roda.wui.client.common.utils.AsyncCallbackUtils; -import org.roda.wui.client.common.utils.HtmlSnippetUtils; -import org.roda.wui.client.common.utils.SidebarUtils; -import org.roda.wui.client.search.Search; +import org.roda.wui.client.main.BreadcrumbUtils; import org.roda.wui.client.services.Services; import org.roda.wui.common.client.HistoryResolver; import org.roda.wui.common.client.tools.HistoryUtils; -import org.roda.wui.common.client.tools.Humanize; import org.roda.wui.common.client.tools.ListUtils; -import org.roda.wui.common.client.tools.StringUtils; import com.google.gwt.core.client.GWT; -import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.i18n.client.LocaleInfo; -import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.InlineHTML; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.FocusPanel; import com.google.gwt.user.client.ui.Widget; import config.i18n.client.ClientMessages; @@ -100,49 +69,21 @@ public String getHistoryToken() { private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); @UiField - Label representationInformationId; + NavigationToolbar navigationToolbar; + @UiField - Label dateCreated; + BrowseRepresentationInformationActionsToolbar actionsToolbar; + @UiField - Label dateUpdated; + FocusPanel keyboardFocus; + @UiField TitlePanel title; + @UiField - Label representationInformationDescriptionKey; - @UiField - HTML representationInformationDescriptionValue; - @UiField - Label representationInformationFamilyKey; - @UiField - Label representationInformationFamilyValue; - @UiField - Label representationInformationTagKey; - @UiField - FlowPanel representationInformationTagValue; - @UiField - Label representationInformationSupportKey; - @UiField - Label representationInformationSupportValue; - @UiField - FlowPanel representationInformationRelationsValue; - @UiField - FlowPanel objectPanel; - @UiField - FlowPanel additionalSeparator; - @UiField - FlowPanel extras; - @UiField - SimplePanel actionsSidebar; - @UiField - FlowPanel contentFlowPanel; - @UiField - FlowPanel sidebarFlowPanel; + BrowseRepresentationInformationTabs browseRepresentationInformationTabs; private RepresentationInformation ri; - private ActionableWidgetBuilder actionableWidgetBuilder; - private List aipParams = new ArrayList<>(); - private List representationParams = new ArrayList<>(); - private List fileParams = new ArrayList<>(); public ShowRepresentationInformation() { this.ri = new RepresentationInformation(); @@ -153,268 +94,26 @@ public ShowRepresentationInformation(final RepresentationInformation ri) { this.ri = ri; initWidget(uiBinder.createAndBindUi(this)); - initEntityFilters(); - objectPanel.addStyleName("ri-entity-relation-section"); - initElements(); - } - public static ShowRepresentationInformation getInstance() { - if (instance == null) { - instance = new ShowRepresentationInformation(); - } - return instance; - } + navigationToolbar.withoutButtons().build(); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getRepresentationInformationBreadCrumbs(ri)); - public void initElements() { title.setText(ri.getName()); - representationInformationId.setText(messages.representationInformationIdentifier() + ": " + ri.getId()); - - if (ri.getCreatedOn() != null && StringUtils.isNotBlank(ri.getCreatedBy())) { - dateCreated.setText(messages.dateCreated(Humanize.formatDateTime(ri.getCreatedOn()), ri.getCreatedBy())); - } - - if (ri.getUpdatedOn() != null && StringUtils.isNotBlank(ri.getUpdatedBy())) { - dateUpdated.setText(messages.dateUpdated(Humanize.formatDateTime(ri.getUpdatedOn()), ri.getUpdatedBy())); - } - - String description = (ri.getDescription() == null) ? "" : ri.getDescription(); - representationInformationDescriptionValue.setHTML(SafeHtmlUtils.fromString(description)); - representationInformationDescriptionKey.setVisible(StringUtils.isNotBlank(ri.getDescription())); - - representationInformationFamilyKey.setVisible(StringUtils.isNotBlank(ri.getFamily())); - representationInformationFamilyValue.setText(ri.getFamilyI18n()); - - List tagsList = ri.getTags(); - representationInformationTagValue.setVisible(tagsList != null && !tagsList.isEmpty()); - representationInformationTagKey.setVisible(tagsList != null && !tagsList.isEmpty()); - - if (tagsList != null) { - for (final String category : tagsList) { - InlineHTML parPanel = new InlineHTML(); - parPanel.setHTML("" - + messages.representationInformationListItems(SafeHtmlUtils.htmlEscape(category)) + ""); - parPanel.addClickHandler(event -> { - List history = new ArrayList<>(RepresentationInformationNetwork.RESOLVER.getHistoryPath()); - history.add(Search.RESOLVER.getHistoryToken()); - history.add(RodaConstants.REPRESENTATION_INFORMATION_TAGS); - history.add(category); - HistoryUtils.newHistory(history); - }); - representationInformationTagValue.add(parPanel); - } - } - - if (ri.getSupport() != null) { - representationInformationSupportValue - .setText(messages.representationInformationSupportValue(ri.getSupport().toString())); - representationInformationSupportKey.setVisible(true); - } else { - representationInformationSupportKey.setVisible(false); - } - - Services services = new Services("Retrieve representation information family metadata", "get"); - services - .representationInformationResource( - s -> s.retrieveRepresentationInformationFamily(ri.getId(), LocaleInfo.getCurrentLocale().getLocaleName())) - .whenComplete((representationInformationFamily, throwable) -> { - if (throwable == null) { - HtmlSnippetUtils.createExtraShow(extras, representationInformationFamily.getFamilyValues(), false); - } - }); + actionsToolbar.setObjectAndBuild(ri, null, null); + browseRepresentationInformationTabs.init(ri); - RepresentationInformationActions representationInformationActions = RepresentationInformationActions.get(); - - actionableWidgetBuilder = new ActionableWidgetBuilder<>(representationInformationActions) - .withActionCallback(new NoAsyncCallback() { - @Override - public void onSuccess(Actionable.ActionImpact result) { - if (result.equals(Actionable.ActionImpact.DESTROYED)) { - HistoryUtils.newHistory(RepresentationInformationNetwork.RESOLVER); - } - } - }); - - SidebarUtils.toggleSidebar(contentFlowPanel, sidebarFlowPanel, representationInformationActions.hasAnyRoles()); - actionsSidebar.setWidget(actionableWidgetBuilder.buildListWithObjects(new ActionableObject<>(ri))); - - initRelations(); + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse browse-file browse_main_panel"); } - public void updateLists() { - aipParams.clear(); - representationParams.clear(); - fileParams.clear(); - - initEntityFilters(); - initRelations(); - } - - private void initEntityFilters() { - for (String filter : ri.getFilters()) { - String[] splittedFilter = filter - .split(RepresentationInformationUtils.REPRESENTATION_INFORMATION_FILTER_SEPARATOR); - - if (splittedFilter[0].equals(RodaConstants.INDEX_AIP)) { - aipParams.add(new SimpleFilterParameter(splittedFilter[1], splittedFilter[2])); - } else if (splittedFilter[0].equals(RodaConstants.INDEX_REPRESENTATION)) { - representationParams.add(new SimpleFilterParameter(splittedFilter[1], splittedFilter[2])); - } else if (splittedFilter[0].equals(RodaConstants.INDEX_FILE)) { - fileParams.add(new SimpleFilterParameter(splittedFilter[1], splittedFilter[2])); - } - } - - if (!aipParams.isEmpty()) { - Filter aipFilter = new Filter(); - aipFilter.add(new OrFiltersParameters(aipParams)); - Services services = new Services("Count AIPs associated with representation information", "count"); - CountRequest countRequest = new CountRequest(aipFilter, true); - services.rodaEntityRestService(s -> s.count(countRequest), IndexedAIP.class).whenComplete((count, - throwable) -> initEntityFiltersObjectPanel(count.getResult(), throwable, IndexedAIP.class.getSimpleName())); - } else if (!representationParams.isEmpty()) { - Filter representationFilter = new Filter(); - representationFilter.add(new OrFiltersParameters(representationParams)); - - Services services = new Services("Count Representations associated with representation information", "count"); - CountRequest countRequest = new CountRequest(representationFilter, true); - services.rodaEntityRestService(s -> s.count(countRequest), IndexedRepresentation.class) - .whenComplete((count, throwable) -> initEntityFiltersObjectPanel(count.getResult(), throwable, - IndexedRepresentation.class.getSimpleName())); - } else if (!fileParams.isEmpty()) { - Filter fileFilter = new Filter(); - fileFilter.add(new OrFiltersParameters(fileParams)); - - Services services = new Services("Count Files associated with representation information", "count"); - CountRequest countRequest = new CountRequest(fileFilter, true); - services.rodaEntityRestService(s -> s.count(countRequest), IndexedFile.class).whenComplete((count, - throwable) -> initEntityFiltersObjectPanel(count.getResult(), throwable, IndexedFile.class.getSimpleName())); - } else { - initEntityFiltersObjectPanel(0L, null, IndexedAIP.class.getSimpleName()); - } - } - - private void initEntityFiltersObjectPanel(final Long count, final Throwable throwable, final String searchType) { - if (throwable != null) { - AsyncCallbackUtils.defaultFailureTreatment(throwable); - } else { - ShowRepresentationInformation.this.objectPanel.clear(); - String url = HistoryUtils.getSearchHistoryByRepresentationInformationFilter( - ShowRepresentationInformation.this.ri.getFilters(), searchType); - - InlineHTML label = new InlineHTML(); - label.addStyleName("ri-form-label-inline"); - if (IndexedAIP.class.getSimpleName().equals(searchType)) { - label.setHTML(messages.representationInformationIntellectualEntities(count.intValue(), url)); - } else if (IndexedRepresentation.class.getSimpleName().equals(searchType)) { - label.setHTML(messages.representationInformationRepresentations(count.intValue(), url)); - } else if (IndexedFile.class.getSimpleName().equals(searchType)) { - label.setHTML(messages.representationInformationFiles(count.intValue(), url)); - } - - ShowRepresentationInformation.this.objectPanel.add(label); - - InlineHTML edit = new InlineHTML(""); - edit.setTitle("Edit association rules"); - edit.addStyleName("ri-category link-color"); - - edit.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - RepresentationInformationDialogs.showPromptDialogRepresentationInformation( - messages.representationInformationEditAssociations(), messages.cancelButton(), messages.confirmButton(), - messages.searchButton(), ShowRepresentationInformation.this.ri, - new AsyncCallback() { - @Override - public void onFailure(Throwable caught) { - // do nothing - } - - @Override - public void onSuccess(RepresentationInformation result) { - // result is ri with updated filters - Services services = new Services("Update representation information", "update"); - RepresentationInformationCreateRequest createRequest = new RepresentationInformationCreateRequest(); - createRequest.setRepresentationInformation(result); - services.representationInformationResource(s -> s.updateRepresentationInformation(createRequest)) - .whenComplete((representationInformation, throwable1) -> { - if (throwable1 == null) { - ShowRepresentationInformation.getInstance().updateLists(); - } - }); - } - }); - } - }); - - ShowRepresentationInformation.this.objectPanel.add(edit); - } - } - - private void initRelations() { - additionalSeparator.setVisible(!ri.getRelations().isEmpty()); - - ri.getRelations().sort(Comparator.comparingInt(o -> o.getObjectType().getWeight())); - - for (RepresentationInformationRelation relation : ri.getRelations()) { - representationInformationRelationsValue.add(createRelationsLayout(relation)); - } - } - - private FlowPanel createRelationsLayout(RepresentationInformationRelation relation) { - FlowPanel panel = new FlowPanel(); - FlowPanel linksPanel = new FlowPanel(); - - Label entryLabel = new Label(relation.getRelationTypeI18n()); - entryLabel.setStyleName("label"); - panel.add(entryLabel); - - Widget w = createRelationViewer(relation); - if (w != null) { - w.addStyleName("ri-links-panel"); - linksPanel.add(w); - } - - panel.add(w); - - return panel; - } - - private Widget createRelationViewer(RepresentationInformationRelation relation) { - Widget widgetToAdd = null; - String title = StringUtils.isNotBlank(relation.getTitle()) ? relation.getTitle() : relation.getLink(); - - if (relation.getObjectType().equals(RelationObjectType.TEXT)) { - widgetToAdd = new Label(title); - } else { - Anchor anchor = null; - - if (relation.getObjectType().equals(RelationObjectType.AIP)) { - anchor = new Anchor(title, - HistoryUtils.createHistoryHashLink(HistoryUtils.getHistoryBrowse(relation.getLink()))); - } else if (relation.getObjectType().equals(RelationObjectType.REPRESENTATION_INFORMATION)) { - List history = new ArrayList<>(); - history.addAll(ShowRepresentationInformation.RESOLVER.getHistoryPath()); - history.add(relation.getLink()); - anchor = new Anchor(title, HistoryUtils.createHistoryHashLink(history)); - } else if (relation.getObjectType().equals(RelationObjectType.WEB)) { - anchor = new Anchor(title, relation.getLink()); - anchor.getElement().setAttribute("target", "_blank"); - } - - if (anchor != null) { - widgetToAdd = anchor; - } + public static ShowRepresentationInformation getInstance() { + if (instance == null) { + instance = new ShowRepresentationInformation(); } - - return widgetToAdd; + return instance; } - // Java method - public native boolean isValidUrl(String url) /*-{ - var pattern = /(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/; - return pattern.test(url); - }-*/; - void resolve(List historyTokens, final AsyncCallback callback) { if (historyTokens.size() == 1) { Services services = new Services("Retrieve representation information", "get"); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRepresentationInformation.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRepresentationInformation.ui.xml index 26591826cf..440ef146e8 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRepresentationInformation.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRepresentationInformation.ui.xml @@ -1,78 +1,22 @@ + xmlns:common="urn:import:org.roda.wui.client.common" + xmlns:tabs="urn:import:org.roda.wui.client.browse.tabs"> - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages.properties b/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages.properties index 534245e153..ceba63c65c 100644 --- a/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages.properties +++ b/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages.properties @@ -874,6 +874,10 @@ representationInformationRepresentations[\=1]:There is one represe representationInformationFiles:There are {0,number} files associated with this representation information representationInformationFiles[\=0]:There are no files associated with this representation information representationInformationFiles[\=1]:There is one file associated with this representation information +representationInformationIntellectualEntitiesAssociations=Intellectual entities associations +representationInformationRepresentationsAssociations=Representation associations +representationInformationFilesAssociations=File associations + # Descriptive Metadata metadataType:Type @@ -1811,7 +1815,9 @@ detailsIdentifier: Identifier detailsLevel: Level detailsType: Type detailsState: State -detailsCreatedOn: Created +detailsCreatedOn: Created on +detailsUpdatedOn: Updated on +detailsUpdatedBy: Updated by detailsCreatedBy: Creator detailsModifiedOn: Modified detailsModifiedBy: Modifier From b13b10b060d01fade1bf354b4a8220677bc0b259 Mon Sep 17 00:00:00 2001 From: Eduardo Teixeira <58005905+eduardojst10@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:58:11 +0100 Subject: [PATCH 5/7] ui updated for risk detailed layout. (#3651) Co-authored-by: Luis Faria --- .../client/browse/tabs/BrowseRiskTabs.java | 46 +++ .../wui/client/browse/tabs/DetailsTab.java | 10 + .../common/BrowseRiskActionsToolbar.java | 39 +++ .../wui/client/common/NavigationToolbar.java | 5 + .../client/common/actions/RiskActions.java | 2 +- .../roda/wui/client/main/BreadcrumbUtils.java | 18 ++ .../wui/client/planning/DetailsPanelRisk.java | 216 +++++++++++++ .../client/planning/DetailsPanelRisk.ui.xml | 10 + .../roda/wui/client/planning/RiskHistory.java | 4 +- .../wui/client/planning/RiskHistory.ui.xml | 2 +- .../wui/client/planning/RiskShowPanel.java | 289 ------------------ .../wui/client/planning/RiskShowPanel.ui.xml | 154 ---------- .../roda/wui/client/planning/ShowRisk.java | 100 +++--- .../roda/wui/client/planning/ShowRisk.ui.xml | 34 +-- 14 files changed, 406 insertions(+), 523 deletions(-) create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseRiskTabs.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/BrowseRiskActionsToolbar.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/DetailsPanelRisk.java create mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/DetailsPanelRisk.ui.xml delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskShowPanel.java delete mode 100644 roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskShowPanel.ui.xml diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseRiskTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseRiskTabs.java new file mode 100644 index 0000000000..4539c6c530 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseRiskTabs.java @@ -0,0 +1,46 @@ +package org.roda.wui.client.browse.tabs; + +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.v2.index.filter.Filter; +import org.roda.core.data.v2.index.filter.SimpleFilterParameter; +import org.roda.core.data.v2.risks.IndexedRisk; +import org.roda.core.data.v2.risks.RiskIncidence; +import org.roda.wui.client.common.lists.RiskIncidenceList; +import org.roda.wui.client.common.lists.utils.AsyncTableCellOptions; +import org.roda.wui.client.common.lists.utils.ListBuilder; +import org.roda.wui.client.common.search.SearchWrapper; + +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.user.client.ui.Widget; + +public class BrowseRiskTabs extends Tabs { + + public void init(IndexedRisk risk) { + createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.detailsTab()), new TabContentBuilder() { + ; + @Override + public Widget buildTabWidget() { + return new DetailsTab(risk); + } + }); + + createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.riskIncidences()), new TabContentBuilder() { + @Override + public Widget buildTabWidget() { + return buildIncidencesSearchWrapper(risk); + } + }); + } + + private Widget buildIncidencesSearchWrapper(IndexedRisk risk) { + String RISK_INCIDENCE_LIST_ID = "RiskShowPanel_riskIncidences"; + Filter filter = new Filter(new SimpleFilterParameter(RodaConstants.RISK_INCIDENCE_RISK_ID, risk.getId())); + + ListBuilder listBuilder = new ListBuilder<>(() -> new RiskIncidenceList(), + new AsyncTableCellOptions<>(RiskIncidence.class, RISK_INCIDENCE_LIST_ID).withSummary(messages.riskIncidences()) + .withFilter(filter).bindOpener().withSearchPlaceholder(messages.riskIncidenceRegisterSearchPlaceHolder())); + + return new SearchWrapper(false).createListAndSearchPanel(listBuilder); + } + +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java index 7bf777e351..74d79ca634 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java @@ -14,6 +14,8 @@ import org.roda.core.data.v2.ri.RepresentationInformation; import org.roda.core.data.v2.notifications.Notification; import org.roda.core.data.v2.log.LogEntry; +import org.roda.core.data.v2.risks.IndexedRisk; +import org.roda.core.data.v2.risks.Risk; import org.roda.wui.client.common.model.BrowseAIPResponse; import org.roda.wui.client.common.model.BrowseRepresentationResponse; import org.roda.wui.client.planning.DetailsPanelRepresentationInformation; @@ -32,6 +34,7 @@ import com.google.gwt.user.client.ui.Widget; import config.i18n.client.ClientMessages; +import org.roda.wui.client.planning.DetailsPanelRisk; /** * @author Carlos Afonso @@ -78,6 +81,13 @@ public DetailsTab(TransferredResource resource) { content.add(detailsPanel); } + public DetailsTab(IndexedRisk risk) { + initWidget(uiBinder.createAndBindUi(this)); + + DetailsPanelRisk detailsPanel = new DetailsPanelRisk(risk, "RiskShowPanel_riskIncidences"); + content.add(detailsPanel); + } + public DetailsTab(Notification notification) { initWidget(uiBinder.createAndBindUi(this)); DetailsPanelNotification detailsPanel = new DetailsPanelNotification(notification); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/BrowseRiskActionsToolbar.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/BrowseRiskActionsToolbar.java new file mode 100644 index 0000000000..86a6717002 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/BrowseRiskActionsToolbar.java @@ -0,0 +1,39 @@ +package org.roda.wui.client.common; + +import java.util.List; + +import org.roda.core.data.v2.risks.IndexedRisk; +import org.roda.wui.client.common.actions.RiskActions; +import org.roda.wui.client.common.actions.model.ActionableObject; +import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; + +/** + * + * @author Eduardo Teixeira + */ +public class BrowseRiskActionsToolbar extends BrowseObjectActionsToolbar { + public void buildIcon() { + setIcon("fa-solid fa-triangle-exclamation"); + } + + public void buildTags() { + // do nothing + } + + public void buildActions() { + this.actions.clear(); + if (object == null) + return; + + RiskActions riskActions = RiskActions.get(); + if (object.hasVersions()) { + riskActions = RiskActions.getWithHistory(); + } + this.actions + .add(new ActionableWidgetBuilder(riskActions).buildGroupedListWithObjects( + new ActionableObject<>(object), List.of(RiskActions.IndexedRiskAction.EDIT, + RiskActions.IndexedRiskAction.REMOVE, RiskActions.IndexedRiskAction.START_PROCESS), + List.of(RiskActions.IndexedRiskAction.START_PROCESS))); + } + +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/NavigationToolbar.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/NavigationToolbar.java index e14596bffc..178c49ae0a 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/NavigationToolbar.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/NavigationToolbar.java @@ -20,6 +20,7 @@ import org.roda.core.data.v2.ip.IndexedRepresentation; import org.roda.core.data.v2.ip.Permissions; import org.roda.core.data.v2.ip.TransferredResource; +import org.roda.core.data.v2.risks.IndexedRisk; import org.roda.wui.client.common.actions.Actionable; import org.roda.wui.client.common.lists.pagination.ListSelectionUtils; import org.roda.wui.client.common.lists.pagination.ListSelectionUtils.ProcessRelativeItem; @@ -233,6 +234,10 @@ public void updateBreadcrumb(TransferredResource transferredResource) { breadcrumb.updatePath(BreadcrumbUtils.getTransferredResourceBreadcrumbs(transferredResource)); } + public void updateBreadcrumb(IndexedRisk risk) { + breadcrumb.updatePath(BreadcrumbUtils.getRiskBreadCrumbs(risk)); + } + public void updateBreadcrumbPath(BreadcrumbItem... items) { updateBreadcrumbPath(Arrays.asList(items)); } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RiskActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RiskActions.java index 909bd9dd22..e262afebc3 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RiskActions.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RiskActions.java @@ -252,7 +252,7 @@ public ActionableBundle createActionsBundle() { ActionableBundle formatActionableBundle = new ActionableBundle<>(); // MANAGEMENT - ActionableGroup managementGroup = new ActionableGroup<>(messages.sidebarActionsTitle()); + ActionableGroup managementGroup = new ActionableGroup<>(messages.manage(), "btn-edit"); managementGroup.addButton(messages.riskHistoryButton(), IndexedRiskAction.HISTORY, ActionImpact.NONE, "btn-history"); managementGroup.addButton(messages.newButton(), IndexedRiskAction.NEW, ActionImpact.UPDATED, "btn-plus-circle"); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java index ef342d269d..b0ff6341ef 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/main/BreadcrumbUtils.java @@ -22,6 +22,7 @@ import org.roda.core.data.v2.user.RODAMember; import org.roda.core.data.v2.log.LogEntry; import org.roda.core.data.v2.ri.RepresentationInformation; +import org.roda.core.data.v2.risks.IndexedRisk; import org.roda.wui.client.browse.BrowseTop; import org.roda.wui.client.management.ShowLogEntry; import org.roda.wui.client.management.UserLog; @@ -29,6 +30,8 @@ import org.roda.wui.client.disposal.DisposalDestroyedRecords; import org.roda.wui.client.ingest.appraisal.IngestAppraisal; import org.roda.wui.client.ingest.transfer.IngestTransfer; +import org.roda.wui.client.planning.RiskRegister; +import org.roda.wui.client.planning.ShowRisk; import org.roda.wui.client.management.members.MemberManagement; import org.roda.wui.client.management.members.ShowMember; import org.roda.wui.client.planning.RepresentationInformationNetwork; @@ -409,6 +412,21 @@ public static List getRepresentationInformationBreadCrumbs(Repre return ret; } + public static List getRiskBreadCrumbs(IndexedRisk risk) { + List ret = new ArrayList<>(); + + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(messages.riskRegisterTitle()), + messages.riskRegisterTitle(), RiskRegister.RESOLVER.getHistoryPath())); + + if (risk != null) { + List path = new ArrayList<>(ShowRisk.RESOLVER.getHistoryPath()); + String label = StringUtils.isNotBlank(risk.getName()) ? risk.getName() : risk.getId(); + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromString(label), label, path)); + } + + return ret; + } + public static List getDipBreadcrumbs(IndexedDIP dip, DIPFile dipFile, List dipFileAncestors) { List ret = new ArrayList<>(); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/DetailsPanelRisk.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/DetailsPanelRisk.java new file mode 100644 index 0000000000..ae49fb7fd5 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/DetailsPanelRisk.java @@ -0,0 +1,216 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE file at the root of the source + * tree and available online at + * + * https://github.com/keeps/roda + */ + +package org.roda.wui.client.planning; + +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.user.client.ui.InlineHTML; +import org.roda.core.data.v2.risks.Risk; +import org.roda.wui.client.common.utils.HtmlSnippetUtils; +import org.roda.wui.client.ingest.transfer.DetailsPanelTransferredResource; +import org.roda.wui.client.services.Services; +import org.roda.wui.common.client.tools.Humanize; +import org.roda.wui.common.client.tools.StringUtils; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.event.logical.shared.HasValueChangeHandlers; +import com.google.gwt.event.logical.shared.ValueChangeEvent; +import com.google.gwt.event.logical.shared.ValueChangeHandler; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.Widget; + +import config.i18n.client.ClientMessages; + +import java.util.List; + +/** + * @author Luis Faria + * + */ +public class DetailsPanelRisk extends Composite implements HasValueChangeHandlers { + + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + + @UiField + FlowPanel detailsPanel; + + + public DetailsPanelRisk() { + initWidget(uiBinder.createAndBindUi(this)); + } + + public DetailsPanelRisk(String listId) { + initWidget(uiBinder.createAndBindUi(this)); + } + + public DetailsPanelRisk(Risk risk, String listId) { + initWidget(uiBinder.createAndBindUi(this)); + init(risk); + } + + public void init(Risk risk) { + // riskId.setText(risk.getId()); + addIfNotBlank(messages.riskIdentifier(), risk.getId()); + addIfNotBlank(messages.riskName(), risk.getName()); + addIfNotBlank(messages.riskDescription(), risk.getDescription()); + addIfNotBlank(messages.riskIdentifiedOn(), Humanize.formatDate(risk.getIdentifiedOn())); + addIfNotBlank(messages.riskIdentifiedBy(), risk.getIdentifiedBy()); + addIfNotBlank(messages.riskNotes(), risk.getNotes()); + + addCategoriesField(risk.getCategories()); + + addMitigationTermsFields(risk); + + // Pre-mitigation notes (termos vêm async abaixo) + addIfNotBlank(messages.riskPreMitigationNotes(), risk.getPreMitigationNotes()); + + // Post-mitigation notes + addIfNotBlank(messages.riskPostMitigationNotes(), risk.getPostMitigationNotes()); + + // Mitigation metadata + addIfNotBlank(messages.riskMitigationStrategy(), risk.getMitigationStrategy()); + addIfNotBlank(messages.riskMitigationOwner(), risk.getMitigationOwner()); + + } + + // public void clear() { + // riskId.setText(""); + // riskName.setText(""); + // riskDescriptionKey.setVisible(false); + // riskDescriptionValue.setText(""); + // riskIdentifiedOn.setText(""); + // riskIdentifiedBy.setText(""); + // riskCategories.clear(); + // riskNotesKey.setVisible(false); + // riskNotesValue.setText(""); + // + // riskPreMitigationProbability.setText(""); + // riskPreMitigationImpact.setText(""); + // riskPreMitigationSeverity.setText(""); + // riskPreMitigationNotesKey.setVisible(false); + // riskPreMitigationNotesValue.setText(""); + // + // riskPosMitigationKey.setVisible(false); + // riskPosMitigationProbabilityKey.setVisible(false); + // riskPosMitigationProbability.setText(""); + // riskPosMitigationImpactKey.setVisible(false); + // riskPosMitigationImpact.setText(""); + // riskPosMitigationSeverityKey.setVisible(false); + // riskPosMitigationSeverity.setText(""); + // riskPosMitigationNotesKey.setVisible(false); + // riskPosMitigationNotesValue.setText(""); + // + // riskMitigationKey.setVisible(false); + // riskMitigationStrategyKey.setVisible(false); + // riskMitigationStrategyValue.setText(""); + // riskMitigationOwnerTypeKey.setVisible(false); + // riskMitigationOwnerTypeValue.setText(""); + // riskMitigationOwnerKey.setVisible(false); + // riskMitigationOwnerValue.setText(""); + // riskMitigationRelatedEventIdentifierTypeKey.setVisible(false); + // riskMitigationRelatedEventIdentifierTypeValue.setText(""); + // riskMitigationRelatedEventIdentifierValueKey.setVisible(false); + // riskMitigationRelatedEventIdentifierValueValue.setText(""); + // } + + public void clear() { + detailsPanel.clear(); + } + + private void addIfNotBlank(String label, String value) { + if (StringUtils.isNotBlank(value)) { + detailsPanel.add(buildField(label, new InlineHTML(SafeHtmlUtils.htmlEscape(value)))); + } + } + + private void addCategoriesField(List categories) { + if (categories == null || categories.isEmpty()) { + return; + } + + FlowPanel categoriesPanel = new FlowPanel(); + categoriesPanel.addStyleName("risk-categories"); + for (String category : categories) { + if (StringUtils.isBlank(category)) { + continue; + } + InlineHTML tag = new InlineHTML( + "" + SafeHtmlUtils.htmlEscape(category) + ""); + categoriesPanel.add(tag); + } + + if (categoriesPanel.getWidgetCount() > 0) { + detailsPanel.add(buildField(messages.riskCategories(), categoriesPanel)); + } + } + + private Widget buildField(String label, Widget valueWidget) { + FlowPanel field = new FlowPanel(); + field.setStyleName("field"); + + Label l = new Label(label); + l.setStyleName("label"); + + FlowPanel value = new FlowPanel(); + value.setStyleName("value"); + value.add(valueWidget); + + field.add(l); + field.add(value); + return field; + } + + @Override + public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) { + return addHandler(handler, ValueChangeEvent.getType()); + } + + private void addMitigationTermsFields(Risk risk) { + final int preSeverity = risk.getPreMitigationSeverity(); + final int posSeverity = risk.getPostMitigationSeverity(); + + Services services = new Services("Retrieve risk mitigation terms", "get"); + services.riskResource(s -> s.retrieveRiskMitigationTerms(risk.getUUID())).whenComplete((terms, throwable) -> { + if (throwable != null || terms == null) { + return; + } + + int severityLowLimit = terms.getSeverityLowLimit(); + int severityHighLimit = terms.getSeverityHighLimit(); + + addIfNotBlank(messages.riskPreMitigationProbability(), + messages.riskMitigationProbability(terms.getPreMitigationProbability().replace(' ', '_'))); + addIfNotBlank(messages.riskPreMitigationImpact(), + messages.riskMitigationImpact(terms.getPreMitigationImpact().replace(' ', '_'))); + + InlineHTML preSeverityHtml = new InlineHTML( + HtmlSnippetUtils.getSeverityDefinition(preSeverity, severityLowLimit, severityHighLimit)); + detailsPanel.add(buildField(messages.riskPreMitigationSeverity(), preSeverityHtml)); + + addIfNotBlank(messages.riskPostMitigationProbability(), + messages.riskMitigationProbability(terms.getPosMitigationProbability().replace(' ', '_'))); + addIfNotBlank(messages.riskPostMitigationImpact(), + messages.riskMitigationImpact(terms.getPosMitigationImpact().replace(' ', '_'))); + + InlineHTML posSeverityHtml = new InlineHTML( + HtmlSnippetUtils.getSeverityDefinition(posSeverity, severityLowLimit, severityHighLimit)); + detailsPanel.add(buildField(messages.riskPostMitigationSeverity(), posSeverityHtml)); + }); + } + + interface MyUiBinder extends UiBinder { + Widget createAndBindUi(DetailsPanelRisk detailsPanelRisk); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/DetailsPanelRisk.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/DetailsPanelRisk.ui.xml new file mode 100644 index 0000000000..395d049b06 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/DetailsPanelRisk.ui.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskHistory.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskHistory.java index 93f8eba2d9..eca46de268 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskHistory.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskHistory.java @@ -90,7 +90,7 @@ public String getHistoryToken() { ListBox list; @UiField(provided = true) - RiskShowPanel oldRisk; + DetailsPanelRisk oldRisk; @UiField Button buttonRevert; @UiField @@ -105,7 +105,7 @@ public RiskHistory(final String riskId, final RiskVersions versions) { this.riskId = riskId; this.riskVersions = versions; - oldRisk = new RiskShowPanel("RiskHistory_riskIncidences"); + oldRisk = new DetailsPanelRisk("RiskHistory_riskIncidences"); initWidget(uiBinder.createAndBindUi(this)); init(); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskHistory.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskHistory.ui.xml index 724cc8b071..ed41e25724 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskHistory.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskHistory.ui.xml @@ -10,7 +10,7 @@ - + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskShowPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskShowPanel.java deleted file mode 100644 index 58c54b9aaf..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskShowPanel.java +++ /dev/null @@ -1,289 +0,0 @@ -/** - * The contents of this file are subject to the license and copyright - * detailed in the LICENSE file at the root of the source - * tree and available online at - * - * https://github.com/keeps/roda - */ - -package org.roda.wui.client.planning; - -import org.roda.core.data.common.RodaConstants; -import org.roda.core.data.v2.index.filter.Filter; -import org.roda.core.data.v2.index.filter.SimpleFilterParameter; -import org.roda.core.data.v2.risks.Risk; -import org.roda.core.data.v2.risks.RiskIncidence; -import org.roda.wui.client.common.actions.RiskIncidenceActions; -import org.roda.wui.client.common.lists.RiskIncidenceList; -import org.roda.wui.client.common.lists.utils.AsyncTableCellOptions; -import org.roda.wui.client.common.lists.utils.ListBuilder; -import org.roda.wui.client.common.search.SearchWrapper; -import org.roda.wui.client.common.utils.HtmlSnippetUtils; -import org.roda.wui.client.services.Services; -import org.roda.wui.common.client.tools.Humanize; -import org.roda.wui.common.client.tools.StringUtils; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.event.logical.shared.HasValueChangeHandlers; -import com.google.gwt.event.logical.shared.ValueChangeEvent; -import com.google.gwt.event.logical.shared.ValueChangeHandler; -import com.google.gwt.event.shared.HandlerRegistration; -import com.google.gwt.uibinder.client.UiBinder; -import com.google.gwt.uibinder.client.UiField; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.Widget; - -import config.i18n.client.ClientMessages; - -/** - * @author Luis Faria - * - */ -public class RiskShowPanel extends Composite implements HasValueChangeHandlers { - - private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - @UiField - Label riskId; - @UiField - Label riskName; - @UiField - Label riskDescriptionKey, riskDescriptionValue; - @UiField - Label riskIdentifiedOn; - @UiField - Label riskIdentifiedBy; - @UiField - FlowPanel riskCategories; - @UiField - Label riskNotesKey, riskNotesValue; - @UiField - Label riskPreMitigationKey; - @UiField - Label riskPreMitigationProbability; - @UiField - Label riskPreMitigationImpact; - @UiField - HTML riskPreMitigationSeverity; - @UiField - Label riskPreMitigationNotesKey, riskPreMitigationNotesValue; - @UiField - Label riskPosMitigationKey; - @UiField - Label riskPosMitigationProbabilityKey; - @UiField - Label riskPosMitigationProbability; - @UiField - Label riskPosMitigationImpactKey; - @UiField - Label riskPosMitigationImpact; - @UiField - Label riskPosMitigationSeverityKey; - @UiField - HTML riskPosMitigationSeverity; - @UiField - Label riskPosMitigationNotesKey, riskPosMitigationNotesValue; - @UiField - Label riskMitigationKey; - @UiField - Label riskMitigationStrategyKey, riskMitigationStrategyValue; - @UiField - Label riskMitigationOwnerTypeKey, riskMitigationOwnerTypeValue; - @UiField - Label riskMitigationOwnerKey, riskMitigationOwnerValue; - @UiField - Label riskMitigationRelatedEventIdentifierTypeKey, riskMitigationRelatedEventIdentifierTypeValue; - @UiField - Label riskMitigationRelatedEventIdentifierValueKey, riskMitigationRelatedEventIdentifierValueValue; - @UiField(provided = true) - SearchWrapper searchWrapper; - - public RiskShowPanel(String listId) { - - ListBuilder riskIncidenceListBuilder = new ListBuilder<>(() -> new RiskIncidenceList(), - new AsyncTableCellOptions<>(RiskIncidence.class, listId).withSummary(messages.riskIncidences()) - .withSearchPlaceholder(messages.riskIncidenceRegisterSearchPlaceHolder()).bindOpener() - .withActionable(RiskIncidenceActions.getForMultipleEdit())); - - searchWrapper = new SearchWrapper(false).createListAndSearchPanel(riskIncidenceListBuilder); - - initWidget(uiBinder.createAndBindUi(this)); - } - - public RiskShowPanel(Risk risk, String listId) { - Filter filter = new Filter(new SimpleFilterParameter(RodaConstants.RISK_INCIDENCE_RISK_ID, risk.getId())); - - ListBuilder riskIncidenceListBuilder = new ListBuilder<>(() -> new RiskIncidenceList(), - new AsyncTableCellOptions<>(RiskIncidence.class, listId).withSummary(messages.riskIncidences()).withFilter(filter) - .bindOpener().withSearchPlaceholder(messages.riskIncidenceRegisterSearchPlaceHolder()) - .withActionable(RiskIncidenceActions.getForMultipleEdit())); - - searchWrapper = new SearchWrapper(false).createListAndSearchPanel(riskIncidenceListBuilder); - - initWidget(uiBinder.createAndBindUi(this)); - init(risk); - } - - public void init(Risk risk) { - riskId.setText(risk.getId()); - riskName.setText(risk.getName()); - - riskDescriptionValue.setText(risk.getDescription()); - riskDescriptionKey.setVisible(StringUtils.isNotBlank(risk.getDescription())); - - riskIdentifiedOn.setText(Humanize.formatDate(risk.getIdentifiedOn())); - riskIdentifiedBy.setText(risk.getIdentifiedBy()); - riskNotesValue.setText(risk.getNotes()); - riskNotesKey.setVisible(StringUtils.isNotBlank(risk.getNotes())); - - for (String category : risk.getCategories()) { - Label categoryLabel = new Label(category); - categoryLabel.addStyleName("value"); - riskCategories.add(categoryLabel); - } - - final int preSeverity = risk.getPreMitigationSeverity(); - final int posSeverity = risk.getPostMitigationSeverity(); - - Services services = new Services("Retrieve risk mitigation terms", "get"); - services.riskResource(s -> s.retrieveRiskMitigationTerms(risk.getUUID())) - .whenComplete((riskMitigationTerms, throwable) -> { - if (throwable == null) { - int severityLowLimit = riskMitigationTerms.getSeverityLowLimit(); - int severityHighLimit = riskMitigationTerms.getSeverityHighLimit(); - - riskPreMitigationProbability.setText( - messages.riskMitigationProbability(riskMitigationTerms.getPreMitigationProbability().replace(' ', '_'))); - riskPreMitigationImpact - .setText(messages.riskMitigationImpact(riskMitigationTerms.getPreMitigationImpact().replace(' ', '_'))); - - riskPreMitigationSeverity - .setHTML(HtmlSnippetUtils.getSeverityDefinition(preSeverity, severityLowLimit, severityHighLimit)); - - riskPosMitigationProbability.setText( - messages.riskMitigationProbability(riskMitigationTerms.getPosMitigationProbability().replace(' ', '_'))); - riskPosMitigationImpact - .setText(messages.riskMitigationImpact(riskMitigationTerms.getPosMitigationImpact().replace(' ', '_'))); - - riskPosMitigationKey.setVisible(true); - riskPosMitigationProbabilityKey.setVisible(true); - riskPosMitigationProbability.setVisible(true); - riskPosMitigationImpactKey.setVisible(true); - riskPosMitigationImpact.setVisible(true); - riskPosMitigationSeverityKey.setVisible(true); - riskPosMitigationSeverity.setVisible(true); - riskPosMitigationSeverity - .setHTML(HtmlSnippetUtils.getSeverityDefinition(posSeverity, severityLowLimit, severityHighLimit)); - } - }); - - riskPreMitigationNotesValue.setText(risk.getPreMitigationNotes()); - riskPreMitigationNotesKey.setVisible(StringUtils.isNotBlank(risk.getPreMitigationNotes())); - - riskPosMitigationNotesValue.setText(risk.getPostMitigationNotes()); - riskPosMitigationNotesKey.setVisible(StringUtils.isNotBlank(risk.getPostMitigationNotes())); - - int mitigationCounter = 0; - - if (StringUtils.isNotBlank(risk.getMitigationStrategy())) { - mitigationCounter++; - riskMitigationStrategyKey.setVisible(true); - riskMitigationStrategyValue.setText(risk.getMitigationStrategy()); - } else { - riskMitigationStrategyKey.setVisible(false); - } - - if (StringUtils.isNotBlank(risk.getMitigationOwnerType())) { - mitigationCounter++; - riskMitigationOwnerTypeKey.setVisible(true); - riskMitigationOwnerTypeValue.setText(risk.getMitigationOwnerType()); - } else { - riskMitigationOwnerTypeKey.setVisible(false); - } - - if (StringUtils.isNotBlank(risk.getMitigationOwner())) { - mitigationCounter++; - riskMitigationOwnerKey.setVisible(true); - riskMitigationOwnerValue.setText(risk.getMitigationOwner()); - } else { - riskMitigationOwnerKey.setVisible(false); - } - - if (StringUtils.isNotBlank(risk.getMitigationRelatedEventIdentifierType())) { - mitigationCounter++; - riskMitigationRelatedEventIdentifierTypeKey.setVisible(true); - riskMitigationRelatedEventIdentifierTypeValue.setText(risk.getMitigationRelatedEventIdentifierType()); - } else { - riskMitigationRelatedEventIdentifierTypeKey.setVisible(false); - } - - if (StringUtils.isNotBlank(risk.getMitigationRelatedEventIdentifierValue())) { - mitigationCounter++; - riskMitigationRelatedEventIdentifierValueKey.setVisible(true); - riskMitigationRelatedEventIdentifierValueValue.setText(risk.getMitigationRelatedEventIdentifierValue()); - } else { - riskMitigationRelatedEventIdentifierValueKey.setVisible(false); - } - - riskMitigationKey.setVisible(mitigationCounter != 0); - - // FIXME it must be visible later - riskMitigationOwnerTypeKey.setVisible(false); - riskMitigationOwnerTypeValue.setVisible(false); - riskMitigationRelatedEventIdentifierTypeKey.setVisible(false); - riskMitigationRelatedEventIdentifierTypeValue.setVisible(false); - riskMitigationRelatedEventIdentifierValueKey.setVisible(false); - riskMitigationRelatedEventIdentifierValueValue.setVisible(false); - } - - public void clear() { - riskId.setText(""); - riskName.setText(""); - riskDescriptionKey.setVisible(false); - riskDescriptionValue.setText(""); - riskIdentifiedOn.setText(""); - riskIdentifiedBy.setText(""); - riskCategories.clear(); - riskNotesKey.setVisible(false); - riskNotesValue.setText(""); - - riskPreMitigationProbability.setText(""); - riskPreMitigationImpact.setText(""); - riskPreMitigationSeverity.setText(""); - riskPreMitigationNotesKey.setVisible(false); - riskPreMitigationNotesValue.setText(""); - - riskPosMitigationKey.setVisible(false); - riskPosMitigationProbabilityKey.setVisible(false); - riskPosMitigationProbability.setText(""); - riskPosMitigationImpactKey.setVisible(false); - riskPosMitigationImpact.setText(""); - riskPosMitigationSeverityKey.setVisible(false); - riskPosMitigationSeverity.setText(""); - riskPosMitigationNotesKey.setVisible(false); - riskPosMitigationNotesValue.setText(""); - - riskMitigationKey.setVisible(false); - riskMitigationStrategyKey.setVisible(false); - riskMitigationStrategyValue.setText(""); - riskMitigationOwnerTypeKey.setVisible(false); - riskMitigationOwnerTypeValue.setText(""); - riskMitigationOwnerKey.setVisible(false); - riskMitigationOwnerValue.setText(""); - riskMitigationRelatedEventIdentifierTypeKey.setVisible(false); - riskMitigationRelatedEventIdentifierTypeValue.setText(""); - riskMitigationRelatedEventIdentifierValueKey.setVisible(false); - riskMitigationRelatedEventIdentifierValueValue.setText(""); - } - - @Override - public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) { - return addHandler(handler, ValueChangeEvent.getType()); - } - - interface MyUiBinder extends UiBinder { - } -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskShowPanel.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskShowPanel.ui.xml deleted file mode 100644 index 1e4cdfb682..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/RiskShowPanel.ui.xml +++ /dev/null @@ -1,154 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRisk.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRisk.java index c8e59cdcf9..1900726da0 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRisk.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRisk.java @@ -13,17 +13,19 @@ import java.util.List; import org.roda.core.data.v2.risks.IndexedRisk; +import org.roda.wui.client.browse.tabs.BrowseRiskTabs; +import org.roda.wui.client.common.BrowseRiskActionsToolbar; +import org.roda.wui.client.common.NavigationToolbar; import org.roda.wui.client.common.NoAsyncCallback; +import org.roda.wui.client.common.TitlePanel; import org.roda.wui.client.common.UserLogin; import org.roda.wui.client.common.actions.Actionable; -import org.roda.wui.client.common.actions.RiskActions; -import org.roda.wui.client.common.actions.model.ActionableObject; -import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; -import org.roda.wui.client.common.utils.SidebarUtils; +import org.roda.wui.client.main.BreadcrumbUtils; import org.roda.wui.client.services.Services; import org.roda.wui.common.client.HistoryResolver; import org.roda.wui.common.client.tools.HistoryUtils; import org.roda.wui.common.client.tools.ListUtils; +import org.roda.wui.common.client.tools.StringUtils; import com.google.gwt.core.client.GWT; import com.google.gwt.i18n.client.LocaleInfo; @@ -31,8 +33,7 @@ import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.FocusPanel; import com.google.gwt.user.client.ui.Widget; import config.i18n.client.ClientMessages; @@ -47,7 +48,7 @@ public class ShowRisk extends Composite { @Override public void resolve(List historyTokens, final AsyncCallback callback) { - getInstance().resolve(historyTokens, callback); + resolveShowRisk(historyTokens, callback); } @Override @@ -66,14 +67,7 @@ public String getHistoryToken() { } }; - private static ShowRisk instance = null; private static final ClientMessages messages = GWT.create(ClientMessages.class); - - interface MyUiBinder extends UiBinder { - } - - private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - private static final AsyncCallback actionCallback = new NoAsyncCallback() { @Override public void onSuccess(Actionable.ActionImpact result) { @@ -82,69 +76,61 @@ public void onSuccess(Actionable.ActionImpact result) { } } }; - - @UiField(provided = true) - RiskShowPanel riskShowPanel; - + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); @UiField - SimplePanel actionsSidebar; + FocusPanel keyboardFocus; @UiField - FlowPanel contentFlowPanel; + NavigationToolbar navigationToolbar; @UiField - FlowPanel sidebarFlowPanel; + TitlePanel title; - /** - * Create a new panel to view a risk - */ - public ShowRisk() { - this.riskShowPanel = new RiskShowPanel("RiskShowPanel_riskIncidences"); - initWidget(uiBinder.createAndBindUi(this)); - } + @UiField(provided = true) + BrowseRiskTabs riskTabs; + + @UiField + BrowseRiskActionsToolbar actionsToolbar; public ShowRisk(IndexedRisk risk) { - this.riskShowPanel = new RiskShowPanel(risk, "RiskShowPanel_riskIncidences"); + riskTabs = new BrowseRiskTabs(); + riskTabs.init(risk); initWidget(uiBinder.createAndBindUi(this)); - // actionsSidebar is set in the resolve callback + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse browse-file browse_main_panel"); + + navigationToolbar.withoutButtons().build(); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getRiskBreadCrumbs(risk)); + actionsToolbar.setLabel(messages.showRiskTitle()); + actionsToolbar.setObjectAndBuild(risk, null, actionCallback); + title.setText(StringUtils.isNotBlank(risk.getName()) ? risk.getName() : risk.getId()); } - public static ShowRisk getInstance() { - if (instance == null) { - instance = new ShowRisk(); - } - return instance; - } + private static void resolveShowRisk(List historyTokens, final AsyncCallback callback) { - void resolve(List historyTokens, final AsyncCallback callback) { + if (historyTokens.size() != 1) { + HistoryUtils.newHistory(RiskRegister.RESOLVER); + callback.onSuccess(null); + return; + } - if (historyTokens.size() == 1) { - Services services = new Services("Retrieve indexed risk", "get"); - SidebarUtils.showSidebar(contentFlowPanel, sidebarFlowPanel); + Services services = new Services("Retrieve indexed risk", "get"); + String riskUUID = historyTokens.get(0); - services - .rodaEntityRestService(s -> s.findByUuid(historyTokens.get(0), LocaleInfo.getCurrentLocale().getLocaleName()), - IndexedRisk.class) - .thenCompose(indexedRisk -> services.riskResource(s -> s.retrieveRiskVersions(historyTokens.get(0))) + services + .rodaEntityRestService(s -> s.findByUuid(riskUUID, LocaleInfo.getCurrentLocale().getLocaleName()), + IndexedRisk.class) + .thenCompose(indexedRisk -> services.riskResource(s -> s.retrieveRiskVersions(riskUUID)) .whenComplete((result, throwable) -> { if (throwable == null) { indexedRisk.setHasVersions(!result.getVersions().isEmpty()); - instance = new ShowRisk(indexedRisk); - RiskActions riskActions = RiskActions.get(); - if (indexedRisk.hasVersions()) { - riskActions = RiskActions.getWithHistory(); - } - SidebarUtils.toggleSidebar(contentFlowPanel, sidebarFlowPanel, riskActions.hasAnyRoles()); - instance.actionsSidebar.setWidget(new ActionableWidgetBuilder<>(riskActions).withBackButton() - .withActionCallback(actionCallback).buildListWithObjects(new ActionableObject<>(indexedRisk))); - callback.onSuccess(instance); + callback.onSuccess(new ShowRisk(indexedRisk)); } else { callback.onFailure(throwable); } })); - } else { - HistoryUtils.newHistory(RiskRegister.RESOLVER); - callback.onSuccess(null); - } + } + + interface MyUiBinder extends UiBinder { } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRisk.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRisk.ui.xml index 1347a69ca3..afc42b4b54 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRisk.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/planning/ShowRisk.ui.xml @@ -1,24 +1,20 @@ + xmlns:tab="urn:import:org.roda.wui.client.browse.tabs" xmlns:common="urn:import:org.roda.wui.client.common" +> - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + From e6d3e678e3678069d6dd2f9f6433275be9137355 Mon Sep 17 00:00:00 2001 From: Luis Faria Date: Mon, 27 Apr 2026 16:26:08 +0100 Subject: [PATCH 6/7] Add .claude/ to .gitignore Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5b807a3430..a166ccbfba 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ roda-installer/roda-installer_v* **/.DS_Store roda-core/roda-core/velocity.log.1 .idea/ +.claude/ *.iml independent_plugin_script.sh compile_plugin.sh From 2f744c5b85597e0294ad85e02909888d8eba6ad3 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Mon, 27 Apr 2026 15:27:31 +0000 Subject: [PATCH 7/7] fix: pom.xml to reduce vulnerabilities The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHEHTTPCOMPONENTSCLIENT5-16134546 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORKBOOT-16191022 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORKBOOT-16191381 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORKBOOT-16191649 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORKBOOT-16198880 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORKBOOT-16200231 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORKBOOT-16201011 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 872ca686ba..a8430095c2 100644 --- a/pom.xml +++ b/pom.xml @@ -120,7 +120,7 @@ 3.2.6 https://roda-community.org all - 3.5.12 + 3.5.14 provided 1.5.25 5.18.0 @@ -839,7 +839,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.6 + 5.6.1 org.apache.httpcomponents.core5