diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 2597910d24..d36a5e6bca 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -44,7 +44,7 @@ jobs: mkdir -p /tmp/roda-jar cp roda-ui/roda-wui/target/roda-wui-*.jar /tmp/roda-jar/ - name: Upload JAR artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: roda-jar path: /tmp/roda-jar/ @@ -66,7 +66,7 @@ jobs: - name: Prepare platform tag run: echo "platform_tag=$(echo "${{ matrix.platform }}" | tr '/' '-')" >> $GITHUB_ENV - name: Download JAR artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: roda-jar path: docker/target @@ -99,7 +99,7 @@ jobs: digest="${{ steps.build.outputs.digest }}" touch "/tmp/digests/${digest#sha256:}" - name: Upload digest - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: digests-${{ env.platform_tag }} path: /tmp/digests/* @@ -111,7 +111,7 @@ jobs: needs: docker steps: - name: Download digests - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: path: /tmp/digests pattern: digests-* diff --git a/.github/workflows/jekyll-gh-pages.yml b/.github/workflows/jekyll-gh-pages.yml index 142f2dc852..c4a431b9de 100644 --- a/.github/workflows/jekyll-gh-pages.yml +++ b/.github/workflows/jekyll-gh-pages.yml @@ -36,7 +36,7 @@ jobs: source: ./ destination: ./_site - name: Upload artifact - uses: actions/upload-pages-artifact@v4 + uses: actions/upload-pages-artifact@v5 # Deployment job deploy: diff --git a/.github/workflows/latest.yml b/.github/workflows/latest.yml index b2303d3eeb..bb2fe40f0d 100644 --- a/.github/workflows/latest.yml +++ b/.github/workflows/latest.yml @@ -43,7 +43,7 @@ jobs: mkdir -p /tmp/roda-jar cp roda-ui/roda-wui/target/roda-wui-*.jar /tmp/roda-jar/ - name: Upload JAR artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: roda-jar path: /tmp/roda-jar/ @@ -65,7 +65,7 @@ jobs: - name: Prepare platform tag run: echo "platform_tag=$(echo "${{ matrix.platform }}" | tr '/' '-')" >> $GITHUB_ENV - name: Download JAR artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: roda-jar path: docker/target @@ -98,7 +98,7 @@ jobs: digest="${{ steps.build.outputs.digest }}" touch "/tmp/digests/${digest#sha256:}" - name: Upload digest - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: digests-${{ env.platform_tag }} path: /tmp/digests/* @@ -110,7 +110,7 @@ jobs: needs: docker steps: - name: Download digests - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: path: /tmp/digests pattern: digests-* diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8973bb79f2..aa8a8fa012 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -51,7 +51,7 @@ jobs: mkdir -p /tmp/roda-jar cp roda-ui/roda-wui/target/roda-wui-${{ steps.get_version.outputs.release_version }}.jar /tmp/roda-jar/ - name: Upload JAR artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: roda-jar path: /tmp/roda-jar/ @@ -73,7 +73,7 @@ jobs: - name: Prepare platform tag run: echo "platform_tag=$(echo "${{ matrix.platform }}" | tr '/' '-')" >> $GITHUB_ENV - name: Download JAR artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: roda-jar path: docker/target @@ -106,7 +106,7 @@ jobs: digest="${{ steps.build.outputs.digest }}" touch "/tmp/digests/${digest#sha256:}" - name: Upload digest - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: digests-${{ env.platform_tag }} path: /tmp/digests/* @@ -118,7 +118,7 @@ jobs: needs: [build, docker] steps: - name: Download digests - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: path: /tmp/digests pattern: digests-* @@ -164,7 +164,7 @@ jobs: - name: Inspect image run: docker buildx imagetools inspect ghcr.io/keeps/roda:${{ steps.meta.outputs.version }} - name: Download JAR artifact for release - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: roda-jar - name: Upload artifact to release diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index a31e573f18..039d35403b 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -40,7 +40,7 @@ jobs: mkdir -p /tmp/roda-jar cp roda-ui/roda-wui/target/roda-wui-*.jar /tmp/roda-jar/ - name: Upload JAR artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: roda-jar path: /tmp/roda-jar/ @@ -62,7 +62,7 @@ jobs: - name: Prepare platform tag run: echo "platform_tag=$(echo "${{ matrix.platform }}" | tr '/' '-')" >> $GITHUB_ENV - name: Download JAR artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: roda-jar path: docker/target @@ -95,7 +95,7 @@ jobs: digest="${{ steps.build.outputs.digest }}" touch "/tmp/digests/${digest#sha256:}" - name: Upload digest - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: digests-${{ env.platform_tag }} path: /tmp/digests/* @@ -107,7 +107,7 @@ jobs: needs: docker steps: - name: Download digests - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: path: /tmp/digests pattern: digests-* 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 diff --git a/deploys/standalone/docker-compose-e2e.yaml b/deploys/standalone/docker-compose-e2e.yaml new file mode 100644 index 0000000000..cbf0fa8d72 --- /dev/null +++ b/deploys/standalone/docker-compose-e2e.yaml @@ -0,0 +1,109 @@ +services: + + zoo: + image: docker.io/zookeeper:3.9-jre-17 + restart: unless-stopped + environment: + - ZOO_4LW_COMMANDS_WHITELIST=mntr,conf,ruok + volumes: + - zookeeper_data:/data + - zookeeper_datalog:/datalog + + solr: + image: docker.io/solr:9.8.1 + restart: unless-stopped + ports: + - "8983:8983" + environment: + SOLR_HEAP: 2g + ZK_HOST: zoo:2181 + command: -c + volumes: + - solr_data:/var/solr + + clamd: + image: docker.io/clamav/clamav:1.4 + restart: unless-stopped + volumes: + - clam_data:/var/lib/clamav + - roda_data:/roda/data/ + + siegfried: + image: ghcr.io/keeps/siegfried:v1.11.0 + restart: unless-stopped + environment: + SIEGFRIED_HOST: 0.0.0.0 + SIEGFRIED_PORT: 5138 + volumes: + - siegfried_data:/root/siegfried/ + - roda_data:/roda/data/ + + mailpit: + image: axllent/mailpit:v1.24 + restart: unless-stopped + ports: + - "1025:1025" + - "8025:8025" + + openldap: + image: docker.io/bitnamilegacy/openldap:2.6 + restart: unless-stopped + user: 1001:root + ports: + - "1389:1389" + - "1636:1636" + environment: + - BITNAMI_DEBUG=true + - LDAP_ROOT=dc=roda,dc=org + - LDAP_SKIP_DEFAULT_TREE=yes + - LDAP_ADMIN_USERNAME=admin + - LDAP_ADMIN_PASSWORD=roda + - LDAP_EXTRA_SCHEMAS=cosine,inetorgperson,nis,pbkdf2 + volumes: + - ./ldap/ldif/pbkdf2.ldif:/opt/bitnami/openldap/etc/schema/pbkdf2.ldif + + roda: + image: docker.io/keeps/roda:development + restart: unless-stopped + ports: + - "8080:8080" + depends_on: + - solr + - clamd + - siegfried + - postgres + volumes: + - roda_data:/roda/data/ + - ./roda/config/roda-core.properties:/roda/config/roda-core.properties:ro + environment: + - SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/roda_core_db + - RODA_CORE_SOLR_TYPE=CLOUD + - RODA_CORE_SOLR_CLOUD_URLS=zoo:2181 + - SOLR_NUM_SHARDS=4 + - SOLR_REPLICATION_FACTOR=1 + - SIEGFRIED_SERVER_URL=http://siegfried:5138 + - CLAMD_TCPADDR=clamd + - CLAMD_TCPSOCKET=3310 + - SMTP_HOST=mailpit + - SMTP_PORT=1025 + + postgres: + image: docker.io/postgres:17 + restart: unless-stopped + environment: + POSTGRES_USER: admin + POSTGRES_PASSWORD: roda + POSTGRES_DB: roda_core_db + ports: + - "5432:5432" + volumes: + - pg_data:/var/lib/postgresql/data + +volumes: + zookeeper_data: + zookeeper_datalog: + solr_data: + clam_data: + siegfried_data: + roda_data: + pg_data: diff --git a/pom.xml b/pom.xml index 78c02b3848..2fd340859b 100644 --- a/pom.xml +++ b/pom.xml @@ -112,7 +112,7 @@ 4.1.0 2.2.41 2.20.1 - 6.2.17 + 6.2.18 9.10.0 1.4.0 5.5 @@ -200,6 +200,14 @@ roda-core/roda-core-tests + + + e2e + + roda-api-tests + + diff --git a/roda-api-tests/pom.xml b/roda-api-tests/pom.xml new file mode 100644 index 0000000000..6d30f0a1ad --- /dev/null +++ b/roda-api-tests/pom.xml @@ -0,0 +1,176 @@ + + + 4.0.0 + + + org.roda-community + roda + 6.3.0-SNAPSHOT + ../pom.xml + + + roda-api-tests + RODA - API End-to-End Tests + jar + + + Black-box, out-of-process E2E API tests for RODA. + Uses Testcontainers (ComposeContainer) to spin up the full stack + from deploys/standalone/docker-compose-e2e.yaml, then exercises the REST API + via REST Assured against generated OpenAPI model POJOs. + + Requires: + - Docker with Compose V2 plugin (docker compose) on PATH + - docker.io/keeps/roda:development image available locally or pullable + - No port conflicts on the host for ports mapped by docker-compose-e2e.yaml + + Run with: + mvn test -Pe2e + + + + 2.0.2 + 5.5.2 + 7.10.0 + + + + + + + org.testng + testng + test + + + + + org.testcontainers + testcontainers + ${testcontainers.version} + test + + + + + io.rest-assured + rest-assured + ${restassured.version} + test + + + + + com.fasterxml.jackson.core + jackson-databind + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + jakarta.annotation + jakarta.annotation-api + + + + + org.slf4j + slf4j-api + + + ch.qos.logback + logback-classic + test + + + + + + + + + + org.openapitools + openapi-generator-maven-plugin + ${openapi.generator.version} + + + generate-api-models + + generate + + + ${project.basedir}/../openapi.json + java + ${project.build.directory}/generated-sources/openapi + org.roda.tests.api.model + org.roda.tests.api.client + org.roda.tests.api.invoker + + + + + false + + false + false + false + false + + + + native + java8 + jackson + + false + + false + false + + true + + false + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + src/test/resources/testng.xml + + + 720 + + + + + + + diff --git a/roda-api-tests/src/test/java/org/roda/tests/api/AbstractApiTest.java b/roda-api-tests/src/test/java/org/roda/tests/api/AbstractApiTest.java new file mode 100644 index 0000000000..5463b28c83 --- /dev/null +++ b/roda-api-tests/src/test/java/org/roda/tests/api/AbstractApiTest.java @@ -0,0 +1,154 @@ +/** + * 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.tests.api; + +import static io.restassured.RestAssured.given; +import static io.restassured.RestAssured.preemptive; + +import java.io.File; +import java.time.Duration; + +import io.restassured.builder.RequestSpecBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.ComposeContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testng.annotations.AfterSuite; +import org.testng.annotations.BeforeSuite; + +import io.restassured.RestAssured; + +/** + * Base class for all RODA API E2E tests. + * + *

Lifecycle: a single {@link ComposeContainer} is started once before the + * entire TestNG suite and stopped after it. All concrete test classes inherit the + * pre-configured {@link RestAssured} base URI/port/auth, so individual tests need + * only declare {@code given()...when()...then()} chains. + * + *

Prerequisites: + *

+ */ +public abstract class AbstractApiTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractApiTest.class); + + protected static final String ADMIN_USER = "admin"; + protected static final String ADMIN_PASSWORD = "roda"; + + private static final String RODA_SERVICE = "roda"; + private static final int RODA_PORT = 8080; + + /** + * Path to the compose file relative to this Maven module's base directory. + * Maven Surefire sets {@code user.dir} to {@code ${project.basedir}} at test time, + * so the relative path resolves correctly regardless of where Maven is invoked from. + */ + private static final File COMPOSE_FILE = + new File("../deploys/standalone/docker-compose-e2e.yaml"); + + // Declared non-final so construction is deferred to @BeforeSuite. + // Initialising ComposeContainer in a static field triggers Testcontainers' + // Docker-detection code at class-load time (during TestNG's discovery phase), + // before any lifecycle method runs, which causes a cryptic "instantiation: null" error. + private static ComposeContainer COMPOSE; + + /** + * Builds, starts the Docker Compose stack, and configures REST Assured. + * + *

Two-phase readiness check: + *

    + *
  1. Testcontainers polls {@code /actuator/health} until Spring Boot reports + * {@code "status":"UP"} — this covers RODA startup including Solr and database.
  2. + *
  3. Poll {@code GET /members/users/authenticated} until RODA returns the admin user, + * confirming LDAP authentication is operational (LDAP readiness lags slightly behind + * the actuator health signal).
  4. + *
+ * + *

TestNG guarantees this runs exactly once before any test method in the suite, + * regardless of how many test classes extend this base. + */ + @BeforeSuite(alwaysRun = true) + public void startEnvironment() { + LOGGER.info("Starting RODA Docker Compose stack from {}", COMPOSE_FILE.getAbsolutePath()); + + COMPOSE = new ComposeContainer(COMPOSE_FILE) + .withExposedService(RODA_SERVICE, RODA_PORT) + .waitingFor( + RODA_SERVICE, + Wait.forHttp("/actuator/health") + .forStatusCode(200) + .forResponsePredicate(body -> body.contains("\"status\":\"UP\"")) + .withStartupTimeout(Duration.ofMinutes(10))); + + COMPOSE.start(); + + String host = COMPOSE.getServiceHost(RODA_SERVICE, RODA_PORT); + int mappedPort = COMPOSE.getServicePort(RODA_SERVICE, RODA_PORT); + + LOGGER.info("RODA available at http://{}:{}/api/v2", host, mappedPort); + + RestAssured.baseURI = "http://" + host; + RestAssured.port = mappedPort; + RestAssured.basePath = "/api/v2"; + RestAssured.defaultParser = io.restassured.parsing.Parser.JSON; + + // requestSpecification is the most reliable way to set default auth globally + // across all given() calls in REST Assured 5.x. + RestAssured.requestSpecification = new RequestSpecBuilder() + .setAuth(preemptive().basic(ADMIN_USER, ADMIN_PASSWORD)) + .build(); + + // Phase 2: /actuator/health reports UP before LDAP is fully operational. + // Poll until the admin user is returned, confirming LDAP auth works. + LOGGER.info("Waiting for LDAP authentication to be ready..."); + pollUntil("LDAP auth", () -> { + String name = given().when().get("/members/users/authenticated").then().extract().path("name"); + return ADMIN_USER.equals(name); + }); + + } + + private void pollUntil(String label, java.util.function.BooleanSupplier condition) { + long deadline = System.currentTimeMillis() + Duration.ofMinutes(5).toMillis(); + int attempt = 0; + while (System.currentTimeMillis() < deadline) { + attempt++; + try { + if (condition.getAsBoolean()) { + LOGGER.info("{} ready after {} poll attempts", label, attempt); + return; + } + LOGGER.info("Attempt {}: {} not ready yet", attempt, label); + } catch (Exception e) { + LOGGER.warn("Attempt {}: {} check failed: {}", attempt, label, e.getMessage()); + } + try { + Thread.sleep(10000); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + return; + } + } + LOGGER.warn("{} readiness timed out after {} attempts — proceeding anyway", label, attempt); + } + + /** + * Stops the Docker Compose stack after all suite tests have finished. + */ + @AfterSuite(alwaysRun = true) + public void stopEnvironment() { + if (COMPOSE != null) { + LOGGER.info("Stopping RODA Docker Compose stack"); + COMPOSE.stop(); + } + } +} diff --git a/roda-api-tests/src/test/java/org/roda/tests/api/aips/AipApiTest.java b/roda-api-tests/src/test/java/org/roda/tests/api/aips/AipApiTest.java new file mode 100644 index 0000000000..4de3191e8f --- /dev/null +++ b/roda-api-tests/src/test/java/org/roda/tests/api/aips/AipApiTest.java @@ -0,0 +1,115 @@ +/** + * 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.tests.api.aips; + +import static io.restassured.RestAssured.given; + +import org.roda.tests.api.AbstractApiTest; +import org.roda.tests.api.model.IndexResult; +import org.roda.tests.api.model.LongResponse; +import org.roda.tests.api.model.User; +import org.testng.Assert; +import org.testng.annotations.Test; + +import io.restassured.http.ContentType; + +/** + * E2E tests for the {@code /api/v2/aips} resource. + * + *

All tests run against a live RODA instance managed by Testcontainers. + * The Docker Compose stack is started once per suite in {@link AbstractApiTest}. + * + *

A freshly started RODA instance has no AIPs, so count/find assertions + * verify structure (non-null, non-negative) rather than exact counts. + * + *

Filter JSON is built as raw strings because the generated OpenAPI model + * uses {@code "@type"} as the Jackson discriminator while RODA expects {@code "type"}. + */ +@Test(groups = {"e2e", "aips"}) +public class AipApiTest extends AbstractApiTest { + + private static final String ALL_FILTER_BODY = + "{\"filter\":{\"parameters\":[{\"type\":\"AllFilterParameter\"}]}}"; + + /** + * Sanity check: the authenticated admin user is returned correctly. + */ + @Test + public void getAuthenticatedUser_returnsAdminUser() { + User user = given() + .when() + .get("/members/users/authenticated") + .then() + .statusCode(200) + .extract().as(User.class); + + Assert.assertNotNull(user, "Authenticated user must not be null"); + Assert.assertEquals(user.getName(), ADMIN_USER, + "Expected the admin user to be authenticated"); + } + + /** + * {@code POST /aips/count} with an AllFilterParameter must return HTTP 200 + * and a non-negative count. + */ + @Test + public void countAips_withAllFilter_returns200AndNonNegativeCount() { + LongResponse response = given() + .contentType(ContentType.JSON) + .body(ALL_FILTER_BODY) + .when() + .post("/aips/count") + .then() + .statusCode(200) + .extract().as(LongResponse.class); + + Assert.assertNotNull(response, "Count response must not be null"); + Assert.assertTrue(response.getResult() >= 0, + "AIP count must be non-negative, got: " + response.getResult()); + } + + /** + * {@code POST /aips/find} with an AllFilterParameter and limit=10 must return + * HTTP 200 and a structurally valid {@link IndexResult}. + */ + @Test + public void findAips_withAllFilter_returns200AndValidIndexResult() { + String body = + "{\"filter\":{\"parameters\":[{\"type\":\"AllFilterParameter\"}]}," + + "\"sublist\":{\"firstElementIndex\":0,\"maximumElementCount\":10}}"; + + IndexResult result = given() + .contentType(ContentType.JSON) + .body(body) + .when() + .post("/aips/find") + .then() + .statusCode(200) + .extract().as(IndexResult.class); + + Assert.assertNotNull(result, "IndexResult must not be null"); + Assert.assertTrue(result.getTotalCount() >= 0, + "Total count must be non-negative, got: " + result.getTotalCount()); + // RODA returns results:null (not []) when totalCount == 0 + if (result.getTotalCount() > 0) { + Assert.assertNotNull(result.getResults(), "IndexResult.results must not be null when totalCount > 0"); + } + } + + /** + * {@code GET /aips/configuration/types} must return HTTP 200. + */ + @Test + public void getAipConfigurationTypes_returns200() { + given() + .when() + .get("/aips/configuration/types") + .then() + .statusCode(200); + } +} diff --git a/roda-api-tests/src/test/resources/logback-test.xml b/roda-api-tests/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..36863c28d1 --- /dev/null +++ b/roda-api-tests/src/test/resources/logback-test.xml @@ -0,0 +1,22 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + + + diff --git a/roda-api-tests/src/test/resources/testng.xml b/roda-api-tests/src/test/resources/testng.xml new file mode 100644 index 0000000000..80f598f327 --- /dev/null +++ b/roda-api-tests/src/test/resources/testng.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + 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..1f59483aff 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 @@ -10,8 +10,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Date; import java.util.List; +import org.roda.core.data.v2.disposal.schedule.DisposalActionCode; +import org.roda.core.data.v2.disposal.schedule.DisposalScheduleState; +import org.roda.core.data.v2.disposal.schedule.RetentionPeriodIntervalCode; import org.roda.core.data.v2.ip.Permissions.PermissionType; public final class RodaConstants { @@ -932,6 +936,9 @@ public enum OrchestratorType { public static final String INDEX_DIP_FILE = "DIPFile"; public static final String INDEX_REPRESENTATION_INFORMATION = "RepresentationInformation"; public static final String INDEX_DISPOSAL_CONFIRMATION = "DisposalConfirmation"; + public static final String INDEX_DISPOSAL_SCHEDULE = "DisposalSchedule"; + public static final String INDEX_DISPOSAL_HOLD = "DisposalHold"; + public static final String INDEX_DISPOSAL_RULE = "DisposalRule"; /* * INDEXED CLASSES @@ -946,7 +953,10 @@ public enum OrchestratorType { "org.roda.core.data.v2.risks.RiskIncidence", "org.roda.core.data.v2.ri.RepresentationInformation", "org.roda.core.data.v2.ip.TransferredResource", "org.roda.core.data.v2.user.User", "org.roda.core.data.v2.user.Group", "org.roda.core.data.v2.user.RODAMember", - "org.roda.core.data.v2.ip.disposal.DisposalConfirmation", "org.roda.core.data.v2.user.RodaPrincipal", + "org.roda.core.data.v2.disposal.hold.DisposalHold", + "org.roda.core.data.v2.disposal.schedule.DisposalSchedule", + "org.roda.core.data.v2.disposal.rule.DisposalRule", + "org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation", "org.roda.core.data.v2.user.RodaPrincipal", "org.roda.core.data.v2.ip.AIP", "org.roda.core.data.v2.risks.Risk", "org.roda.core.events.pekko.CRDTWrapper", "org.roda.core.data.v2.ip.DIP", "org.roda.core.data.v2.ip.metadata.DescriptiveMetadata", "org.roda.core.data.v2.ip.disposal.DisposalConfirmationAIPEntry", @@ -1307,8 +1317,32 @@ public enum OrchestratorType { public static final String DISPOSAL_CONFIRMATION_EXTRA_INFO = "extraInformation"; public static final String DISPOSAL_CONFIRMATION_STORAGE_SIZE = "size"; + public static final String DISPOSAL_SCHEDULE_TITLE = "title"; + public static final String DISPOSAL_SCHEDULE_DESCRIPTION = "description"; + public static final String DISPOSAL_SCHEDULE_MANDATE = "mandate"; + public static final String DISPOSAL_SCHEDULE_SCOPE_NOTES = "scopeNotes"; + public static final String DISPOSAL_SCHEDULE_STATE = "state"; + public static final String DISPOSAL_SCHEDULE_ACTION = "action"; + public static final String DISPOSAL_SCHEDULE_RETENTION_PERIOD_INTERVAL_CODE = "retentionPeriodIntervalCode"; + public static final String DISPOSAL_SCHEDULE_RETENTION_PERIOD_DURATION = "retentionPeriodDuration"; + + public static final String DISPOSAL_HOLD_TITLE = "title"; + public static final String DISPOSAL_HOLD_DESCRIPTION = "description"; + public static final String DISPOSAL_HOLD_MANDATE = "mandate"; + public static final String DISPOSAL_HOLD_SCOPE_NOTES = "scopeNotes"; + public static final String DISPOSAL_HOLD_STATE = "state"; + + public static final String DISPOSAL_RULE_ORDER = "order"; + public static final String DISPOSAL_RULE_TITLE = "title"; + public static final String DISPOSAL_RULE_DESCRIPTION = "description"; + public static final String DISPOSAL_RULE_SELECTION_METHOD = "selectionMethod"; + public static final String DISPOSAL_RULE_CONDITION_KEY = "conditionKey"; + public static final String DISPOSAL_RULE_CONDITION_VALUE = "conditionvalue"; + public static final String DISPOSAL_RULE_SCHEDULE_ID = "scheduleId"; + /* Disposal related parameters */ public static final String DISPOSAL_HOLD_FILE_EXTENSION = ".json"; + public static final String DISPOSAL_RULE_FILE_EXTENSION = ".json"; /* Distributed related parameters */ public static final String DISTRIBUTED_INSTANCE_FILE_EXTENSION = ".json"; @@ -2161,11 +2195,15 @@ 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"; public static final String PERMISSION_METHOD_CREATE_DISPOSAL_RULE = "org.roda.wui.api.v2.controller.DisposalRuleController.createDisposalRule"; public static final String PERMISSION_METHOD_UPDATE_DISPOSAL_RULE = "org.roda.wui.api.v2.controller.DisposalRuleController.updateDisposalRule"; + public static final String PERMISSION_METHOD_DELETE_DISPOSAL_RULE = "org.roda.wui.api.v2.controller.DisposalRuleController.deleteDisposalRule"; public static final String PERMISSION_METHOD_CREATE_DISPOSAL_SCHEDULE = "org.roda.wui.api.v2.controller.DisposalScheduleController.createDisposalSchedule"; public static final String PERMISSION_METHOD_UPDATE_DISPOSAL_SCHEDULE = "org.roda.wui.api.v2.controller.DisposalScheduleController.updateDisposalSchedule"; diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/common/RODAObjectList.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/common/RODAObjectList.java index 72144f4da0..4263ac6169 100644 --- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/common/RODAObjectList.java +++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/common/RODAObjectList.java @@ -14,9 +14,9 @@ public interface RODAObjectList extends Serializable { - public List getObjects(); + List getObjects(); - public void setObjects(List objects); + void setObjects(List objects); - public void addObject(T object); + void addObject(T object); } diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/hold/DisposalHold.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/hold/DisposalHold.java index 791823505f..063219f753 100644 --- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/hold/DisposalHold.java +++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/hold/DisposalHold.java @@ -7,7 +7,12 @@ */ package org.roda.core.data.v2.disposal.hold; +import java.io.Serial; +import java.util.Arrays; import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Objects; import org.roda.core.data.common.RodaConstants; @@ -15,6 +20,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; +import org.roda.core.data.v2.index.IsIndexed; +import org.roda.core.data.v2.ip.HasId; /** * @author Tiago Fraga @@ -22,8 +29,9 @@ @jakarta.xml.bind.annotation.XmlRootElement(name = RodaConstants.RODA_OBJECT_DISPOSAL_HOLD) @JsonInclude(JsonInclude.Include.NON_NULL) -public class DisposalHold implements IsModelObject { +public class DisposalHold implements IsIndexed, IsModelObject, HasId { + @Serial private static final long serialVersionUID = 8291490773422089586L; private String id; @@ -57,6 +65,8 @@ public class DisposalHold implements IsModelObject { private DisposalHoldState state; + private Map fields = new HashMap<>(); + public DisposalHold() { super(); this.state = DisposalHoldState.ACTIVE; @@ -242,4 +252,38 @@ public String toString() { + ", liftedBy='" + liftedBy + '\'' + ", firstTimeUsed=" + firstTimeUsed + ", aipCounter=" + aipCounter + ", state=" + state + '}'; } + + @JsonIgnore + @Override + public String getUUID() { + return getId(); + } + + @Override + public List toCsvHeaders() { + return Arrays.asList("id", "title", "description", "mandate", "scopeNotes", "createOn", "createBy", "updatedOn", + "updatedBy", "originatedOn", "originatedBy", "liftedOn", "liftedBy", "firstTimeUsed", "state"); + } + + @Override + public List toCsvValues() { + return Arrays.asList(id, title, description, mandate, scopeNotes, createdOn, createdBy, updatedOn, updatedBy, + originatedOn, originatedBy, liftedOn, liftedBy, firstTimeUsed, state); + } + + @Override + public List liteFields() { + return List.of(); + } + + @Override + public Map getFields() { + return this.fields; + } + + @Override + public void setFields(Map fields) { + fields.entrySet().stream().filter(p -> p.getValue() != null) + .forEach(e -> this.fields.put(e.getKey(), e.getValue())); + } } diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/hold/DisposalHoldState.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/hold/DisposalHoldState.java index 3227e7854b..c48af2c710 100644 --- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/hold/DisposalHoldState.java +++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/hold/DisposalHoldState.java @@ -11,5 +11,9 @@ * @author Tiago Fraga */ public enum DisposalHoldState { - ACTIVE, LIFTED + ACTIVE, LIFTED; + + public static DisposalHoldState getDefault() { + return ACTIVE; + } } diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/ChangeOrderRequest.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/ChangeOrderRequest.java new file mode 100644 index 0000000000..dea7ab9447 --- /dev/null +++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/ChangeOrderRequest.java @@ -0,0 +1,53 @@ +package org.roda.core.data.v2.disposal.rule; + +import org.roda.core.data.v2.generics.select.SelectedItemsRequest; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author Miguel Guimarães + */ + +public class ChangeOrderRequest implements Serializable { + @Serial + private static final long serialVersionUID = -1604170151297177958L; + + private SelectedItemsRequest items; + private OrderPositions position; + private int newOrder; + + public ChangeOrderRequest() { + position = OrderPositions.TOP; + } + + public ChangeOrderRequest(SelectedItemsRequest items, OrderPositions position, int newOrder) { + this.items = items; + this.position = position; + this.newOrder = newOrder; + } + + public SelectedItemsRequest getItems() { + return items; + } + + public void setItems(SelectedItemsRequest items) { + this.items = items; + } + + public int getNewOrder() { + return newOrder; + } + + public void setNewOrder(int newOrder) { + this.newOrder = newOrder; + } + + public OrderPositions getPosition() { + return position; + } + + public void setPosition(OrderPositions position) { + this.position = position; + } +} diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/ConditionType.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/ConditionType.java index df4f3b8147..291fd58f9a 100644 --- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/ConditionType.java +++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/ConditionType.java @@ -11,5 +11,9 @@ * @author Tiago Fraga */ public enum ConditionType { - IS_CHILD_OF, METADATA_FIELD + IS_CHILD_OF, METADATA_FIELD; + + public static ConditionType getDefault() { + return IS_CHILD_OF; + } } diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/DisposalRule.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/DisposalRule.java index 9ff812252e..9d8fdf8d6a 100644 --- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/DisposalRule.java +++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/DisposalRule.java @@ -8,11 +8,15 @@ package org.roda.core.data.v2.disposal.rule; import java.io.Serial; +import java.util.Arrays; import java.util.Date; +import java.util.List; +import java.util.Map; import java.util.Objects; import org.roda.core.data.common.RodaConstants; import org.roda.core.data.v2.IsModelObject; +import org.roda.core.data.v2.index.IsIndexed; import org.roda.core.data.v2.ip.HasId; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -23,7 +27,7 @@ */ @jakarta.xml.bind.annotation.XmlRootElement(name = RodaConstants.RODA_OBJECT_DISPOSAL_RULE) @JsonInclude(JsonInclude.Include.NON_NULL) -public class DisposalRule implements IsModelObject, HasId, Comparable { +public class DisposalRule implements IsModelObject, IsIndexed, HasId, Comparable { private static final int VERSION = 1; @Serial @@ -50,8 +54,11 @@ public class DisposalRule implements IsModelObject, HasId, Comparable fields; + public DisposalRule() { super(); + type = ConditionType.IS_CHILD_OF; } @Override @@ -201,4 +208,37 @@ public int getClassVersion() { public int compareTo(DisposalRule otherRule) { return Integer.compare(this.getOrder(), otherRule.getOrder()); } + + @JsonIgnore + @Override + public String getUUID() { + return getId(); + } + + @Override + public List toCsvHeaders() { + return Arrays.asList("id", "title", "description", "type", "conditionKey", "conditionValue", "disposalScheduleId", + "disposalScheduleName", "order", "createdOn", "createdBy", "updatedOn", "updatedBy"); + } + + @Override + public List toCsvValues() { + return Arrays.asList(id, title, description, type, conditionKey, conditionValue, disposalScheduleId, + disposalScheduleName, order, createdOn, createdBy, updatedOn, updatedBy); + } + + @Override + public List liteFields() { + return List.of(); + } + + @Override + public Map getFields() { + return this.fields; + } + + @Override + public void setFields(Map fields) { + this.fields = fields; + } } diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/DisposalRules.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/DisposalRules.java index a98fb9e61c..7256972b88 100644 --- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/DisposalRules.java +++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/DisposalRules.java @@ -7,6 +7,7 @@ */ package org.roda.core.data.v2.disposal.rule; +import java.io.Serial; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -23,6 +24,8 @@ @JsonInclude(JsonInclude.Include.NON_NULL) public class DisposalRules implements RODAObjectList { + @Serial + private static final long serialVersionUID = 5273072223178069428L; private List disposalRuleList; public DisposalRules() { diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/OrderPositions.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/OrderPositions.java new file mode 100644 index 0000000000..fa245a8f5f --- /dev/null +++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/OrderPositions.java @@ -0,0 +1,9 @@ +package org.roda.core.data.v2.disposal.rule; + +/** + * @author Miguel Guimarães + */ + +public enum OrderPositions { + TOP, BOTTOM, POSITION +} diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/DisposalSchedule.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/DisposalSchedule.java index 45aa44af63..2d6bf63812 100644 --- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/DisposalSchedule.java +++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/DisposalSchedule.java @@ -8,10 +8,16 @@ package org.roda.core.data.v2.disposal.schedule; import java.io.Serial; +import java.util.Arrays; import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Objects; +import org.roda.core.data.common.RodaConstants; import org.roda.core.data.v2.IsModelObject; +import org.roda.core.data.v2.index.IsIndexed; import org.roda.core.data.v2.ip.HasId; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -21,7 +27,7 @@ * @author Miguel Guimarães */ @JsonInclude(JsonInclude.Include.NON_NULL) -public class DisposalSchedule implements IsModelObject, HasId { +public class DisposalSchedule implements IsModelObject, IsIndexed, HasId { @Serial private static final long serialVersionUID = -2870778207871507847L; private static final int VERSION = 1; @@ -54,6 +60,10 @@ public class DisposalSchedule implements IsModelObject, HasId { private DisposalScheduleState state; + private Map fields = new HashMap<>(); + + private boolean usedInDisposalRule = false; + public DisposalSchedule() { super(); this.state = DisposalScheduleState.ACTIVE; @@ -221,6 +231,14 @@ public void setState(DisposalScheduleState state) { this.state = state; } + public boolean isUsedInDisposalRule() { + return usedInDisposalRule; + } + + public void setUsedInDisposalRule(boolean usedInDisposalRule) { + this.usedInDisposalRule = usedInDisposalRule; + } + @Override public boolean equals(Object o) { if (this == o) @@ -255,4 +273,39 @@ public String toString() { + firstTimeUsed + ", numberOfAIPUnder=" + apiCounter + ", createdOn=" + createdOn + ", createdBy='" + createdBy + '\'' + ", updatedOn=" + updatedOn + ", updatedBy='" + updatedBy + '\'' + ", state=" + state + '}'; } + + @JsonIgnore + @Override + public String getUUID() { + return getId(); + } + + @Override + public List toCsvHeaders() { + return Arrays.asList("id", "title", "description", "mandate", "scopeNotes", "actionCode", + "retentionTriggerElementId", "retentionPeriodIntervalCode", "retentionPeriodDuration", "createdOn", "createdBy", + "updatedOn", "updatedBy", "state"); + } + + @Override + public List toCsvValues() { + return Arrays.asList(id, title, description, mandate, scopeNotes, actionCode, retentionTriggerElementId, + retentionPeriodIntervalCode, retentionPeriodDuration, createdOn, createdBy, updatedOn, updatedBy, state); + } + + @Override + public List liteFields() { + return Arrays.asList(RodaConstants.INDEX_UUID); + } + + @Override + public Map getFields() { + return fields; + } + + @Override + public void setFields(Map fields) { + fields.entrySet().stream().filter(p -> p.getValue() != null) + .forEach(e -> this.fields.put(e.getKey(), e.getValue())); + } } diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/DisposalScheduleState.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/DisposalScheduleState.java index 53f54b2098..a499448443 100644 --- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/DisposalScheduleState.java +++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/DisposalScheduleState.java @@ -11,5 +11,9 @@ * @author Miguel Guimarães */ public enum DisposalScheduleState { - ACTIVE, INACTIVE + ACTIVE, INACTIVE; + + public static DisposalScheduleState getDefault() { + return ACTIVE; + } } diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/IndexedDisposalSchedule.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/IndexedDisposalSchedule.java new file mode 100644 index 0000000000..b5d0ebcbbb --- /dev/null +++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/IndexedDisposalSchedule.java @@ -0,0 +1,47 @@ +package org.roda.core.data.v2.disposal.schedule; + +import org.roda.core.data.v2.index.IsIndexed; + +import java.util.List; +import java.util.Map; + +/** + * @author Miguel Guimarães + */ + +public class IndexedDisposalSchedule implements IsIndexed { + @Override + public String getUUID() { + return ""; + } + + @Override + public List toCsvHeaders() { + return List.of(); + } + + @Override + public List toCsvValues() { + return List.of(); + } + + @Override + public List liteFields() { + return List.of(); + } + + @Override + public Map getFields() { + return Map.of(); + } + + @Override + public void setFields(Map fields) { + + } + + @Override + public String getId() { + return ""; + } +} diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/RetentionPeriodIntervalCode.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/RetentionPeriodIntervalCode.java index 84079d53d8..83cc2a18b6 100644 --- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/RetentionPeriodIntervalCode.java +++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/RetentionPeriodIntervalCode.java @@ -11,5 +11,9 @@ * @author Miguel Guimarães */ public enum RetentionPeriodIntervalCode { - NO_RETENTION_PERIOD, DAYS, WEEKS, MONTHS, YEARS + NO_RETENTION_PERIOD, DAYS, WEEKS, MONTHS, YEARS; + + public static RetentionPeriodIntervalCode getDefault() { + return NO_RETENTION_PERIOD; + } } diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/index/select/SelectedItemsList.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/index/select/SelectedItemsList.java index 534349b270..696a73a47d 100644 --- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/index/select/SelectedItemsList.java +++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/index/select/SelectedItemsList.java @@ -14,8 +14,6 @@ import org.roda.core.data.v2.IsRODAObject; -import com.fasterxml.jackson.annotation.JsonTypeName; - public class SelectedItemsList implements SelectedItems { @Serial diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/representation/ChangeTypeRequest.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/representation/ChangeTypeRequest.java index aeb807336d..5e0f78e950 100644 --- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/representation/ChangeTypeRequest.java +++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/representation/ChangeTypeRequest.java @@ -10,10 +10,6 @@ import java.io.Serial; import java.io.Serializable; -import io.swagger.v3.oas.annotations.media.DiscriminatorMapping; -import io.swagger.v3.oas.annotations.media.Schema; -import org.roda.core.data.v2.generics.select.SelectedItemsFilterRequest; -import org.roda.core.data.v2.generics.select.SelectedItemsListRequest; import org.roda.core.data.v2.generics.select.SelectedItemsRequest; /** 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/index/IndexModelObserver.java b/roda-core/roda-core/src/main/java/org/roda/core/index/IndexModelObserver.java index 0647dba256..2c7d68af30 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/index/IndexModelObserver.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/index/IndexModelObserver.java @@ -37,6 +37,8 @@ import org.roda.core.data.v2.LiteRODAObject; import org.roda.core.data.v2.common.OptionalWithCause; import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation; +import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.core.data.v2.disposal.rule.DisposalRule; import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; import org.roda.core.data.v2.index.IsIndexed; import org.roda.core.data.v2.index.filter.Filter; @@ -1509,6 +1511,36 @@ public ReturnWithExceptions disposalConfirmationDeleted(Str return deleteDocumentFromIndex(DisposalConfirmation.class, confirmationId); } + @Override + public ReturnWithExceptions disposalScheduleCreatedOrUpdated(DisposalSchedule schedule) { + return SolrUtils.create2(index, model, this, DisposalSchedule.class, schedule); + } + + @Override + public ReturnWithExceptions disposalScheduleDeleted(String scheduleId, boolean commit) { + return deleteDocumentFromIndex(DisposalSchedule.class, scheduleId); + } + + @Override + public ReturnWithExceptions disposalHoldCreatedOrUpdated(DisposalHold hold) { + return SolrUtils.create2(index, model, this, DisposalHold.class, hold); + } + + @Override + public ReturnWithExceptions disposalHoldDeleted(String holdId, boolean commit) { + return deleteDocumentFromIndex(DisposalHold.class, holdId); + } + + @Override + public ReturnWithExceptions disposalRuleCreatedOrUpdated(DisposalRule rule) { + return SolrUtils.create2(index, model, this, DisposalRule.class, rule); + } + + @Override + public ReturnWithExceptions disposalRuleDeleted(String ruleId, boolean commit) { + return deleteDocumentFromIndex(DisposalRule.class, ruleId); + } + public ReturnWithExceptions liteRODAObjectCreated(LiteRODAObject liteRODAObject) { ReturnWithExceptions ret = new ReturnWithExceptions<>(this); OptionalWithCause liteObject = LiteRODAObjectFactory.get(model, liteRODAObject); diff --git a/roda-core/roda-core/src/main/java/org/roda/core/index/IndexService.java b/roda-core/roda-core/src/main/java/org/roda/core/index/IndexService.java index 6a109866aa..6864437de0 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/index/IndexService.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/index/IndexService.java @@ -40,6 +40,9 @@ import org.roda.core.data.v2.IsRODAObject; import org.roda.core.data.v2.common.OptionalWithCause; import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation; +import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.core.data.v2.disposal.rule.DisposalRule; +import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; import org.roda.core.data.v2.index.FindRequest; import org.roda.core.data.v2.index.IndexResult; import org.roda.core.data.v2.index.IndexRunnable; @@ -322,6 +325,30 @@ public ReturnWithExceptions reindexDisposalConfirmation(Dis return ret; } + public ReturnWithExceptions reindexDisposalSchedule(DisposalSchedule schedule) { + ReturnWithExceptions ret = RodaCoreFactory.checkIfWriteIsAllowedAndIfFalseReturn(nodeType); + if (ret.isEmpty()) { + ret = observer.disposalScheduleCreatedOrUpdated(schedule); + } + return ret; + } + + public ReturnWithExceptions reindexDisposalHold(DisposalHold hold) { + ReturnWithExceptions ret = RodaCoreFactory.checkIfWriteIsAllowedAndIfFalseReturn(nodeType); + if (ret.isEmpty()) { + ret = observer.disposalHoldCreatedOrUpdated(hold); + } + return ret; + } + + public ReturnWithExceptions reindexDisposalRule(DisposalRule rule) { + ReturnWithExceptions ret = RodaCoreFactory.checkIfWriteIsAllowedAndIfFalseReturn(nodeType); + if (ret.isEmpty()) { + ret = observer.disposalRuleCreatedOrUpdated(rule); + } + return ret; + } + public ReturnWithExceptions reindexAIPPreservationEvents(AIP aip) { ReturnWithExceptions ret = RodaCoreFactory.checkIfWriteIsAllowedAndIfFalseReturn(nodeType); if (ret.isEmpty()) { @@ -519,6 +546,12 @@ public ReturnWithExceptions reinde return reindexDIPFile(DIPFile.class.cast(object)); } else if (DisposalConfirmation.class.equals(objectClass)) { return reindexDisposalConfirmation(DisposalConfirmation.class.cast(object)); + } else if (DisposalSchedule.class.equals(objectClass)) { + return reindexDisposalSchedule(DisposalSchedule.class.cast(object)); + } else if (DisposalHold.class.equals(objectClass)) { + return reindexDisposalHold(DisposalHold.class.cast(object)); + } else if (DisposalRule.class.equals(objectClass)) { + return reindexDisposalRule(DisposalRule.class.cast(object)); } else { LOGGER.error("Error trying to reindex an unconfigured object class: {}", objectClass.getName()); ReturnWithExceptions exceptions = new ReturnWithExceptions<>(); diff --git a/roda-core/roda-core/src/main/java/org/roda/core/index/schema/SolrCollectionRegistry.java b/roda-core/roda-core/src/main/java/org/roda/core/index/schema/SolrCollectionRegistry.java index ebd2acba4d..a2262239cd 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/index/schema/SolrCollectionRegistry.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/index/schema/SolrCollectionRegistry.java @@ -29,6 +29,9 @@ import org.roda.core.index.schema.collections.DIPCollection; import org.roda.core.index.schema.collections.DIPFileCollection; import org.roda.core.index.schema.collections.DisposalConfirmationCollection; +import org.roda.core.index.schema.collections.DisposalHoldCollection; +import org.roda.core.index.schema.collections.DisposalRuleCollection; +import org.roda.core.index.schema.collections.DisposalScheduleCollection; import org.roda.core.index.schema.collections.FileCollection; import org.roda.core.index.schema.collections.JobCollection; import org.roda.core.index.schema.collections.JobReportCollection; @@ -46,10 +49,6 @@ public final class SolrCollectionRegistry { - private SolrCollectionRegistry() { - - } - private static final Map, SolrCollection> REGISTRY = new HashMap<>(); private static final Map, Class> MODEL_TO_INDEX = new HashMap<>(); @@ -79,9 +78,16 @@ private SolrCollectionRegistry() { register(new RepresentationInformationCollection()); + register(new DisposalScheduleCollection()); + register(new DisposalHoldCollection()); + register(new DisposalRuleCollection()); register(new DisposalConfirmationCollection()); } + private SolrCollectionRegistry() { + + } + public static void register(SolrCollection collection) { REGISTRY.put(collection.getIndexClass(), collection); MODEL_TO_INDEX.put(collection.getModelClass(), collection.getIndexClass()); @@ -138,9 +144,9 @@ public static I fromSolrDocument(Class indexClass, Solr return fromSolrDocument(indexClass, doc, Collections.emptyList()); } - public static SolrInputDocument toSolrDocument(Class indexClass, ModelService model, - M object, IndexingAdditionalInfo utils) throws GenericException, NotSupportedException, RequestNotValidException, - NotFoundException, AuthorizationDeniedException { + public static SolrInputDocument toSolrDocument(Class indexClass, + ModelService model, M object, IndexingAdditionalInfo utils) throws GenericException, NotSupportedException, + RequestNotValidException, NotFoundException, AuthorizationDeniedException { SolrCollection solrCollection = get(indexClass); if (solrCollection != null) { return solrCollection.toSolrDocument(model, object, utils); @@ -150,9 +156,9 @@ public static SolrInputDocument t } } - public static SolrInputDocument toSolrDocument(Class indexClass, ModelService model, - M object) throws GenericException, NotSupportedException, RequestNotValidException, NotFoundException, - AuthorizationDeniedException { + public static SolrInputDocument toSolrDocument(Class indexClass, + ModelService model, M object) throws GenericException, NotSupportedException, RequestNotValidException, + NotFoundException, AuthorizationDeniedException { return toSolrDocument(indexClass, model, object, IndexingAdditionalInfo.empty()); } diff --git a/roda-core/roda-core/src/main/java/org/roda/core/index/schema/collections/DisposalHoldCollection.java b/roda-core/roda-core/src/main/java/org/roda/core/index/schema/collections/DisposalHoldCollection.java new file mode 100644 index 0000000000..f2b3457c61 --- /dev/null +++ b/roda-core/roda-core/src/main/java/org/roda/core/index/schema/collections/DisposalHoldCollection.java @@ -0,0 +1,108 @@ +package org.roda.core.index.schema.collections; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.solr.common.SolrDocument; +import org.apache.solr.common.SolrInputDocument; +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.exceptions.AuthorizationDeniedException; +import org.roda.core.data.exceptions.GenericException; +import org.roda.core.data.exceptions.NotFoundException; +import org.roda.core.data.exceptions.RequestNotValidException; +import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.core.data.v2.disposal.hold.DisposalHoldState; +import org.roda.core.index.IndexingAdditionalInfo; +import org.roda.core.index.schema.AbstractSolrCollection; +import org.roda.core.index.schema.CopyField; +import org.roda.core.index.schema.Field; +import org.roda.core.index.schema.SolrCollection; +import org.roda.core.index.utils.SolrUtils; +import org.roda.core.model.ModelService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Miguel Guimarães + */ + +public class DisposalHoldCollection extends AbstractSolrCollection { + + private static final Logger LOGGER = LoggerFactory.getLogger(DisposalHoldCollection.class); + + @Override + public Class getIndexClass() { + return DisposalHold.class; + } + + @Override + public Class getModelClass() { + return DisposalHold.class; + } + + @Override + public String getIndexName() { + return RodaConstants.INDEX_DISPOSAL_HOLD; + } + + @Override + public List getCommitIndexNames() { + return Arrays.asList(RodaConstants.INDEX_DISPOSAL_HOLD); + } + + @Override + public String getUniqueId(DisposalHold modelObject) { + return modelObject.getUUID(); + } + + @Override + public List getCopyFields() { + return Collections.singletonList(SolrCollection.getCopyAllToSearchField()); + } + + @Override + public List getFields() { + List fields = new ArrayList<>(super.getFields()); + + fields.add(new Field(RodaConstants.DISPOSAL_HOLD_TITLE, Field.TYPE_STRING)); + fields.add(new Field(RodaConstants.DISPOSAL_HOLD_DESCRIPTION, Field.TYPE_STRING)); + fields.add(new Field(RodaConstants.DISPOSAL_HOLD_MANDATE, Field.TYPE_STRING)); + fields.add(new Field(RodaConstants.DISPOSAL_HOLD_SCOPE_NOTES, Field.TYPE_STRING)); + fields.add(new Field(RodaConstants.DISPOSAL_HOLD_STATE, Field.TYPE_STRING)); + + return fields; + } + + @Override + public SolrInputDocument toSolrDocument(ModelService model, DisposalHold hold, IndexingAdditionalInfo info) + throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException { + + SolrInputDocument doc = super.toSolrDocument(model, hold, info); + + doc.addField(RodaConstants.DISPOSAL_HOLD_TITLE, hold.getTitle()); + doc.addField(RodaConstants.DISPOSAL_HOLD_DESCRIPTION, hold.getDescription()); + doc.addField(RodaConstants.DISPOSAL_HOLD_MANDATE, hold.getMandate()); + doc.addField(RodaConstants.DISPOSAL_HOLD_SCOPE_NOTES, hold.getScopeNotes()); + doc.addField(RodaConstants.DISPOSAL_HOLD_STATE, hold.getState().toString()); + + return doc; + } + + @Override + public DisposalHold fromSolrDocument(SolrDocument doc, List fieldsToReturn) throws GenericException { + + final DisposalHold hold = super.fromSolrDocument(doc, fieldsToReturn); + + hold.setTitle(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_HOLD_TITLE), null)); + hold.setDescription(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_HOLD_DESCRIPTION), null)); + hold.setMandate(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_HOLD_MANDATE), null)); + hold.setScopeNotes(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_HOLD_SCOPE_NOTES), null)); + DisposalHoldState disposalHoldState = SolrUtils.objectToEnum(doc.get(RodaConstants.DISPOSAL_HOLD_STATE), + DisposalHoldState.class, DisposalHoldState.getDefault()); + hold.setState(disposalHoldState); + + return hold; + } +} diff --git a/roda-core/roda-core/src/main/java/org/roda/core/index/schema/collections/DisposalRuleCollection.java b/roda-core/roda-core/src/main/java/org/roda/core/index/schema/collections/DisposalRuleCollection.java new file mode 100644 index 0000000000..c1344ef71d --- /dev/null +++ b/roda-core/roda-core/src/main/java/org/roda/core/index/schema/collections/DisposalRuleCollection.java @@ -0,0 +1,114 @@ +package org.roda.core.index.schema.collections; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.solr.common.SolrDocument; +import org.apache.solr.common.SolrInputDocument; +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.exceptions.AuthorizationDeniedException; +import org.roda.core.data.exceptions.GenericException; +import org.roda.core.data.exceptions.NotFoundException; +import org.roda.core.data.exceptions.RequestNotValidException; +import org.roda.core.data.v2.disposal.rule.ConditionType; +import org.roda.core.data.v2.disposal.rule.DisposalRule; +import org.roda.core.index.IndexingAdditionalInfo; +import org.roda.core.index.schema.AbstractSolrCollection; +import org.roda.core.index.schema.CopyField; +import org.roda.core.index.schema.Field; +import org.roda.core.index.schema.SolrCollection; +import org.roda.core.index.utils.SolrUtils; +import org.roda.core.model.ModelService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Miguel Guimarães + */ + +public class DisposalRuleCollection extends AbstractSolrCollection { + + private static final Logger LOGGER = LoggerFactory.getLogger(DisposalRuleCollection.class); + + @Override + public Class getIndexClass() { + return DisposalRule.class; + } + + @Override + public Class getModelClass() { + return DisposalRule.class; + } + + @Override + public String getIndexName() { + return RodaConstants.INDEX_DISPOSAL_RULE; + } + + @Override + public List getCommitIndexNames() { + return Arrays.asList(RodaConstants.INDEX_DISPOSAL_RULE); + } + + @Override + public String getUniqueId(DisposalRule modelObject) { + return modelObject.getUUID(); + } + + @Override + public List getCopyFields() { + return Collections.singletonList(SolrCollection.getCopyAllToSearchField()); + } + + @Override + public List getFields() { + List fields = new ArrayList<>(super.getFields()); + + fields.add(new Field(RodaConstants.DISPOSAL_RULE_ORDER, Field.TYPE_INT)); + fields.add(new Field(RodaConstants.DISPOSAL_RULE_TITLE, Field.TYPE_STRING)); + fields.add(new Field(RodaConstants.DISPOSAL_RULE_DESCRIPTION, Field.TYPE_STRING)); + fields.add(new Field(RodaConstants.DISPOSAL_RULE_SELECTION_METHOD, Field.TYPE_STRING)); + fields.add(new Field(RodaConstants.DISPOSAL_RULE_CONDITION_KEY, Field.TYPE_STRING)); + fields.add(new Field(RodaConstants.DISPOSAL_RULE_CONDITION_VALUE, Field.TYPE_STRING)); + fields.add(new Field(RodaConstants.DISPOSAL_RULE_SCHEDULE_ID, Field.TYPE_STRING)); + + return fields; + } + + @Override + public SolrInputDocument toSolrDocument(ModelService model, DisposalRule rule, IndexingAdditionalInfo info) + throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException { + + SolrInputDocument doc = super.toSolrDocument(model, rule, info); + + doc.addField(RodaConstants.DISPOSAL_RULE_ORDER, rule.getOrder()); + doc.addField(RodaConstants.DISPOSAL_RULE_TITLE, rule.getTitle()); + doc.addField(RodaConstants.DISPOSAL_RULE_DESCRIPTION, rule.getDescription()); + doc.addField(RodaConstants.DISPOSAL_RULE_SELECTION_METHOD, rule.getType().toString()); + doc.addField(RodaConstants.DISPOSAL_RULE_CONDITION_KEY, rule.getConditionKey()); + doc.addField(RodaConstants.DISPOSAL_RULE_CONDITION_VALUE, rule.getConditionValue()); + doc.addField(RodaConstants.DISPOSAL_RULE_SCHEDULE_ID, rule.getDisposalScheduleId()); + + return doc; + } + + @Override + public DisposalRule fromSolrDocument(SolrDocument doc, List fieldsToReturn) throws GenericException { + + final DisposalRule rule = super.fromSolrDocument(doc, fieldsToReturn); + + rule.setOrder(SolrUtils.objectToInteger(doc.get(RodaConstants.DISPOSAL_RULE_ORDER), null)); + rule.setTitle(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_RULE_TITLE), null)); + rule.setDescription(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_RULE_DESCRIPTION), null)); + rule.setConditionKey(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_RULE_CONDITION_KEY), null)); + rule.setConditionValue(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_RULE_CONDITION_VALUE), null)); + rule.setDisposalScheduleId(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_RULE_SCHEDULE_ID), null)); + ConditionType conditionType = SolrUtils.objectToEnum(doc.get(RodaConstants.DISPOSAL_RULE_SELECTION_METHOD), + ConditionType.class, ConditionType.getDefault()); + rule.setType(conditionType); + + return rule; + } +} diff --git a/roda-core/roda-core/src/main/java/org/roda/core/index/schema/collections/DisposalScheduleCollection.java b/roda-core/roda-core/src/main/java/org/roda/core/index/schema/collections/DisposalScheduleCollection.java new file mode 100644 index 0000000000..462e356f22 --- /dev/null +++ b/roda-core/roda-core/src/main/java/org/roda/core/index/schema/collections/DisposalScheduleCollection.java @@ -0,0 +1,132 @@ +package org.roda.core.index.schema.collections; + +import org.apache.solr.common.SolrDocument; +import org.apache.solr.common.SolrInputDocument; +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.exceptions.AuthorizationDeniedException; +import org.roda.core.data.exceptions.GenericException; +import org.roda.core.data.exceptions.NotFoundException; +import org.roda.core.data.exceptions.RequestNotValidException; +import org.roda.core.data.v2.disposal.schedule.DisposalActionCode; +import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; +import org.roda.core.data.v2.disposal.schedule.DisposalScheduleState; +import org.roda.core.data.v2.disposal.schedule.RetentionPeriodIntervalCode; +import org.roda.core.index.IndexingAdditionalInfo; +import org.roda.core.index.schema.AbstractSolrCollection; +import org.roda.core.index.schema.CopyField; +import org.roda.core.index.schema.Field; +import org.roda.core.index.schema.SolrCollection; +import org.roda.core.index.utils.SolrUtils; +import org.roda.core.model.ModelService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * @author Miguel Guimarães + */ + +public class DisposalScheduleCollection extends AbstractSolrCollection { + + private static final Logger LOGGER = LoggerFactory.getLogger(DisposalScheduleCollection.class); + + @Override + public Class getIndexClass() { + return DisposalSchedule.class; + } + + @Override + public Class getModelClass() { + return DisposalSchedule.class; + } + + @Override + public String getIndexName() { + return RodaConstants.INDEX_DISPOSAL_SCHEDULE; + } + + @Override + public List getCommitIndexNames() { + return Arrays.asList(RodaConstants.INDEX_DISPOSAL_SCHEDULE); + } + + @Override + public String getUniqueId(DisposalSchedule modelObject) { + return modelObject.getUUID(); + } + + @Override + public List getCopyFields() { + return Collections.singletonList(SolrCollection.getCopyAllToSearchField()); + } + + @Override + public List getFields() { + List fields = new ArrayList<>(super.getFields()); + + fields.add(new Field(RodaConstants.DISPOSAL_SCHEDULE_TITLE, Field.TYPE_STRING)); + fields.add(new Field(RodaConstants.DISPOSAL_SCHEDULE_DESCRIPTION, Field.TYPE_STRING)); + fields.add(new Field(RodaConstants.DISPOSAL_SCHEDULE_MANDATE, Field.TYPE_STRING)); + fields.add(new Field(RodaConstants.DISPOSAL_SCHEDULE_SCOPE_NOTES, Field.TYPE_STRING)); + fields.add(new Field(RodaConstants.DISPOSAL_SCHEDULE_STATE, Field.TYPE_STRING)); + fields.add(new Field(RodaConstants.DISPOSAL_SCHEDULE_ACTION, Field.TYPE_STRING)); + fields.add(new Field(RodaConstants.DISPOSAL_SCHEDULE_RETENTION_PERIOD_INTERVAL_CODE, Field.TYPE_STRING)); + fields.add(new Field(RodaConstants.DISPOSAL_SCHEDULE_RETENTION_PERIOD_DURATION, Field.TYPE_INT)); + + return fields; + } + + @Override + public SolrInputDocument toSolrDocument(ModelService model, DisposalSchedule disposalSchedule, + IndexingAdditionalInfo info) + throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException { + + SolrInputDocument doc = super.toSolrDocument(model, disposalSchedule, info); + + doc.addField(RodaConstants.DISPOSAL_SCHEDULE_TITLE, disposalSchedule.getTitle()); + doc.addField(RodaConstants.DISPOSAL_SCHEDULE_DESCRIPTION, disposalSchedule.getDescription()); + doc.addField(RodaConstants.DISPOSAL_SCHEDULE_MANDATE, disposalSchedule.getMandate()); + doc.addField(RodaConstants.DISPOSAL_SCHEDULE_SCOPE_NOTES, disposalSchedule.getScopeNotes()); + if (!DisposalActionCode.RETAIN_PERMANENTLY.equals(disposalSchedule.getActionCode())) { + doc.addField(RodaConstants.DISPOSAL_SCHEDULE_RETENTION_PERIOD_INTERVAL_CODE, + disposalSchedule.getRetentionPeriodIntervalCode().toString()); + doc.addField(RodaConstants.DISPOSAL_SCHEDULE_RETENTION_PERIOD_DURATION, + disposalSchedule.getRetentionPeriodDuration()); + } + doc.addField(RodaConstants.DISPOSAL_SCHEDULE_STATE, disposalSchedule.getState().toString()); + doc.addField(RodaConstants.DISPOSAL_SCHEDULE_ACTION, disposalSchedule.getActionCode().toString()); + + return doc; + } + + @Override + public DisposalSchedule fromSolrDocument(SolrDocument doc, List fieldsToReturn) throws GenericException { + + final DisposalSchedule disposalSchedule = super.fromSolrDocument(doc, fieldsToReturn); + + disposalSchedule.setTitle(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_SCHEDULE_TITLE), null)); + disposalSchedule + .setDescription(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_SCHEDULE_DESCRIPTION), null)); + disposalSchedule.setMandate(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_SCHEDULE_MANDATE), null)); + disposalSchedule + .setScopeNotes(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_SCHEDULE_SCOPE_NOTES), null)); + DisposalScheduleState disposalScheduleState = SolrUtils.objectToEnum(doc.get(RodaConstants.DISPOSAL_SCHEDULE_STATE), + DisposalScheduleState.class, DisposalScheduleState.getDefault()); + disposalSchedule.setState(disposalScheduleState); + disposalSchedule.setRetentionPeriodDuration( + SolrUtils.objectToInteger(doc.get(RodaConstants.DISPOSAL_SCHEDULE_RETENTION_PERIOD_DURATION), null)); + RetentionPeriodIntervalCode retentionPeriodIntervalCode = SolrUtils.objectToEnum( + doc.get(RodaConstants.DISPOSAL_SCHEDULE_RETENTION_PERIOD_INTERVAL_CODE), RetentionPeriodIntervalCode.class, + RetentionPeriodIntervalCode.getDefault()); + disposalSchedule.setRetentionPeriodIntervalCode(retentionPeriodIntervalCode); + DisposalActionCode actionCode = SolrUtils.objectToEnum(doc.get(RodaConstants.DISPOSAL_SCHEDULE_ACTION), + DisposalActionCode.class, null); + disposalSchedule.setActionCode(actionCode); + + return disposalSchedule; + } +} 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..20116411ad 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)); @@ -4479,6 +4510,7 @@ public DisposalHold createDisposalHold(DisposalHold disposalHold, String created String disposalHoldAsJson = JsonUtils.getJsonFromObject(newDisposalHold); StoragePath disposalHoldPath = ModelUtils.getDisposalHoldStoragePath(newDisposalHold.getId()); storage.createBinary(disposalHoldPath, new StringContentPayload(disposalHoldAsJson), false); + notifyDisposalHoldCreatedOrUpdated(newDisposalHold).failOnError(); return newDisposalHold; } @@ -4529,6 +4561,7 @@ public DisposalHold updateDisposalHold(DisposalHold disposalHold, String updated storage.updateBinaryContent(disposalHoldPath, new StringContentPayload(disposalHoldAsJson), false, true, false, null); + notifyDisposalHoldCreatedOrUpdated(currentDisposalHold).failOnError(); createRepositoryEvent(PreservationEventType.UPDATE, "Update disposal hold", PluginState.SUCCESS, "", details, "", true, null); @@ -4546,6 +4579,7 @@ public void deleteDisposalHold(String disposalHoldId) throws RequestNotValidExce if (disposalHold.getFirstTimeUsed() == null) { StoragePath disposalHoldPath = ModelUtils.getDisposalHoldStoragePath(disposalHold.getId()); storage.deleteResource(disposalHoldPath); + notifyDisposalHoldDeleted(disposalHoldId, false); } else { throw new IllegalOperationException("Error deleting disposal hold: " + disposalHold.getId() + ". Reason: One or more AIPs where associated under this disposal hold"); @@ -4678,6 +4712,7 @@ public DisposalSchedule createDisposalSchedule(DisposalSchedule disposalSchedule String disposalScheduleAsJson = JsonUtils.getJsonFromObject(disposalSchedule); StoragePath disposalSchedulePath = ModelUtils.getDisposalScheduleStoragePath(disposalSchedule.getId()); storage.createBinary(disposalSchedulePath, new StringContentPayload(disposalScheduleAsJson), false); + notifyDisposalScheduleCreatedOrUpdated(disposalSchedule).failOnError(); return disposalSchedule; } @@ -4723,6 +4758,7 @@ public DisposalSchedule updateDisposalSchedule(DisposalSchedule disposalSchedule StoragePath disposalSchedulePath = ModelUtils.getDisposalScheduleStoragePath(currentDisposalSchedule.getId()); storage.updateBinaryContent(disposalSchedulePath, new StringContentPayload(disposalScheduleAsJson), false, false, false, null); + notifyDisposalScheduleCreatedOrUpdated(currentDisposalSchedule).failOnError(); return currentDisposalSchedule; } @@ -4782,7 +4818,7 @@ public void deleteDisposalSchedule(String disposalScheduleId) throws NotFoundExc // if so, block the action and keep the disposal schedule if (retrieveDisposalSchedule(disposalScheduleId).getFirstTimeUsed() != null) { throw new IllegalOperationException("Error deleting disposal schedule: " + disposalScheduleId - + ". Reason: One or more AIPs where destroyed under this disposal schedule"); + + ". Reason: This disposal schedule was already associated to an AIP or used to destroy on ore more AIP"); } // check if the disposal schedule is being used in a disposal rule @@ -4800,6 +4836,7 @@ public void deleteDisposalSchedule(String disposalScheduleId) throws NotFoundExc } storage.deleteResource(disposalSchedulePath); + notifyDisposalScheduleDeleted(disposalScheduleId, false).failOnError(); } /********************************** @@ -5048,6 +5085,7 @@ public DisposalRule createDisposalRule(DisposalRule disposalRule, String created String disposalRuleAsJson = JsonUtils.getJsonFromObject(disposalRule); StoragePath disposalRulePath = ModelUtils.getDisposalRuleStoragePath(disposalRule.getId()); storage.createBinary(disposalRulePath, new StringContentPayload(disposalRuleAsJson), false); + notifyDisposalRuleCreatedOrUpdated(disposalRule).failOnError(); return disposalRule; } @@ -5064,6 +5102,7 @@ public DisposalRule updateDisposalRule(DisposalRule disposalRule, String updated StoragePath disposalRulePath = ModelUtils.getDisposalRuleStoragePath(disposalRule.getId()); storage.updateBinaryContent(disposalRulePath, new StringContentPayload(disposalRuleAsJson), false, false, false, null); + notifyDisposalRuleCreatedOrUpdated(disposalRule).failOnError(); return disposalRule; } @@ -5075,6 +5114,7 @@ public void deleteDisposalRule(String disposalRuleId, String updatedBy) StoragePath disposalRulePath = ModelUtils.getDisposalRuleStoragePath(disposalRuleId); storage.deleteResource(disposalRulePath); + notifyDisposalRuleDeleted(disposalRuleId, false); DisposalRules disposalRules = listDisposalRules(); int index = 0; @@ -5266,9 +5306,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 +5320,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; } @@ -5730,6 +5772,36 @@ public ReturnWithExceptionsWrapper notifyDisposalConfirmationDeleted(String disp return notifyObserversSafely(observer -> observer.disposalConfirmationDeleted(disposalConfirmationId, commit)); } + @Override + public ReturnWithExceptionsWrapper notifyDisposalScheduleCreatedOrUpdated(DisposalSchedule schedule) { + return notifyObserversSafely(observer -> observer.disposalScheduleCreatedOrUpdated(schedule)); + } + + @Override + public ReturnWithExceptionsWrapper notifyDisposalScheduleDeleted(String disposalScheduleId, boolean commit) { + return notifyObserversSafely(observer -> observer.disposalScheduleDeleted(disposalScheduleId, commit)); + } + + @Override + public ReturnWithExceptionsWrapper notifyDisposalHoldCreatedOrUpdated(DisposalHold hold) { + return notifyObserversSafely(observer -> observer.disposalHoldCreatedOrUpdated(hold)); + } + + @Override + public ReturnWithExceptionsWrapper notifyDisposalHoldDeleted(String disposalHoldId, boolean commit) { + return notifyObserversSafely(observer -> observer.disposalHoldDeleted(disposalHoldId, commit)); + } + + @Override + public ReturnWithExceptionsWrapper notifyDisposalRuleCreatedOrUpdated(DisposalRule rule) { + return notifyObserversSafely(observer -> observer.disposalRuleCreatedOrUpdated(rule)); + } + + @Override + public ReturnWithExceptionsWrapper notifyDisposalRuleDeleted(String disposalRuleId, boolean commit) { + return notifyObserversSafely(observer -> observer.disposalRuleDeleted(disposalRuleId, commit)); + } + /************************************ * Storage Utils ************************************/ 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..32ab73b5e7 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); @@ -4483,6 +4490,36 @@ public ReturnWithExceptionsWrapper notifyDisposalConfirmationDeleted(String disp return getModelService().notifyDisposalConfirmationDeleted(disposalConfirmationId, commit); } + @Override + public ReturnWithExceptionsWrapper notifyDisposalScheduleCreatedOrUpdated(DisposalSchedule schedule) { + return getModelService().notifyDisposalScheduleCreatedOrUpdated(schedule); + } + + @Override + public ReturnWithExceptionsWrapper notifyDisposalScheduleDeleted(String disposalScheduleId, boolean commit) { + return getModelService().notifyDisposalScheduleDeleted(disposalScheduleId, commit); + } + + @Override + public ReturnWithExceptionsWrapper notifyDisposalHoldCreatedOrUpdated(DisposalHold hold) { + return getModelService().notifyDisposalHoldCreatedOrUpdated(hold); + } + + @Override + public ReturnWithExceptionsWrapper notifyDisposalHoldDeleted(String disposalHoldId, boolean commit) { + return getModelService().notifyDisposalHoldDeleted(disposalHoldId, commit); + } + + @Override + public ReturnWithExceptionsWrapper notifyDisposalRuleCreatedOrUpdated(DisposalRule rule) { + return getModelService().notifyDisposalRuleCreatedOrUpdated(rule); + } + + @Override + public ReturnWithExceptionsWrapper notifyDisposalRuleDeleted(String disposalRuleId, boolean commit) { + return getModelService().notifyDisposalRuleDeleted(disposalRuleId, commit); + } + @Override public void commit() throws RODATransactionException { for (TransactionalModelOperationLog modelOperation : transactionLogService diff --git a/roda-core/roda-core/src/main/java/org/roda/core/model/LiteRODAObjectFactory.java b/roda-core/roda-core/src/main/java/org/roda/core/model/LiteRODAObjectFactory.java index 906fe14ea8..c21b9b4a88 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/model/LiteRODAObjectFactory.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/model/LiteRODAObjectFactory.java @@ -195,6 +195,12 @@ public static Optional get(T object) { ret = getIndexedPreservationAgent(object); } else if (object instanceof DisposalHold) { ret = get(DisposalHold.class, Arrays.asList(object.getId()), false); + } else if (object instanceof DisposalSchedule) { + ret = get(DisposalSchedule.class, Arrays.asList(object.getId()), false); + } else if (object instanceof DisposalHold) { + ret = get(DisposalHold.class, Arrays.asList(object.getId()), false); + } else if (object instanceof DisposalRule) { + ret = get(DisposalRule.class, Arrays.asList(object.getId()), false); } if (!ret.isPresent()) { diff --git a/roda-core/roda-core/src/main/java/org/roda/core/model/ModelObservable.java b/roda-core/roda-core/src/main/java/org/roda/core/model/ModelObservable.java index bbfb2f6823..5ac3ccf738 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/model/ModelObservable.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/model/ModelObservable.java @@ -9,6 +9,9 @@ import org.roda.core.common.ReturnWithExceptionsWrapper; import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation; +import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.core.data.v2.disposal.rule.DisposalRule; +import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; import org.roda.core.data.v2.ip.AIP; import org.roda.core.data.v2.ip.DIP; import org.roda.core.data.v2.ip.DIPFile; @@ -34,123 +37,133 @@ * @author Gabriel Barros */ public interface ModelObservable { - void addModelObserver(ModelObserver observer); + void addModelObserver(ModelObserver observer); - void removeModelObserver(ModelObserver observer); + void removeModelObserver(ModelObserver observer); - ReturnWithExceptionsWrapper notifyAipCreated(AIP aip); + ReturnWithExceptionsWrapper notifyAipCreated(AIP aip); - ReturnWithExceptionsWrapper notifyAipUpdated(AIP aip); + ReturnWithExceptionsWrapper notifyAipUpdated(AIP aip); - ReturnWithExceptionsWrapper notifyAipUpdatedOnChanged(AIP aip); + ReturnWithExceptionsWrapper notifyAipUpdatedOnChanged(AIP aip); - ReturnWithExceptionsWrapper notifyAipOnHoldStatusUpdated(AIP aip, boolean status); + ReturnWithExceptionsWrapper notifyAipOnHoldStatusUpdated(AIP aip, boolean status); - ReturnWithExceptionsWrapper notifyAipDestroyed(AIP aip); + ReturnWithExceptionsWrapper notifyAipDestroyed(AIP aip); - ReturnWithExceptionsWrapper notifyAipMoved(AIP aip, String oldParentId, String newParentId); + ReturnWithExceptionsWrapper notifyAipMoved(AIP aip, String oldParentId, String newParentId); - ReturnWithExceptionsWrapper notifyAipStateUpdated(AIP aip); + ReturnWithExceptionsWrapper notifyAipStateUpdated(AIP aip); - ReturnWithExceptionsWrapper notifyAipInstanceIdUpdated(AIP aip); + ReturnWithExceptionsWrapper notifyAipInstanceIdUpdated(AIP aip); - ReturnWithExceptionsWrapper notifyAipDeleted(String aipId); + ReturnWithExceptionsWrapper notifyAipDeleted(String aipId); - ReturnWithExceptionsWrapper notifyDescriptiveMetadataCreated(DescriptiveMetadata descriptiveMetadata); + ReturnWithExceptionsWrapper notifyDescriptiveMetadataCreated(DescriptiveMetadata descriptiveMetadata); - ReturnWithExceptionsWrapper notifyDescriptiveMetadataUpdated(DescriptiveMetadata descriptiveMetadata); + ReturnWithExceptionsWrapper notifyDescriptiveMetadataUpdated(DescriptiveMetadata descriptiveMetadata); - ReturnWithExceptionsWrapper notifyDescriptiveMetadataDeleted(String aipId, String representationId, - String descriptiveMetadataBinaryId); + ReturnWithExceptionsWrapper notifyDescriptiveMetadataDeleted(String aipId, String representationId, + String descriptiveMetadataBinaryId); - ReturnWithExceptionsWrapper notifyRepresentationCreated(Representation representation); + ReturnWithExceptionsWrapper notifyRepresentationCreated(Representation representation); - ReturnWithExceptionsWrapper notifyRepresentationUpdated(Representation representation); + ReturnWithExceptionsWrapper notifyRepresentationUpdated(Representation representation); - ReturnWithExceptionsWrapper notifyRepresentationDeleted(String aipId, String representationId); + ReturnWithExceptionsWrapper notifyRepresentationDeleted(String aipId, String representationId); - ReturnWithExceptionsWrapper notifyRepresentationUpdatedOnChanged(Representation representation); + ReturnWithExceptionsWrapper notifyRepresentationUpdatedOnChanged(Representation representation); - ReturnWithExceptionsWrapper notifyFileCreated(File file); + ReturnWithExceptionsWrapper notifyFileCreated(File file); - ReturnWithExceptionsWrapper notifyFileUpdated(File file); + ReturnWithExceptionsWrapper notifyFileUpdated(File file); - ReturnWithExceptionsWrapper notifyFileDeleted(String aipId, String representationId, - List fileDirectoryPath, String fileId); + ReturnWithExceptionsWrapper notifyFileDeleted(String aipId, String representationId, List fileDirectoryPath, + String fileId); - ReturnWithExceptionsWrapper notifyLogEntryCreated(LogEntry entry); + ReturnWithExceptionsWrapper notifyLogEntryCreated(LogEntry entry); - ReturnWithExceptionsWrapper notifyUserCreated(User user); + ReturnWithExceptionsWrapper notifyUserCreated(User user); - ReturnWithExceptionsWrapper notifyUserUpdated(User user); + ReturnWithExceptionsWrapper notifyUserUpdated(User user); - ReturnWithExceptionsWrapper notifyUserDeleted(String userID); + ReturnWithExceptionsWrapper notifyUserDeleted(String userID); - ReturnWithExceptionsWrapper notifyGroupCreated(Group group); + ReturnWithExceptionsWrapper notifyGroupCreated(Group group); - ReturnWithExceptionsWrapper notifyGroupUpdated(Group group); + ReturnWithExceptionsWrapper notifyGroupUpdated(Group group); - ReturnWithExceptionsWrapper notifyGroupDeleted(String groupID); + ReturnWithExceptionsWrapper notifyGroupDeleted(String groupID); - ReturnWithExceptionsWrapper notifyPreservationMetadataCreated( - PreservationMetadata preservationMetadataBinary); + ReturnWithExceptionsWrapper notifyPreservationMetadataCreated(PreservationMetadata preservationMetadataBinary); - ReturnWithExceptionsWrapper notifyPreservationMetadataUpdated( - PreservationMetadata preservationMetadataBinary); + ReturnWithExceptionsWrapper notifyPreservationMetadataUpdated(PreservationMetadata preservationMetadataBinary); - ReturnWithExceptionsWrapper notifyPreservationMetadataDeleted(PreservationMetadata pm); + ReturnWithExceptionsWrapper notifyPreservationMetadataDeleted(PreservationMetadata pm); - ReturnWithExceptionsWrapper notifyOtherMetadataCreated(OtherMetadata otherMetadataBinary); + ReturnWithExceptionsWrapper notifyOtherMetadataCreated(OtherMetadata otherMetadataBinary); - ReturnWithExceptionsWrapper notifyJobCreatedOrUpdated(Job job, boolean reindexJobReports); + ReturnWithExceptionsWrapper notifyJobCreatedOrUpdated(Job job, boolean reindexJobReports); - ReturnWithExceptionsWrapper notifyJobDeleted(String jobId); + ReturnWithExceptionsWrapper notifyJobDeleted(String jobId); - ReturnWithExceptionsWrapper notifyJobReportCreatedOrUpdated(Report jobReport, Job cachedJob); + ReturnWithExceptionsWrapper notifyJobReportCreatedOrUpdated(Report jobReport, Job cachedJob); - ReturnWithExceptionsWrapper notifyJobReportCreatedOrUpdated(Report jobReport, IndexedJob indexedJob); + ReturnWithExceptionsWrapper notifyJobReportCreatedOrUpdated(Report jobReport, IndexedJob indexedJob); - ReturnWithExceptionsWrapper notifyJobReportDeleted(String jobReportId); + ReturnWithExceptionsWrapper notifyJobReportDeleted(String jobReportId); - ReturnWithExceptionsWrapper notifyAipPermissionsUpdated(AIP aip); + ReturnWithExceptionsWrapper notifyAipPermissionsUpdated(AIP aip); - ReturnWithExceptionsWrapper notifyDipPermissionsUpdated(DIP dip); + ReturnWithExceptionsWrapper notifyDipPermissionsUpdated(DIP dip); - ReturnWithExceptionsWrapper notifyDipInstanceIdUpdated(DIP dip); + ReturnWithExceptionsWrapper notifyDipInstanceIdUpdated(DIP dip); - ReturnWithExceptionsWrapper notifyTransferredResourceDeleted(String transferredResourceID); + ReturnWithExceptionsWrapper notifyTransferredResourceDeleted(String transferredResourceID); - ReturnWithExceptionsWrapper notifyRiskCreatedOrUpdated(Risk risk, int incidences, boolean commit); + ReturnWithExceptionsWrapper notifyRiskCreatedOrUpdated(Risk risk, int incidences, boolean commit); - ReturnWithExceptionsWrapper notifyRiskDeleted(String riskId, boolean commit); + ReturnWithExceptionsWrapper notifyRiskDeleted(String riskId, boolean commit); - ReturnWithExceptionsWrapper notifyRiskIncidenceCreatedOrUpdated(RiskIncidence riskIncidence, boolean commit); + ReturnWithExceptionsWrapper notifyRiskIncidenceCreatedOrUpdated(RiskIncidence riskIncidence, boolean commit); - ReturnWithExceptionsWrapper notifyRiskIncidenceDeleted(String riskIncidenceId, boolean commit); + ReturnWithExceptionsWrapper notifyRiskIncidenceDeleted(String riskIncidenceId, boolean commit); - ReturnWithExceptionsWrapper notifyRepresentationInformationCreatedOrUpdated(RepresentationInformation ri, - boolean commit); + ReturnWithExceptionsWrapper notifyRepresentationInformationCreatedOrUpdated(RepresentationInformation ri, + boolean commit); - ReturnWithExceptionsWrapper notifyRepresentationInformationDeleted(String representationInformationId, - boolean commit); + ReturnWithExceptionsWrapper notifyRepresentationInformationDeleted(String representationInformationId, + boolean commit); - ReturnWithExceptionsWrapper notifyNotificationCreatedOrUpdated(Notification notification); + ReturnWithExceptionsWrapper notifyNotificationCreatedOrUpdated(Notification notification); - ReturnWithExceptionsWrapper notifyNotificationDeleted(String notificationId); + ReturnWithExceptionsWrapper notifyNotificationDeleted(String notificationId); - ReturnWithExceptionsWrapper notifyDIPCreated(DIP dip, boolean commit); + ReturnWithExceptionsWrapper notifyDIPCreated(DIP dip, boolean commit); - ReturnWithExceptionsWrapper notifyDIPUpdated(DIP dip, boolean commit); + ReturnWithExceptionsWrapper notifyDIPUpdated(DIP dip, boolean commit); - ReturnWithExceptionsWrapper notifyDIPDeleted(String dipId, boolean commit); + ReturnWithExceptionsWrapper notifyDIPDeleted(String dipId, boolean commit); - ReturnWithExceptionsWrapper notifyDIPFileCreated(DIPFile file); + ReturnWithExceptionsWrapper notifyDIPFileCreated(DIPFile file); - ReturnWithExceptionsWrapper notifyDIPFileUpdated(DIPFile file); + ReturnWithExceptionsWrapper notifyDIPFileUpdated(DIPFile file); - ReturnWithExceptionsWrapper notifyDIPFileDeleted(String dipId, List path, String fileId); + ReturnWithExceptionsWrapper notifyDIPFileDeleted(String dipId, List path, String fileId); - ReturnWithExceptionsWrapper notifyDisposalConfirmationCreatedOrUpdated(DisposalConfirmation confirmation); + ReturnWithExceptionsWrapper notifyDisposalConfirmationCreatedOrUpdated(DisposalConfirmation confirmation); - ReturnWithExceptionsWrapper notifyDisposalConfirmationDeleted(String disposalConfirmationId, boolean commit); + ReturnWithExceptionsWrapper notifyDisposalConfirmationDeleted(String disposalConfirmationId, boolean commit); + + ReturnWithExceptionsWrapper notifyDisposalScheduleCreatedOrUpdated(DisposalSchedule schedule); + + ReturnWithExceptionsWrapper notifyDisposalScheduleDeleted(String disposalScheduleId, boolean commit); + + ReturnWithExceptionsWrapper notifyDisposalHoldCreatedOrUpdated(DisposalHold hold); + + ReturnWithExceptionsWrapper notifyDisposalHoldDeleted(String disposalHoldId, boolean commit); + + ReturnWithExceptionsWrapper notifyDisposalRuleCreatedOrUpdated(DisposalRule rule); + + ReturnWithExceptionsWrapper notifyDisposalRuleDeleted(String disposalRuleId, boolean commit); } diff --git a/roda-core/roda-core/src/main/java/org/roda/core/model/ModelObserver.java b/roda-core/roda-core/src/main/java/org/roda/core/model/ModelObserver.java index bdd62307e3..9cb01ee8f7 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/model/ModelObserver.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/model/ModelObserver.java @@ -12,6 +12,9 @@ import org.roda.core.data.exceptions.ReturnWithExceptions; import org.roda.core.data.v2.LiteRODAObject; import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation; +import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.core.data.v2.disposal.rule.DisposalRule; +import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; import org.roda.core.data.v2.ip.AIP; import org.roda.core.data.v2.ip.DIP; import org.roda.core.data.v2.ip.DIPFile; @@ -155,6 +158,18 @@ public ReturnWithExceptions disposalConfirmationCreateOrUpd public ReturnWithExceptions disposalConfirmationDeleted(String confirmationId, boolean commit); + ReturnWithExceptions disposalScheduleCreatedOrUpdated(DisposalSchedule schedule); + + ReturnWithExceptions disposalScheduleDeleted(String scheduleId, boolean commit); + + ReturnWithExceptions disposalHoldCreatedOrUpdated(DisposalHold hold); + + ReturnWithExceptions disposalHoldDeleted(String holdId, boolean commit); + + ReturnWithExceptions disposalRuleCreatedOrUpdated(DisposalRule rule); + + ReturnWithExceptions disposalRuleDeleted(String ruleId, boolean commit); + public ReturnWithExceptions liteRODAObjectCreated(LiteRODAObject liteRODAObject); public ReturnWithExceptions liteRODAObjectUpdated(LiteRODAObject liteRODAObject); 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/TransactionalModelService.java b/roda-core/roda-core/src/main/java/org/roda/core/model/TransactionalModelService.java index e014849c21..8bd0260f2e 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/model/TransactionalModelService.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/model/TransactionalModelService.java @@ -7,10 +7,19 @@ */ package org.roda.core.model; +import org.roda.core.data.exceptions.AuthorizationDeniedException; +import org.roda.core.data.exceptions.GenericException; +import org.roda.core.data.exceptions.NotFoundException; +import org.roda.core.data.exceptions.RequestNotValidException; +import org.roda.core.data.v2.jobs.Job; import org.roda.core.transaction.TransactionalService; /** * @author Gabriel Barros */ public interface TransactionalModelService extends ModelService, TransactionalService { + @Override + default void createOrUpdateJob(Job job) throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException { + + } } 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/java/org/roda/core/model/utils/ModelUtils.java b/roda-core/roda-core/src/main/java/org/roda/core/model/utils/ModelUtils.java index 06652ca493..168d5bc67c 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/model/utils/ModelUtils.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/model/utils/ModelUtils.java @@ -32,6 +32,7 @@ import org.roda.core.data.v2.common.OptionalWithCause; import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation; import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.core.data.v2.disposal.rule.DisposalRule; import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; import org.roda.core.data.v2.index.select.SelectedItems; import org.roda.core.data.v2.index.select.SelectedItemsFilter; @@ -718,6 +719,18 @@ public static String getJobId(StoragePath jobPath) { return jobPath.getName().replace(RodaConstants.JOB_FILE_EXTENSION, ""); } + public static String getDisposalScheduleId(StoragePath disposalSchedulePath) { + return disposalSchedulePath.getName().replace(RodaConstants.DISPOSAL_SCHEDULE_FILE_EXTENSION, ""); + } + + public static String getDisposalHoldId(StoragePath disposalHoldPath) { + return disposalHoldPath.getName().replace(RodaConstants.DISPOSAL_HOLD_FILE_EXTENSION, ""); + } + + public static String getDisposalRuleId(StoragePath disposalRulePath) { + return disposalRulePath.getName().replace(RodaConstants.DISPOSAL_RULE_FILE_EXTENSION, ""); + } + public static StoragePath getJobReportContainerPath() throws RequestNotValidException { return DefaultStoragePath.parse(RodaConstants.STORAGE_CONTAINER_JOB_REPORT); } @@ -1051,6 +1064,8 @@ public static StoragePath getContainerPath(Class cla return getDisposalScheduleContainerPath(); } else if (clazz.equals(DisposalHold.class)) { return getDisposalHoldContainerPath(); + } else if (clazz.equals(DisposalRule.class)) { + return getDisposalRuleContainerPath(); } else if (clazz.equals(DisposalConfirmation.class)) { return getDisposalConfirmationContainerPath(); } else if (clazz.equals(DistributedInstance.class)) { diff --git a/roda-core/roda-core/src/main/java/org/roda/core/model/utils/ResourceParseUtils.java b/roda-core/roda-core/src/main/java/org/roda/core/model/utils/ResourceParseUtils.java index 4efe7bf847..f5949b0016 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/model/utils/ResourceParseUtils.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/model/utils/ResourceParseUtils.java @@ -37,6 +37,9 @@ import org.roda.core.data.v2.LiteRODAObject; import org.roda.core.data.v2.common.OptionalWithCause; import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation; +import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.core.data.v2.disposal.rule.DisposalRule; +import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; import org.roda.core.data.v2.ip.AIP; import org.roda.core.data.v2.ip.DIP; import org.roda.core.data.v2.ip.DIPFile; @@ -387,7 +390,14 @@ public static OptionalWithCause convert } ids.add(fileName.replace(RodaConstants.PREMIS_SUFFIX, "")); ret = OptionalWithCause.of(LiteRODAObjectFactory.get(classToReturn, ids)); - } else { + } else if (classToReturn.equals(DisposalSchedule.class)) { + ret = OptionalWithCause.of(LiteRODAObjectFactory.get(classToReturn, ModelUtils.getDisposalScheduleId(storagePath))); + } else if (classToReturn.equals(DisposalHold.class)) { + ret = OptionalWithCause.of(LiteRODAObjectFactory.get(classToReturn, ModelUtils.getDisposalHoldId(storagePath))); + } else if (classToReturn.equals(DisposalRule.class)) { + ret = OptionalWithCause.of(LiteRODAObjectFactory.get(classToReturn, ModelUtils.getDisposalRuleId(storagePath))); + } + else { ret = OptionalWithCause.of(LiteRODAObjectFactory.get(classToReturn, fileName)); } diff --git a/roda-core/roda-core/src/main/java/org/roda/core/plugins/PluginHelper.java b/roda-core/roda-core/src/main/java/org/roda/core/plugins/PluginHelper.java index a5826ab416..e07ca01c9c 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/plugins/PluginHelper.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/plugins/PluginHelper.java @@ -54,6 +54,9 @@ import org.roda.core.data.v2.Void; import org.roda.core.data.v2.common.OptionalWithCause; import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation; +import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.core.data.v2.disposal.rule.DisposalRule; +import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; import org.roda.core.data.v2.index.IndexResult; import org.roda.core.data.v2.index.facet.Facets; import org.roda.core.data.v2.index.filter.Filter; @@ -97,6 +100,9 @@ import org.roda.core.plugins.base.maintenance.reindex.ReindexActionLogPlugin; import org.roda.core.plugins.base.maintenance.reindex.ReindexDIPPlugin; import org.roda.core.plugins.base.maintenance.reindex.ReindexDisposalConfirmationPlugin; +import org.roda.core.plugins.base.maintenance.reindex.ReindexDisposalHoldPlugin; +import org.roda.core.plugins.base.maintenance.reindex.ReindexDisposalRulePlugin; +import org.roda.core.plugins.base.maintenance.reindex.ReindexDisposalSchedulePlugin; import org.roda.core.plugins.base.maintenance.reindex.ReindexIncidencePlugin; import org.roda.core.plugins.base.maintenance.reindex.ReindexJobPlugin; import org.roda.core.plugins.base.maintenance.reindex.ReindexNotificationPlugin; @@ -731,6 +737,9 @@ public static List> getReindexObjectClasses() { list.add(IndexedPreservationAgent.class); list.add(DIP.class); list.add(DisposalConfirmation.class); + list.add(DisposalSchedule.class); + list.add(DisposalHold.class); + list.add(DisposalRule.class); return list; } @@ -1605,6 +1614,12 @@ public static String getReindexPluginName(Class reindexClass) throws NotFound return ReindexDIPPlugin.class.getName(); } else if (reindexClass.equals(DisposalConfirmation.class)) { return ReindexDisposalConfirmationPlugin.class.getName(); + } else if (reindexClass.equals(DisposalSchedule.class)) { + return ReindexDisposalSchedulePlugin.class.getName(); + } else if (reindexClass.equals(DisposalHold.class)) { + return ReindexDisposalHoldPlugin.class.getName(); + } else if (reindexClass.equals(DisposalRule.class)) { + return ReindexDisposalRulePlugin.class.getName(); } else { throw new NotFoundException("No reindex plugin available"); } diff --git a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/disposal/hold/LiftDisposalHoldPlugin.java b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/disposal/hold/LiftDisposalHoldPlugin.java index 20d7d98840..ad793519f5 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/disposal/hold/LiftDisposalHoldPlugin.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/disposal/hold/LiftDisposalHoldPlugin.java @@ -25,7 +25,6 @@ import org.roda.core.data.exceptions.RequestNotValidException; import org.roda.core.data.v2.LiteOptionalWithCause; import org.roda.core.data.v2.Void; -import org.roda.core.data.v2.common.Pair; import org.roda.core.data.v2.disposal.hold.DisposalHold; import org.roda.core.data.v2.disposal.hold.DisposalHoldState; import org.roda.core.data.v2.index.filter.Filter; @@ -195,7 +194,8 @@ private void liftDisposalHold(IndexService index, ModelService model, Report rep try { AIP aip = model.retrieveAIP(indexedAIP.getId()); - long children = processTransitiveAIP(model, index, cachedJob, aip.getId(), disposalHoldId, jobPluginInfo, report); + long children = processTransitiveAIP(model, index, cachedJob, aip.getId(), disposalHoldId, jobPluginInfo, + report); model.updateAIPOnHoldStatus(aip, model.onDisposalHold(aip.getId())); jobPluginInfo.incrementObjectsProcessedWithSuccess(); reportItem.setPluginState(state); diff --git a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/maintenance/reindex/ReindexDisposalHoldPlugin.java b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/maintenance/reindex/ReindexDisposalHoldPlugin.java new file mode 100644 index 0000000000..6152088658 --- /dev/null +++ b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/maintenance/reindex/ReindexDisposalHoldPlugin.java @@ -0,0 +1,47 @@ +/** + * 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.core.plugins.base.maintenance.reindex; + +import java.util.Collections; +import java.util.List; + +import org.roda.core.data.exceptions.GenericException; +import org.roda.core.data.exceptions.RequestNotValidException; +import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.core.index.IndexService; +import org.roda.core.plugins.Plugin; + +public class ReindexDisposalHoldPlugin extends ReindexRodaEntityPlugin { + + @Override + public String getName() { + return "Rebuild disposal hold index"; + } + + @Override + public String getVersionImpl() { + return "1.0"; + } + + @Override + public Plugin cloneMe() { + return new ReindexDisposalHoldPlugin(); + } + + @Override + public List> getObjectClasses() { + return Collections.singletonList(DisposalHold.class); + } + + @Override + public void clearSpecificIndexes(IndexService index, List ids) + throws GenericException, RequestNotValidException { + // do nothing + } + +} diff --git a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/maintenance/reindex/ReindexDisposalRulePlugin.java b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/maintenance/reindex/ReindexDisposalRulePlugin.java new file mode 100644 index 0000000000..506546f37f --- /dev/null +++ b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/maintenance/reindex/ReindexDisposalRulePlugin.java @@ -0,0 +1,47 @@ +/** + * 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.core.plugins.base.maintenance.reindex; + +import java.util.Collections; +import java.util.List; + +import org.roda.core.data.exceptions.GenericException; +import org.roda.core.data.exceptions.RequestNotValidException; +import org.roda.core.data.v2.disposal.rule.DisposalRule; +import org.roda.core.index.IndexService; +import org.roda.core.plugins.Plugin; + +public class ReindexDisposalRulePlugin extends ReindexRodaEntityPlugin { + + @Override + public String getName() { + return "Rebuild disposal rule index"; + } + + @Override + public String getVersionImpl() { + return "1.0"; + } + + @Override + public Plugin cloneMe() { + return new ReindexDisposalRulePlugin(); + } + + @Override + public List> getObjectClasses() { + return Collections.singletonList(DisposalRule.class); + } + + @Override + public void clearSpecificIndexes(IndexService index, List ids) + throws GenericException, RequestNotValidException { + // do nothing + } + +} diff --git a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/maintenance/reindex/ReindexDisposalSchedulePlugin.java b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/maintenance/reindex/ReindexDisposalSchedulePlugin.java new file mode 100644 index 0000000000..5599bfda47 --- /dev/null +++ b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/maintenance/reindex/ReindexDisposalSchedulePlugin.java @@ -0,0 +1,47 @@ +/** + * 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.core.plugins.base.maintenance.reindex; + +import java.util.Collections; +import java.util.List; + +import org.roda.core.data.exceptions.GenericException; +import org.roda.core.data.exceptions.RequestNotValidException; +import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; +import org.roda.core.index.IndexService; +import org.roda.core.plugins.Plugin; + +public class ReindexDisposalSchedulePlugin extends ReindexRodaEntityPlugin { + + @Override + public String getName() { + return "Rebuild disposal schedule index"; + } + + @Override + public String getVersionImpl() { + return "1.0"; + } + + @Override + public Plugin cloneMe() { + return new ReindexDisposalSchedulePlugin(); + } + + @Override + public List> getObjectClasses() { + return Collections.singletonList(DisposalSchedule.class); + } + + @Override + public void clearSpecificIndexes(IndexService index, List ids) + throws GenericException, RequestNotValidException { + // do nothing + } + +} 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..4f9b9b786d 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 @@ -94,6 +94,7 @@ core.roles.org.roda.wui.api.v2.controller.JobsController.createJob = job.manage core.roles.org.roda.wui.api.v2.controller.JobsController.obtainJobCommand = job.manage # Member roles +core.roles.org.roda.wui.api.v2.controller.MembersController.getMember = member.read core.roles.org.roda.wui.api.v2.controller.MembersController.getUser = member.read core.roles.org.roda.wui.api.v2.controller.MembersController.getGroup = member.read core.roles.org.roda.wui.api.v2.controller.MembersController.deleteMultipleMembers = member.manage @@ -113,6 +114,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 @@ -175,6 +182,7 @@ core.roles.org.roda.wui.api.v2.controller.DisposalRuleController.retrieveDisposa core.roles.org.roda.wui.api.v2.controller.DisposalRuleController.updateDisposalRule = disposal_hold.manage core.roles.org.roda.wui.api.v2.controller.DisposalRuleController.deleteDisposalRule = disposal_rule.manage core.roles.org.roda.wui.api.v2.controller.DisposalRuleController.applyDisposalRules = disposal_schedule.associate +core.roles.org.roda.wui.api.v2.controller.DisposalRuleController.changeDisposalRuleOrder = disposal_rule.manage # Representation information core.roles.org.roda.wui.api.v2.controller.RepresentationInformationController.retrieveRepresentationInformationFamily = ri.read @@ -278,7 +286,6 @@ core.roles.org.roda.wui.api.v2.services.IndexService.find(IndexedReport) = job. core.roles.org.roda.wui.api.v2.services.IndexService.find(IndexedPreservationEvent) = preservation_metadata.read core.roles.org.roda.wui.api.v2.services.IndexService.find(IndexedPreservationAgent) = preservation_metadata.read core.roles.org.roda.wui.api.v2.services.IndexService.find(LogEntry) = log_entry.read -core.roles.org.roda.wui.api.v2.services.IndexService.find(DisposalConfirmation) = disposal_confirmation.read core.roles.org.roda.wui.api.v2.services.IndexService.find(RepresentationInformation) = ri.read core.roles.org.roda.wui.api.v2.services.IndexService.find(RODAMember) = member.read core.roles.org.roda.wui.api.v2.services.IndexService.find(IndexedDIP) = aip.view @@ -286,6 +293,10 @@ core.roles.org.roda.wui.api.v2.services.IndexService.find(IndexedDIP) = aip.read core.roles.org.roda.wui.api.v2.services.IndexService.find(IndexedFile) = representation.read core.roles.org.roda.wui.api.v2.services.IndexService.find(IndexedRepresentation) = representation.read core.roles.org.roda.wui.api.v2.services.IndexService.find(DIPFile) = aip.read +core.roles.org.roda.wui.api.v2.services.IndexService.find(DisposalSchedule) = disposal_schedule.read +core.roles.org.roda.wui.api.v2.services.IndexService.find(DisposalRule) = disposal_rule.read +core.roles.org.roda.wui.api.v2.services.IndexService.find(DisposalHold) = disposal_hold.read +core.roles.org.roda.wui.api.v2.services.IndexService.find(DisposalConfirmation) = disposal_confirmation.read # Generic count roles core.roles.org.roda.wui.api.v2.services.IndexService.count(TransferredResource) = transfer.read @@ -311,6 +322,10 @@ core.roles.org.roda.wui.api.v2.services.IndexService.count(IndexedFile) = aip.vi core.roles.org.roda.wui.api.v2.services.IndexService.count(IndexedFile) = aip.read core.roles.org.roda.wui.api.v2.services.IndexService.count(DIPFile) = aip.read core.roles.org.roda.wui.api.v2.services.IndexService.count(IndexedReport) = job.read +core.roles.org.roda.wui.api.v2.services.IndexService.count(DisposalSchedule) = disposal_schedule.read +core.roles.org.roda.wui.api.v2.services.IndexService.count(DisposalRule) = disposal_rule.read +core.roles.org.roda.wui.api.v2.services.IndexService.count(DisposalHold) = disposal_hold.read +core.roles.org.roda.wui.api.v2.services.IndexService.count(DisposalConfirmation) = disposal_confirmation.read # Generic suggest roles core.roles.org.roda.wui.api.v2.services.IndexService.suggest(IndexedAIP) = aip.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..65974519af 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(); @@ -1016,6 +1018,10 @@ SafeHtml searchPreFilterLongRangeFilterParameterGreaterThan(String searchPreFilt String detailsCreatedBy(); + String detailsUpdatedOn(); + + String detailsUpdatedBy(); + String detailsModifiedOn(); String detailsModifiedBy(); @@ -1100,6 +1106,14 @@ SafeHtml searchPreFilterLongRangeFilterParameterGreaterThan(String searchPreFilt String userRemoveConfirmDialogMessage(); + String singleUserRemoveConfirmDialogTitle(); + + String singleGroupRemoveConfirmDialogTitle(); + + String singleUserRemoveConfirmDialogMessage(String username); + + String singleGroupRemoveConfirmDialogMessage(String username); + // Member management String addUserButton(); @@ -1312,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(); @@ -1389,7 +1408,7 @@ SafeHtml searchPreFilterLongRangeFilterParameterGreaterThan(String searchPreFilt String permissionAssignedUsersEmpty(); - String listOfAIPs(); + String listOfAIPs(); String listOfRepresentations(); @@ -2086,6 +2105,12 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String String disposalScheduleUsedInRule(); + String disposalScheduleRemoveConfirmDialogTitle(); + + String disposalScheduleRemoveConfirmDialogMessage(); + + String disposalScheduleRemovedWithSuccess(); + String disposalHoldIdentifier(); String disposalHoldTitle(); @@ -2128,6 +2153,8 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String String showDisposalScheduleTitle(); + String showDisposalConfirmationTitle(); + String disposalConfirmationTitle(); String disposalConfirmationCreationDate(); @@ -2499,6 +2526,8 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String String createAccessKeyTitle(); + String regenerateAccessKeyTitle(); + String showAccessKeyTitle(); String editAccessKeyTitle(); @@ -2537,6 +2566,8 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String String accessKeySuccessfullyRegenerated(); + String accessKeySuccessfullyDeleted(); + String accessKeySuccessfullyRevoked(); String accessKeyDeleteConfirmationMessage(); @@ -2545,6 +2576,8 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String String accessKeyRegenerateConfirmationMessage(); + String accessKeyExpirationDateInThePast(); + /** Market **/ String marketPluginsActionsTabLabel(); @@ -2640,6 +2673,63 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String String reasonCantActOnGroup(); + String reasonHoldAlreadyLifted(); + + // 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(); @@ -2667,4 +2757,40 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String String emailViewerLoadImagesOnce(); String emailViewerAlwaysTrustSender(); + + String disposalHoldSuccessfullyCreated(); + + String disposalHoldSuccessfullyUpdated(); + + String updateDisposalRuleOrderSuccessMessage(); + + String moveToTop(); + + String moveToBottom(); + + String moveToPositionNumber(); + + String disposalRuleChangeOrderMessage(); + + String disposalRuleSuccessfullyCreated(); + + String disposalRuleSuccessfullyUpdated(); + + String userSuccessfullyCreated(); + + String userSuccessfullyUpdated(); + + String groupSuccessfullyCreated(); + + String groupSuccessfullyUpdated(); + + String failedToUpdateGroup(); + + String failedToUpdateUser(); + + String disposalScheduleSuccessfullyCreated(); + + String disposalScheduleSuccessfullyUpdated(); + + String disposalPolicyOverdueRecordsTitle(); } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalHoldController.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalHoldController.java index 432de2aba1..1b74633d2c 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalHoldController.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalHoldController.java @@ -8,7 +8,10 @@ package org.roda.wui.api.v2.controller; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import jakarta.servlet.http.HttpServletRequest; import org.roda.core.data.common.RodaConstants; import org.roda.core.data.exceptions.RODAException; import org.roda.core.data.utils.JsonUtils; @@ -16,17 +19,25 @@ import org.roda.core.data.v2.disposal.hold.DisposalHolds; import org.roda.core.data.v2.disposal.metadata.DisposalHoldsAIPMetadata; import org.roda.core.data.v2.disposal.metadata.DisposalTransitiveHoldsAIPMetadata; +import org.roda.core.data.v2.generics.LongResponse; import org.roda.core.data.v2.generics.select.SelectedItemsRequest; +import org.roda.core.data.v2.index.CountRequest; +import org.roda.core.data.v2.index.FindRequest; +import org.roda.core.data.v2.index.IndexResult; +import org.roda.core.data.v2.index.SuggestRequest; import org.roda.core.data.v2.ip.IndexedAIP; import org.roda.core.data.v2.ip.disposalhold.DisassociateDisposalHoldRequest; import org.roda.core.data.v2.ip.disposalhold.UpdateDisposalHoldRequest; import org.roda.core.data.v2.jobs.Job; +import org.roda.core.model.utils.UserUtility; import org.roda.wui.api.v2.exceptions.RESTException; import org.roda.wui.api.v2.services.DisposalHoldService; +import org.roda.wui.api.v2.services.IndexService; import org.roda.wui.api.v2.utils.CommonServicesUtils; import org.roda.wui.client.services.DisposalHoldRestService; import org.roda.wui.common.RequestControllerAssistant; import org.roda.wui.common.model.RequestContext; +import org.roda.wui.common.utils.RequestUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -40,6 +51,11 @@ @RestController @RequestMapping(path = "/api/v2/disposal/holds") public class DisposalHoldController implements DisposalHoldRestService { + @Autowired + HttpServletRequest request; + + @Autowired + IndexService indexService; @Autowired DisposalHoldService disposalHoldService; @@ -189,4 +205,29 @@ public DisposalHoldsAIPMetadata process(RequestContext requestContext, } }); } + + @Override + public DisposalHold findByUuid(String uuid, String localeString) { + return indexService.retrieve(DisposalHold.class, uuid, new ArrayList<>()); + } + + @Override + public IndexResult find(@RequestBody FindRequest findRequest, String localeString) { + return indexService.find(DisposalHold.class, findRequest, localeString); + } + + @Override + public LongResponse count(@RequestBody CountRequest countRequest) { + RequestContext requestContext = RequestUtils.parseHTTPRequest(request); + if (UserUtility.hasPermissions(requestContext.getUser(), RodaConstants.PERMISSION_METHOD_FIND_FILE)) { + return new LongResponse(indexService.count(DisposalHold.class, countRequest)); + } else { + return new LongResponse(-1L); + } + } + + @Override + public List suggest(@RequestBody SuggestRequest suggestRequest) { + return indexService.suggest(suggestRequest, DisposalHold.class); + } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalRuleController.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalRuleController.java index b1cbffea16..9ac2beda40 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalRuleController.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalRuleController.java @@ -8,18 +8,32 @@ package org.roda.wui.api.v2.controller; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import jakarta.servlet.http.HttpServletRequest; import org.roda.core.data.common.RodaConstants; import org.roda.core.data.exceptions.RODAException; import org.roda.core.data.utils.JsonUtils; +import org.roda.core.data.v2.disposal.rule.ChangeOrderRequest; import org.roda.core.data.v2.disposal.rule.DisposalRule; import org.roda.core.data.v2.disposal.rule.DisposalRules; +import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; +import org.roda.core.data.v2.generics.LongResponse; +import org.roda.core.data.v2.index.CountRequest; +import org.roda.core.data.v2.index.FindRequest; +import org.roda.core.data.v2.index.IndexResult; +import org.roda.core.data.v2.index.SuggestRequest; import org.roda.core.data.v2.jobs.Job; +import org.roda.core.model.utils.UserUtility; import org.roda.wui.api.v2.exceptions.RESTException; import org.roda.wui.api.v2.services.DisposalRuleService; +import org.roda.wui.api.v2.services.DisposalScheduleService; +import org.roda.wui.api.v2.services.IndexService; import org.roda.wui.client.services.DisposalRuleRestService; import org.roda.wui.common.RequestControllerAssistant; import org.roda.wui.common.model.RequestContext; +import org.roda.wui.common.utils.RequestUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -33,10 +47,18 @@ @RestController @RequestMapping(path = "/api/v2/disposal/rules") public class DisposalRuleController implements DisposalRuleRestService { + @Autowired + HttpServletRequest request; + + @Autowired + IndexService indexService; @Autowired DisposalRuleService disposalRuleService; + @Autowired + DisposalScheduleService disposalScheduleService; + @Autowired RequestHandler requestHandler; @@ -56,12 +78,17 @@ public DisposalRule retrieveDisposalRule(String id) { return requestHandler.processRequest(new RequestHandler.RequestProcessor() { @Override public DisposalRule process(RequestContext requestContext, RequestControllerAssistant controllerAssistant) - throws RODAException, RESTException, IOException { + throws RODAException, RESTException { // check user permissions controllerAssistant.setRelatedObjectId(id); controllerAssistant.setParameters(RodaConstants.DISPOSAL_RULE_ID, id); - return disposalRuleService.retrieveDisposalHold(id, requestContext.getModelService()); + DisposalRule disposalRule = disposalRuleService.retrieveDisposalHold(id, requestContext.getModelService()); + DisposalSchedule disposalSchedule = disposalScheduleService + .retrieveDisposalSchedule(requestContext.getModelService(), disposalRule.getDisposalScheduleId()); + disposalRule.setDisposalScheduleName(disposalSchedule.getTitle()); + + return disposalRule; } }); } @@ -138,4 +165,42 @@ public Job process(RequestContext requestContext, RequestControllerAssistant con } }); } + + @Override + public Void changeDisposalRuleOrder(@RequestBody ChangeOrderRequest request) { + return requestHandler.processRequestWithTransaction(new RequestHandler.RequestProcessor() { + @Override + public Void process(RequestContext requestContext, RequestControllerAssistant controllerAssistant) throws RODAException, RESTException, IOException { + + // delegate action to service + disposalRuleService.changeDisposalRuleOrder(requestContext, request); + return null; + } + }); + } + + @Override + public DisposalRule findByUuid(String uuid, String localeString) { + return indexService.retrieve(DisposalRule.class, uuid, new ArrayList<>()); + } + + @Override + public IndexResult find(@RequestBody FindRequest findRequest, String localeString) { + return indexService.find(DisposalRule.class, findRequest, localeString); + } + + @Override + public LongResponse count(@RequestBody CountRequest countRequest) { + RequestContext requestContext = RequestUtils.parseHTTPRequest(request); + if (UserUtility.hasPermissions(requestContext.getUser(), RodaConstants.PERMISSION_METHOD_FIND_FILE)) { + return new LongResponse(indexService.count(DisposalRule.class, countRequest)); + } else { + return new LongResponse(-1L); + } + } + + @Override + public List suggest(@RequestBody SuggestRequest suggestRequest) { + return indexService.suggest(suggestRequest, DisposalRule.class); + } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalScheduleController.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalScheduleController.java index de7040cd22..fe7395d91c 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalScheduleController.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalScheduleController.java @@ -8,21 +8,35 @@ package org.roda.wui.api.v2.controller; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import jakarta.servlet.http.HttpServletRequest; import org.roda.core.data.common.RodaConstants; import org.roda.core.data.exceptions.RODAException; import org.roda.core.data.utils.JsonUtils; +import org.roda.core.data.v2.disposal.rule.DisposalRule; import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; import org.roda.core.data.v2.disposal.schedule.DisposalSchedules; +import org.roda.core.data.v2.generics.LongResponse; import org.roda.core.data.v2.generics.select.SelectedItemsRequest; +import org.roda.core.data.v2.index.CountRequest; +import org.roda.core.data.v2.index.FindRequest; +import org.roda.core.data.v2.index.IndexResult; +import org.roda.core.data.v2.index.SuggestRequest; +import org.roda.core.data.v2.index.filter.Filter; +import org.roda.core.data.v2.index.filter.SimpleFilterParameter; import org.roda.core.data.v2.ip.IndexedAIP; import org.roda.core.data.v2.jobs.Job; +import org.roda.core.model.utils.UserUtility; import org.roda.wui.api.v2.exceptions.RESTException; import org.roda.wui.api.v2.services.DisposalScheduleService; +import org.roda.wui.api.v2.services.IndexService; import org.roda.wui.api.v2.utils.CommonServicesUtils; import org.roda.wui.client.services.DisposalScheduleRestService; import org.roda.wui.common.RequestControllerAssistant; import org.roda.wui.common.model.RequestContext; +import org.roda.wui.common.utils.RequestUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -37,6 +51,12 @@ @RequestMapping(path = "/api/v2/disposal/schedules") public class DisposalScheduleController implements DisposalScheduleRestService { + @Autowired + HttpServletRequest request; + + @Autowired + IndexService indexService; + @Autowired DisposalScheduleService disposalScheduleService; @@ -64,7 +84,21 @@ public DisposalSchedule process(RequestContext requestContext, RequestController controllerAssistant.setRelatedObjectId(id); // delegate action to service - return disposalScheduleService.retrieveDisposalSchedule(requestContext.getModelService(), id); + DisposalSchedule disposalSchedule = disposalScheduleService + .retrieveDisposalSchedule(requestContext.getModelService(), id); + + CountRequest countRequest = new CountRequest(); + SimpleFilterParameter filterParameter = new SimpleFilterParameter(RodaConstants.DISPOSAL_RULE_SCHEDULE_ID, id); + Filter filter = new Filter(); + filter.add(filterParameter); + + countRequest.setFilter(filter); + Long count = indexService.count(DisposalRule.class, countRequest); + if (count > 0) { + disposalSchedule.setUsedInDisposalRule(true); + } + + return disposalSchedule; } })); } @@ -157,4 +191,29 @@ public Job process(RequestContext requestContext, RequestControllerAssistant con } }); } + + @Override + public DisposalSchedule findByUuid(String uuid, String localeString) { + return indexService.retrieve(DisposalSchedule.class, uuid, new ArrayList<>()); + } + + @Override + public IndexResult find(@RequestBody FindRequest findRequest, String localeString) { + return indexService.find(DisposalSchedule.class, findRequest, localeString); + } + + @Override + public LongResponse count(@RequestBody CountRequest countRequest) { + RequestContext requestContext = RequestUtils.parseHTTPRequest(request); + if (UserUtility.hasPermissions(requestContext.getUser(), RodaConstants.PERMISSION_METHOD_FIND_FILE)) { + return new LongResponse(indexService.count(DisposalSchedule.class, countRequest)); + } else { + return new LongResponse(-1L); + } + } + + @Override + public List suggest(@RequestBody SuggestRequest suggestRequest) { + return indexService.suggest(suggestRequest, DisposalSchedule.class); + } } 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..45632027a2 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; @@ -271,6 +272,31 @@ private static Set mapCasGroupstoRODAGroups(Set casGroups) { return result; } + @Override + public RODAMember getMember(String name) { + RequestContext requestContext = RequestUtils.parseHTTPRequest(request); + ControllerAssistant controllerAssistant = new ControllerAssistant() {}; + LogEntryState state = LogEntryState.SUCCESS; + + try { + // check user permissions + controllerAssistant.checkRoles(requestContext.getUser()); + + 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; + throw new RESTException(e); + } finally { + // register action + controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_USERNAME_PARAM, name); + } + } + @Override public User getUser(String name) { RequestContext requestContext = RequestUtils.parseHTTPRequest(request); @@ -282,7 +308,6 @@ public User getUser(String name) { controllerAssistant.checkRoles(requestContext.getUser()); return membersService.retrieveUser(name); - } catch (RODAException e) { state = LogEntryState.FAILURE; throw new RESTException(e); @@ -611,7 +636,7 @@ public Group createGroup(@RequestBody CreateGroupRequest groupRequest) { } @Override - public Void updateGroup(@RequestBody Group modifiedGroup) { + public Group updateGroup(@RequestBody Group modifiedGroup) { ControllerAssistant controllerAssistant = new ControllerAssistant() {}; RequestContext requestContext = RequestUtils.parseHTTPRequest(request); LogEntryState state = LogEntryState.SUCCESS; @@ -620,7 +645,7 @@ public Void updateGroup(@RequestBody Group modifiedGroup) { // check user permissions controllerAssistant.checkRoles(requestContext.getUser()); // delegate - membersService.updateGroup(modifiedGroup); + return membersService.updateGroup(modifiedGroup); } catch (RODAException e) { state = LogEntryState.FAILURE; throw new RESTException(e); @@ -628,7 +653,136 @@ public Void updateGroup(@RequestBody Group modifiedGroup) { // register action controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_GROUP_PARAM, 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 @@ -650,7 +804,7 @@ public User updateUser(@RequestBody UpdateUserRequest userRequest) { } finally { // register action controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_USER_PARAM, - requestContext.getUser()); + requestContext.getUser().getFullName()); } } @@ -672,7 +826,7 @@ public User updateMyUser(@RequestBody UpdateUserRequest userOperations) { } finally { // register action controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_USER_PARAM, - userOperations.getUser()); + userOperations.getUser().getFullName()); } } @@ -861,7 +1015,7 @@ public LongResponse count(@RequestBody CountRequest countRequest) { } @Override - public List suggest(SuggestRequest suggestRequest) { + public List suggest(@RequestBody SuggestRequest suggestRequest) { return indexService.suggest(suggestRequest, RODAMember.class); } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/DisposalRuleService.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/DisposalRuleService.java index 42daad85df..6a66425597 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/DisposalRuleService.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/DisposalRuleService.java @@ -8,8 +8,13 @@ package org.roda.wui.api.v2.services; import java.io.IOException; +import java.util.ArrayList; 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.roda.core.data.common.RodaConstants; import org.roda.core.data.exceptions.AlreadyExistsException; @@ -18,6 +23,7 @@ import org.roda.core.data.exceptions.GenericException; import org.roda.core.data.exceptions.NotFoundException; import org.roda.core.data.exceptions.RequestNotValidException; +import org.roda.core.data.v2.disposal.rule.ChangeOrderRequest; import org.roda.core.data.v2.disposal.rule.ConditionType; import org.roda.core.data.v2.disposal.rule.DisposalRule; import org.roda.core.data.v2.disposal.rule.DisposalRules; @@ -26,7 +32,9 @@ import org.roda.core.data.v2.disposal.schedule.DisposalSchedules; 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.select.SelectedItems; import org.roda.core.data.v2.index.select.SelectedItemsFilter; +import org.roda.core.data.v2.index.select.SelectedItemsList; import org.roda.core.data.v2.ip.AIPState; import org.roda.core.data.v2.ip.IndexedAIP; import org.roda.core.data.v2.jobs.Job; @@ -81,6 +89,73 @@ public Job applyDisposalRules(User user, boolean overrideManualAssociations) ApplyDisposalRulesPlugin.class, user, pluginParameters, "Could not execute apply disposal rules to repository"); } + public void changeDisposalRuleOrder(RequestContext context, ChangeOrderRequest request) + throws AuthorizationDeniedException, RequestNotValidException, IOException, GenericException, NotFoundException { + ModelService modelService = context.getModelService(); + DisposalRules disposalRules = modelService.listDisposalRules(); + + SelectedItems disposalRuleSelectedItems = CommonServicesUtils.convertSelectedItems(request.getItems(), + DisposalRule.class); + if (disposalRuleSelectedItems instanceof SelectedItemsList itemsList) { + Set selectedIds = new HashSet<>(itemsList.getIds()); + + List selectedRules = new ArrayList<>(); + List remainingRules = new ArrayList<>(); + + for (DisposalRule rule : disposalRules.getObjects()) { + if (selectedIds.contains(rule.getId())) { + selectedRules.add(rule); + } else { + remainingRules.add(rule); + } + } + + // 3. Calculate the 0-based insertion index + int targetIndex = 0; + + switch (request.getPosition()) { + case TOP: + break; + case BOTTOM: + targetIndex = remainingRules.size(); + break; + case POSITION: + // Convert the 1-based UI position to a 0-based list index + targetIndex = request.getNewOrder() - 1; + + // Safety bounds checks + if (targetIndex < 0) { + targetIndex = 0; + } else if (targetIndex > remainingRules.size()) { + targetIndex = remainingRules.size(); + } + break; + } + + // 4. Insert the selected block into the remaining list + remainingRules.addAll(targetIndex, selectedRules); + + // 5. Reassign the "order" integer and collect the ones that actually changed + List rulesToUpdate = new ArrayList<>(); + + for (int i = 0; i < remainingRules.size(); i++) { + DisposalRule rule = remainingRules.get(i); + int newExpectedOrder = i; // 1-based ordering for the database + + // Only update if the order actually changed to save database/index calls + if (rule.getOrder() == null || rule.getOrder() != newExpectedOrder) { + rule.setOrder(newExpectedOrder); + rulesToUpdate.add(rule); + } + } + + // 6. Save the updated rules to your IndexService / Database + for (DisposalRule rule : rulesToUpdate) { + context.getModelService().updateDisposalRule(rule, context.getUser().getName()); + } + } + } + public void validateDisposalRule(DisposalRule disposalRule, ModelService model) throws GenericException, AuthorizationDeniedException, RequestNotValidException, IOException { DisposalSchedules disposalSchedules = model.listDisposalSchedules(); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/DisposalScheduleService.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/DisposalScheduleService.java index 1c173633a6..28aba79dce 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/DisposalScheduleService.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/DisposalScheduleService.java @@ -50,7 +50,7 @@ public DisposalSchedules getDisposalSchedules(ModelService model) public DisposalSchedule retrieveDisposalSchedule(ModelService model, String id) throws AuthorizationDeniedException, RequestNotValidException, NotFoundException, GenericException { - return model.retrieveDisposalSchedule(id); + return model.retrieveDisposalSchedule(id); } public DisposalSchedule updateDisposalSchedule(DisposalSchedule schedule, RequestContext requestContext) 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..e0a1a758a0 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; @@ -153,7 +152,7 @@ public Long count(Class returnClass, CountRequest reque public Long process(RequestContext requestContext, RequestControllerAssistant controllerAssistant) throws RODAException, RESTException { controllerAssistant.setParameters(RodaConstants.CONTROLLER_CLASS_PARAM, returnClass.getSimpleName(), - RodaConstants.CONTROLLER_FILTER_PARAM, request.getFilter().toString()); + RodaConstants.CONTROLLER_FILTER_PARAM, String.valueOf(request.getFilter())); return requestContext.getIndexService().count(returnClass, request.getFilter(), requestContext.getUser(), request.isOnlyActive()); } 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..8ef3124a3a 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) @@ -258,9 +271,10 @@ public Group createGroup(Group group) throws GenericException, AlreadyExistsExce return newGroup; } - public void updateGroup(Group group) throws GenericException, NotFoundException, AuthorizationDeniedException { - RodaCoreFactory.getModelService().updateGroup(group, true); + public Group updateGroup(Group group) throws GenericException, NotFoundException, AuthorizationDeniedException { + Group updatedGroup = RodaCoreFactory.getModelService().updateGroup(group, true); RodaCoreFactory.getIndexService().commit(true, RODAMember.class); + return updatedGroup; } public void deleteGroup(String groupname) throws GenericException, AuthorizationDeniedException { @@ -297,6 +311,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/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/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/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/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 734718e613..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 @@ -11,8 +11,16 @@ 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.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; +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; import org.roda.wui.client.planning.DetailsPanelFile; @@ -26,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 @@ -58,6 +67,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)); @@ -65,6 +81,25 @@ 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); + 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/browse/tabs/DisposalConfirmationTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalConfirmationTabs.java new file mode 100644 index 0000000000..5ebb9b757d --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalConfirmationTabs.java @@ -0,0 +1,55 @@ +package org.roda.wui.client.browse.tabs; + +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation; +import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmationState; +import org.roda.core.data.v2.index.filter.Filter; +import org.roda.core.data.v2.index.filter.SimpleFilterParameter; +import org.roda.core.data.v2.ip.AIPState; +import org.roda.core.data.v2.ip.IndexedAIP; +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 org.roda.wui.client.common.utils.PermissionClientUtils; +import org.roda.wui.client.disposal.confirmations.tabs.DisposalConfirmationDetailsPanel; + +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.user.client.ui.Widget; + +public class DisposalConfirmationTabs extends Tabs { + + public void init(DisposalConfirmation confirmation) { + + 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 DisposalConfirmationDetailsPanel(confirmation); + } + }); + + // Check if user has permissions to see the AIP + if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_FIND_AIP) && DisposalConfirmationState.PENDING.equals(confirmation.getState())) { + ListBuilder aipsListBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), + new AsyncTableCellOptions<>(IndexedAIP.class, "ShowDisposalSchedule_aips") + .withFilter( + new Filter(new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_CONFIRMATION_ID, confirmation.getId()), + new SimpleFilterParameter(RodaConstants.AIP_STATE, AIPState.ACTIVE.name()))) + .withSummary(messages.listOfAIPs()).bindOpener()); + + createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.allIntellectualEntities()), new TabContentBuilder() { + @Override + public Widget buildTabWidget() { + return new SearchWrapper(false).createListAndSearchPanel(aipsListBuilder); + } + }); + } + + 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/DisposalHoldTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalHoldTabs.java new file mode 100644 index 0000000000..e31f814f9c --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalHoldTabs.java @@ -0,0 +1,57 @@ +package org.roda.wui.client.browse.tabs; + +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.core.data.v2.index.filter.Filter; +import org.roda.core.data.v2.index.filter.SimpleFilterParameter; +import org.roda.core.data.v2.ip.AIPState; +import org.roda.core.data.v2.ip.IndexedAIP; +import org.roda.wui.client.common.actions.Actionable; +import org.roda.wui.client.common.actions.IndexedAIPDisposalHoldSearchWrapperActions; +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 org.roda.wui.client.common.utils.PermissionClientUtils; +import org.roda.wui.client.disposal.hold.tabs.DisposalHoldDetailsPanel; + +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Widget; + +public class DisposalHoldTabs extends Tabs { + + public void init(DisposalHold hold, 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 DisposalHoldDetailsPanel(hold, actionCallback); + } + }); + + // Check if user has permissions to see the AIP + if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_FIND_AIP)) { + + ListBuilder aipsListBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), + new AsyncTableCellOptions<>(IndexedAIP.class, "ShowDisposalSchedule_aips") + .withFilter(new Filter(new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_HOLDS_ID, hold.getId()), + new SimpleFilterParameter(RodaConstants.AIP_STATE, AIPState.ACTIVE.name()))) + .withActionable(new IndexedAIPDisposalHoldSearchWrapperActions(hold)).withSummary(messages.listOfAIPs()).bindOpener()); + + createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.disposalScheduleListAips()), new TabContentBuilder() { + @Override + public Widget buildTabWidget() { + return new SearchWrapper(false).createListAndSearchPanel(aipsListBuilder); + } + }); + } + + 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/DisposalOverdueRecordsTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalOverdueRecordsTabs.java new file mode 100644 index 0000000000..c0d7d78720 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalOverdueRecordsTabs.java @@ -0,0 +1,92 @@ +package org.roda.wui.client.browse.tabs; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.i18n.client.DateTimeFormat; +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.user.client.ui.Widget; +import config.i18n.client.ClientMessages; +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.v2.disposal.schedule.DisposalActionCode; +import org.roda.core.data.v2.disposal.schedule.RetentionPeriodCalculation; +import org.roda.core.data.v2.index.filter.DateIntervalFilterParameter; +import org.roda.core.data.v2.index.filter.EmptyKeyFilterParameter; +import org.roda.core.data.v2.index.filter.Filter; +import org.roda.core.data.v2.index.filter.SimpleFilterParameter; +import org.roda.core.data.v2.ip.AIPState; +import org.roda.core.data.v2.ip.IndexedAIP; +import org.roda.wui.client.common.actions.DisposalCreateConfirmationDestroyActions; +import org.roda.wui.client.common.actions.DisposalCreateConfirmationReviewActions; +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 java.util.Date; + +public class DisposalOverdueRecordsTabs extends Tabs { + + private static final DateTimeFormat formatter = DateTimeFormat.getFormat(RodaConstants.SIMPLE_DATE_FORMATTER); + + private static final Filter SHOW_RECORDS_TO_REVIEW = new Filter( + new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_ACTION, DisposalActionCode.REVIEW.name()), + new SimpleFilterParameter(RodaConstants.AIP_STATE, AIPState.ACTIVE.name()), + new DateIntervalFilterParameter(RodaConstants.AIP_OVERDUE_DATE, RodaConstants.AIP_OVERDUE_DATE, null, + formatter.parse(formatter.format(new Date()))), + new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_HOLD_STATUS, Boolean.FALSE.toString()), + new EmptyKeyFilterParameter(RodaConstants.AIP_DISPOSAL_CONFIRMATION_ID)); + + private static final Filter SHOW_RECORDS_TO_DESTROY = new Filter( + new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_ACTION, DisposalActionCode.DESTROY.name()), + new SimpleFilterParameter(RodaConstants.AIP_STATE, AIPState.ACTIVE.name()), + new DateIntervalFilterParameter(RodaConstants.AIP_OVERDUE_DATE, RodaConstants.AIP_OVERDUE_DATE, null, + formatter.parse(formatter.format(new Date()))), + new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_HOLD_STATUS, Boolean.FALSE.toString()), + new EmptyKeyFilterParameter(RodaConstants.AIP_DISPOSAL_CONFIRMATION_ID)); + + private static final Filter SHOW_RECORDS_WITH_RETENTION_PERIOD_ERRORS = new Filter(new SimpleFilterParameter( + RodaConstants.AIP_DISPOSAL_RETENTION_PERIOD_CALCULATION, RetentionPeriodCalculation.ERROR.name())); + + private static final ClientMessages messages = GWT.create(ClientMessages.class); + + public void init() { + + ListBuilder overdueRecordsListBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), + new AsyncTableCellOptions<>(IndexedAIP.class, "DisposalOverdueRecords_aip").withSummary(messages.listOfAIPs()) + .withFilter(SHOW_RECORDS_TO_DESTROY).withActionable(DisposalCreateConfirmationDestroyActions.get()) + .withJustActive(true).bindOpener()); + + createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.disposalConfirmationShowRecordsToDestroy()), + new TabContentBuilder() { + @Override + public Widget buildTabWidget() { + return new SearchWrapper(false).createListAndSearchPanel(overdueRecordsListBuilder); + } + }); + + ListBuilder overdueRecordsToReviewListBuilder = new ListBuilder<>( + () -> new ConfigurableAsyncTableCell<>(), + new AsyncTableCellOptions<>(IndexedAIP.class, "DisposalOverdueRecords_aip").withSummary(messages.listOfAIPs()) + .withFilter(SHOW_RECORDS_TO_REVIEW).withActionable(DisposalCreateConfirmationReviewActions.get()) + .withJustActive(true).bindOpener()); + + createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.disposalConfirmationShowRecordsToReview()), + new TabContentBuilder() { + @Override + public Widget buildTabWidget() { + return new SearchWrapper(false).createListAndSearchPanel(overdueRecordsToReviewListBuilder); + } + }); + + ListBuilder recordsWithErrorListBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), + new AsyncTableCellOptions<>(IndexedAIP.class, "DisposalOverdueRecords_aip").withSummary(messages.listOfAIPs()) + .withFilter(SHOW_RECORDS_WITH_RETENTION_PERIOD_ERRORS).withJustActive(true).bindOpener()); + + createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.disposalConfirmationShowRecordsRetentionPeriodCalculationError()), new TabContentBuilder() { + @Override + public Widget buildTabWidget() { + return new SearchWrapper(false).createListAndSearchPanel(recordsWithErrorListBuilder); + } + }); + + } +} \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalPolicyTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalPolicyTabs.java new file mode 100644 index 0000000000..505f803569 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalPolicyTabs.java @@ -0,0 +1,68 @@ +package org.roda.wui.client.browse.tabs; + +import java.util.List; + +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.core.data.v2.disposal.rule.DisposalRule; +import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; +import org.roda.wui.client.common.actions.DisposalHoldSearchWrapperActions; +import org.roda.wui.client.common.actions.DisposalRuleSearchWrapperActions; +import org.roda.wui.client.common.actions.DisposalScheduleSearchWrapperActions; +import org.roda.wui.client.common.lists.DisposalHoldList; +import org.roda.wui.client.common.lists.DisposalRuleList; +import org.roda.wui.client.common.lists.DisposalScheduleList; +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.PermissionClientUtils; + +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.user.client.ui.Widget; + +public class DisposalPolicyTabs extends Tabs { + + public void init() { + + if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_LIST_DISPOSAL_SCHEDULES)) { + ListBuilder scheduleListBuilder = new ListBuilder<>(() -> new DisposalScheduleList(), + new AsyncTableCellOptions<>(DisposalSchedule.class, "DisposalPolicyPage_disposalSchedules") + .withActionable(DisposalScheduleSearchWrapperActions.get()).withCsvDownloadButtonVisibility(false) + .bindOpener()); + + createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.disposalSchedulesTitle()), new TabContentBuilder() { + @Override + public Widget buildTabWidget() { + return new SearchWrapper(false).createListAndSearchPanel(scheduleListBuilder); + } + }); + } + + if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_LIST_DISPOSAL_HOLDS)) { + ListBuilder holdListBuilder = new ListBuilder<>(() -> new DisposalHoldList(), + new AsyncTableCellOptions<>(DisposalHold.class, "DisposalPolicyPage_disposalHolds") + .withActionable(DisposalHoldSearchWrapperActions.get()).withCsvDownloadButtonVisibility(false).bindOpener()); + + createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.disposalHoldsTitle()), new TabContentBuilder() { + @Override + public Widget buildTabWidget() { + return new SearchWrapper(false).createListAndSearchPanel(holdListBuilder); + } + }); + } + + if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_LIST_DISPOSAL_RULES)) { + ListBuilder ruleListBuilder = new ListBuilder<>(() -> new DisposalRuleList(), + new AsyncTableCellOptions<>(DisposalRule.class, "DisposalPolicyPage_disposalRules") + .withActionable(DisposalRuleSearchWrapperActions.get()) + .withCsvDownloadButtonVisibility(false).bindOpener()); + + createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.disposalRulesTitle()), new TabContentBuilder() { + @Override + public Widget buildTabWidget() { + return new SearchWrapper(false).createListAndSearchPanel(ruleListBuilder); + } + }); + } + } +} \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalRuleTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalRuleTabs.java new file mode 100644 index 0000000000..cea2581211 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalRuleTabs.java @@ -0,0 +1,23 @@ +package org.roda.wui.client.browse.tabs; + +import org.roda.core.data.v2.disposal.rule.DisposalRule; +import org.roda.wui.client.common.actions.Actionable; +import org.roda.wui.client.disposal.rule.tabs.DisposalRuleDetailsPanel; + +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Widget; + +public class DisposalRuleTabs extends Tabs { + + public void init(DisposalRule rule, AsyncCallback actionCallback) { + this.clear(); + + createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.detailsTab()), new TabContentBuilder() { + @Override + public Widget buildTabWidget() { + return new DisposalRuleDetailsPanel(rule, actionCallback); + } + }); + } +} \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalScheduleTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalScheduleTabs.java new file mode 100644 index 0000000000..9b92b8665c --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalScheduleTabs.java @@ -0,0 +1,57 @@ +package org.roda.wui.client.browse.tabs; + +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; +import org.roda.core.data.v2.index.filter.Filter; +import org.roda.core.data.v2.index.filter.SimpleFilterParameter; +import org.roda.core.data.v2.ip.AIPState; +import org.roda.core.data.v2.ip.IndexedAIP; +import org.roda.wui.client.common.actions.Actionable; +import org.roda.wui.client.common.actions.IndexedAIPDisposalScheduleSearchWrapperActions; +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 org.roda.wui.client.common.utils.PermissionClientUtils; +import org.roda.wui.client.disposal.schedule.tabs.DisposalScheduleDetailsPanel; + +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Widget; + +public class DisposalScheduleTabs extends Tabs { + + public void init(DisposalSchedule schedule, 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 DisposalScheduleDetailsPanel(schedule, actionCallback); + } + }); + + // Check if user has permissions to see the AIP + if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_FIND_AIP)) { + + ListBuilder aipsListBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), + new AsyncTableCellOptions<>(IndexedAIP.class, "ShowDisposalSchedule_aips") + .withFilter(new Filter(new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_SCHEDULE_ID, schedule.getId()), + new SimpleFilterParameter(RodaConstants.AIP_STATE, AIPState.ACTIVE.name()))) + .withActionable(IndexedAIPDisposalScheduleSearchWrapperActions.get()).withSummary(messages.listOfAIPs()).bindOpener()); + + createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.disposalScheduleListAips()), new TabContentBuilder() { + @Override + public Widget buildTabWidget() { + return new SearchWrapper(false).createListAndSearchPanel(aipsListBuilder); + } + }); + } + + 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/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..3103c39022 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 @@ -22,6 +22,7 @@ import com.google.gwt.user.client.ui.FocusPanel; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.Widget; +import com.google.gwt.storage.client.Storage; import config.i18n.client.ClientMessages; @@ -32,15 +33,14 @@ public class Tabs extends Composite { protected static final ClientMessages messages = GWT.create(ClientMessages.class); private static Tabs.MyUiBinder uiBinder = GWT.create(Tabs.MyUiBinder.class); - + private final Map tabs; @UiField FlowPanel tabButtons; @UiField SimplePanel tabContentWrapper; - private Widget selectedTab; - private final Map tabs; private TabContentBuilder defaultContent; + private String storageKey = null; public Tabs() { initWidget(uiBinder.createAndBindUi(this)); @@ -55,6 +55,24 @@ public Widget buildTabWidget() { selectTab(null); } + public void setStorageKey(String storageKey) { + this.storageKey = storageKey; + + // Immediately attempt to restore the saved tab index + Storage sessionStorage = Storage.getSessionStorageIfSupported(); + if (sessionStorage != null) { + String savedIndexStr = sessionStorage.getItem(storageKey); + if (savedIndexStr != null) { + try { + int activeIndex = Integer.parseInt(savedIndexStr); + selectTabByIndex(activeIndex); + } catch (NumberFormatException e) { + // If parsing fails, it safely ignores and keeps the default tab + } + } + } + } + public void setDefaultContent(TabContentBuilder tabContentBuilder) { defaultContent = tabContentBuilder; } @@ -110,6 +128,14 @@ protected void selectTab(Widget tabButtonContainer) { selectedTab = tabButtonContainer; tab.getKey().addStyleName("tabSelected"); tabContentWrapper.setWidget(tab.getValue().buildTabWidget()); + + if (storageKey != null) { + Storage sessionStorage = Storage.getSessionStorageIfSupported(); + if (sessionStorage != null) { + sessionStorage.setItem(storageKey, String.valueOf(getSelectedTabIndex())); + } + } + } else { tab.getKey().removeStyleName("tabSelected"); } @@ -120,6 +146,57 @@ 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); + } + } + + // --- NEW: Add the onLoad method --- + @Override + protected void onLoad() { + super.onLoad(); + + // 1. Dynamically generate a unique key based on the child class (e.g., + // "org.roda...DisposalPolicyTabs_ActiveIndex") + if (this.storageKey == null) { + this.storageKey = this.getClass().getName() + "_ActiveIndex"; + } + + // 2. Restore the saved index now that the tabs are built and the widget is + // attached + Storage sessionStorage = Storage.getSessionStorageIfSupported(); + if (sessionStorage != null) { + String savedIndexStr = sessionStorage.getItem(storageKey); + if (savedIndexStr != null) { + try { + int activeIndex = Integer.parseInt(savedIndexStr); + // Only select if the saved index is valid for the current number of tabs + if (activeIndex >= 0 && activeIndex < tabButtons.getWidgetCount()) { + selectTabByIndex(activeIndex); + } + } catch (NumberFormatException e) { + // Defaults gracefully if parsing fails + } + } + } + } + public interface TabContentBuilder { public Widget buildTabWidget(); } 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/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/DisposalConfirmationActionsToolbar.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/DisposalConfirmationActionsToolbar.java new file mode 100644 index 0000000000..41a6e77d32 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/DisposalConfirmationActionsToolbar.java @@ -0,0 +1,74 @@ +/** + * 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 com.google.gwt.core.client.GWT; +import config.i18n.client.ClientMessages; +import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation; +import org.roda.wui.client.common.actions.DisposalConfirmationToolbarActions; +import org.roda.wui.client.common.actions.model.ActionableObject; +import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; +import org.roda.wui.client.common.labels.Tag; + +/** + * + * @author Miguel Guimarães + */ +public class DisposalConfirmationActionsToolbar extends BrowseObjectActionsToolbar { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + + public void buildIcon() { + // do nothing + } + + public void buildTags() { + tags.clear(); + + Tag.TagStyle style; + switch (object.getState()) { + case PENDING: + style = Tag.TagStyle.WARNING_LIGHT; + break; + case APPROVED: + style = Tag.TagStyle.SUCCESS; + break; + case RESTORED: + style = Tag.TagStyle.SUCCESS; + break; + case PERMANENTLY_DELETED: + style = Tag.TagStyle.DANGER_LIGHT; + break; + case EXECUTION_FAILED: + style = Tag.TagStyle.FAILURE; + break; + default: + style = Tag.TagStyle.NEUTRAL; + } + + GWT.log("Disposal Confirmation state: " + object.getState() + " - Tag style: " + style); + Tag tag = Tag.fromText(messages.disposalConfirmationState(object.getState()), style); + tags.add(tag); + } + + public void buildActions() { + this.actions.clear(); + + this.actions.add(new ActionableWidgetBuilder(DisposalConfirmationToolbarActions.get()) + .withActionCallback(actionCallback).buildGroupedListWithObjects(new ActionableObject<>(object), + List.of(DisposalConfirmationToolbarActions.DisposalConfirmationReportAction.DESTROY, + DisposalConfirmationToolbarActions.DisposalConfirmationReportAction.WITHDRAW, + DisposalConfirmationToolbarActions.DisposalConfirmationReportAction.REMOVE_FROM_BIN, + DisposalConfirmationToolbarActions.DisposalConfirmationReportAction.RESTORE_FROM_BIN), + List.of(DisposalConfirmationToolbarActions.DisposalConfirmationReportAction.WITHDRAW, + DisposalConfirmationToolbarActions.DisposalConfirmationReportAction.REMOVE_FROM_BIN, + DisposalConfirmationToolbarActions.DisposalConfirmationReportAction.RESTORE_FROM_BIN, + DisposalConfirmationToolbarActions.DisposalConfirmationReportAction.DESTROY))); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/DisposalHoldActionsToolbar.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/DisposalHoldActionsToolbar.java new file mode 100644 index 0000000000..c09f6b0eb9 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/DisposalHoldActionsToolbar.java @@ -0,0 +1,38 @@ +/** + * 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.disposal.hold.DisposalHold; +import org.roda.wui.client.common.actions.DisposalHoldAction; +import org.roda.wui.client.common.actions.DisposalHoldToolbarActions; +import org.roda.wui.client.common.actions.model.ActionableObject; +import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; + +/** + * + * @author Miguel Guimarães + */ +public class DisposalHoldActionsToolbar extends BrowseObjectActionsToolbar { + public void buildIcon() { + // do nothing + } + + public void buildTags() { + // do nothing + } + + public void buildActions() { + this.actions.clear(); + + DisposalHoldToolbarActions toolbarActions = DisposalHoldToolbarActions.get(); + this.actions.add(new ActionableWidgetBuilder(toolbarActions).withActionCallback(actionCallback) + .buildGroupedListWithObjects(new ActionableObject<>(object), List.of(DisposalHoldAction.LIFT), List.of())); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/DisposalRuleActionsToolbar.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/DisposalRuleActionsToolbar.java new file mode 100644 index 0000000000..1d254db8c5 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/DisposalRuleActionsToolbar.java @@ -0,0 +1,38 @@ +/** + * 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 org.roda.core.data.v2.disposal.rule.DisposalRule; +import org.roda.wui.client.common.actions.DisposalRuleAction; +import org.roda.wui.client.common.actions.DisposalRuleToolbarActions; +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 DisposalRuleActionsToolbar extends BrowseObjectActionsToolbar { + public void buildIcon() { + // do nothing + } + + public void buildTags() { + // do nothing + } + + public void buildActions() { + this.actions.clear(); + + this.actions.add( + new ActionableWidgetBuilder(DisposalRuleToolbarActions.get()).withActionCallback(actionCallback) + .buildGroupedListWithObjects(new ActionableObject<>(object), List.of(DisposalRuleAction.REMOVE), List.of())); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/DisposalScheduleActionsToolbar.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/DisposalScheduleActionsToolbar.java new file mode 100644 index 0000000000..cdc279d350 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/DisposalScheduleActionsToolbar.java @@ -0,0 +1,44 @@ +/** + * 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 org.roda.core.data.v2.disposal.schedule.DisposalSchedule; +import org.roda.wui.client.common.actions.DisposalScheduleAction; +import org.roda.wui.client.common.actions.DisposalScheduleToolbarActions; +import org.roda.wui.client.common.actions.model.ActionableObject; +import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; +import org.roda.wui.client.common.labels.Tag; + +import java.util.List; + +/** + * + * @author Miguel Guimarães + */ +public class DisposalScheduleActionsToolbar extends BrowseObjectActionsToolbar { + public void buildIcon() { + // do nothing + } + + public void buildTags() { + this.tags.clear(); + + if (object.isUsedInDisposalRule()) { + Tag tag = Tag.fromText(messages.disposalScheduleUsedInRule(), Tag.TagStyle.WARNING_LIGHT); + tags.add(tag); + } + } + + public void buildActions() { + this.actions.clear(); + + DisposalScheduleToolbarActions toolbarActions = DisposalScheduleToolbarActions.get(); + this.actions.add(new ActionableWidgetBuilder(toolbarActions).withActionCallback(actionCallback) + .buildGroupedListWithObjects(new ActionableObject<>(object), List.of(DisposalScheduleAction.REMOVE), List.of())); + } +} 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..9f9934e371 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; @@ -61,15 +62,12 @@ public class NavigationToolbar extends Composite implements // buttons on the right side @UiField - AccessibleFocusPanel infoSidebarButton; - @UiField FlowPanel toolbarPanel; private boolean requireControlKeyModifier = true; private boolean requireShiftKeyModifier = false; private boolean requireAltKeyModifier = false; private boolean skipButtonSetup = false; private T currentObject = null; - private Permissions permissions = null; private Map handlers = new EnumMap<>(Actionable.ActionImpact.class); private AsyncCallback handler = new NoAsyncCallback() { @Override @@ -102,7 +100,6 @@ public NavigationToolbar withProcessor(ProcessRelativeItem processor) { } public NavigationToolbar withPermissions(Permissions permissions) { - this.permissions = permissions; return this; } @@ -111,11 +108,6 @@ public NavigationToolbar withAlternativeStyle(boolean useAltStyle) { return this; } - public AccessibleFocusPanel getInfoSidebarButton() { - infoSidebarButton.setVisible(true); - return infoSidebarButton; - } - public NavigationToolbar withModifierKeys(boolean requireControlKeyModifier, boolean requireShiftKeyModifier, boolean requireAltKeyModifier) { this.requireControlKeyModifier = requireControlKeyModifier; @@ -128,7 +120,6 @@ private void hideButtons() { previousButton.setVisible(false); nextButton.setVisible(false); pageInformation.setVisible(false); - infoSidebarButton.setVisible(false); } public void build() { @@ -144,6 +135,7 @@ public void build() { int index = ListSelectionUtils.getIndex(currentObject.getClass().getName()) + 1; long total = ListSelectionUtils.getTotal(currentObject.getClass().getName()); + pageInformation.setHTML("" + NumberFormat.getDecimalFormat().format(index) + " / " + NumberFormat.getDecimalFormat().format(total) + ""); @@ -233,6 +225,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/NavigationToolbar.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/NavigationToolbar.ui.xml index 59683fd7cb..0efa8e1cf3 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/NavigationToolbar.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/NavigationToolbar.ui.xml @@ -1,41 +1,29 @@ + xmlns:g="urn:import:com.google.gwt.user.client.ui" + xmlns:wcag="urn:import:org.roda.wui.common.client.widgets.wcag"> - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/NoActionsToolbar.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/NoActionsToolbar.java new file mode 100644 index 0000000000..d34e7c773c --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/NoActionsToolbar.java @@ -0,0 +1,33 @@ +/** + * 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 org.roda.core.data.v2.user.RODAMember; + +/** + * + * @author Miguel Guimarães + */ +public class NoActionsToolbar extends BrowseObjectActionsToolbar { + + public void build() { + setObjectAndBuild(null, null, null); + } + + public void buildIcon() { + // do nothing + } + + public void buildTags() { + // do nothing + } + + public void buildActions() { + this.actions.clear(); + } +} 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/DisposalConfirmationAction.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalConfirmationAction.java new file mode 100644 index 0000000000..8a336adc0a --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalConfirmationAction.java @@ -0,0 +1,29 @@ +package org.roda.wui.client.common.actions; + +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation; + +import java.util.Arrays; +import java.util.List; + +/** + * + * @author Miguel Guimarães + */ +public enum DisposalConfirmationAction implements Actionable.Action { + NEW(RodaConstants.PERMISSION_METHOD_CREATE_DISPOSAL_CONFIRMATION), + DESTROY(RodaConstants.PERMISSION_METHOD_DESTROY_RECORDS_DISPOSAL_CONFIRMATION), + PERM_DELETE(RodaConstants.PERMISSION_METHOD_PERMANENTLY_DELETE_RECORDS_DISPOSAL_CONFIRMATION), + RESTORE(RodaConstants.PERMISSION_METHOD_RESTORE_RECORDS_DISPOSAL_CONFIRMATION); + + private final List methods; + + DisposalConfirmationAction(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/DisposalConfirmationActionsUtils.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalConfirmationActionsUtils.java index 43dd8dfd6b..650de22b75 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalConfirmationActionsUtils.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalConfirmationActionsUtils.java @@ -134,20 +134,18 @@ public void onSuccess(Boolean result) { public void onFailure(Throwable caught) { Toast.showInfo(messages.deleteConfirmationReportSuccessTitle(), messages.deleteConfirmationReportSuccessMessage()); - doActionCallbackUpdated(); - HistoryUtils.newHistory(DisposalConfirmations.RESOLVER); + doActionCallbackDestroyed(); } @Override public void onSuccess(final Void nothing) { - doActionCallbackUpdated(); HistoryUtils.newHistory(ShowJob.RESOLVER, job.getId()); } }); } }); } else { - doActionCallbackNone(); + callback.onSuccess(Actionable.ActionImpact.NONE); } } }); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalConfirmationActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalConfirmationSearchWrapperActions.java similarity index 84% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalConfirmationActions.java rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalConfirmationSearchWrapperActions.java index 8a8933de40..389fbe9e42 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalConfirmationActions.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalConfirmationSearchWrapperActions.java @@ -7,29 +7,26 @@ */ package org.roda.wui.client.common.actions; -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.core.client.GWT; +import com.google.gwt.user.client.rpc.AsyncCallback; +import config.i18n.client.ClientMessages; import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation; import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmationState; import org.roda.core.data.v2.index.select.SelectedItems; import org.roda.wui.client.common.actions.model.ActionableBundle; import org.roda.wui.client.common.actions.model.ActionableGroup; -import org.roda.wui.client.disposal.confirmations.CreateDisposalConfirmation; +import org.roda.wui.client.disposal.confirmations.OverdueRecords; import org.roda.wui.common.client.tools.HistoryUtils; -import com.google.gwt.core.client.GWT; -import com.google.gwt.user.client.rpc.AsyncCallback; - -import config.i18n.client.ClientMessages; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** * @author Miguel Guimarães */ -public class DisposalConfirmationActions extends AbstractActionable { +public class DisposalConfirmationSearchWrapperActions extends AbstractActionable { private static final ClientMessages messages = GWT.create(ClientMessages.class); private static final Set> POSSIBLE_ACTIONS_WITHOUT_OBJECT_SELECTED = new HashSet<>( @@ -39,15 +36,11 @@ public class DisposalConfirmationActions extends AbstractActionable> POSSIBLE_ACTIONS_FOR_APPROVED = new HashSet<>( Arrays.asList(DisposalConfirmationAction.PERM_DELETE, DisposalConfirmationAction.RESTORE)); - private DisposalConfirmationActions() { + private DisposalConfirmationSearchWrapperActions() { // Singleton } - private static class SingletonHelper { - private static final DisposalConfirmationActions INSTANCE = new DisposalConfirmationActions(); - } - - public static DisposalConfirmationActions getInstance() { + public static DisposalConfirmationSearchWrapperActions getInstance() { return SingletonHelper.INSTANCE; } @@ -132,7 +125,7 @@ public void act(Action action, DisposalConfirmation object // ACTIONS private void newConfirmation(AsyncCallback callback) { callback.onSuccess(ActionImpact.NONE); - HistoryUtils.newHistory(CreateDisposalConfirmation.RESOLVER); + HistoryUtils.newHistory(OverdueRecords.RESOLVER); } @Override @@ -140,7 +133,8 @@ public ActionableBundle createActionsBundle() { ActionableBundle confirmationActionableBundle = new ActionableBundle<>(); // management - ActionableGroup actionsGroup = new ActionableGroup<>(messages.sidebarDisposalConfirmationTitle()); + ActionableGroup actionsGroup = new ActionableGroup<>( + messages.sidebarDisposalConfirmationTitle()); actionsGroup.addButton(messages.newDisposalConfirmationButton(), DisposalConfirmationAction.NEW, ActionImpact.UPDATED, "btn-plus-circle"); actionsGroup.addButton(messages.applyDisposalScheduleButton(), DisposalConfirmationAction.DESTROY, @@ -159,21 +153,7 @@ public ActionableBundle createActionsBundle() { return confirmationActionableBundle; } - public enum DisposalConfirmationAction implements Action { - NEW(RodaConstants.PERMISSION_METHOD_CREATE_DISPOSAL_CONFIRMATION), - DESTROY(RodaConstants.PERMISSION_METHOD_DESTROY_RECORDS_DISPOSAL_CONFIRMATION), - PERM_DELETE(RodaConstants.PERMISSION_METHOD_PERMANENTLY_DELETE_RECORDS_DISPOSAL_CONFIRMATION), - RESTORE(RodaConstants.PERMISSION_METHOD_RESTORE_RECORDS_DISPOSAL_CONFIRMATION); - - private final List methods; - - DisposalConfirmationAction(String... methods) { - this.methods = Arrays.asList(methods); - } - - @Override - public List getMethods() { - return this.methods; - } + private static class SingletonHelper { + private static final DisposalConfirmationSearchWrapperActions INSTANCE = new DisposalConfirmationSearchWrapperActions(); } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalConfirmationReportActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalConfirmationToolbarActions.java similarity index 90% rename from roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalConfirmationReportActions.java rename to roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalConfirmationToolbarActions.java index c6452f0ea5..d77bf3887b 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalConfirmationReportActions.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalConfirmationToolbarActions.java @@ -7,18 +7,16 @@ */ package org.roda.wui.client.common.actions; -import static org.roda.core.data.common.RodaConstants.PERMISSION_METHOD_DELETE_DISPOSAL_CONFIRMATION; -import static org.roda.core.data.common.RodaConstants.PERMISSION_METHOD_DESTROY_RECORDS_DISPOSAL_CONFIRMATION; -import static org.roda.core.data.common.RodaConstants.PERMISSION_METHOD_PERMANENTLY_DELETE_RECORDS_DISPOSAL_CONFIRMATION; -import static org.roda.core.data.common.RodaConstants.PERMISSION_METHOD_RESTORE_RECORDS_DISPOSAL_CONFIRMATION; -import static org.roda.core.data.common.RodaConstants.PERMISSION_METHOD_RETRIEVE_DISPOSAL_CONFIRMATION_REPORT; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - +import com.google.gwt.core.client.GWT; +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.regexp.shared.RegExp; +import com.google.gwt.safehtml.shared.SafeUri; +import com.google.gwt.user.client.rpc.AsyncCallback; +import config.i18n.client.ClientMessages; import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation; import org.roda.core.data.v2.generics.select.SelectedItemsListRequest; import org.roda.wui.client.common.actions.callbacks.ActionNoAsyncCallback; @@ -27,6 +25,7 @@ import org.roda.wui.client.common.dialogs.Dialogs; import org.roda.wui.client.common.utils.AsyncCallbackUtils; import org.roda.wui.client.common.utils.JavascriptUtils; +import org.roda.wui.client.disposal.DisposalConfirmations; import org.roda.wui.client.disposal.confirmations.ShowDisposalConfirmation; import org.roda.wui.client.ingest.process.ShowJob; import org.roda.wui.client.services.Services; @@ -34,28 +33,28 @@ import org.roda.wui.common.client.tools.RestUtils; import org.roda.wui.common.client.widgets.Toast; -import com.google.gwt.core.client.GWT; -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.regexp.shared.RegExp; -import com.google.gwt.safehtml.shared.SafeUri; -import com.google.gwt.user.client.rpc.AsyncCallback; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; -import config.i18n.client.ClientMessages; +import static org.roda.core.data.common.RodaConstants.PERMISSION_METHOD_DELETE_DISPOSAL_CONFIRMATION; +import static org.roda.core.data.common.RodaConstants.PERMISSION_METHOD_DESTROY_RECORDS_DISPOSAL_CONFIRMATION; +import static org.roda.core.data.common.RodaConstants.PERMISSION_METHOD_PERMANENTLY_DELETE_RECORDS_DISPOSAL_CONFIRMATION; +import static org.roda.core.data.common.RodaConstants.PERMISSION_METHOD_RESTORE_RECORDS_DISPOSAL_CONFIRMATION; +import static org.roda.core.data.common.RodaConstants.PERMISSION_METHOD_RETRIEVE_DISPOSAL_CONFIRMATION_REPORT; /** * @author Miguel Guimarães */ -public class DisposalConfirmationReportActions extends AbstractActionable { - private static final DisposalConfirmationReportActions INSTANCE = new DisposalConfirmationReportActions(); +public class DisposalConfirmationToolbarActions extends AbstractActionable { + private static final DisposalConfirmationToolbarActions INSTANCE = new DisposalConfirmationToolbarActions(); private static final ClientMessages messages = GWT.create(ClientMessages.class); private static final Set POSSIBLE_ACTIONS_FOR_PENDING = new HashSet<>( Arrays.asList(DisposalConfirmationReportAction.PRINT, DisposalConfirmationReportAction.DESTROY, - DisposalConfirmationReportAction.DELETE_REPORT)); + DisposalConfirmationReportAction.WITHDRAW)); private static final Set POSSIBLE_ACTIONS_FOR_APPROVED = new HashSet<>( Arrays.asList(DisposalConfirmationReportAction.PRINT, DisposalConfirmationReportAction.REMOVE_FROM_BIN, @@ -68,10 +67,10 @@ public class DisposalConfirmationReportActions extends AbstractActionable POSSIBLE_ACTIONS_FOR_EXECUTION_FAILED = new HashSet<>( Arrays.asList(DisposalConfirmationReportAction.RE_EXECUTE, DisposalConfirmationReportAction.RECOVER_STATE)); - private DisposalConfirmationReportActions() { + private DisposalConfirmationToolbarActions() { } - public static DisposalConfirmationReportActions get() { + public static DisposalConfirmationToolbarActions get() { return INSTANCE; } @@ -128,7 +127,7 @@ public void act(Action action, DisposalConfirmation object AsyncCallback callback) { if (DisposalConfirmationReportAction.DESTROY.equals(action)) { DisposalConfirmationActionsUtils.destroyDisposalConfirmationContent(object, callback); - } else if (DisposalConfirmationReportAction.DELETE_REPORT.equals(action)) { + } else if (DisposalConfirmationReportAction.WITHDRAW.equals(action)) { deleteDisposalConfirmationReport(object, callback); } else if (DisposalConfirmationReportAction.REMOVE_FROM_BIN.equals(action)) { DisposalConfirmationActionsUtils.permanentlyDeleteDisposalConfirmationReport(object, callback); @@ -199,12 +198,10 @@ public void onFailure(Throwable caught) { Toast.showInfo(messages.deleteConfirmationReportSuccessTitle(), messages.deleteConfirmationReportSuccessMessage()); doActionCallbackDestroyed(); - HistoryUtils.newHistory(ShowDisposalConfirmation.RESOLVER); } @Override public void onSuccess(final Void nothing) { - doActionCallbackNone(); HistoryUtils.newHistory(ShowJob.RESOLVER, job.getId()); } }); @@ -223,32 +220,32 @@ public void onSuccess(final Void nothing) { public ActionableBundle createActionsBundle() { ActionableBundle confirmationActionableBundle = new ActionableBundle<>(); - // SCHEDULE - ActionableGroup scheduleGroup = new ActionableGroup<>( - messages.sidebarDisposalScheduleTitle()); - scheduleGroup.addButton(messages.applyDisposalScheduleButton(), DisposalConfirmationReportAction.DESTROY, - ActionImpact.UPDATED, "btn-trash-alt"); - // DISPOSAL CONFIRMATION ActionableGroup confirmationGroup = new ActionableGroup<>( - messages.sidebarDisposalConfirmationTitle()); + messages.sidebarDisposalConfirmationTitle()); confirmationGroup.addButton(messages.printButton(), DisposalConfirmationReportAction.PRINT, ActionImpact.NONE, - "btn-print"); + "btn-print"); confirmationGroup.addButton(messages.deleteDisposalConfirmationReport(), - DisposalConfirmationReportAction.DELETE_REPORT, ActionImpact.DESTROYED, "btn-remove"); + DisposalConfirmationReportAction.WITHDRAW, ActionImpact.DESTROYED, "btn-undo"); confirmationGroup.addButton(messages.reExecuteDisposalDestroyActionButton(), - DisposalConfirmationReportAction.RE_EXECUTE, ActionImpact.UPDATED, "btn-play-circle"); + DisposalConfirmationReportAction.RE_EXECUTE, ActionImpact.UPDATED, "btn-play-circle"); confirmationGroup.addButton(messages.recoverDisposalConfirmationExecutionFailedButton(), - DisposalConfirmationReportAction.RECOVER_STATE, ActionImpact.UPDATED, "btn-history"); + DisposalConfirmationReportAction.RECOVER_STATE, ActionImpact.UPDATED, "btn-history"); + + // SCHEDULE + ActionableGroup scheduleGroup = new ActionableGroup<>( + messages.sidebarDisposalScheduleTitle()); + scheduleGroup.addButton(messages.applyDisposalScheduleButton(), DisposalConfirmationReportAction.DESTROY, + ActionImpact.UPDATED, "btn-trash-alt"); // DISPOSAL BIN ActionableGroup disposalBinGroup = new ActionableGroup<>(messages.sidebarDisposalBinTitle()); disposalBinGroup.addButton(messages.permanentlyDeleteFromBinButton(), - DisposalConfirmationReportAction.REMOVE_FROM_BIN, ActionImpact.DESTROYED, "btn-eraser"); + DisposalConfirmationReportAction.REMOVE_FROM_BIN, ActionImpact.DESTROYED, "btn-delete-forever"); disposalBinGroup.addButton(messages.restoreFromBinButton(), DisposalConfirmationReportAction.RESTORE_FROM_BIN, - ActionImpact.UPDATED, "btn-history"); + ActionImpact.UPDATED, "btn-restore"); - confirmationActionableBundle.addGroup(scheduleGroup).addGroup(confirmationGroup).addGroup(disposalBinGroup); + confirmationActionableBundle.addGroup(confirmationGroup).addGroup(scheduleGroup).addGroup(disposalBinGroup); return confirmationActionableBundle; } @@ -256,7 +253,7 @@ public ActionableBundle createActionsBundle() { public enum DisposalConfirmationReportAction implements Action { PRINT(PERMISSION_METHOD_RETRIEVE_DISPOSAL_CONFIRMATION_REPORT), DESTROY(PERMISSION_METHOD_DESTROY_RECORDS_DISPOSAL_CONFIRMATION), - DELETE_REPORT(PERMISSION_METHOD_DELETE_DISPOSAL_CONFIRMATION), + WITHDRAW(PERMISSION_METHOD_DELETE_DISPOSAL_CONFIRMATION), REMOVE_FROM_BIN(PERMISSION_METHOD_PERMANENTLY_DELETE_RECORDS_DISPOSAL_CONFIRMATION), RESTORE_FROM_BIN(PERMISSION_METHOD_RESTORE_RECORDS_DISPOSAL_CONFIRMATION), RE_EXECUTE(PERMISSION_METHOD_DESTROY_RECORDS_DISPOSAL_CONFIRMATION), diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalCreateConfirmationDestroyActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalCreateConfirmationDestroyActions.java index 89f9316e18..0f4cc838b8 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalCreateConfirmationDestroyActions.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalCreateConfirmationDestroyActions.java @@ -26,7 +26,7 @@ import org.roda.wui.client.common.dialogs.utils.DisposalScheduleDialogResult; import org.roda.wui.client.common.lists.utils.ClientSelectedItemsUtils; import org.roda.wui.client.common.utils.AsyncCallbackUtils; -import org.roda.wui.client.disposal.confirmations.CreateDisposalConfirmationDataPanel; +import org.roda.wui.client.disposal.confirmations.CreateDisposalConfirmation; import org.roda.wui.client.ingest.process.ShowJob; import org.roda.wui.client.process.InternalProcess; import org.roda.wui.client.services.DisposalScheduleRestService; @@ -134,7 +134,8 @@ public void onSuccess(final Long size) { public void onSuccess(Boolean result) { if (result) { doActionCallbackUpdated(); - HistoryUtils.newHistory(CreateDisposalConfirmationDataPanel.RESOLVER); + CreateDisposalConfirmation.setPendingSelection(selectedItemsList); + HistoryUtils.newHistory(CreateDisposalConfirmation.RESOLVER); } else { doActionCallbackNone(); } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalHoldAction.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalHoldAction.java new file mode 100644 index 0000000000..69f2b2ab46 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalHoldAction.java @@ -0,0 +1,30 @@ +package org.roda.wui.client.common.actions; + +import java.util.Arrays; +import java.util.List; + +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.v2.disposal.hold.DisposalHold; + +/** + * @author Miguel Guimarães + */ + +public enum DisposalHoldAction implements Actionable.Action { + NEW(RodaConstants.PERMISSION_METHOD_CREATE_DISPOSAL_HOLD), + EDIT(RodaConstants.PERMISSION_METHOD_UPDATE_DISPOSAL_HOLD), + LIFT(RodaConstants.PERMISSION_METHOD_ASSOCIATE_DISPOSAL_HOLD), + DISASSOCIATE(RodaConstants.PERMISSION_METHOD_ASSOCIATE_DISPOSAL_HOLD); + + + private final List methods; + + DisposalHoldAction(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/DisposalHoldActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalHoldActions.java index c291b126e5..6fe8ff59ac 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalHoldActions.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalHoldActions.java @@ -1,198 +1,85 @@ -/** - * 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.actions; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - +import com.google.gwt.core.client.GWT; +import com.google.gwt.regexp.shared.RegExp; +import com.google.gwt.user.client.Timer; +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.disposal.hold.DisposalHold; -import org.roda.core.data.v2.index.select.SelectedItems; -import org.roda.core.data.v2.ip.IndexedAIP; -import org.roda.core.data.v2.ip.disposalhold.DisassociateDisposalHoldRequest; -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.actions.model.ActionableObject; +import org.roda.wui.client.common.actions.callbacks.ActionAsyncCallback; import org.roda.wui.client.common.dialogs.Dialogs; -import org.roda.wui.client.common.lists.utils.ClientSelectedItemsUtils; -import org.roda.wui.client.ingest.process.ShowJob; -import org.roda.wui.client.process.InternalProcess; +import org.roda.wui.client.common.utils.AsyncCallbackUtils; +import org.roda.wui.client.disposal.hold.CreateDisposalHold; +import org.roda.wui.client.disposal.hold.EditDisposalHold; import org.roda.wui.client.services.Services; import org.roda.wui.common.client.tools.HistoryUtils; import org.roda.wui.common.client.widgets.Toast; -import com.google.gwt.core.client.GWT; -import com.google.gwt.regexp.shared.RegExp; -import com.google.gwt.user.client.Timer; -import com.google.gwt.user.client.rpc.AsyncCallback; - -import config.i18n.client.ClientMessages; - /** - * @author Tiago Fraga + * @author Miguel Guimarães */ -public class DisposalHoldActions extends AbstractActionable { - private static final DisposalHoldActions INSTANCE = new DisposalHoldActions(); - private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static final Set POSSIBLE_ACTIONS_ON_DISPOSAL_HOLD = new HashSet<>( - Arrays.asList(DisposalHoldAction.DISASSOCIATE)); - private DisposalHold disposalHold; - - public DisposalHoldActions() { - } - - public DisposalHoldActions(DisposalHold disposalHold) { - this.disposalHold = disposalHold; - } - - public static DisposalHoldActions get() { - return INSTANCE; - } - @Override - public Action[] getActions() { - return DisposalHoldActions.DisposalHoldAction.values(); - } - - @Override - public Action actionForName(String name) { - return DisposalHoldActions.DisposalHoldAction.valueOf(name); - } - - @Override - public CanActResult userCanAct(Action action, ActionableObject object) { - return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); - } +public class DisposalHoldActions { + private static final ClientMessages messages = GWT.create(ClientMessages.class); - @Override - public CanActResult contextCanAct(Action action, ActionableObject object) { - if (object.getObject() != null || object.getObjects() != null) { - return new CanActResult(POSSIBLE_ACTIONS_ON_DISPOSAL_HOLD.contains(action), CanActResult.Reason.CONTEXT, - messages.reasonInvalidContext()); - } else { - return new CanActResult(false, CanActResult.Reason.CONTEXT, messages.reasonNoObjectSelected()); - } + private DisposalHoldActions() { + // private constructor to prevent instantiation } - @Override - public void act(Action action, IndexedAIP aip, AsyncCallback callback) { - if (DisposalHoldAction.DISASSOCIATE.equals(action)) { - disassociate(objectToSelectedItems(aip, IndexedAIP.class), callback); - } else { - unsupportedAction(action, callback); - } + public static void newDisposalHold(AsyncCallback callback) { + HistoryUtils.newHistory(CreateDisposalHold.RESOLVER.getHistoryPath()); + callback.onSuccess(Actionable.ActionImpact.UPDATED); } - @Override - public void act(Action action, SelectedItems aips, AsyncCallback callback) { - if (DisposalHoldAction.DISASSOCIATE.equals(action)) { - disassociate(aips, callback); - } else { - unsupportedAction(action, callback); - } + public static void edit(DisposalHold hold, AsyncCallback callback) { + HistoryUtils.newHistory(EditDisposalHold.RESOLVER, hold.getId()); + callback.onSuccess(Actionable.ActionImpact.NONE); } - private void disassociate(final SelectedItems aips, final AsyncCallback callback) { - ClientSelectedItemsUtils.size(IndexedAIP.class, aips, new ActionNoAsyncCallback(callback) { - @Override - public void onSuccess(final Long size) { - Dialogs.showConfirmDialog(messages.disassociateDisposalHoldDialogTitle(), - messages.disassociateDisposalHoldDialogMessage(size.intValue()), messages.dialogNo(), messages.dialogYes(), - new ActionNoAsyncCallback(callback) { - @Override - public void onSuccess(Boolean result) { - if (result) { - Dialogs.showPromptDialog(messages.outcomeDetailTitle(), null, null, messages.outcomeDetailPlaceholder(), - RegExp.compile(".*"), messages.cancelButton(), messages.confirmButton(), false, true, - new ActionNoAsyncCallback(callback) { - @Override - public void onFailure(Throwable caught) { - // do nothing - } - - @Override - public void onSuccess(String details) { - DisassociateDisposalHoldRequest request = new DisassociateDisposalHoldRequest(); - request.setClear(false); - request.setDetails(details); - request.setSelectedItems(SelectedItemsUtils.convertToRESTRequest(aips)); - Services services = new Services("Lift disposal hold", "job"); - services.disposalHoldResource(s -> s.disassociateDisposalHold(request, disposalHold.getId())) - .whenComplete((job, throwable) -> { - if (throwable != null) { - callback.onFailure(throwable); - HistoryUtils.newHistory(InternalProcess.RESOLVER); - } else { - Dialogs.showJobRedirectDialog(messages.jobCreatedMessage(), new AsyncCallback() { - - @Override - public void onFailure(Throwable caught) { - Toast.showInfo(messages.runningInBackgroundTitle(), - messages.runningInBackgroundDescription()); - - Timer timer = new Timer() { - @Override - public void run() { - doActionCallbackUpdated(); - } - }; - - timer.schedule(RodaConstants.ACTION_TIMEOUT); - } - - @Override - public void onSuccess(final Void nothing) { - doActionCallbackNone(); - HistoryUtils.newHistory(ShowJob.RESOLVER, job.getId()); - } - }); + public static void lift(DisposalHold hold, AsyncCallback callback) { + Dialogs.showConfirmDialog(messages.liftDisposalHoldDialogTitle(), messages.liftDisposalHoldDialogMessage(1), + messages.cancelButton(), messages.confirmButton(), new ActionAsyncCallback(callback) { + @Override + public void onFailure(Throwable throwable) { + callback.onSuccess(Actionable.ActionImpact.NONE); + } + + @Override + public void onSuccess(Boolean confirm) { + if (confirm) { + Dialogs.showPromptDialog(messages.outcomeDetailTitle(), null, null, messages.outcomeDetailPlaceholder(), + RegExp.compile(".*"), messages.cancelButton(), messages.confirmButton(), false, true, + new AsyncCallback() { + @Override + public void onFailure(Throwable throwable) { + // do nothing + } + + @Override + public void onSuccess(String details) { + Services services = new Services("Lift disposal hold", "job"); + + services.disposalHoldResource(s -> s.liftDisposalHold(hold.getUUID(), details)) + .whenComplete((job, throwable) -> { + if (throwable != null) { + AsyncCallbackUtils.defaultFailureTreatment(throwable); + } else { + Toast.showInfo(messages.runningInBackgroundTitle(), messages.updateDisposalHoldMessage()); + Timer timer = new Timer() { + @Override + public void run() { + callback.onSuccess(Actionable.ActionImpact.UPDATED); } - }); - } - }); - } else { - doActionCallbackNone(); - } - } - }); - } - }); + }; + + timer.schedule(RodaConstants.ACTION_TIMEOUT); + } + }); + } + }); + } + } + }); } - - @Override - public ActionableBundle createActionsBundle() { - ActionableBundle disposalHoldActionableBundle = new ActionableBundle<>(); - - ActionableGroup managementGroup = new ActionableGroup<>(messages.sidebarActionsTitle()); - managementGroup.addButton(messages.disassociateDisposalHoldButton(), DisposalHoldAction.DISASSOCIATE, - ActionImpact.UPDATED, "btn-lift-hold"); - - disposalHoldActionableBundle.addGroup(managementGroup); - return disposalHoldActionableBundle; - } - - public enum DisposalHoldAction implements Action { - DISASSOCIATE(RodaConstants.PERMISSION_METHOD_ASSOCIATE_DISPOSAL_HOLD); - - private final List methods; - - DisposalHoldAction(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/DisposalHoldSearchWrapperActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalHoldSearchWrapperActions.java new file mode 100644 index 0000000000..7583bc8446 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalHoldSearchWrapperActions.java @@ -0,0 +1,118 @@ +package org.roda.wui.client.common.actions; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.core.data.v2.disposal.hold.DisposalHoldState; +import org.roda.core.data.v2.index.select.SelectedItems; +import org.roda.wui.client.common.actions.model.ActionableBundle; +import org.roda.wui.client.common.actions.model.ActionableGroup; +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.rpc.AsyncCallback; + +import config.i18n.client.ClientMessages; + +/** + * @author Miguel Guimarães + */ + +public class DisposalHoldSearchWrapperActions extends AbstractActionable { + private static final DisposalHoldSearchWrapperActions INSTANCE = new DisposalHoldSearchWrapperActions(); + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static final Set POSSIBLE_ACTIONS_WITHOUT_HOLD = new HashSet<>( + Arrays.asList(DisposalHoldAction.NEW)); + + private static final Set POSSIBLE_ACTIONS_ON_SINGLE_DISPOSAL_HOLD = new HashSet<>( + Arrays.asList(DisposalHoldAction.EDIT, DisposalHoldAction.LIFT)); + + private DisposalHoldSearchWrapperActions() { + } + + public static DisposalHoldSearchWrapperActions get() { + return INSTANCE; + } + + @Override + public Action[] getActions() { + return DisposalHoldAction.values(); + } + + @Override + public CanActResult userCanAct(Action action) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult contextCanAct(Action action) { + return new CanActResult(POSSIBLE_ACTIONS_WITHOUT_HOLD.contains(action), CanActResult.Reason.CONTEXT, + messages.reasonNoObjectSelected()); + } + + @Override + public CanActResult userCanAct(Action action, DisposalHold object) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult contextCanAct(Action action, DisposalHold object) { + if (DisposalHoldState.LIFTED.equals(object.getState())) { + return new CanActResult(false, CanActResult.Reason.CONTEXT, messages.reasonHoldAlreadyLifted()); + } + + return new CanActResult(POSSIBLE_ACTIONS_ON_SINGLE_DISPOSAL_HOLD.contains(action), CanActResult.Reason.CONTEXT, + messages.reasonCantActOnSingleObject()); + } + + @Override + public CanActResult userCanAct(Action action, SelectedItems objects) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult contextCanAct(Action action, SelectedItems objects) { + return new CanActResult(false, CanActResult.Reason.CONTEXT, messages.reasonCantActOnMultipleObjects()); + } + + @Override + public void act(Action action, DisposalHold hold, AsyncCallback callback) { + if (DisposalHoldAction.EDIT.equals(action)) { + DisposalHoldActions.edit(hold, callback); + } else if (DisposalHoldAction.LIFT.equals(action)) { + DisposalHoldActions.lift(hold, callback); + } else { + unsupportedAction(action, callback); + } + } + + @Override + public void act(Action action, AsyncCallback callback) { + if (DisposalHoldAction.NEW.equals(action)) { + DisposalHoldActions.newDisposalHold(callback); + } else { + unsupportedAction(action, callback); + } + } + + @Override + public ActionableBundle createActionsBundle() { + ActionableBundle disposalHoldActionableBundle = new ActionableBundle<>(); + + ActionableGroup managementGroup = new ActionableGroup<>(messages.sidebarActionsTitle()); + managementGroup.addButton(messages.newDisposalHoldTitle(), DisposalHoldAction.NEW, ActionImpact.UPDATED, + "btn-plus-circle"); + + managementGroup.addButton(messages.editButton(), DisposalHoldAction.EDIT, ActionImpact.UPDATED, "btn-edit"); + + managementGroup.addButton(messages.liftButton(), DisposalHoldAction.LIFT, ActionImpact.UPDATED, "btn-lift-hold"); + + disposalHoldActionableBundle.addGroup(managementGroup); + return disposalHoldActionableBundle; + } + + @Override + public Action actionForName(String name) { + return null; + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalHoldToolbarActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalHoldToolbarActions.java new file mode 100644 index 0000000000..b4372aa7cf --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalHoldToolbarActions.java @@ -0,0 +1,102 @@ +/** + * 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.actions; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.rpc.AsyncCallback; +import config.i18n.client.ClientMessages; +import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.core.data.v2.disposal.hold.DisposalHoldState; +import org.roda.wui.client.common.actions.model.ActionableBundle; +import org.roda.wui.client.common.actions.model.ActionableGroup; +import org.roda.wui.client.common.actions.model.ActionableObject; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Miguel Guimarães + */ +public class DisposalHoldToolbarActions extends AbstractActionable { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + + private static final Set POSSIBLE_ACTIONS_ON_DISPOSAL_HOLD = new HashSet<>( + Arrays.asList(DisposalHoldAction.LIFT, DisposalHoldAction.EDIT)); + + private static final DisposalHoldToolbarActions GENERAL_INSTANCE = new DisposalHoldToolbarActions(); + + private DisposalHoldToolbarActions() { + } + + public static DisposalHoldToolbarActions get() { + return GENERAL_INSTANCE; + } + + @Override + public Action[] getActions() { + return DisposalHoldAction.values(); + } + + @Override + public CanActResult userCanAct(Action action, ActionableObject object) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult userCanAct(Action action, DisposalHold object) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult contextCanAct(Action action, DisposalHold object) { + if (object.getState().equals(DisposalHoldState.LIFTED)) { + return new CanActResult(false, CanActResult.Reason.CONTEXT, messages.reasonHoldAlreadyLifted()); + } + + return new CanActResult(POSSIBLE_ACTIONS_ON_DISPOSAL_HOLD.contains(action), CanActResult.Reason.CONTEXT, + messages.reasonCantActOnGroup()); + } + + @Override + public CanActResult contextCanAct(Action action, ActionableObject object) { + if (object.getObject().getState().equals(DisposalHoldState.LIFTED)) { + return new CanActResult(false, CanActResult.Reason.CONTEXT, messages.reasonHoldAlreadyLifted()); + } + return new CanActResult(POSSIBLE_ACTIONS_ON_DISPOSAL_HOLD.contains(action), CanActResult.Reason.CONTEXT, + messages.reasonCantActOnGroup()); + } + + @Override + public void act(Action action, DisposalHold hold, AsyncCallback callback) { + if (DisposalHoldAction.EDIT.equals(action)) { + DisposalHoldActions.edit(hold, callback); + } else if (DisposalHoldAction.LIFT.equals(action)) { + DisposalHoldActions.lift(hold, callback); + } else { + unsupportedAction(action, callback); + } + } + + @Override + public ActionableBundle createActionsBundle() { + ActionableBundle disposalHoldActionableBundle = new ActionableBundle<>(); + + ActionableGroup managementGroup = new ActionableGroup<>(messages.manage(), "btn-edit"); + managementGroup.addButton(messages.editButton(), DisposalHoldAction.EDIT, ActionImpact.UPDATED, "fas fa-pencil"); + managementGroup.addButton(messages.liftButton(), DisposalHoldAction.LIFT, ActionImpact.UPDATED, "btn-lift-hold"); + + disposalHoldActionableBundle.addGroup(managementGroup); + return disposalHoldActionableBundle; + } + + @Override + public Action actionForName(String name) { + return null; + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalRuleAction.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalRuleAction.java new file mode 100644 index 0000000000..f7ccd4f2c2 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalRuleAction.java @@ -0,0 +1,30 @@ +package org.roda.wui.client.common.actions; + +import java.util.Arrays; +import java.util.List; + +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.v2.disposal.rule.DisposalRule; + +/** + * @author Miguel Guimarães + */ + +public enum DisposalRuleAction implements Actionable.Action { + NEW(RodaConstants.PERMISSION_METHOD_CREATE_DISPOSAL_RULE), + EDIT(RodaConstants.PERMISSION_METHOD_UPDATE_DISPOSAL_RULE), + APPLY_RULES(RodaConstants.PERMISSION_METHOD_ASSOCIATE_DISPOSAL_SCHEDULE), + CHANGE_ORDER(RodaConstants.PERMISSION_METHOD_UPDATE_DISPOSAL_RULE), + REMOVE(RodaConstants.PERMISSION_METHOD_DELETE_DISPOSAL_RULE); + + private final List methods; + + DisposalRuleAction(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/DisposalRuleActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalRuleActions.java index 007b8ecdac..370e0816a7 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalRuleActions.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalRuleActions.java @@ -7,11 +7,19 @@ */ package org.roda.wui.client.common.actions; +import com.google.gwt.user.client.Timer; +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.utils.SelectedItemsUtils; +import org.roda.core.data.v2.disposal.rule.ChangeOrderRequest; +import org.roda.core.data.v2.disposal.rule.DisposalRule; +import org.roda.core.data.v2.index.select.SelectedItems; import org.roda.wui.client.common.NoAsyncCallback; import org.roda.wui.client.common.dialogs.Dialogs; import org.roda.wui.client.common.dialogs.DisposalDialogs; import org.roda.wui.client.common.utils.AsyncCallbackUtils; import org.roda.wui.client.disposal.policy.DisposalPolicy; +import org.roda.wui.client.disposal.rule.CreateDisposalRule; +import org.roda.wui.client.disposal.rule.EditDisposalRule; import org.roda.wui.client.ingest.process.ShowJob; import org.roda.wui.client.process.InternalProcess; import org.roda.wui.client.services.Services; @@ -29,6 +37,14 @@ public class DisposalRuleActions { private static final ClientMessages messages = GWT.create(ClientMessages.class); + private DisposalRuleActions() { + } + + public static void newRule(AsyncCallback callback) { + HistoryUtils.newHistory(CreateDisposalRule.RESOLVER.getHistoryPath()); + callback.onSuccess(Actionable.ActionImpact.NONE); + } + public static void applyDisposalRulesAction() { Dialogs.showConfirmDialog(messages.applyDisposalRulesDialogTitle(), messages.applyDisposalRulesDialogMessage(), messages.dialogNo(), messages.dialogYes(), new NoAsyncCallback() { @@ -66,4 +82,73 @@ public void onSuccess(final Void nothing) { } }); } + + public static void editDisposalRule(DisposalRule rule, AsyncCallback callback) { + HistoryUtils.newHistory(EditDisposalRule.RESOLVER, rule.getId()); + callback.onSuccess(Actionable.ActionImpact.UPDATED); + } + + public static void removeDisposalRule(DisposalRule rule, AsyncCallback callback) { + Dialogs.showConfirmDialog(messages.deleteDisposalRuleDialogTitle(), + messages.deleteDisposalRuleDialogMessage(rule.getTitle()), messages.dialogNo(), messages.dialogYes(), + new NoAsyncCallback() { + @Override + public void onSuccess(Boolean confirm) { + if (confirm) { + Services services = new Services("Delete disposal rule", "delete"); + services.disposalRuleResource(s -> s.deleteDisposalRule(rule.getId())).whenComplete((unused, throwable) -> { + if (throwable != null) { + AsyncCallbackUtils.defaultFailureTreatment(throwable); + } else { + Toast.showInfo(messages.showDisposalRuleTitle(), + messages.deleteDisposalRuleSuccessMessage(rule.getTitle())); + DisposalRuleActions.applyDisposalRulesAction(); + } + }); + } else { + callback.onSuccess(Actionable.ActionImpact.NONE); + } + } + }); + } + + public static void removeMultipleDisposalRules(SelectedItems items, + AsyncCallback callback) { + + } + + public static void changeDisposalRulesOrder(SelectedItems items, + AsyncCallback callback) { + DisposalDialogs.reOrderDisposalRules(messages.editRules(), messages.disposalRuleChangeOrderMessage(), + new AsyncCallback() { + @Override + public void onSuccess(ChangeOrderRequest changeOrderRequest) { + changeOrderRequest.setItems(SelectedItemsUtils.convertToRESTRequest(items)); + + Services services = new Services("Update disposal rule order", "update"); + services.disposalRuleResource(s -> s.changeDisposalRuleOrder(changeOrderRequest)) + .whenComplete((unused, throwable) -> { + if (throwable != null) { + AsyncCallbackUtils.defaultFailureTreatment(throwable); + callback.onSuccess(Actionable.ActionImpact.NONE); + } else { + Toast.showInfo(messages.showDisposalRuleTitle(), messages.updateDisposalRuleOrderSuccessMessage()); + Timer timer = new Timer() { + @Override + public void run() { + callback.onSuccess(Actionable.ActionImpact.UPDATED); + } + }; + + timer.schedule(RodaConstants.ACTION_TIMEOUT); + } + }); + } + + @Override + public void onFailure(Throwable caught) { + callback.onSuccess(Actionable.ActionImpact.NONE); + } + }); + } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalRuleSearchWrapperActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalRuleSearchWrapperActions.java new file mode 100644 index 0000000000..546a8323c0 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalRuleSearchWrapperActions.java @@ -0,0 +1,136 @@ +package org.roda.wui.client.common.actions; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.rpc.AsyncCallback; +import config.i18n.client.ClientMessages; +import org.roda.core.data.v2.disposal.rule.DisposalRule; +import org.roda.core.data.v2.index.select.SelectedItems; +import org.roda.wui.client.common.actions.model.ActionableBundle; +import org.roda.wui.client.common.actions.model.ActionableGroup; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Miguel Guimarães + */ + +public class DisposalRuleSearchWrapperActions extends AbstractActionable { + private static final DisposalRuleSearchWrapperActions INSTANCE = new DisposalRuleSearchWrapperActions(); + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static final Set POSSIBLE_ACTIONS_WITHOUT_DISPOSAL_RULE = new HashSet<>( + Arrays.asList(DisposalRuleAction.NEW, DisposalRuleAction.APPLY_RULES)); + + private static final Set POSSIBLE_ACTIONS_ON_SINGLE_DISPOSAL_RULE = new HashSet<>( + Arrays.asList(DisposalRuleAction.EDIT, DisposalRuleAction.REMOVE, DisposalRuleAction.CHANGE_ORDER)); + + private static final Set POSSIBLE_ACTIONS_ON_MULTIPLE_DISPOSAL_RULE = new HashSet<>( + Arrays.asList(DisposalRuleAction.REMOVE, DisposalRuleAction.CHANGE_ORDER)); + + private DisposalRuleSearchWrapperActions() { + } + + public static DisposalRuleSearchWrapperActions get() { + return INSTANCE; + } + + @Override + public Action[] getActions() { + return DisposalRuleAction.values(); + } + + @Override + public CanActResult userCanAct(Action action) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult contextCanAct(Action action) { + return new CanActResult(POSSIBLE_ACTIONS_WITHOUT_DISPOSAL_RULE.contains(action), CanActResult.Reason.CONTEXT, + messages.reasonNoObjectSelected()); + } + + @Override + public CanActResult userCanAct(Action action, DisposalRule object) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult contextCanAct(Action action, DisposalRule object) { + return new CanActResult(POSSIBLE_ACTIONS_ON_SINGLE_DISPOSAL_RULE.contains(action), CanActResult.Reason.CONTEXT, + messages.reasonCantActOnSingleObject()); + } + + @Override + public CanActResult userCanAct(Action action, SelectedItems objects) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult contextCanAct(Action action, SelectedItems objects) { + return new CanActResult(POSSIBLE_ACTIONS_ON_MULTIPLE_DISPOSAL_RULE.contains(action), CanActResult.Reason.CONTEXT, + messages.reasonCantActOnMultipleObjects()); + } + + @Override + public void act(Action action, AsyncCallback callback) { + if (DisposalRuleAction.NEW.equals(action)) { + DisposalRuleActions.newRule(callback); + } else if (DisposalRuleAction.APPLY_RULES.equals(action)) { + DisposalRuleActions.applyDisposalRulesAction(); + callback.onSuccess(ActionImpact.NONE); + } else { + unsupportedAction(action, callback); + } + } + + @Override + public void act(Action action, DisposalRule rule, AsyncCallback callback) { + if (DisposalRuleAction.EDIT.equals(action)) { + DisposalRuleActions.editDisposalRule(rule, callback); + } else if (DisposalRuleAction.REMOVE.equals(action)) { + DisposalRuleActions.removeDisposalRule(rule, callback); + } else if (DisposalRuleAction.CHANGE_ORDER.equals(action)) { + DisposalRuleActions.changeDisposalRulesOrder(objectToSelectedItems(rule, DisposalRule.class), callback); + } else { + unsupportedAction(action, callback); + } + } + + @Override + public void act(Action action, SelectedItems objects, + AsyncCallback callback) { + if (DisposalRuleAction.REMOVE.equals(action)) { + DisposalRuleActions.removeMultipleDisposalRules(objects, callback); + } else if (DisposalRuleAction.CHANGE_ORDER.equals(action)) { + DisposalRuleActions.changeDisposalRulesOrder(objects, callback); + } else { + unsupportedAction(action, callback); + } + } + + @Override + public ActionableBundle createActionsBundle() { + ActionableBundle disposalRuleActionableBundle = new ActionableBundle<>(); + + ActionableGroup managementGroup = new ActionableGroup<>(messages.sidebarActionsTitle()); + managementGroup.addButton(messages.newDisposalRuleTitle(), DisposalRuleAction.NEW, ActionImpact.UPDATED, + "btn-plus-circle"); + + managementGroup.addButton(messages.editButton(), DisposalRuleAction.EDIT, ActionImpact.UPDATED, "btn-edit"); + managementGroup.addButton(messages.editRulesOrder(), DisposalRuleAction.CHANGE_ORDER, ActionImpact.UPDATED, + "btn-edit-order"); + managementGroup.addButton(messages.removeButton(), DisposalRuleAction.REMOVE, ActionImpact.UPDATED, "btn-ban"); + managementGroup.addButton(messages.applyRules(), DisposalRuleAction.APPLY_RULES, ActionImpact.UPDATED, "btn-play"); + + disposalRuleActionableBundle.addGroup(managementGroup); + return disposalRuleActionableBundle; + } + + @Override + public Action actionForName(String name) { + return null; + } + +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalRuleToolbarActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalRuleToolbarActions.java new file mode 100644 index 0000000000..40f7b06400 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalRuleToolbarActions.java @@ -0,0 +1,98 @@ +package org.roda.wui.client.common.actions; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.roda.core.data.v2.disposal.rule.DisposalRule; +import org.roda.core.data.v2.index.select.SelectedItems; +import org.roda.wui.client.common.actions.model.ActionableBundle; +import org.roda.wui.client.common.actions.model.ActionableGroup; +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.rpc.AsyncCallback; + +import config.i18n.client.ClientMessages; + +/** + * @author Miguel Guimarães + */ + +public class DisposalRuleToolbarActions extends AbstractActionable { + private static final DisposalRuleToolbarActions INSTANCE = new DisposalRuleToolbarActions(); + private static final ClientMessages messages = GWT.create(ClientMessages.class); + + private static final Set POSSIBLE_ACTIONS_ON_SINGLE_DISPOSAL_RULE = new HashSet<>( + Arrays.asList(DisposalRuleAction.EDIT, DisposalRuleAction.REMOVE)); + + private DisposalRuleToolbarActions() { + } + + public static DisposalRuleToolbarActions get() { + return INSTANCE; + } + + @Override + public Action[] getActions() { + return DisposalRuleAction.values(); + } + + @Override + public CanActResult userCanAct(Action action) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult contextCanAct(Action action) { + return new CanActResult(false, CanActResult.Reason.CONTEXT, messages.reasonNoObjectSelected()); + } + + @Override + public CanActResult userCanAct(Action action, DisposalRule object) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult contextCanAct(Action action, DisposalRule object) { + return new CanActResult(POSSIBLE_ACTIONS_ON_SINGLE_DISPOSAL_RULE.contains(action), CanActResult.Reason.CONTEXT, + messages.reasonCantActOnSingleObject()); + } + + @Override + public CanActResult userCanAct(Action action, SelectedItems objects) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult contextCanAct(Action action, SelectedItems objects) { + return new CanActResult(false, CanActResult.Reason.CONTEXT, messages.reasonCantActOnMultipleObjects()); + } + + @Override + public void act(Action action, DisposalRule rule, AsyncCallback callback) { + if (DisposalRuleAction.REMOVE.equals(action)) { + DisposalRuleActions.removeDisposalRule(rule, callback); + } else if (DisposalRuleAction.EDIT.equals(action)) { + DisposalRuleActions.editDisposalRule(rule, callback); + } else { + unsupportedAction(action, callback); + } + } + + @Override + public ActionableBundle createActionsBundle() { + ActionableBundle disposalRuleActionableBundle = new ActionableBundle<>(); + + ActionableGroup managementGroup = new ActionableGroup<>(messages.manage()); + + managementGroup.addButton(messages.editButton(), DisposalRuleAction.EDIT, ActionImpact.UPDATED, "btn-edit"); + managementGroup.addButton(messages.removeButton(), DisposalRuleAction.REMOVE, ActionImpact.UPDATED, "btn-ban"); + + disposalRuleActionableBundle.addGroup(managementGroup); + return disposalRuleActionableBundle; + } + + @Override + public Action actionForName(String name) { + return null; + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalScheduleAction.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalScheduleAction.java new file mode 100644 index 0000000000..7440db1d7a --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalScheduleAction.java @@ -0,0 +1,29 @@ +package org.roda.wui.client.common.actions; + +import java.util.Arrays; +import java.util.List; + +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; + +/** + * @author Miguel Guimarães + */ + +public enum DisposalScheduleAction implements Actionable.Action { + NEW(RodaConstants.PERMISSION_METHOD_CREATE_DISPOSAL_SCHEDULE), + REMOVE(RodaConstants.PERMISSION_METHOD_DELETE_DISPOSAL_SCHEDULE), + DISASSOCIATE(RodaConstants.PERMISSION_METHOD_ASSOCIATE_DISPOSAL_SCHEDULE), + EDIT(RodaConstants.PERMISSION_METHOD_UPDATE_DISPOSAL_SCHEDULE); + + private final List methods; + + DisposalScheduleAction(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/DisposalScheduleActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalScheduleActions.java index 1eea5961b5..6d744b3ab5 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalScheduleActions.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalScheduleActions.java @@ -1,186 +1,76 @@ -/** - * 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.actions; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.Timer; +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.index.select.SelectedItems; -import org.roda.core.data.v2.ip.IndexedAIP; -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.actions.model.ActionableObject; +import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; +import org.roda.wui.client.common.actions.callbacks.ActionAsyncCallback; import org.roda.wui.client.common.dialogs.Dialogs; -import org.roda.wui.client.common.lists.utils.ClientSelectedItemsUtils; -import org.roda.wui.client.ingest.process.ShowJob; -import org.roda.wui.client.process.InternalProcess; +import org.roda.wui.client.disposal.schedule.CreateDisposalSchedule; +import org.roda.wui.client.disposal.schedule.EditDisposalSchedule; import org.roda.wui.client.services.Services; import org.roda.wui.common.client.tools.HistoryUtils; import org.roda.wui.common.client.widgets.Toast; -import com.google.gwt.core.client.GWT; -import com.google.gwt.user.client.Timer; -import com.google.gwt.user.client.rpc.AsyncCallback; - -import config.i18n.client.ClientMessages; - /** * @author Miguel Guimarães */ -public class DisposalScheduleActions extends AbstractActionable { - public static final String NO_DISPOSAL_SCHEDULE_ID = null; - - private static final DisposalScheduleActions INSTANCE = new DisposalScheduleActions(NO_DISPOSAL_SCHEDULE_ID); - private static final ClientMessages messages = GWT.create(ClientMessages.class); - - private final String disposalScheduleId; - - private DisposalScheduleActions(String disposalScheduleId) { - this.disposalScheduleId = disposalScheduleId; - } - - private static final Set POSSIBLE_ACTIONS_ON_DISPOSAL_SCHEDULE = new HashSet<>( - Collections.singletonList(DisposalScheduleAction.DISASSOCIATE)); - - public enum DisposalScheduleAction implements Action { - DISASSOCIATE(RodaConstants.PERMISSION_METHOD_ASSOCIATE_DISPOSAL_SCHEDULE); - - private List methods; - DisposalScheduleAction(String... methods) { - this.methods = Arrays.asList(methods); - } +public class DisposalScheduleActions { - @Override - public List getMethods() { - return this.methods; - } - } - - @Override - public Action[] getActions() { - return DisposalScheduleAction.values(); - } - - public static DisposalScheduleActions get() { - return INSTANCE; - } - - public static DisposalScheduleActions get(String disposalScheduleId) { - return new DisposalScheduleActions(disposalScheduleId); - } - - @Override - public CanActResult userCanAct(Action action, ActionableObject object) { - return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); - } + private static final ClientMessages messages = GWT.create(ClientMessages.class); - @Override - public CanActResult contextCanAct(Action action, ActionableObject object) { - if (object.getObject() != null || object.getObjects() != null) { - return new CanActResult(POSSIBLE_ACTIONS_ON_DISPOSAL_SCHEDULE.contains(action), CanActResult.Reason.CONTEXT, - messages.reasonInvalidContext()); - } else { - return new CanActResult(false, CanActResult.Reason.CONTEXT, messages.reasonNoObjectSelected()); - } + private DisposalScheduleActions() { + // Private constructor to prevent instantiation } - @Override - public void act(Action action, IndexedAIP aip, AsyncCallback callback) { - if (DisposalScheduleAction.DISASSOCIATE.equals(action)) { - disassociate(objectToSelectedItems(aip, IndexedAIP.class), callback); - } else { - unsupportedAction(action, callback); - } + public static void newDisposalSchedule(AsyncCallback callback) { + HistoryUtils.newHistory(CreateDisposalSchedule.RESOLVER.getHistoryPath()); + callback.onSuccess(Actionable.ActionImpact.UPDATED); } - @Override - public void act(Action action, SelectedItems items, AsyncCallback callback) { - if (DisposalScheduleAction.DISASSOCIATE.equals(action)) { - disassociate(items, callback); - } else { - unsupportedAction(action, callback); - } + public static void editDisposalSchedule(DisposalSchedule schedule, AsyncCallback callback) { + HistoryUtils.newHistory(EditDisposalSchedule.RESOLVER, schedule.getId()); + callback.onSuccess(Actionable.ActionImpact.UPDATED); } - private void disassociate(SelectedItems items, AsyncCallback callback) { - ClientSelectedItemsUtils.size(IndexedAIP.class, items, new ActionNoAsyncCallback(callback) { - @Override - public void onSuccess(final Long size) { - Dialogs.showConfirmDialog(messages.disassociateDisposalScheduleDialogTitle(), - messages.disassociateDisposalScheduleDialogMessage(size.intValue()), messages.dialogNo(), - messages.dialogYes(), new ActionNoAsyncCallback(callback) { - @Override - public void onSuccess(Boolean result) { - if (result) { - Services services = new Services("Disassociate disposal schedule from AIP", "job"); - services - .disposalScheduleResource( - s -> s.disassociatedDisposalSchedule(SelectedItemsUtils.convertToRESTRequest(items))) - .whenComplete((job, throwable) -> { - if (throwable != null) { - callback.onFailure(throwable); - HistoryUtils.newHistory(InternalProcess.RESOLVER); - } else { - Dialogs.showJobRedirectDialog(messages.jobCreatedMessage(), new AsyncCallback() { - - @Override - public void onFailure(Throwable caught) { - Toast.showInfo(messages.runningInBackgroundTitle(), - messages.runningInBackgroundDescription()); - - Timer timer = new Timer() { - @Override - public void run() { - doActionCallbackUpdated(); - } - }; - - timer.schedule(RodaConstants.ACTION_TIMEOUT); - } - - @Override - public void onSuccess(final Void nothing) { - doActionCallbackNone(); - HistoryUtils.newHistory(ShowJob.RESOLVER, job.getId()); - } - }); + public static void removeDisposalSchedule(DisposalSchedule schedule, + AsyncCallback callback) { + Dialogs.showConfirmDialog(messages.disposalScheduleRemoveConfirmDialogTitle(), + messages.disposalScheduleRemoveConfirmDialogMessage(), messages.dialogNo(), messages.dialogYes(), + new ActionAsyncCallback(callback) { + @Override + public void onSuccess(Boolean confirmed) { + if (confirmed) { + Services services = new Services("Delete disposal schedule", "delete"); + services.disposalScheduleResource(s -> s.deleteDisposalSchedule(schedule.getId())) + .whenComplete((res, error) -> { + if (error == null) { + Timer timer = new Timer() { + @Override + public void run() { + Toast.showInfo(messages.disposalSchedulesTitle(), messages.disposalScheduleRemovedWithSuccess()); + doActionCallbackDestroyed(); } - }); - } else { - doActionCallbackNone(); - } - } - }); - } - }); - } - - @Override - public ActionableBundle createActionsBundle() { - ActionableBundle disposalScheduleActionableBundle = new ActionableBundle<>(); - - ActionableGroup managementGroup = new ActionableGroup<>(messages.sidebarActionsTitle()); - managementGroup.addButton(messages.disassociateDisposalScheduleButton(), DisposalScheduleAction.DISASSOCIATE, - ActionImpact.UPDATED, "fas fa-calendar"); - - disposalScheduleActionableBundle.addGroup(managementGroup); - return disposalScheduleActionableBundle; - } - - @Override - public Action actionForName(String name) { - return null; + }; + + timer.schedule(RodaConstants.ACTION_TIMEOUT); + } else { + Toast.showError(error.getMessage()); + callback.onSuccess(Actionable.ActionImpact.NONE); + } + }); + } else { + doActionCallbackNone(); + } + } + + @Override + public void onFailure(Throwable caught) { + callback.onFailure(caught); + } + }); } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalScheduleSearchWrapperActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalScheduleSearchWrapperActions.java new file mode 100644 index 0000000000..86c3529c7e --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalScheduleSearchWrapperActions.java @@ -0,0 +1,113 @@ +package org.roda.wui.client.common.actions; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.rpc.AsyncCallback; +import config.i18n.client.ClientMessages; +import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; +import org.roda.core.data.v2.index.select.SelectedItems; +import org.roda.wui.client.common.actions.model.ActionableBundle; +import org.roda.wui.client.common.actions.model.ActionableGroup; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Miguel Guimarães + */ + +public class DisposalScheduleSearchWrapperActions extends AbstractActionable { + private static final DisposalScheduleSearchWrapperActions INSTANCE = new DisposalScheduleSearchWrapperActions(); + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static final Set POSSIBLE_ACTIONS_WITHOUT_SCHEDULE = new HashSet<>( + Arrays.asList(DisposalScheduleAction.NEW)); + + private static final Set POSSIBLE_ACTIONS_ON_SINGLE_DISPOSAL_SCHEDULE = new HashSet<>( + Arrays.asList(DisposalScheduleAction.EDIT, DisposalScheduleAction.REMOVE)); + + private DisposalScheduleSearchWrapperActions() { + } + + public static DisposalScheduleSearchWrapperActions get() { + return INSTANCE; + } + + @Override + public Action[] getActions() { + return DisposalScheduleAction.values(); + } + + @Override + public CanActResult userCanAct(Action action) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult contextCanAct(Action action) { + return new CanActResult(POSSIBLE_ACTIONS_WITHOUT_SCHEDULE.contains(action), CanActResult.Reason.CONTEXT, + messages.reasonNoObjectSelected()); + } + + @Override + public CanActResult userCanAct(Action action, DisposalSchedule object) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult contextCanAct(Action action, DisposalSchedule object) { + return new CanActResult(POSSIBLE_ACTIONS_ON_SINGLE_DISPOSAL_SCHEDULE.contains(action), CanActResult.Reason.CONTEXT, + messages.reasonCantActOnSingleObject()); + } + + @Override + public CanActResult userCanAct(Action action, SelectedItems objects) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult contextCanAct(Action action, SelectedItems objects) { + return new CanActResult(false, CanActResult.Reason.CONTEXT, messages.reasonCantActOnMultipleObjects()); + } + + @Override + public void act(Action action, AsyncCallback callback) { + if (DisposalScheduleAction.NEW.equals(action)) { + DisposalScheduleActions.newDisposalSchedule(callback); + } else { + unsupportedAction(action, callback); + } + } + + @Override + public void act(Action action, DisposalSchedule schedule, AsyncCallback callback) { + if (DisposalScheduleAction.REMOVE.equals(action)) { + DisposalScheduleActions.removeDisposalSchedule(schedule, callback); + } else if (DisposalScheduleAction.EDIT.equals(action)) { + DisposalScheduleActions.editDisposalSchedule(schedule, callback); + } else { + unsupportedAction(action, callback); + } + } + + @Override + public ActionableBundle createActionsBundle() { + ActionableBundle disposalScheduleActionableBundle = new ActionableBundle<>(); + + ActionableGroup managementGroup = new ActionableGroup<>(messages.sidebarActionsTitle()); + managementGroup.addButton(messages.newDisposalScheduleTitle(), DisposalScheduleAction.NEW, ActionImpact.UPDATED, + "btn-plus-circle"); + + managementGroup.addButton(messages.editButton(), DisposalScheduleAction.EDIT, ActionImpact.UPDATED, "btn-edit"); + + managementGroup.addButton(messages.removeButton(), DisposalScheduleAction.REMOVE, ActionImpact.DESTROYED, + "btn-ban"); + + disposalScheduleActionableBundle.addGroup(managementGroup); + return disposalScheduleActionableBundle; + } + + @Override + public Action actionForName(String name) { + return null; + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalScheduleToolbarActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalScheduleToolbarActions.java new file mode 100644 index 0000000000..cfa382269e --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/DisposalScheduleToolbarActions.java @@ -0,0 +1,103 @@ +/** + * 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.actions; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; +import org.roda.wui.client.common.actions.model.ActionableBundle; +import org.roda.wui.client.common.actions.model.ActionableGroup; +import org.roda.wui.client.common.actions.model.ActionableObject; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.rpc.AsyncCallback; + +import config.i18n.client.ClientMessages; +import org.roda.wui.client.disposal.schedule.EditDisposalSchedule; +import org.roda.wui.client.disposal.schedule.ShowDisposalSchedule; +import org.roda.wui.client.services.Services; +import org.roda.wui.common.client.tools.HistoryUtils; +import org.roda.wui.common.client.widgets.Toast; + +/** + * @author Miguel Guimarães + */ +public class DisposalScheduleToolbarActions extends AbstractActionable { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + + private static final Set POSSIBLE_ACTIONS_ON_DISPOSAL_SCHEDULE = new HashSet<>( + Arrays.asList(DisposalScheduleAction.EDIT, DisposalScheduleAction.REMOVE)); + + private static final DisposalScheduleToolbarActions GENERAL_INSTANCE = new DisposalScheduleToolbarActions(); + + private DisposalScheduleToolbarActions() { + } + + public static DisposalScheduleToolbarActions get() { + return GENERAL_INSTANCE; + } + + @Override + public Action[] getActions() { + return DisposalScheduleAction.values(); + } + + @Override + public CanActResult userCanAct(Action action, ActionableObject object) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult contextCanAct(Action action, ActionableObject object) { + return new CanActResult(POSSIBLE_ACTIONS_ON_DISPOSAL_SCHEDULE.contains(action), CanActResult.Reason.CONTEXT, + messages.reasonCantActOnGroup()); + } + + @Override + public CanActResult userCanAct(Action action, DisposalSchedule object) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult contextCanAct(Action action, DisposalSchedule object) { + return new CanActResult(POSSIBLE_ACTIONS_ON_DISPOSAL_SCHEDULE.contains(action), CanActResult.Reason.CONTEXT, + messages.reasonCantActOnSingleObject()); + } + + @Override + public void act(Action action, DisposalSchedule schedule, AsyncCallback callback) { + if (DisposalScheduleAction.EDIT.equals(action)) { + DisposalScheduleActions.editDisposalSchedule(schedule, callback); + } else if (DisposalScheduleAction.REMOVE.equals(action)) { + DisposalScheduleActions.removeDisposalSchedule(schedule, callback); + } else { + unsupportedAction(action, callback); + } + } + + @Override + public ActionableBundle createActionsBundle() { + ActionableBundle disposalScheduleActionableBundle = new ActionableBundle<>(); + + ActionableGroup managementGroup = new ActionableGroup<>(messages.manage(), "btn-edit"); + managementGroup.addButton(messages.editButton(), DisposalScheduleAction.EDIT, ActionImpact.UPDATED, + "fas fa-pencil"); + managementGroup.addButton(messages.removeButton(), DisposalScheduleAction.REMOVE, ActionImpact.UPDATED, + "fas fa-trash"); + + disposalScheduleActionableBundle.addGroup(managementGroup); + return disposalScheduleActionableBundle; + } + + @Override + public Action actionForName(String name) { + return null; + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/IndexedAIPDisposalHoldSearchWrapperActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/IndexedAIPDisposalHoldSearchWrapperActions.java new file mode 100644 index 0000000000..9af43e80e5 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/IndexedAIPDisposalHoldSearchWrapperActions.java @@ -0,0 +1,198 @@ +/** + * 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.actions; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.utils.SelectedItemsUtils; +import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.core.data.v2.index.select.SelectedItems; +import org.roda.core.data.v2.ip.IndexedAIP; +import org.roda.core.data.v2.ip.disposalhold.DisassociateDisposalHoldRequest; +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.actions.model.ActionableObject; +import org.roda.wui.client.common.dialogs.Dialogs; +import org.roda.wui.client.common.lists.utils.ClientSelectedItemsUtils; +import org.roda.wui.client.ingest.process.ShowJob; +import org.roda.wui.client.process.InternalProcess; +import org.roda.wui.client.services.Services; +import org.roda.wui.common.client.tools.HistoryUtils; +import org.roda.wui.common.client.widgets.Toast; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.regexp.shared.RegExp; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.rpc.AsyncCallback; + +import config.i18n.client.ClientMessages; + +/** + * @author Tiago Fraga + */ +public class IndexedAIPDisposalHoldSearchWrapperActions extends AbstractActionable { + private static final IndexedAIPDisposalHoldSearchWrapperActions INSTANCE = new IndexedAIPDisposalHoldSearchWrapperActions(); + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static final Set POSSIBLE_ACTIONS_ON_DISPOSAL_HOLD = new HashSet<>( + Arrays.asList(DisposalHoldAction.DISASSOCIATE)); + private DisposalHold disposalHold; + + public IndexedAIPDisposalHoldSearchWrapperActions() { + } + + public IndexedAIPDisposalHoldSearchWrapperActions(DisposalHold disposalHold) { + this.disposalHold = disposalHold; + } + + public static IndexedAIPDisposalHoldSearchWrapperActions get() { + return INSTANCE; + } + + @Override + public Action[] getActions() { + return IndexedAIPDisposalHoldSearchWrapperActions.DisposalHoldAction.values(); + } + + @Override + public Action actionForName(String name) { + return IndexedAIPDisposalHoldSearchWrapperActions.DisposalHoldAction.valueOf(name); + } + + @Override + public CanActResult userCanAct(Action action, ActionableObject object) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult contextCanAct(Action action, ActionableObject object) { + if (object.getObject() != null || object.getObjects() != null) { + return new CanActResult(POSSIBLE_ACTIONS_ON_DISPOSAL_HOLD.contains(action), CanActResult.Reason.CONTEXT, + messages.reasonInvalidContext()); + } else { + return new CanActResult(false, CanActResult.Reason.CONTEXT, messages.reasonNoObjectSelected()); + } + } + + @Override + public void act(Action action, IndexedAIP aip, AsyncCallback callback) { + if (DisposalHoldAction.DISASSOCIATE.equals(action)) { + disassociate(objectToSelectedItems(aip, IndexedAIP.class), callback); + } else { + unsupportedAction(action, callback); + } + } + + @Override + public void act(Action action, SelectedItems aips, AsyncCallback callback) { + if (DisposalHoldAction.DISASSOCIATE.equals(action)) { + disassociate(aips, callback); + } else { + unsupportedAction(action, callback); + } + } + + private void disassociate(final SelectedItems aips, final AsyncCallback callback) { + ClientSelectedItemsUtils.size(IndexedAIP.class, aips, new ActionNoAsyncCallback(callback) { + @Override + public void onSuccess(final Long size) { + Dialogs.showConfirmDialog(messages.disassociateDisposalHoldDialogTitle(), + messages.disassociateDisposalHoldDialogMessage(size.intValue()), messages.dialogNo(), messages.dialogYes(), + new ActionNoAsyncCallback(callback) { + @Override + public void onSuccess(Boolean result) { + if (result) { + Dialogs.showPromptDialog(messages.outcomeDetailTitle(), null, null, messages.outcomeDetailPlaceholder(), + RegExp.compile(".*"), messages.cancelButton(), messages.confirmButton(), false, true, + new ActionNoAsyncCallback(callback) { + @Override + public void onFailure(Throwable caught) { + // do nothing + } + + @Override + public void onSuccess(String details) { + DisassociateDisposalHoldRequest request = new DisassociateDisposalHoldRequest(); + request.setClear(false); + request.setDetails(details); + request.setSelectedItems(SelectedItemsUtils.convertToRESTRequest(aips)); + Services services = new Services("Lift disposal hold", "job"); + services.disposalHoldResource(s -> s.disassociateDisposalHold(request, disposalHold.getId())) + .whenComplete((job, throwable) -> { + if (throwable != null) { + callback.onFailure(throwable); + HistoryUtils.newHistory(InternalProcess.RESOLVER); + } else { + Dialogs.showJobRedirectDialog(messages.jobCreatedMessage(), new AsyncCallback() { + + @Override + public void onFailure(Throwable caught) { + Toast.showInfo(messages.runningInBackgroundTitle(), + messages.runningInBackgroundDescription()); + + Timer timer = new Timer() { + @Override + public void run() { + doActionCallbackUpdated(); + } + }; + + timer.schedule(RodaConstants.ACTION_TIMEOUT); + } + + @Override + public void onSuccess(final Void nothing) { + doActionCallbackNone(); + HistoryUtils.newHistory(ShowJob.RESOLVER, job.getId()); + } + }); + } + }); + } + }); + } else { + doActionCallbackNone(); + } + } + }); + } + }); + } + + @Override + public ActionableBundle createActionsBundle() { + ActionableBundle disposalHoldActionableBundle = new ActionableBundle<>(); + + ActionableGroup managementGroup = new ActionableGroup<>(messages.sidebarActionsTitle()); + managementGroup.addButton(messages.disassociateDisposalHoldButton(), DisposalHoldAction.DISASSOCIATE, + ActionImpact.UPDATED, "btn-lift-hold"); + + disposalHoldActionableBundle.addGroup(managementGroup); + return disposalHoldActionableBundle; + } + + public enum DisposalHoldAction implements Action { + DISASSOCIATE(RodaConstants.PERMISSION_METHOD_ASSOCIATE_DISPOSAL_HOLD); + + private final List methods; + + DisposalHoldAction(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/IndexedAIPDisposalScheduleSearchWrapperActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/IndexedAIPDisposalScheduleSearchWrapperActions.java new file mode 100644 index 0000000000..4d8d674977 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/IndexedAIPDisposalScheduleSearchWrapperActions.java @@ -0,0 +1,170 @@ +package org.roda.wui.client.common.actions; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.utils.SelectedItemsUtils; +import org.roda.core.data.v2.index.select.SelectedItems; +import org.roda.core.data.v2.ip.IndexedAIP; +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.actions.model.ActionableObject; +import org.roda.wui.client.common.dialogs.Dialogs; +import org.roda.wui.client.common.lists.utils.ClientSelectedItemsUtils; +import org.roda.wui.client.ingest.process.ShowJob; +import org.roda.wui.client.process.InternalProcess; +import org.roda.wui.client.services.Services; +import org.roda.wui.common.client.tools.HistoryUtils; +import org.roda.wui.common.client.widgets.Toast; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.rpc.AsyncCallback; + +import config.i18n.client.ClientMessages; + +/** + * @author Miguel Guimarães + */ + +public class IndexedAIPDisposalScheduleSearchWrapperActions extends AbstractActionable { + private static final IndexedAIPDisposalScheduleSearchWrapperActions INSTANCE = new IndexedAIPDisposalScheduleSearchWrapperActions(); + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static final Set POSSIBLE_ACTIONS_ON_DISPOSAL_SCHEDULE = new HashSet<>( + Collections.singletonList(DisposalScheduleAction.DISASSOCIATE)); + + private IndexedAIPDisposalScheduleSearchWrapperActions() { + } + + public static IndexedAIPDisposalScheduleSearchWrapperActions get() { + return INSTANCE; + } + + @Override + public Action[] getActions() { + return DisposalScheduleAction.values(); + } + + @Override + public CanActResult userCanAct(Action action, ActionableObject object) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult contextCanAct(Action action, ActionableObject object) { + if (object.getObject() != null || object.getObjects() != null) { + return new CanActResult(POSSIBLE_ACTIONS_ON_DISPOSAL_SCHEDULE.contains(action), CanActResult.Reason.CONTEXT, + messages.reasonInvalidContext()); + } else { + return new CanActResult(false, CanActResult.Reason.CONTEXT, messages.reasonNoObjectSelected()); + } + } + + @Override + public void act(Action action, IndexedAIP aip, AsyncCallback callback) { + if (DisposalScheduleAction.DISASSOCIATE.equals(action)) { + disassociate(objectToSelectedItems(aip, IndexedAIP.class), callback); + } else { + unsupportedAction(action, callback); + } + } + + @Override + public void act(Action action, SelectedItems items, AsyncCallback callback) { + if (DisposalScheduleAction.DISASSOCIATE.equals(action)) { + disassociate(items, callback); + } else { + unsupportedAction(action, callback); + } + } + + private void disassociate(SelectedItems items, AsyncCallback callback) { + ClientSelectedItemsUtils.size(IndexedAIP.class, items, new ActionNoAsyncCallback(callback) { + @Override + public void onSuccess(final Long size) { + Dialogs.showConfirmDialog(messages.disassociateDisposalScheduleDialogTitle(), + messages.disassociateDisposalScheduleDialogMessage(size.intValue()), messages.dialogNo(), + messages.dialogYes(), new ActionNoAsyncCallback(callback) { + @Override + public void onSuccess(Boolean result) { + if (result) { + Services services = new Services("Disassociate disposal schedule from AIP", "job"); + services + .disposalScheduleResource( + s -> s.disassociatedDisposalSchedule(SelectedItemsUtils.convertToRESTRequest(items))) + .whenComplete((job, throwable) -> { + if (throwable != null) { + callback.onFailure(throwable); + HistoryUtils.newHistory(InternalProcess.RESOLVER); + } else { + Dialogs.showJobRedirectDialog(messages.jobCreatedMessage(), new AsyncCallback() { + + @Override + public void onFailure(Throwable caught) { + Toast.showInfo(messages.runningInBackgroundTitle(), + messages.runningInBackgroundDescription()); + + Timer timer = new Timer() { + @Override + public void run() { + doActionCallbackUpdated(); + } + }; + + timer.schedule(RodaConstants.ACTION_TIMEOUT); + } + + @Override + public void onSuccess(final Void nothing) { + doActionCallbackNone(); + HistoryUtils.newHistory(ShowJob.RESOLVER, job.getId()); + } + }); + } + }); + } else { + doActionCallbackNone(); + } + } + }); + } + }); + } + + @Override + public ActionableBundle createActionsBundle() { + ActionableBundle disposalScheduleActionableBundle = new ActionableBundle<>(); + + ActionableGroup managementGroup = new ActionableGroup<>(messages.sidebarActionsTitle()); + managementGroup.addButton(messages.disassociateDisposalScheduleButton(), DisposalScheduleAction.DISASSOCIATE, + ActionImpact.UPDATED, "fas fa-calendar"); + + disposalScheduleActionableBundle.addGroup(managementGroup); + return disposalScheduleActionableBundle; + } + + @Override + public Action actionForName(String name) { + return null; + } + + public enum DisposalScheduleAction implements Action { + DISASSOCIATE(RodaConstants.PERMISSION_METHOD_ASSOCIATE_DISPOSAL_SCHEDULE); + + private List methods; + + DisposalScheduleAction(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/RODAMemberAction.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RODAMemberAction.java new file mode 100644 index 0000000000..8d49a19c1d --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RODAMemberAction.java @@ -0,0 +1,33 @@ +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), + CHANGE_PASSWORD(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..27ff71bdc4 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 @@ -1,221 +1,75 @@ -/** - * 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.actions; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - +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.common.pekko.messages.events.EventUserUpdated; import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.common.SecureString; 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.wui.client.common.actions.model.ActionableBundle; -import org.roda.wui.client.common.actions.model.ActionableGroup; +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.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.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.CreateGroup; +import org.roda.wui.client.management.members.CreateUser; +import org.roda.wui.client.management.members.EditGroup; +import org.roda.wui.client.management.members.EditUser; +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 com.google.gwt.core.client.GWT; -import com.google.gwt.user.client.rpc.AsyncCallback; - -import config.i18n.client.ClientMessages; +import java.util.HashSet; +import java.util.List; /** - * @author Bruno Ferreira + * @author Miguel Guimarães */ -public class RODAMemberActions extends AbstractActionable { - private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static final RODAMemberActions GENERAL_INSTANCE = new RODAMemberActions(); - - private static final Set POSSIBLE_ACTIONS_WITHOUT_MEMBER = new HashSet<>( - Arrays.asList(RODAMemberAction.NEW_USER, RODAMemberAction.NEW_GROUP)); - - 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.REMOVE)); - - private static final Set POSSIBLE_ACTIONS_ON_MEMBERS = new HashSet<>( - Arrays.asList(RODAMemberAction.ACTIVATE, RODAMemberAction.DEACTIVATE, RODAMemberAction.REMOVE)); +public class RODAMemberActions { + private static final ClientMessages messages = GWT.create(ClientMessages.class); private RODAMemberActions() { - // do nothing - } - - public static RODAMemberActions 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) { - return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); - } - - @Override - public CanActResult contextCanAct(Action action) { - return new CanActResult(POSSIBLE_ACTIONS_WITHOUT_MEMBER.contains(action), CanActResult.Reason.CONTEXT, - messages.reasonNoObjectSelected()); - } - - @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.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()); - } + // private constructor } - @Override - public CanActResult userCanAct(Action action, SelectedItems objects) { - return new CanActResult(hasPermissions(action), CanActResult.Reason.CONTEXT, messages.reasonCantActOnGroup()); - } - - @Override - public CanActResult contextCanAct(Action action, SelectedItems objects) { - return new CanActResult(POSSIBLE_ACTIONS_ON_MEMBERS.contains(action), CanActResult.Reason.CONTEXT, - messages.reasonCantActOnMultipleObjects()); - } - - @Override - public void act(Action action, AsyncCallback callback) { - if (action.equals(RODAMemberAction.NEW_USER)) { - createUser(callback); - } else if (action.equals(RODAMemberAction.NEW_GROUP)) { - createGroup(callback); - } else { - unsupportedAction(action, callback); - } - } - - @Override - public void act(Action action, RODAMember object, AsyncCallback callback) { - if (action.equals(RODAMemberAction.ACTIVATE)) { - activate(objectToSelectedItems(object, RODAMember.class), callback); - } else if (action.equals(RODAMemberAction.DEACTIVATE)) { - deactivate(objectToSelectedItems(object, RODAMember.class), callback); - } else if (action.equals(RODAMemberAction.EDIT)) { - edit(object, callback); - } else if (action.equals(RODAMemberAction.REMOVE)) { - remove(objectToSelectedItems(object, RODAMember.class), callback); - } else if (action.equals(RODAMemberAction.NEW_ACCESS_KEY)) { - createNewAccessKey(callback, object); - } else { - unsupportedAction(action, callback); - } - } - - @Override - public void act(Action action, SelectedItems objects, AsyncCallback callback) { - if (action.equals(RODAMemberAction.ACTIVATE)) { - activate(objects, callback); - } else if (action.equals(RODAMemberAction.DEACTIVATE)) { - deactivate(objects, callback); - } else if (action.equals(RODAMemberAction.REMOVE)) { - remove(objects, callback); - } else { - unsupportedAction(action, callback); - } + public static void createUser(AsyncCallback callback) { + HistoryUtils.newHistory(CreateUser.RESOLVER.getHistoryPath()); + callback.onSuccess(Actionable.ActionImpact.UPDATED); } - private void activate(SelectedItems objects, AsyncCallback callback) { - Services services = new Services("Activate RODA member", "activate"); - ChangeUserStatusRequest request = new ChangeUserStatusRequest(SelectedItemsUtils.convertToRESTRequest(objects), - 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) { - callback.onSuccess(Actionable.ActionImpact.UPDATED); - HistoryUtils.newHistory(MemberManagement.RESOLVER.getHistoryPath()); - } - - @Override - public void onSuccess(final Void nothing) { - callback.onSuccess(ActionImpact.NONE); - HistoryUtils.newHistory(ShowJob.RESOLVER, res.getId()); - } - }); - } - }); + public static void createGroup(AsyncCallback callback) { + HistoryUtils.newHistory(CreateGroup.RESOLVER.getHistoryPath()); + callback.onSuccess(Actionable.ActionImpact.UPDATED); } - private void edit(RODAMember object, AsyncCallback callback) { - callback.onSuccess(ActionImpact.NONE); - if (object.isUser()) { - HistoryUtils.newHistory(EditUser.RESOLVER, object.getId()); + public static void edit(RODAMember member, AsyncCallback callback) { + if (member.isUser()) { + HistoryUtils.newHistory(EditUser.RESOLVER, member.getId()); + callback.onSuccess(Actionable.ActionImpact.UPDATED); } else { - HistoryUtils.newHistory(EditGroup.RESOLVER, object.getId()); + HistoryUtils.newHistory(EditGroup.RESOLVER, member.getId()); + callback.onSuccess(Actionable.ActionImpact.UPDATED); } } - private void deactivate(SelectedItems objects, AsyncCallback callback) { - Services services = new Services("Deactivate RODA member", "deactivate"); - ChangeUserStatusRequest request = new ChangeUserStatusRequest(SelectedItemsUtils.convertToRESTRequest(objects), - 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) { - callback.onSuccess(Actionable.ActionImpact.UPDATED); - HistoryUtils.newHistory(MemberManagement.RESOLVER.getHistoryPath()); - } - - @Override - public void onSuccess(final Void nothing) { - callback.onSuccess(ActionImpact.NONE); - HistoryUtils.newHistory(ShowJob.RESOLVER, res.getId()); - } - }); - } - }); - } - - private void remove(SelectedItems objects, AsyncCallback callback) { + public static void remove(SelectedItems objects, AsyncCallback callback) { Dialogs.showConfirmDialog(messages.userRemoveConfirmDialogTitle(), messages.userRemoveConfirmDialogMessage(), messages.dialogNo(), messages.dialogYes(), new AsyncCallback() { @Override @@ -239,60 +93,256 @@ public void onFailure(Throwable caught) { }); } - private void createUser(AsyncCallback callback) { - callback.onSuccess(ActionImpact.NONE); - HistoryUtils.newHistory(CreateUser.RESOLVER.getHistoryPath()); + public static void remove(SelectedItems items, 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(items))) + .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 createGroup(AsyncCallback callback) { - callback.onSuccess(ActionImpact.NONE); - HistoryUtils.newHistory(CreateGroup.RESOLVER.getHistoryPath()); + public static void activate(SelectedItems objects, 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(objects), 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) { + callback.onSuccess(Actionable.ActionImpact.UPDATED); + HistoryUtils.newHistory(MemberManagement.RESOLVER.getHistoryPath()); + } + + @Override + public void onSuccess(final Void nothing) { + callback.onSuccess(Actionable.ActionImpact.NONE); + HistoryUtils.newHistory(ShowJob.RESOLVER, res.getId()); + } + }); + } + }); + } else { + doActionCallbackNone(); + } + } + + @Override + public void onFailure(Throwable caught) { + callback.onFailure(caught); + } + }); } - private void createNewAccessKey(AsyncCallback callback, RODAMember user) { - callback.onSuccess(ActionImpact.NONE); - HistoryUtils.newHistory(CreateAccessKey.RESOLVER, user.getName()); + public static void deactivate(SelectedItems objects, 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(objects), 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) { + callback.onSuccess(Actionable.ActionImpact.UPDATED); + HistoryUtils.newHistory(MemberManagement.RESOLVER.getHistoryPath()); + } + + @Override + public void onSuccess(final Void nothing) { + callback.onSuccess(Actionable.ActionImpact.NONE); + HistoryUtils.newHistory(ShowJob.RESOLVER, res.getId()); + } + }); + } + }); + } else { + doActionCallbackNone(); + } + } + + @Override + public void onFailure(Throwable caught) { + doActionCallbackNone(); + callback.onFailure(caught); + } + }); } - @Override - public ActionableBundle createActionsBundle() { - ActionableBundle transferredResourcesActionableBundle = new ActionableBundle<>(); + public static 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))); - // ACTIONS - ActionableGroup actionableGroup = new ActionableGroup<>(messages.sidebarActionsTitle()); - actionableGroup.addButton(messages.addUserButton(), RODAMemberAction.NEW_USER, ActionImpact.UPDATED, - "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"); + 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()); + } + }); + } + }); + } - 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"); + public static 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))); - transferredResourcesActionableBundle.addGroup(actionableGroup); - return transferredResourcesActionableBundle; + 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()); + } + }); + } + }); } - 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); + public static void createNewAccessKey(AsyncCallback callback, RODAMember user) { + callback.onSuccess(Actionable.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(Actionable.ActionImpact.UPDATED); + } + }); + } + }); + } + }); + } - private List methods; + public static void changePassword(RODAMember object, AsyncCallback callback) { + RODAMembersDialogs.setUserPassword(messages.userDataChangePassword(), new ActionAsyncCallback(callback) { + @Override + public void onSuccess(String result) { + UpdateUserRequest request = new UpdateUserRequest(); + request.setUser((User) object); + request.setPassword(new SecureString(result.toCharArray())); + request.setValues(((User) object).getExtra()); + + Services services = new Services("Update user password", "update"); + services.membersResource(s -> s.updateUser(request)).whenComplete((res, error) -> { + if (error != null) { + callback.onSuccess(Actionable.ActionImpact.NONE); + } else { + callback.onSuccess(Actionable.ActionImpact.NONE); + Toast.showError(messages.editUserFailure(object.getFullName(), error.getMessage())); + } + }); + } + }); + } - RODAMemberAction(String... methods) { - this.methods = Arrays.asList(methods); - } + public static void editPermissions(RODAMember object, AsyncCallback callback) { + PermissionsPanel permissionsPanel = new PermissionsPanel(object, false, true); - @Override - public List getMethods() { - return this.methods; - } + 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()); + } + }); + } + } + }); } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RODAMemberSearchWrapperActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RODAMemberSearchWrapperActions.java new file mode 100644 index 0000000000..7c57436c0f --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RODAMemberSearchWrapperActions.java @@ -0,0 +1,155 @@ +/** + * 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.actions; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.roda.core.data.v2.index.select.SelectedItems; +import org.roda.core.data.v2.user.RODAMember; +import org.roda.wui.client.common.actions.model.ActionableBundle; +import org.roda.wui.client.common.actions.model.ActionableGroup; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.rpc.AsyncCallback; + +import config.i18n.client.ClientMessages; + +/** + * @author Bruno Ferreira + */ +public class RODAMemberSearchWrapperActions extends AbstractActionable { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static final RODAMemberSearchWrapperActions GENERAL_INSTANCE = new RODAMemberSearchWrapperActions(); + + private static final Set POSSIBLE_ACTIONS_WITHOUT_MEMBER = new HashSet<>( + Arrays.asList(RODAMemberAction.NEW_USER, RODAMemberAction.NEW_GROUP)); + + private static final Set POSSIBLE_ACTIONS_ON_GROUP = new HashSet<>( + Arrays.asList(RODAMemberAction.REMOVE)); + + private static final Set POSSIBLE_ACTIONS_ON_MEMBERS = new HashSet<>( + Arrays.asList(RODAMemberAction.ACTIVATE, RODAMemberAction.DEACTIVATE, RODAMemberAction.REMOVE)); + + private RODAMemberSearchWrapperActions() { + // do nothing + } + + public static RODAMemberSearchWrapperActions 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) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); + } + + @Override + public CanActResult contextCanAct(Action action) { + return new CanActResult(POSSIBLE_ACTIONS_WITHOUT_MEMBER.contains(action), CanActResult.Reason.CONTEXT, + messages.reasonNoObjectSelected()); + } + + @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)), + CanActResult.Reason.CONTEXT, messages.reasonCantActOnUser()); + } else { + return new CanActResult(POSSIBLE_ACTIONS_ON_GROUP.contains(action), CanActResult.Reason.CONTEXT, + messages.reasonCantActOnGroup()); + } + } + + @Override + public CanActResult userCanAct(Action action, SelectedItems objects) { + return new CanActResult(hasPermissions(action), CanActResult.Reason.CONTEXT, messages.reasonCantActOnGroup()); + } + + @Override + public CanActResult contextCanAct(Action action, SelectedItems objects) { + return new CanActResult(POSSIBLE_ACTIONS_ON_MEMBERS.contains(action), CanActResult.Reason.CONTEXT, + messages.reasonCantActOnMultipleObjects()); + } + + @Override + public void act(Action action, AsyncCallback callback) { + if (action.equals(RODAMemberAction.NEW_USER)) { + RODAMemberActions.createUser(callback); + } else if (action.equals(RODAMemberAction.NEW_GROUP)) { + RODAMemberActions.createGroup(callback); + } else { + unsupportedAction(action, callback); + } + } + + @Override + public void act(Action action, RODAMember object, AsyncCallback callback) { + if (action.equals(RODAMemberAction.ACTIVATE)) { + RODAMemberActions.activate(objectToSelectedItems(object, RODAMember.class), callback); + } else if (action.equals(RODAMemberAction.DEACTIVATE)) { + RODAMemberActions.deactivate(objectToSelectedItems(object, RODAMember.class), callback); + } else if (action.equals(RODAMemberAction.REMOVE)) { + RODAMemberActions.remove(objectToSelectedItems(object, RODAMember.class), callback); + } else if (action.equals(RODAMemberAction.EDIT)) { + RODAMemberActions.edit(object, callback); + } else { + unsupportedAction(action, callback); + } + } + + @Override + public void act(Action action, SelectedItems objects, AsyncCallback callback) { + if (action.equals(RODAMemberAction.ACTIVATE)) { + RODAMemberActions.activate(objects, callback); + } else if (action.equals(RODAMemberAction.DEACTIVATE)) { + RODAMemberActions.deactivate(objects, callback); + } else if (action.equals(RODAMemberAction.REMOVE)) { + RODAMemberActions.remove(objects, callback); + } else { + unsupportedAction(action, callback); + } + } + + @Override + public ActionableBundle createActionsBundle() { + ActionableBundle transferredResourcesActionableBundle = new ActionableBundle<>(); + + // ACTIONS + ActionableGroup actionableGroup = new ActionableGroup<>(messages.sidebarActionsTitle()); + actionableGroup.addButton(messages.addUserButton(), RODAMemberAction.NEW_USER, ActionImpact.UPDATED, + "btn-plus-circle"); + actionableGroup.addButton(messages.addGroupButton(), RODAMemberAction.NEW_GROUP, ActionImpact.UPDATED, + "btn-plus-circle"); + 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.editUserRemove(), RODAMemberAction.REMOVE, ActionImpact.DESTROYED, "btn-ban"); + + transferredResourcesActionableBundle.addGroup(actionableGroup); + return transferredResourcesActionableBundle; + } +} 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..427c9481c3 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RODAMemberToolbarActions.java @@ -0,0 +1,118 @@ +package org.roda.wui.client.common.actions; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.rpc.AsyncCallback; +import config.i18n.client.ClientMessages; + +import org.roda.core.data.v2.user.RODAMember; +import org.roda.wui.client.common.actions.model.ActionableBundle; +import org.roda.wui.client.common.actions.model.ActionableGroup; + +import java.util.Arrays; +import java.util.HashSet; +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_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.CHANGE_PASSWORD)) + || (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)) { + RODAMemberActions.activate(objectToSelectedItems(object, RODAMember.class), callback); + } else if (action.equals(RODAMemberAction.DEACTIVATE)) { + RODAMemberActions.deactivate(objectToSelectedItems(object, RODAMember.class), callback); + } else if (action.equals(RODAMemberAction.EDIT)) { + RODAMemberActions.edit(object, callback); + } else if (action.equals(RODAMemberAction.REMOVE)) { + RODAMemberActions.remove(objectToSelectedItems(object, RODAMember.class), object, callback); + } else if (action.equals(RODAMemberAction.NEW_ACCESS_KEY)) { + RODAMemberActions.createNewAccessKey(callback, object); + } else if (action.equals(RODAMemberAction.ADD_NEW_GROUP)) { + RODAMemberActions.addNewGroup(object, callback); + } else if (action.equals(RODAMemberAction.ADD_NEW_MEMBER)) { + RODAMemberActions.addNewMember(object, callback); + } else if (action.equals(RODAMemberAction.EDIT_PERMISSIONS)) { + RODAMemberActions.editPermissions(object, callback); + } else if (action.equals(RODAMemberAction.CHANGE_PASSWORD)) { + RODAMemberActions.changePassword(object, callback); + } else { + unsupportedAction(action, callback); + } + } + + @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.userDataChangePassword(), RODAMemberAction.CHANGE_PASSWORD, ActionImpact.UPDATED, "btn-password"); + + 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/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/actions/RiskActions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/RiskActions.java index 909bd9dd22..2dbcdee485 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 @@ -124,7 +124,6 @@ public CanActResult contextCanAct(Action action, IndexedRisk object @Override public CanActResult userCanAct(Action action, SelectedItems objects) { return new CanActResult(hasPermissions(action), CanActResult.Reason.USER, messages.reasonUserLacksPermission()); - } @Override @@ -252,7 +251,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/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/actions/widgets/ActionableWidgetBuilder.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/widgets/ActionableWidgetBuilder.java index a0c6fdb7be..6307f7af9e 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/widgets/ActionableWidgetBuilder.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/actions/widgets/ActionableWidgetBuilder.java @@ -72,7 +72,7 @@ public ActionableWidgetBuilder withActionCallback(AsyncCallback /** * Add a consumer to be called when the actionable widget is generated, the - * integer parameter will have the number of buttons the uer can act on. + * integer parameter will have the number of buttons the user can act on. */ public ActionableWidgetBuilder withWidgetCreatedHandler(Consumer widgetCreatedHandler) { this.widgetCreatedHandler = widgetCreatedHandler; @@ -168,6 +168,13 @@ public FlowPanel buildGroupedListWithObjects(ActionableObject objects, List objects, + List> ungroupedActions) { + ActionableBundle actionableBundle = actionable.createActionsBundle(); + + return createGroupedActionsMenu(actionableBundle, objects, ungroupedActions, true); + } + // Internal (GUI elements creation) private FlowPanel createActionsMenu(ActionableBundle actionableBundle, ActionableObject objects) { @@ -319,7 +326,12 @@ public void onSuccess(Actionable.ActionImpact result) { } private FlowPanel createGroupedActionsMenu(ActionableBundle actionableBundle, ActionableObject objects, - List> ungroupedActions) { + List> ungroupedActions) { + return createGroupedActionsMenu(actionableBundle, objects, ungroupedActions, false); + } + + private FlowPanel createGroupedActionsMenu(ActionableBundle actionableBundle, ActionableObject objects, + List> ungroupedActions, boolean hideGroups) { FlowPanel panel = new FlowPanel(); panel.addStyleName("groupedActionableMenu"); @@ -328,6 +340,7 @@ private FlowPanel createGroupedActionsMenu(ActionableBundle actionableBundle, int addedButtonCount = 0; boolean firstGroup = true; + for (ActionableGroup actionGroup : actionableBundle.getGroups()) { FlowPanel groupPanel = null; FlowPanel buttonsPanel = new FlowPanel(); @@ -369,13 +382,18 @@ public void onClick(ClickEvent event) { if (!firstGroup) { SimplePanel verticalDivider = new SimplePanel(); verticalDivider.addStyleName("verticalDivider"); - panel.add(verticalDivider); + if (!hideGroups) { + panel.add(verticalDivider); + } } else { firstGroup = false; } - panel.add(groupPanel); - groupPanel.add(groupButton); - groupPanel.add(anchorPanel); + + if (!hideGroups) { + panel.add(groupPanel); + groupPanel.add(groupButton); + groupPanel.add(anchorPanel); + } popupPanel.add(buttonsPanel); break; } @@ -413,7 +431,9 @@ public void onSuccess(Actionable.ActionImpact result) { if (!firstGroup) { SimplePanel verticalDivider = new SimplePanel(); verticalDivider.addStyleName("verticalDivider"); - panel.add(verticalDivider); + if (!hideGroups) { + panel.add(verticalDivider); + } } else { firstGroup = false; } 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/DefaultSelectDialog.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/DefaultSelectDialog.ui.xml index fc7da1f1cb..9f6c88d86e 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/DefaultSelectDialog.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/DefaultSelectDialog.ui.xml @@ -1,16 +1,15 @@ + xmlns:commonsearch="urn:import:org.roda.wui.client.common.search"> - - .emptyParentButton { - margin-right: 10px; - } - + + .emptyParentButton { + margin-right: 10px; + } + - + 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/DisposalDialogs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/DisposalDialogs.java index fcf23e6226..cc4585df03 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/DisposalDialogs.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/DisposalDialogs.java @@ -7,9 +7,17 @@ */ package org.roda.wui.client.common.dialogs; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.cellview.client.TextColumn; +import com.google.gwt.user.client.ui.HasVerticalAlignment; +import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.IntegerBox; +import com.google.gwt.user.client.ui.RadioButton; import org.roda.core.data.v2.disposal.hold.DisposalHold; import org.roda.core.data.v2.disposal.hold.DisposalHolds; +import org.roda.core.data.v2.disposal.rule.ChangeOrderRequest; +import org.roda.core.data.v2.disposal.rule.OrderPositions; import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; import org.roda.core.data.v2.disposal.schedule.DisposalSchedules; import org.roda.wui.client.common.dialogs.utils.DisposalHoldDialogResult; @@ -37,14 +45,103 @@ import config.i18n.client.ClientMessages; -; - /** * @author Miguel Guimarães */ public class DisposalDialogs { private static final ClientMessages messages = GWT.create(ClientMessages.class); + public static void reOrderDisposalRules(String title, String message, AsyncCallback callback) { + IntegerBox positionInput; + 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(messages.cancelButton()); + final Button saveButton = new Button(messages.saveButton()); + buttonPanel.add(cancelButton); + buttonPanel.add(saveButton); + + final FlowPanel content = new FlowPanel(); + content.addStyleName("wui-dialog-content"); + + + HTML messageLabel = new HTML(message); + messageLabel.addStyleName("wui-dialog-message"); + layout.add(messageLabel); + + // Create Radio Buttons in the same group + RadioButton moveTopRadio = new RadioButton("destinationGroup", messages.moveToTop()); + RadioButton moveBottomRadio = new RadioButton("destinationGroup", messages.moveToBottom()); + RadioButton movePosRadio = new RadioButton("destinationGroup", messages.moveToPositionNumber()); + + moveTopRadio.setValue(true); // Default selection + + // Input box for specific position + positionInput = new IntegerBox(); + positionInput.setWidth("50px"); + positionInput.setEnabled(false); // Disabled by default until radio is clicked + + // Handler to toggle the input box based on radio selection + ClickHandler radioClickHandler = event -> positionInput.setEnabled(movePosRadio.getValue()); + moveTopRadio.addClickHandler(radioClickHandler); + moveBottomRadio.addClickHandler(radioClickHandler); + movePosRadio.addClickHandler(radioClickHandler); + + HorizontalPanel posPanel = new HorizontalPanel(); + posPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE); + posPanel.add(movePosRadio); + posPanel.add(positionInput); + + content.add(moveTopRadio); + content.add(moveBottomRadio); + content.add(posPanel); + + 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) { + ChangeOrderRequest changeOrderRequest = new ChangeOrderRequest(); + dialogBox.hide(); + if (moveTopRadio.getValue()) { + changeOrderRequest.setPosition(OrderPositions.TOP); + } else if (moveBottomRadio.getValue()) { + changeOrderRequest.setPosition(OrderPositions.BOTTOM); + } else { + changeOrderRequest.setPosition(OrderPositions.POSITION); + changeOrderRequest.setNewOrder(positionInput.getValue() != null ? positionInput.getValue() : 1); + } + callback.onSuccess(changeOrderRequest); + } + }); + + cancelButton.addStyleName("btn btn-link"); + saveButton.addStyleName("pull-right btn btn-play"); + + dialogBox.center(); + dialogBox.show(); + } + public static void showDisposalHoldSelection(String title, DisposalHolds holds, 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..00e031f987 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/RODAMembersDialogs.java @@ -0,0 +1,235 @@ +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.i18n.client.DateTimeFormat; +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.Label; +import com.google.gwt.user.client.ui.PasswordTextBox; +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; +import org.roda.core.data.v2.accessKey.CreateAccessKeyRequest; +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.RODAMember; +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.PasswordPanel; +import org.roda.wui.client.management.members.data.panels.PasswordDataPanel; +import org.roda.wui.client.management.members.tabs.PermissionsPanel; +import org.roda.wui.common.client.tools.StringUtils; + +import java.util.Date; +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 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; + } + + public static void setUserPassword(String title, 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(); + + PasswordDataPanel passwordDataPanel = new PasswordDataPanel(true); + + dialogBox.setText(title); + layout.add(header); + layout.add(footer); + header.add(passwordDataPanel); + + footer.add(cancelButton); + footer.add(confirmButton); + + confirmButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent clickEvent) { + if (passwordDataPanel.isValid()) { + dialogBox.hide(); + callback.onSuccess(passwordDataPanel.getValue()); + } + } + }); + + cancelButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent clickEvent) { + dialogBox.hide(); + callback.onSuccess(null); + } + }); + + dialogBox.setWidget(layout); + dialogBox.setGlassEnabled(true); + dialogBox.setAnimationEnabled(false); + + dialogBox.addStyleName("wui-dialog-prompt"); + layout.addStyleName("wui-dialog-layout"); + header.addStyleName("wui-dialog-message"); + footer.addStyleName("wui-dialog-layout-footer"); + + confirmButton.addStyleName("btn btn-play"); + cancelButton.addStyleName("btn btn-link"); + + dialogBox.center(); + dialogBox.show(); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/SelectAipDialog.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/SelectAipDialog.java index 050c5ae74f..1ca5470db5 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/SelectAipDialog.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/dialogs/SelectAipDialog.java @@ -35,6 +35,6 @@ public SelectAipDialog(String title, Filter filter, boolean justActive) { public SelectAipDialog(String title, Filter filter, boolean justActive, boolean exportCsvVisible) { super(title, new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), new AsyncTableCellOptions<>(IndexedAIP.class, listId) - .withSummary(messages.selectAipSearchResults()).withJustActive(justActive).withFilter(filter))); + .withCsvDownloadButtonVisibility(exportCsvVisible).withSummary(messages.selectAipSearchResults()).withJustActive(justActive).withFilter(filter))); } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/forms/GenericDataForm.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/forms/GenericDataForm.java new file mode 100644 index 0000000000..dc4c4e50ec --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/forms/GenericDataForm.java @@ -0,0 +1,447 @@ +package org.roda.wui.client.common.forms; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Function; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.event.dom.client.ChangeHandler; +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.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.ListBox; +import com.google.gwt.user.client.ui.TextArea; +import com.google.gwt.user.client.ui.TextBox; +import com.google.gwt.user.client.ui.TextBoxBase; +import com.google.gwt.user.client.ui.Widget; + +/** + * @author Miguel Guimarães + */ +public class GenericDataForm extends Composite implements GenericDataPanel, HasValueChangeHandlers { + + private static GenericDataFormUiBinder uiBinder = GWT.create(GenericDataFormUiBinder.class); + + private final List> bindings = new ArrayList<>(); + + @UiField + FlowPanel fieldsContainer; + + @UiField + HTML errors; + + private T model; + private boolean changed = false; + + public GenericDataForm() { + initWidget(uiBinder.createAndBindUi(this)); + } + + public void setModel(T model) { + this.model = model; + this.changed = false; + refreshUI(); + } + + @Override + public T getValue() { + for (FormBinding binding : bindings) { + binding.flushToModel(model); + } + return model; + } + + @Override + public boolean isValid() { + List errorMessages = new ArrayList<>(); + boolean valid = true; + + for (FormBinding binding : bindings) { + if (!binding.validate()) { + valid = false; + String errMsg = binding.getRegexErrorMessage() != null ? binding.getRegexErrorMessage() + : "Field '" + binding.getLabelText() + "' is invalid or mandatory."; + errorMessages.add(errMsg); + } + } + + if (!valid) { + StringBuilder sb = new StringBuilder(); + for (String err : errorMessages) { + sb.append("").append(err).append("
"); + } + errors.setHTML(sb.toString()); + errors.setVisible(true); + } else { + errors.setVisible(false); + } + + return valid; + } + + public HTML getErrors() { + return errors; + } + + public boolean isChanged() { + return changed; + } + + public void addCustomWidget(Widget widget) { + fieldsContainer.add(widget); + } + + // --- READ ONLY --- + + public FlowPanel addReadOnlyField(String labelText, Function getter, boolean mandatory) { + FlowPanel searchField = new FlowPanel(); + searchField.addStyleName("generic-form-field"); + + FlowPanel leftPanel = new FlowPanel(); + leftPanel.addStyleName("generic-form-field-left-panel"); + + Label label = new Label(labelText); + if (mandatory) { + label.setText(labelText + " *"); + } + label.addStyleName("form-label"); + + FlowPanel inputPanel = new FlowPanel(); + inputPanel.addStyleName("generic-form-field-input-panel full_width"); + + InlineHTML inlineHTML = new InlineHTML(); + inlineHTML.addStyleName("form-readonly-value"); + + inputPanel.add(inlineHTML); + leftPanel.add(label); + leftPanel.add(inputPanel); + searchField.add(leftPanel); + + fieldsContainer.add(searchField); + bindings.add(new ReadOnlyFieldBinding(labelText, searchField, inlineHTML, getter)); + + return searchField; + } + + // --- TEXT FIELDS & TEXT AREAS --- + + public FlowPanel addTextField(String labelText, Function getter, BiConsumer setter, + boolean mandatory) { + return addTextBoxBase(labelText, new TextBox(), getter, setter, mandatory, false, null, null); + } + + public FlowPanel addTextField(String labelText, Function getter, BiConsumer setter, + boolean mandatory, boolean readOnly) { + return addTextBoxBase(labelText, new TextBox(), getter, setter, mandatory, readOnly, null, null); + } + + public FlowPanel addTextField(String labelText, Function getter, BiConsumer setter, + boolean mandatory, boolean readOnly, String regex, String regexErrorMessage) { + return addTextBoxBase(labelText, new TextBox(), getter, setter, mandatory, readOnly, regex, regexErrorMessage); + } + + public FlowPanel addTextArea(String labelText, Function getter, BiConsumer setter, + boolean mandatory) { + return addTextArea(labelText, getter, setter, mandatory, false); + } + + public FlowPanel addTextArea(String labelText, Function getter, BiConsumer setter, + boolean mandatory, boolean readOnly) { + TextArea textArea = new TextArea(); + textArea.addStyleName("metadata-form-text-area"); + return addTextBoxBase(labelText, textArea, getter, setter, mandatory, readOnly, null, null); + } + + public FlowPanel addTextBoxBase(String labelText, TextBoxBase textBoxBase, Function getter, + BiConsumer setter, boolean mandatory, boolean readOnly, String regex, String regexErrorMessage) { + + if (readOnly) { + return addReadOnlyField(labelText, getter, mandatory); + } + + FlowPanel searchField = new FlowPanel(); + searchField.addStyleName("generic-form-field"); + + FlowPanel leftPanel = new FlowPanel(); + leftPanel.addStyleName("generic-form-field-left-panel"); + + Label label = new Label(labelText); + if (mandatory) { + label.setText(labelText + " *"); + } + label.addStyleName("form-label"); + + FlowPanel inputPanel = new FlowPanel(); + inputPanel.addStyleName("generic-form-field-input-panel full_width"); + + textBoxBase.addStyleName("form-textbox"); + + inputPanel.add(textBoxBase); + leftPanel.add(label); + leftPanel.add(inputPanel); + searchField.add(leftPanel); + fieldsContainer.add(searchField); + + ChangeHandler changeHandler = event -> onChange(); + KeyUpHandler keyUpHandler = event -> onChange(); + textBoxBase.addChangeHandler(changeHandler); + textBoxBase.addKeyUpHandler(keyUpHandler); + + bindings.add( + new TextBoxBaseBinding(labelText, searchField, textBoxBase, getter, setter, mandatory, regex, regexErrorMessage)); + return searchField; + } + + // --- LIST BOX --- + + public FlowPanel addListBox(String labelText, ListBox listBox, Function getter, + BiConsumer setter, boolean mandatory) { + FlowPanel searchField = new FlowPanel(); + searchField.addStyleName("generic-form-field"); + + FlowPanel leftPanel = new FlowPanel(); + leftPanel.addStyleName("generic-form-field-left-panel"); + + Label label = new Label(labelText); + if (mandatory) { + label.setText(labelText + " *"); + } + label.addStyleName("form-label"); + + FlowPanel inputPanel = new FlowPanel(); + inputPanel.addStyleName("generic-form-field-input-panel full_width"); + + listBox.addStyleName("form-listbox"); + + inputPanel.add(listBox); + leftPanel.add(label); + leftPanel.add(inputPanel); + searchField.add(leftPanel); + fieldsContainer.add(searchField); + + listBox.addChangeHandler(event -> onChange()); + + bindings.add(new ListBoxBinding(labelText, searchField, listBox, getter, setter, mandatory)); + return searchField; + } + + protected void onChange() { + changed = true; + ValueChangeEvent.fire(this, getValue()); + } + + private void refreshUI() { + if (model == null) + return; + for (FormBinding binding : bindings) { + binding.refreshFromModel(model); + } + } + + @Override + public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) { + return addHandler(handler, ValueChangeEvent.getType()); + } + + @SuppressWarnings("rawtypes") + interface GenericDataFormUiBinder extends UiBinder { + } + + // --- BINDING INTERFACES & CLASSES --- + + private interface FormBinding { + String getLabelText(); + + void refreshFromModel(T model); + + void flushToModel(T model); + + boolean validate(); + + String getRegexErrorMessage(); + } + + private class TextBoxBaseBinding implements FormBinding { + private final String labelText; + private final FlowPanel container; + private final TextBoxBase widget; + private final Function getter; + private final BiConsumer setter; + private final boolean mandatory; + private final String regex; + private final String regexErrorMessage; + + public TextBoxBaseBinding(String labelText, FlowPanel container, TextBoxBase widget, Function getter, + BiConsumer setter, boolean mandatory, String regex, String regexErrorMessage) { + this.labelText = labelText; + this.container = container; + this.widget = widget; + this.getter = getter; + this.setter = setter; + this.mandatory = mandatory; + this.regex = regex; + this.regexErrorMessage = regexErrorMessage; + } + + @Override + public String getLabelText() { + return labelText; + } + + @Override + public String getRegexErrorMessage() { + return regexErrorMessage; + } + + @Override + public void refreshFromModel(T model) { + String value = getter.apply(model); + widget.setText(value != null ? value : ""); + } + + @Override + public void flushToModel(T model) { + if (!container.isVisible() || !widget.isEnabled()) + return; // SKIP IF HIDDEN OR DISABLED + setter.accept(model, widget.getText()); + } + + @Override + public boolean validate() { + if (!container.isVisible() || !widget.isEnabled()) + return true; // SKIP IF HIDDEN OR DISABLED + + String text = widget.getText(); + boolean isBlank = (text == null || text.trim().isEmpty()); + + if (mandatory && isBlank) { + widget.addStyleName("isWrong"); + return false; + } + + if (!isBlank && regex != null && !text.matches(regex)) { + widget.addStyleName("isWrong"); + return false; + } + + widget.removeStyleName("isWrong"); + return true; + } + } + + private class ListBoxBinding implements FormBinding { + private final String labelText; + private final FlowPanel container; + private final ListBox widget; + private final Function getter; + private final BiConsumer setter; + private final boolean mandatory; + + public ListBoxBinding(String labelText, FlowPanel container, ListBox widget, Function getter, + BiConsumer setter, boolean mandatory) { + this.labelText = labelText; + this.container = container; + this.widget = widget; + this.getter = getter; + this.setter = setter; + this.mandatory = mandatory; + } + + @Override + public String getLabelText() { + return labelText; + } + + @Override + public String getRegexErrorMessage() { + return null; + } + + @Override + public void refreshFromModel(T model) { + String value = getter.apply(model); + if (value != null && !value.isEmpty()) { + for (int i = 0; i < widget.getItemCount(); i++) { + if (widget.getValue(i).equals(value)) { + widget.setSelectedIndex(i); + return; + } + } + } + if (widget.getItemCount() > 0) + widget.setSelectedIndex(0); + } + + @Override + public void flushToModel(T model) { + if (!container.isVisible() || !widget.isEnabled()) + return; // SKIP IF HIDDEN + setter.accept(model, widget.getSelectedValue()); + } + + @Override + public boolean validate() { + if (!container.isVisible() || !widget.isEnabled()) return true; // SKIP IF HIDDEN + + // FIX: Changed <= 0 to < 0 since index 0 is now a valid selection! + boolean isBlank = widget.getSelectedIndex() < 0 || widget.getSelectedValue().trim().isEmpty(); + + if (mandatory && isBlank) { + widget.addStyleName("isWrong"); + return false; + } + + widget.removeStyleName("isWrong"); + return true; + } + } + + private class ReadOnlyFieldBinding implements FormBinding { + private final String labelText; + private final FlowPanel container; + private final InlineHTML widget; + private final Function getter; + + public ReadOnlyFieldBinding(String labelText, FlowPanel container, InlineHTML widget, Function getter) { + this.labelText = labelText; + this.container = container; + this.widget = widget; + this.getter = getter; + } + + @Override + public String getLabelText() { + return labelText; + } + + @Override + public String getRegexErrorMessage() { + return null; + } + + @Override + public void refreshFromModel(T model) { + String value = getter.apply(model); + widget.setText(value != null ? value : ""); + } + + @Override + public void flushToModel(T model) { + } + + @Override + public boolean validate() { + return true; + } + } +} \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/forms/GenericDataForm.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/forms/GenericDataForm.ui.xml new file mode 100644 index 0000000000..c961181c4d --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/forms/GenericDataForm.ui.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/forms/GenericDataPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/forms/GenericDataPanel.java new file mode 100644 index 0000000000..545e5a93a9 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/forms/GenericDataPanel.java @@ -0,0 +1,12 @@ +package org.roda.wui.client.common.forms; + +/** + * + * @author Miguel Guimarães + */ +public interface GenericDataPanel { + + boolean isValid(); + + T getValue(); +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/labels/Tag.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/labels/Tag.java index ac847a8a5e..25e3703154 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/labels/Tag.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/labels/Tag.java @@ -27,7 +27,7 @@ public class Tag extends Composite { public enum TagStyle { DEFAULT, SUCCESS, FAILURE, WARNING_LIGHT, DANGER_LIGHT, NEUTRAL, MONO, ICON_CALENDAR, ICON_LOCK, ICON_CLOCK, - BORDER_BLACK, BORDER_DANGER; + ICON_GAVEL, BORDER_BLACK, BORDER_DANGER; private static String toStyleName(TagStyle tagStyle) { switch (tagStyle) { @@ -45,6 +45,8 @@ private static String toStyleName(TagStyle tagStyle) { return "tagGrey"; case ICON_CALENDAR: return "tagIconCalendar"; + case ICON_GAVEL: + return "tagIconGavel"; case ICON_LOCK: return "tagIconLock"; case ICON_CLOCK: diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/DisposalHoldList.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/DisposalHoldList.java new file mode 100644 index 0000000000..117a389569 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/DisposalHoldList.java @@ -0,0 +1,105 @@ +package org.roda.wui.client.common.lists; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.core.data.v2.index.sort.Sorter; +import org.roda.wui.client.common.lists.utils.AsyncTableCell; +import org.roda.wui.client.common.lists.utils.AsyncTableCellOptions; +import org.roda.wui.client.common.utils.HtmlSnippetUtils; + +import com.google.gwt.cell.client.SafeHtmlCell; +import com.google.gwt.core.client.GWT; +import com.google.gwt.safehtml.shared.SafeHtml; +import com.google.gwt.user.cellview.client.CellTable; +import com.google.gwt.user.cellview.client.Column; +import com.google.gwt.user.cellview.client.ColumnSortList; +import com.google.gwt.user.cellview.client.TextColumn; + +import config.i18n.client.ClientMessages; + +/** + * @author Miguel Guimarães + */ + +public class DisposalHoldList extends AsyncTableCell { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static final List fieldsToReturn = Arrays.asList(RodaConstants.INDEX_UUID, + RodaConstants.DISPOSAL_HOLD_TITLE, RodaConstants.DISPOSAL_HOLD_MANDATE, + RodaConstants.DISPOSAL_HOLD_SCOPE_NOTES, RodaConstants.DISPOSAL_HOLD_STATE); + + private TextColumn titleColumn; + private TextColumn mandateColumn; + private TextColumn scopeNotesColumn; + private Column stateColumn; + + @Override + protected void adjustOptions(AsyncTableCellOptions options) { + options.withFieldsToReturn(fieldsToReturn); + } + + @Override + protected void configureDisplay(CellTable display) { + titleColumn = new TextColumn() { + @Override + public String getValue(DisposalHold hold) { + return hold != null ? hold.getTitle() : null; + } + }; + + mandateColumn = new TextColumn() { + @Override + public String getValue(DisposalHold hold) { + return hold != null ? hold.getMandate() : null; + } + }; + + scopeNotesColumn = new TextColumn() { + @Override + public String getValue(DisposalHold hold) { + return hold != null ? hold.getScopeNotes() : null; + } + }; + + stateColumn = new Column(new SafeHtmlCell()) { + @Override + public SafeHtml getValue(DisposalHold hold) { + SafeHtml ret = null; + if (hold != null) { + ret = HtmlSnippetUtils.getDisposalHoldStateHtml(hold); + } + + return ret; + } + }; + + titleColumn.setSortable(true); + mandateColumn.setSortable(true); + scopeNotesColumn.setSortable(true); + stateColumn.setSortable(true); + + addColumn(titleColumn, messages.disposalHoldTitle(), false, false); + addColumn(mandateColumn, messages.disposalHoldMandate(), false, false, 20); + addColumn(scopeNotesColumn, messages.disposalHoldNotes(), false, false, 10); + addColumn(stateColumn, messages.disposalHoldStateCol(), false, false, 9); + + // default sorting + display.getColumnSortList().push(new ColumnSortList.ColumnSortInfo(titleColumn, false)); + } + + @Override + protected Sorter getSorter(ColumnSortList columnSortList) { + Map, List> columnSortingKeyMap = new HashMap<>(); + + columnSortingKeyMap.put(titleColumn, Collections.singletonList(RodaConstants.DISPOSAL_HOLD_TITLE)); + columnSortingKeyMap.put(mandateColumn, Collections.singletonList(RodaConstants.DISPOSAL_HOLD_MANDATE)); + columnSortingKeyMap.put(scopeNotesColumn, Collections.singletonList(RodaConstants.DISPOSAL_HOLD_SCOPE_NOTES)); + columnSortingKeyMap.put(stateColumn, Collections.singletonList(RodaConstants.DISPOSAL_HOLD_STATE)); + return createSorter(columnSortList, columnSortingKeyMap); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/DisposalRuleList.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/DisposalRuleList.java new file mode 100644 index 0000000000..85eaa08c87 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/DisposalRuleList.java @@ -0,0 +1,106 @@ +package org.roda.wui.client.common.lists; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.v2.disposal.rule.ConditionType; +import org.roda.core.data.v2.disposal.rule.DisposalRule; +import org.roda.core.data.v2.index.sort.Sorter; +import org.roda.wui.client.common.lists.utils.AsyncTableCell; +import org.roda.wui.client.common.lists.utils.AsyncTableCellOptions; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.cellview.client.CellTable; +import com.google.gwt.user.cellview.client.Column; +import com.google.gwt.user.cellview.client.ColumnSortList; +import com.google.gwt.user.cellview.client.TextColumn; + +import config.i18n.client.ClientMessages; + +/** + * @author Miguel Guimarães + */ + +public class DisposalRuleList extends AsyncTableCell { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static final List fieldsToReturn = Arrays.asList(RodaConstants.INDEX_UUID, + RodaConstants.DISPOSAL_RULE_ORDER, RodaConstants.DISPOSAL_RULE_TITLE, RodaConstants.DISPOSAL_RULE_SELECTION_METHOD, + RodaConstants.DISPOSAL_RULE_CONDITION_KEY, RodaConstants.DISPOSAL_RULE_CONDITION_VALUE); + + private TextColumn orderColumn; + private TextColumn titleColumn; + private TextColumn selectionMethodColumn; + private TextColumn conditionColumn; + + @Override + protected void adjustOptions(AsyncTableCellOptions options) { + options.withFieldsToReturn(fieldsToReturn); + } + + @Override + protected void configureDisplay(CellTable display) { + orderColumn = new TextColumn() { + @Override + public String getValue(DisposalRule rule) { + int showOrder = rule.getOrder() + 1; + return "" + showOrder; + } + }; + + titleColumn = new TextColumn() { + @Override + public String getValue(DisposalRule rule) { + return rule != null ? rule.getTitle() : null; + } + }; + + selectionMethodColumn = new TextColumn() { + @Override + public String getValue(DisposalRule rule) { + return rule != null ? messages.disposalRuleTypeValue(rule.getType().toString()) : null; + } + }; + + conditionColumn = new TextColumn() { + @Override + public String getValue(DisposalRule rule) { + String condition = ""; + if (rule.getType().equals(ConditionType.METADATA_FIELD)) { + condition = rule.getConditionKey() + " " + messages.disposalRuleConditionOperator() + " " + + rule.getConditionValue(); + } else { + condition = rule.getConditionValue(); + } + return messages.disposalRuleTypeValue(condition); + } + }; + + orderColumn.setSortable(true); + titleColumn.setSortable(true); + selectionMethodColumn.setSortable(true); + conditionColumn.setSortable(false); + + addColumn(orderColumn, messages.disposalRuleOrder(), false, false, 4); + addColumn(titleColumn, messages.disposalRuleTitle(), false, false); + addColumn(selectionMethodColumn, messages.disposalRuleType(), false, false, 20); + addColumn(conditionColumn, messages.disposalRuleCondition(), false, false, 25); + + // default sorting + display.getColumnSortList().push(new ColumnSortList.ColumnSortInfo(orderColumn, true)); + } + + @Override + protected Sorter getSorter(ColumnSortList columnSortList) { + Map, List> columnSortingKeyMap = new HashMap<>(); + + columnSortingKeyMap.put(orderColumn, Collections.singletonList(RodaConstants.DISPOSAL_RULE_ORDER)); + columnSortingKeyMap.put(titleColumn, Collections.singletonList(RodaConstants.DISPOSAL_RULE_TITLE)); + columnSortingKeyMap.put(selectionMethodColumn, + Collections.singletonList(RodaConstants.DISPOSAL_RULE_SELECTION_METHOD)); + return createSorter(columnSortList, columnSortingKeyMap); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/DisposalScheduleList.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/DisposalScheduleList.java new file mode 100644 index 0000000000..d86f7b6f9b --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/DisposalScheduleList.java @@ -0,0 +1,124 @@ +package org.roda.wui.client.common.lists; + +import com.google.gwt.cell.client.SafeHtmlCell; +import com.google.gwt.core.client.GWT; +import com.google.gwt.safehtml.shared.SafeHtml; +import com.google.gwt.user.cellview.client.CellTable; +import com.google.gwt.user.cellview.client.Column; +import com.google.gwt.user.cellview.client.ColumnSortList; +import com.google.gwt.user.cellview.client.TextColumn; +import config.i18n.client.ClientMessages; +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; +import org.roda.core.data.v2.index.sort.Sorter; +import org.roda.wui.client.common.lists.utils.AsyncTableCell; +import org.roda.wui.client.common.lists.utils.AsyncTableCellOptions; +import org.roda.wui.client.common.utils.HtmlSnippetUtils; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Miguel Guimarães + */ + +public class DisposalScheduleList extends AsyncTableCell { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static final List fieldsToReturn = Arrays.asList(RodaConstants.INDEX_UUID, + RodaConstants.DISPOSAL_SCHEDULE_TITLE, RodaConstants.DISPOSAL_SCHEDULE_MANDATE, + RodaConstants.DISPOSAL_SCHEDULE_SCOPE_NOTES, RodaConstants.DISPOSAL_SCHEDULE_STATE, + RodaConstants.DISPOSAL_SCHEDULE_ACTION, RodaConstants.DISPOSAL_SCHEDULE_RETENTION_PERIOD_DURATION, + RodaConstants.DISPOSAL_SCHEDULE_RETENTION_PERIOD_INTERVAL_CODE); + + private TextColumn titleColumn; + private TextColumn mandateColumn; + private TextColumn periodColumn; + private TextColumn actionColumn; + private Column stateColumn; + + @Override + protected void adjustOptions(AsyncTableCellOptions options) { + options.withFieldsToReturn(fieldsToReturn); + } + + @Override + protected void configureDisplay(CellTable display) { + titleColumn = new TextColumn() { + @Override + public String getValue(DisposalSchedule schedule) { + return schedule != null ? schedule.getTitle() : null; + } + }; + + mandateColumn = new TextColumn() { + @Override + public String getValue(DisposalSchedule schedule) { + return schedule != null ? schedule.getMandate() : null; + } + }; + + periodColumn = new TextColumn() { + @Override + public String getValue(DisposalSchedule schedule) { + if (schedule.getRetentionPeriodDuration() == null && schedule.getRetentionPeriodIntervalCode() == null) { + return ""; + } else if (schedule.getRetentionPeriodDuration() == null) { + return messages.retentionPeriod(0, schedule.getRetentionPeriodIntervalCode().toString()); + } else { + return messages.retentionPeriod(schedule.getRetentionPeriodDuration(), + schedule.getRetentionPeriodIntervalCode().toString()); + } + } + }; + + actionColumn = new TextColumn() { + @Override + public String getValue(DisposalSchedule schedule) { + return messages.disposalScheduleAction(schedule.getActionCode().toString()); + } + }; + + stateColumn = new Column(new SafeHtmlCell()) { + @Override + public SafeHtml getValue(DisposalSchedule schedule) { + SafeHtml ret = null; + if (schedule != null) { + ret = HtmlSnippetUtils.getDisposalScheduleStateHtml(schedule); + } + + return ret; + } + }; + + titleColumn.setSortable(true); + mandateColumn.setSortable(true); + periodColumn.setSortable(true); + actionColumn.setSortable(true); + stateColumn.setSortable(true); + + addColumn(titleColumn, messages.disposalScheduleTitle(), false, false); + addColumn(mandateColumn, messages.disposalScheduleMandate(), false, false, 20); + addColumn(periodColumn, messages.disposalSchedulePeriod(), false, false, 15); + addColumn(actionColumn, messages.disposalScheduleActionCol(), false, false, 15); + addColumn(stateColumn, messages.disposalScheduleStateCol(), false, false, 9); + + // default sorting + display.getColumnSortList().push(new ColumnSortList.ColumnSortInfo(titleColumn, false)); + } + + @Override + protected Sorter getSorter(ColumnSortList columnSortList) { + Map, List> columnSortingKeyMap = new HashMap<>(); + + columnSortingKeyMap.put(titleColumn, Collections.singletonList(RodaConstants.DISPOSAL_SCHEDULE_TITLE)); + columnSortingKeyMap.put(mandateColumn, Collections.singletonList(RodaConstants.DISPOSAL_SCHEDULE_MANDATE)); + columnSortingKeyMap.put(periodColumn, + Collections.singletonList(RodaConstants.DISPOSAL_SCHEDULE_RETENTION_PERIOD_DURATION)); + columnSortingKeyMap.put(actionColumn, Collections.singletonList(RodaConstants.DISPOSAL_SCHEDULE_ACTION)); + columnSortingKeyMap.put(stateColumn, Collections.singletonList(RodaConstants.DISPOSAL_SCHEDULE_STATE)); + return createSorter(columnSortList, columnSortingKeyMap); + } +} 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..a7226e00fa 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); @@ -109,8 +107,8 @@ public SafeHtml getValue(RODAMember member) { activeColumn.setSortable(true); addColumn(typeColumn, SafeHtmlUtils.fromSafeConstant(""), false, false, 3); - addColumn(nameColumn, messages.userIdentifier(), true, false); addColumn(fullNameColumn, messages.userFullName(), true, false); + addColumn(nameColumn, messages.userIdentifier(), true, false); addColumn(groupsColumn, messages.userGroups(), true, false); addColumn(activeColumn, SafeHtmlUtils.fromSafeConstant(""), false, false, 3); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/pagination/ListSelectionStateMappers.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/pagination/ListSelectionStateMappers.java index c331a5a186..8b501eecbf 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/pagination/ListSelectionStateMappers.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/pagination/ListSelectionStateMappers.java @@ -7,6 +7,9 @@ */ package org.roda.wui.client.common.lists.pagination; +import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.core.data.v2.disposal.rule.DisposalRule; +import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; import org.roda.core.data.v2.index.IsIndexed; import org.roda.core.data.v2.ip.DIPFile; import org.roda.core.data.v2.ip.IndexedAIP; @@ -14,51 +17,24 @@ import org.roda.core.data.v2.ip.IndexedFile; import org.roda.core.data.v2.ip.IndexedRepresentation; import org.roda.core.data.v2.jobs.IndexedReport; +import org.roda.wui.common.client.ClientLogger; import com.github.nmorel.gwtjackson.client.ObjectMapper; import com.google.gwt.core.client.GWT; -import org.roda.wui.common.client.ClientLogger; public class ListSelectionStateMappers { - // IndexedAIP - public interface IndexedAIPMapper extends ObjectMapper> { - } - + private static final ClientLogger logger = new ClientLogger(ListSelectionUtils.class.getName()); private static IndexedAIPMapper indexedAIPMapper = GWT.create(IndexedAIPMapper.class); - - // IndexedRepresentation - public interface IndexedRepresentationMapper extends ObjectMapper> { - } - private static IndexedRepresentationMapper indexedRepresentationMapper = GWT .create(IndexedRepresentationMapper.class); - - // IndexedFile - public interface IndexedFileMapper extends ObjectMapper> { - } - private static IndexedFileMapper indexedFileMapper = GWT.create(IndexedFileMapper.class); - - // IndexedDIP - public interface IndexedDIPMapper extends ObjectMapper> { - } - private static IndexedDIPMapper indexedDIPMapper = GWT.create(IndexedDIPMapper.class); - - // DIPFile - public interface DIPFileMapper extends ObjectMapper> { - } - private static DIPFileMapper dipFileMapper = GWT.create(DIPFileMapper.class); - - // Job report - public interface IndexedReportMapper extends ObjectMapper> { - } - private static IndexedReportMapper indexedReportMapper = GWT.create(IndexedReportMapper.class); - - private static final ClientLogger logger = new ClientLogger(ListSelectionUtils.class.getName()); + private static DisposalHoldMapper disposalHoldMapper = GWT.create(DisposalHoldMapper.class); + private static DisposalScheduleMapper disposalScheduleMapper = GWT.create(DisposalScheduleMapper.class); + private static DisposalRuleMapper disposalRuleMapper = GWT.create(DisposalRuleMapper.class); private ListSelectionStateMappers() { // do nothing @@ -79,13 +55,18 @@ public static String getJson(String objectClass, ListSelec ret = dipFileMapper.write((ListSelectionState) object); } else if (IndexedReport.class.getName().equals(objectClass)) { ret = indexedReportMapper.write((ListSelectionState) object); + } else if (DisposalHold.class.getName().equals(objectClass)) { + ret = disposalHoldMapper.write((ListSelectionState) object); + } else if (DisposalSchedule.class.getName().equals(objectClass)) { + ret = disposalScheduleMapper.write((ListSelectionState) object); + } else if (DisposalRule.class.getName().equals(objectClass)) { + ret = disposalRuleMapper.write((ListSelectionState) object); } else { ret = null; } return ret; } - @SuppressWarnings("unchecked") public static ListSelectionState getObject(String objectClass, String json) { ListSelectionState state; if (IndexedAIP.class.getName().equals(objectClass)) { @@ -100,10 +81,52 @@ public static ListSelectionState getObject(String objec state = (ListSelectionState) dipFileMapper.read(json); } else if (IndexedReport.class.getName().equals(objectClass)) { state = (ListSelectionState) indexedReportMapper.read(json); + } else if (DisposalHold.class.getName().equals(objectClass)) { + state = (ListSelectionState) disposalHoldMapper.read(json); + } else if (DisposalSchedule.class.getName().equals(objectClass)) { + state = (ListSelectionState) disposalScheduleMapper.read(json); + } else if (DisposalRule.class.getName().equals(objectClass)) { + state = (ListSelectionState) disposalRuleMapper.read(json); } else { state = null; } return state; } + // IndexedAIP + public interface IndexedAIPMapper extends ObjectMapper> { + } + + // IndexedRepresentation + public interface IndexedRepresentationMapper extends ObjectMapper> { + } + + // IndexedFile + public interface IndexedFileMapper extends ObjectMapper> { + } + + // IndexedDIP + public interface IndexedDIPMapper extends ObjectMapper> { + } + + // DIPFile + public interface DIPFileMapper extends ObjectMapper> { + } + + // Job report + public interface IndexedReportMapper extends ObjectMapper> { + } + + // Disposal hold + public interface DisposalHoldMapper extends ObjectMapper> { + } + + // Disposal schedule + public interface DisposalScheduleMapper extends ObjectMapper> { + } + + // Disposal rule + public interface DisposalRuleMapper extends ObjectMapper> { + } + } 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/AsyncTableCellOptions.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/AsyncTableCellOptions.java index 7073dac391..87fc83ca53 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/AsyncTableCellOptions.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/AsyncTableCellOptions.java @@ -121,7 +121,6 @@ public AsyncTableCellOptions withFilter(Filter filter) { } public AsyncTableCellOptions withJustActive(boolean justActive) { - this.justActive = justActive; return this; } 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..ac79c105f8 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 { @@ -709,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; } @@ -966,6 +983,10 @@ pre code { text-overflow: ellipsis; } +.btn-slim { +padding: 8px !important; +} + .btn-block { width: 100%; } @@ -1119,6 +1140,14 @@ button:after { content: "\e3c9"; } +.btn-edit-order:after { + content: "\eb61"; +} + +.btn-password:after { + content: "\f042"; +} + .btn-key:after { content: "\e73c"; } @@ -1164,7 +1193,7 @@ button:after { } .btn-print:after { - content: "\e935"; + content: "\e8ad"; } .btn-refresh:after { @@ -1191,6 +1220,14 @@ button:after { content: "\e14b"; } +.btn-group:after { + content: "\f0c0"; +} + +.btn-user:after { + content: "\e7fe"; +} + .btn-preview:after { content: "\e8f4"; } @@ -1292,6 +1329,14 @@ button:after { content: "\e97a"; } +.ma-pageview:after { + content: "\e87a"; +} + +.btn-undo:after { + content: "\e166"; +} + .ma-tune:after { content: "\e429"; } @@ -1614,7 +1659,13 @@ button:after { } .selectAipResultsPanel { - max-height: 60vh; + width: 800px; + max-height: 400px; + 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; + /*max-height: 60vh; */ } .wui-dialog-layout .table-empty { @@ -2556,6 +2607,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 { @@ -3539,6 +3591,10 @@ td.datePickerMonth, td.datePickerYear { content: "\f023"; } +.tagIconGavel .gwt-Label:before { + content: "\e90e"; +} + .tagIconClock .gwt-Label:before { @mixin fontawesome(); content: "\e900"; @@ -4777,12 +4833,14 @@ td.datePickerMonth, td.datePickerYear { flex: 1; display: flex; gap: 16px; + align-items: flex-start; } .search-field { margin-right: 20px; - height: 35px; + min-height: 35px; + height: auto; } .search-field-warning { @@ -4826,7 +4884,7 @@ td.datePickerMonth, td.datePickerYear { .search-field-left-panel .search-field-input-panel { display: flex; gap: 8px; - align-items: center; + align-items: flex-start; } .search-field-left-panel .search-field-input-panel .gwt-Label { @@ -4953,6 +5011,13 @@ td.datePickerMonth, td.datePickerYear { max-width: 60vw; } +.wui-dialog-layout .wui-dialog-content { + display: flex; + flex-direction: column; + gap: 5px; + margin-bottom: 10px; +} + .gwt-DialogBox.wui-dialog-confirm .wui-dialog-layout, .gwt-DialogBox.wui-dialog-information .wui-dialog-layout, .gwt-DialogBox.wui-dialog-prompt .wui-dialog-layout { @@ -7214,8 +7279,9 @@ td.datePickerMonth, td.datePickerYear { .ri-dialog .ri-dialog-list-scroll { width: 800px; - height: 400px; - overflow-x: scroll; + max-height: 400px; + 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 +9249,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 +9267,7 @@ wui-disposal-hold-data .form-textbox { text-align: left; text-transform: uppercase; text-decoration: none; + font-weight: bold; } .badge-panel-dark { @@ -9713,3 +9780,257 @@ 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; +} + +.gwt-DialogBox.wui-dialog-confirm .wui-dialog-layout, .gwt-DialogBox.wui-dialog-information .wui-dialog-layout, .gwt-DialogBox.wui-dialog-prompt .wui-dialog-layout { + max-width: 95vw; +} + +.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; + display: flex; + flex-direction: column; +} + +.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; +} + +.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; +} + +/* ============================================================ + GENERIC FORM FIELD STRUCTURE + ============================================================ */ +.generic-data-panel { + min-height: auto; +} + +.generic-data-panel-fields { + display: flex; + flex-direction: column; +} + +.generic-form-field { + display: flex; + max-width: 750px; + margin-right: 20px; + min-height: 35px; /* Allows the row to grow for TextAreas */ + height: auto; +} + +.generic-form-field .form-textbox { + font-size: 14px; + color: black; + flex: 1; +} + +.generic-form-field.full_width { + margin-right: 0px; +} + +.generic-form-field-left-panel { + flex: 1; + display: flex; + gap: 16px; + align-items: flex-start; /* Keeps the label at the top for tall inputs */ + width: 100%; +} + +.generic-form-field-input-panel { + display: flex; + gap: 8px; + align-items: flex-start; /* Prevents upward overflow for tall inputs */ +} + +.generic-form-field-input-panel.full_width { + width: 100%; +} + +/* Ensure child textboxes expand correctly within the new layout */ +.generic-form-field-input-panel .form-textbox { + background-color: #FAFAFA; + border-bottom: 1px solid #D9D9D9; + border-left: none; + flex: 1; +} + +.mb-20 { + margin-bottom: 20px; +} + +.generic-data-panel-fields .form-separator { + font-size: 14px; + font-weight: bold; + margin: 0 -15px 0 -15px !important; +} + +.generic-data-panel-fields .form-readonly-value { + padding-top: 5px; /* Adjust to align with the labels */ + font-size: 14px; + color: black; + flex: 1; +} + +.generic-data-panel-fields .form-label { + font-size: 14px; + font-weight: 300; + color: #666666; + width: 250px; +} + +.generic-form-field-input-panel .form-listbox { + border:none; + margin-left: -4px; + font-size: 14px; + color: black; + flex: 1; +} + + +/* ============================================================ + GENERIC STACKED FIELD (Label on top, Input below) + ============================================================ */ +.generic-stacked-field { + display: flex; + flex-direction: column; + max-width: 750px; + margin-bottom: 15px; /* Spacing between the fields */ +} + +/* Override the 250px fixed width from the side-by-side layout */ +.generic-stacked-field .form-label { + width: auto; + margin-bottom: 4px; /* Small gap between label and password box */ + font-size: 14px; + font-weight: 300; + color: #666666; +} + +/* Ensure the password box inherits the exact same generic look */ +.generic-stacked-field .form-textbox { + background-color: #FAFAFA; + border: none; + border-bottom: 1px solid #D9D9D9; + font-size: 14px; + color: black; + width: 100%; + box-sizing: border-box; + padding: 6px 8px; /* Slightly taller padding for standalone look */ +} \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/SearchFieldPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/SearchFieldPanel.java index 95017845a1..7f75b639aa 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/SearchFieldPanel.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/SearchFieldPanel.java @@ -25,7 +25,6 @@ import org.roda.wui.common.client.tools.Humanize; import com.google.gwt.core.client.GWT; -import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ClickHandler; 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..df28c26481 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,9 @@ import java.util.Set; import java.util.concurrent.Callable; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.safehtml.shared.SafeHtml; +import com.google.gwt.user.client.ui.InlineHTML; import org.roda.core.data.v2.generics.MetadataValue; import org.roda.wui.client.common.RichTextToolbar; @@ -38,9 +41,11 @@ import com.google.gwt.user.client.ui.RichTextArea; import com.google.gwt.user.client.ui.TextArea; 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; +import org.roda.wui.common.client.tools.StringUtils; /** * Created by adrapereira on 13-06-2016. @@ -58,7 +63,7 @@ public static void create(FlowPanel panel, Set bundle, boolean ad } public static void create(FlowPanel panel, Set bundle, boolean addStyle, - final Callable onChange) { + final Callable onChange) { if (bundle != null) { for (MetadataValue mv : bundle) { boolean mandatory = mv.get("mandatory") != null && "true".equalsIgnoreCase(mv.get("mandatory")); @@ -106,6 +111,34 @@ public static void create(FlowPanel panel, Set bundle, boolean ad } } + /** + * Helper method to construct the standardized DOM structure for every generated field. + */ + private static void assembleFormRow(FlowPanel layout, Widget labelWidget, Widget inputWidget, Widget descriptionWidget, Widget errorWidget) { + layout.addStyleName("generic-form-field"); + + FlowPanel leftPanel = new FlowPanel(); + leftPanel.addStyleName("generic-form-field-left-panel"); + + FlowPanel inputPanel = new FlowPanel(); + inputPanel.addStyleName("generic-form-field-input-panel full_width"); + + // Assemble components + inputPanel.add(inputWidget); + leftPanel.add(labelWidget); + leftPanel.add(inputPanel); + + if (descriptionWidget != null) { + leftPanel.add(descriptionWidget); + } + + if (errorWidget != null) { + leftPanel.add(errorWidget); + } + + layout.add(leftPanel); + } + private static String getFieldLabel(MetadataValue mv) { String result = mv.getId(); String rawLabel = mv.get("label"); @@ -142,13 +175,15 @@ private static String getFieldLabel(MetadataValue mv) { } private static void addTextField(FlowPanel panel, final FlowPanel layout, final MetadataValue mv, - final boolean mandatory, final Callable onChange) { + final boolean mandatory, final Callable onChange) { // Top label Label mvLabel = new Label(getFieldLabel(mv)); mvLabel.addStyleName("form-label"); if (mandatory) { mvLabel.addStyleName("form-label-mandatory"); + mvLabel.setText(mvLabel.getText()); } + // Field final TextBox mvText = new TextBox(); mvText.setTitle(mvLabel.getText()); @@ -170,35 +205,36 @@ public void onChange(ChangeEvent changeEvent) { } }); - layout.add(mvLabel); - layout.add(mvText); - // Description + Label mvDescription = null; String description = mv.get("description"); if (description != null && description.length() > 0) { - Label mvDescription = new Label(description); + mvDescription = new Label(description); mvDescription.addStyleName("form-help"); - layout.add(mvDescription); } + // Error + Label errorLabel = null; if (mv.get("error") != null && !"".equals(mv.get("error").trim())) { - Label errorLabel = new Label(mv.get("error")); + errorLabel = new Label(mv.get("error")); errorLabel.addStyleName("form-label-error"); - layout.add(errorLabel); mvText.addStyleName("isWrong"); } + assembleFormRow(layout, mvLabel, mvText, mvDescription, errorLabel); panel.add(layout); } private static void addTextArea(FlowPanel panel, final FlowPanel layout, final MetadataValue mv, - final boolean mandatory, final Callable onChange) { + final boolean mandatory, final Callable onChange) { // Top label Label mvLabel = new Label(getFieldLabel(mv)); mvLabel.addStyleName("form-label"); if (mandatory) { mvLabel.addStyleName("form-label-mandatory"); + mvLabel.setText(mvLabel.getText()); } + // Field final TextArea mvText = new TextArea(); mvText.setTitle(mvLabel.getText()); @@ -220,37 +256,37 @@ public void onChange(ChangeEvent changeEvent) { } }); - layout.add(mvLabel); - layout.add(mvText); - // Description + Label mvDescription = null; String description = mv.get("description"); if (description != null && description.length() > 0) { - Label mvDescription = new Label(description); + mvDescription = new Label(description); mvDescription.addStyleName("form-help"); - layout.add(mvDescription); } + // Error + Label errorLabel = null; if (mv.get("error") != null && !"".equalsIgnoreCase(mv.get("error").trim())) { - Label errorLabel = new Label(mv.get("error")); + errorLabel = new Label(mv.get("error")); errorLabel.addStyleName("form-label-error"); - layout.add(errorLabel); mvText.addStyleName("isWrong"); } + assembleFormRow(layout, mvLabel, mvText, mvDescription, errorLabel); panel.add(layout); } private static void addRichTextArea(FlowPanel panel, final FlowPanel layout, final MetadataValue mv, - final boolean mandatory, final Callable onChange) { + final boolean mandatory, final Callable onChange) { // Top label Label mvLabel = new Label(getFieldLabel(mv)); mvLabel.addStyleName("form-label"); if (mandatory) { mvLabel.addStyleName("form-label-mandatory"); + mvLabel.setText(mvLabel.getText() + "*"); } - // Field + // Field wrapped with toolbar final FlowPanel fieldLayout = new FlowPanel(); final RichTextArea mvText = new RichTextArea(); @@ -278,34 +314,36 @@ public void onBlur(BlurEvent arg0) { fieldLayout.add(toolbar); fieldLayout.add(mvText); - layout.add(mvLabel); - layout.add(fieldLayout); - // Description + Label mvDescription = null; String description = mv.get("description"); if (description != null && description.length() > 0) { - Label mvDescription = new Label(description); + mvDescription = new Label(description); mvDescription.addStyleName("form-help"); - layout.add(mvDescription); } + // Error + Label errorLabel = null; if (mv.get("error") != null && !"".equalsIgnoreCase(mv.get("error").trim())) { - Label errorLabel = new Label(mv.get("error")); + errorLabel = new Label(mv.get("error")); errorLabel.addStyleName("form-label-error"); - layout.add(errorLabel); mvText.addStyleName("isWrong"); } + + assembleFormRow(layout, mvLabel, fieldLayout, mvDescription, errorLabel); panel.add(layout); } private static void addList(FlowPanel panel, final FlowPanel layout, final MetadataValue mv, final boolean mandatory, - final Callable onChange) { + final Callable onChange) { // Top Label Label mvLabel = new Label(getFieldLabel(mv)); mvLabel.addStyleName("form-label"); if (mandatory) { mvLabel.addStyleName("form-label-mandatory"); + mvLabel.setText(mvLabel.getText()); } + // Field final ListBox mvList = new ListBox(); mvList.setTitle(mvLabel.getText()); @@ -407,7 +445,7 @@ public void onChange(ChangeEvent changeEvent) { } if (mandatory && (mvList.getSelectedValue() == null || "".equalsIgnoreCase(mvList.getSelectedValue().trim()))) { - mvList.removeStyleName("isWrong"); + mvList.addStyleName("isWrong"); // Added isWrong logic here for empty list selection } } }); @@ -417,35 +455,37 @@ public void onChange(ChangeEvent changeEvent) { mv.set("value", mvList.getSelectedValue()); } - layout.add(mvLabel); - layout.add(mvList); - // Description + Label mvDescription = null; String description = mv.get("description"); if (description != null && description.length() > 0) { - Label mvDescription = new Label(description); + mvDescription = new Label(description); mvDescription.addStyleName("form-help"); - layout.add(mvDescription); } + // Error + Label errorLabel = null; if (mv.get("error") != null && !"".equals(mv.get("error").trim())) { - Label errorLabel = new Label(mv.get("error")); + errorLabel = new Label(mv.get("error")); errorLabel.addStyleName("form-label-error"); - layout.add(errorLabel); mvList.addStyleName("isWrong"); } + + assembleFormRow(layout, mvLabel, mvList, mvDescription, errorLabel); panel.add(layout); } private static void addDatePicker(FlowPanel panel, final FlowPanel layout, final MetadataValue mv, - final boolean mandatory, final Callable onChange) { + final boolean mandatory, final Callable onChange) { // Top label final DateTimeFormat dateTimeFormat = DateTimeFormat.getFormat("yyyy-MM-dd"); Label mvLabel = new Label(getFieldLabel(mv)); mvLabel.addStyleName("form-label"); if (mandatory) { mvLabel.addStyleName("form-label-mandatory"); + mvLabel.setText(mvLabel.getText()); } + // Field final DateBox mvDate = new DateBox(); mvDate.setTitle(mvLabel.getText()); @@ -486,7 +526,6 @@ public void onValueChange(ValueChangeEvent valueChangeEvent) { }); mvDate.getTextBox().addValueChangeHandler(new ValueChangeHandler() { - @Override public void onValueChange(ValueChangeEvent event) { FormUtilities.callOnChange(onChange); @@ -504,23 +543,23 @@ public void onValueChange(ValueChangeEvent event) { } }); - layout.add(mvLabel); - layout.add(mvDate); - // Description + Label mvDescription = null; String description = mv.get("description"); if (description != null && description.length() > 0) { - Label mvDescription = new Label(description); + mvDescription = new Label(description); mvDescription.addStyleName("form-help"); - layout.add(mvDescription); } + // Error + Label errorLabel = null; if (mv.get("error") != null && !"".equals(mv.get("error").trim())) { - Label errorLabel = new Label(mv.get("error")); + errorLabel = new Label(mv.get("error")); errorLabel.addStyleName("form-label-error"); - layout.add(errorLabel); mvDate.addStyleName("isWrong"); } + + assembleFormRow(layout, mvLabel, mvDate, mvDescription, errorLabel); panel.add(layout); } @@ -548,10 +587,10 @@ public static List validate(Set values, FlowPanel extra) if (mandatory && (value == null || value.trim().isEmpty())) { String labels = mv.get("l"); errors.add(messages.isAMandatoryField(labels)); - mv.set("error", messages.mandatoryField()); - } else { - mv.set("error", null); - } + //mv.set("error", messages.mandatoryField()); + } //else { + //mv.set("error", null); + //} } } @@ -569,4 +608,59 @@ 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 void addIfNotBlank(FlowPanel panel, String label, SafeHtml value) { + if (StringUtils.isNotBlank(value.asString())) { + panel.add(buildField(label, new InlineHTML(value))); + } + } + + public static void addIfNotBlank(FlowPanel panel, String label, String value, String styles, ClickHandler clickHandler) { + if (StringUtils.isNotBlank(value)) { + InlineHTML inlineHTML = new InlineHTML(SafeHtmlUtils.htmlEscape(value)); + inlineHTML.addStyleName(styles); + inlineHTML.addClickHandler(clickHandler); + panel.add(buildField(label, inlineHTML)); + } + } + + public static void addSeparator(FlowPanel panel, String label) { + panel.add(buildSeparator(label)); + } + + public static FlowPanel buildSeparator(String text) { + FlowPanel separator = new FlowPanel(); + separator.addStyleName("form-separator"); + Label separatorLabel = new Label(text); + separatorLabel.addStyleName("separator-label"); + separator.add(separatorLabel); + + return separator; + } +} \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/utils/HtmlSnippetUtils.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/utils/HtmlSnippetUtils.java index 752b1ad321..e7160d83f2 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/utils/HtmlSnippetUtils.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/utils/HtmlSnippetUtils.java @@ -17,6 +17,7 @@ import org.roda.core.data.v2.disposal.hold.DisposalHold; import org.roda.core.data.v2.disposal.hold.DisposalHoldAssociation; import org.roda.core.data.v2.disposal.hold.DisposalHoldState; +import org.roda.core.data.v2.disposal.rule.DisposalRule; import org.roda.core.data.v2.disposal.schedule.DisposalActionCode; import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; import org.roda.core.data.v2.generics.MetadataValue; @@ -71,7 +72,6 @@ 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.UIObject; import config.i18n.client.ClientMessages; @@ -152,6 +152,18 @@ public static SafeHtml getDisposalScheduleStateHtml(DisposalSchedule disposalSch return ret; } + public static SafeHtml getDisposalRuleTypeHtml(DisposalRule disposalRule) { + SafeHtml ret = null; + if (disposalRule != null && disposalRule.getType() != null) { + SafeHtmlBuilder b = new SafeHtmlBuilder(); + b.append(SafeHtmlUtils.fromSafeConstant(OPEN_SPAN_CLASS_LABEL_INFO)); + b.append(SafeHtmlUtils.fromString(messages.disposalRuleTypeValue(disposalRule.getType().toString()))); + b.append(SafeHtmlUtils.fromSafeConstant(CLOSE_SPAN)); + ret = b.toSafeHtml(); + } + return ret; + } + public static SafeHtml getDisposalHoldStateHtml(DisposalHoldAssociation disposalHoldAssociation) { SafeHtmlBuilder b = new SafeHtmlBuilder(); if (disposalHoldAssociation.getLiftedOn() != null) { @@ -537,6 +549,10 @@ public static final void setCssClassDisabled(UIObject uiobject, boolean disabled } } + public static String getMetadataValueLabel(MetadataValue metadataValue) { + return getFieldLabel(metadataValue); + } + public static void createExtraShow(FlowPanel panel, Set bundle, boolean addStyle) { FlowPanel lastSeparator = null; boolean hasFields = false; @@ -723,9 +739,9 @@ public static String getTransferredResourceStateHTML(String state) { switch (state) { case "Deleted": - response = OPEN_SPAN_CLASS_LABEL_DANGER + messages.sipDeleted() + CLOSE_SPAN; + response = OPEN_SPAN_CLASS_LABEL_DANGER + messages.sipDeleted() + CLOSE_SPAN; break; - } + } return response; } 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/disposal/Disposal.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/Disposal.java index 13eee040f4..c5fcef3d9e 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/Disposal.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/Disposal.java @@ -11,6 +11,7 @@ import java.util.List; import org.roda.wui.client.common.UserLogin; +import org.roda.wui.client.disposal.confirmations.OverdueRecords; import org.roda.wui.client.disposal.policy.DisposalPolicy; import org.roda.wui.common.client.HistoryResolver; import org.roda.wui.common.client.tools.HistoryUtils; @@ -23,6 +24,7 @@ * @author Tiago Fraga */ public class Disposal { + private static Disposal instance = null; public static final HistoryResolver RESOLVER = new HistoryResolver() { @Override @@ -47,8 +49,12 @@ public String getHistoryToken() { return "disposal"; } }; + private boolean initialized; + private HTMLWidgetWrapper page; - private static Disposal instance = null; + private Disposal() { + initialized = false; + } /** * Get the singleton instance @@ -62,19 +68,13 @@ public static Disposal getInstance() { return instance; } - private boolean initialized; - - private HTMLWidgetWrapper page; - - private Disposal() { - initialized = false; - } - public void resolve(List historyTokens, AsyncCallback callback) { if (historyTokens.isEmpty()) { callback.onSuccess(page); } else if (historyTokens.get(0).equals(DisposalPolicy.RESOLVER.getHistoryToken())) { DisposalPolicy.RESOLVER.resolve(HistoryUtils.tail(historyTokens), callback); + } else if (historyTokens.get(0).equals(OverdueRecords.RESOLVER.getHistoryToken())) { + OverdueRecords.RESOLVER.resolve(HistoryUtils.tail(historyTokens), callback); } else if (historyTokens.get(0).equals(DisposalConfirmations.RESOLVER.getHistoryToken())) { DisposalConfirmations.RESOLVER.resolve(HistoryUtils.tail(historyTokens), callback); } else if (historyTokens.get(0).equals(DisposalDestroyedRecords.RESOLVER.getHistoryToken())) { diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/DisposalConfirmations.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/DisposalConfirmations.java index 41b1856e17..791ff05ea8 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/DisposalConfirmations.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/DisposalConfirmations.java @@ -11,7 +11,7 @@ import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation; import org.roda.wui.client.common.UserLogin; -import org.roda.wui.client.common.actions.DisposalConfirmationActions; +import org.roda.wui.client.common.actions.DisposalConfirmationSearchWrapperActions; import org.roda.wui.client.common.lists.DisposalConfirmationList; import org.roda.wui.client.common.lists.utils.AsyncTableCellOptions; import org.roda.wui.client.common.lists.utils.ListBuilder; @@ -71,7 +71,7 @@ public String getHistoryToken() { */ public DisposalConfirmations() { - final DisposalConfirmationActions confirmationActions = DisposalConfirmationActions.getInstance(); + final DisposalConfirmationSearchWrapperActions confirmationActions = DisposalConfirmationSearchWrapperActions.getInstance(); ListBuilder disposalConfirmationListBuilder = new ListBuilder<>( () -> new DisposalConfirmationList(), diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/CreateDisposalConfirmation.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/CreateDisposalConfirmation.java index 90c0e01b58..18d9095779 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/CreateDisposalConfirmation.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/CreateDisposalConfirmation.java @@ -1,89 +1,72 @@ -/** - * 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.disposal.confirmations; -import java.util.Date; import java.util.List; -import org.roda.core.data.common.RodaConstants; -import org.roda.core.data.v2.disposal.schedule.DisposalActionCode; -import org.roda.core.data.v2.disposal.schedule.RetentionPeriodCalculation; -import org.roda.core.data.v2.index.filter.DateIntervalFilterParameter; -import org.roda.core.data.v2.index.filter.EmptyKeyFilterParameter; -import org.roda.core.data.v2.index.filter.Filter; -import org.roda.core.data.v2.index.filter.SimpleFilterParameter; +import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation; +import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmationCreateRequest; +import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmationForm; import org.roda.core.data.v2.index.select.SelectedItems; -import org.roda.core.data.v2.ip.AIPState; import org.roda.core.data.v2.ip.IndexedAIP; -import org.roda.wui.client.common.NoAsyncCallback; +import org.roda.wui.client.common.NavigationToolbar; +import org.roda.wui.client.common.NoActionsToolbar; +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.DisposalCreateConfirmationDestroyActions; -import org.roda.wui.client.common.actions.DisposalCreateConfirmationReviewActions; -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 org.roda.wui.client.common.dialogs.Dialogs; import org.roda.wui.client.disposal.DisposalConfirmations; +import org.roda.wui.client.disposal.confirmations.data.panels.DisposalConfirmationDataPanel; +import org.roda.wui.client.ingest.process.ShowJob; +import org.roda.wui.client.main.BreadcrumbUtils; +import org.roda.wui.client.process.InternalProcess; +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.HTMLWidgetWrapper; +import org.roda.wui.common.client.widgets.Toast; import com.google.gwt.core.client.GWT; -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.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.RadioButton; +import com.google.gwt.user.client.ui.FocusPanel; import com.google.gwt.user.client.ui.Widget; import config.i18n.client.ClientMessages; /** - * @author Miguel Guimarães + * @author Tiago Fraga */ public class CreateDisposalConfirmation extends Composite { - private static final DateTimeFormat formatter = DateTimeFormat.getFormat(RodaConstants.SIMPLE_DATE_FORMATTER); - - private static final Filter SHOW_RECORDS_TO_REVIEW = new Filter( - new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_ACTION, DisposalActionCode.REVIEW.name()), - new SimpleFilterParameter(RodaConstants.AIP_STATE, AIPState.ACTIVE.name()), - new DateIntervalFilterParameter(RodaConstants.AIP_OVERDUE_DATE, RodaConstants.AIP_OVERDUE_DATE, null, - formatter.parse(formatter.format(new Date()))), - new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_HOLD_STATUS, Boolean.FALSE.toString()), - new EmptyKeyFilterParameter(RodaConstants.AIP_DISPOSAL_CONFIRMATION_ID)); - - private static final Filter SHOW_RECORDS_TO_DESTROY = new Filter( - new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_ACTION, DisposalActionCode.DESTROY.name()), - new SimpleFilterParameter(RodaConstants.AIP_STATE, AIPState.ACTIVE.name()), - new DateIntervalFilterParameter(RodaConstants.AIP_OVERDUE_DATE, RodaConstants.AIP_OVERDUE_DATE, null, - formatter.parse(formatter.format(new Date()))), - new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_HOLD_STATUS, Boolean.FALSE.toString()), - new EmptyKeyFilterParameter(RodaConstants.AIP_DISPOSAL_CONFIRMATION_ID)); - - private static final Filter SHOW_RECORDS_WITH_RETENTION_PERIOD_ERRORS = new Filter(new SimpleFilterParameter( - RodaConstants.AIP_DISPOSAL_RETENTION_PERIOD_CALCULATION, RetentionPeriodCalculation.ERROR.name())); private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static CreateDisposalConfirmation.MyUiBinder uiBinder = GWT - .create(CreateDisposalConfirmation.MyUiBinder.class); - private static CreateDisposalConfirmation instance; + // --- IN-MEMORY TRANSFER VARIABLE --- + private static SelectedItems pendingSelection; + // ----------------------------------- public static final HistoryResolver RESOLVER = new HistoryResolver() { @Override - public void resolve(List historyTokens, AsyncCallback callback) { - getInstance().resolve(historyTokens, callback); + public void resolve(List historyTokens, final AsyncCallback callback) { + // Safety check: if pendingSelection is null, it means the user hit F5 on this + // page. + // We bounce them back to the Overdue Records page because the context is lost. + if (pendingSelection == null) { + HistoryUtils.newHistory(OverdueRecords.RESOLVER); + callback.onSuccess(null); + return; + } + + CreateDisposalConfirmation confirmation = new CreateDisposalConfirmation(pendingSelection); + + // Clear the static variable immediately so it doesn't leak or accidentally get + // reused + pendingSelection = null; + + callback.onSuccess(confirmation); } @Override public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRole(this, callback); + UserLogin.getInstance().checkRoles(new HistoryResolver[] {DisposalConfirmations.RESOLVER}, false, callback); } @Override @@ -93,115 +76,74 @@ public List getHistoryPath() { @Override public String getHistoryToken() { - return "create_confirmation"; + return "create"; } }; - @UiField(provided = true) - SearchWrapper overdueRecordsSearch; - + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); @UiField - FlowPanel createDisposalConfirmationDescription; - + FocusPanel keyboardFocus; @UiField - RadioButton destroyScheduleOpt; - + NavigationToolbar navigationToolbar; @UiField - RadioButton reviewScheduleOpt; - + NoActionsToolbar actionsToolbar; @UiField - RadioButton retentionCalculationFailedOpt; - + TitlePanel title; @UiField - FlowPanel content; - private SelectedItems selected = null; - private final AsyncCallback listActionableCallback = new NoAsyncCallback() { - @Override - public void onSuccess(Actionable.ActionImpact impact) { - if (impact.equals(Actionable.ActionImpact.UPDATED)) { - selected = overdueRecordsSearch.getSelectedItems(IndexedAIP.class); - } - } - }; - - /** - * Create a new panel to create a disposal confirmation - */ - private CreateDisposalConfirmation() { - ListBuilder overdueRecordsListBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), - new AsyncTableCellOptions<>(IndexedAIP.class, "DisposalOverdueRecords_aip").withSummary(messages.listOfAIPs()) - .withFilter(SHOW_RECORDS_TO_DESTROY).withActionable(DisposalCreateConfirmationDestroyActions.get()) - .withActionableCallback(listActionableCallback).withJustActive(true).bindOpener()); - overdueRecordsSearch = new SearchWrapper(false).createListAndSearchPanel(overdueRecordsListBuilder); - + FlowPanel dataPanel; + public CreateDisposalConfirmation(final SelectedItems selectedItems) { initWidget(uiBinder.createAndBindUi(this)); - configureDisposalAction(); + DisposalConfirmationDataPanel confirmationDataPanel = new DisposalConfirmationDataPanel(); + confirmationDataPanel.setDisposalConfirmation(new DisposalConfirmation()); + dataPanel.add(confirmationDataPanel); + + confirmationDataPanel.setSaveHandler(() -> { + DisposalConfirmation value = confirmationDataPanel.getValue(); + DisposalConfirmationCreateRequest request = new DisposalConfirmationCreateRequest(value.getTitle(), selectedItems, + new DisposalConfirmationForm(confirmationDataPanel.getDisposalConfirmationExtra())); + Services services = new Services("Create disposal confirmation", "create"); + services.disposalConfirmationResource(s -> s.createDisposalConfirmation(request)) + .whenComplete((job, throwable) -> { + if (throwable != null) { + HistoryUtils.newHistory(InternalProcess.RESOLVER); + } else { + Dialogs.showJobRedirectDialog(messages.jobCreatedMessage(), new AsyncCallback() { + + @Override + public void onFailure(Throwable caught) { + Toast.showInfo(messages.runningInBackgroundTitle(), messages.runningInBackgroundDescription()); + HistoryUtils.newHistory(DisposalConfirmations.RESOLVER); + } + + @Override + public void onSuccess(final Void nothing) { + HistoryUtils.newHistory(ShowJob.RESOLVER, job.getId()); + } + }); + } + }); + }); - createDisposalConfirmationDescription.add(new HTMLWidgetWrapper("CreateDisposalConfirmationDescription.html")); - } + confirmationDataPanel.setCancelHandler(() -> HistoryUtils.newHistory(OverdueRecords.RESOLVER)); - public static CreateDisposalConfirmation getInstance() { - if (instance == null) { - instance = new CreateDisposalConfirmation(); - } + navigationToolbar.withoutButtons().build(); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getCreateDisposalConfirmationBreadcrumbs()); - return instance; - } + actionsToolbar.setLabel(messages.showDisposalConfirmationTitle()); + actionsToolbar.build(); - public SelectedItems getSelected() { - return selected; - } + title.setText(messages.createDisposalConfirmationTitle()); + title.setIconClass("DisposalConfirmations"); + title.addStyleName("mb-20"); - public void clear() { - instance = null; - selected = null; + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse"); } - private void configureDisposalAction() { - destroyScheduleOpt.setText(messages.disposalConfirmationShowRecordsToDestroy()); - destroyScheduleOpt.addValueChangeHandler(valueChangeEvent -> { - ListBuilder overdueRecordsListBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), - new AsyncTableCellOptions<>(IndexedAIP.class, "DisposalOverdueRecords_aip").withSummary(messages.listOfAIPs()) - .withFilter(SHOW_RECORDS_TO_DESTROY).withActionable(DisposalCreateConfirmationDestroyActions.get()) - .withActionableCallback(listActionableCallback).withJustActive(true).bindOpener()); - content.remove(overdueRecordsSearch); - overdueRecordsSearch = new SearchWrapper(false).createListAndSearchPanel(overdueRecordsListBuilder); - content.add(overdueRecordsSearch); - }); - - reviewScheduleOpt.setText(messages.disposalConfirmationShowRecordsToReview()); - reviewScheduleOpt.addValueChangeHandler(valueChangeEvent -> { - ListBuilder overdueRecordsListBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), - new AsyncTableCellOptions<>(IndexedAIP.class, "DisposalOverdueRecords_aip").withSummary(messages.listOfAIPs()) - .withFilter(SHOW_RECORDS_TO_REVIEW).withActionable(DisposalCreateConfirmationReviewActions.get()) - .withActionableCallback(listActionableCallback).withJustActive(true).bindOpener()); - content.remove(overdueRecordsSearch); - overdueRecordsSearch = new SearchWrapper(false).createListAndSearchPanel(overdueRecordsListBuilder); - content.add(overdueRecordsSearch); - }); - - retentionCalculationFailedOpt.setText(messages.disposalConfirmationShowRecordsRetentionPeriodCalculationError()); - retentionCalculationFailedOpt.addValueChangeHandler(valueChangeEvent -> { - ListBuilder overdueRecordsListBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), - new AsyncTableCellOptions<>(IndexedAIP.class, "DisposalOverdueRecords_aip").withSummary(messages.listOfAIPs()) - .withFilter(SHOW_RECORDS_WITH_RETENTION_PERIOD_ERRORS).withJustActive(true).bindOpener()); - content.remove(overdueRecordsSearch); - overdueRecordsSearch = new SearchWrapper(false).createListAndSearchPanel(overdueRecordsListBuilder); - content.add(overdueRecordsSearch); - }); - } - - private void resolve(List historyTokens, AsyncCallback callback) { - if (historyTokens.isEmpty()) { - callback.onSuccess(this); - } else { - String basePage = historyTokens.remove(0); - if (CreateDisposalConfirmationDataPanel.RESOLVER.getHistoryToken().equals(basePage)) { - CreateDisposalConfirmationDataPanel.RESOLVER.resolve(historyTokens, callback); - } - } + public static void setPendingSelection(SelectedItems selection) { + pendingSelection = selection; } interface MyUiBinder extends UiBinder { } -} +} \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/CreateDisposalConfirmation.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/CreateDisposalConfirmation.ui.xml index fdea4b364d..6b4037acd7 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/CreateDisposalConfirmation.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/CreateDisposalConfirmation.ui.xml @@ -1,22 +1,21 @@ - + - - + + + - - - - - - - - + + + + + + - - + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/CreateDisposalConfirmationDataPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/CreateDisposalConfirmationDataPanel.java deleted file mode 100644 index 562635bbf3..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/CreateDisposalConfirmationDataPanel.java +++ /dev/null @@ -1,217 +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.disposal.confirmations; - -import java.util.ArrayList; -import java.util.List; - -import org.roda.core.data.common.RodaConstants; -import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmationCreateRequest; -import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmationForm; -import org.roda.core.data.v2.index.select.SelectedItems; -import org.roda.core.data.v2.ip.IndexedAIP; -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.common.utils.FormUtilities; -import org.roda.wui.client.common.utils.PermissionClientUtils; -import org.roda.wui.client.disposal.DisposalConfirmations; -import org.roda.wui.client.ingest.process.ShowJob; -import org.roda.wui.client.process.InternalProcess; -import org.roda.wui.client.services.DisposalConfirmationRestService; -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.HTMLWidgetWrapper; -import org.roda.wui.common.client.widgets.Toast; - -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.Window; -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.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 config.i18n.client.ClientMessages; - -/** - * @author Miguel Guimarães - */ -public class CreateDisposalConfirmationDataPanel extends Composite { - - public static final HistoryResolver RESOLVER = new HistoryResolver() { - @Override - public void resolve(List historyTokens, AsyncCallback callback) { - CreateDisposalConfirmationDataPanel createDisposalConfirmation = new CreateDisposalConfirmationDataPanel(); - callback.onSuccess(createDisposalConfirmation); - } - - @Override - public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRole(this, callback); - } - - @Override - public List getHistoryPath() { - return ListUtils.concat(CreateDisposalConfirmation.RESOLVER.getHistoryPath(), getHistoryToken()); - } - - @Override - public String getHistoryToken() { - return "insert_information"; - } - }; - - private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static CreateDisposalConfirmationDataPanel.MyUiBinder uiBinder = GWT - .create(CreateDisposalConfirmationDataPanel.MyUiBinder.class); - @UiField - FlowPanel createDisposalConfirmationFormDescription; - @UiField - FlowPanel content; - @UiField - FlowPanel extra; - @UiField - HTML errors; - @UiField - TextBox title; - @UiField - Label titleError; - @UiField - FlowPanel buttonsPanel; - private DisposalConfirmationForm disposalConfirmationForm = null; - - /** - * Create a new panel to create a disposal confirmation - */ - public CreateDisposalConfirmationDataPanel() { - initWidget(uiBinder.createAndBindUi(this)); - - initActions(); - createDisposalConfirmationFormDescription - .add(new HTMLWidgetWrapper("CreateDisposalConfirmationExtraDescription.html")); - - errors.setVisible(false); - - Services services = new Services("Get disposal confirmation configurable form", "get"); - services.disposalConfirmationResource(DisposalConfirmationRestService::retrieveDisposalConfirmationForm) - .whenComplete((result, throwable) -> { - if (throwable != null) { - AsyncCallbackUtils.defaultFailureTreatment(throwable); - } else { - disposalConfirmationForm = result; - FormUtilities.create(extra, disposalConfirmationForm.getValues(), true); - } - }); - } - - private void initActions() { - Button btnCancel = new Button(); - btnCancel.setText(messages.cancelButton()); - btnCancel.addStyleName("btn btn-block btn-default btn-times-circle"); - btnCancel.addClickHandler(e -> { - CreateDisposalConfirmation.getInstance().clear(); - HistoryUtils.newHistory(ShowDisposalConfirmation.RESOLVER); - }); - - if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_CREATE_DISPOSAL_CONFIRMATION)) { - Button btnCreate = new Button(); - btnCreate.setText(messages.confirmButton()); - btnCreate.addStyleName("btn btn-block btn-play"); - btnCreate.addClickHandler(e -> { - boolean valid = isValid(); - if (valid) { - createDisposalConfirmationReport(); - } - }); - - buttonsPanel.add(btnCreate); - } - - buttonsPanel.add(btnCancel); - } - - private void createDisposalConfirmationReport() { - SelectedItems selectedItemsList = CreateDisposalConfirmation.getInstance().getSelected(); - if (selectedItemsList == null) { - Toast.showInfo("Error", "Error"); - HistoryUtils.newHistory(ShowDisposalConfirmation.RESOLVER); - } else { - DisposalConfirmationCreateRequest request = new DisposalConfirmationCreateRequest(title.getText(), - selectedItemsList, disposalConfirmationForm); - Services services = new Services("Create disposal confirmation", "create"); - services.disposalConfirmationResource(s -> s.createDisposalConfirmation(request)) - .whenComplete((job, throwable) -> { - if (throwable != null) { - HistoryUtils.newHistory(InternalProcess.RESOLVER); - } else { - Dialogs.showJobRedirectDialog(messages.jobCreatedMessage(), new AsyncCallback() { - - @Override - public void onFailure(Throwable caught) { - Toast.showInfo(messages.runningInBackgroundTitle(), messages.runningInBackgroundDescription()); - CreateDisposalConfirmation.getInstance().clear(); - HistoryUtils.newHistory(DisposalConfirmations.RESOLVER); - } - - @Override - public void onSuccess(final Void nothing) { - CreateDisposalConfirmation.getInstance().clear(); - HistoryUtils.newHistory(ShowJob.RESOLVER, job.getId()); - } - }); - } - }); - } - } - - /** - * Is data panel valid - * - * @return true if valid - */ - private boolean isValid() { - List errorList = new ArrayList<>(); - if (title.getText().isEmpty()) { - title.addStyleName("isWrong"); - titleError.setText(messages.mandatoryField()); - titleError.setVisible(true); - Window.scrollTo(title.getAbsoluteLeft(), title.getAbsoluteTop()); - errorList.add(messages.isAMandatoryField(messages.disposalConfirmationDataPanelTitle())); - } else { - title.removeStyleName("isWrong"); - titleError.setVisible(false); - } - - List extraErrors = FormUtilities.validate(disposalConfirmationForm.getValues(), extra); - errorList.addAll(extraErrors); - - if (!errorList.isEmpty()) { - errors.setVisible(true); - StringBuilder errorString = new StringBuilder(); - for (String error : errorList) { - errorString.append("").append(error).append(""); - errorString.append("
"); - } - errors.setHTML(errorString.toString()); - } else { - errors.setVisible(false); - } - return errorList.isEmpty(); - } - - interface MyUiBinder extends UiBinder { - } -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/CreateDisposalConfirmationDataPanel.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/CreateDisposalConfirmationDataPanel.ui.xml deleted file mode 100644 index 1adfef45cf..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/CreateDisposalConfirmationDataPanel.ui.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - * - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/OverdueRecords.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/OverdueRecords.java new file mode 100644 index 0000000000..e87acbe3ac --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/OverdueRecords.java @@ -0,0 +1,85 @@ +/** + * 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.disposal.confirmations; + +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 org.roda.wui.client.browse.tabs.DisposalOverdueRecordsTabs; +import org.roda.wui.client.common.UserLogin; +import org.roda.wui.client.disposal.Disposal; +import org.roda.wui.common.client.HistoryResolver; +import org.roda.wui.common.client.tools.ListUtils; +import org.roda.wui.common.client.widgets.HTMLWidgetWrapper; + +import java.util.List; + +/** + * @author Miguel Guimarães + */ +public class OverdueRecords extends Composite { + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + private static OverdueRecords instance; + public static final HistoryResolver RESOLVER = new HistoryResolver() { + @Override + public void resolve(List historyTokens, AsyncCallback callback) { + getInstance().resolve(historyTokens, callback); + } + + @Override + public void isCurrentUserPermitted(AsyncCallback callback) { + UserLogin.getInstance().checkRole(this, callback); + } + + @Override + public List getHistoryPath() { + return ListUtils.concat(Disposal.RESOLVER.getHistoryPath(), getHistoryToken()); + } + + @Override + public String getHistoryToken() { + return "overdue"; + } + }; + @UiField + FlowPanel createDisposalConfirmationDescription; + + @UiField + DisposalOverdueRecordsTabs browseTab; + + /** + * Create a new panel to create a disposal confirmation + */ + private OverdueRecords() { + initWidget(uiBinder.createAndBindUi(this)); + createDisposalConfirmationDescription.add(new HTMLWidgetWrapper("CreateDisposalConfirmationDescription.html")); + + browseTab.init(); + } + + public static OverdueRecords getInstance() { + if (instance == null) { + instance = new OverdueRecords(); + } + + return instance; + } + + private void resolve(List historyTokens, AsyncCallback callback) { + if (historyTokens.isEmpty()) { + callback.onSuccess(this); + } + } + + interface MyUiBinder extends UiBinder { + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/OverdueRecords.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/OverdueRecords.ui.xml new file mode 100644 index 0000000000..1c598af09c --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/OverdueRecords.ui.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/ShowDisposalConfirmation.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/ShowDisposalConfirmation.java index ed50e1db0f..e7d531ba61 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/ShowDisposalConfirmation.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/ShowDisposalConfirmation.java @@ -7,38 +7,33 @@ */ package org.roda.wui.client.disposal.confirmations; +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.disposal.confirmation.DisposalConfirmation; +import org.roda.wui.client.browse.tabs.DisposalConfirmationTabs; +import org.roda.wui.client.common.DisposalConfirmationActionsToolbar; +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.DisposalConfirmationReportActions; -import org.roda.wui.client.common.actions.model.ActionableObject; -import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; +import org.roda.wui.client.common.actions.Actionable; import org.roda.wui.client.common.utils.AsyncCallbackUtils; -import org.roda.wui.client.common.utils.SidebarUtils; import org.roda.wui.client.disposal.DisposalConfirmations; +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.RestUtils; import com.google.gwt.core.client.GWT; -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.i18n.client.LocaleInfo; -import com.google.gwt.safehtml.shared.SafeHtmlUtils; -import com.google.gwt.safehtml.shared.SafeUri; 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.SimplePanel; import com.google.gwt.user.client.ui.Widget; import config.i18n.client.ClientMessages; @@ -47,12 +42,24 @@ * @author Miguel Guimarães */ public class ShowDisposalConfirmation extends Composite { - private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static ShowDisposalConfirmation instance = null; public static final HistoryResolver RESOLVER = new HistoryResolver() { @Override public void resolve(List historyTokens, AsyncCallback callback) { - getInstance().resolve(historyTokens, callback); + if (historyTokens.size() == 1) { + String confirmationId = historyTokens.get(0); + Services services = new Services("Retrieve disposal hold", "get"); + services + .disposalConfirmationResource( + s -> s.findByUuid(confirmationId, LocaleInfo.getCurrentLocale().getLocaleName())) + .whenComplete((confirmation, throwable) -> { + if (throwable != null) { + AsyncCallbackUtils.defaultFailureTreatment(throwable); + } else { + ShowDisposalConfirmation panel = new ShowDisposalConfirmation(confirmation); + callback.onSuccess(panel); + } + }); + } } @Override @@ -70,68 +77,49 @@ public String getHistoryToken() { return "confirmation"; } }; - private static ShowDisposalConfirmation.MyUiBinder uiBinder = GWT.create(ShowDisposalConfirmation.MyUiBinder.class); + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + + @UiField + FocusPanel keyboardFocus; @UiField - SimplePanel actionsSidebar; + NavigationToolbar navigationToolbar; @UiField - FlowPanel contentFlowPanel; + DisposalConfirmationActionsToolbar actionsToolbar; @UiField - FlowPanel sidebarFlowPanel; + TitlePanel title; + @UiField + DisposalConfirmationTabs 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(); + } + } + }; - private ShowDisposalConfirmation() { + private ShowDisposalConfirmation(DisposalConfirmation confirmation) { initWidget(uiBinder.createAndBindUi(this)); - } - public static ShowDisposalConfirmation getInstance() { - if (instance == null) { - instance = new ShowDisposalConfirmation(); - } - return instance; - } + handlers.put(Actionable.ActionImpact.DESTROYED, () -> HistoryUtils.newHistory(DisposalConfirmations.RESOLVER)); - void resolve(List historyTokens, final AsyncCallback callback) { - if (historyTokens.size() == 1) { - String confirmationId = historyTokens.get(0); - Services services = new Services("Retrieve the disposal confirmation", "get"); - services.rodaEntityRestService(s -> s.findByUuid(confirmationId, LocaleInfo.getCurrentLocale().getLocaleName()), - DisposalConfirmation.class).whenComplete((disposalConfirmation, throwable) -> { - if (throwable != null) { - AsyncCallbackUtils.defaultFailureTreatment(throwable); - } else { - SafeUri uri = RestUtils.createDisposalConfirmationHTMLUri(disposalConfirmation.getId(), false); - RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, uri.asString()); - try { - requestBuilder.sendRequest(null, new RequestCallback() { - - @Override - public void onResponseReceived(Request request, Response response) { - if (200 == response.getStatusCode()) { - final DisposalConfirmationReportActions confirmationActions = DisposalConfirmationReportActions - .get(); - instance = new ShowDisposalConfirmation(); - SidebarUtils.toggleSidebar(contentFlowPanel, sidebarFlowPanel, confirmationActions.hasAnyRoles()); - instance.actionsSidebar.setWidget(new ActionableWidgetBuilder<>(confirmationActions) - .withBackButton().buildListWithObjects(new ActionableObject<>(disposalConfirmation))); - HTML reportHtml = new HTML(SafeHtmlUtils.fromSafeConstant(response.getText())); - instance.contentFlowPanel.add(reportHtml); - callback.onSuccess(instance); - } - } - - @Override - public void onError(Request request, Throwable throwable) { - callback.onFailure(throwable); - } - }); - } catch (RequestException e) { - callback.onFailure(e); - } - } - }); - } else { - HistoryUtils.newHistory(DisposalConfirmations.RESOLVER); - callback.onSuccess(null); - } + navigationToolbar.withObject(confirmation); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getDisposalConfirmationBreadcrumbs(confirmation)); + navigationToolbar.build(); + + actionsToolbar.setLabel(messages.showDisposalConfirmationTitle()); + actionsToolbar.setObjectAndBuild(confirmation, null, handler); + + title.setText(confirmation.getTitle()); + title.setIconClass("DisposalConfirmations"); + + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse"); + + browseTab.init(confirmation); } interface MyUiBinder extends UiBinder { diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/ShowDisposalConfirmation.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/ShowDisposalConfirmation.ui.xml index 0b222d2a3b..f9cbb0d21a 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/ShowDisposalConfirmation.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/ShowDisposalConfirmation.ui.xml @@ -1,21 +1,23 @@ - + - - + + + - - - + - - - - - - - - - - + + + + + + + +
+ +
\ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/data/panels/DisposalConfirmationDataPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/data/panels/DisposalConfirmationDataPanel.java new file mode 100644 index 0000000000..cb29f237ee --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/data/panels/DisposalConfirmationDataPanel.java @@ -0,0 +1,149 @@ +package org.roda.wui.client.disposal.confirmations.data.panels; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.event.logical.shared.HasValueChangeHandlers; +import com.google.gwt.event.logical.shared.ValueChangeHandler; +import com.google.gwt.event.shared.HandlerRegistration; +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 config.i18n.client.ClientMessages; +import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation; +import org.roda.core.data.v2.generics.MetadataValue; +import org.roda.wui.client.common.forms.GenericDataForm; +import org.roda.wui.client.common.forms.GenericDataPanel; +import org.roda.wui.client.common.utils.AsyncCallbackUtils; +import org.roda.wui.client.common.utils.FormUtilities; +import org.roda.wui.client.services.DisposalConfirmationRestService; +import org.roda.wui.client.services.Services; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * + * @author Miguel Guimarães + */ +public class DisposalConfirmationDataPanel extends Composite + implements GenericDataPanel, HasValueChangeHandlers { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + + private final GenericDataForm form; + private final FlowPanel extraFieldsPanel = new FlowPanel(); + private final Button saveButton; + private final Button cancelButton; + private Set extraMetadataValues; + + public DisposalConfirmationDataPanel() { + this.form = new GenericDataForm<>(); + + form.addTextField(messages.disposalHoldTitle(), DisposalConfirmation::getTitle, DisposalConfirmation::setTitle, + true); + + form.addCustomWidget(extraFieldsPanel); + + // 1. Initialize Buttons + saveButton = new Button(messages.saveButton()); + saveButton.addStyleName("btn btn-primary btn-play"); + + cancelButton = new Button(messages.cancelButton()); + cancelButton.addStyleName("btn btn-link"); + + // 2. Wrap buttons in a FlowPanel for spacing + FlowPanel actionsPanel = new FlowPanel(); + actionsPanel.addStyleName("alignButtonsPanel"); // Uses your existing CSS spacing + actionsPanel.add(saveButton); + actionsPanel.add(cancelButton); + + // 3. Inject the buttons at the bottom of the generic form + form.addCustomWidget(actionsPanel); + + // Initialize the composite using the generic form as the root widget + initWidget(form); + } + + /** + * Defines what happens when the Save button is clicked. It automatically + * validates the form before executing the runnable. + */ + public void setSaveHandler(Runnable onSave) { + saveButton.addClickHandler(event -> { + if (isValid()) { + onSave.run(); + } + }); + } + + /** + * Defines what happens when the Cancel button is clicked. + */ + public void setCancelHandler(Runnable onCancel) { + cancelButton.addClickHandler(event -> onCancel.run()); + } + + public void setDisposalConfirmation(DisposalConfirmation confirmation) { + + Services services = new Services("Get disposal confirmation configurable form", "get"); + services.disposalConfirmationResource(DisposalConfirmationRestService::retrieveDisposalConfirmationForm) + .whenComplete((result, throwable) -> { + if (throwable != null) { + AsyncCallbackUtils.defaultFailureTreatment(throwable); + } else { + this.extraMetadataValues = result.getValues(); + createForm(extraMetadataValues); + } + }); + form.setModel(confirmation); + } + + private void createForm(Set userExtra) { + extraFieldsPanel.clear(); + FormUtilities.create(extraFieldsPanel, userExtra, false); + } + + @Override + public DisposalConfirmation getValue() { + return form.getValue(); + } + + public Set getDisposalConfirmationExtra() { + return extraMetadataValues; + } + + @Override + public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) { + return form.addValueChangeHandler(handler); + } + + @Override + public boolean isValid() { + List errorList = new ArrayList<>(); + + // 1. Validate Generic Form + if (!form.isValid()) { + errorList.add(form.getErrors().getHTML()); + } + + // 3. Validate Extra Fields + List extraErrors = FormUtilities.validate(extraMetadataValues, extraFieldsPanel); + + // 4. Render Errors + if (!errorList.isEmpty()) { + form.getErrors().setVisible(true); + StringBuilder errorString = new StringBuilder(); + for (String error : errorList) { + errorString.append(error); + } + + for (String extraError : extraErrors) { + errorString.append("").append(extraError).append("
"); + } + form.getErrors().setHTML(errorString.toString()); + } else { + form.getErrors().setVisible(false); + } + + return errorList.isEmpty(); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/tabs/DisposalConfirmationDetailsPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/tabs/DisposalConfirmationDetailsPanel.java new file mode 100644 index 0000000000..891887f166 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/tabs/DisposalConfirmationDetailsPanel.java @@ -0,0 +1,87 @@ +package org.roda.wui.client.disposal.confirmations.tabs; + +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.SafeHtmlUtils; +import com.google.gwt.safehtml.shared.SafeUri; +import com.google.gwt.user.client.ui.HTML; +import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation; +import org.roda.wui.client.common.ActionsToolbar; + +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 config.i18n.client.ClientMessages; +import org.roda.wui.client.common.actions.DisposalConfirmationToolbarActions; +import org.roda.wui.client.common.actions.model.ActionableObject; +import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder; +import org.roda.wui.common.client.tools.RestUtils; + +import java.util.List; + +/** + * @author Miguel Guimarães + */ + +public class DisposalConfirmationDetailsPanel 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; + + public DisposalConfirmationDetailsPanel(DisposalConfirmation disposalConfirmation) { + initWidget(uiBinder.createAndBindUi(this)); + + actionsToolbar + .setActionableMenu(new ActionableWidgetBuilder(DisposalConfirmationToolbarActions.get()) + .buildGroupedListWithObjects(new ActionableObject<>(disposalConfirmation), + List.of(DisposalConfirmationToolbarActions.DisposalConfirmationReportAction.PRINT), + List.of(DisposalConfirmationToolbarActions.DisposalConfirmationReportAction.PRINT)), + true); + + actionsToolbar.setLabelVisible(false); + actionsToolbar.setTagsVisible(false); + + showDisposalConfirmationDetails(disposalConfirmation); + } + + private void showDisposalConfirmationDetails(DisposalConfirmation disposalConfirmation) { + SafeUri uri = RestUtils.createDisposalConfirmationHTMLUri(disposalConfirmation.getId(), false); + RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, uri.asString()); + try { + requestBuilder.sendRequest(null, new RequestCallback() { + + @Override + public void onResponseReceived(Request request, Response response) { + if (200 == response.getStatusCode()) { + HTML reportHtml = new HTML(SafeHtmlUtils.fromSafeConstant(response.getText())); + detailsPanel.add(reportHtml); + } + } + + @Override + public void onError(Request request, Throwable throwable) { + + } + + }); + } catch (RequestException e) { + throw new RuntimeException(e); + } + } + + interface MyUiBinder extends UiBinder { + Widget createAndBindUi(DisposalConfirmationDetailsPanel detailsPanel); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/tabs/DisposalConfirmationDetailsPanel.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/tabs/DisposalConfirmationDetailsPanel.ui.xml new file mode 100644 index 0000000000..6771c159e8 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/confirmations/tabs/DisposalConfirmationDetailsPanel.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/disposal/hold/CreateDisposalHold.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/CreateDisposalHold.java index 531c286a31..35fc8cdc82 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/CreateDisposalHold.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/CreateDisposalHold.java @@ -7,28 +7,31 @@ */ package org.roda.wui.client.disposal.hold; -import java.util.List; - +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.FocusPanel; +import com.google.gwt.user.client.ui.Widget; +import config.i18n.client.ClientMessages; import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.wui.client.common.NavigationToolbar; +import org.roda.wui.client.common.NoActionsToolbar; +import org.roda.wui.client.common.TitlePanel; import org.roda.wui.client.common.UserLogin; import org.roda.wui.client.common.utils.AsyncCallbackUtils; +import org.roda.wui.client.disposal.hold.data.panels.DisposalHoldDataPanel; import org.roda.wui.client.disposal.policy.DisposalPolicy; +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.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; +import java.util.List; /** * @author Tiago Fraga @@ -38,7 +41,7 @@ public class CreateDisposalHold extends Composite { @Override public void resolve(List historyTokens, final AsyncCallback callback) { - CreateDisposalHold createDisposalHold = new CreateDisposalHold(new DisposalHold()); + CreateDisposalHold createDisposalHold = new CreateDisposalHold(); callback.onSuccess(createDisposalHold); } @@ -58,49 +61,63 @@ public String getHistoryToken() { } }; private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static CreateDisposalHold.MyUiBinder uiBinder = GWT.create(CreateDisposalHold.MyUiBinder.class); + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + @UiField - Button buttonApply; + FocusPanel keyboardFocus; @UiField - Button buttonCancel; - @UiField(provided = true) - DisposalHoldDataPanel disposalHoldDataPanel; - private DisposalHold disposalHold; - - public CreateDisposalHold(DisposalHold disposalHold) { - this.disposalHold = disposalHold; - this.disposalHoldDataPanel = new DisposalHoldDataPanel(disposalHold, false); + NavigationToolbar navigationToolbar; + @UiField + NoActionsToolbar actionsToolbar; + @UiField + TitlePanel title; + @UiField + FlowPanel disposalHoldDataPanel; + public CreateDisposalHold() { initWidget(uiBinder.createAndBindUi(this)); + + // 1. Create the panel and keep a reference + DisposalHoldDataPanel dataPanel = getDisposalHoldDataPanel(); + dataPanel.setDisposalHold(new DisposalHold()); + disposalHoldDataPanel.add(dataPanel); + + navigationToolbar.withoutButtons().build(); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getCreateDisposalHoldBreadcrumbs()); + + actionsToolbar.setLabel(messages.showDisposalHoldTitle()); + + // 3. Pass the shared object + actionsToolbar.build(); + + title.setText(messages.newDisposalHoldTitle()); + title.setIconClass("DisposalHold"); + title.addStyleName("mb-20"); + + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse"); } - @UiHandler("buttonApply") - void buttonApplyHandler(ClickEvent e) { - if (disposalHoldDataPanel.isValid()) { - disposalHold = disposalHoldDataPanel.getDisposalHold(); - Services services = new Services("Create disposal hold", "create"); - services.disposalHoldResource(s -> s.createDisposalHold(disposalHold)) - .whenComplete((disposalHold1, throwable) -> { - if (throwable != null) { - AsyncCallbackUtils.defaultFailureTreatment(throwable); + private static DisposalHoldDataPanel getDisposalHoldDataPanel() { + DisposalHoldDataPanel dataPanel = new DisposalHoldDataPanel(); + + dataPanel.setSaveHandler(() -> { + Services services = new Services("Create Disposal Hold", "post"); + services.disposalHoldResource(s -> s.createDisposalHold(dataPanel.getValue())) + .whenComplete((createdHold, error) -> { + if (error != null) { + AsyncCallbackUtils.defaultFailureTreatment(error); } else { - HistoryUtils.newHistory(DisposalPolicy.RESOLVER); + Toast.showInfo(messages.showDisposalHoldTitle(), messages.disposalHoldSuccessfullyCreated()); + HistoryUtils.newHistory(ShowDisposalHold.RESOLVER, createdHold.getId()); } }); - } + }); - } - - @UiHandler("buttonCancel") - void buttonCancelHandler(ClickEvent e) { - cancel(); - } - - private void cancel() { - HistoryUtils.newHistory(DisposalPolicy.RESOLVER); + dataPanel.setCancelHandler(() -> HistoryUtils.newHistory(DisposalPolicy.RESOLVER)); + return dataPanel; } interface MyUiBinder extends UiBinder { } - } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/CreateDisposalHold.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/CreateDisposalHold.ui.xml index fb57bb8905..d294c24e85 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/CreateDisposalHold.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/CreateDisposalHold.ui.xml @@ -1,32 +1,21 @@ - + - + + + - - - - - - + - - - - - - - - - - - - - + + + + - + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/DisposalHoldDataPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/DisposalHoldDataPanel.java index 65d134fc42..e69de29bb2 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/DisposalHoldDataPanel.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/DisposalHoldDataPanel.java @@ -1,181 +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.disposal.hold; - -import java.util.ArrayList; -import java.util.List; - -import com.google.gwt.event.dom.client.ChangeEvent; -import com.google.gwt.event.dom.client.KeyUpEvent; -import org.roda.core.data.v2.disposal.hold.DisposalHold; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.event.dom.client.ChangeHandler; -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.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.TextArea; -import com.google.gwt.user.client.ui.TextBox; -import com.google.gwt.user.client.ui.Widget; - -import config.i18n.client.ClientMessages; -import org.roda.wui.client.disposal.schedule.DisposalScheduleDataPanel; - -/** - * @author Tiago Fraga - */ -public class DisposalHoldDataPanel extends Composite implements HasValueChangeHandlers { - - private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static DisposalHoldDataPanel.MyUiBinder uiBinder = GWT.create(DisposalHoldDataPanel.MyUiBinder.class); - @UiField - TextBox title; - @UiField - Label titleError; - @UiField - TextBox description; - @UiField - TextBox mandate; - @UiField - TextArea notes; - @UiField - HTML errors; - private boolean editMode; - - private boolean changed = false; - private boolean checked = false; - - public DisposalHoldDataPanel(DisposalHold disposalHold, boolean editMode) { - initWidget(uiBinder.createAndBindUi(this)); - - this.editMode = editMode; - errors.setVisible(false); - - ChangeHandler changeHandler = new ChangeHandler() { - @Override - public void onChange(ChangeEvent event) { - DisposalHoldDataPanel.this.onChange(); - } - }; - - KeyUpHandler keyUpHandler = new KeyUpHandler() { - @Override - public void onKeyUp(KeyUpEvent keyUpEvent) { - DisposalHoldDataPanel.this.onChange(); - } - }; - - title.addChangeHandler(changeHandler); - title.addKeyUpHandler(keyUpHandler); - description.addChangeHandler(changeHandler); - description.addKeyUpHandler(keyUpHandler); - mandate.addChangeHandler(changeHandler); - mandate.addKeyUpHandler(keyUpHandler); - notes.addChangeHandler(changeHandler); - notes.addKeyUpHandler(keyUpHandler); - - if (editMode) { - setDisposalHold(disposalHold); - } - } - - public DisposalHold getDisposalHold() { - DisposalHold disposalHold = new DisposalHold(); - disposalHold.setTitle(title.getText()); - disposalHold.setDescription(description.getText()); - disposalHold.setMandate(mandate.getText()); - disposalHold.setScopeNotes(notes.getText()); - return disposalHold; - } - - public void setDisposalHold(DisposalHold disposalHold) { - this.title.setText(disposalHold.getTitle()); - this.description.setText(disposalHold.getDescription()); - this.mandate.setText(disposalHold.getMandate()); - this.notes.setText(disposalHold.getScopeNotes()); - } - - /** - * Is disposal hold data panel valid - * - * @return true if valid - */ - public boolean isValid() { - List errorList = new ArrayList<>(); - if (title.getText().length() == 0) { - title.addStyleName("isWrong"); - titleError.setText(messages.mandatoryField()); - titleError.setVisible(true); - Window.scrollTo(title.getAbsoluteLeft(), title.getAbsoluteTop()); - errorList.add(messages.isAMandatoryField(messages.disposalHoldTitle())); - } else { - title.removeStyleName("isWrong"); - titleError.setVisible(false); - } - - checked = true; - - if (!errorList.isEmpty()) { - errors.setVisible(true); - StringBuilder errorString = new StringBuilder(); - for (String error : errorList) { - errorString.append("").append(error).append(""); - errorString.append("
"); - } - errors.setHTML(errorString.toString()); - } else { - errors.setVisible(false); - } - - return errorList.isEmpty(); - } - - public void clear() { - title.setText(""); - description.setText(""); - mandate.setText(""); - notes.setText(""); - } - - public boolean isEditMode() { - return editMode; - } - - 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 DisposalHold getValue() { - return getDisposalHold(); - } - - interface MyUiBinder extends UiBinder { - } - -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/DisposalHoldDataPanel.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/DisposalHoldDataPanel.ui.xml deleted file mode 100644 index da5b411140..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/DisposalHoldDataPanel.ui.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - .section { - margin-top: 20px; - } - - - - - - - - - - - * - - - - - - - - - - - - - - - - - - - - - - - diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/EditDisposalHold.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/EditDisposalHold.java index 4fe94e1467..688b91bee1 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/EditDisposalHold.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/EditDisposalHold.java @@ -7,30 +7,33 @@ */ package org.roda.wui.client.disposal.hold; -import java.util.List; - +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.FocusPanel; +import com.google.gwt.user.client.ui.Widget; +import config.i18n.client.ClientMessages; import org.roda.core.data.v2.disposal.hold.DisposalHold; import org.roda.core.data.v2.disposal.hold.DisposalHoldState; import org.roda.core.data.v2.ip.disposalhold.UpdateDisposalHoldRequest; +import org.roda.wui.client.common.NavigationToolbar; +import org.roda.wui.client.common.NoActionsToolbar; +import org.roda.wui.client.common.TitlePanel; import org.roda.wui.client.common.UserLogin; import org.roda.wui.client.common.utils.AsyncCallbackUtils; +import org.roda.wui.client.disposal.hold.data.panels.DisposalHoldDataPanel; import org.roda.wui.client.disposal.policy.DisposalPolicy; +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.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; +import java.util.List; /** * @author Tiago Fraga @@ -49,7 +52,7 @@ public void resolve(List historyTokens, final AsyncCallback call AsyncCallbackUtils.defaultFailureTreatment(throwable); } else { if (DisposalHoldState.LIFTED.equals(hold.getState())) { - HistoryUtils.newHistory(DisposalPolicy.RESOLVER); + HistoryUtils.newHistory(ShowDisposalHold.RESOLVER, hold.getId()); } else { EditDisposalHold panel = new EditDisposalHold(hold); callback.onSuccess(panel); @@ -75,62 +78,64 @@ public String getHistoryToken() { } }; private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static EditDisposalHold instance = null; + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - private static EditDisposalHold.MyUiBinder uiBinder = GWT.create(EditDisposalHold.MyUiBinder.class); @UiField - Button buttonApply; + FocusPanel keyboardFocus; + @UiField + NavigationToolbar navigationToolbar; + @UiField + NoActionsToolbar actionsToolbar; @UiField - Button buttonCancel; - @UiField(provided = true) - DisposalHoldDataPanel disposalHoldDataPanel; - private DisposalHold disposalHold; + TitlePanel title; + @UiField + FlowPanel disposalHoldDataPanel; - public EditDisposalHold() { + public EditDisposalHold(DisposalHold disposalHold) { initWidget(uiBinder.createAndBindUi(this)); - } - public EditDisposalHold(DisposalHold disposalHold) { - this.disposalHold = disposalHold; - this.disposalHoldDataPanel = new DisposalHoldDataPanel(disposalHold, true); + // 1. Create the panel and keep a reference + DisposalHoldDataPanel dataPanel = getDisposalHoldDataPanel(disposalHold); + disposalHoldDataPanel.add(dataPanel); - initWidget(uiBinder.createAndBindUi(this)); - } + navigationToolbar.withoutButtons().build(); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getEditDisposalHoldBreadcrumbs(disposalHold)); - public static EditDisposalHold getInstance() { - if (instance == null) { - instance = new EditDisposalHold(); - } - return instance; + actionsToolbar.setLabel(messages.showDisposalHoldTitle()); + + // 3. Pass the shared object + actionsToolbar.build(); + + title.setText(disposalHold.getTitle()); + title.setIconClass("DisposalHold"); + title.addStyleName("mb-20"); + + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse"); } - @UiHandler("buttonApply") - void buttonApplyHandler(ClickEvent e) { - if (disposalHoldDataPanel.isChanged() && disposalHoldDataPanel.isValid()) { + private static DisposalHoldDataPanel getDisposalHoldDataPanel(DisposalHold disposalHold) { + DisposalHoldDataPanel dataPanel = new DisposalHoldDataPanel(); + dataPanel.setDisposalHold(disposalHold); - DisposalHold disposalHoldUpdated = disposalHoldDataPanel.getDisposalHold(); - disposalHold.setTitle(disposalHoldUpdated.getTitle()); - disposalHold.setMandate(disposalHoldUpdated.getMandate()); - disposalHold.setDescription(disposalHoldUpdated.getDescription()); - disposalHold.setScopeNotes(disposalHoldUpdated.getScopeNotes()); + dataPanel.setSaveHandler(() -> { + // This will trigger the validation and, if valid, execute the save logic + Services services = new Services("Update Disposal Hold", "update"); UpdateDisposalHoldRequest request = new UpdateDisposalHoldRequest(); - request.setDisposalHold(disposalHold); - Services services = new Services("Update disposal hold", "update"); - services.disposalHoldResource(s -> s.updateDisposalHold(request)).whenComplete((hold, throwable) -> { - if (throwable != null) { - AsyncCallbackUtils.defaultFailureTreatment(throwable); + request.setDisposalHold(dataPanel.getValue()); + services.disposalHoldResource(s -> s.updateDisposalHold(request)).whenComplete((result, error) -> { + if (error != null) { + AsyncCallbackUtils.defaultFailureTreatment(error); } else { - HistoryUtils.newHistory(ShowDisposalHold.RESOLVER, hold.getId()); + Toast.showInfo(messages.showDisposalHoldTitle(), messages.disposalHoldSuccessfullyUpdated()); + HistoryUtils.newHistory(ShowDisposalHold.RESOLVER, result.getId()); } }); - } else { - HistoryUtils.newHistory(ShowDisposalHold.RESOLVER, disposalHold.getId()); - } - } + }); - @UiHandler("buttonCancel") - void buttonCancelHandler(ClickEvent e) { - HistoryUtils.newHistory(ShowDisposalHold.RESOLVER, disposalHold.getId()); + // Bind the Cancel Action logic + dataPanel.setCancelHandler(() -> HistoryUtils.newHistory(ShowDisposalHold.RESOLVER, disposalHold.getId())); + return dataPanel; } interface MyUiBinder extends UiBinder { diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/EditDisposalHold.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/EditDisposalHold.ui.xml index b57ff3642d..174e319fa4 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/EditDisposalHold.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/EditDisposalHold.ui.xml @@ -1,32 +1,21 @@ - + - + + + - - - - - - + - - - - - - - - - - - - - + + + + - + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/ShowDisposalHold.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/ShowDisposalHold.java index 8da13d3901..53137e4c76 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/ShowDisposalHold.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/ShowDisposalHold.java @@ -7,57 +7,32 @@ */ package org.roda.wui.client.disposal.hold; +import java.util.HashMap; import java.util.List; +import java.util.Map; -import org.roda.core.data.common.RodaConstants; -import org.roda.core.data.exceptions.DisposalHoldAlreadyExistsException; +import com.google.gwt.user.client.ui.FocusPanel; import org.roda.core.data.v2.disposal.hold.DisposalHold; -import org.roda.core.data.v2.disposal.hold.DisposalHoldState; -import org.roda.core.data.v2.generics.select.SelectedItemsFilterRequest; -import org.roda.core.data.v2.generics.select.SelectedItemsRequest; -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.SimpleFilterParameter; -import org.roda.core.data.v2.ip.IndexedAIP; -import org.roda.core.data.v2.ip.disposalhold.DisassociateDisposalHoldRequest; -import org.roda.core.data.v2.ip.disposalhold.UpdateDisposalHoldRequest; +import org.roda.wui.client.browse.tabs.DisposalHoldTabs; +import org.roda.wui.client.common.DisposalHoldActionsToolbar; +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.DisposalHoldActions; -import org.roda.wui.client.common.dialogs.Dialogs; -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 org.roda.wui.client.common.utils.AsyncCallbackUtils; -import org.roda.wui.client.common.utils.HtmlSnippetUtils; -import org.roda.wui.client.common.utils.PermissionClientUtils; import org.roda.wui.client.disposal.policy.DisposalPolicy; -import org.roda.wui.client.ingest.process.ShowJob; -import org.roda.wui.client.process.InternalProcess; +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 org.roda.wui.common.client.widgets.Toast; import com.google.gwt.core.client.GWT; -import com.google.gwt.regexp.shared.RegExp; -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.Timer; 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.HTML; -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; @@ -66,13 +41,22 @@ * @author Tiago Fraga */ public class ShowDisposalHold extends Composite { - private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static ShowDisposalHold instance = null; public static final HistoryResolver RESOLVER = new HistoryResolver() { @Override public void resolve(List historyTokens, final AsyncCallback callback) { - getInstance().resolve(historyTokens, callback); + if (historyTokens.size() == 1) { + Services services = new Services("Retrieve disposal hold", "get"); + services.disposalHoldResource(s -> s.retrieveDisposalHold(historyTokens.get(0))) + .whenComplete((hold, throwable) -> { + if (throwable != null) { + AsyncCallbackUtils.defaultFailureTreatment(throwable); + } else { + ShowDisposalHold panel = new ShowDisposalHold(hold); + callback.onSuccess(panel); + } + }); + } } @Override @@ -90,232 +74,80 @@ public String getHistoryToken() { return "disposal_hold"; } }; - private static ShowDisposalHold.MyUiBinder uiBinder = GWT.create(ShowDisposalHold.MyUiBinder.class); - @UiField - Label disposalHoldId; - @UiField - Label dateCreated, dateUpdated; - @UiField - TitlePanel title; - @UiField - Label disposalHoldMandateKey; - @UiField - HTML disposalHoldMandateValue; - @UiField - Label disposalHoldDescriptionKey; - @UiField - HTML disposalHoldDescriptionValue; - @UiField - Label disposalHoldNotesKey; - @UiField - HTML disposalHoldNotesValue; + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + @UiField - Label disposalHoldStateKey; + FocusPanel keyboardFocus; @UiField - HTML disposalHoldStateValue; + NavigationToolbar navigationToolbar; @UiField - FlowPanel buttonsPanel; + DisposalHoldActionsToolbar actionsToolbar; @UiField - FlowPanel aipListTitle; + TitlePanel title; @UiField - SimplePanel aipsListCard; - private DisposalHold disposalHold; - private SearchWrapper aipsSearchWrapper; - - public ShowDisposalHold() { - this.disposalHold = new DisposalHold(); - } + DisposalHoldTabs browseTab; - public ShowDisposalHold(final DisposalHold disposalHold) { - instance = this; - this.disposalHold = disposalHold; - initAipsList(); - - initWidget(uiBinder.createAndBindUi(this)); - initElements(); - initButtons(); - } - - public static ShowDisposalHold getInstance() { - if (instance == null) { - instance = new ShowDisposalHold(); - } - return instance; - } - - private void initAipsList() { - DisposalHoldActions disposalHoldActions = new DisposalHoldActions(disposalHold); - - AsyncCallback listActionableCallback = new NoAsyncCallback() { - @Override - public void onSuccess(Actionable.ActionImpact impact) { - if (!Actionable.ActionImpact.NONE.equals(impact)) { - refresh(); - } + 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(); } - }; - - ListBuilder aipsListBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), - new AsyncTableCellOptions<>(IndexedAIP.class, "ShowDisposalHold_aips") - .withFilter(new Filter(new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_HOLDS_ID, disposalHold.getId()))) - .withSummary(messages.listOfAIPs()).bindOpener().withActionable(disposalHoldActions) - .withActionableCallback(listActionableCallback)); - - aipsSearchWrapper = new SearchWrapper(false).createListAndSearchPanel(aipsListBuilder); - } - - public void initElements() { - title.setText(disposalHold.getTitle()); - - disposalHoldId.setText(messages.disposalHoldIdentifier() + ": " + disposalHold.getId()); - - if (disposalHold.getCreatedOn() != null && StringUtils.isNotBlank(disposalHold.getCreatedBy())) { - dateCreated.setText( - messages.dateCreated(Humanize.formatDateTime(disposalHold.getCreatedOn()), disposalHold.getCreatedBy())); - } - - if (disposalHold.getUpdatedOn() != null && StringUtils.isNotBlank(disposalHold.getUpdatedBy())) { - dateUpdated.setText( - messages.dateUpdated(Humanize.formatDateTime(disposalHold.getUpdatedOn()), disposalHold.getUpdatedBy())); } + }; + private DisposalHold disposalHold; - disposalHoldDescriptionValue.setHTML(SafeHtmlUtils.fromString(disposalHold.getDescription())); - disposalHoldDescriptionKey.setVisible(StringUtils.isNotBlank(disposalHold.getDescription())); - - disposalHoldMandateValue.setHTML(SafeHtmlUtils.fromString(disposalHold.getMandate())); - disposalHoldMandateKey.setVisible(StringUtils.isNotBlank(disposalHold.getMandate())); - - disposalHoldNotesValue.setHTML(SafeHtmlUtils.fromString(disposalHold.getScopeNotes())); - disposalHoldNotesKey.setVisible(StringUtils.isNotBlank(disposalHold.getScopeNotes())); - - disposalHoldStateValue.setHTML(HtmlSnippetUtils.getDisposalHoldStateHtml(disposalHold)); - - // Records with this hold - - if (disposalHold.getState().equals(DisposalHoldState.ACTIVE) - && PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_FIND_AIP)) { - Label aipTitle = new Label(); - aipTitle.addStyleName("h5"); - aipTitle.setText(messages.disposalHoldListAips()); - aipListTitle.clear(); - aipListTitle.add(aipTitle); - - aipsListCard.setWidget(aipsSearchWrapper); - aipsListCard.setVisible(true); - } else { - aipListTitle.setVisible(false); - aipsListCard.setVisible(false); - } - - } - - public void initButtons() { - buttonsPanel.clear(); - - if (disposalHold.getState().equals(DisposalHoldState.ACTIVE) - && PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_UPDATE_DISPOSAL_HOLD)) { - Button editHoldBtn = new Button(); - editHoldBtn.addStyleName("btn btn-block btn-edit"); - editHoldBtn.setText(messages.editButton()); - editHoldBtn - .addClickHandler(clickEvent -> HistoryUtils.newHistory(EditDisposalHold.RESOLVER, disposalHold.getId())); - - buttonsPanel.add(editHoldBtn); - - Button liftHoldBtn = new Button(); - liftHoldBtn.addStyleName("btn btn-block btn-danger btn-lift-hold"); - liftHoldBtn.setText(messages.liftButton()); + public ShowDisposalHold(final DisposalHold hold) { + this.disposalHold = hold; - liftHoldBtn.addClickHandler(clickEvent -> { - Dialogs.showConfirmDialog(messages.liftDisposalHoldDialogTitle(), messages.liftDisposalHoldDialogMessage(1), - messages.cancelButton(), messages.confirmButton(), new AsyncCallback() { - @Override - public void onFailure(Throwable throwable) { - // do nothing - } + initWidget(uiBinder.createAndBindUi(this)); - @Override - public void onSuccess(Boolean confirm) { - if (confirm) { - Dialogs.showPromptDialog(messages.outcomeDetailTitle(), null, null, messages.outcomeDetailPlaceholder(), - RegExp.compile(".*"), messages.cancelButton(), messages.confirmButton(), false, true, - new AsyncCallback() { - @Override - public void onFailure(Throwable throwable) { - // do nothing - } + initHandlers(hold); - @Override - public void onSuccess(String details) { - Services services = new Services("Lift disposal hold", "job"); + navigationToolbar.withObject(disposalHold); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getDisposalHoldBreadcrumbs(hold)); + navigationToolbar.build(); - services.disposalHoldResource(s -> s.liftDisposalHold(disposalHold.getId(), details)) - .whenComplete((job, throwable) -> { - if (throwable != null) { - AsyncCallbackUtils.defaultFailureTreatment(throwable); - } else { - Toast.showInfo(messages.runningInBackgroundTitle(), messages.updateDisposalHoldMessage()); - Timer timer = new Timer() { - @Override - public void run() { - refresh(); - } - }; + actionsToolbar.setLabel(messages.showDisposalHoldTitle()); + actionsToolbar.setObjectAndBuild(hold, null, handler); - timer.schedule(RodaConstants.ACTION_TIMEOUT); - } - }); - } - }); - } - } - }); - }); - buttonsPanel.add(liftHoldBtn); - } + title.setText(hold.getTitle()); + title.setIconClass("DisposalHold"); - Button backBtn = new Button(); - backBtn.setText(messages.backButton()); - backBtn.addStyleName("btn btn-block btn-default btn-times-circle"); - backBtn.addClickHandler(clickEvent -> HistoryUtils.newHistory(DisposalPolicy.RESOLVER)); - buttonsPanel.add(backBtn); - } + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse"); - private void errorMessage(Throwable caught) { - if (caught instanceof DisposalHoldAlreadyExistsException) { - Toast.showError(messages.createDisposalHoldAlreadyExists(disposalHold.getTitle())); - } else { - Toast.showError(messages.createDisposalHoldFailure(caught.getMessage())); - } + browseTab.init(hold, handler); } - private void refresh() { - Services services = new Services("Retrieve disposal hold", "get"); - services.disposalHoldResource(s -> s.retrieveDisposalHold(disposalHold.getId())).whenComplete((hold, throwable) -> { - if (throwable != null) { - AsyncCallbackUtils.defaultFailureTreatment(throwable); - } else { - disposalHold = hold; - initElements(); - initAipsList(); - initButtons(); - } - }); - } + private void initHandlers(DisposalHold hold) { + handlers.put(Actionable.ActionImpact.DESTROYED, + () -> HistoryUtils.newHistory(DisposalPolicy.RESOLVER.getHistoryPath())); - private void resolve(List historyTokens, final AsyncCallback callback) { - if (historyTokens.size() == 1) { - Services services = new Services("Retrieve disposal hold", "get"); - services.disposalHoldResource(s -> s.retrieveDisposalHold(historyTokens.get(0))) - .whenComplete((hold, throwable) -> { + // Use the DOM Swap refresh method instead of HistoryUtils + handlers.put(Actionable.ActionImpact.UPDATED, () -> { + Services services = new Services("Retrieve updated disposal hold", "get"); + services.disposalHoldResource(s -> s.retrieveDisposalHold(hold.getId())) + .whenComplete((updatedHold, throwable) -> { if (throwable != null) { AsyncCallbackUtils.defaultFailureTreatment(throwable); } else { - ShowDisposalHold panel = new ShowDisposalHold(hold); - callback.onSuccess(panel); + // 1. Update local reference + this.disposalHold = updatedHold; + + // 2. Update the main TitlePanel + title.setText(updatedHold.getTitle()); + + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getDisposalHoldBreadcrumbs(updatedHold)); + + // 4. Re-initialize tabs (this recreates the details panel with the new + // schedule) + browseTab.init(updatedHold, handler); } }); - } + }); } interface MyUiBinder extends UiBinder { diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/ShowDisposalHold.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/ShowDisposalHold.ui.xml index 9bd1b5dbc9..0d3ebd5911 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/ShowDisposalHold.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/ShowDisposalHold.ui.xml @@ -1,69 +1,23 @@ - + - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - + + \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/data/panels/DisposalHoldDataPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/data/panels/DisposalHoldDataPanel.java new file mode 100644 index 0000000000..c38c862067 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/data/panels/DisposalHoldDataPanel.java @@ -0,0 +1,96 @@ +package org.roda.wui.client.disposal.hold.data.panels; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.event.logical.shared.HasValueChangeHandlers; +import com.google.gwt.event.logical.shared.ValueChangeHandler; +import com.google.gwt.event.shared.HandlerRegistration; +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 config.i18n.client.ClientMessages; +import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.wui.client.common.forms.GenericDataForm; +import org.roda.wui.client.common.forms.GenericDataPanel; + +/** + * + * @author Miguel Guimarães + */ +public class DisposalHoldDataPanel extends Composite + implements GenericDataPanel, HasValueChangeHandlers { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + + private final GenericDataForm disposalHoldForm; + + private final Button saveButton; + private final Button cancelButton; + + public DisposalHoldDataPanel() { + this.disposalHoldForm = new GenericDataForm<>(); + + disposalHoldForm.addTextField(messages.disposalHoldTitle(), DisposalHold::getTitle, DisposalHold::setTitle, true); + disposalHoldForm.addTextField(messages.disposalHoldDescription(), DisposalHold::getDescription, + DisposalHold::setDescription, false); + disposalHoldForm.addTextField(messages.disposalHoldMandate(), DisposalHold::getMandate, DisposalHold::setMandate, + false); + disposalHoldForm.addTextArea(messages.disposalHoldNotes(), DisposalHold::getScopeNotes, + DisposalHold::setScopeNotes, false); + + // 1. Initialize Buttons + saveButton = new Button(messages.saveButton()); + saveButton.addStyleName("btn btn-primary btn-play"); + + cancelButton = new Button(messages.cancelButton()); + cancelButton.addStyleName("btn btn-link"); + + // 2. Wrap buttons in a FlowPanel for spacing + FlowPanel actionsPanel = new FlowPanel(); + actionsPanel.addStyleName("alignButtonsPanel"); // Uses your existing CSS spacing + actionsPanel.add(saveButton); + actionsPanel.add(cancelButton); + + // 3. Inject the buttons at the bottom of the generic form + disposalHoldForm.addCustomWidget(actionsPanel); + + // Initialize the composite using the generic form as the root widget + initWidget(disposalHoldForm); + } + + /** + * Defines what happens when the Save button is clicked. It automatically + * validates the form before executing the runnable. + */ + public void setSaveHandler(Runnable onSave) { + saveButton.addClickHandler(event -> { + if (isValid()) { + onSave.run(); + } + }); + } + + /** + * Defines what happens when the Cancel button is clicked. + */ + public void setCancelHandler(Runnable onCancel) { + cancelButton.addClickHandler(event -> onCancel.run()); + } + + public void setDisposalHold(DisposalHold hold) { + disposalHoldForm.setModel(hold); + } + + @Override + public DisposalHold getValue() { + return disposalHoldForm.getValue(); + } + + @Override + public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) { + return disposalHoldForm.addValueChangeHandler(handler); + } + + @Override + public boolean isValid() { + return disposalHoldForm.isValid(); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/tabs/DisposalHoldDetailsPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/tabs/DisposalHoldDetailsPanel.java new file mode 100644 index 0000000000..4fc0f4c12e --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/tabs/DisposalHoldDetailsPanel.java @@ -0,0 +1,117 @@ +package org.roda.wui.client.disposal.hold.tabs; + +import java.util.List; + +import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.wui.client.common.ActionsToolbar; +import org.roda.wui.client.common.actions.Actionable; +import org.roda.wui.client.common.actions.DisposalHoldAction; +import org.roda.wui.client.common.actions.DisposalHoldToolbarActions; +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 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.wui.common.client.tools.Humanize; + +/** + * @author Miguel Guimarães + */ + +public class DisposalHoldDetailsPanel 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 DisposalHold hold; + private AsyncCallback localCallback; + + public DisposalHoldDetailsPanel(DisposalHold disposalHold, AsyncCallback actionCallback) { + initWidget(uiBinder.createAndBindUi(this)); + + // Promote localCallback to an instance variable so refresh() can use it + this.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); + + // Initial load + refresh(disposalHold); + } + + // Update the method signature to accept the new hold + public void refresh(DisposalHold newHold) { + this.hold = newHold; + + // 1. Clear out the old details + clear(); + + // 2. Re-populate text fields with the new data + init(this.hold); + + // 3. Re-bind the actions toolbar with the new hold object + actionsToolbar.setActionableMenu(new ActionableWidgetBuilder(DisposalHoldToolbarActions.get()) + .withActionCallback(localCallback).buildGroupedListWithObjects(new ActionableObject<>(hold), + List.of(DisposalHoldAction.EDIT), List.of(DisposalHoldAction.EDIT)), + true); + } + + private void init(DisposalHold hold) { + FormUtilities.addIfNotBlank(detailsPanel, messages.disposalHoldTitle(), hold.getTitle()); + FormUtilities.addIfNotBlank(detailsPanel, messages.disposalHoldDescription(), hold.getDescription()); + FormUtilities.addIfNotBlank(detailsPanel, messages.disposalHoldMandate(), hold.getMandate()); + FormUtilities.addIfNotBlank(detailsPanel, messages.disposalHoldNotes(), hold.getScopeNotes()); + + 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.getDisposalHoldStateHtml(hold)); + status.add(statusValue); + status.addStyleName("field"); + topPanel.addStyleName("descriptiveMetadata"); + topPanel.add(status); + detailsPanel.add(topPanel); + } + + public void clear() { + detailsPanel.clear(); + } + + interface MyUiBinder extends UiBinder { + Widget createAndBindUi(DisposalHoldDetailsPanel detailsPanel); + } + +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/tabs/DisposalHoldDetailsPanel.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/tabs/DisposalHoldDetailsPanel.ui.xml new file mode 100644 index 0000000000..ec66c371e8 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/hold/tabs/DisposalHoldDetailsPanel.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/disposal/policy/DisposalPolicy.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/policy/DisposalPolicy.java index 4bf411962d..2c57eccea6 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/policy/DisposalPolicy.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/policy/DisposalPolicy.java @@ -7,20 +7,21 @@ */ package org.roda.wui.client.disposal.policy; -import java.util.List; - -import org.roda.core.data.common.RodaConstants; +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 org.roda.wui.client.browse.tabs.DisposalPolicyTabs; import org.roda.wui.client.common.UserLogin; -import org.roda.wui.client.common.actions.DisposalRuleActions; -import org.roda.wui.client.common.utils.PermissionClientUtils; -import org.roda.wui.client.common.utils.SidebarUtils; import org.roda.wui.client.disposal.Disposal; import org.roda.wui.client.disposal.hold.CreateDisposalHold; import org.roda.wui.client.disposal.hold.EditDisposalHold; import org.roda.wui.client.disposal.hold.ShowDisposalHold; import org.roda.wui.client.disposal.rule.CreateDisposalRule; import org.roda.wui.client.disposal.rule.EditDisposalRule; -import org.roda.wui.client.disposal.rule.OrderDisposalRules; import org.roda.wui.client.disposal.rule.ShowDisposalRule; import org.roda.wui.client.disposal.schedule.CreateDisposalSchedule; import org.roda.wui.client.disposal.schedule.EditDisposalSchedule; @@ -30,25 +31,14 @@ import org.roda.wui.common.client.tools.ListUtils; import org.roda.wui.common.client.widgets.HTMLWidgetWrapper; -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.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.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.Widget; - -import config.i18n.client.ClientMessages; +import java.util.List; /** * @author Tiago Fraga */ public class DisposalPolicy extends Composite { + private static DisposalPolicy instance = null; public static final HistoryResolver RESOLVER = new HistoryResolver() { @Override public void resolve(List historyTokens, AsyncCallback callback) { @@ -70,15 +60,17 @@ public String getHistoryToken() { return "policy"; } }; - - private static DisposalPolicy instance = null; - - interface MyUiBinder extends UiBinder { - } - private static DisposalPolicy.MyUiBinder uiBinder = GWT.create(DisposalPolicy.MyUiBinder.class); + @UiField + FlowPanel disposalPolicyDescription; + @UiField + DisposalPolicyTabs browseTab; - private static final ClientMessages messages = GWT.create(ClientMessages.class); + private DisposalPolicy() { + initWidget(uiBinder.createAndBindUi(this)); + disposalPolicyDescription.add(new HTMLWidgetWrapper("DisposalPolicyDescription.html")); + browseTab.init(); + } /** * Get the singleton instance @@ -92,181 +84,6 @@ public static DisposalPolicy getInstance() { return instance; } - @UiField - FlowPanel disposalPolicyDescription; - - @UiField - FlowPanel contentFlowPanel; - - @UiField - FlowPanel sidebarFlowPanel; - - @UiField - FlowPanel sidebarButtonsPanel; - - // Disposal Schedules - @UiField(provided = true) - DisposalPolicySchedulesPanel disposalPolicySchedulesPanel; - - // Disposal Holds - @UiField(provided = true) - DisposalPolicyHoldsPanel disposalPolicyHoldsPanel; - - // Disposal Rules - @UiField(provided = true) - DisposalPolicyRulesPanel disposalPolicyRulesPanel; - - private boolean initSidebarButtons(FlowPanel sidebar) { - boolean hasCreatedScheduleBtns = initDisposalScheduleButtons(sidebar, true, true); - boolean hasCreatedRuleBtns = initDisposalRuleButtons(sidebar, true, true); - boolean hasCreatedHoldBtns = initDisposalHoldButtons(sidebar, true, true); - - return hasCreatedRuleBtns || hasCreatedScheduleBtns || hasCreatedHoldBtns; - } - - private boolean initDisposalScheduleButtons(FlowPanel panel, boolean isGroup, boolean isSidebar) { - if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_CREATE_DISPOSAL_SCHEDULE)) { - if (isSidebar) { - Label label = new Label(); - label.setText(messages.disposalSchedulesTitle()); - panel.add(label); - } - Button newDisposalScheduleBtn = new Button(); - newDisposalScheduleBtn.addStyleName("btn btn-plus"); - if (isGroup) { - newDisposalScheduleBtn.addStyleName("btn-block"); - } - newDisposalScheduleBtn.setText(messages.newDisposalScheduleTitle()); - newDisposalScheduleBtn.addClickHandler(new ClickHandler() { - - @Override - public void onClick(ClickEvent event) { - HistoryUtils.newHistory(CreateDisposalSchedule.RESOLVER); - } - }); - panel.add(newDisposalScheduleBtn); - return true; - } - return false; - } - - private boolean initDisposalHoldButtons(FlowPanel panel, boolean isGroup, boolean isSidebar) { - if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_CREATE_DISPOSAL_HOLD)) { - if (isSidebar) { - Label label = new Label(); - label.setText(messages.disposalHoldsTitle()); - label.addStyleName("sidebarDisposalLabels"); - panel.add(label); - } - Button newDisposalHoldBtn = new Button(); - newDisposalHoldBtn.addStyleName("btn btn-plus"); - if (isGroup) { - newDisposalHoldBtn.addStyleName("btn-block"); - } - newDisposalHoldBtn.setText(messages.newDisposalHoldTitle()); - newDisposalHoldBtn.addClickHandler(new ClickHandler() { - - @Override - public void onClick(ClickEvent event) { - HistoryUtils.newHistory(Disposal.RESOLVER, DisposalPolicy.RESOLVER.getHistoryToken(), - CreateDisposalHold.RESOLVER.getHistoryToken()); - } - }); - panel.add(newDisposalHoldBtn); - return true; - } - return false; - } - - private boolean initDisposalRuleButtons(FlowPanel panel, boolean isGroup, boolean isSidebar) { - boolean ret = false; - - if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_CREATE_DISPOSAL_RULE)) { - Button createRuleBtn = new Button(); - if (isSidebar) { - Label label = new Label(); - label.setText(messages.disposalRulesTitle()); - label.addStyleName("sidebarDisposalLabels"); - panel.add(label); - } - createRuleBtn.addStyleName("btn btn-plus"); - if (isGroup) { - createRuleBtn.addStyleName("btn-block"); - } - createRuleBtn.setText(messages.newDisposalRuleTitle()); - createRuleBtn.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - HistoryUtils.newHistory(DisposalPolicy.RESOLVER, CreateDisposalRule.RESOLVER.getHistoryToken()); - } - }); - panel.add(createRuleBtn); - ret = true; - } - - if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_UPDATE_DISPOSAL_RULE)) { - if (isSidebar && !ret) { - Label label = new Label(); - label.setText(messages.disposalRulesTitle()); - label.addStyleName("sidebarDisposalLabels"); - panel.add(label); - } - Button editRulesOrderBtn = new Button(); - editRulesOrderBtn.addStyleName("btn btn-edit"); - if (isGroup) { - editRulesOrderBtn.addStyleName("btn-block"); - } - editRulesOrderBtn.setText(messages.editRules()); - editRulesOrderBtn.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - HistoryUtils.newHistory(DisposalPolicy.RESOLVER, OrderDisposalRules.RESOLVER.getHistoryToken()); - } - }); - panel.add(editRulesOrderBtn); - ret = true; - } - - if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_ASSOCIATE_DISPOSAL_SCHEDULE)) { - if (!ret && isSidebar) { - Label label = new Label(); - label.setText(messages.disposalRulesTitle()); - label.addStyleName("sidebarDisposalLabels"); - panel.add(label); - } - Button applyRules = new Button(); - applyRules.addStyleName("btn btn-play"); - if (isGroup) { - applyRules.addStyleName("btn-block"); - } - applyRules.setText(messages.applyRules()); - applyRules.addClickHandler(event -> { - DisposalRuleActions.applyDisposalRulesAction(); - }); - panel.add(applyRules); - ret = true; - } - - return ret; - } - - private void initSidebar() { - if (initSidebarButtons(sidebarButtonsPanel)) { - SidebarUtils.showSidebar(contentFlowPanel, sidebarFlowPanel); - } else { - SidebarUtils.hideSidebar(contentFlowPanel, sidebarFlowPanel); - } - } - - private DisposalPolicy() { - this.disposalPolicySchedulesPanel = new DisposalPolicySchedulesPanel(); - this.disposalPolicyHoldsPanel = new DisposalPolicyHoldsPanel(); - this.disposalPolicyRulesPanel = new DisposalPolicyRulesPanel(); - initWidget(uiBinder.createAndBindUi(this)); - initSidebar(); - disposalPolicyDescription.add(new HTMLWidgetWrapper("DisposalPolicyDescription.html")); - } - public void resolve(List historyTokens, AsyncCallback callback) { if (historyTokens.isEmpty()) { DisposalPolicy disposalPolicy = new DisposalPolicy(); @@ -289,8 +106,9 @@ public void resolve(List historyTokens, AsyncCallback callback) ShowDisposalRule.RESOLVER.resolve(HistoryUtils.tail(historyTokens), callback); } else if (historyTokens.get(0).equals(EditDisposalRule.RESOLVER.getHistoryToken())) { EditDisposalRule.RESOLVER.resolve(HistoryUtils.tail(historyTokens), callback); - } else if (historyTokens.get(0).equals(OrderDisposalRules.RESOLVER.getHistoryToken())) { - OrderDisposalRules.RESOLVER.resolve(HistoryUtils.tail(historyTokens), callback); } } + + interface MyUiBinder extends UiBinder { + } } \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/policy/DisposalPolicy.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/policy/DisposalPolicy.ui.xml index 8283f9da43..96bcff695b 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/policy/DisposalPolicy.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/policy/DisposalPolicy.ui.xml @@ -1,29 +1,19 @@ + 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 diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/ChildOfPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/ChildOfPanel.java index 041ad08ec5..ef6616d893 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/ChildOfPanel.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/ChildOfPanel.java @@ -106,9 +106,9 @@ private void setEditPanel() { private void initPluginParameterPanel() { pluginParameterPanel = new PluginParameterPanel( - PluginParameter.getBuilder(RodaConstants.PLUGIN_PARAMS_PARENT_ID, messages.selectParentTitle(), - PluginParameter.PluginParameterType.AIP_ID).withDescription("").isMandatory(false).isReadOnly(false) - .withDescription("Use the provided parent node if the SIPs does not provide one.").build()); + PluginParameter.getBuilder(RodaConstants.PLUGIN_PARAMS_PARENT_ID, messages.selectParentTitle(), + PluginParameter.PluginParameterType.AIP_ID).withDescription("").isMandatory(false).isReadOnly(false) + .withDescription("Use the provided parent node if the SIPs does not provide one.").build()); pluginParameterPanel.getLayout().removeStyleName("plugin-options-parameter"); } @@ -160,8 +160,18 @@ public Pair getValue() { private Pair getChildOfFields() { Pair childOfFields = new Pair<>(); + childOfFields.setFirst(pluginParameterPanel.getValue()); childOfFields.setSecond(pluginParameterPanel.getAipTitle()); + + if (pluginParameterPanel.getValue() == null) { + childOfFields.setFirst(aipId); + } + + if (pluginParameterPanel.getAipTitle() == null) { + childOfFields.setSecond(aipName); + } + return childOfFields; } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/CreateDisposalRule.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/CreateDisposalRule.java index af3901dc8b..4900dd0268 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/CreateDisposalRule.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/CreateDisposalRule.java @@ -1,57 +1,61 @@ -/** - * 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.disposal.rule; -import java.util.List; - +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.FocusPanel; +import com.google.gwt.user.client.ui.Widget; +import config.i18n.client.ClientMessages; import org.roda.core.data.v2.disposal.rule.DisposalRule; -import org.roda.core.data.v2.disposal.rule.DisposalRules; import org.roda.core.data.v2.disposal.schedule.DisposalSchedules; +import org.roda.core.data.v2.generics.LongResponse; +import org.roda.core.data.v2.index.CountRequest; +import org.roda.core.data.v2.index.filter.AllFilterParameter; +import org.roda.core.data.v2.index.filter.Filter; +import org.roda.wui.client.common.NavigationToolbar; +import org.roda.wui.client.common.NoActionsToolbar; +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.utils.AsyncCallbackUtils; import org.roda.wui.client.disposal.policy.DisposalPolicy; -import org.roda.wui.client.services.DisposalRuleRestService; +import org.roda.wui.client.disposal.rule.data.panels.DisposalRuleDataPanel; +import org.roda.wui.client.main.BreadcrumbUtils; import org.roda.wui.client.services.DisposalScheduleRestService; 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; +import java.util.List; /** - * @author Tiago Fraga + * @author Miguel Guimarães */ -public class CreateDisposalRule extends Composite { +public class CreateDisposalRule extends Composite { public static final HistoryResolver RESOLVER = new HistoryResolver() { @Override public void resolve(List historyTokens, final AsyncCallback callback) { Services services = new Services("List disposal schedules", "get"); + CountRequest request = new CountRequest(); + Filter filter = new Filter(); + + filter.add(new AllFilterParameter()); + request.setFilter(filter); services.disposalScheduleResource(DisposalScheduleRestService::listDisposalSchedules) - .thenCompose(disposalSchedules -> services.disposalRuleResource(DisposalRuleRestService::listDisposalRules) - .whenComplete((disposalRulesResult, throwable) -> { + .thenCompose(disposalSchedules -> services.disposalRuleResource(s -> s.count(request)) + .whenComplete((disposalRulesCount, throwable) -> { if (throwable != null) { AsyncCallbackUtils.defaultFailureTreatment(throwable); } else { CreateDisposalRule createDisposalRule = new CreateDisposalRule(new DisposalRule(), disposalSchedules, - disposalRulesResult); + disposalRulesCount); callback.onSuccess(createDisposalRule); } })); @@ -72,46 +76,59 @@ public String getHistoryToken() { return "create_disposal_rule"; } }; + private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static CreateDisposalRule.MyUiBinder uiBinder = GWT.create(CreateDisposalRule.MyUiBinder.class); + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + + @UiField + FocusPanel keyboardFocus; + @UiField + NavigationToolbar navigationToolbar; @UiField - Button buttonSave; + NoActionsToolbar actionsToolbar; @UiField - Button buttonCancel; - @UiField(provided = true) - DisposalRuleDataPanel disposalRuleDataPanel; - private DisposalRule disposalRule; - private DisposalRules disposalRules; + TitlePanel title; + @UiField + FlowPanel disposalRuleDataPanel; public CreateDisposalRule(DisposalRule disposalRule, DisposalSchedules disposalSchedules, - DisposalRules disposalRules) { - this.disposalRule = disposalRule; - this.disposalRules = disposalRules; - - this.disposalRuleDataPanel = new DisposalRuleDataPanel(disposalRule, disposalSchedules, false); + LongResponse disposalRulesCount) { initWidget(uiBinder.createAndBindUi(this)); - } + disposalRule.setOrder(disposalRulesCount.getResult().intValue()); + + // 1. Create the panel and keep a reference + DisposalRuleDataPanel dataPanel = new DisposalRuleDataPanel(disposalRule, disposalSchedules, false); + disposalRuleDataPanel.add(dataPanel); - @UiHandler("buttonSave") - void buttonApplyHandler(ClickEvent e) { - if (disposalRuleDataPanel.isValid()) { - disposalRule = disposalRuleDataPanel.getDisposalRule(); - disposalRule.setOrder(disposalRules.getObjects().size()); + dataPanel.setSaveHandler(() -> { Services services = new Services("Create disposal rule", "create"); - services.disposalRuleResource(s -> s.createDisposalRule(disposalRule)).whenComplete((result, throwable) -> { + services.disposalRuleResource(s -> s.createDisposalRule(dataPanel.getValue())).whenComplete((created, throwable) -> { if (throwable != null) { AsyncCallbackUtils.defaultFailureTreatment(throwable); } else { - HistoryUtils.newHistory(DisposalPolicy.RESOLVER); + Toast.showInfo(messages.disposalRulesTitle(), messages.disposalRuleSuccessfullyCreated()); + HistoryUtils.newHistory(ShowDisposalRule.RESOLVER, created.getId()); } }); - } - } + }); + + dataPanel.setCancelHandler(() -> HistoryUtils.newHistory(DisposalPolicy.RESOLVER.getHistoryPath())); + + navigationToolbar.withoutButtons().build(); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getCreateDisposalRuleBreadcrumbs()); + + actionsToolbar.setLabel(messages.showDisposalRuleTitle()); + + // 3. Pass the shared object + actionsToolbar.build(); + + title.setText(messages.newDisposalRuleTitle()); + title.setIconClass("DisposalRule"); + title.addStyleName("mb-20"); - @UiHandler("buttonCancel") - void buttonCancelHandler(ClickEvent e) { - HistoryUtils.newHistory(DisposalPolicy.RESOLVER); + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse"); } interface MyUiBinder extends UiBinder { diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/CreateDisposalRule.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/CreateDisposalRule.ui.xml index a549e1a800..574f8195bb 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/CreateDisposalRule.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/CreateDisposalRule.ui.xml @@ -1,33 +1,21 @@ - + - + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/DisposalRuleDataPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/DisposalRuleDataPanel.java index c70b352bb1..e69de29bb2 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/DisposalRuleDataPanel.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/DisposalRuleDataPanel.java @@ -1,405 +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.disposal.rule; - -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.common.Pair; -import org.roda.core.data.v2.disposal.rule.ConditionType; -import org.roda.core.data.v2.disposal.rule.DisposalRule; -import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; -import org.roda.core.data.v2.disposal.schedule.DisposalScheduleState; -import org.roda.core.data.v2.disposal.schedule.DisposalSchedules; -import org.roda.core.data.v2.index.filter.Filter; -import org.roda.core.data.v2.index.filter.SimpleFilterParameter; -import org.roda.core.data.v2.ip.AIPState; -import org.roda.core.data.v2.ip.IndexedAIP; -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 org.roda.wui.common.client.tools.StringUtils; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.event.dom.client.ChangeHandler; -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.Window; -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.ListBox; -import com.google.gwt.user.client.ui.SimplePanel; -import com.google.gwt.user.client.ui.TextArea; -import com.google.gwt.user.client.ui.TextBox; -import com.google.gwt.user.client.ui.Widget; - -import config.i18n.client.ClientMessages; - -/** - * @author Tiago Fraga - */ -public class DisposalRuleDataPanel extends Composite implements HasValueChangeHandlers { - - private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static DisposalRuleDataPanel.MyUiBinder uiBinder = GWT.create(DisposalRuleDataPanel.MyUiBinder.class); - private final DisposalRule disposalRule; - private final DisposalSchedules disposalSchedules; - private final boolean editMode; - @UiField - TextBox title; - @UiField - Label titleError; - @UiField - TextArea description; - // disposal schedules list - @UiField - ListBox disposalSchedulesList; - @UiField - Label disposalSchedulesListError; - // condition type - @UiField - ListBox conditionTypeList; - @UiField - Label conditionTypeListError; - @UiField(provided = true) - MetadataFieldsPanel metadataFieldsPanel; - @UiField(provided = true) - ChildOfPanel childOfPanel; - @UiField - FlowPanel previewAIPListHeader; - @UiField - SimplePanel previewAIPListCard; - @UiField - HTML errors; - private int selectedScheduleIndex; - private int selectedTypeIndex; - - private boolean changed = false; - private boolean checked = false; - - private SelectionMethod selectionMethod; - - private Button btnPreview; - private Label previewHelpText; - - public DisposalRuleDataPanel(DisposalRule disposalRule, DisposalSchedules disposalSchedules, boolean editMode) { - metadataFieldsPanel = new MetadataFieldsPanel(disposalRule.getConditionKey(), disposalRule.getConditionValue(), - editMode, disposalRule); - childOfPanel = new ChildOfPanel(disposalRule.getConditionKey(), disposalRule.getConditionValue(), editMode, - disposalRule); - initWidget(uiBinder.createAndBindUi(this)); - - errors.setVisible(false); - this.editMode = editMode; - this.disposalRule = disposalRule; - this.disposalSchedules = disposalSchedules; - - metadataFieldsPanel.setVisible(false); - childOfPanel.setVisible(false); - - initPreviewAIPList(); - initDisposalSchedulesList(); - initConditionTypeList(); - - initHandlers(); - - if (editMode) { - setEditMode(); - } - } - - private void initPreviewAIPList() { - Label aipTitle = new Label(); - aipTitle.addStyleName("h5"); - aipTitle.setText(messages.disposalRulePreviewAIPListTitle()); - previewHelpText = new Label(); - previewHelpText.addStyleName("no-preview-label"); - - previewHelpText.setText(messages.disposalRulePreviewHelpText()); - btnPreview = new Button(messages.disposalRulePreviewButtonText()); - btnPreview.addStyleName("btn btn-preview"); - btnPreview.setEnabled(isEditMode()); - - btnPreview.addClickHandler(e -> { - switch (selectionMethod) { - case CHILD_OF: - String parentAIPID = childOfPanel.getValue().getFirst(); - if (StringUtils.isNotBlank(parentAIPID)) { - previewHelpText.setVisible(false); - refreshPreviewAIPList(RodaConstants.AIP_PARENT_ID, parentAIPID); - } else { - previewHelpText.setVisible(true); - previewAIPListCard.setVisible(false); - } - break; - case METADATA_FIELD: - String solrField = metadataFieldsPanel.getValue().getFirst(); - String searchCriteria = metadataFieldsPanel.getValue().getSecond(); - - if (StringUtils.isNotBlank(solrField) && StringUtils.isNotBlank(searchCriteria)) { - previewHelpText.setVisible(false); - refreshPreviewAIPList(solrField, searchCriteria); - } else { - previewHelpText.setVisible(true); - previewAIPListCard.setVisible(false); - } - break; - case NONE: - default: - previewHelpText.setVisible(true); - btnPreview.setEnabled(false); - previewAIPListCard.clear(); - } - }); - - previewAIPListHeader.add(btnPreview); - previewAIPListHeader.add(aipTitle); - previewAIPListHeader.add(previewHelpText); - - previewAIPListCard.setVisible(false); - } - - private void refreshPreviewAIPList(final String solrMetadataField, final String searchCriteria) { - ListBuilder aipsListBuilder = new ListBuilder<>(ConfigurableAsyncTableCell::new, - new AsyncTableCellOptions<>(IndexedAIP.class, "ShowDisposalSchedule_aips") - .withFilter(new Filter(new SimpleFilterParameter(solrMetadataField, searchCriteria), - new SimpleFilterParameter(RodaConstants.AIP_STATE, AIPState.ACTIVE.name()))) - .withSummary(messages.listOfAIPs()).bindOpener()); - - SearchWrapper aipsSearchWrapper = new SearchWrapper(false).createListAndSearchPanel(aipsListBuilder); - previewAIPListCard.setWidget(aipsSearchWrapper); - previewAIPListCard.setVisible(true); - } - - private void setEditMode() { - errors.setVisible(false); - this.title.setText(disposalRule.getTitle()); - this.description.setText(disposalRule.getDescription()); - this.disposalSchedulesList.setSelectedIndex(selectedScheduleIndex); - this.conditionTypeList.setSelectedIndex(selectedTypeIndex); - - if (disposalRule.getType().equals(ConditionType.METADATA_FIELD)) { - selectionMethod = SelectionMethod.METADATA_FIELD; - metadataFieldsPanel.setVisible(true); - } else { - childOfPanel.setVisible(true); - selectionMethod = SelectionMethod.CHILD_OF; - } - } - - private void initConditionTypeList() { - List conditionTypes = Arrays.asList(ConditionType.values()); - conditionTypeList.addItem("", ""); - if (!editMode) { - for (ConditionType ruleType : conditionTypes) { - conditionTypeList.addItem(messages.disposalRuleTypeValue(ruleType.toString()), ruleType.toString()); - } - } else { - int index = 1; - for (ConditionType ruleType : conditionTypes) { - conditionTypeList.addItem(messages.disposalRuleTypeValue(ruleType.toString()), ruleType.toString()); - if (ruleType.equals(disposalRule.getType())) { - selectedTypeIndex = index; - } - index++; - } - } - } - - private void initDisposalSchedulesList() { - disposalSchedulesList.addItem("", ""); - disposalSchedules.getObjects().removeIf(p -> p.getState().equals(DisposalScheduleState.INACTIVE)); - - if (!editMode) { - for (DisposalSchedule schedule : disposalSchedules.getObjects()) { - disposalSchedulesList.addItem(schedule.getTitle(), schedule.getId()); - } - } else { - int index = 1; - for (DisposalSchedule schedule : disposalSchedules.getObjects()) { - disposalSchedulesList.addItem(schedule.getTitle(), schedule.getId()); - if (schedule.getId().equals(disposalRule.getDisposalScheduleId())) { - selectedScheduleIndex = index; - } - index++; - } - } - } - - private void initHandlers() { - ChangeHandler changeHandler = event -> DisposalRuleDataPanel.this.onChange(); - - ChangeHandler typeListChangeHandler = event -> { - if (conditionTypeList.getSelectedValue().equals(ConditionType.IS_CHILD_OF.toString())) { - selectionMethod = SelectionMethod.CHILD_OF; - metadataFieldsPanel.setVisible(false); - childOfPanel.setVisible(true); - btnPreview.setEnabled(true); - } else if (conditionTypeList.getSelectedValue().equals(ConditionType.METADATA_FIELD.toString())) { - selectionMethod = SelectionMethod.METADATA_FIELD; - metadataFieldsPanel.setVisible(true); - childOfPanel.setVisible(false); - btnPreview.setEnabled(true); - } else { - selectionMethod = SelectionMethod.NONE; - metadataFieldsPanel.setVisible(false); - childOfPanel.setVisible(false); - btnPreview.setEnabled(false); - previewAIPListCard.setVisible(false); - } - }; - - ValueChangeHandler> valueChangeHandler = valueChangeEvent -> DisposalRuleDataPanel.this - .onChange(); - - title.addChangeHandler(changeHandler); - description.addChangeHandler(changeHandler); - - disposalSchedulesList.addChangeHandler(changeHandler); - - conditionTypeList.addChangeHandler(changeHandler); - conditionTypeList.addChangeHandler(typeListChangeHandler); - - metadataFieldsPanel.addValueChangeHandler(valueChangeHandler); - childOfPanel.addValueChangeHandler(valueChangeHandler); - } - - public DisposalRule getDisposalRule() { - DisposalRule disposalRule = new DisposalRule(); - disposalRule.setTitle(title.getText()); - disposalRule.setDescription(description.getText()); - disposalRule.setDisposalScheduleId(disposalSchedulesList.getSelectedValue()); - disposalRule.setDisposalScheduleName(disposalSchedulesList.getSelectedItemText()); - if (conditionTypeList.getSelectedValue().equals(ConditionType.METADATA_FIELD.name())) { - disposalRule.setType(ConditionType.METADATA_FIELD); - disposalRule.setConditionKey(metadataFieldsPanel.getValue().getFirst()); - disposalRule.setConditionValue(metadataFieldsPanel.getValue().getSecond()); - } else { - disposalRule.setType(ConditionType.IS_CHILD_OF); - if (childOfPanel.getValue() != null) { - disposalRule.setConditionKey(childOfPanel.getValue().getFirst()); - disposalRule.setConditionValue(childOfPanel.getValue().getSecond()); - } - } - return disposalRule; - } - - public boolean isValid() { - List errorList = new ArrayList<>(); - if (title.getText().length() == 0) { - title.addStyleName("isWrong"); - titleError.setText(messages.mandatoryField()); - titleError.setVisible(true); - Window.scrollTo(title.getAbsoluteLeft(), title.getAbsoluteTop()); - errorList.add(messages.isAMandatoryField(messages.disposalRuleTitle())); - } else { - title.removeStyleName("isWrong"); - titleError.setVisible(false); - } - - if (disposalSchedulesList.getSelectedValue().length() == 0) { - disposalSchedulesList.addStyleName("isWrong"); - disposalSchedulesListError.setText(messages.mandatoryField()); - disposalSchedulesListError.setVisible(true); - Window.scrollTo(disposalSchedulesList.getAbsoluteLeft(), disposalSchedulesList.getAbsoluteTop()); - errorList.add(messages.isAMandatoryField(messages.disposalRuleScheduleName())); - } else { - disposalSchedulesList.removeStyleName("isWrong"); - disposalSchedulesListError.setVisible(false); - } - - if (conditionTypeList.getSelectedValue().length() == 0) { - conditionTypeList.addStyleName("isWrong"); - conditionTypeListError.setText(messages.mandatoryField()); - conditionTypeListError.setVisible(true); - Window.scrollTo(conditionTypeList.getAbsoluteLeft(), conditionTypeList.getAbsoluteTop()); - errorList.add(messages.isAMandatoryField(messages.disposalRuleType())); - } else { - conditionTypeList.removeStyleName("isWrong"); - conditionTypeListError.setVisible(false); - - if (conditionTypeList.getSelectedValue().equals(ConditionType.METADATA_FIELD.name()) - && !metadataFieldsPanel.isValid()) { - errorList.add(messages.isAMandatoryField(messages.disposalRuleCondition())); - } - - if (conditionTypeList.getSelectedValue().equals(ConditionType.IS_CHILD_OF.name()) && !childOfPanel.isValid()) { - errorList.add(messages.isAMandatoryField(messages.disposalRuleCondition())); - } - } - - checked = true; - - if (!errorList.isEmpty()) { - errors.setVisible(true); - StringBuilder errorString = new StringBuilder(); - for (String error : errorList) { - errorString.append("").append(error).append(""); - errorString.append("
"); - } - errors.setHTML(errorString.toString()); - } else { - errors.setVisible(false); - } - - return errorList.isEmpty(); - } - - public boolean isEditMode() { - return editMode; - } - - public boolean isChanged() { - boolean conditionChanged = false; - - if (conditionTypeList.getSelectedValue().equals(ConditionType.METADATA_FIELD.name())) { - conditionChanged = metadataFieldsPanel.isChanged(); - } - - if (conditionTypeList.getSelectedValue().equals(ConditionType.IS_CHILD_OF.name())) { - conditionChanged = childOfPanel.isChanged(); - } - - return changed || conditionChanged; - } - - @Override - public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) { - return addHandler(handler, ValueChangeEvent.getType()); - } - - protected void onChange() { - changed = true; - if (checked) { - isValid(); - } - ValueChangeEvent.fire(this, getValue()); - } - - public DisposalRule getValue() { - return getDisposalRule(); - } - - private enum SelectionMethod { - CHILD_OF, METADATA_FIELD, NONE - } - - interface MyUiBinder extends UiBinder { - } - -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/DisposalRuleDataPanel.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/DisposalRuleDataPanel.ui.xml deleted file mode 100644 index eb73192ba9..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/DisposalRuleDataPanel.ui.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - .section { - margin-top: 20px; - } - - - - - - - - - - - - * - - - - - - - - - - - - * - - - - - - * - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/EditDisposalRule.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/EditDisposalRule.java index 6b7d23a218..1ea78f0f63 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/EditDisposalRule.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/EditDisposalRule.java @@ -7,33 +7,33 @@ */ package org.roda.wui.client.disposal.rule; -import java.util.List; - +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.FocusPanel; +import com.google.gwt.user.client.ui.Widget; +import config.i18n.client.ClientMessages; import org.roda.core.data.v2.disposal.rule.DisposalRule; import org.roda.core.data.v2.disposal.schedule.DisposalSchedules; -import org.roda.wui.client.common.NoAsyncCallback; +import org.roda.wui.client.common.NavigationToolbar; +import org.roda.wui.client.common.NoActionsToolbar; +import org.roda.wui.client.common.TitlePanel; import org.roda.wui.client.common.UserLogin; -import org.roda.wui.client.common.actions.DisposalRuleActions; -import org.roda.wui.client.common.dialogs.Dialogs; import org.roda.wui.client.common.utils.AsyncCallbackUtils; import org.roda.wui.client.disposal.policy.DisposalPolicy; +import org.roda.wui.client.disposal.rule.data.panels.DisposalRuleDataPanel; +import org.roda.wui.client.main.BreadcrumbUtils; import org.roda.wui.client.services.DisposalScheduleRestService; 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; +import java.util.List; /** * @author Tiago Fraga @@ -45,7 +45,6 @@ public class EditDisposalRule extends Composite { @Override public void resolve(List historyTokens, final AsyncCallback callback) { if (historyTokens.size() == 1) { - Services services = new Services("List disposal schedules", "get"); services.disposalRuleResource(s -> s.retrieveDisposalRule(historyTokens.get(0))) .thenCompose(rule -> services.disposalScheduleResource(DisposalScheduleRestService::listDisposalSchedules) @@ -75,106 +74,58 @@ public String getHistoryToken() { return "edit_disposal_rule"; } }; + private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static EditDisposalRule instance = null; + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - private static EditDisposalRule.MyUiBinder uiBinder = GWT.create(EditDisposalRule.MyUiBinder.class); @UiField - Button buttonApply; + FocusPanel keyboardFocus; @UiField - Button buttonCancel; - @UiField(provided = true) - DisposalRuleDataPanel disposalRuleDataPanel; - private DisposalRule disposalRule; - - public EditDisposalRule() { - initWidget(uiBinder.createAndBindUi(this)); - } + NavigationToolbar navigationToolbar; + @UiField + NoActionsToolbar actionsToolbar; + @UiField + TitlePanel title; + @UiField + FlowPanel disposalRuleDataPanel; public EditDisposalRule(DisposalRule disposalRule, DisposalSchedules disposalSchedules) { - this.disposalRule = disposalRule; - this.disposalRuleDataPanel = new DisposalRuleDataPanel(disposalRule, disposalSchedules, true); initWidget(uiBinder.createAndBindUi(this)); - } - - public static EditDisposalRule getInstance() { - if (instance == null) { - instance = new EditDisposalRule(); - } - return instance; - } - - @UiHandler("buttonApply") - void buttonApplyHandler(ClickEvent e) { - if (disposalRuleDataPanel.isChanged() && disposalRuleDataPanel.isValid()) { - boolean runApplyRulesPlugin = false; - DisposalRule disposalRuleUpdated = disposalRuleDataPanel.getDisposalRule(); - disposalRule.setTitle(disposalRuleUpdated.getTitle()); - disposalRule.setDescription(disposalRuleUpdated.getDescription()); - - if (!disposalRule.getDisposalScheduleId().equals(disposalRuleUpdated.getDisposalScheduleId()) - && !disposalRule.getDisposalScheduleName().equals(disposalRuleUpdated.getDisposalScheduleName())) { - runApplyRulesPlugin = true; - } - disposalRule.setDisposalScheduleId(disposalRuleUpdated.getDisposalScheduleId()); - disposalRule.setDisposalScheduleName(disposalRuleUpdated.getDisposalScheduleName()); - if (!disposalRule.getType().equals(disposalRuleUpdated.getType())) { - runApplyRulesPlugin = true; - } - disposalRule.setType(disposalRuleUpdated.getType()); - - if (disposalRuleUpdated.getConditionKey() != null) { - if (!disposalRule.getConditionKey().equals(disposalRuleUpdated.getConditionKey())) { - runApplyRulesPlugin = true; - } - disposalRule.setConditionKey(disposalRuleUpdated.getConditionKey()); - } + // 1. Create the panel and keep a reference + DisposalRuleDataPanel dataPanel = new DisposalRuleDataPanel(disposalRule, disposalSchedules, true); + disposalRuleDataPanel.add(dataPanel); - if (disposalRuleUpdated.getConditionValue() != null) { - if (!disposalRule.getConditionValue().equals(disposalRuleUpdated.getConditionValue())) { - runApplyRulesPlugin = true; - } - disposalRule.setConditionValue(disposalRuleUpdated.getConditionValue()); - } - - if (!runApplyRulesPlugin) { - Services services = new Services("Update disposal rule", "update"); - services.disposalRuleResource(s -> s.updateDisposalRule(disposalRule)).whenComplete((result, throwable) -> { + dataPanel.setSaveHandler(() -> { + Services services = new Services("Update disposal rule", "update"); + services.disposalRuleResource(s -> s.updateDisposalRule(dataPanel.getValue())) + .whenComplete((updated, throwable) -> { if (throwable != null) { - AsyncCallbackUtils.defaultFailureTreatment(throwable); + Toast.showError(throwable); } else { - HistoryUtils.newHistory(ShowDisposalRule.RESOLVER, result.getId()); + Toast.showInfo(messages.disposalRulesTitle(), messages.disposalRuleSuccessfullyUpdated()); + HistoryUtils.newHistory(ShowDisposalRule.RESOLVER, updated.getId()); } }); - } else { - Dialogs.showConfirmDialog(messages.saveButton(), messages.confirmEditRuleMessage(), messages.dialogNo(), - messages.dialogYes(), new NoAsyncCallback() { - @Override - public void onSuccess(Boolean confirm) { - if (confirm) { - Services services = new Services("Update disposal rule", "update"); - services.disposalRuleResource(s -> s.updateDisposalRule(disposalRule)) - .whenComplete((result, throwable) -> { - if (throwable != null) { - AsyncCallbackUtils.defaultFailureTreatment(throwable); - } else { - DisposalRuleActions.applyDisposalRulesAction(); - } - }); - } - } - }); - } - } else { - HistoryUtils.newHistory(ShowDisposalRule.RESOLVER, disposalRule.getId()); - } - } + }); + + dataPanel.setCancelHandler(() -> HistoryUtils.newHistory(ShowDisposalRule.RESOLVER, disposalRule.getId())); + + navigationToolbar.withoutButtons().build(); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getEditDisposalRuleBreadcrumbs(disposalRule)); + + actionsToolbar.setLabel(messages.showDisposalRuleTitle()); + + // 3. Pass the shared object + actionsToolbar.build(); + + title.setText(disposalRule.getTitle()); + title.setIconClass("DisposalRule"); + title.addStyleName("mb-20"); - @UiHandler("buttonCancel") - void buttonCancelHandler(ClickEvent e) { - HistoryUtils.newHistory(ShowDisposalRule.RESOLVER, disposalRule.getId()); + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse"); } interface MyUiBinder extends UiBinder { diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/EditDisposalRule.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/EditDisposalRule.ui.xml index 0e077c494b..a95dc5f312 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/EditDisposalRule.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/EditDisposalRule.ui.xml @@ -1,32 +1,21 @@ - + - + + + - - - - - - + - - - - - - - - - - - - - + + + + - + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/MetadataFieldsPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/MetadataFieldsPanel.java index 8e95b91556..07ef003de5 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/MetadataFieldsPanel.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/MetadataFieldsPanel.java @@ -7,18 +7,6 @@ */ package org.roda.wui.client.disposal.rule; -import java.util.ArrayList; -import java.util.List; -import java.util.MissingResourceException; - -import org.roda.core.data.common.RodaConstants; -import org.roda.core.data.v2.common.Pair; -import org.roda.core.data.v2.disposal.rule.ConditionType; -import org.roda.core.data.v2.disposal.rule.DisposalRule; -import org.roda.core.data.v2.ip.IndexedAIP; -import org.roda.wui.common.client.tools.ConfigurationManager; -import org.roda.wui.common.client.tools.StringUtils; - import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.KeyUpHandler; @@ -34,8 +22,18 @@ import com.google.gwt.user.client.ui.ListBox; 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.RodaConstants; +import org.roda.core.data.v2.common.Pair; +import org.roda.core.data.v2.disposal.rule.ConditionType; +import org.roda.core.data.v2.disposal.rule.DisposalRule; +import org.roda.core.data.v2.ip.IndexedAIP; +import org.roda.wui.common.client.tools.ConfigurationManager; +import org.roda.wui.common.client.tools.StringUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.MissingResourceException; /** * @author Tiago Fraga @@ -62,10 +60,12 @@ public class MetadataFieldsPanel extends Composite implements HasValueChangeHand @UiField TextBox fieldValue; + private String conditionKey; private int selectedConditionIndex; private boolean changed = false; private boolean checked = false; + public MetadataFieldsPanel(String conditionKey, String conditionValue, boolean editMode, DisposalRule disposalRule) { initWidget(uiBinder.createAndBindUi(this)); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/OrderDisposalRules.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/OrderDisposalRules.java deleted file mode 100644 index 6e9e8f912b..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/OrderDisposalRules.java +++ /dev/null @@ -1,308 +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.disposal.rule; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; - -import org.roda.core.data.v2.disposal.rule.ConditionType; -import org.roda.core.data.v2.disposal.rule.DisposalRule; -import org.roda.core.data.v2.disposal.rule.DisposalRules; -import org.roda.wui.client.common.NoAsyncCallback; -import org.roda.wui.client.common.UserLogin; -import org.roda.wui.client.common.actions.DisposalRuleActions; -import org.roda.wui.client.common.dialogs.Dialogs; -import org.roda.wui.client.common.lists.utils.BasicTablePanel; -import org.roda.wui.client.common.utils.AsyncCallbackUtils; -import org.roda.wui.client.disposal.policy.DisposalPolicy; -import org.roda.wui.client.services.DisposalRuleRestService; -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.HTMLWidgetWrapper; -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.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.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 Tiago Fraga - */ -public class OrderDisposalRules extends Composite { - - public static final HistoryResolver RESOLVER = new HistoryResolver() { - - @Override - public void resolve(List historyTokens, final AsyncCallback callback) { - Services services = new Services("List disposal rules", "get"); - services.disposalRuleResource(DisposalRuleRestService::listDisposalRules).whenComplete((result, throwable) -> { - if (throwable != null) { - AsyncCallbackUtils.defaultFailureTreatment(throwable); - } else { - OrderDisposalRules orderDisposalRules = new OrderDisposalRules(result); - callback.onSuccess(orderDisposalRules); - } - }); - } - - @Override - public void isCurrentUserPermitted(AsyncCallback callback) { - UserLogin.getInstance().checkRoles(new HistoryResolver[] {DisposalPolicy.RESOLVER}, false, callback); - } - - @Override - public List getHistoryPath() { - return ListUtils.concat(DisposalPolicy.RESOLVER.getHistoryPath(), getHistoryToken()); - } - - @Override - public String getHistoryToken() { - return "change_disposal_rule_order"; - } - }; - private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static OrderDisposalRules.MyUiBinder uiBinder = GWT.create(OrderDisposalRules.MyUiBinder.class); - private final DisposalRules disposalRules; - @UiField - FlowPanel orderDisposalRulesDescription; - - @UiField - FlowPanel orderDisposalRulesTablePanel; - - @UiField - Label orderButtonsLabel; - - @UiField - FlowPanel orderButtonsPanel; - - @UiField - Button buttonSave; - - @UiField - Button buttonCancel; - private DisposalRule selectedRule; - private int selectedIndex; - private BasicTablePanel tableRules; - - public OrderDisposalRules(DisposalRules disposalRules) { - initWidget(uiBinder.createAndBindUi(this)); - this.disposalRules = disposalRules; - createDescription(); - createDisposalRulesPanel(); - if (disposalRules.getObjects().isEmpty()) { - buttonSave.setVisible(false); - orderButtonsLabel.setVisible(false); - } else { - buttonSave.setVisible(true); - orderButtonsLabel.setVisible(true); - createOrderButtons(); - } - } - - private void createOrderButtons() { - createTopButton(); - createUpButton(); - createDownButton(); - createBottomButton(); - - } - - private void createTopButton() { - Button buttonTop = new Button(); - buttonTop.setText(messages.editRulesOrderTop()); - buttonTop.addStyleName("btn btn-block btn-default btn-top"); - buttonTop.addClickHandler(clickEvent -> { - if (selectedRule != null && selectedIndex != 0) { - orderDisposalRulesTablePanel.clear(); - disposalRules.moveToTop(selectedRule); - disposalRules.sortRules(); - createDisposalRulesPanel(); - tableRules.getSelectionModel().setSelected(selectedRule, true); - } - }); - orderButtonsPanel.add(buttonTop); - } - - private void createUpButton() { - Button buttonUp = new Button(); - buttonUp.setText(messages.editRulesOrderUp()); - buttonUp.addStyleName("btn btn-block btn-default btn-up"); - buttonUp.addClickHandler(clickEvent -> { - int previousIndex = selectedIndex - 1; - if (selectedRule != null && previousIndex >= 0) { - disposalRules.getObjects().get(previousIndex).setOrder(selectedIndex); - selectedRule.setOrder(previousIndex); - orderDisposalRulesTablePanel.clear(); - disposalRules.sortRules(); - createDisposalRulesPanel(); - tableRules.getSelectionModel().setSelected(selectedRule, true); - } - }); - orderButtonsPanel.add(buttonUp); - } - - private void createDownButton() { - Button buttonDown = new Button(); - buttonDown.setText(messages.editRulesOrderDown()); - buttonDown.addStyleName("btn btn-block btn-default btn-down"); - buttonDown.addClickHandler(clickEvent -> { - int nextIndex = selectedIndex + 1; - if (selectedRule != null && nextIndex < disposalRules.getObjects().size()) { - disposalRules.getObjects().get(nextIndex).setOrder(selectedIndex); - selectedRule.setOrder(nextIndex); - orderDisposalRulesTablePanel.clear(); - disposalRules.sortRules(); - createDisposalRulesPanel(); - tableRules.getSelectionModel().setSelected(selectedRule, true); - } - }); - orderButtonsPanel.add(buttonDown); - } - - private void createBottomButton() { - Button buttonBottom = new Button(); - buttonBottom.setText(messages.editRulesOrderBottom()); - buttonBottom.addStyleName("btn btn-block btn-default btn-bottom"); - buttonBottom.addClickHandler(clickEvent -> { - int lastIndex = disposalRules.getObjects().size() - 1; - if (selectedRule != null && selectedIndex != lastIndex) { - orderDisposalRulesTablePanel.clear(); - disposalRules.moveToBottom(selectedRule, selectedIndex); - disposalRules.sortRules(); - createDisposalRulesPanel(); - tableRules.getSelectionModel().setSelected(selectedRule, true); - } - }); - orderButtonsPanel.add(buttonBottom); - } - - private void createDescription() { - orderDisposalRulesDescription.add(new HTMLWidgetWrapper("OrderDisposalRulesDescription.html")); - } - - private void createDisposalRulesPanel() { - if (disposalRules.getObjects().isEmpty()) { - Label label = new HTML( - SafeHtmlUtils.fromSafeConstant(messages.noItemsToDisplayPreFilters(messages.disposalRulesTitle()))); - label.addStyleName("basicTableEmpty"); - orderDisposalRulesTablePanel.add(label); - } else { - FlowPanel rulesPanel = new FlowPanel(); - tableRules = getBasicTablePanelForDisposalRules(disposalRules); - tableRules.getSelectionModel().addSelectionChangeHandler(event -> { - selectedRule = tableRules.getSelectionModel().getSelectedObject(); - if (selectedRule != null) { - selectedIndex = selectedRule.getOrder(); - } - }); - - rulesPanel.add(tableRules); - orderDisposalRulesTablePanel.add(rulesPanel); - orderDisposalRulesTablePanel.addStyleName("disposalPolicyScrollPanel"); - } - } - - private BasicTablePanel getBasicTablePanelForDisposalRules(DisposalRules disposalRules) { - if (disposalRules.getObjects().isEmpty()) { - return new BasicTablePanel<>(messages.noItemsToDisplayPreFilters(messages.disposalRulesTitle())); - } else { - return new BasicTablePanel(disposalRules.getObjects().iterator(), - - new BasicTablePanel.ColumnInfo<>(messages.disposalRuleOrder(), 4, new TextColumn() { - @Override - public String getValue(DisposalRule rule) { - Integer showOrder = rule.getOrder() + 1; - return showOrder.toString(); - } - }), - - new BasicTablePanel.ColumnInfo<>(messages.disposalRuleTitle(), 0, new TextColumn() { - @Override - public String getValue(DisposalRule rule) { - return rule.getTitle(); - } - }), - - new BasicTablePanel.ColumnInfo<>(messages.disposalRuleType(), 12, new TextColumn() { - @Override - public String getValue(DisposalRule rule) { - return messages.disposalRuleTypeValue(rule.getType().toString()); - } - }), new BasicTablePanel.ColumnInfo<>(messages.disposalRuleCondition(), 24, new TextColumn() { - @Override - public String getValue(DisposalRule rule) { - String condition; - if (rule.getType().equals(ConditionType.METADATA_FIELD)) { - condition = rule.getConditionKey() + " " + messages.disposalRuleConditionOperator() + " " - + rule.getConditionValue(); - } else { - condition = rule.getConditionValue(); - } - return messages.disposalRuleTypeValue(condition); - } - }), - - new BasicTablePanel.ColumnInfo<>(messages.disposalRuleScheduleName(), 12, new TextColumn() { - @Override - public String getValue(DisposalRule rule) { - return rule.getDisposalScheduleName(); - } - })); - } - } - - @UiHandler("buttonSave") - void buttonSaveHandler(ClickEvent e) { - Dialogs.showConfirmDialog(messages.saveButton(), messages.confirmChangeRulesOrder(), messages.dialogNo(), - messages.dialogYes(), new NoAsyncCallback() { - @Override - public void onSuccess(Boolean confirm) { - if (confirm) { - Services services = new Services("Update multiple disposal rules", "update"); - List> futures = new ArrayList<>(); - for (DisposalRule rule : disposalRules.getObjects()) { - futures.add(services.disposalRuleResource(s -> s.updateDisposalRule(rule)).toCompletableFuture()); - } - CompletableFuture[] futuresArray = futures.toArray(new CompletableFuture[0]); - CompletableFuture> listFuture = CompletableFuture.allOf(futuresArray) - .thenApply(v -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList())); - listFuture.join(); - - Toast.showInfo(messages.updateDisposalRulesOrderSuccessTitle(), - messages.updateDisposalRulesOrderSuccessMessage()); - DisposalRuleActions.applyDisposalRulesAction(); - } - } - }); - } - - @UiHandler("buttonCancel") - void buttonCancelHandler(ClickEvent e) { - HistoryUtils.newHistory(DisposalPolicy.RESOLVER); - } - - interface MyUiBinder extends UiBinder { - } -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/OrderDisposalRules.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/OrderDisposalRules.ui.xml deleted file mode 100644 index 6c85440d3c..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/OrderDisposalRules.ui.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/ShowDisposalRule.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/ShowDisposalRule.java index eb2b7723ae..94850ee5b0 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/ShowDisposalRule.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/ShowDisposalRule.java @@ -7,41 +7,32 @@ */ package org.roda.wui.client.disposal.rule; +import java.util.HashMap; import java.util.List; +import java.util.Map; -import org.roda.core.data.common.RodaConstants; -import org.roda.core.data.v2.disposal.rule.ConditionType; +import com.google.gwt.user.client.ui.FocusPanel; import org.roda.core.data.v2.disposal.rule.DisposalRule; -import org.roda.wui.client.browse.BrowseTop; +import org.roda.wui.client.browse.tabs.DisposalRuleTabs; +import org.roda.wui.client.common.DisposalRuleActionsToolbar; +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.DisposalRuleActions; -import org.roda.wui.client.common.dialogs.Dialogs; +import org.roda.wui.client.common.actions.Actionable; import org.roda.wui.client.common.utils.AsyncCallbackUtils; -import org.roda.wui.client.common.utils.PermissionClientUtils; import org.roda.wui.client.disposal.policy.DisposalPolicy; -import org.roda.wui.client.disposal.schedule.ShowDisposalSchedule; +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 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.event.dom.client.ClickHandler; -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.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.Widget; import config.i18n.client.ClientMessages; @@ -50,13 +41,24 @@ * @author Tiago Fraga */ public class ShowDisposalRule extends Composite { - private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static ShowDisposalRule instance = null; public static final HistoryResolver RESOLVER = new HistoryResolver() { @Override public void resolve(List historyTokens, final AsyncCallback callback) { - getInstance().resolve(historyTokens, callback); + if (historyTokens.size() == 1) { + Services services = new Services("Retrieve disposal rule", "get"); + services.disposalRuleResource(s -> s.retrieveDisposalRule(historyTokens.get(0))) + .whenComplete((rule, throwable) -> { + if (throwable != null) { + // TODO: REVIEW defaultFailureTreatment + AsyncCallbackUtils.defaultFailureTreatment(throwable); + HistoryUtils.newHistory(DisposalPolicy.RESOLVER.getHistoryPath()); + } else { + ShowDisposalRule panel = new ShowDisposalRule(rule); + callback.onSuccess(panel); + } + }); + } } @Override @@ -74,175 +76,61 @@ public String getHistoryToken() { return "disposal_rule"; } }; - private static ShowDisposalRule.MyUiBinder uiBinder = GWT.create(ShowDisposalRule.MyUiBinder.class); - @UiField - Label disposalRuleId; - @UiField - Label dateCreated, dateUpdated; - @UiField - TitlePanel title; - @UiField - Label disposalRuleDescriptionLabel; - @UiField - HTML disposalRuleDescription; - @UiField - Label disposalRuleScheduleLabel; - @UiField - HTML disposalRuleScheduleName; - @UiField - Label disposalRuleTypeLabel; - @UiField - HTML disposalRuleType; - @UiField - Label conditionsLabel; - @UiField - FlowPanel conditionsPanel; + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - // Conditions @UiField - FlowPanel buttonsPanel; - private DisposalRule disposalRule; + FocusPanel keyboardFocus; - // Sidebar + @UiField + NavigationToolbar navigationToolbar; - public ShowDisposalRule() { - this.disposalRule = new DisposalRule(); - } + @UiField + DisposalRuleActionsToolbar actionsToolbar; - public ShowDisposalRule(final DisposalRule disposalRule) { - instance = this; - this.disposalRule = disposalRule; + @UiField + TitlePanel title; - initWidget(uiBinder.createAndBindUi(this)); - initElements(); - initButtons(); - } + @UiField + DisposalRuleTabs browseTab; - public static ShowDisposalRule getInstance() { - if (instance == null) { - instance = new ShowDisposalRule(); + 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(); + } } - return instance; - } - - public void initElements() { - title.setText(disposalRule.getTitle()); - - disposalRuleId.setText(messages.disposalRuleIdentifier() + ": " + disposalRule.getId()); + }; - if (disposalRule.getCreatedOn() != null && StringUtils.isNotBlank(disposalRule.getCreatedBy())) { - dateCreated.setText( - messages.dateCreated(Humanize.formatDateTime(disposalRule.getCreatedOn()), disposalRule.getCreatedBy())); - } + public ShowDisposalRule(final DisposalRule rule) { + initWidget(uiBinder.createAndBindUi(this)); - if (disposalRule.getUpdatedOn() != null && StringUtils.isNotBlank(disposalRule.getUpdatedBy())) { - dateUpdated.setText( - messages.dateUpdated(Humanize.formatDateTime(disposalRule.getUpdatedOn()), disposalRule.getUpdatedBy())); - } + initHandlers(); - disposalRuleDescription.setHTML(SafeHtmlUtils.fromString(disposalRule.getDescription())); - disposalRuleDescriptionLabel.setVisible(StringUtils.isNotBlank(disposalRule.getDescription())); + navigationToolbar.withObject(rule); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getDisposalRuleBreadcrumbs(rule)); + navigationToolbar.build(); - disposalRuleScheduleName.setHTML(SafeHtmlUtils.fromString(disposalRule.getDisposalScheduleName())); - disposalRuleScheduleLabel.setVisible(StringUtils.isNotBlank(disposalRule.getDisposalScheduleName())); - disposalRuleScheduleName.addStyleName("btn-link addCursorPointer"); - disposalRuleScheduleName.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent clickEvent) { - HistoryUtils.newHistory(ShowDisposalSchedule.RESOLVER, disposalRule.getDisposalScheduleId()); - } - }); - - disposalRuleType - .setHTML(SafeHtmlUtils.fromString(messages.disposalRuleTypeValue(disposalRule.getType().toString()))); - disposalRuleTypeLabel.setVisible(StringUtils.isNotBlank(disposalRule.getType().toString())); - - conditionsLabel.setVisible(true); - HTML condition = new HTML(); - if (disposalRule.getType().equals(ConditionType.IS_CHILD_OF)) { - String conditionTxt = messages.disposalRuleTypeValue(disposalRule.getType().toString()) + " " - + disposalRule.getConditionValue() + " (" + disposalRule.getConditionKey() + ")"; - condition.setHTML(SafeHtmlUtils.fromString(conditionTxt)); - condition.addStyleName("btn-link addCursorPointer"); - condition.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent clickEvent) { - HistoryUtils.newHistory(BrowseTop.RESOLVER, disposalRule.getConditionKey()); - } - }); - conditionsPanel.add(condition); - } else if (disposalRule.getType().equals(ConditionType.METADATA_FIELD)) { - String conditionTxt = disposalRule.getConditionKey() + " " + messages.disposalRuleConditionOperator() + " " - + disposalRule.getConditionValue(); - condition.setHTML(SafeHtmlUtils.fromString(conditionTxt)); - conditionsPanel.add(condition); - } - } + actionsToolbar.setLabel(messages.showDisposalRuleTitle()); + actionsToolbar.setObjectAndBuild(rule, null, handler); - public void initButtons() { - - if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_UPDATE_DISPOSAL_RULE)) { - Button editRuleBtn = new Button(); - editRuleBtn.addStyleName("btn btn-block btn-edit"); - editRuleBtn.setText(messages.editButton()); - editRuleBtn.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent clickEvent) { - HistoryUtils.newHistory(EditDisposalRule.RESOLVER, disposalRule.getId()); - } - }); - - buttonsPanel.add(editRuleBtn); - - Button removeRuleBtn = new Button(); - removeRuleBtn.addStyleName("btn btn-block btn-danger btn-ban"); - removeRuleBtn.setText(messages.removeButton()); - removeRuleBtn.addClickHandler(clickEvent -> Dialogs.showConfirmDialog(messages.deleteDisposalRuleDialogTitle(), - messages.deleteDisposalRuleDialogMessage(disposalRule.getTitle()), messages.dialogNo(), messages.dialogYes(), - new NoAsyncCallback() { - @Override - public void onSuccess(Boolean confirm) { - if (confirm) { - Services services = new Services("Delete disposal rule", "deletion"); - services.disposalRuleResource(s -> s.deleteDisposalRule(disposalRule.getId())) - .whenComplete((unused, throwable) -> { - if (throwable != null) { - AsyncCallbackUtils.defaultFailureTreatment(throwable); - } else { - Toast.showInfo(messages.deleteDisposalRuleSuccessTitle(), - messages.deleteDisposalRuleSuccessMessage(disposalRule.getTitle())); - DisposalRuleActions.applyDisposalRulesAction(); - } - }); - } - } - })); + title.setText(rule.getTitle()); + title.setIconClass("DisposalRule"); - buttonsPanel.add(removeRuleBtn); - } + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse"); - Button backBtn = new Button(); - backBtn.setText(messages.backButton()); - backBtn.addStyleName("btn btn-block btn-default btn-times-circle"); - backBtn.addClickHandler(event -> HistoryUtils.newHistory(DisposalPolicy.RESOLVER)); - buttonsPanel.add(backBtn); + browseTab.init(rule, handler); } - public void resolve(List historyTokens, final AsyncCallback callback) { - if (historyTokens.size() == 1) { - Services services = new Services("Retrieve disposal rule", "get"); - services.disposalRuleResource(s -> s.retrieveDisposalRule(historyTokens.get(0))) - .whenComplete((result, throwable) -> { - if (throwable != null) { - AsyncCallbackUtils.defaultFailureTreatment(throwable); - } else { - ShowDisposalRule panel = new ShowDisposalRule(result); - callback.onSuccess(panel); - } - }); - } + private void initHandlers() { + handlers.put(Actionable.ActionImpact.DESTROYED, + () -> HistoryUtils.newHistory(DisposalPolicy.RESOLVER.getHistoryPath())); } interface MyUiBinder extends UiBinder { } + } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/ShowDisposalRule.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/ShowDisposalRule.ui.xml index 1c76906f03..a2546915a7 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/ShowDisposalRule.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/ShowDisposalRule.ui.xml @@ -1,65 +1,23 @@ - + - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - + + \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/data/panels/DisposalRuleDataPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/data/panels/DisposalRuleDataPanel.java new file mode 100644 index 0000000000..0d6b303854 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/data/panels/DisposalRuleDataPanel.java @@ -0,0 +1,360 @@ +package org.roda.wui.client.disposal.rule.data.panels; + +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.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.ListBox; +import com.google.gwt.user.client.ui.TextBox; +import config.i18n.client.ClientMessages; +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.v2.common.Pair; +import org.roda.core.data.v2.disposal.rule.ConditionType; +import org.roda.core.data.v2.disposal.rule.DisposalRule; +import org.roda.core.data.v2.disposal.schedule.DisposalScheduleState; +import org.roda.core.data.v2.disposal.schedule.DisposalSchedules; +import org.roda.core.data.v2.index.filter.AllFilterParameter; +import org.roda.core.data.v2.index.filter.Filter; +import org.roda.core.data.v2.ip.IndexedAIP; +import org.roda.wui.client.common.dialogs.SelectAipDialog; +import org.roda.wui.client.common.forms.GenericDataForm; +import org.roda.wui.client.common.forms.GenericDataPanel; +import org.roda.wui.common.client.tools.ConfigurationManager; +import org.roda.wui.common.client.tools.StringUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.MissingResourceException; + +/** + * @author Tiago Fraga + */ +public class DisposalRuleDataPanel extends Composite + implements GenericDataPanel, HasValueChangeHandlers { + + private static final ClientMessages messages = GWT.create(ClientMessages.class); + + private final GenericDataForm form; + private final boolean editMode; + private final Button saveButton; + private final Button cancelButton; + + // --- Child Of Sub-Panel Variables --- + private FlowPanel childOfRow; + private TextBox childOfTextBox; + private String childOfAipId; + private String childOfAipTitle; + + private SelectionMethod selectionMethod = SelectionMethod.NONE; + + public DisposalRuleDataPanel(DisposalRule disposalRule, DisposalSchedules disposalSchedules, boolean editMode) { + this.editMode = editMode; + this.form = new GenericDataForm<>(); + + // 1. Title + form.addTextField(messages.disposalRuleTitle(), DisposalRule::getTitle, DisposalRule::setTitle, true, false); + + // 2. Description + form.addTextArea(messages.disposalRuleDescription(), DisposalRule::getDescription, DisposalRule::setDescription, + false, false); + + // 3. Disposal Schedules List + ListBox disposalSchedulesList = new ListBox(); + disposalSchedules.getObjects().stream().filter(p -> !p.getState().equals(DisposalScheduleState.INACTIVE)) + .forEach(schedule -> disposalSchedulesList.addItem(schedule.getTitle(), schedule.getId())); + + form.addListBox(messages.disposalRuleScheduleName(), disposalSchedulesList, DisposalRule::getDisposalScheduleId, + (r, val) -> { + r.setDisposalScheduleId(val); + if (disposalSchedulesList.getSelectedIndex() >= 0) { + r.setDisposalScheduleName(disposalSchedulesList.getSelectedItemText()); + } else { + r.setDisposalScheduleName(null); + } + }, true); + + // 4. Condition Type + ListBox conditionTypeList = new ListBox(); + for (ConditionType ruleType : ConditionType.values()) { + conditionTypeList.addItem(messages.disposalRuleTypeValue(ruleType.toString()), ruleType.toString()); + } + + form.addListBox(messages.disposalRuleType(), conditionTypeList, r -> r.getType() != null ? r.getType().name() : "", + (r, val) -> r.setType(StringUtils.isNotBlank(val) ? ConditionType.valueOf(val) : null), true); + + // 5. Metadata Field Condition + ListBox metadataFieldList = new ListBox(); + for (Pair p : getElementsFromConfig()) { + metadataFieldList.addItem(p.getSecond(), p.getFirst()); + } + FlowPanel metadataFieldRow = form.addListBox(messages.disposalRuleCondition(), metadataFieldList, + DisposalRule::getConditionKey, DisposalRule::setConditionKey, true); + + // 6. Metadata Value Condition + FlowPanel metadataValueRow = form.addTextField(messages.disposalRuleConditionOperator(), + DisposalRule::getConditionValue, DisposalRule::setConditionValue, true, false); + + // 7. IS_CHILD_OF Panel (Custom generic-form-field layout) + initChildOfRow(); + form.addCustomWidget(childOfRow); + + // --- DEPENDENCY VISIBILITY LOGIC --- + + Runnable evaluateVisibility = () -> { + String val = conditionTypeList.getSelectedValue(); + if (ConditionType.IS_CHILD_OF.name().equals(val)) { + selectionMethod = SelectionMethod.CHILD_OF; + metadataFieldRow.setVisible(false); + metadataValueRow.setVisible(false); + childOfRow.setVisible(true); + } else if (ConditionType.METADATA_FIELD.name().equals(val)) { + selectionMethod = SelectionMethod.METADATA_FIELD; + metadataFieldRow.setVisible(true); + metadataValueRow.setVisible(true); + childOfRow.setVisible(false); + } else { + selectionMethod = SelectionMethod.NONE; + metadataFieldRow.setVisible(false); + metadataValueRow.setVisible(false); + childOfRow.setVisible(false); + } + }; + + conditionTypeList.addChangeHandler(event -> evaluateVisibility.run()); + + // --- INITIALIZATION --- + if (disposalRule != null) { + form.setModel(disposalRule); + + // Initialize ChildOf variables if in edit mode + if (editMode && ConditionType.IS_CHILD_OF.equals(disposalRule.getType())) { + this.childOfAipId = disposalRule.getConditionKey(); + this.childOfAipTitle = disposalRule.getConditionValue(); + this.childOfTextBox.setText(childOfAipTitle + " (" + childOfAipId + ")"); + } + + if (conditionTypeList.getItemCount() > 0) { + evaluateVisibility.run(); + } + } + + // 1. Initialize Buttons + saveButton = new Button(messages.saveButton()); + saveButton.addStyleName("btn btn-primary btn-play"); + + cancelButton = new Button(messages.cancelButton()); + cancelButton.addStyleName("btn btn-link"); + + // 2. Wrap buttons in a FlowPanel for spacing + FlowPanel actionsPanel = new FlowPanel(); + actionsPanel.addStyleName("alignButtonsPanel"); // Uses your existing CSS spacing + actionsPanel.add(saveButton); + actionsPanel.add(cancelButton); + + // 3. Inject the buttons at the bottom of the generic form + form.addCustomWidget(actionsPanel); + + // Initialize the composite using the generic form as the root widget + initWidget(form); + } + + // --- CHILD OF CUSTOM ROW --- + + private void initChildOfRow() { + childOfRow = new FlowPanel(); + childOfRow.addStyleName("generic-form-field"); + + FlowPanel leftPanel = new FlowPanel(); + leftPanel.addStyleName("generic-form-field-left-panel"); + + Label childOfLabel = new Label(messages.selectParentTitle() + "*"); + childOfLabel.addStyleName("form-label"); + + FlowPanel inputPanel = new FlowPanel(); + inputPanel.addStyleName("generic-form-field-input-panel full_width"); + + // Read-only textbox + childOfTextBox = new TextBox(); + childOfTextBox.addStyleName("form-textbox"); + childOfTextBox.setReadOnly(true); + + // Button to open dialog + Button selectAipBtn = new Button(messages.selectButton()); + selectAipBtn.addStyleName("btn btn-primary ma-pageview"); + selectAipBtn.addClickHandler(e -> { + + SelectAipDialog selectAipDialog = new SelectAipDialog(messages.selectParentTitle(), + new Filter(new AllFilterParameter()), true, false); + + selectAipDialog.setSingleSelectionMode(); + + selectAipDialog.addValueChangeHandler(evt -> { + this.childOfAipId = evt.getValue().getId(); + this.childOfAipTitle = evt.getValue().getTitle(); + this.childOfTextBox.setText(childOfAipTitle + " (" + childOfAipId + ")"); + this.childOfTextBox.removeStyleName("isWrong"); + onChange(); + }); + }); + + // Clear Button (Optional, but good UX to allow them to remove selection) + Button clearBtn = new Button(); + clearBtn.setHTML(""); + clearBtn.addStyleName("btn btn-danger btn-slim"); + clearBtn.addClickHandler(e -> { + this.childOfAipId = null; + this.childOfAipTitle = null; + this.childOfTextBox.setText(""); + onChange(); + }); + + inputPanel.add(childOfTextBox); + inputPanel.add(selectAipBtn); + inputPanel.add(clearBtn); + + leftPanel.add(childOfLabel); + leftPanel.add(inputPanel); + childOfRow.add(leftPanel); + } + + // --- MIGRATED CONFIG METHODS --- + + private List> getElementsFromConfig() { + List> elements = new ArrayList<>(); + String classSimpleName = IndexedAIP.class.getSimpleName(); + List fields = ConfigurationManager.getStringList(RodaConstants.SEARCH_FIELD_PREFIX, classSimpleName); + + for (String field : fields) { + String fieldPrefix = RodaConstants.SEARCH_FIELD_PREFIX + '.' + classSimpleName + '.' + field; + String fieldType = ConfigurationManager.getString(fieldPrefix, RodaConstants.SEARCH_FIELD_TYPE); + String fieldsName = ConfigurationManager.getString(fieldPrefix, RodaConstants.SEARCH_FIELD_FIELDS); + + if (RodaConstants.SEARCH_FIELD_TYPE_TEXT.equals(fieldType) && showField(field)) { + String fieldLabelI18N = ConfigurationManager.getString(fieldPrefix, RodaConstants.SEARCH_FIELD_I18N); + String translation = fieldLabelI18N; + try { + translation = ConfigurationManager.getTranslation(fieldLabelI18N); + } catch (MissingResourceException e) { + // do nothing + } + + Pair pair = new Pair<>(fieldsName, translation); + elements.add(pair); + } + } + return elements; + } + + private boolean showField(String fieldsName) { + List blackList = ConfigurationManager.getStringList(RodaConstants.DISPOSAL_RULE_BLACKLIST_CONDITION); + return !blackList.contains(fieldsName); + } + + /** + * Defines what happens when the Save button is clicked. It automatically + * validates the form before executing the runnable. + */ + public void setSaveHandler(Runnable onSave) { + saveButton.addClickHandler(event -> { + if (isValid()) { + onSave.run(); + } + }); + } + + /** + * Defines what happens when the Cancel button is clicked. + */ + public void setCancelHandler(Runnable onCancel) { + cancelButton.addClickHandler(event -> onCancel.run()); + } + + // --- GENERIC DATA PANEL OVERRIDES --- + + @Override + public DisposalRule getValue() { + DisposalRule rule = form.getValue(); + + // Inject custom sub-panel values back into the model before returning + if (rule.getType() == ConditionType.IS_CHILD_OF) { + rule.setConditionKey(childOfAipId); + rule.setConditionValue(childOfAipTitle); + } + + return rule; + } + + @Override + public boolean isValid() { + boolean valid = form.isValid(); + List extraErrors = new ArrayList<>(); + + // Manually validate the custom ChildOf row + if (selectionMethod == SelectionMethod.CHILD_OF) { + if (StringUtils.isBlank(childOfAipId)) { + valid = false; + childOfTextBox.addStyleName("isWrong"); + extraErrors.add(messages.isAMandatoryField(messages.selectParentTitle())); + } else { + childOfTextBox.removeStyleName("isWrong"); + } + } + + // Append extra errors to the GenericDataForm's native error block + if (!extraErrors.isEmpty()) { + String currentErrorsHTML = form.getErrors().getHTML() != null ? form.getErrors().getHTML() : ""; + StringBuilder sb = new StringBuilder(currentErrorsHTML); + for (String err : extraErrors) { + sb.append("").append(err).append("
"); + } + form.getErrors().setHTML(sb.toString()); + form.getErrors().setVisible(true); + } + + return valid; + } + + public boolean isEditMode() { + return editMode; + } + + public boolean isChanged() { + boolean conditionChanged = false; + + if (selectionMethod == SelectionMethod.CHILD_OF) { + // Check if the current ID differs from the original model's ID + String originalId = null; + if (form.getValue() != null && ConditionType.IS_CHILD_OF.equals(form.getValue().getType())) { + originalId = form.getValue().getConditionKey(); + } + if (childOfAipId != null && !childOfAipId.equals(originalId)) { + conditionChanged = true; + } else if (childOfAipId == null && originalId != null) { + conditionChanged = true; + } + } + + return form.isChanged() || conditionChanged; + } + + @Override + public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) { + return form.addValueChangeHandler(handler); + } + + protected void onChange() { + if (form.isChanged()) { + isValid(); + } + ValueChangeEvent.fire(this, getValue()); + } + + private enum SelectionMethod { + CHILD_OF, METADATA_FIELD, NONE + } +} \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/tabs/DisposalRuleDetailsPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/tabs/DisposalRuleDetailsPanel.java new file mode 100644 index 0000000000..50dc4a95e2 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/tabs/DisposalRuleDetailsPanel.java @@ -0,0 +1,130 @@ +package org.roda.wui.client.disposal.rule.tabs; + +import java.util.List; + +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import org.roda.core.data.v2.disposal.rule.ConditionType; +import org.roda.core.data.v2.disposal.rule.DisposalRule; +import org.roda.wui.client.browse.BrowseTop; +import org.roda.wui.client.common.ActionsToolbar; +import org.roda.wui.client.common.actions.Actionable; +import org.roda.wui.client.common.actions.DisposalRuleAction; +import org.roda.wui.client.common.actions.DisposalRuleToolbarActions; +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 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.wui.client.common.utils.HtmlSnippetUtils; +import org.roda.wui.client.disposal.schedule.ShowDisposalSchedule; +import org.roda.wui.common.client.tools.HistoryUtils; + +/** + * @author Miguel Guimarães + */ + +public class DisposalRuleDetailsPanel 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 DisposalRule rule; + private AsyncCallback localCallback; + + public DisposalRuleDetailsPanel(DisposalRule disposalRule, AsyncCallback actionCallback) { + initWidget(uiBinder.createAndBindUi(this)); + + // Promote localCallback to an instance variable so refresh() can use it + this.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); + + // Initial load + refresh(disposalRule); + } + + // Update the method signature to accept the new rule + public void refresh(DisposalRule newDisposalRule) { + this.rule = newDisposalRule; + + // 1. Clear out the old details + clear(); + + // 2. Re-populate text fields with the new data + init(this.rule); + + // 3. Re-bind the actions toolbar with the new rule object + actionsToolbar.setActionableMenu(new ActionableWidgetBuilder(DisposalRuleToolbarActions.get()) + .withActionCallback(localCallback).buildGroupedListWithObjects(new ActionableObject<>(rule), + List.of(DisposalRuleAction.EDIT), List.of(DisposalRuleAction.EDIT)), + true); + } + + private void init(DisposalRule rule) { + FormUtilities.addIfNotBlank(detailsPanel, messages.disposalRuleTitle(), rule.getTitle()); + FormUtilities.addIfNotBlank(detailsPanel, messages.disposalRuleDescription(), rule.getDescription()); + FormUtilities.addIfNotBlank(detailsPanel, messages.disposalRuleScheduleName(), rule.getDisposalScheduleName(), + "btn-link addCursorPointer", new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + HistoryUtils.newHistory(ShowDisposalSchedule.RESOLVER, rule.getDisposalScheduleId()); + } + }); + FormUtilities.addIfNotBlank(detailsPanel, messages.disposalRuleType(), HtmlSnippetUtils.getDisposalRuleTypeHtml(rule)); + + + if (rule.getType().equals(ConditionType.IS_CHILD_OF)) { + String conditionTxt = messages.disposalRuleTypeValue(rule.getType().toString()) + " " + rule.getConditionValue() + + " (" + rule.getConditionKey() + ")"; + FormUtilities.addIfNotBlank(detailsPanel, messages.disposalRuleCondition(), conditionTxt, + "btn-link addCursorPointer", new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + HistoryUtils.newHistory(BrowseTop.RESOLVER, rule.getConditionKey()); + } + }); + } else if (rule.getType().equals(ConditionType.METADATA_FIELD)) { + + String conditionTxt = rule.getConditionKey() + " " + messages.disposalRuleConditionOperator() + " " + + rule.getConditionValue(); + FormUtilities.addIfNotBlank(detailsPanel, messages.disposalRuleCondition(), SafeHtmlUtils.fromString(conditionTxt)); + } + } + + public void clear() { + detailsPanel.clear(); + } + + interface MyUiBinder extends UiBinder { + Widget createAndBindUi(DisposalRuleDetailsPanel detailsPanel); + } + +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/tabs/DisposalRuleDetailsPanel.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/tabs/DisposalRuleDetailsPanel.ui.xml new file mode 100644 index 0000000000..ea70d608e6 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/rule/tabs/DisposalRuleDetailsPanel.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/disposal/schedule/CreateDisposalSchedule.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/CreateDisposalSchedule.java index ba51fb7ab8..e9972fe782 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/CreateDisposalSchedule.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/CreateDisposalSchedule.java @@ -7,28 +7,32 @@ */ package org.roda.wui.client.disposal.schedule; -import java.util.List; - +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.FocusPanel; +import com.google.gwt.user.client.ui.Widget; +import config.i18n.client.ClientMessages; +import org.roda.core.data.v2.disposal.rule.DisposalRule; import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; +import org.roda.wui.client.common.NavigationToolbar; +import org.roda.wui.client.common.NoActionsToolbar; +import org.roda.wui.client.common.TitlePanel; import org.roda.wui.client.common.UserLogin; import org.roda.wui.client.common.utils.AsyncCallbackUtils; import org.roda.wui.client.disposal.policy.DisposalPolicy; +import org.roda.wui.client.disposal.schedule.data.panels.DisposalScheduleDataPanel; +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.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; +import java.util.List; /** * @author Tiago Fraga @@ -39,7 +43,7 @@ public class CreateDisposalSchedule extends Composite { @Override public void resolve(List historyTokens, final AsyncCallback callback) { - CreateDisposalSchedule createDisposalSchedule = new CreateDisposalSchedule(new DisposalSchedule()); + CreateDisposalSchedule createDisposalSchedule = new CreateDisposalSchedule(); callback.onSuccess(createDisposalSchedule); } @@ -59,48 +63,54 @@ public String getHistoryToken() { } }; private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static CreateDisposalSchedule.MyUiBinder uiBinder = GWT.create(CreateDisposalSchedule.MyUiBinder.class); + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + @UiField - Button buttonSave; + FocusPanel keyboardFocus; @UiField - Button buttonCancel; - @UiField(provided = true) - DisposalScheduleDataPanel disposalScheduleDataPanel; - private DisposalSchedule disposalSchedule; - - public CreateDisposalSchedule(DisposalSchedule disposalSchedule) { - this.disposalSchedule = disposalSchedule; - - this.disposalScheduleDataPanel = new DisposalScheduleDataPanel(disposalSchedule, false); - this.disposalScheduleDataPanel.setDisposalSchedule(disposalSchedule); + NavigationToolbar navigationToolbar; + @UiField + NoActionsToolbar actionsToolbar; + @UiField + TitlePanel title; + @UiField + FlowPanel disposalScheduleDataPanel; + public CreateDisposalSchedule() { initWidget(uiBinder.createAndBindUi(this)); - } - @UiHandler("buttonSave") - void buttonApplyHandler(ClickEvent e) { - if (disposalScheduleDataPanel.isValid()) { - disposalSchedule = disposalScheduleDataPanel.getDisposalSchedule(); + DisposalScheduleDataPanel dataPanel = new DisposalScheduleDataPanel(new DisposalSchedule(), false); + disposalScheduleDataPanel.add(dataPanel); + dataPanel.setSaveHandler(() -> { Services services = new Services("Create disposal schedule", "create"); - services.disposalScheduleResource(s -> s.createDisposalSchedule(disposalSchedule)) + services.disposalScheduleResource(s -> s.createDisposalSchedule(dataPanel.getValue())) .whenComplete((created, throwable) -> { if (throwable != null) { AsyncCallbackUtils.defaultFailureTreatment(throwable); } else { - HistoryUtils.newHistory(DisposalPolicy.RESOLVER); + Toast.showInfo(messages.showDisposalScheduleTitle(), messages.disposalScheduleSuccessfullyCreated()); + HistoryUtils.newHistory(ShowDisposalSchedule.RESOLVER, created.getId()); } }); - } - } + }); - @UiHandler("buttonCancel") - void buttonCancelHandler(ClickEvent e) { - cancel(); - } + dataPanel.setCancelHandler(() -> HistoryUtils.newHistory(DisposalPolicy.RESOLVER)); + + navigationToolbar.withoutButtons().build(); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getCreateDisposalScheduleBreadcrumbs()); + + actionsToolbar.setLabel(messages.showDisposalScheduleTitle()); + + // 3. Pass the shared object + actionsToolbar.build(); + + title.setText(messages.newDisposalScheduleTitle()); + title.setIconClass("DisposalSchedule"); + title.addStyleName("mb-20"); - private void cancel() { - HistoryUtils.newHistory(DisposalPolicy.RESOLVER); + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse"); } interface MyUiBinder extends UiBinder { diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/CreateDisposalSchedule.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/CreateDisposalSchedule.ui.xml index a2661ae4e2..6bc8a97b4a 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/CreateDisposalSchedule.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/CreateDisposalSchedule.ui.xml @@ -1,31 +1,21 @@ - + - + + + - - - - - - - - - - - - - - - - - - - + + + + + + - + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/DisposalScheduleDataPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/DisposalScheduleDataPanel.java index edff46c59b..e69de29bb2 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/DisposalScheduleDataPanel.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/DisposalScheduleDataPanel.java @@ -1,450 +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.disposal.schedule; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.roda.core.data.v2.common.Pair; -import org.roda.core.data.v2.disposal.schedule.DisposalActionCode; -import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; -import org.roda.core.data.v2.disposal.schedule.RetentionPeriodIntervalCode; -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.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.ListBox; -import com.google.gwt.user.client.ui.TextArea; -import com.google.gwt.user.client.ui.TextBox; -import com.google.gwt.user.client.ui.Widget; - -import config.i18n.client.ClientMessages; - -/** - * @author Tiago Fraga - */ -public class DisposalScheduleDataPanel extends Composite implements HasValueChangeHandlers { - - public static final String IS_WRONG = "isWrong"; - private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static DisposalScheduleDataPanel.MyUiBinder uiBinder = GWT.create(DisposalScheduleDataPanel.MyUiBinder.class); - private final boolean editMode; - @UiField - TextBox title; - - @UiField - Label titleError; - - @UiField - TextBox description; - - @UiField - TextArea mandate; - - @UiField - TextArea notes; - - // disposal actions - - @UiField - Label disposalActionsLabel; - - @UiField - ListBox disposalActions; - - @UiField - Label disposalActionsError; - - // retention triggers elementId - - @UiField - Label retentionTriggerElementIdLabel; - - @UiField - ListBox retentionTriggerElementIdList; - - @UiField - Label retentionTriggerElementIdError; - - // retention period interval - - @UiField - ListBox retentionPeriodIntervals; - - // retention period duration - @UiField - Label retentionPeriodDurationLabel; - - @UiField - TextBox retentionPeriodDuration; - - @UiField - Label retentionPeriodDurationError; - @UiField - HTML errors; - private boolean changed = false; - private boolean checked = false; - - public DisposalScheduleDataPanel(DisposalSchedule disposalSchedule, boolean editMode) { - - initWidget(uiBinder.createAndBindUi(this)); - - this.editMode = editMode; - - setInitialState(); - initHandlers(); - initList(); - - if (editMode) { - setEditInitialState(); - setDisposalSchedule(disposalSchedule); - } - } - - private void setEditInitialState() { - disposalActionsLabel.setVisible(false); - disposalActions.setVisible(false); - disposalActionsError.setVisible(false); - } - - private void initHandlers() { - - ChangeHandler changeHandler = new ChangeHandler() { - @Override - public void onChange(ChangeEvent event) { - DisposalScheduleDataPanel.this.onChange(); - } - }; - - KeyUpHandler keyUpHandler = new KeyUpHandler() { - @Override - public void onKeyUp(KeyUpEvent keyUpEvent) { - DisposalScheduleDataPanel.this.onChange(); - } - }; - - ChangeHandler retentionPeriodChangeHandler = event -> { - if (retentionPeriodIntervals.getSelectedValue() - .equals(RetentionPeriodIntervalCode.NO_RETENTION_PERIOD.toString())) { - retentionPeriodDuration.setEnabled(false); - retentionPeriodDuration.setValue(""); - retentionPeriodDuration.addStyleName("wui-cursor-not-allowed"); - } else { - retentionPeriodDurationLabel.setVisible(true); - retentionPeriodDurationLabel.setVisible(true); - retentionPeriodDuration.setEnabled(true); - retentionPeriodDuration.removeStyleName("wui-cursor-not-allowed"); - } - }; - - ChangeHandler disposalActionChangeHandler = event -> { - - if (disposalActions.getSelectedValue().equals(DisposalActionCode.RETAIN_PERMANENTLY.name()) - || disposalActions.getSelectedIndex() == 0) { - retentionTriggerElementIdLabel.setVisible(false); - retentionTriggerElementIdList.setVisible(false); - retentionTriggerElementIdList.setSelectedIndex(0); - - retentionPeriodIntervals.setVisible(false); - retentionPeriodIntervals.setSelectedIndex(0); - - retentionPeriodDurationLabel.setVisible(false); - retentionPeriodDuration.setVisible(false); - retentionPeriodDuration.setValue(""); - } else { - retentionTriggerElementIdLabel.setVisible(true); - retentionTriggerElementIdList.setVisible(true); - } - }; - - ChangeHandler retentionTriggerElement = event -> { - boolean value = true; - if (retentionTriggerElementIdList.getSelectedValue().equals("")) { - value = false; - } - retentionPeriodIntervals.setVisible(value); - retentionPeriodIntervals.setSelectedIndex(0); - - retentionPeriodDurationLabel.setVisible(value); - retentionPeriodDuration.setVisible(value); - retentionPeriodDuration.setValue(""); - }; - - disposalActions.addChangeHandler(disposalActionChangeHandler); - retentionPeriodIntervals.addChangeHandler(retentionPeriodChangeHandler); - retentionTriggerElementIdList.addChangeHandler(retentionTriggerElement); - - title.addChangeHandler(changeHandler); - title.addKeyUpHandler(keyUpHandler); - disposalActions.addChangeHandler(changeHandler); - retentionTriggerElementIdList.addChangeHandler(changeHandler); - retentionPeriodDuration.addKeyUpHandler(keyUpHandler); - retentionPeriodDuration.addChangeHandler(changeHandler); - retentionPeriodIntervals.addChangeHandler(changeHandler); - description.addChangeHandler(changeHandler); - mandate.addChangeHandler(changeHandler); - notes.addChangeHandler(changeHandler); - } - - private void setInitialState() { - errors.setVisible(false); - - retentionTriggerElementIdLabel.setVisible(false); - retentionTriggerElementIdList.setVisible(false); - - retentionPeriodIntervals.setVisible(false); - - retentionPeriodDurationLabel.setVisible(false); - retentionPeriodDuration.setVisible(false); - } - - private void initList() { - List disposalActionCodes = Arrays.asList(DisposalActionCode.values()); - disposalActions.addItem("", ""); - for (DisposalActionCode actionCode : disposalActionCodes) { - disposalActions.addItem(messages.disposalScheduleAction(actionCode.toString()), actionCode.toString()); - } - - List retentionPeriodIntervalCodes = Arrays - .asList(RetentionPeriodIntervalCode.values()); - retentionPeriodIntervals.addItem("", ""); - for (RetentionPeriodIntervalCode retentionPeriodIntervalCode : retentionPeriodIntervalCodes) { - retentionPeriodIntervals.addItem( - messages.disposalScheduleRetentionPeriodIntervalValue(retentionPeriodIntervalCode.toString()), - retentionPeriodIntervalCode.toString()); - } - - List> retentionElementsIds = DisposalScheduleUtils.getElementsFromConfig(); - retentionTriggerElementIdList.addItem("", ""); - for (Pair value : retentionElementsIds) { - retentionTriggerElementIdList.addItem(value.getSecond(), value.getFirst()); - } - } - - public DisposalSchedule getDisposalSchedule() { - DisposalSchedule disposalSchedule = new DisposalSchedule(); - disposalSchedule.setTitle(title.getText()); - disposalSchedule.setDescription(description.getText()); - disposalSchedule.setMandate(mandate.getText()); - disposalSchedule.setScopeNotes(notes.getText()); - if (!editMode) { - disposalSchedule.setActionCode(getDisposalActionCode(disposalActions.getSelectedValue())); - if (getDisposalActionCode(disposalActions.getSelectedValue()) != DisposalActionCode.RETAIN_PERMANENTLY) { - disposalSchedule.setRetentionTriggerElementId(retentionTriggerElementIdList.getSelectedValue()); - disposalSchedule - .setRetentionPeriodIntervalCode(getRetentionPeriodIntervalCode(retentionPeriodIntervals.getSelectedValue())); - if (retentionPeriodDurationLabel.isVisible() && StringUtils.isNotBlank(retentionPeriodDuration.getText())) { - disposalSchedule.setRetentionPeriodDuration(Integer.parseInt(retentionPeriodDuration.getText())); - } - } - } - return disposalSchedule; - } - - public void setDisposalSchedule(DisposalSchedule disposalSchedule) { - this.title.setText(disposalSchedule.getTitle()); - this.description.setText(disposalSchedule.getDescription()); - this.mandate.setText(disposalSchedule.getMandate()); - this.notes.setText(disposalSchedule.getScopeNotes()); - } - - private DisposalActionCode getDisposalActionCode(String string) { - switch (string) { - case "RETAIN_PERMANENTLY": - return DisposalActionCode.RETAIN_PERMANENTLY; - case "DESTROY": - return DisposalActionCode.DESTROY; - default: - return DisposalActionCode.REVIEW; - } - } - - private RetentionPeriodIntervalCode getRetentionPeriodIntervalCode(String string) { - switch (string) { - case "NO_RETENTION_PERIOD": - return RetentionPeriodIntervalCode.NO_RETENTION_PERIOD; - case "YEARS": - return RetentionPeriodIntervalCode.YEARS; - case "MONTHS": - return RetentionPeriodIntervalCode.MONTHS; - case "WEEKS": - return RetentionPeriodIntervalCode.WEEKS; - case "DAYS": - return RetentionPeriodIntervalCode.DAYS; - default: - return null; - } - } - - /** - * Is disposal schedule data panel valid - * - * @return true if valid - */ - public boolean isValid() { - List errorList = new ArrayList<>(); - - if (StringUtils.isBlank(title.getText())) { - title.addStyleName(IS_WRONG); - titleError.setText(messages.mandatoryField()); - titleError.setVisible(true); - Window.scrollTo(title.getAbsoluteLeft(), title.getAbsoluteTop()); - errorList.add(messages.isAMandatoryField(messages.disposalScheduleTitle())); - } else { - title.removeStyleName(IS_WRONG); - titleError.setVisible(false); - } - - if (!editMode) { - if (disposalActions.getSelectedIndex() == 0) { - disposalActions.addStyleName(IS_WRONG); - disposalActionsError.setText(messages.mandatoryField()); - disposalActionsError.setVisible(true); - Window.scrollTo(disposalActions.getAbsoluteLeft(), disposalActions.getAbsoluteTop()); - errorList.add(messages.isAMandatoryField(messages.disposalScheduleActionCol())); - } else { - disposalActions.removeStyleName(IS_WRONG); - disposalActionsError.setVisible(false); - } - - if (retentionTriggerElementIdList.isVisible()) { - if (retentionTriggerElementIdList.getSelectedIndex() == 0) { - retentionTriggerElementIdList.addStyleName(IS_WRONG); - retentionTriggerElementIdError.setText(messages.mandatoryField()); - retentionTriggerElementIdError.setVisible(true); - Window.scrollTo(retentionTriggerElementIdList.getAbsoluteLeft(), - retentionTriggerElementIdList.getAbsoluteTop()); - errorList.add(messages.isAMandatoryField(messages.disposalScheduleRetentionTriggerElementId())); - } else { - retentionTriggerElementIdList.removeStyleName(IS_WRONG); - retentionTriggerElementIdError.setVisible(false); - } - } else { - retentionTriggerElementIdList.removeStyleName(IS_WRONG); - retentionTriggerElementIdError.setVisible(false); - retentionPeriodDuration.removeStyleName(IS_WRONG); - retentionPeriodDurationError.setVisible(false); - } - - if (retentionPeriodDurationLabel.isVisible()) { - if (retentionPeriodIntervals.getSelectedIndex() == 0) { - retentionPeriodIntervals.addStyleName(IS_WRONG); - retentionPeriodDuration.addStyleName(IS_WRONG); - retentionPeriodDurationError.setText(messages.mandatoryField()); - retentionPeriodDurationError.setVisible(true); - Window.scrollTo(retentionPeriodIntervals.getAbsoluteLeft(), retentionPeriodIntervals.getAbsoluteTop()); - errorList.add(messages.isAMandatoryField(messages.disposalScheduleRetentionPeriodInterval())); - } else if (RetentionPeriodIntervalCode.NO_RETENTION_PERIOD.toString() - .equals(retentionPeriodIntervals.getSelectedValue())) { - retentionPeriodIntervals.removeStyleName(IS_WRONG); - retentionPeriodDuration.removeStyleName(IS_WRONG); - retentionPeriodDurationError.setVisible(false); - } else { - if (!isNumberValid(retentionPeriodDuration.getText())) { - retentionPeriodDuration.addStyleName(IS_WRONG); - retentionPeriodDurationError.setText(messages.numberIsRequired()); - retentionPeriodIntervals.removeStyleName(IS_WRONG); - retentionPeriodDurationError.setVisible(true); - Window.scrollTo(retentionPeriodIntervals.getAbsoluteLeft(), retentionPeriodIntervals.getAbsoluteTop()); - errorList.add(messages.isAMandatoryField(messages.disposalScheduleRetentionPeriodInterval())); - } else { - retentionPeriodDuration.removeStyleName(IS_WRONG); - retentionPeriodIntervals.removeStyleName(IS_WRONG); - retentionPeriodDurationError.setVisible(false); - } - } - } else { - retentionPeriodDuration.removeStyleName(IS_WRONG); - retentionPeriodIntervals.removeStyleName(IS_WRONG); - retentionPeriodDurationError.setVisible(false); - } - } - - checked = true; - - if (!errorList.isEmpty()) { - errors.setVisible(true); - StringBuilder errorString = new StringBuilder(); - for (String error : errorList) { - errorString.append("").append(error).append(""); - errorString.append("
"); - } - errors.setHTML(errorString.toString()); - } else { - errors.setVisible(false); - } - - return errorList.isEmpty(); - } - - private boolean isNumberValid(String string) { - boolean isNumber = true; - try { - int intNum = Integer.parseInt(string); - if (intNum <= 0) { - isNumber = false; - } - } catch (NumberFormatException e) { - isNumber = false; - } - return isNumber; - } - - public void clear() { - title.setText(""); - description.setText(""); - mandate.setText(""); - notes.setText(""); - disposalActions.setSelectedIndex(0); - retentionTriggerElementIdList.setSelectedIndex(0); - retentionPeriodIntervals.setSelectedIndex(0); - retentionPeriodDuration.setText(""); - } - - public boolean isEditMode() { - return editMode; - } - - 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 DisposalSchedule getValue() { - return getDisposalSchedule(); - } - - interface MyUiBinder extends UiBinder { - } -} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/DisposalScheduleDataPanel.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/DisposalScheduleDataPanel.ui.xml deleted file mode 100644 index deca54d904..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/DisposalScheduleDataPanel.ui.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - .section { - margin-top: 20px; - } - - .group { - display: flex; - } - - - - - - - - - - - * - - - - - - - - - - - - - - - - - - - - - - * - - - - - - - * - - - - - - - * - - - - - - - - - - - - - - - - - - diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/EditDisposalSchedule.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/EditDisposalSchedule.java index 5bba92fedf..0b78589ebf 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/EditDisposalSchedule.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/EditDisposalSchedule.java @@ -9,27 +9,33 @@ import java.util.List; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.FocusPanel; +import org.roda.core.data.v2.disposal.rule.DisposalRule; import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; import org.roda.core.data.v2.disposal.schedule.DisposalScheduleState; +import org.roda.wui.client.common.NavigationToolbar; +import org.roda.wui.client.common.NoActionsToolbar; +import org.roda.wui.client.common.TitlePanel; import org.roda.wui.client.common.UserLogin; import org.roda.wui.client.common.utils.AsyncCallbackUtils; import org.roda.wui.client.disposal.policy.DisposalPolicy; +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.client.disposal.schedule.data.panels.DisposalScheduleDataPanel; 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; +import org.roda.wui.common.client.widgets.Toast; /** * @author Tiago Fraga @@ -73,65 +79,59 @@ public String getHistoryToken() { return "edit_disposal_schedule"; } }; + private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static EditDisposalSchedule instance = null; + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - private static EditDisposalSchedule.MyUiBinder uiBinder = GWT.create(EditDisposalSchedule.MyUiBinder.class); @UiField - Button buttonApply; + FocusPanel keyboardFocus; @UiField - Button buttonCancel; - @UiField(provided = true) - DisposalScheduleDataPanel disposalScheduleDataPanel; - private DisposalSchedule disposalSchedule; - - public EditDisposalSchedule() { - initWidget(uiBinder.createAndBindUi(this)); - } + NavigationToolbar navigationToolbar; + @UiField + NoActionsToolbar actionsToolbar; + @UiField + TitlePanel title; + @UiField + FlowPanel disposalScheduleDataPanel; public EditDisposalSchedule(DisposalSchedule disposalSchedule) { - this.disposalSchedule = disposalSchedule; - this.disposalScheduleDataPanel = new DisposalScheduleDataPanel(disposalSchedule, true); - initWidget(uiBinder.createAndBindUi(this)); - } - - public static EditDisposalSchedule getInstance() { - if (instance == null) { - instance = new EditDisposalSchedule(); - } - return instance; - } - @UiHandler("buttonApply") - void buttonApplyHandler(ClickEvent e) { - if (disposalScheduleDataPanel.isChanged() && disposalScheduleDataPanel.isValid()) { - DisposalSchedule disposalScheduleUpdated = disposalScheduleDataPanel.getDisposalSchedule(); - disposalSchedule.setTitle(disposalScheduleUpdated.getTitle()); - disposalSchedule.setMandate(disposalScheduleUpdated.getMandate()); - disposalSchedule.setDescription(disposalScheduleUpdated.getDescription()); - disposalSchedule.setScopeNotes(disposalScheduleUpdated.getScopeNotes()); + // 1. Create the panel and keep a reference + DisposalScheduleDataPanel dataPanel = new DisposalScheduleDataPanel(disposalSchedule, true); + disposalScheduleDataPanel.add(dataPanel); + dataPanel.setSaveHandler(() -> { Services services = new Services("Update disposal schedule", "update"); - services.disposalScheduleResource(s -> s.updateDisposalSchedule(disposalSchedule)) - .whenComplete((updatedDisposalSchedule, throwable) -> { + services.disposalScheduleResource(s -> s.updateDisposalSchedule(dataPanel.getValue())) + .whenComplete((updated, throwable) -> { if (throwable != null) { AsyncCallbackUtils.defaultFailureTreatment(throwable); } else { - HistoryUtils.newHistory(ShowDisposalSchedule.RESOLVER, updatedDisposalSchedule.getId()); + Toast.showInfo(messages.disposalScheduleTitle(), messages.disposalScheduleSuccessfullyUpdated()); + HistoryUtils.newHistory(ShowDisposalSchedule.RESOLVER, updated.getId()); } }); - } else { - HistoryUtils.newHistory(ShowDisposalSchedule.RESOLVER, disposalSchedule.getId()); - } - } + }); + + dataPanel.setCancelHandler(() -> HistoryUtils.newHistory(ShowDisposalSchedule.RESOLVER, disposalSchedule.getId())); - @UiHandler("buttonCancel") - void buttonCancelHandler(ClickEvent e) { - HistoryUtils.newHistory(ShowDisposalSchedule.RESOLVER, disposalSchedule.getId()); + navigationToolbar.withoutButtons().build(); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getEditDisposalScheduleBreadcrumbs(disposalSchedule)); + + actionsToolbar.setLabel(messages.showDisposalRuleTitle()); + + // 3. Pass the shared object + actionsToolbar.build(); + + title.setText(disposalSchedule.getTitle()); + title.setIconClass("DisposalSchedule"); + title.addStyleName("mb-20"); + + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse"); } interface MyUiBinder extends UiBinder { } - } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/EditDisposalSchedule.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/EditDisposalSchedule.ui.xml index 428787e514..e35441ffea 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/EditDisposalSchedule.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/EditDisposalSchedule.ui.xml @@ -1,32 +1,21 @@ - + - + + + - - - - - - + - - - - - - - - - - - - - + + + + - + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/ShowDisposalSchedule.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/ShowDisposalSchedule.java index d141006ebe..a24e88608f 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/ShowDisposalSchedule.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/ShowDisposalSchedule.java @@ -7,50 +7,32 @@ */ package org.roda.wui.client.disposal.schedule; +import java.util.HashMap; import java.util.List; +import java.util.Map; -import org.roda.core.data.common.RodaConstants; -import org.roda.core.data.v2.disposal.rule.DisposalRule; -import org.roda.core.data.v2.disposal.rule.DisposalRules; +import com.google.gwt.user.client.ui.FocusPanel; import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; -import org.roda.core.data.v2.disposal.schedule.DisposalScheduleState; -import org.roda.core.data.v2.disposal.schedule.RetentionPeriodIntervalCode; -import org.roda.core.data.v2.index.filter.Filter; -import org.roda.core.data.v2.index.filter.SimpleFilterParameter; -import org.roda.core.data.v2.ip.AIPState; -import org.roda.core.data.v2.ip.IndexedAIP; -import org.roda.wui.client.common.DisposalPolicySummaryPanel; +import org.roda.wui.client.browse.tabs.DisposalScheduleTabs; +import org.roda.wui.client.common.DisposalScheduleActionsToolbar; +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.DisposalScheduleActions; -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 org.roda.wui.client.common.actions.Actionable; import org.roda.wui.client.common.utils.AsyncCallbackUtils; -import org.roda.wui.client.common.utils.HtmlSnippetUtils; -import org.roda.wui.client.common.utils.PermissionClientUtils; import org.roda.wui.client.disposal.policy.DisposalPolicy; -import org.roda.wui.client.services.DisposalRuleRestService; +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 org.roda.wui.common.client.widgets.Toast; 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.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.HTML; -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; @@ -59,13 +41,22 @@ * @author Tiago Fraga */ public class ShowDisposalSchedule extends Composite { - private static final ClientMessages messages = GWT.create(ClientMessages.class); - private static ShowDisposalSchedule instance = null; public static final HistoryResolver RESOLVER = new HistoryResolver() { @Override public void resolve(List historyTokens, final AsyncCallback callback) { - getInstance().resolve(historyTokens, callback); + if (historyTokens.size() == 1) { + Services services = new Services("Retrieve disposal schedule", "get"); + services.disposalScheduleResource(s -> s.retrieveDisposalSchedule(historyTokens.get(0))) + .whenComplete((schedule, throwable) -> { + if (throwable != null) { + AsyncCallbackUtils.defaultFailureTreatment(throwable); + } else { + ShowDisposalSchedule panel = new ShowDisposalSchedule(schedule); + callback.onSuccess(panel); + } + }); + } } @Override @@ -83,249 +74,83 @@ public String getHistoryToken() { return "disposal_schedule"; } }; - private static ShowDisposalSchedule.MyUiBinder uiBinder = GWT.create(ShowDisposalSchedule.MyUiBinder.class); - @UiField - TitlePanel title; - @UiField - DisposalPolicySummaryPanel usedInRulePanel; - @UiField - Label disposalScheduleId; - @UiField - Label dateCreated, dateUpdated; - @UiField - Label descriptionLabel; - @UiField - HTML descriptionValue; - @UiField - Label mandateLabel; - @UiField - HTML mandateValue; - @UiField - Label notesLabel; - @UiField - HTML notesValue; - @UiField - Label disposalActionsLabel; - @UiField - HTML disposalActionsValue; - @UiField - Label retentionTriggersLabel; - @UiField - HTML retentionTriggersValue; + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); - // disposal actions - @UiField - Label retentionPeriodLabel; @UiField - HTML retentionPeriodValue; + FocusPanel keyboardFocus; - // retention triggers @UiField - Label stateLabel; - @UiField - HTML stateValue; + NavigationToolbar navigationToolbar; - // retention period - @UiField - FlowPanel buttonsPanel; @UiField - FlowPanel aipListTitle; - @UiField - SimplePanel aipsListCard; - private DisposalSchedule disposalSchedule; - private DisposalRules disposalRules; - - public ShowDisposalSchedule() { - this.disposalSchedule = new DisposalSchedule(); - } - - public ShowDisposalSchedule(final DisposalSchedule disposalSchedule, final DisposalRules disposalRules) { - instance = this; - this.disposalSchedule = disposalSchedule; - this.disposalRules = disposalRules; - - initWidget(uiBinder.createAndBindUi(this)); - initElements(); - initButtons(); - } - - public static ShowDisposalSchedule getInstance() { - if (instance == null) { - instance = new ShowDisposalSchedule(); - } - return instance; - } - - public void initElements() { - title.setText(disposalSchedule.getTitle()); - - if (isScheduleInRule()) { - usedInRulePanel.setIcon("fas fa-info-circle"); - usedInRulePanel.setText(messages.disposalScheduleUsedInRule()); - } else { - usedInRulePanel.setVisible(false); - } + DisposalScheduleActionsToolbar actionsToolbar; - disposalScheduleId.setText(messages.disposalScheduleIdentifier() + ": " + disposalSchedule.getId()); + @UiField + TitlePanel title; - if (disposalSchedule.getCreatedOn() != null && StringUtils.isNotBlank(disposalSchedule.getCreatedBy())) { - dateCreated.setText(messages.dateCreated(Humanize.formatDateTime(disposalSchedule.getCreatedOn()), - disposalSchedule.getCreatedBy())); - } + @UiField + DisposalScheduleTabs browseTab; - if (disposalSchedule.getUpdatedOn() != null && StringUtils.isNotBlank(disposalSchedule.getUpdatedBy())) { - dateUpdated.setText(messages.dateUpdated(Humanize.formatDateTime(disposalSchedule.getUpdatedOn()), - disposalSchedule.getUpdatedBy())); + 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(); + } } + }; + private DisposalSchedule disposalSchedule; - descriptionValue.setHTML(SafeHtmlUtils.fromString(disposalSchedule.getDescription())); - descriptionLabel.setVisible(StringUtils.isNotBlank(disposalSchedule.getDescription())); - - mandateValue.setHTML(SafeHtmlUtils.fromString(disposalSchedule.getMandate())); - mandateLabel.setVisible(StringUtils.isNotBlank(disposalSchedule.getMandate())); - - notesValue.setHTML(SafeHtmlUtils.fromString(disposalSchedule.getScopeNotes())); - notesLabel.setVisible(StringUtils.isNotBlank(disposalSchedule.getScopeNotes())); - - disposalActionsValue.setHTML(messages.disposalScheduleAction(disposalSchedule.getActionCode().toString())); - disposalActionsLabel.setVisible(StringUtils.isNotBlank(disposalSchedule.getActionCode().toString())); + public ShowDisposalSchedule(final DisposalSchedule schedule) { + this.disposalSchedule = schedule; - retentionTriggersValue.setHTML( - DisposalScheduleUtils.getI18nRetentionTriggerIdentifier(disposalSchedule.getRetentionTriggerElementId())); - retentionTriggersLabel.setVisible(StringUtils.isNotBlank(disposalSchedule.getRetentionTriggerElementId())); + initWidget(uiBinder.createAndBindUi(this)); - if (disposalSchedule.getRetentionPeriodIntervalCode() == null) { - retentionPeriodValue.setHTML(""); - retentionPeriodLabel.setVisible(false); - } else if (disposalSchedule.getRetentionPeriodIntervalCode() - .equals(RetentionPeriodIntervalCode.NO_RETENTION_PERIOD)) { - String retentionPeriod = messages.retentionPeriod(0, - disposalSchedule.getRetentionPeriodIntervalCode().toString()); - retentionPeriodValue.setHTML(retentionPeriod); - retentionPeriodLabel.setVisible(true); - } else { - String retentionPeriod = messages.retentionPeriod(disposalSchedule.getRetentionPeriodDuration(), - disposalSchedule.getRetentionPeriodIntervalCode().toString()); - retentionPeriodValue.setHTML(retentionPeriod); - retentionPeriodLabel.setVisible(true); - } + initHandlers(schedule); - stateValue.setHTML(HtmlSnippetUtils.getDisposalScheduleStateHtml(disposalSchedule)); + navigationToolbar.withObject(schedule).build(); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getDisposalScheduleBreadcrumbs(schedule)); - // Records with this schedule + actionsToolbar.setLabel(messages.showDisposalScheduleTitle()); + actionsToolbar.setObjectAndBuild(schedule, null, handler); - if (disposalSchedule.getState().equals(DisposalScheduleState.ACTIVE) - && PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_FIND_AIP)) { - Label aipTitle = new Label(); - aipTitle.addStyleName("h5"); - aipTitle.setText(messages.disposalScheduleListAips()); - aipListTitle.add(aipTitle); + title.setText(schedule.getTitle()); + title.setIconClass("DisposalSchedule"); - ListBuilder aipsListBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), - new AsyncTableCellOptions<>(IndexedAIP.class, "ShowDisposalSchedule_aips") - .withFilter( - new Filter(new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_SCHEDULE_ID, disposalSchedule.getId()), - new SimpleFilterParameter(RodaConstants.AIP_STATE, AIPState.ACTIVE.name()))) - .withActionable(DisposalScheduleActions.get(disposalSchedule.getId())).withSummary(messages.listOfAIPs()) - .bindOpener()); + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse"); - SearchWrapper aipsSearchWrapper = new SearchWrapper(false).createListAndSearchPanel(aipsListBuilder); - aipsListCard.setWidget(aipsSearchWrapper); - aipsListCard.setVisible(true); - } else { - aipsListCard.setVisible(false); - } + browseTab.init(schedule, handler); } - public void initButtons() { - - if (disposalSchedule.getState().equals(DisposalScheduleState.ACTIVE)) { - Button editScheduleBtn = new Button(); - editScheduleBtn.addStyleName("btn btn-block btn-edit"); - editScheduleBtn.setText(messages.editButton()); - if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_UPDATE_DISPOSAL_SCHEDULE)) { - editScheduleBtn.addClickHandler( - clickEvent -> HistoryUtils.newHistory(EditDisposalSchedule.RESOLVER, disposalSchedule.getId())); - } - buttonsPanel.add(editScheduleBtn); - - if (!isScheduleInRule() && disposalSchedule.getFirstTimeUsed() != null - && PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_DELETE_DISPOSAL_SCHEDULE)) { - // Change the state to inactive - Button deactivateScheduleButton = new Button(); - deactivateScheduleButton.addStyleName("btn btn-block btn-danger btn-ban"); - deactivateScheduleButton.setText(messages.deactivateButton()); - deactivateScheduleButton.addClickHandler(clickEvent -> { - disposalSchedule.setState(DisposalScheduleState.INACTIVE); - Services services = new Services("Update disposal schedule", "update"); - services.disposalScheduleResource(s -> s.updateDisposalSchedule(disposalSchedule)) - .whenComplete((disposalSchedule1, throwable) -> { - if (throwable != null) { - AsyncCallbackUtils.defaultRestErrorTreatment(throwable); - } else { - HistoryUtils.newHistory(DisposalPolicy.RESOLVER); - } - }); - }); - buttonsPanel.add(deactivateScheduleButton); - } - - if (!isScheduleInRule() && disposalSchedule.getFirstTimeUsed() == null - && PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_DELETE_DISPOSAL_SCHEDULE)) { - // Delete the disposal schedule - Button deleteDisposalSchedule = new Button(); - deleteDisposalSchedule.addStyleName("btn btn-block btn-danger btn-delete"); - deleteDisposalSchedule.setText(messages.removeButton()); - deleteDisposalSchedule.addClickHandler(clickEvent -> { - Services services = new Services("Delete disposal schedule", "delete"); - services.disposalScheduleResource(s -> s.deleteDisposalSchedule(disposalSchedule.getId())) - .whenComplete((unused, throwable) -> { - if (throwable != null) { - AsyncCallbackUtils.defaultRestErrorTreatment(throwable); - } else { - Toast.showInfo(messages.disposalSchedulesTitle(), - messages.deleteDisposalSchedule(disposalSchedule.getTitle())); - HistoryUtils.newHistory(DisposalPolicy.RESOLVER); - } - }); + private void initHandlers(DisposalSchedule schedule) { + handlers.put(Actionable.ActionImpact.DESTROYED, + () -> HistoryUtils.newHistory(DisposalPolicy.RESOLVER.getHistoryPath())); + + // Use the DOM Swap refresh method instead of HistoryUtils + handlers.put(Actionable.ActionImpact.UPDATED, () -> { + Services services = new Services("Retrieve updated disposal schedule", "get"); + services.disposalScheduleResource(s -> s.retrieveDisposalSchedule(disposalSchedule.getId())) + .whenComplete((updatedSchedule, throwable) -> { + if (throwable != null) { + AsyncCallbackUtils.defaultFailureTreatment(throwable); + } else { + // 1. Update local reference + this.disposalSchedule = updatedSchedule; + + // 2. Update the main TitlePanel + title.setText(updatedSchedule.getTitle()); + + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getDisposalScheduleBreadcrumbs(updatedSchedule)); + + // 4. Re-initialize tabs (this recreates the details panel with the new + // schedule) + browseTab.init(updatedSchedule, handler); + } }); - buttonsPanel.add(deleteDisposalSchedule); - } - } - - Button backBtn = new Button(); - backBtn.setText(messages.backButton()); - backBtn.addStyleName("btn btn-block btn-default btn-times-circle"); - backBtn.addClickHandler(clickEvent -> HistoryUtils.newHistory(DisposalPolicy.RESOLVER)); - buttonsPanel.add(backBtn); - - } - - private boolean isScheduleInRule() { - boolean ret = false; - for (DisposalRule rule : disposalRules.getObjects()) { - if (rule.getDisposalScheduleId().equals(disposalSchedule.getId())) { - ret = true; - break; - } - } - return ret; - } - - void resolve(List historyTokens, final AsyncCallback callback) { - if (historyTokens.size() == 1) { - Services services = new Services("Retrieve disposal schedule", "get"); - services.disposalScheduleResource(s -> s.retrieveDisposalSchedule(historyTokens.get(0))) - .thenCompose(schedule -> services.disposalRuleResource(DisposalRuleRestService::listDisposalRules) - .whenComplete((rules, throwable) -> { - if (throwable != null) { - AsyncCallbackUtils.defaultFailureTreatment(throwable); - } else { - ShowDisposalSchedule panel = new ShowDisposalSchedule(schedule, rules); - callback.onSuccess(panel); - } - })); - } + }); } interface MyUiBinder extends UiBinder { diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/ShowDisposalSchedule.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/ShowDisposalSchedule.ui.xml index 69d879fa1a..30b1b14673 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/ShowDisposalSchedule.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/ShowDisposalSchedule.ui.xml @@ -1,87 +1,23 @@ - + - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/data/panels/DisposalScheduleDataPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/data/panels/DisposalScheduleDataPanel.java new file mode 100644 index 0000000000..fb5bee80f8 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/data/panels/DisposalScheduleDataPanel.java @@ -0,0 +1,260 @@ +package org.roda.wui.client.disposal.schedule.data.panels; + +import org.roda.core.data.v2.common.Pair; +import org.roda.core.data.v2.disposal.schedule.DisposalActionCode; +import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; +import org.roda.core.data.v2.disposal.schedule.RetentionPeriodIntervalCode; +import org.roda.wui.client.common.forms.GenericDataForm; +import org.roda.wui.client.common.forms.GenericDataPanel; +import org.roda.wui.client.disposal.schedule.DisposalScheduleUtils; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.event.logical.shared.HasValueChangeHandlers; +import com.google.gwt.event.logical.shared.ValueChangeHandler; +import com.google.gwt.event.shared.HandlerRegistration; +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.ListBox; +import com.google.gwt.user.client.ui.TextBox; + +import config.i18n.client.ClientMessages; + +/** + * @author Tiago Fraga + */ +public class DisposalScheduleDataPanel extends Composite + implements GenericDataPanel, HasValueChangeHandlers { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + + private final GenericDataForm form; + private final boolean editMode; + + private final Button saveButton; + private final Button cancelButton; + + public DisposalScheduleDataPanel(DisposalSchedule disposalSchedule, boolean editMode) { + this.editMode = editMode; + this.form = new GenericDataForm<>(); + + if (editMode) { + initEditMode(disposalSchedule); + } else { + initCreateMode(disposalSchedule); + } + + // 1. Initialize Buttons + saveButton = new Button(messages.saveButton()); + saveButton.addStyleName("btn btn-primary btn-play"); + + cancelButton = new Button(messages.cancelButton()); + cancelButton.addStyleName("btn btn-link"); + + // 2. Wrap buttons in a FlowPanel for spacing + FlowPanel actionsPanel = new FlowPanel(); + actionsPanel.addStyleName("alignButtonsPanel"); + actionsPanel.add(saveButton); + actionsPanel.add(cancelButton); + + // 3. Inject the buttons at the bottom of the generic form + form.addCustomWidget(actionsPanel); + + // Initialize the composite using the generic form as the root widget + initWidget(form); + } + + private void initEditMode(DisposalSchedule disposalSchedule) { + form.addTextField(messages.disposalScheduleTitle(), DisposalSchedule::getTitle, DisposalSchedule::setTitle, true); + form.addTextField(messages.disposalScheduleDescription(), DisposalSchedule::getDescription, + DisposalSchedule::setDescription, false); + form.addTextArea(messages.disposalScheduleMandate(), DisposalSchedule::getMandate, DisposalSchedule::setMandate, + false); + form.addTextArea(messages.disposalScheduleNotes(), DisposalSchedule::getScopeNotes, DisposalSchedule::setScopeNotes, + false); + + form.addReadOnlyField(messages.disposalActionLabel(), + s -> messages.disposalScheduleActionCode(s.getActionCode().toString()), false); + + if (!DisposalActionCode.RETAIN_PERMANENTLY.equals(disposalSchedule.getActionCode())) { + form.addReadOnlyField(messages.disposalScheduleRetentionTriggerElementId(), + s -> DisposalScheduleUtils.getI18nRetentionTriggerIdentifier(s.getRetentionTriggerElementId()), false); + form.addReadOnlyField(messages.disposalScheduleRetentionPeriodDuration(), s -> { + if (!DisposalActionCode.RETAIN_PERMANENTLY.equals(s.getActionCode())) { + String retentionPeriod; + if (s.getRetentionPeriodIntervalCode().equals(RetentionPeriodIntervalCode.NO_RETENTION_PERIOD)) { + retentionPeriod = messages.retentionPeriod(0, s.getRetentionPeriodIntervalCode().toString()); + } else { + retentionPeriod = messages.retentionPeriod(s.getRetentionPeriodDuration(), + s.getRetentionPeriodIntervalCode().toString()); + } + return retentionPeriod; + } else { + return ""; + } + }, false); + } + + form.setModel(disposalSchedule); + } + + private void initCreateMode(DisposalSchedule disposalSchedule) { + form.addTextField(messages.disposalScheduleTitle(), DisposalSchedule::getTitle, DisposalSchedule::setTitle, true); + form.addTextField(messages.disposalScheduleDescription(), DisposalSchedule::getDescription, + DisposalSchedule::setDescription, false); + form.addTextArea(messages.disposalScheduleMandate(), DisposalSchedule::getMandate, DisposalSchedule::setMandate, + false); + form.addTextArea(messages.disposalScheduleNotes(), DisposalSchedule::getScopeNotes, DisposalSchedule::setScopeNotes, + false); + + // 4. Actions (ListBox) + ListBox actionList = new ListBox(); + for (DisposalActionCode code : DisposalActionCode.values()) { + actionList.addItem(messages.disposalScheduleAction(code.name()), code.name()); + } + FlowPanel actionRow = form.addListBox(messages.disposalActionLabel(), actionList, + s -> s.getActionCode() != null ? s.getActionCode().name() : "", + (s, val) -> s.setActionCode(getDisposalActionCode(val)), true); + + // 5. Trigger Element (ListBox) + ListBox triggerList = new ListBox(); + for (Pair p : DisposalScheduleUtils.getElementsFromConfig()) { + triggerList.addItem(p.getSecond(), p.getFirst()); + } + FlowPanel triggerRow = form.addListBox(messages.disposalScheduleRetentionTriggerElementId(), triggerList, + DisposalSchedule::getRetentionTriggerElementId, DisposalSchedule::setRetentionTriggerElementId, true); + + // 6. Interval (ListBox) + ListBox intervalList = new ListBox(); + for (RetentionPeriodIntervalCode code : RetentionPeriodIntervalCode.values()) { + intervalList.addItem(messages.disposalScheduleRetentionPeriodIntervalValue(code.name()), code.name()); + } + FlowPanel intervalRow = form.addListBox(messages.disposalScheduleRetentionPeriodInterval(), intervalList, + s -> s.getRetentionPeriodIntervalCode() != null ? s.getRetentionPeriodIntervalCode().name() : "", + (s, val) -> s.setRetentionPeriodIntervalCode(getRetentionPeriodIntervalCode(val)), true); + + // 7. Duration (TextBox with Number Validation) + TextBox durationBox = new TextBox(); + FlowPanel durationRow = form.addTextBoxBase(messages.disposalScheduleRetentionPeriodDuration(), durationBox, + s -> s.getRetentionPeriodDuration() != null ? String.valueOf(s.getRetentionPeriodDuration()) : "", (s, val) -> { + if (val != null && !val.isEmpty()) + s.setRetentionPeriodDuration(Integer.parseInt(val)); + else + s.setRetentionPeriodDuration(null); + }, true, false, "^[1-9]\\d*$", messages.numberIsRequired()); + + // --- DEPENDENCY VISIBILITY LOGIC --- + + actionRow.setVisible(!editMode); + + Runnable evaluateVisibility = () -> { + String actionVal = actionList.getSelectedValue(); + + // If action is RETAIN PERMANENTLY, hide the downstream fields + if (DisposalActionCode.RETAIN_PERMANENTLY.name().equals(actionVal)) { + triggerRow.setVisible(false); + intervalRow.setVisible(false); + durationRow.setVisible(false); + + // Reset the UI fields so they are clean if the user switches back + if (triggerList.getItemCount() > 0) triggerList.setSelectedIndex(0); + if (intervalList.getItemCount() > 0) intervalList.setSelectedIndex(0); + durationBox.setText(""); + } else { + triggerRow.setVisible(true); + intervalRow.setVisible(true); + durationRow.setVisible(true); + + String intervalVal = intervalList.getSelectedValue(); + if (RetentionPeriodIntervalCode.NO_RETENTION_PERIOD.name().equals(intervalVal)) { + durationBox.setEnabled(false); + durationBox.setText(""); + durationBox.addStyleName("wui-cursor-not-allowed"); + } else { + durationBox.setEnabled(true); + durationBox.removeStyleName("wui-cursor-not-allowed"); + } + } + }; + + // Attach listeners + actionList.addChangeHandler(event -> evaluateVisibility.run()); + intervalList.addChangeHandler(event -> evaluateVisibility.run()); + + if (disposalSchedule != null) { + form.setModel(disposalSchedule); + if (actionList.getItemCount() > 0) { + evaluateVisibility.run(); + } + } + } + + private DisposalActionCode getDisposalActionCode(String string) { + if (string == null || string.isEmpty()) return null; + switch (string) { + case "RETAIN_PERMANENTLY": + return DisposalActionCode.RETAIN_PERMANENTLY; + case "DESTROY": + return DisposalActionCode.DESTROY; + default: + return DisposalActionCode.REVIEW; + } + } + + private RetentionPeriodIntervalCode getRetentionPeriodIntervalCode(String string) { + if (string == null || string.isEmpty()) return null; + switch (string) { + case "NO_RETENTION_PERIOD": + return RetentionPeriodIntervalCode.NO_RETENTION_PERIOD; + case "YEARS": + return RetentionPeriodIntervalCode.YEARS; + case "MONTHS": + return RetentionPeriodIntervalCode.MONTHS; + case "WEEKS": + return RetentionPeriodIntervalCode.WEEKS; + case "DAYS": + return RetentionPeriodIntervalCode.DAYS; + default: + return null; + } + } + + public void setSaveHandler(Runnable onSave) { + saveButton.addClickHandler(event -> { + if (isValid()) { + onSave.run(); + } + }); + } + + public void setCancelHandler(Runnable onCancel) { + cancelButton.addClickHandler(event -> onCancel.run()); + } + + @Override + public DisposalSchedule getValue() { + DisposalSchedule schedule = form.getValue(); + + // GUARANTEE CLEAN MODEL: If RETAIN_PERMANENTLY is selected, actively clear out the dependent fields + if (DisposalActionCode.RETAIN_PERMANENTLY.equals(schedule.getActionCode())) { + schedule.setRetentionTriggerElementId(null); + schedule.setRetentionPeriodIntervalCode(null); + schedule.setRetentionPeriodDuration(null); + } + + return schedule; + } + + @Override + public boolean isValid() { + return form.isValid(); + } + + public boolean isChanged() { + return form.isChanged(); + } + + @Override + public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) { + return form.addValueChangeHandler(handler); + } +} \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/tabs/DisposalScheduleDetailsPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/tabs/DisposalScheduleDetailsPanel.java new file mode 100644 index 0000000000..7f8b89a77a --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/tabs/DisposalScheduleDetailsPanel.java @@ -0,0 +1,120 @@ +package org.roda.wui.client.disposal.schedule.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.disposal.schedule.DisposalActionCode; +import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; +import org.roda.core.data.v2.disposal.schedule.RetentionPeriodIntervalCode; +import org.roda.wui.client.common.ActionsToolbar; +import org.roda.wui.client.common.actions.Actionable; +import org.roda.wui.client.common.actions.DisposalScheduleAction; +import org.roda.wui.client.common.actions.DisposalScheduleToolbarActions; +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.disposal.schedule.DisposalScheduleUtils; + +import java.util.List; + +/** + * @author Miguel Guimarães + */ + +public class DisposalScheduleDetailsPanel 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 DisposalSchedule schedule; + private AsyncCallback localCallback; + + public DisposalScheduleDetailsPanel(DisposalSchedule disposalSchedule, + AsyncCallback actionCallback) { + initWidget(uiBinder.createAndBindUi(this)); + + // Promote localCallback to an instance variable so refresh() can use it + this.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); + + // Initial load + refresh(disposalSchedule); + } + + // Update the method signature to accept the new schedule + public void refresh(DisposalSchedule newSchedule) { + this.schedule = newSchedule; + + // 1. Clear out the old details + clear(); + + // 2. Re-populate text fields with the new data + init(this.schedule); + + // 3. Re-bind the actions toolbar with the new schedule object + actionsToolbar.setActionableMenu(new ActionableWidgetBuilder(DisposalScheduleToolbarActions.get()) + .withActionCallback(localCallback).buildGroupedListWithObjects(new ActionableObject<>(schedule), + List.of(DisposalScheduleAction.EDIT), List.of(DisposalScheduleAction.EDIT)), + true); + } + + private void init(DisposalSchedule schedule) { + FormUtilities.addIfNotBlank(detailsPanel, messages.disposalScheduleTitle(), schedule.getTitle()); + FormUtilities.addIfNotBlank(detailsPanel, messages.disposalScheduleDescription(), schedule.getDescription()); + FormUtilities.addIfNotBlank(detailsPanel, messages.disposalScheduleMandate(), schedule.getMandate()); + FormUtilities.addIfNotBlank(detailsPanel, messages.disposalScheduleNotes(), schedule.getScopeNotes()); + FormUtilities.addIfNotBlank(detailsPanel, messages.disposalScheduleActionCol(), + messages.disposalScheduleActionCode(schedule.getActionCode().toString())); + FormUtilities.addIfNotBlank(detailsPanel, messages.disposalScheduleRetentionTriggerElementId(), + DisposalScheduleUtils.getI18nRetentionTriggerIdentifier(schedule.getRetentionTriggerElementId())); + + if (!DisposalActionCode.RETAIN_PERMANENTLY.equals(schedule.getActionCode())) { + String retentionPeriod; + if (schedule.getRetentionPeriodIntervalCode().equals(RetentionPeriodIntervalCode.NO_RETENTION_PERIOD)) { + retentionPeriod = messages.retentionPeriod(0, schedule.getRetentionPeriodIntervalCode().toString()); + } else { + retentionPeriod = messages.retentionPeriod(schedule.getRetentionPeriodDuration(), + schedule.getRetentionPeriodIntervalCode().toString()); + } + FormUtilities.addIfNotBlank(detailsPanel, messages.disposalScheduleRetentionPeriodDuration(), retentionPeriod); + } + + FormUtilities.addIfNotBlank(detailsPanel, messages.showUserStatusLabel(), HtmlSnippetUtils.getDisposalScheduleStateHtml(schedule)); + } + + public void clear() { + detailsPanel.clear(); + } + + interface MyUiBinder extends UiBinder { + Widget createAndBindUi(DisposalScheduleDetailsPanel detailsPanel); + } + +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/tabs/DisposalScheduleDetailsPanel.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/tabs/DisposalScheduleDetailsPanel.ui.xml new file mode 100644 index 0000000000..fb3e0cf09d --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/disposal/schedule/tabs/DisposalScheduleDetailsPanel.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/ingest/transfer/IngestTransfer.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/ingest/transfer/IngestTransfer.java index f6cce8797f..eddd440812 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/ingest/transfer/IngestTransfer.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/ingest/transfer/IngestTransfer.java @@ -176,26 +176,6 @@ public void onSuccess(Actionable.ActionImpact impact) { } }; - private IngestTransfer(final TransferredResource resource) { - this.resource = resource; - - if (resource.isFile()) { - resourceSearch = new TransferredResourceSearch(); - } else { - resourceSearch = new TransferredResourceSearch("IngestTransfer_transferredResources", - new Filter(new SimpleFilterParameter(RodaConstants.TRANSFERRED_RESOURCE_PARENT_ID, resource.getRelativePath())), - TransferredResourceActions.get(resource), actionCallback); - } - - initWidget(uiBinder.createAndBindUi(this)); - - objectToolbar.setObjectAndBuild(resource, null, null); - - ingestTransferDescription.add(new HTMLWidgetWrapper("IngestTransferDescription.html")); - - draw(); - } - private IngestTransfer() { this.resource = null; 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..62c909a8b4 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 @@ -12,6 +12,10 @@ import java.util.List; import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation; +import org.roda.core.data.v2.disposal.hold.DisposalHold; +import org.roda.core.data.v2.disposal.rule.DisposalRule; +import org.roda.core.data.v2.disposal.schedule.DisposalSchedule; import org.roda.core.data.v2.ip.AIPState; import org.roda.core.data.v2.ip.DIPFile; import org.roda.core.data.v2.ip.IndexedAIP; @@ -19,14 +23,47 @@ 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.notifications.Notification; +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.disposal.DisposalConfirmations; +import org.roda.wui.client.disposal.confirmations.CreateDisposalConfirmation; +import org.roda.wui.client.disposal.confirmations.ShowDisposalConfirmation; +import org.roda.wui.client.disposal.hold.CreateDisposalHold; +import org.roda.wui.client.disposal.hold.EditDisposalHold; +import org.roda.wui.client.disposal.hold.ShowDisposalHold; +import org.roda.wui.client.disposal.rule.CreateDisposalRule; +import org.roda.wui.client.disposal.rule.EditDisposalRule; +import org.roda.wui.client.disposal.rule.ShowDisposalRule; +import org.roda.wui.client.disposal.schedule.CreateDisposalSchedule; +import org.roda.wui.client.disposal.schedule.EditDisposalSchedule; +import org.roda.wui.client.management.NotificationRegister; +import org.roda.wui.client.management.ShowLogEntry; +import org.roda.wui.client.management.ShowNotification; +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.disposal.policy.DisposalPolicy; +import org.roda.wui.client.disposal.schedule.ShowDisposalSchedule; import org.roda.wui.client.ingest.appraisal.IngestAppraisal; import org.roda.wui.client.ingest.transfer.IngestTransfer; +import org.roda.wui.client.management.members.CreateGroup; +import org.roda.wui.client.management.members.CreateUser; +import org.roda.wui.client.management.members.EditGroup; +import org.roda.wui.client.management.members.EditUser; +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; +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; +import org.roda.wui.common.client.tools.StringUtils; import org.roda.wui.common.client.widgets.Toast; import com.google.gwt.core.client.GWT; @@ -352,6 +389,66 @@ 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 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 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 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<>(); @@ -436,6 +533,196 @@ private static SafeHtml getBreadcrumbLabel(IndexedAIP aip) { return breadcrumbLabel; } + public static List getRODAMembersBreadcrumbs() { + List ret = new ArrayList<>(); + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(messages.usersAndGroupsTitle()), + messages.usersAndGroupsTitle(), MemberManagement.RESOLVER.getHistoryPath())); + + return ret; + } + + public static List getCreateUserBreadcrumbs() { + List ret = getRODAMembersBreadcrumbs(); + + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(messages.createUserTitle()), messages.createUserTitle(), + CreateUser.RESOLVER.getHistoryPath())); + + return ret; + } + + public static List getCreateGroupBreadcrumbs() { + List ret = getRODAMembersBreadcrumbs(); + + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(messages.createGroupTitle()), messages.createGroupTitle(), + CreateGroup.RESOLVER.getHistoryPath())); + + return ret; + } + + public static List getEditMemberBreadcrumbs(RODAMember member) { + List ret = getRODAMemberBreadcrumbs(member); + + if (member.isUser()) { + List path = new ArrayList<>(EditUser.RESOLVER.getHistoryPath()); + path.add(member.getId()); + + ret.add( + new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(messages.editUserTitle()), messages.editUserTitle(), path)); + } else { + List path = new ArrayList<>(EditGroup.RESOLVER.getHistoryPath()); + path.add(member.getId()); + + ret.add( + new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(messages.editGroupTitle()), messages.editGroupTitle(), path)); + } + return ret; + } + + 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.getFullName(); + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromString(label), label, path)); + } + + return ret; + } + + public static List getDisposalPolicyBreadcrumbs() { + List ret = new ArrayList<>(); + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(messages.disposalPolicyTitle()), + messages.disposalPolicyTitle(), DisposalPolicy.RESOLVER.getHistoryPath())); + + return ret; + } + + public static List getCreateDisposalScheduleBreadcrumbs() { + List ret = getDisposalPolicyBreadcrumbs(); + + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(messages.newDisposalScheduleTitle()), + messages.newDisposalScheduleTitle(), CreateDisposalSchedule.RESOLVER.getHistoryPath())); + + return ret; + } + + public static List getEditDisposalScheduleBreadcrumbs(DisposalSchedule schedule) { + List ret = getDisposalScheduleBreadcrumbs(schedule); + + List path = new ArrayList<>(EditDisposalSchedule.RESOLVER.getHistoryPath()); + path.add(schedule.getId()); + + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(messages.editDisposalScheduleTitle()), + messages.editDisposalScheduleTitle(), path)); + + return ret; + } + + public static List getDisposalScheduleBreadcrumbs(DisposalSchedule schedule) { + List ret = new ArrayList<>(); + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(messages.disposalPolicyTitle()), + messages.disposalPolicyTitle(), DisposalPolicy.RESOLVER.getHistoryPath())); + + if (schedule != null) { + StringBuilder b = new StringBuilder(); + b.append(""); + b.append(" "); + b.append(schedule.getTitle()); + SafeHtml safeHtml = SafeHtmlUtils.fromSafeConstant(b.toString()); + + List path = new ArrayList<>(ShowDisposalSchedule.RESOLVER.getHistoryPath()); + path.add(schedule.getId()); + String label = schedule.getTitle(); + ret.add(new BreadcrumbItem(safeHtml, label, path)); + } + + return ret; + } + + public static List getCreateDisposalHoldBreadcrumbs() { + List ret = getDisposalPolicyBreadcrumbs(); + + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(messages.newDisposalHoldTitle()), + messages.newDisposalHoldTitle(), CreateDisposalHold.RESOLVER.getHistoryPath())); + + return ret; + } + + public static List getDisposalHoldBreadcrumbs(DisposalHold hold) { + List ret = getDisposalPolicyBreadcrumbs(); + + if (hold != null) { + StringBuilder b = new StringBuilder(); + b.append(""); + b.append(" "); + b.append(hold.getTitle()); + SafeHtml safeHtml = SafeHtmlUtils.fromSafeConstant(b.toString()); + + List path = new ArrayList<>(ShowDisposalHold.RESOLVER.getHistoryPath()); + path.add(hold.getId()); + String label = hold.getTitle(); + ret.add(new BreadcrumbItem(safeHtml, label, path)); + } + + return ret; + } + + public static List getEditDisposalHoldBreadcrumbs(DisposalHold hold) { + List ret = getDisposalHoldBreadcrumbs(hold); + + List path = new ArrayList<>(EditDisposalHold.RESOLVER.getHistoryPath()); + path.add(hold.getId()); + + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(messages.editDisposalHoldTitle()), + messages.editDisposalHoldTitle(), path)); + + return ret; + } + + public static List getDisposalRuleBreadcrumbs(DisposalRule rule) { + List ret = getDisposalPolicyBreadcrumbs(); + + if (rule != null) { + StringBuilder b = new StringBuilder(); + b.append(""); + b.append(" "); + b.append(rule.getTitle()); + SafeHtml safeHtml = SafeHtmlUtils.fromSafeConstant(b.toString()); + + List path = new ArrayList<>(ShowDisposalRule.RESOLVER.getHistoryPath()); + path.add(rule.getId()); + String label = rule.getTitle(); + ret.add(new BreadcrumbItem(safeHtml, label, path)); + } + + return ret; + } + + public static List getEditDisposalRuleBreadcrumbs(DisposalRule rule) { + List ret = getDisposalRuleBreadcrumbs(rule); + + List path = new ArrayList<>(EditDisposalRule.RESOLVER.getHistoryPath()); + path.add(rule.getId()); + + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(messages.editDisposalRuleTitle()), + messages.editDisposalRuleTitle(), path)); + + return ret; + } + + public static List getCreateDisposalRuleBreadcrumbs() { + List ret = getDisposalPolicyBreadcrumbs(); + + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(messages.newDisposalRuleTitle()), + messages.newDisposalRuleTitle(), CreateDisposalRule.RESOLVER.getHistoryPath())); + + return ret; + } + private static String getBreadcrumbTitle(IndexedAIP aip) { String title; if (aip.getGhost()) { @@ -447,6 +734,35 @@ private static String getBreadcrumbTitle(IndexedAIP aip) { return title; } + public static List getDisposalConfirmationBreadcrumbs() { + List ret = new ArrayList<>(); + + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(messages.disposalConfirmationsTitle()), + messages.disposalConfirmationsTitle(), DisposalConfirmations.RESOLVER.getHistoryPath())); + + return ret; + } + + public static List getDisposalConfirmationBreadcrumbs(DisposalConfirmation confirmation) { + List ret = getDisposalConfirmationBreadcrumbs(); + + List path = new ArrayList<>(ShowDisposalConfirmation.RESOLVER.getHistoryPath()); + path.add(confirmation.getId()); + + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(confirmation.getTitle()), confirmation.getTitle(), path)); + + return ret; + } + + public static List getCreateDisposalConfirmationBreadcrumbs() { + List ret = getDisposalConfirmationBreadcrumbs(); + + ret.add(new BreadcrumbItem(SafeHtmlUtils.fromSafeConstant(messages.createDisposalConfirmationTitle()), + messages.createDisposalConfirmationTitle(), CreateDisposalConfirmation.RESOLVER.getHistoryPath())); + + return ret; + } + private static final List getViewItemEventsHistoryToken(String id) { return ListUtils.concat(BrowseTop.RESOLVER.getHistoryPath(), PreservationEvents.BROWSE_RESOLVER.getHistoryToken(), id); 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..96a7342213 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 @@ -22,14 +22,14 @@ import org.roda.wui.client.disposal.Disposal; import org.roda.wui.client.disposal.DisposalConfirmations; import org.roda.wui.client.disposal.DisposalDestroyedRecords; -import org.roda.wui.client.disposal.confirmations.CreateDisposalConfirmation; +import org.roda.wui.client.disposal.confirmations.OverdueRecords; import org.roda.wui.client.disposal.policy.DisposalPolicy; import org.roda.wui.client.ingest.Ingest; import org.roda.wui.client.ingest.appraisal.IngestAppraisal; 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; @@ -227,7 +227,7 @@ public void init() { createCommand(DisposalConfirmations.RESOLVER.getHistoryPath())); disposalConfirmation.addStyleName("disposal_confirmation_item"); overdueActions = disposalMenu.addItem(messages.title("overdue_actions"), - createCommand(CreateDisposalConfirmation.RESOLVER.getHistoryPath())); + createCommand(OverdueRecords.RESOLVER.getHistoryPath())); overdueActions.addStyleName("overdue_actions_item"); disposalDestroyedRecords = disposalMenu.addItem(messages.title("disposal_destroyed_records"), createCommand(DisposalDestroyedRecords.RESOLVER.getHistoryPath())); 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..a95823c355 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; @@ -121,7 +121,7 @@ private void updateVisibles(User user) { registerItem.addStyleName("user_menu_item_register"); menu.addItem(registerItem); } else { - MenuItem userItem = customMenuItem("fa fa-user", user.getName(), "navigationMenu-item-label", userMenu, null); + MenuItem userItem = customMenuItem("fa fa-user", user.getFullName(), "navigationMenu-item-label", userMenu, null); userItem.addStyleName("user_menu_item"); menu.addItem(userItem); } 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.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateUserPanel.java deleted file mode 100644 index 78bdb2636b..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/CreateUserPanel.java +++ /dev/null @@ -1,510 +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 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.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; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.ListBox; -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.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.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; - -/** - * @author António Lindo - */ -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); - - @UiField - TextBox username; - - @UiField - Label usernameError; - - @UiField - TextBox fullname; - - @UiField - 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; - - // has to be true to detected new field changes - private boolean changed = true; - 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 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(); - - initWidget(uiBinder.createAndBindUi(this)); - - this.editmode = editmode; - super.setVisible(visible); - this.enableGroupSelect = enableGroupSelect; - - 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 - public void onChange(ChangeEvent event) { - CreateUserPanel.this.onChange(); - } - }; - - KeyUpHandler keyUpHandler = new KeyUpHandler() { - - @Override - public void onKeyUp(KeyUpEvent event) { - onChange(); - } - }; - - username.addKeyDownHandler(new UserAndGroupKeyDownHandler()); - - 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(); - } - }); - - usernameError.setVisible(false); - fullnameError.setVisible(false); - emailError.setVisible(false); - } - - @SuppressWarnings("unused") - private int setSelected(ListBox listbox, String text) { - int index = -1; - if (text != null) { - for (int i = 0; i < listbox.getItemCount(); i++) { - if (listbox.getValue(i).equals(text)) { - index = i; - break; - } - } - if (index >= 0) { - listbox.setSelectedIndex(index); - } else { - listbox.addItem(text); - index = listbox.getItemCount() - 1; - listbox.setSelectedIndex(index); - } - } else { - listbox.setSelectedIndex(-1); - } - 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.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) -> { - if (extra != null) { - CreateUserPanel.this.userExtra = extra.getExtraFormFields(); - createForm(userExtra); - } 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); - } - } - }); - } - - 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 - * - * @return true if valid - */ - public boolean isValid() { - List errorList = new ArrayList<>(); - if (username.getText().length() == 0) { - username.addStyleName("isWrong"); - usernameError.setText(messages.mandatoryField()); - usernameError.setVisible(true); - Window.scrollTo(username.getAbsoluteLeft(), username.getAbsoluteTop()); - errorList.add(messages.isAMandatoryField(messages.username())); - } else { - username.removeStyleName("isWrong"); - usernameError.setVisible(false); - } - - if (fullname.getText().length() == 0) { - fullname.addStyleName("isWrong"); - fullnameError.setText(messages.mandatoryField()); - fullnameError.setVisible(true); - if (errorList.isEmpty()) { - Window.scrollTo(fullname.getAbsoluteLeft(), fullname.getAbsoluteTop()); - } - errorList.add(messages.isAMandatoryField(messages.fullname())); - } else { - fullname.removeStyleName("isWrong"); - fullnameError.setVisible(false); - } - - if (email.getText() == null || "".equals(email.getText().trim())) { - email.addStyleName("isWrong"); - emailError.setText(messages.mandatoryField()); - emailError.setVisible(true); - if (errorList.isEmpty()) { - Window.scrollTo(email.getAbsoluteLeft(), email.getAbsoluteTop()); - } - errorList.add(messages.isAMandatoryField(messages.email())); - } else if (!email.getText() - .matches("^[_A-Za-z0-9-%+]+(\\.[_A-Za-z0-9-%+]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[_A-Za-z0-9-]+)")) { - email.addStyleName("isWrong"); - emailError.setText(messages.wrongMailFormat()); - emailError.setVisible(true); - if (errorList.isEmpty()) { - Window.scrollTo(email.getAbsoluteLeft(), email.getAbsoluteTop()); - } - errorList.add(messages.emailNotValid()); - } else { - email.removeStyleName("isWrong"); - emailError.setVisible(false); - } - - List extraErrors = FormUtilities.validate(userExtra, extra); - errorList.addAll(extraErrors); - checked = true; - - if (!errorList.isEmpty()) { - errors.setVisible(true); - StringBuilder errorString = new StringBuilder(); - for (String error : errorList) { - errorString.append("").append(error).append(""); - errorString.append("
"); - } - errors.setHTML(errorString.toString()); - } else { - errors.setVisible(false); - } - 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(""); - 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 - * - * @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 User getValue() { - return getUser(); - } - - public Set getUserExtra() { - return userExtra; - } - - public void setUserExtra(Set userExtra) { - this.userExtra = userExtra; - createForm(userExtra); - } -} - 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/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/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/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/GroupDataPanel.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/GroupDataPanel.ui.xml deleted file mode 100644 index 759655ad52..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/GroupDataPanel.ui.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - .section { - margin-top: 20px; - } - - - - - - - * - - - - * - - - - - - - - - - - - - - - - - - - - - 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/GroupSelect.java deleted file mode 100644 index 2bc1873912..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/GroupSelect.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; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Vector; - -import org.roda.core.data.common.RodaConstants; -import org.roda.core.data.v2.index.FindRequest; -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.user.Group; -import org.roda.core.data.v2.user.RODAMember; -import org.roda.wui.client.services.Services; - -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.i18n.client.LocaleInfo; -import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.CheckBox; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.HorizontalPanel; -import com.google.gwt.user.client.ui.Label; - -/** - * - * @author Luis Faria - * - */ -public class GroupSelect extends FlowPanel implements HasValueChangeHandlers> { - - private final List blacklist; - private final List groups; - private HashMap userSelections; - private boolean enabled; - - /** - * Create a new group selection widget - * - * @param visible - * start as visible or wait until its initialized - */ - public GroupSelect() { - this.groups = new Vector<>(); - this.blacklist = new Vector<>(); - this.userSelections = new HashMap<>(); - enabled = true; - this.addStyleName("groups"); - } - - public void init(final AsyncCallback callback) { - // TODO use RodaMemberList instead of a list of checkboxes - - boolean isUser = false; - boolean justActive = true; - Filter filter = new Filter(); - filter.add(new SimpleFilterParameter(RodaConstants.MEMBERS_IS_USER, Boolean.toString(isUser))); - Sorter sorter = new Sorter(new SortParameter(RodaConstants.MEMBERS_FULLNAME, false)); - - Services services = new Services("Find RODA members", "get"); - FindRequest request = FindRequest.getBuilder(filter, justActive).withSorter(sorter) - .build(); - services.membersResource(s -> s.find(request, LocaleInfo.getCurrentLocale().getLocaleName())) - .whenComplete((indexedResult, throwable) -> { - if (throwable != null) { - callback.onFailure(throwable); - } else { - for (RODAMember member : indexedResult.getResults()) { - if (member instanceof Group) { - Group group = (Group) member; - GroupCheckbox groupCheckbox = new GroupCheckbox(group, group.getFullName(), group.getId()); - groups.add(groupCheckbox); - } - } - - for (final GroupCheckbox groupCheckbox : groups) { - GroupSelect.this.add(groupCheckbox); - groupCheckbox.addValueChangeHandler(new ValueChangeHandler() { - - @Override - public void onValueChange(ValueChangeEvent event) { - if (userSelections.keySet().contains(event.getValue().getId())) { - userSelections.remove(event.getValue().getId()); - } else { - userSelections.put(event.getValue().getId(), event.getValue()); - } - onChange(); - } - }); - } - callback.onSuccess(true); - } - }); - } - - public Map getUserSelections() { - return userSelections; - } - - public boolean isEnabled() { - return enabled; - } - - public Set getMemberGroups() { - Set memberGroups = new HashSet<>(); - for (GroupCheckbox g : groups) { - if (g.isChecked()) { - memberGroups.add(g.getGroup().getId()); - } - } - return memberGroups; - } - - public void setMemberGroups(Set memberGroups) { - Iterator it = memberGroups.iterator(); - - while (it.hasNext()) { - String group = it.next(); - boolean foundit = false; - for (Iterator j = groups.iterator(); j.hasNext() && !foundit;) { - GroupCheckbox g = j.next(); - if (g.getGroup().getId().equals(group)) { - foundit = true; - g.setChecked(true); - userSelections.put(group, g.getGroup()); - } - } - } - } - - public void addGroupToBlacklist(String group) { - blacklist.add(group); - } - - @Override - public HandlerRegistration addValueChangeHandler(ValueChangeHandler> handler) { - return addHandler(handler, ValueChangeEvent.getType()); - } - - protected void onChange() { - ValueChangeEvent.fire(this, getValue()); - } - - public List getValue() { - return new Vector<>(getUserSelections().values()); - } - - private class GroupCheckbox extends HorizontalPanel - implements HasValueChangeHandlers, Comparable { - - private final String sortingkeyword; - private final Group group; - private final CheckBox checkbox; - private final Label descriptionLabel; - - public GroupCheckbox(Group group, String description, String sortingkeyword) { - this.group = group; - this.checkbox = new CheckBox(); - this.checkbox.setTitle(description); - this.checkbox.getElement().getFirstChildElement().setTitle(description); - this.descriptionLabel = new Label(description); - this.sortingkeyword = sortingkeyword; - this.add(checkbox); - this.add(descriptionLabel); - - this.descriptionLabel.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - checkbox.setValue(!checkbox.getValue()); - onChange(); - } - }); - - this.checkbox.addValueChangeHandler(new ValueChangeHandler() { - @Override - public void onValueChange(ValueChangeEvent event) { - onChange(); - } - }); - - this.addStyleName("group"); - checkbox.addStyleName("group-checkbox"); - descriptionLabel.setStylePrimaryName("group-description"); - } - - public boolean isChecked() { - return checkbox.getValue(); - } - - public void setChecked(boolean checked) { - checkbox.setValue(checked); - } - - public Group getGroup() { - return group; - } - - @Override - public int compareTo(GroupCheckbox groupCheckbox) { - return sortingkeyword.compareTo(groupCheckbox.sortingkeyword); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + getOuterType().hashCode(); - result = prime * result + ((checkbox == null) ? 0 : checkbox.hashCode()); - result = prime * result + ((descriptionLabel == null) ? 0 : descriptionLabel.hashCode()); - result = prime * result + ((group == null) ? 0 : group.hashCode()); - result = prime * result + ((sortingkeyword == null) ? 0 : sortingkeyword.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - GroupCheckbox other = (GroupCheckbox) obj; - if (!getOuterType().equals(other.getOuterType())) { - return false; - } - if (group == null) { - if (other.group != null) { - return false; - } - } else if (!group.equals(other.group)) { - return false; - } - if (sortingkeyword == null) { - if (other.sortingkeyword != null) { - return false; - } - } else if (!sortingkeyword.equals(other.sortingkeyword)) { - return false; - } - return true; - } - - public String getSortingkeyword() { - return sortingkeyword; - } - - @Override - public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) { - return addHandler(handler, ValueChangeEvent.getType()); - } - - protected void onChange() { - ValueChangeEvent.fire(this, getValue()); - } - - public Group getValue() { - return getGroup(); - } - - private GroupSelect getOuterType() { - return GroupSelect.this; - } - } -} 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/PermissionsPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/PermissionsPanel.java deleted file mode 100644 index ab7baca98b..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/PermissionsPanel.java +++ /dev/null @@ -1,291 +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.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.MissingResourceException; -import java.util.Set; - -import org.roda.wui.common.client.ClientLogger; -import org.roda.wui.common.client.tools.ConfigurationManager; -import org.roda.wui.common.client.widgets.LoadingPopup; -import org.roda.wui.common.client.widgets.wcag.WCAGUtilities; - -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.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.CheckBox; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.HorizontalPanel; -import com.google.gwt.user.client.ui.Label; - -import config.i18n.client.ClientMessages; - -/** - * @author Luis Faria - * - */ -public class PermissionsPanel extends FlowPanel implements HasValueChangeHandlers> { - - private class Permission extends HorizontalPanel implements HasValueChangeHandlers { - - private final String role; - private boolean locked; - private boolean enabled; - private final CheckBox checkbox; - private final Label descriptionLabel; - - 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"); - WCAGUtilities.addTitleToCheckbox(checkbox, description); - 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 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); - if (enabled) { - this.descriptionLabel.removeStyleDependentName("off"); - this.descriptionLabel.addStyleDependentName("on"); - } else { - this.descriptionLabel.removeStyleDependentName("on"); - this.descriptionLabel.addStyleDependentName("off"); - } - } - } - - @Override - public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) { - return addHandler(handler, ValueChangeEvent.getType()); - } - - protected void onChange() { - ValueChangeEvent.fire(this, getValue()); - } - - public String getValue() { - return getRole(); - } - - private PermissionsPanel getOuterType() { - return PermissionsPanel.this; - } - } - - private static final ClientMessages messages = GWT.create(ClientMessages.class); - - @SuppressWarnings("unused") - private final ClientLogger logger = new ClientLogger(getClass().getName()); - - private final List permissions; - - private List userSelections; - - private boolean enabled; - - private final LoadingPopup loading; - - /** - * - */ - public PermissionsPanel() { - this.permissions = new ArrayList<>(); - this.userSelections = new ArrayList<>(); - - loading = new LoadingPopup(this); - loading.show(); - - this.enabled = true; - - this.addStyleName("permissions"); - } - - public void init(final AsyncCallback callback) { - 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); - permissions.add(permission); - PermissionsPanel.this.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(); - } - }); - } - - loading.hide(); - callback.onSuccess(true); - } - - public List getUserSelections() { - return userSelections; - } - - /** - * Set all permissions defined by roles checked and set locked with parameters - * - * @param roles - * roles of the permissions to check - * @param lock - * if permissions should also be locked - */ - public void checkPermissions(Set roles, boolean lock) { - Iterator it = roles.iterator(); - - while (it.hasNext()) { - String role = it.next(); - boolean foundit = false; - for (Iterator j = permissions.iterator(); j.hasNext() && !foundit;) { - Permission p = j.next(); - if (p.getRole().equals(role)) { - foundit = true; - p.setChecked(true); - p.setLocked(lock); - } - } - } - } - - @Override - public void clear() { - for (Permission p : permissions) { - p.setChecked(false); - p.setLocked(false); - } - } - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - for (Permission p : permissions) { - p.setEnabled(enabled); - } - } - - public void updateLockedPermissions(Set memberGroups) { - if (!memberGroups.isEmpty()) { - this.setEnabled(false); - loading.show(); - } - } - - /** - * Get roles that are directly defined, i.e. are not inherited - * - * @return - */ - public Set getDirectRoles() { - List checkedPermissions = new ArrayList<>(); - for (Permission p : permissions) { - 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(); - } -} 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..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; @@ -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,129 +87,36 @@ 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 - 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 SimplePanel expandedAuditLogsList; - /** * Create a new panel to view a log entry * */ 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(); @@ -218,16 +125,20 @@ 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 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..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; @@ -69,7 +67,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 @@ -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 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/UserDataPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/UserDataPanel.java deleted file mode 100644 index a1afa3bf82..0000000000 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/UserDataPanel.java +++ /dev/null @@ -1,536 +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.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; -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.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; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.ListBox; -import com.google.gwt.user.client.ui.TextBox; -import com.google.gwt.user.client.ui.Widget; - -import config.i18n.client.ClientMessages; - -/** - * @author Luis Faria - * - */ -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); - - @UiField - TextBox username; - - @UiField - Label usernameError; - - @UiField(provided = true) - PasswordPanel password; - - @UiField - Label passwordError; - - @UiField - TextBox fullname; - - @UiField - 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; - - // has to be true to detected new field changes - private boolean changed = true; - 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) { - - password = new PasswordPanel(editMode); - groupSelect = new GroupSelect(); - - initWidget(uiBinder.createAndBindUi(this)); - - this.editmode = editMode; - super.setVisible(visible); - this.enableGroupSelect = enableGroupSelect; - - 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 - public void onChange(ChangeEvent event) { - UserDataPanel.this.onChange(); - } - }; - - KeyUpHandler keyUpHandler = new KeyUpHandler() { - - @Override - public void onKeyUp(KeyUpEvent event) { - onChange(); - } - }; - - username.addKeyDownHandler(new UserAndGroupKeyDownHandler()); - - 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(); - } - }); - - usernameError.setVisible(false); - passwordError.setVisible(false); - fullnameError.setVisible(false); - emailError.setVisible(false); - } - - @SuppressWarnings("unused") - private int setSelected(ListBox listbox, String text) { - int index = -1; - if (text != null) { - for (int i = 0; i < listbox.getItemCount(); i++) { - if (listbox.getValue(i).equals(text)) { - index = i; - break; - } - } - if (index >= 0) { - listbox.setSelectedIndex(index); - } else { - listbox.addItem(text); - index = listbox.getItemCount() - 1; - listbox.setSelectedIndex(index); - } - } else { - listbox.setSelectedIndex(-1); - } - 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, - * 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; - } - - /** - * Get the password - * - * @return the password if changed, or null if it remains the same - */ - public String getPassword() { - return password.getValue(); - } - - /** - * Check if password changed - * - * @return true if password changed, false otherwise - */ - public boolean isPasswordChanged() { - return password.isChanged(); - } - - /** - * Is user data panel valid - * - * @return true if valid - */ - public boolean isValid() { - List errorList = new ArrayList<>(); - if (username.getText().isEmpty()) { - username.addStyleName("isWrong"); - usernameError.setText(messages.mandatoryField()); - usernameError.setVisible(true); - Window.scrollTo(username.getAbsoluteLeft(), username.getAbsoluteTop()); - errorList.add(messages.isAMandatoryField(messages.username())); - } else { - username.removeStyleName("isWrong"); - usernameError.setVisible(false); - } - - if (!password.matchConfirmation()) { - if (errorList.isEmpty()) { - Window.scrollTo(password.getAbsoluteLeft(), password.getAbsoluteTop()); - } - errorList.add(messages.passwordDoesNotMatchConfirmation()); - } else if (password.isSmall()) { - if (errorList.isEmpty()) { - Window.scrollTo(password.getAbsoluteLeft(), password.getAbsoluteTop()); - } - errorList.add(messages.passwordIsTooSmall()); - } - - if (fullname.getText().isEmpty()) { - fullname.addStyleName("isWrong"); - fullnameError.setText(messages.mandatoryField()); - fullnameError.setVisible(true); - if (errorList.isEmpty()) { - Window.scrollTo(fullname.getAbsoluteLeft(), fullname.getAbsoluteTop()); - } - errorList.add(messages.isAMandatoryField(messages.fullname())); - } else { - fullname.removeStyleName("isWrong"); - fullnameError.setVisible(false); - } - - if (email.getText() == null || "".equals(email.getText().trim())) { - email.addStyleName("isWrong"); - emailError.setText(messages.mandatoryField()); - emailError.setVisible(true); - if (errorList.isEmpty()) { - Window.scrollTo(email.getAbsoluteLeft(), email.getAbsoluteTop()); - } - errorList.add(messages.isAMandatoryField(messages.email())); - } else if (!email.getText() - .matches("^[_A-Za-z0-9-%+]+(\\.[_A-Za-z0-9-%+]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[_A-Za-z0-9-]+)")) { - email.addStyleName("isWrong"); - emailError.setText(messages.wrongMailFormat()); - emailError.setVisible(true); - if (errorList.isEmpty()) { - Window.scrollTo(email.getAbsoluteLeft(), email.getAbsoluteTop()); - } - errorList.add(messages.emailNotValid()); - } else { - email.removeStyleName("isWrong"); - emailError.setVisible(false); - } - - List extraErrors = FormUtilities.validate(userExtra, extra); - errorList.addAll(extraErrors); - checked = true; - - if (!errorList.isEmpty()) { - errors.setVisible(true); - StringBuilder errorString = new StringBuilder(); - for (String error : errorList) { - errorString.append("").append(error).append(""); - errorString.append("
"); - } - errors.setHTML(errorString.toString()); - } else { - errors.setVisible(false); - } - - return errorList.isEmpty(); - } - - /** - * 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(""); - password.clear(); - 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 - * - * @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 User getValue() { - return getUser(); - } - - public Set getUserExtra() { - return userExtra; - } -} 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/members/CreateGroup.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/CreateGroup.java new file mode 100644 index 0000000000..245a6935d7 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/CreateGroup.java @@ -0,0 +1,123 @@ +package org.roda.wui.client.management.members; + +import java.util.HashSet; +import java.util.List; + +import com.google.gwt.user.client.ui.Button; +import org.roda.core.data.v2.user.Group; +import org.roda.core.data.v2.user.requests.CreateGroupRequest; +import org.roda.wui.client.common.NavigationToolbar; +import org.roda.wui.client.common.NoActionsToolbar; +import org.roda.wui.client.common.TitlePanel; +import org.roda.wui.client.common.UserLogin; +import org.roda.wui.client.common.forms.GenericDataPanel; +import org.roda.wui.client.main.BreadcrumbUtils; +import org.roda.wui.client.management.members.data.panels.GroupDataPanel; +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.FlowPanel; +import com.google.gwt.user.client.ui.FocusPanel; +import com.google.gwt.user.client.ui.Widget; + +import config.i18n.client.ClientMessages; +import org.roda.wui.common.client.widgets.Toast; + +/** + * @author Miguel Guimarães + */ + +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(); + 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"; + } + }; + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + + @UiField + FocusPanel keyboardFocus; + @UiField + NavigationToolbar navigationToolbar; + @UiField + NoActionsToolbar actionsToolbar; + @UiField + TitlePanel title; + @UiField + FlowPanel groupDataPanel; + + public CreateGroup() { + initWidget(uiBinder.createAndBindUi(this)); + + // 1. Create the panel and keep a reference + GroupDataPanel dataPanel = new GroupDataPanel(false); + dataPanel.setGroup(new Group()); + // Bind the Save Action logic + dataPanel.setSaveHandler(() -> { + Group group = dataPanel.getValue(); + CreateGroupRequest request = new CreateGroupRequest(); + request.setName(group.getName()); + request.setFullName(group.getFullName()); + request.setDirectRoles(new HashSet<>()); + Services services = new Services("Create group", "create"); + services.membersResource(s -> s.createGroup(request)).whenComplete((created, error) -> { + if (error == null) { + Toast.showInfo(messages.groups(), messages.groupSuccessfullyCreated()); + HistoryUtils.newHistory(ShowMember.RESOLVER, created.getUUID()); + } else { + Toast.showError(messages.groups(), error.getMessage()); + HistoryUtils.newHistory(MemberManagement.RESOLVER); + } + }); + }); + + // Bind the Cancel Action logic + dataPanel.setCancelHandler(() -> HistoryUtils.newHistory(MemberManagement.RESOLVER)); + + groupDataPanel.add(dataPanel); + + navigationToolbar.withoutButtons().build(); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getCreateGroupBreadcrumbs()); + + actionsToolbar.setLabel(messages.showGroupTitle()); + + // 3. Pass the shared object + actionsToolbar.build(); + + title.setText(messages.createGroupTitle()); + title.setIconClass("Group"); + title.addStyleName("mb-20"); + + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse"); + } + + interface MyUiBinder extends UiBinder { + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/CreateGroup.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/CreateGroup.ui.xml new file mode 100644 index 0000000000..2adb801a9c --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/CreateGroup.ui.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/CreateUser.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/CreateUser.java new file mode 100644 index 0000000000..bdb0ec88ed --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/CreateUser.java @@ -0,0 +1,131 @@ +package org.roda.wui.client.management.members; + +import com.google.gwt.core.client.GWT; +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.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.FocusPanel; +import com.google.gwt.user.client.ui.Widget; +import config.i18n.client.ClientMessages; +import org.roda.core.data.v2.user.User; +import org.roda.core.data.v2.user.requests.CreateUserRequest; +import org.roda.wui.client.common.NavigationToolbar; +import org.roda.wui.client.common.NoActionsToolbar; +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.utils.AsyncCallbackUtils; +import org.roda.wui.client.main.BreadcrumbUtils; +import org.roda.wui.client.management.members.data.panels.UserDataPanel; +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 java.util.HashSet; +import java.util.List; + +/** + * @author Miguel Guimarães + */ + +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"; + } + }; + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + + @UiField + FocusPanel keyboardFocus; + @UiField + NavigationToolbar navigationToolbar; + @UiField + NoActionsToolbar actionsToolbar; + @UiField + TitlePanel title; + @UiField + FlowPanel userDataPanel; + + public CreateUser(User user) { + initWidget(uiBinder.createAndBindUi(this)); + + // 1. Create the panel and keep a reference + UserDataPanel dataPanel = new UserDataPanel(false); + dataPanel.setUser(user); + dataPanel.setSaveHandler(() -> { + + CreateUserRequest request = new CreateUserRequest(user.getEmail(), user.getName(), user.getFullName(), + new HashSet<>(), new HashSet<>(), user.isGuest(), null, dataPanel.getUserExtra()); + + Services services = new Services("Create user", "create"); + services.membersResource(s -> s.createUser(request, LocaleInfo.getCurrentLocale().getLocaleName())) + .whenComplete((created, error) -> { + if (error == null) { + Toast.showInfo(messages.groups(), messages.userSuccessfullyCreated()); + HistoryUtils.newHistory(ShowMember.RESOLVER, created.getUUID()); + } else { + Toast.showError(messages.groups(), error.getMessage()); + HistoryUtils.newHistory(MemberManagement.RESOLVER); + } + }); + }); + + // Bind the Cancel Action logic + dataPanel.setCancelHandler(() -> HistoryUtils.newHistory(MemberManagement.RESOLVER)); + + userDataPanel.add(dataPanel); + + navigationToolbar.withoutButtons().build(); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getCreateUserBreadcrumbs()); + + actionsToolbar.setLabel(messages.showUserTitle()); + + // 3. Pass the shared object + actionsToolbar.setObjectAndBuild(user, null, new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + AsyncCallbackUtils.defaultFailureTreatment(caught); + } + + @Override + public void onSuccess(Actionable.ActionImpact result) { + // Redirect user or show success message if result == ActionImpact.UPDATED + } + }); + + title.setText(messages.createUserTitle()); + title.setIconClass("User"); + title.addStyleName("mb-20"); + + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse"); + } + + interface MyUiBinder extends UiBinder { + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/CreateUser.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/CreateUser.ui.xml new file mode 100644 index 0000000000..d878268eb0 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/CreateUser.ui.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/EditGroup.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/EditGroup.java new file mode 100644 index 0000000000..03a2fcffce --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/EditGroup.java @@ -0,0 +1,125 @@ +package org.roda.wui.client.management.members; + +import java.util.List; + +import org.roda.core.data.v2.user.Group; +import org.roda.wui.client.common.NavigationToolbar; +import org.roda.wui.client.common.NoActionsToolbar; +import org.roda.wui.client.common.TitlePanel; +import org.roda.wui.client.common.UserLogin; +import org.roda.wui.client.main.BreadcrumbUtils; +import org.roda.wui.client.management.members.data.panels.GroupDataPanel; +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.FlowPanel; +import com.google.gwt.user.client.ui.FocusPanel; +import com.google.gwt.user.client.ui.Widget; + +import config.i18n.client.ClientMessages; +import org.roda.wui.common.client.widgets.Toast; + +/** + * @author Miguel Guimarães + */ +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 groupId = historyTokens.get(0); + Services services = new Services("Get Group", "get"); + services.membersResource(s -> s.getGroup(groupId)).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"; + } + }; + + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + + @UiField + FocusPanel keyboardFocus; + @UiField + NavigationToolbar navigationToolbar; + @UiField + NoActionsToolbar actionsToolbar; + @UiField + TitlePanel title; + @UiField + FlowPanel groupDataPanel; + + public EditGroup(Group group) { + initWidget(uiBinder.createAndBindUi(this)); + + GroupDataPanel groupDataForm = new GroupDataPanel(true); + groupDataForm.setGroup(group); + + // Bind the Save Action logic + groupDataForm.setSaveHandler(() -> { + Group updatedGroup = groupDataForm.getValue(); + Services services = new Services("Update group", "update"); + services.membersResource(s -> s.updateGroup(updatedGroup)).whenComplete((updated, error) -> { + if (error == null) { + Toast.showInfo(messages.groups(), messages.groupSuccessfullyUpdated()); + HistoryUtils.newHistory(ShowMember.RESOLVER, updated.getUUID()); + } else { + Toast.showError(messages.groups(), messages.failedToUpdateGroup()); + HistoryUtils.newHistory(ShowMember.RESOLVER, group.getUUID()); + } + }); + }); + + // Bind the Cancel Action logic + groupDataForm.setCancelHandler(() -> HistoryUtils.newHistory(ShowMember.RESOLVER, group.getUUID())); + + groupDataPanel.add(groupDataForm); + + navigationToolbar.withoutButtons().build(); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getEditMemberBreadcrumbs(group)); + + actionsToolbar.setLabel(messages.showGroupTitle()); + actionsToolbar.build(); + + title.setText(group.getFullName()); + title.setIconClass("Group"); + title.addStyleName("mb-20"); + + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse"); + } + + 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/EditGroup.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/EditGroup.ui.xml new file mode 100644 index 0000000000..601ecc27b7 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/EditGroup.ui.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/EditUser.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/EditUser.java new file mode 100644 index 0000000000..90d54d0a0b --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/EditUser.java @@ -0,0 +1,133 @@ +package org.roda.wui.client.management.members; + +import java.util.List; + +import com.google.gwt.user.client.ui.Button; +import org.roda.core.data.v2.user.User; +import org.roda.core.data.v2.user.requests.UpdateUserRequest; +import org.roda.wui.client.common.NavigationToolbar; +import org.roda.wui.client.common.NoActionsToolbar; +import org.roda.wui.client.common.TitlePanel; +import org.roda.wui.client.common.UserLogin; +import org.roda.wui.client.management.members.data.panels.UserDataPanel; +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.FlowPanel; +import com.google.gwt.user.client.ui.FocusPanel; +import com.google.gwt.user.client.ui.Widget; + +import config.i18n.client.ClientMessages; +import org.roda.wui.common.client.widgets.Toast; + +/** + * @author Miguel Guimarães + */ + +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"; + } + }; + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + + @UiField + FocusPanel keyboardFocus; + @UiField + NavigationToolbar navigationToolbar; + @UiField + NoActionsToolbar actionsToolbar; + @UiField + TitlePanel title; + @UiField + FlowPanel userDataPanel; + @UiField + FlowPanel actions; + + public EditUser(User user) { + initWidget(uiBinder.createAndBindUi(this)); + + // 1. Create the panel and keep a reference + UserDataPanel dataPanel = new UserDataPanel(true); + dataPanel.setUser(user); + dataPanel.setSaveHandler(() ->{ + UpdateUserRequest request = new UpdateUserRequest(); + request.setUser(dataPanel.getValue()); + request.setPassword(null); + request.setValues(dataPanel.getUserExtra()); + + Services services = new Services("Update user", "update"); + services.membersResource(s -> s.updateUser(request)).whenComplete((updated, error) -> { + if (error == null) { + Toast.showInfo(messages.groups(), messages.userSuccessfullyUpdated()); + HistoryUtils.newHistory(ShowMember.RESOLVER, updated.getUUID()); + } else { + Toast.showError(messages.groups(), messages.failedToUpdateUser()); + HistoryUtils.newHistory(ShowMember.RESOLVER, updated.getUUID()); + } + }); + }); + + dataPanel.setCancelHandler(() -> HistoryUtils.newHistory(ShowMember.RESOLVER, user.getUUID())); + + userDataPanel.add(dataPanel); + + navigationToolbar.withoutButtons().build(); + navigationToolbar.updateBreadcrumbPath(BreadcrumbUtils.getEditMemberBreadcrumbs(user)); + + actionsToolbar.setLabel(messages.showUserTitle()); + + actionsToolbar.build(); + + title.setText(user.getFullName()); + title.setIconClass("User"); + title.addStyleName("mb-20"); + + keyboardFocus.setFocus(true); + keyboardFocus.addStyleName("browse"); + } + + interface MyUiBinder extends UiBinder { + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/EditUser.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/EditUser.ui.xml new file mode 100644 index 0000000000..4a60e43cbb --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/EditUser.ui.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + 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 78% 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..dcddac2785 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,19 +5,18 @@ * * https://github.com/keeps/roda */ -package org.roda.wui.client.management; +package org.roda.wui.client.management.members; import java.util.List; import org.roda.core.data.v2.user.RODAMember; import org.roda.wui.client.common.UserLogin; -import org.roda.wui.client.common.actions.RODAMemberActions; +import org.roda.wui.client.common.actions.RODAMemberSearchWrapperActions; import org.roda.wui.client.common.lists.RodaMemberList; 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,12 @@ 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); + @UiField + FlowPanel memberManagementDescription; + @UiField(provided = true) + SearchWrapper searchWrapper; public static final HistoryResolver RESOLVER = new HistoryResolver() { @Override public void resolve(List historyTokens, AsyncCallback callback) { @@ -58,8 +61,18 @@ public String getHistoryToken() { return "user"; } }; + public MemberManagement() { - private static MemberManagement instance = null; + ListBuilder rodaMemberListBuilder = new ListBuilder<>(() -> new RodaMemberList(), + new AsyncTableCellOptions<>(RODAMember.class, "MemberManagement_rodaMembers") + .withSummary(messages.usersAndGroupsTitle()).bindOpener().withActionable(RODAMemberSearchWrapperActions.get())); + + searchWrapper = new SearchWrapper(false).createListAndSearchPanel(rodaMemberListBuilder); + + initWidget(uiBinder.createAndBindUi(this)); + + memberManagementDescription.add(new HTMLWidgetWrapper("MemberManagementDescription.html")); + } /** * Get the singleton instance @@ -75,31 +88,6 @@ public static MemberManagement getInstance() { 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(), - new AsyncTableCellOptions<>(RODAMember.class, "MemberManagement_rodaMembers") - .withSummary(messages.usersAndGroupsTitle()).bindOpener() - .withSearchPlaceholder(messages.usersAndGroupsSearchPlaceHolder()).withActionable(RODAMemberActions.get())); - - searchWrapper = new SearchWrapper(false).createListAndSearchPanel(rodaMemberListBuilder); - - initWidget(uiBinder.createAndBindUi(this)); - - memberManagementDescription.add(new HTMLWidgetWrapper("MemberManagementDescription.html")); - } - private void refresh() { searchWrapper.refreshCurrentList(); } @@ -117,18 +105,12 @@ public void resolve(List historyTokens, AsyncCallback callback) 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); + if (historyTokens.get(0).equals(ShowMember.RESOLVER.getHistoryToken())) { + ShowMember.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); + } else if (historyTokens.get(0).equals(EditUser.RESOLVER.getHistoryToken())) { + EditUser.RESOLVER.resolve(HistoryUtils.tail(historyTokens), callback); } else { HistoryUtils.newHistory(RESOLVER); callback.onSuccess(null); @@ -138,4 +120,7 @@ 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 81% 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..2f27824437 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,8 +5,9 @@ * * https://github.com/keeps/roda */ -package org.roda.wui.client.management; +package org.roda.wui.client.management.members; +import com.google.gwt.user.client.ui.Widget; import org.roda.wui.common.client.tools.StringUtils; import com.google.gwt.core.shared.GWT; @@ -45,6 +46,8 @@ public PasswordPanel(boolean editmode) { changed = false; editLayout = new FlowPanel(); + + // Initialize Widgets editPassword = new PasswordTextBox(); editPasswordRepeat = new PasswordTextBox(); editPasswordLabel = new Label(messages.password()); @@ -53,10 +56,9 @@ public PasswordPanel(boolean editmode) { editPasswordRepeatLabel.addStyleName("form-label"); editPasswordNote = new Label(messages.passwordNote()); - editLayout.add(editPasswordLabel); - editLayout.add(editPassword); - editLayout.add(editPasswordRepeatLabel); - editLayout.add(editPasswordRepeat); + // Build the layout using the generic form structure + editLayout.add(createFieldRow(editPasswordLabel, editPassword)); + editLayout.add(createFieldRow(editPasswordRepeatLabel, editPasswordRepeat)); editButton = new Button(messages.userDataChangePassword()); editButton.addClickHandler(new ClickHandler() { @@ -70,11 +72,11 @@ public void onClick(ClickEvent event) { }); if (editmode) { - FlowPanel editButtonPanel = new FlowPanel(); Label passwordLabel = new Label(messages.password()); passwordLabel.addStyleName("form-label"); - editButtonPanel.add(passwordLabel); - editButtonPanel.add(editButton); + + // Wrap the button in the same generic form structure + FlowPanel editButtonPanel = createFieldRow(passwordLabel, editButton); setWidget(editButtonPanel); buttonMode = true; } else { @@ -97,21 +99,41 @@ public void onKeyUp(KeyUpEvent event) { editPassword.addStyleName("password-input"); editPassword.addStyleName("form-textbox"); editPassword.getElement().setTitle(messages.password()); - // WCAGUtilities.getInstance().makeAccessible(editPassword.getParent().getElement()); editPasswordRepeat.addStyleName("password-input"); editPasswordRepeat.addStyleName("form-textbox"); editPasswordRepeat.getElement().setTitle(messages.password()); - // WCAGUtilities.getInstance().makeAccessible(editPasswordRepeat.getParent().getElement()); editPasswordNote.addStyleName("password-note"); - // WCAGUtilities.getInstance().makeAccessible(editPasswordNote.getParent().getElement()); editButton.addStyleName("password-button"); editButton.addStyleName("btn"); editButton.addStyleName("btn-play"); } + /** + * Helper method to structure custom widgets exactly like the GenericDataForm + * text boxes. + */ + private FlowPanel createFieldRow(Widget labelWidget, Widget inputWidget) { + FlowPanel searchField = new FlowPanel(); + searchField.addStyleName("search-field"); + + FlowPanel leftPanel = new FlowPanel(); + leftPanel.addStyleName("search-field-left-panel"); + + FlowPanel inputPanel = new FlowPanel(); + inputPanel.addStyleName("search-field-input-panel full_width"); + + // Assemble the DOM + inputPanel.add(inputWidget); + leftPanel.add(labelWidget); + leftPanel.add(inputPanel); + searchField.add(leftPanel); + + return searchField; + } + public boolean isChanged() { return changed; } @@ -181,5 +203,4 @@ public void clear() { public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) { return addHandler(handler, ValueChangeEvent.getType()); } - -} +} \ No newline at end of file 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 96% 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..18881847f2 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,12 +117,12 @@ 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)); - userDataPanel.setUsernameReadOnly(true); + //userDataPanel.setUsernameReadOnly(true); } private SecureString getPassword() { 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..47b0f2c4be 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) { @@ -145,7 +145,7 @@ public Register() { } void setExtra(Set b) { - this.userDataPanel.setUserExtra(b); + //this.userDataPanel.setUserExtra(b); } @UiHandler("buttonApply") 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..d11cb899ce --- /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 Member", "get"); + services.membersResource(s -> s.getMember(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.getId(), 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/members/UserDataPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UserDataPanel.java new file mode 100644 index 0000000000..8152137c11 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/UserDataPanel.java @@ -0,0 +1,168 @@ +/** + * 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 java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.roda.core.data.v2.generics.MetadataValue; +import org.roda.core.data.v2.user.User; +import org.roda.wui.client.common.forms.GenericDataForm; +import org.roda.wui.client.common.forms.GenericDataPanel; +import org.roda.wui.client.common.utils.FormUtilities; +import org.roda.wui.common.client.ClientLogger; + +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.user.client.Window; +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 config.i18n.client.ClientMessages; + +/** + * @author Luis Faria + * + */ +public class UserDataPanel extends Composite implements GenericDataPanel, HasValueChangeHandlers { + + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private final ClientLogger logger = new ClientLogger(getClass().getName()); + + private final boolean editMode; + private boolean changed = false; + private Set userExtra; + + // UI Components + private FlowPanel mainContainer = new FlowPanel(); + private GenericDataForm userForm = new GenericDataForm<>(); + private PasswordPanel passwordPanel; + private FlowPanel extraFieldsPanel = new FlowPanel(); + private HTML errors = new HTML(); + + public UserDataPanel(boolean visible, boolean editMode) { + this.editMode = editMode; + this.passwordPanel = new PasswordPanel(editMode); + + // 1. Setup Generic Fields + userForm.addReadOnlyField(messages.username(), User::getName, true); + userForm.addTextField(messages.fullname(), User::getFullName, User::setFullName, true, false); + + // Email with Regex Validation + userForm.addTextField(messages.email(), User::getEmail, User::setEmail, true, false, + "^[_A-Za-z0-9-%+]+(\\.[_A-Za-z0-9-%+]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[_A-Za-z0-9-]+)", + messages.emailNotValid()); + + // userForm.addCustomWidget(passwordPanel); + userForm.addCustomWidget(extraFieldsPanel); + + // 3. Assemble Main Layout + mainContainer.add(userForm); + + errors.setVisible(false); + mainContainer.add(errors); + + initWidget(mainContainer); + super.setVisible(visible); + + // 4. Handlers + ValueChangeHandler changeHandler = event -> onChange(); + userForm.addValueChangeHandler(changeHandler); + passwordPanel.addValueChangeHandler(event -> onChange()); + } + + public User getUser() { + return userForm.getValue(); + } + + public void setUser(User user) { + userForm.setModel(user); + this.userExtra = user.getExtra(); + createForm(this.userExtra); + this.changed = false; + } + + public void createForm(Set userExtra) { + extraFieldsPanel.clear(); + FormUtilities.create(extraFieldsPanel, userExtra, false); + } + + public String getPassword() { + return passwordPanel.getValue(); + } + + public boolean isPasswordChanged() { + return passwordPanel.isChanged(); + } + + public Set getUserExtra() { + return userExtra; + } + + @Override + public boolean isValid() { + List errorList = new ArrayList<>(); + + // 1. Validate Generic Form + if (!userForm.isValid()) { + errorList.add(messages.mandatoryField() + " / " + messages.emailNotValid()); + } + + // 2. Validate Password Panel + if (!passwordPanel.matchConfirmation()) { + Window.scrollTo(passwordPanel.getAbsoluteLeft(), passwordPanel.getAbsoluteTop()); + errorList.add(messages.passwordDoesNotMatchConfirmation()); + } else if (passwordPanel.isSmall()) { + Window.scrollTo(passwordPanel.getAbsoluteLeft(), passwordPanel.getAbsoluteTop()); + errorList.add(messages.passwordIsTooSmall()); + } + + // 3. Validate Extra Fields + List extraErrors = FormUtilities.validate(userExtra, extraFieldsPanel); + errorList.addAll(extraErrors); + + // 4. Render Errors + if (!errorList.isEmpty()) { + errors.setVisible(true); + StringBuilder errorString = new StringBuilder(); + for (String error : errorList) { + errorString.append("").append(error).append("
"); + } + errors.setHTML(errorString.toString()); + } else { + errors.setVisible(false); + } + + return errorList.isEmpty(); + } + + public boolean isChanged() { + return changed || userForm.isChanged(); + } + + protected void onChange() { + changed = true; + ValueChangeEvent.fire(this, getUser()); + } + + @Override + public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) { + return addHandler(handler, ValueChangeEvent.getType()); + } + + @Override + public User getValue() { + return getUser(); + } +} 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/data/panels/GroupDataPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/data/panels/GroupDataPanel.java new file mode 100644 index 0000000000..5eb2fb1569 --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/data/panels/GroupDataPanel.java @@ -0,0 +1,103 @@ +package org.roda.wui.client.management.members.data.panels; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.FlowPanel; +import config.i18n.client.ClientMessages; +import org.roda.core.data.v2.user.Group; +import org.roda.wui.client.common.forms.GenericDataForm; +import org.roda.wui.client.common.forms.GenericDataPanel; + +import com.google.gwt.event.logical.shared.HasValueChangeHandlers; +import com.google.gwt.event.logical.shared.ValueChangeHandler; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.user.client.ui.Composite; + +/** + * @author Miguel Guimarães + */ +public class GroupDataPanel extends Composite implements GenericDataPanel, HasValueChangeHandlers { + private static final ClientMessages messages = GWT.create(ClientMessages.class); + + private final GenericDataForm groupForm; + + private final Button saveButton; + private final Button cancelButton; + + public GroupDataPanel(boolean editMode) { + this.groupForm = new GenericDataForm<>(); + + if (editMode) { + groupForm.addReadOnlyField(messages.groupName(), Group::getName, true); + groupForm.addTextField(messages.groupFullname(), Group::getFullName, Group::setFullName, true); + } else { + groupForm.addTextField(messages.groupName(), Group::getName, Group::setName, true); + groupForm.addTextField(messages.groupFullname(), Group::getFullName, Group::setFullName, true); + } + + // 1. Initialize Buttons + saveButton = new Button(messages.saveButton()); + saveButton.addStyleName("btn btn-primary btn-play"); + + cancelButton = new Button(messages.cancelButton()); + cancelButton.addStyleName("btn btn-link"); + + // 2. Wrap buttons in a FlowPanel for spacing + FlowPanel actionsPanel = new FlowPanel(); + actionsPanel.addStyleName("alignButtonsPanel"); // Uses your existing CSS spacing + actionsPanel.add(saveButton); + actionsPanel.add(cancelButton); + + // 3. Inject the buttons at the bottom of the generic form + groupForm.addCustomWidget(actionsPanel); + + // Initialize the composite using the generic form as the root widget + initWidget(groupForm); + } + + /** + * Defines what happens when the Save button is clicked. It automatically + * validates the form before executing the runnable. + */ + public void setSaveHandler(Runnable onSave) { + saveButton.addClickHandler(event -> { + if (isValid()) { + onSave.run(); + } + }); + } + + /** + * Defines what happens when the Cancel button is clicked. + */ + public void setCancelHandler(Runnable onCancel) { + cancelButton.addClickHandler(event -> onCancel.run()); + } + + public void setGroup(Group group) { + groupForm.setModel(group); + } + + @Override + public Group getValue() { + return groupForm.getValue(); + } + + @Override + public boolean isValid() { + return groupForm.isValid(); + } + + public boolean isChanged() { + return groupForm.isChanged(); + } + + @Override + public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) { + return groupForm.addValueChangeHandler(handler); + } + + public void clear() { + setGroup(new Group()); + } +} \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/data/panels/PasswordDataPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/data/panels/PasswordDataPanel.java new file mode 100644 index 0000000000..0cadb2cafe --- /dev/null +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/management/members/data/panels/PasswordDataPanel.java @@ -0,0 +1,189 @@ +package org.roda.wui.client.management.members.data.panels; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.event.dom.client.ChangeHandler; +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.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.PasswordTextBox; +import com.google.gwt.user.client.ui.Widget; +import config.i18n.client.ClientMessages; +import org.roda.wui.client.common.forms.GenericDataPanel; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Miguel Guimarães + */ + +public class PasswordDataPanel extends Composite implements GenericDataPanel, HasValueChangeHandlers { + + private static final ClientMessages messages = GWT.create(ClientMessages.class); + private final boolean mandatory; + private PasswordTextBox passwordBox; + private PasswordTextBox confirmBox; + private HTML errors; + private boolean changed = false; + + /** + * @param mandatory + * If true, the password cannot be empty. If false (e.g., Edit Mode), + * it only validates if the user types something. + */ + public PasswordDataPanel(boolean mandatory) { + this.mandatory = mandatory; + + FlowPanel mainContainer = new FlowPanel(); + mainContainer.addStyleName("generic-data-panel-fields"); + + // 1. Initialize Inputs + passwordBox = new PasswordTextBox(); + passwordBox.addStyleName("form-textbox"); + + confirmBox = new PasswordTextBox(); + confirmBox.addStyleName("form-textbox"); + + // 2. Initialize Labels + Label passwordLabel = new Label(messages.password()); + if (mandatory) { + passwordLabel.setText(passwordLabel.getText() + "*"); + } + passwordLabel.addStyleName("form-label"); + + Label confirmLabel = new Label(messages.passwordConfirmation()); + if (mandatory) { + confirmLabel.setText(confirmLabel.getText() + "*"); + } + confirmLabel.addStyleName("form-label"); + + // 4. Setup Error HTML + errors = new HTML(); + errors.setVisible(false); + mainContainer.add(errors); + + // 3. Add to layout using the stacked helper + mainContainer.add(createStackedFieldRow(passwordLabel, passwordBox)); + mainContainer.add(createStackedFieldRow(confirmLabel, confirmBox)); + + // 5. Setup Handlers + ChangeHandler changeHandler = event -> onChange(); + KeyUpHandler keyUpHandler = event -> onChange(); + + passwordBox.addChangeHandler(changeHandler); + passwordBox.addKeyUpHandler(keyUpHandler); + confirmBox.addChangeHandler(changeHandler); + confirmBox.addKeyUpHandler(keyUpHandler); + + initWidget(mainContainer); + } + + /** + * Helper method to create a stacked layout:
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 @@ -1111,7 +1125,7 @@ username:User name password:Password passwordConfirmation:Password confirmation passwordNote:(more than 6 characters) -userDataChangePassword:Change +userDataChangePassword:Set password fullname:Full name email:Email address address:Postal address @@ -1448,7 +1462,10 @@ disposalScheduleStateCol:State disposalScheduleState:{0} disposalScheduleState[ACTIVE]:Active disposalScheduleState[INACTIVE]:Inactive -disposalScheduleUsedInRule:Is being used in a disposal rule +disposalScheduleUsedInRule:in use by a disposal rule +disposalScheduleRemoveConfirmDialogTitle: Remove disposal schedule +disposalScheduleRemoveConfirmDialogMessage: Are you sure you want to remove the disposal schedule? +disposalScheduleRemovedWithSuccess: Disposal schedule removed with success disposalHoldTitle:Title disposalHoldIdentifier:Identifier disposalHoldDescription:Description @@ -1484,8 +1501,9 @@ createDisposalHoldFailure:Could not create disposal hold due to an error: {0}. showDisposalRuleTitle:Disposal rule showDisposalHoldTitle:Disposal hold showDisposalScheduleTitle:Disposal Schedule +showDisposalConfirmationTitle:Disposal confirmation applyDisposalScheduleButton:Destroy -deleteDisposalConfirmationReport:Cancel +deleteDisposalConfirmationReport:Withdraw records permanentlyDeleteFromBinButton:Permanently delete reExecuteDisposalDestroyActionButton:Re-execute destruction recoverDisposalConfirmationExecutionFailedButton:Recover AIPs @@ -1494,7 +1512,7 @@ newDisposalConfirmationButton:New confirmation disassociateDisposalScheduleButton:Disassociate schedule disassociateDisposalHoldButton:Disassociate hold associateDisposalScheduleButton:Associate disposal schedule -createDisposalScheduleButton:Create schedule +createDisposalScheduleButton:Create disposal schedule disposalScheduleSelectionDialogTitle:Change disposal schedule changeDisposalScheduleActionTitle:Change disposal schedule createDisposalConfirmationActionTitle:Create disposal confirmation @@ -1511,7 +1529,7 @@ disposalActionLabel:Disposal action holdStatusLabel:Hold status disposalOnHoldStatusLabel:On hold disposalClearStatusLabel:Not on hold -disposalScheduleListAips:Records with this schedule +disposalScheduleListAips:Intellectual entities associated disposalHoldListAips:Records with this hold deleteConfirmationReportDialogTitle:Confirm disposal confirmation deletion deleteConfirmationReportDialogMessage:Are you sure you want to withdraw the intellectual entities from this disposal confirmation? @@ -1537,7 +1555,7 @@ disposalRulePreviewHelpText:Select a selection method and add the criteria. Clic disposalRulePreviewButtonText:Preview disposalTitle:Disposal conditionActualParent:Actual parent -editRulesOrder:Edit rules order +editRulesOrder:Change order editRulesOrderTop:Top editRulesOrderUp:Up editRulesOrderDown:Down @@ -1708,9 +1726,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 +1743,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 @@ -1790,6 +1813,7 @@ reasonPluginIsNotIngest: The plugin is not an ingest plugin reasonRiskHasNoHistory: The risk has no history reasonCantActOnUser: Cannot execute on user reasonCantActOnGroup: Cannot execute on group +reasonHoldAlreadyLifted: The disposal hold is lifted # Details page detailsAIP: AIP @@ -1799,7 +1823,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 @@ -1808,6 +1834,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 @@ -1823,3 +1881,29 @@ emailViewerExternalImagesTitle=External images blocked emailViewerExternalImagesMessage=Images from the following domains are blocked to protect your privacy: emailViewerLoadImagesOnce=Load images once emailViewerAlwaysTrustSender=Always trust sender + +disposalScheduleSuccessfullyCreated: Disposal schedule successfully created +disposalScheduleSuccessfullyUpdated: Disposal schedule successfully updated + +disposalHoldSuccessfullyCreated: Disposal hold successfully created +disposalHoldSuccessfullyUpdated: Disposal hold successfully updated + +updateDisposalRuleOrderSuccessMessage: Disposal rules order successfully updated + +moveToTop: Move to the top +moveToBottom: Move to the bottom +moveToPositionNumber: Move to position #: + +disposalRuleChangeOrderMessage: Where would you like to move them? + +disposalRuleSuccessfullyCreated: Disposal rule successfully created +disposalRuleSuccessfullyUpdated: Disposal rule successfully updated + +userSuccessfullyCreated: User successfully created +userSuccessfullyUpdated: User successfully updated +failedToUpdateUser: Failed to update user +groupSuccessfullyCreated: Group successfully created +groupSuccessfullyUpdated: Group successfully updated +failedToUpdateGroup: Failed to update group + +disposalPolicyOverdueRecordsTitle: Overdue records \ No newline at end of file diff --git a/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_de_AT.properties b/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_de_AT.properties index 0d4b365cdb..4203bcf7b6 100644 --- a/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_de_AT.properties +++ b/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_de_AT.properties @@ -225,10 +225,10 @@ someOfAObject[org.roda.core.data.v2.formats.Format]:Formate someOfAObject[org.roda.core.data.v2.ip.metadata.IndexedPreservationEvent]:Erhaltungsmaßnahmen someOfAObject[org.roda.core.data.v2.ip.metadata.IndexedPreservationAgent]:Preservation-Werkzeuge someOfAObject[org.roda.core.data.v2.Void]:Keine Eingabeobjekte -someOfAObject[org.roda.core.data.v2.disposal.schedule.DisposalSchedules]:Entsorgungspläne -someOfAObject[org.roda.core.data.v2.disposal.hold.DisposalHolds]:Entsorgungssperren +someOfAObject[org.roda.core.data.v2.disposal.schedule.DisposalSchedule]:Entsorgungspläne +someOfAObject[org.roda.core.data.v2.disposal.hold.DisposalHold]:Entsorgungssperren someOfAObject[org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation]:Entsorgungsbestätigung -someOfAObject[org.roda.core.data.v2.disposal.rule.DisposalRules]:Entsorgungsregeln +someOfAObject[org.roda.core.data.v2.disposal.rule.DisposalRule]:Entsorgungsregeln someOfAObject[org.roda.core.data.v2.synchronization.central.DistributedInstances]:verteilte Instanzen someOfAObject[org.roda.core.data.v2.accessKey.AccessKeys]:Zugangstoken oneOfAObject:{0} diff --git a/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_de_DE.properties b/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_de_DE.properties index aa64179729..58308e8e32 100644 --- a/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_de_DE.properties +++ b/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_de_DE.properties @@ -225,10 +225,10 @@ someOfAObject[org.roda.core.data.v2.formats.Format]:Formate someOfAObject[org.roda.core.data.v2.ip.metadata.IndexedPreservationEvent]:Erhaltungsereignisse someOfAObject[org.roda.core.data.v2.ip.metadata.IndexedPreservationAgent]:Erhaltungsmittel someOfAObject[org.roda.core.data.v2.Void]:Keine Eingabeobjekte -someOfAObject[org.roda.core.data.v2.disposal.schedule.DisposalSchedules]:Kassationspläne -someOfAObject[org.roda.core.data.v2.disposal.hold.DisposalHolds]:Kassationssperren +someOfAObject[org.roda.core.data.v2.disposal.schedule.DisposalSchedule]:Kassationspläne +someOfAObject[org.roda.core.data.v2.disposal.hold.DisposalHold]:Kassationssperren someOfAObject[org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation]:Kassationsbestätigung -someOfAObject[org.roda.core.data.v2.disposal.rule.DisposalRules]:Kassationsregeln +someOfAObject[org.roda.core.data.v2.disposal.rule.DisposalRule]:Kassationsregeln someOfAObject[org.roda.core.data.v2.synchronization.central.DistributedInstances]:verteilte Instanzen someOfAObject[org.roda.core.data.v2.accessKey.AccessKeys]:Zugangstoken oneOfAObject:{0} diff --git a/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_es.properties b/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_es.properties index 7433cbc9b0..16ea5b2a27 100644 --- a/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_es.properties +++ b/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_es.properties @@ -225,10 +225,10 @@ someOfAObject[org.roda.core.data.v2.formats.Format]:formats someOfAObject[org.roda.core.data.v2.ip.metadata.IndexedPreservationEvent]:preservation events someOfAObject[org.roda.core.data.v2.ip.metadata.IndexedPreservationAgent]:preservation agents someOfAObject[org.roda.core.data.v2.Void]:No hay objetos de entrada -someOfAObject[org.roda.core.data.v2.disposal.schedule.DisposalSchedules]:disposal schedules -someOfAObject[org.roda.core.data.v2.disposal.hold.DisposalHolds]:disposal holds -someOfAObject[org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation]:disposal confirmation -someOfAObject[org.roda.core.data.v2.disposal.rule.DisposalRules]:disposal rules +someOfAObject[org.roda.core.data.v2.disposal.schedule.DisposalSchedule]:disposal schedules +someOfAObject[org.roda.core.data.v2.disposal.hold.DisposalHold]:disposal holds +someOfAObject[org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation]:disposal confirmations +someOfAObject[org.roda.core.data.v2.disposal.rule.DisposalRule]:disposal rules someOfAObject[org.roda.core.data.v2.synchronization.central.DistributedInstances]:distributed instances someOfAObject[org.roda.core.data.v2.accessKey.AccessKeys]:access tokens oneOfAObject:{0} 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..d6e7f00ffa 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 @@ -228,12 +228,14 @@ someOfAObject[org.roda.core.data.v2.formats.Format]:formatos someOfAObject[org.roda.core.data.v2.ip.metadata.IndexedPreservationEvent]:eventos de preservação someOfAObject[org.roda.core.data.v2.ip.metadata.IndexedPreservationAgent]:agentes de preservação someOfAObject[org.roda.core.data.v2.Void]:Sem objetos de entrada -someOfAObject[org.roda.core.data.v2.disposal.schedule.DisposalSchedules]:tabelas de seleção -someOfAObject[org.roda.core.data.v2.disposal.hold.DisposalHolds]:suspensões de eliminação +someOfAObject[org.roda.core.data.v2.disposal.schedule.DisposalSchedule]:tabelas de seleção +someOfAObject[org.roda.core.data.v2.disposal.hold.DisposalHold]:suspensões de eliminação someOfAObject[org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation]:autos de eliminação -someOfAObject[org.roda.core.data.v2.disposal.rule.DisposalRules]:condições de eliminação +someOfAObject[org.roda.core.data.v2.disposal.rule.DisposalRule]: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 @@ -522,6 +525,9 @@ searchDropdownLabels[org.roda.core.data.v2.ip.metadata.IndexedPreservationEvent] searchDropdownLabels[org.roda.core.data.v2.formats.Format]:formatos searchDropdownLabels[org.roda.core.data.v2.Void]:Sem objetos de entrada searchDropdownLabels[org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation]:autos de eliminação +searchDropdownLabels[org.roda.core.data.v2.disposal.scheule.DisposalSchedule]:Tabelas de seleção +searchDropdownLabels[org.roda.core.data.v2.disposal.hold.DisposalHold]:Suspensões de eliminação +searchDropdownLabels[org.roda.core.data.v2.disposal.rule.DisposalRule]:Condições de eliminação searchListBoxItems:Entidades intelectuais searchListBoxRepresentations:Representações searchListBoxFiles:Ficheiros @@ -732,6 +738,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 @@ -1106,7 +1116,7 @@ username:Nome de utilizador password:Senha passwordConfirmation:Confirmação de senha passwordNote:(mais de 6 caracteres) -userDataChangePassword:Alterar +userDataChangePassword:Alterar password fullname:Nome completo email:Endereço electrónico address:Endereço postal @@ -1443,6 +1453,9 @@ disposalScheduleState:{0} disposalScheduleState[ACTIVE]:Ativo disposalScheduleState[INACTIVE]:Inativo disposalScheduleUsedInRule:Está a ser usado numa codição de eliminação +disposalScheduleRemoveConfirmDialogTitle: Eliminar tabela de seleção +disposalScheduleRemoveConfirmDialogMessage: Tem a certeza que deseja eliminar esta tabela de seleção? +disposalScheduleRemovedWithSuccess: Tabela de seleção eliminada com sucesso disposalHoldTitle:Título disposalHoldIdentifier:Identificador disposalHoldDescription:Descrição @@ -1784,6 +1797,7 @@ reasonPluginIsNotIngest: O plugin não é um plugin de ingestão reasonRiskHasNoHistory: O risco não tem histórico reasonCantActOnUser: Não é possível executar num utilizador reasonCantActOnGroup: Não é possível executar num grupo +reasonHoldAlreadyLifted: A suspensão de eliminação foi levantada # Details page detailsAIP: AIP @@ -1801,3 +1815,59 @@ 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 + +disposalScheduleSuccessfullyCreated: Tabela de seleção criada com sucesso +disposalScheduleSuccessfullyUpdated: Tabela de seleção atualizada com sucesso + +disposalHoldSuccessfullyCreated: Suspensão de eliminação criada com sucesso +disposalHoldSuccessfullyUpdated: Suspensão de eliminação atualizada com sucesso + +updateDisposalRuleOrderSuccessMessage: Ordem das condições de eliminação atualizada com sucesso + +moveToTop: Mover para o topo +moveToBottom: Mover para o fundo +moveToPositionNumber: Mover para a posição #: + +disposalRuleChangeOrderMessage: Para onde os quer mover? + +disposalRuleSuccessfullyCreated: Condição de eliminação criada com sucesso +disposalRuleSuccessfullyUpdated: Condição de eliminação atualizada com sucesso + +userSuccessfullyCreated: Utilizador criado com sucesso +userSuccessfullyUpdated: Utilizador atualizado com sucesso +groupSuccessfullyCreated: Grupo criado com sucesso +groupSuccessfullyUpdated: Grupo atualizado com sucesso + +disposalPolicyOverdueRecordsTitle: Entidades expiradas \ 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..92f3a6e920 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 @@ -2038,6 +2038,30 @@ ui.lists.Disposal_confirmations.facets.parameters.state.sort = COUNT ui.lists.Disposal_confirmations.facets.parameters.createdBy.type = SimpleFacetParameter ui.lists.Disposal_confirmations.facets.parameters.createdBy.sort = COUNT +# Disposal Schedule on PolicyPage +ui.lists.DisposalPolicyPage_disposalSchedules.facets.parameters = state +ui.lists.DisposalPolicyPage_disposalSchedules.facets.parameters = action +ui.lists.DisposalPolicyPage_disposalSchedules.facets.parameters = retentionPeriodDuration +ui.lists.DisposalPolicyPage_disposalSchedules.facets.parameters = retentionPeriodIntervalCode +ui.lists.DisposalPolicyPage_disposalSchedules.facets.parameters.state.type = SimpleFacetParameter +ui.lists.DisposalPolicyPage_disposalSchedules.facets.parameters.state.sort = COUNT +ui.lists.DisposalPolicyPage_disposalSchedules.facets.parameters.action.type = SimpleFacetParameter +ui.lists.DisposalPolicyPage_disposalSchedules.facets.parameters.action.sort = COUNT +ui.lists.DisposalPolicyPage_disposalSchedules.facets.parameters.retentionPeriodDuration.type = SimpleFacetParameter +ui.lists.DisposalPolicyPage_disposalSchedules.facets.parameters.retentionPeriodDuration.sort = COUNT +ui.lists.DisposalPolicyPage_disposalSchedules.facets.parameters.retentionPeriodIntervalCode.type = SimpleFacetParameter +ui.lists.DisposalPolicyPage_disposalSchedules.facets.parameters.retentionPeriodIntervalCode.sort = COUNT + +# Disposal Hold on PolicyPage +ui.lists.DisposalPolicyPage_disposalHolds.facets.parameters = state +ui.lists.DisposalPolicyPage_disposalHolds.facets.parameters.state.type = SimpleFacetParameter +ui.lists.DisposalPolicyPage_disposalHolds.facets.parameters.state.sort = COUNT + +# Disposal Rule on PolicyPage +ui.lists.DisposalPolicyPage_disposalRules.facets.parameters = selectionMethod +ui.lists.DisposalPolicyPage_disposalRules.facets.parameters.selectionMethod.type = SimpleFacetParameter +ui.lists.DisposalPolicyPage_disposalRules.facets.parameters.selectionMethod.sort = COUNT + ########################################################################## # List configuration # @@ -2613,11 +2637,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)