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/CLAUDE.md b/CLAUDE.md index fae39dd540..dd661b9e99 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -84,7 +84,7 @@ roda/ 1. **Java 21** (Oracle JDK) — strictly required for compilation 2. **Maven 3.8.6+** — build tool -3. **Docker & Docker Compose** — for running Solr, PostgreSQL, LDAP, etc. +3. **Docker** — required for running the application locally and for tests (via Testcontainers — no manual `docker compose` needed for tests) 4. **GitHub account with PAT** — required for GitHub Packages dependency resolution **Configure Maven for GitHub Packages** (`~/.m2/settings.xml`): @@ -112,6 +112,10 @@ The PAT must have `read:packages` permission. Without this, the build will fail ### Starting Development Dependencies +**For running tests:** No manual setup needed — tests use **Testcontainers** (`TestContainersManager`) which automatically starts ZooKeeper, Solr, PostgreSQL, Mailpit, ClamAV, and Siegfried as ephemeral Docker containers. The `RodaContainersLifecycleListener` TestNG suite listener wires this up before any test class loads. Docker must be running on the host, but no `docker compose` command is required. + +**For running the application locally:** + ```bash # Create required data directories mkdir -p $HOME/.roda/data/{storage,staging-storage} @@ -120,7 +124,7 @@ mkdir -p $HOME/.roda/data/{storage,staging-storage} docker compose -f deploys/standalone/docker-compose-dev.yaml up -d ``` -Services and ports: +Services and ports (for local app, not tests): - ZooKeeper: `2181` - Apache Solr: `8983` - PostgreSQL: `5432` @@ -196,25 +200,23 @@ mvn -f dev/codeserver gwt:codeserver -DrodaPath=$(pwd) ### Running Tests +Tests use **Testcontainers** — no environment variables or `docker compose` setup required. Docker must be available on the host. + ```bash -# All tests (requires Docker services running) -mvn clean test +# All tests +mvn clean test -Pcore # CI subset only (faster) -mvn -Dtestng.groups="travis" -Denforcer.skip=true clean org.jacoco:jacoco-maven-plugin:prepare-agent test +mvn -Dtestng.groups="travis" -Denforcer.skip=true clean org.jacoco:jacoco-maven-plugin:prepare-agent test -Pcore + +# Specific test class +mvn -pl roda-core/roda-core-tests -am test -Dtest=NestedDocumentSearchTest -Dtestng.groups=dev -Denforcer.skip=true -DfailIfNoTests=false # Skip tests mvn clean package -Dmaven.test.skip=true ``` -### Required Environment Variables (for tests matching CI) - -``` -RODA_CORE_SOLR_TYPE=CLOUD -SIEGFRIED_MODE=standalone -``` - -See `.github/workflows/CI.yml` for the full CI test environment configuration. +See `.github/workflows/CI.yml` for the full CI configuration. ### Key Test Classes 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..4c97634bd3 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 { @@ -172,6 +176,15 @@ public enum DateGranularity { public static final String UI_LISTS_PAGE_SIZE_INITIAL = "pageSize.initial"; public static final String UI_LISTS_PAGE_SIZE_INCREMENT = "pageSize.increment"; + public static final String UI_LISTS_INCLUDE_NESTED_DOCUMENTS = "includeNestedDocuments"; + + public static final String UI_CATALOGUE_VIRTUAL_PROPERTY = "ui.catalogue.virtual"; + public static final String UI_LISTS_CATALOGUE_LABEL_I18N_PROPERTY = "catalogue.label.i18n"; + public static final String UI_LISTS_CATALOGUE_ICON_PROPERTY = "catalogue.icon"; + public static final String UI_LISTS_CATALOGUE_FILTER = "catalogue.filter"; + public static final String UI_LISTS_CATALOGUE_CHILDOF_FILTER = "catalogue.childOf.filter"; + public static final String UI_LISTS_CATALOGUE_CLICK_ACTION = "catalogue.click_action"; + public static final String UI_LISTS_CATALOGUE_CLICK_ACTION_BROWSE_PARENT = "browse_parent"; public static final String UI_ICONS_CLASS = "ui.icons.class"; public static final String UI_SERVICE_DROPFOLDER_URL = "ui.service.dropfolder.url"; @@ -932,6 +945,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 +962,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 +1326,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"; @@ -1946,6 +1989,8 @@ public String toString() { public static final String SEARCH_FIELD_TYPE_SUGGEST_FIELD = "suggestField"; public static final String SEARCH_FIELD_TYPE_SUGGEST_PARTIAL = "suggestPartial"; public static final String SEARCH_FIELD_TYPE_CONTROLLED = "controlled"; + public static final String SEARCH_FIELD_NESTED_TYPE = "nestedType"; + public static final String SEARCH_FIELD_NESTED_PARENT_TYPE = "nestedParentType"; public static final String SEARCH_WITH_PREFILTER_HANDLER = "$prefilter"; public static final String SEARCH_WITH_SAVED_HANDLER = "$savedSearch"; @@ -2161,11 +2206,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/FindRequest.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/index/FindRequest.java index 060580bee0..048bcf34a0 100644 --- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/index/FindRequest.java +++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/index/FindRequest.java @@ -61,6 +61,8 @@ public class FindRequest extends CountRequest { private Long childrenLimit; @JsonProperty("childrenFilter") private Filter childrenFilter; + @JsonProperty("includeNestedDocuments") + private boolean includeNestedDocuments; // Private constructor for Jackson deserialization private FindRequest(FindRequestBuilder builder) { @@ -76,6 +78,7 @@ private FindRequest(FindRequestBuilder builder) { this.childrenFieldsToReturn = builder.childrenFieldsToReturn; this.childrenLimit = builder.childrenLimit; this.childrenFilter = builder.childrenFilter; + this.includeNestedDocuments = builder.includeNestedDocuments; } public Sorter getSorter() { @@ -122,6 +125,10 @@ public Filter getChildrenFilter() { return childrenFilter; } + public boolean isIncludeNestedDocuments() { + return includeNestedDocuments; + } + public static FindRequestBuilder getBuilder(final Filter filter, boolean onlyActive) { return new FindRequestBuilder(filter, onlyActive); } @@ -141,6 +148,7 @@ public static class FindRequestBuilder { private List childrenFieldsToReturn; private Long childrenLimit; private Filter childrenFilter; + private boolean includeNestedDocuments; public FindRequestBuilder(@JsonProperty("filter") final Filter filter, @JsonProperty("onlyActive") boolean onlyActive) { @@ -155,6 +163,7 @@ public FindRequestBuilder(@JsonProperty("filter") final Filter filter, this.fieldsToReturn = Collections.emptyList(); this.collapse = null; this.children = false; + this.includeNestedDocuments = false; } public FindRequest build() { @@ -215,5 +224,10 @@ public FindRequestBuilder withChildrenFilter(Filter childrenFilter) { this.childrenFilter = childrenFilter; return this; } + + public FindRequestBuilder withIncludeNestedDocuments(boolean includeNestedDocuments) { + this.includeNestedDocuments = includeNestedDocuments; + return this; + } } } 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-tests/src/main/java/org/roda/core/CorporaConstants.java b/roda-core/roda-core-tests/src/main/java/org/roda/core/CorporaConstants.java index 57add1a0d6..ce60ca7d59 100644 --- a/roda-core/roda-core-tests/src/main/java/org/roda/core/CorporaConstants.java +++ b/roda-core/roda-core-tests/src/main/java/org/roda/core/CorporaConstants.java @@ -94,6 +94,10 @@ public final class CorporaConstants { public static final String SOURCE_DESC_METADATA_CONTAINER = "DescriptiveMetadata"; public static final String STRANGE_DESC_METADATA_FILE = "strange.xml"; + public static final String EMAIL_ARCHIVE_FULL_FILE = "emailarchive_full.xml"; + public static final String EMAIL_ARCHIVE_MINIMAL_FILE = "emailarchive_minimal.xml"; + public static final String EMAIL_ARCHIVE_NO_EMAILS_FILE = "emailarchive_no_emails.xml"; + public static final String EMAIL_ARCHIVE_METADATA_TYPE = "emailarchive"; public static final String TEXT_XML = "text/xml"; public static final String REPRESENTATION_1_PREMIS_EVENT_ID = "urn:roda:premis:event:roda_398"; diff --git a/roda-core/roda-core-tests/src/main/java/org/roda/core/index/EmailArchiveCrosswalkTest.java b/roda-core/roda-core-tests/src/main/java/org/roda/core/index/EmailArchiveCrosswalkTest.java new file mode 100644 index 0000000000..0721ed9bbd --- /dev/null +++ b/roda-core/roda-core-tests/src/main/java/org/roda/core/index/EmailArchiveCrosswalkTest.java @@ -0,0 +1,239 @@ +/** + * 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.index; + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertNull; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collection; + +import org.apache.solr.common.SolrInputDocument; +import org.apache.solr.common.SolrInputField; +import org.roda.core.CorporaConstants; +import org.roda.core.RodaCoreFactory; +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.exceptions.GenericException; +import org.roda.core.data.exceptions.NotFoundException; +import org.roda.core.data.exceptions.RODAException; +import org.roda.core.index.utils.SolrUtils; +import org.roda.core.storage.Binary; +import org.roda.core.storage.DefaultStoragePath; +import org.roda.core.storage.StorageService; +import org.roda.core.storage.fs.FileStorageService; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +@Test(groups = {RodaConstants.TEST_GROUP_ALL, RodaConstants.TEST_GROUP_DEV, RodaConstants.TEST_GROUP_TRAVIS}) +public class EmailArchiveCrosswalkTest { + + private static StorageService corporaService; + + @BeforeClass + public static void setUp() throws URISyntaxException, GenericException { + URL corporaURL = IndexServiceTest.class.getResource("/corpora"); + Path corporaPath = Paths.get(corporaURL.toURI()); + corporaService = new FileStorageService(corporaPath); + } + + @BeforeMethod + public void init() { + RodaCoreFactory.instantiateTest(false, false, false, false, false, false, false); + } + + @AfterMethod + public void cleanup() throws NotFoundException, GenericException, IOException { + RodaCoreFactory.shutdown(); + } + + // --------------------------------------------------------------------------- + // Full fixture — 3 emails + // --------------------------------------------------------------------------- + + @Test + public void testFullCrosswalkProducesParentFields() throws RODAException { + SolrInputDocument doc = getCrosswalkResult(CorporaConstants.EMAIL_ARCHIVE_FULL_FILE); + + assertNotNull(doc); + assertFieldValue(doc, "custodian_txt", "João Silva"); + assertFieldValue(doc, "emailAddress_s", "joao.silva@empresa.pt"); + assertFieldValue(doc, "totalMessages_i", "3"); + assertFieldValue(doc, "originalFormat_s", "PST"); + assertFieldValue(doc, "archivingMotive_txt", "Offboarding"); + assertFieldValue(doc, "content_type", "emailarchive"); + } + + @Test + public void testFullCrosswalkProducesDateFields() throws RODAException { + SolrInputDocument doc = getCrosswalkResult(CorporaConstants.EMAIL_ARCHIVE_FULL_FILE); + + assertNotNull(doc); + assertFieldValue(doc, "dateStart_dt", "2020-01-01T00:00:00Z"); + assertFieldValue(doc, "dateEnd_dt", "2023-12-31T00:00:00Z"); + } + + @Test + public void testFullCrosswalkProducesThreeChildDocuments() throws RODAException { + SolrInputDocument doc = getCrosswalkResult(CorporaConstants.EMAIL_ARCHIVE_FULL_FILE); + + assertNotNull(doc); + SolrInputField emailsField = doc.getField("emails"); + assertNotNull("'emails' field must be present for nested children", emailsField); + + Collection children = getChildDocuments(emailsField); + assertEquals("Expected 3 child email documents", 3, children.size()); + } + + @Test + public void testFullCrosswalkFirstChildFields() throws RODAException { + SolrInputDocument doc = getCrosswalkResult(CorporaConstants.EMAIL_ARCHIVE_FULL_FILE); + SolrInputDocument first = getChildAt(doc, 0); + + assertFieldValue(first, "content_type", "email"); + assertFieldValue(first, "messageId_s", ""); + assertFieldValue(first, "subject_txt", "Quarterly Report Q1 2021"); + assertFieldValue(first, "sender_s", "joao.silva@empresa.pt"); + assertFieldValue(first, "sentDate_dt", "2021-03-15T09:42:00Z"); + assertFieldValue(first, "folderPath_s", "Inbox/Projects"); + assertFieldValue(first, "hasAttachments_b", "true"); + assertFieldValue(first, "filePath_s", "Inbox/Projects/msg_001.eml"); + } + + @Test + public void testFullCrosswalkMultipleRecipients() throws RODAException { + SolrInputDocument doc = getCrosswalkResult(CorporaConstants.EMAIL_ARCHIVE_FULL_FILE); + SolrInputDocument first = getChildAt(doc, 0); + + // First email has two recipients: ana.costa and rui.pinto + SolrInputField recipientsField = first.getField("recipients_txt"); + assertNotNull("recipients_txt field must be present", recipientsField); + Collection values = recipientsField.getValues(); + assertNotNull(values); + assertEquals("Expected 2 recipient values", 2, values.size()); + } + + @Test + public void testFullCrosswalkThirdChildFields() throws RODAException { + SolrInputDocument doc = getCrosswalkResult(CorporaConstants.EMAIL_ARCHIVE_FULL_FILE); + SolrInputDocument third = getChildAt(doc, 2); + + assertFieldValue(third, "subject_txt", "Budget Approval Request"); + assertFieldValue(third, "folderPath_s", "Sent"); + assertFieldValue(third, "filePath_s", "Sent/msg_003.eml"); + } + + // --------------------------------------------------------------------------- + // Minimal fixture — 1 email, only required fields + // --------------------------------------------------------------------------- + + @Test + public void testMinimalCrosswalkProducesRequiredFields() throws RODAException { + SolrInputDocument doc = getCrosswalkResult(CorporaConstants.EMAIL_ARCHIVE_MINIMAL_FILE); + + assertNotNull(doc); + assertFieldValue(doc, "custodian_txt", "Jane Doe"); + assertFieldValue(doc, "emailAddress_s", "jane.doe@example.org"); + assertFieldValue(doc, "content_type", "emailarchive"); + } + + @Test + public void testMinimalCrosswalkOmitsAbsentOptionalFields() throws RODAException { + SolrInputDocument doc = getCrosswalkResult(CorporaConstants.EMAIL_ARCHIVE_MINIMAL_FILE); + + assertNotNull(doc); + assertNull("dateStart_dt should be absent when not in source", doc.getField("dateStart_dt")); + assertNull("dateEnd_dt should be absent when not in source", doc.getField("dateEnd_dt")); + assertNull("totalMessages_i should be absent when not in source", doc.getField("totalMessages_i")); + assertNull("originalFormat_s should be absent when not in source", doc.getField("originalFormat_s")); + assertNull("archivingMotive_txt should be absent when not in source", doc.getField("archivingMotive_txt")); + } + + @Test + public void testMinimalCrosswalkProducesOneChild() throws RODAException { + SolrInputDocument doc = getCrosswalkResult(CorporaConstants.EMAIL_ARCHIVE_MINIMAL_FILE); + SolrInputField emailsField = doc.getField("emails"); + assertNotNull(emailsField); + assertEquals(1, getChildDocuments(emailsField).size()); + } + + @Test + public void testMinimalCrosswalkChildHasRequiredFields() throws RODAException { + SolrInputDocument doc = getCrosswalkResult(CorporaConstants.EMAIL_ARCHIVE_MINIMAL_FILE); + SolrInputDocument child = getChildAt(doc, 0); + + assertFieldValue(child, "content_type", "email"); + assertFieldValue(child, "messageId_s", ""); + assertFieldValue(child, "subject_txt", "Hello World"); + assertFieldValue(child, "hasAttachments_b", "false"); + } + + // --------------------------------------------------------------------------- + // No-emails fixture — mailbox with zero email records + // --------------------------------------------------------------------------- + + @Test + public void testNoEmailsCrosswalkProducesParentFieldsOnly() throws RODAException { + SolrInputDocument doc = getCrosswalkResult(CorporaConstants.EMAIL_ARCHIVE_NO_EMAILS_FILE); + + assertNotNull(doc); + assertFieldValue(doc, "custodian_txt", "Empty Mailbox User"); + assertFieldValue(doc, "content_type", "emailarchive"); + assertFieldValue(doc, "totalMessages_i", "0"); + } + + @Test + public void testNoEmailsCrosswalkProducesNoChildDocumentsField() throws RODAException { + SolrInputDocument doc = getCrosswalkResult(CorporaConstants.EMAIL_ARCHIVE_NO_EMAILS_FILE); + assertNull("'emails' field must be absent when there are no child emails", doc.getField("emails")); + } + + // --------------------------------------------------------------------------- + // Helpers + // --------------------------------------------------------------------------- + + private SolrInputDocument getCrosswalkResult(String filename) throws RODAException { + try { + DefaultStoragePath path = DefaultStoragePath.parse( + CorporaConstants.SOURCE_DESC_METADATA_CONTAINER, filename); + Binary binary = corporaService.getBinary(path); + return SolrUtils.getDescriptiveMetadataFields(binary, CorporaConstants.EMAIL_ARCHIVE_METADATA_TYPE, null); + } catch (Exception e) { + Assert.fail("Unexpected exception loading fixture '" + filename + "': " + e.getMessage()); + return null; + } + } + + private void assertFieldValue(SolrInputDocument doc, String fieldName, String expectedValue) { + SolrInputField field = doc.getField(fieldName); + assertNotNull("Field '" + fieldName + "' must be present", field); + assertEquals("Field '" + fieldName + "' value mismatch", expectedValue, field.getValue().toString()); + } + + @SuppressWarnings("unchecked") + private Collection getChildDocuments(SolrInputField emailsField) { + Object value = emailsField.getValue(); + assertNotNull("'emails' field value must not be null", value); + return (Collection) value; + } + + private SolrInputDocument getChildAt(SolrInputDocument parent, int index) { + SolrInputField emailsField = parent.getField("emails"); + assertNotNull(emailsField); + Collection children = getChildDocuments(emailsField); + return children.stream().skip(index).findFirst() + .orElseThrow(() -> new AssertionError("No child document at index " + index)); + } +} diff --git a/roda-core/roda-core-tests/src/main/java/org/roda/core/index/NestedDocumentSearchTest.java b/roda-core/roda-core-tests/src/main/java/org/roda/core/index/NestedDocumentSearchTest.java new file mode 100644 index 0000000000..37e35187bc --- /dev/null +++ b/roda-core/roda-core-tests/src/main/java/org/roda/core/index/NestedDocumentSearchTest.java @@ -0,0 +1,188 @@ +/** + * 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.index; + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertTrue; + +import java.io.IOException; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; + +import org.apache.solr.client.solrj.SolrQuery; +import org.apache.solr.client.solrj.response.QueryResponse; +import org.roda.core.CorporaConstants; +import org.roda.core.RodaCoreFactory; +import org.roda.core.TestsHelper; +import org.roda.core.config.TestConfig; +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.exceptions.RODAException; +import org.roda.core.data.v2.index.IndexResult; +import org.roda.core.data.v2.index.filter.AllFilterParameter; +import org.roda.core.data.v2.index.filter.AndFiltersParameters; +import org.roda.core.data.v2.index.filter.Filter; +import org.roda.core.data.v2.index.filter.ParentWhichFilterParameter; +import org.roda.core.data.v2.index.filter.SimpleFilterParameter; +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.AIP; +import org.roda.core.data.v2.ip.IndexedAIP; +import org.roda.core.index.utils.IterableIndexResult; +import org.roda.core.index.utils.SolrUtils; +import org.roda.core.model.ModelService; +import org.roda.core.security.LdapUtilityTestHelper; +import org.roda.core.storage.DefaultStoragePath; +import org.roda.core.storage.StorageService; +import org.roda.core.storage.fs.FSUtils; +import org.roda.core.storage.fs.FileStorageService; +import org.roda.core.util.IdUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +@SpringBootTest(classes = TestConfig.class) +@Test(groups = {RodaConstants.TEST_GROUP_ALL, RodaConstants.TEST_GROUP_DEV, RodaConstants.TEST_GROUP_TRAVIS}) +public class NestedDocumentSearchTest extends AbstractTestNGSpringContextTests { + + private static final Logger LOGGER = LoggerFactory.getLogger(NestedDocumentSearchTest.class); + + private static Path basePath; + private static ModelService model; + private static IndexService index; + private static LdapUtilityTestHelper ldapUtilityTestHelper; + private static StorageService corporaService; + + @BeforeClass + public void setUp() throws Exception { + basePath = TestsHelper.createBaseTempDir(getClass(), true); + ldapUtilityTestHelper = new LdapUtilityTestHelper(); + + RodaCoreFactory.instantiateTest(true, true, false, false, false, false, false, + ldapUtilityTestHelper.getLdapUtility()); + + model = RodaCoreFactory.getModelService(); + index = RodaCoreFactory.getIndexService(); + + URL corporaURL = getClass().getResource("/corpora"); + corporaService = new FileStorageService(Paths.get(corporaURL.toURI())); + + LOGGER.debug("Running nested document search tests under {}", basePath); + } + + @AfterClass + public void tearDown() throws Exception { + IndexTestUtils.resetIndex(); + ldapUtilityTestHelper.shutdown(); + RodaCoreFactory.shutdown(); + FSUtils.deletePath(basePath); + } + + @AfterMethod + public void cleanUp() throws RODAException { + try (IterableIndexResult result = index.findAll(IndexedAIP.class, Filter.ALL, Collections.emptyList())) { + for (IndexedAIP aip : result) { + try { + model.deleteAIP(aip.getId()); + } catch (Exception e) { + // ignore + } + } + index.clearAIPs(); + } catch (IOException e) { + LOGGER.error("Error cleaning up AIPs", e); + } finally { + index.commitAIPs(); + } + } + + @Test + public void testParentWhichFilterFindsEmailArchiveAIPByChildSender() throws Exception { + // Create AIP and attach emailarchive descriptive metadata + String aipId = IdUtils.createUUID(); + model.createAIP(aipId, corporaService, + DefaultStoragePath.parse(CorporaConstants.SOURCE_AIP_CONTAINER, CorporaConstants.SOURCE_AIP_EMPTY), + RodaConstants.ADMIN); + + org.roda.core.storage.Binary metaBinary = corporaService + .getBinary(DefaultStoragePath.parse(CorporaConstants.SOURCE_DESC_METADATA_CONTAINER, + CorporaConstants.EMAIL_ARCHIVE_FULL_FILE)); + + model.createDescriptiveMetadata(aipId, "emailarchive.xml", metaBinary.getContent(), + CorporaConstants.EMAIL_ARCHIVE_METADATA_TYPE, null, RodaConstants.ADMIN); + + // Reindex and commit + AIP aip = model.retrieveAIP(aipId); + index.reindexAIP(aip); + index.commitAIPs(); + + // Layer 1: parent AIP is indexed + long parentCount = index.count(IndexedAIP.class, + new Filter(new SimpleFilterParameter(RodaConstants.INDEX_UUID, aipId))); + assertEquals("Parent AIP should be indexed", 1L, parentCount); + + // Layer 2: content_type field is populated on the parent document + long contentTypeCount = index.count(IndexedAIP.class, + new Filter(new SimpleFilterParameter("content_type", "emailarchive"))); + assertEquals("AIP should be findable by content_type=emailarchive", 1L, contentTypeCount); + + // Layer 3: child email documents are indexed in Solr + QueryResponse childResp = index.getSolrClient().query(RodaConstants.INDEX_AIP, + new SolrQuery("content_type:email").setRows(0)); + long childCount = childResp.getResults().getNumFound(); + assertTrue("Child email documents should be indexed in Solr (found " + childCount + ")", childCount > 0); + + // Layer 4: block-join ParentWhichFilterParameter — verify generated query string and Solr result + Filter nestedFilter = new Filter( + new ParentWhichFilterParameter( + new SimpleFilterParameter("content_type", "emailarchive"), + new AndFiltersParameters(Arrays.asList( + new SimpleFilterParameter("sender_s", "joao.silva@empresa.pt"), + new SimpleFilterParameter("content_type", "email"))))); + + String nestedQuery = SolrUtils.parseFilter(nestedFilter); + assertEquals("Block-join query must use v= local param to avoid outer-parser whitespace splitting", + "{!parent which=\"content_type:emailarchive\" v=\"(+sender_s:\\\"joao.silva@empresa.pt\\\" +content_type:\\\"email\\\")\"}", + nestedQuery); + + IndexResult result = index.find(IndexedAIP.class, nestedFilter, Sorter.NONE, + new Sublist(0, 10), Collections.emptyList()); + + assertEquals("ParentWhichFilterParameter should return exactly 1 AIP", 1L, result.getTotalCount()); + assertEquals("Returned AIP id should match", aipId, result.getResults().get(0).getId()); + + // Layer 5: AllFilterParameter combined with ParentWhichFilterParameter must not emit *:* prefix + // (production scenario: default filter adds AllFilterParameter, should not break block-join) + Filter combinedFilter = new Filter( + new AllFilterParameter(), + new ParentWhichFilterParameter( + new SimpleFilterParameter("content_type", "emailarchive"), + new AndFiltersParameters(Arrays.asList( + new SimpleFilterParameter("sender_s", "joao.silva@empresa.pt"), + new SimpleFilterParameter("content_type", "email"))))); + + String combinedQuery = SolrUtils.parseFilter(combinedFilter); + assertEquals("AllFilterParameter combined with block-join produces *:* AND prefix (harmless, matches everything)", + "*:* AND {!parent which=\"content_type:emailarchive\" v=\"(+sender_s:\\\"joao.silva@empresa.pt\\\" +content_type:\\\"email\\\")\"}", + combinedQuery); + + IndexResult combinedResult = index.find(IndexedAIP.class, combinedFilter, Sorter.NONE, + new Sublist(0, 10), Collections.emptyList()); + + assertEquals("AllFilter + ParentWhichFilterParameter should return exactly 1 AIP", 1L, + combinedResult.getTotalCount()); + assertEquals("Returned AIP id should match", aipId, combinedResult.getResults().get(0).getId()); + } +} diff --git a/roda-core/roda-core-tests/src/main/resources/corpora/DescriptiveMetadata/emailarchive_full.xml b/roda-core/roda-core-tests/src/main/resources/corpora/DescriptiveMetadata/emailarchive_full.xml new file mode 100644 index 0000000000..c3b239f680 --- /dev/null +++ b/roda-core/roda-core-tests/src/main/resources/corpora/DescriptiveMetadata/emailarchive_full.xml @@ -0,0 +1,44 @@ + + + João Silva + joao.silva@empresa.pt + 2020-01-01 + 2023-12-31 + 3 + PST + Offboarding + + + <msg001@empresa.pt> + Quarterly Report Q1 2021 + joao.silva@empresa.pt + ana.costa@empresa.pt + rui.pinto@empresa.pt + 2021-03-15T09:42:00Z + Inbox/Projects + true + Inbox/Projects/msg_001.eml + + + + <msg002@empresa.pt> + Re: Quarterly Report Q1 2021 + ana.costa@empresa.pt + joao.silva@empresa.pt + 2021-03-15T11:05:00Z + Inbox/Projects + false + Inbox/Projects/msg_002.eml + + + + <msg003@empresa.pt> + Budget Approval Request + joao.silva@empresa.pt + board@empresa.pt + 2021-06-01T08:00:00Z + Sent + true + Sent/msg_003.eml + + diff --git a/roda-core/roda-core-tests/src/main/resources/corpora/DescriptiveMetadata/emailarchive_minimal.xml b/roda-core/roda-core-tests/src/main/resources/corpora/DescriptiveMetadata/emailarchive_minimal.xml new file mode 100644 index 0000000000..c955368e18 --- /dev/null +++ b/roda-core/roda-core-tests/src/main/resources/corpora/DescriptiveMetadata/emailarchive_minimal.xml @@ -0,0 +1,15 @@ + + + Jane Doe + jane.doe@example.org + + + <only-email@example.org> + Hello World + sender@example.org + jane.doe@example.org + 2022-05-10T14:30:00Z + false + Inbox/msg_0001.eml + + diff --git a/roda-core/roda-core-tests/src/main/resources/corpora/DescriptiveMetadata/emailarchive_no_emails.xml b/roda-core/roda-core-tests/src/main/resources/corpora/DescriptiveMetadata/emailarchive_no_emails.xml new file mode 100644 index 0000000000..f74b622617 --- /dev/null +++ b/roda-core/roda-core-tests/src/main/resources/corpora/DescriptiveMetadata/emailarchive_no_emails.xml @@ -0,0 +1,11 @@ + + + + Empty Mailbox User + empty@example.org + 2023-01-01 + 2023-01-01 + 0 + MBOX + Legal Hold + 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/index/utils/SolrUtils.java b/roda-core/roda-core/src/main/java/org/roda/core/index/utils/SolrUtils.java index af3212aaa2..50d297409b 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/index/utils/SolrUtils.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/index/utils/SolrUtils.java @@ -312,6 +312,11 @@ public static Pair, String> find(SolrClient query.addFilterQuery(getFilterQueries(user, justActive, classToRetrieve)); } + if (IndexedAIP.class.isAssignableFrom(classToRetrieve) + && !hasNestedDocumentsFilter(filter, classToRetrieve)) { + query.addFilterQuery("-_nest_path_:*"); + } + query.set(CursorMarkParams.CURSOR_MARK_PARAM, cursorMark); query.setRows(pageSize); query.setSorts(Arrays.asList(SortClause.asc(RodaConstants.INDEX_UUID))); @@ -361,6 +366,12 @@ public static IndexResult find(SolrClient index, Class< query.addFilterQuery(getFilterQueries(user, findRequest.isOnlyActive(), classToRetrieve)); } + if (IndexedAIP.class.isAssignableFrom(classToRetrieve) + && !hasNestedDocumentsFilter(findRequest.getFilter(), classToRetrieve) + && !findRequest.isIncludeNestedDocuments()) { + query.addFilterQuery("-_nest_path_:*"); + } + if (hasNestedDocumentsFilter(findRequest.getFilter(), classToRetrieve)) { ChildOfFilterParameter childOfFilter = (ChildOfFilterParameter) findRequest.getFilter().getParameters() .getFirst(); @@ -1032,33 +1043,115 @@ private static void parseFilterParameter(StringBuilder ret, FilterParameter para private static void appendBlockJoinChildrenFilterParameter(StringBuilder ret, ChildOfFilterParameter parameter, boolean prefixWithANDOperatorIfBuilderNotEmpty) throws RequestNotValidException { - StringBuilder blockMask = new StringBuilder(); - parseFilterParameter(blockMask, parameter.getChildrenOfFilter(), prefixWithANDOperatorIfBuilderNotEmpty); - String replace = blockMask.toString().replace(": ", ":"); + appendANDOperator(ret, prefixWithANDOperatorIfBuilderNotEmpty); + String maskValue = buildBlockJoinMask(parameter.getChildrenOfFilter()); + String escapedMask = maskValue.replace("\"", "\\\""); if (parameter.getParentFilter() != null) { - StringBuilder someParents = new StringBuilder(); - parseFilterParameter(someParents, parameter.getParentFilter(), prefixWithANDOperatorIfBuilderNotEmpty); - - ret.append("{!child of=").append(replace).append("} ").append(someParents); + String parentQuery = buildBlockJoinSubQuery(parameter.getParentFilter()).replace("\"", "\\\""); + ret.append("{!child of=\"").append(escapedMask).append("\" v=\"").append(parentQuery).append("\"}"); } else { - ret.append("{!child of=").append(replace).append("}"); + ret.append("{!child of=\"").append(escapedMask).append("\"}"); } } private static void appendBlockJoinFilterParameter(StringBuilder ret, ParentWhichFilterParameter parameter, boolean prefixWithANDOperatorIfBuilderNotEmpty) throws RequestNotValidException { - StringBuilder blockMask = new StringBuilder(); - parseFilterParameter(blockMask, parameter.getParentFilter(), prefixWithANDOperatorIfBuilderNotEmpty); - String replace = blockMask.toString().replace(": ", ":"); + appendANDOperator(ret, prefixWithANDOperatorIfBuilderNotEmpty); + String maskValue = buildBlockJoinMask(parameter.getParentFilter()); + String escapedMask = maskValue.replace("\"", "\\\""); if (parameter.getChildrenFilter() != null) { - StringBuilder someChildren = new StringBuilder(); - parseFilterParameter(someChildren, parameter.getChildrenFilter(), prefixWithANDOperatorIfBuilderNotEmpty); + String childQuery = buildBlockJoinSubQuery(parameter.getChildrenFilter()).replace("\"", "\\\""); + ret.append("{!parent which=\"").append(escapedMask).append("\" v=\"").append(childQuery).append("\"}"); + } else { + ret.append("{!parent which=\"").append(escapedMask).append("\"}"); + } + } + + private static String buildBlockJoinMask(FilterParameter parameter) throws RequestNotValidException { + if (parameter instanceof SimpleFilterParameter simplePar) { + String value = simplePar.getValue(); + if (value.matches("[\\w.-]+")) { + return simplePar.getName() + ":" + value; + } else { + return simplePar.getName() + ":\"" + value.replace("\"", "\\\"") + "\""; + } + } + StringBuilder sb = new StringBuilder(); + parseFilterParameter(sb, parameter, false); + String s = sb.toString().replace(": ", ":"); + if (s.startsWith("(") && s.endsWith(")")) { + s = s.substring(1, s.length() - 1); + } + return s; + } - ret.append("{!parent which=").append(replace).append("} ").append(someChildren); + private static String buildBlockJoinSubQuery(FilterParameter parameter) throws RequestNotValidException { + StringBuilder sb = new StringBuilder(); + buildBlockJoinSubQueryClause(sb, parameter); + return "(" + sb.toString() + ")"; + } + + private static void buildBlockJoinSubQueryClause(StringBuilder sb, FilterParameter parameter) + throws RequestNotValidException { + if (parameter instanceof SimpleFilterParameter simplePar) { + sb.append("+").append(simplePar.getName()).append(":\"") + .append(simplePar.getValue().replace("\"", "\\\"")).append("\""); + } else if (parameter instanceof BasicSearchFilterParameter basicPar) { + String value = basicPar.getValue(); + if (StringUtils.isBlank(value) || "*".equals(value)) { + sb.append("+").append(basicPar.getName()).append(":*"); + } else if (value.contains("*") || value.contains("?")) { + // wildcards cannot appear inside quoted phrases — emit each token unquoted + for (String token : value.trim().split("\\s+")) { + if (sb.length() > 0) { + sb.append(" "); + } + sb.append("+").append(basicPar.getName()).append(":").append(escapeSolrSpecialChars(token)); + } + } else { + String cleanValue = value.matches("^\".+\"$") ? value.substring(1, value.length() - 1) : value; + sb.append("+").append(basicPar.getName()).append(":\"").append(cleanValue.replace("\"", "\\\"")).append("\""); + } + } else if (parameter instanceof AndFiltersParameters andPar) { + List values = andPar.getValues(); + for (int i = 0; i < values.size(); i++) { + if (i > 0) { + sb.append(" "); + } + buildBlockJoinSubQueryClause(sb, values.get(i)); + } + } else if (parameter instanceof OrFiltersParameters orPar) { + sb.append("("); + List values = orPar.getValues(); + for (int i = 0; i < values.size(); i++) { + if (i > 0) { + sb.append(" OR "); + } + buildBlockJoinSubQueryClause(sb, values.get(i)); + } + sb.append(")"); + } else if (parameter instanceof DateIntervalFilterParameter dateIntervalPar) { + if (dateIntervalPar.getFromValue() != null || dateIntervalPar.getToValue() != null) { + sb.append("+").append(dateIntervalPar.getFromName()).append(":[") + .append(processFromDate(dateIntervalPar.getFromValue())).append(" TO ") + .append(processToDate(dateIntervalPar.getToValue(), dateIntervalPar.getGranularity())).append("]"); + } + } else if (parameter instanceof DateRangeFilterParameter dateRangePar) { + if (dateRangePar.getFromValue() != null || dateRangePar.getToValue() != null) { + sb.append("+").append(dateRangePar.getName()).append(":[") + .append(processFromDate(dateRangePar.getFromValue())).append(" TO ") + .append(processToDate(dateRangePar.getToValue(), dateRangePar.getGranularity(), false)).append("]"); + } + } else if (parameter instanceof LongRangeFilterParameter longRangePar) { + if (longRangePar.getFromValue() != null || longRangePar.getToValue() != null) { + sb.append("+").append(longRangePar.getName()).append(":[") + .append(longRangePar.getFromValue() != null ? longRangePar.getFromValue() : "*").append(" TO ") + .append(longRangePar.getToValue() != null ? longRangePar.getToValue() : "*").append("]"); + } } else { - ret.append("{!parent which=").append(replace).append("}"); + parseFilterParameter(sb, parameter, !sb.isEmpty()); } } @@ -1784,6 +1877,9 @@ public static List suggest(SolrClient index, Class if (hasPermissionFilters(classToRetrieve)) { query.addFilterQuery(getFilterQueries(user, justActive, classToRetrieve)); } + if (IndexedAIP.class.isAssignableFrom(classToRetrieve)) { + query.addFilterQuery("-_nest_path_:*"); + } parseAndConfigureFacets(new Facets(new SimpleFacetParameter(field)), query); List suggestions = new ArrayList<>(); try { 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/crosswalks/ingest/emailarchive.xslt b/roda-core/roda-core/src/main/resources/config/crosswalks/ingest/emailarchive.xslt new file mode 100644 index 0000000000..a7303a3983 --- /dev/null +++ b/roda-core/roda-core/src/main/resources/config/crosswalks/ingest/emailarchive.xslt @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + item + + + + + + + + + T00:00:00Z + T00:00:00Z + + + T00:00:00Z + T00:00:00Z + + + + + + + + + + + emailarchive + + + + + + + email + ACTIVE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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-core/roda-core/src/main/resources/config/schemas/emailarchive.xsd b/roda-core/roda-core/src/main/resources/config/schemas/emailarchive.xsd new file mode 100644 index 0000000000..1f9afec551 --- /dev/null +++ b/roda-core/roda-core/src/main/resources/config/schemas/emailarchive.xsd @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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/AsyncTableCell.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/AsyncTableCell.java index 10046ebc4d..8bc786bb03 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/AsyncTableCell.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/AsyncTableCell.java @@ -389,7 +389,16 @@ public void onValueChange(ValueChangeEvent> event) { // autoUpdateConsumers.add(st -> GWT.log(st.toString())); addAutoUpdateControlListener(); - if (options.isBindOpener()) { + if (options.getCustomOpener() != null) { + final java.util.function.Consumer opener = options.getCustomOpener(); + getSelectionModel().addSelectionChangeHandler(event -> { + ListSelectionState state = getListSelectionState(); + if (state != null) { + clearSelected(); + opener.accept(state.getSelected()); + } + }); + } else if (options.isBindOpener()) { ListSelectionUtils.bindBrowseOpener(this); } @@ -615,7 +624,7 @@ private CompletableFuture> getData(Sublist sublist, Sorter sorter Services services = new Services(reason, "get"); FindRequest findRequest = FindRequest.getBuilder(getFilter(), getJustActive()).withSublist(sublist) .withFacets(getFacets()).withExportFacets(false).withSorter(sorter).withFieldsToReturn(fieldsToReturn) - .withCollapse(getCollapse()).build(); + .withCollapse(getCollapse()).withIncludeNestedDocuments(options.isIncludeNestedDocuments()).build(); return services.rodaEntityRestService(s -> s.find(findRequest, LocaleInfo.getCurrentLocale().getLocaleName()), getClassToReturn()); } 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..fabc33fa4b 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 @@ -10,6 +10,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.function.Consumer; import org.roda.core.data.common.RodaConstants; import org.roda.core.data.v2.index.IndexResult; @@ -54,6 +55,8 @@ public class AsyncTableCellOptions { private String searchPlaceholder; private boolean forceSelectable; private boolean redirectOnSingleResult; + private boolean includeNestedDocuments; + private Consumer customOpener; private List columnOptions; private String defaultSortListColumnName; @@ -88,6 +91,9 @@ public AsyncTableCellOptions(Class classToReturn, String listId) { searchPlaceholder = null; forceSelectable = false; redirectOnSingleResult = false; + customOpener = null; + includeNestedDocuments = ConfigurationManager.getBoolean(false, RodaConstants.UI_LISTS_PROPERTY, listId, + RodaConstants.UI_LISTS_INCLUDE_NESTED_DOCUMENTS); columnOptions = ColumnOptionsFactory.getColumnOptions(listId); defaultSortListColumnName = ConfigurationManager.getStringWithDefault("", RodaConstants.UI_LISTS_PROPERTY, listId, @@ -121,7 +127,6 @@ public AsyncTableCellOptions withFilter(Filter filter) { } public AsyncTableCellOptions withJustActive(boolean justActive) { - this.justActive = justActive; return this; } @@ -319,6 +324,24 @@ public boolean getRedirectOnSingleResult() { return redirectOnSingleResult; } + public boolean isIncludeNestedDocuments() { + return includeNestedDocuments; + } + + public AsyncTableCellOptions withIncludeNestedDocuments(boolean includeNestedDocuments) { + this.includeNestedDocuments = includeNestedDocuments; + return this; + } + + public Consumer getCustomOpener() { + return customOpener; + } + + public AsyncTableCellOptions withCustomOpener(Consumer customOpener) { + this.customOpener = customOpener; + return this; + } + /** * @return the columnOptions */ 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/ConfigurableAsyncTableCell.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/ConfigurableAsyncTableCell.java index b44f3bdd90..8afba84fea 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/ConfigurableAsyncTableCell.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/lists/utils/ConfigurableAsyncTableCell.java @@ -375,7 +375,7 @@ private String renderValue(Object value, RenderingHint hint) { case DATE_FORMAT_SIMPLE: return Humanize.formatDate((String) value, false); case LIST: - List renderedList = ((List) value).stream().map(v -> renderValue(v, hint)).collect(Collectors.toList()); + List renderedList = ((List) value).stream().map(v -> renderValue(v, null)).collect(Collectors.toList()); return StringUtils.prettyPrint(renderedList); default: return value.toString(); 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/AdvancedSearchFieldsPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/AdvancedSearchFieldsPanel.java index 072081bde1..c133fc3636 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/AdvancedSearchFieldsPanel.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/AdvancedSearchFieldsPanel.java @@ -91,6 +91,17 @@ private static List getSearchFieldsFromConfig(String classSimpleNam } searchField.setSuggestPartial(suggestPartial); + + String nestedType = ConfigurationManager.getString(fieldPrefix, RodaConstants.SEARCH_FIELD_NESTED_TYPE); + String nestedParentType = ConfigurationManager.getString(fieldPrefix, + RodaConstants.SEARCH_FIELD_NESTED_PARENT_TYPE); + if (nestedType != null) { + searchField.setNestedType(nestedType); + } + if (nestedParentType != null) { + searchField.setNestedParentType(nestedParentType); + } + searchFields.add(searchField); } } @@ -204,6 +215,10 @@ private void setSuggestions(SearchFieldPanel searchFieldPanel) { } } + public SearchField getSearchField(String id) { + return searchFields.get(id); + } + @Override public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) { return addHandler(handler, ValueChangeEvent.getType()); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/CatalogueSearch.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/CatalogueSearch.java index b2e36cdef1..89f227b893 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/CatalogueSearch.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/CatalogueSearch.java @@ -20,7 +20,9 @@ import org.roda.core.data.common.RodaConstants; import org.roda.core.data.v2.index.IsIndexed; import org.roda.core.data.v2.index.filter.AllFilterParameter; +import org.roda.core.data.v2.index.filter.ChildOfFilterParameter; 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.core.data.v2.ip.IndexedFile; @@ -35,6 +37,8 @@ import org.roda.wui.client.common.utils.JavascriptUtils; import org.roda.wui.client.common.utils.PermissionClientUtils; import org.roda.wui.common.client.ClientLogger; +import org.roda.wui.common.client.tools.ConfigurationManager; +import org.roda.wui.common.client.tools.HistoryUtils; import org.roda.wui.common.client.tools.StringUtils; import com.google.gwt.core.client.GWT; @@ -83,50 +87,93 @@ public CatalogueSearch(List filterHistoryTokens, boolean justActive, Str searchWrapper = new SearchWrapper(true); + // 1. Intellectual Entities (AIP) String preselectedDropdownValue = null; - for (Class searchableClass : searchableClasses) { - if (classFilters.containsKey(searchableClass.getSimpleName())) { - - Filter filter = classFilters.get(searchableClass.getSimpleName()); - ListBuilder listBuilder = null; - if (searchableClass.equals(IndexedAIP.class) - && PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_FIND_AIP)) { - - listBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), - new AsyncTableCellOptions<>(IndexedAIP.class, itemsListId) - .withActionable(AipSearchWrapperActions.getWithoutNoAipActions(null, AIPState.ACTIVE, permissions)) - .withRedirectOnSingleResult(redirectOnSingleResult).withJustActive(justActive).bindOpener() - .withFilter(filter).withStartHidden(startHidden) - .withActionBlacklist(List.of(AipSearchWrapperActions.AipSearchWrapperAction.NEW_CHILD_AIP_BELOW, - AipSearchWrapperActions.AipSearchWrapperAction.APPRAISAL_ACCEPT, - AipSearchWrapperActions.AipSearchWrapperAction.APPRAISAL_REJECT))); - } else if (searchableClass.equals(IndexedRepresentation.class) - && PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_FIND_REPRESENTATION)) { - - listBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), - new AsyncTableCellOptions<>(IndexedRepresentation.class, representationsListId) - .withActionable(RepresentationSearchWrapperActions.getWithoutNoRepresentationActions(null, null)) - .withRedirectOnSingleResult(redirectOnSingleResult).withJustActive(justActive).bindOpener() - .withFilter(filter).withStartHidden(startHidden)); - } else if (searchableClass.equals(IndexedFile.class) - && PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_FIND_FILE)) { - - listBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), - new AsyncTableCellOptions<>(IndexedFile.class, filesListId) - .withActionable(FileSearchWrapperActions.getWithoutNoFileActions(null, null, null, null, null)) - .withRedirectOnSingleResult(redirectOnSingleResult).withJustActive(justActive).bindOpener() - .withFilter(filter).withStartHidden(startHidden)); - } + if (classFilters.containsKey(IndexedAIP.class.getSimpleName()) + && PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_FIND_AIP)) { + Filter filter = classFilters.get(IndexedAIP.class.getSimpleName()); + ListBuilder listBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), + new AsyncTableCellOptions<>(IndexedAIP.class, itemsListId) + .withActionable(AipSearchWrapperActions.getWithoutNoAipActions(null, AIPState.ACTIVE, permissions)) + .withRedirectOnSingleResult(redirectOnSingleResult).withJustActive(justActive).bindOpener() + .withFilter(filter).withStartHidden(startHidden) + .withActionBlacklist(List.of(AipSearchWrapperActions.AipSearchWrapperAction.NEW_CHILD_AIP_BELOW, + AipSearchWrapperActions.AipSearchWrapperAction.APPRAISAL_ACCEPT, + AipSearchWrapperActions.AipSearchWrapperAction.APPRAISAL_REJECT))); + preselectedDropdownValue = IndexedAIP.class.getSimpleName(); + searchWrapper.createListAndSearchPanel(listBuilder, true, true); + } - if (listBuilder != null) { - if (preselectedDropdownValue == null) { - preselectedDropdownValue = searchableClass.getSimpleName(); + // 2. Virtual catalogues (after AIPs, before Representations/Files) + if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_FIND_AIP)) { + List virtualCatalogueIds = ConfigurationManager + .getStringList(RodaConstants.UI_CATALOGUE_VIRTUAL_PROPERTY); + for (String virtualListId : virtualCatalogueIds) { + String childOfFilterStr = ConfigurationManager.getString(RodaConstants.UI_LISTS_PROPERTY, virtualListId, + RodaConstants.UI_LISTS_CATALOGUE_CHILDOF_FILTER); + String filterStr = ConfigurationManager.getString(RodaConstants.UI_LISTS_PROPERTY, virtualListId, + RodaConstants.UI_LISTS_CATALOGUE_FILTER); + + ListBuilder virtualListBuilder = null; + + if (childOfFilterStr != null && !childOfFilterStr.isEmpty()) { + SimpleFilterParameter maskParam = parseFieldValueFilter(childOfFilterStr); + if (maskParam != null) { + Filter childFilter = new Filter(new ChildOfFilterParameter(null, maskParam)); + virtualListBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), + new AsyncTableCellOptions<>(IndexedAIP.class, virtualListId).withFilter(childFilter) + .withIncludeNestedDocuments(true).withStartHidden(startHidden) + .withCustomOpener(aip -> { + String id = aip.getId(); + String parentId = id.contains("/") ? id.split("/")[0] : id; + HistoryUtils.newHistory(HistoryUtils.getHistoryBrowse(parentId)); + })); + } + } else if (filterStr != null && !filterStr.isEmpty()) { + SimpleFilterParameter filterParam = parseFieldValueFilter(filterStr); + if (filterParam != null) { + Filter aipFilter = new Filter(filterParam); + virtualListBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), + new AsyncTableCellOptions<>(IndexedAIP.class, virtualListId).withFilter(aipFilter) + .withJustActive(justActive).bindOpener().withStartHidden(startHidden)); } - searchWrapper.createListAndSearchPanel(listBuilder, true, true); + } + + if (virtualListBuilder != null) { + searchWrapper.createVirtualListAndSearchPanel(virtualListBuilder, true, true); } } } + // 3. Representations and Files + if (classFilters.containsKey(IndexedRepresentation.class.getSimpleName()) + && PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_FIND_REPRESENTATION)) { + Filter filter = classFilters.get(IndexedRepresentation.class.getSimpleName()); + ListBuilder listBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), + new AsyncTableCellOptions<>(IndexedRepresentation.class, representationsListId) + .withActionable(RepresentationSearchWrapperActions.getWithoutNoRepresentationActions(null, null)) + .withRedirectOnSingleResult(redirectOnSingleResult).withJustActive(justActive).bindOpener() + .withFilter(filter).withStartHidden(startHidden)); + if (preselectedDropdownValue == null) { + preselectedDropdownValue = IndexedRepresentation.class.getSimpleName(); + } + searchWrapper.createListAndSearchPanel(listBuilder, true, true); + } + + if (classFilters.containsKey(IndexedFile.class.getSimpleName()) + && PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_FIND_FILE)) { + Filter filter = classFilters.get(IndexedFile.class.getSimpleName()); + ListBuilder listBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(), + new AsyncTableCellOptions<>(IndexedFile.class, filesListId) + .withActionable(FileSearchWrapperActions.getWithoutNoFileActions(null, null, null, null, null)) + .withRedirectOnSingleResult(redirectOnSingleResult).withJustActive(justActive).bindOpener() + .withFilter(filter).withStartHidden(startHidden)); + if (preselectedDropdownValue == null) { + preselectedDropdownValue = IndexedFile.class.getSimpleName(); + } + searchWrapper.createListAndSearchPanel(listBuilder, true, true); + } + if (preselectedDropdownValue != null) { searchWrapper.changeDropdownSelectedValue(preselectedDropdownValue); } @@ -134,6 +181,16 @@ public CatalogueSearch(List filterHistoryTokens, boolean justActive, Str initWidget(uiBinder.createAndBindUi(this)); } + private static SimpleFilterParameter parseFieldValueFilter(String filterStr) { + int colonIdx = filterStr.indexOf(':'); + if (colonIdx > 0 && colonIdx < filterStr.length() - 1) { + String field = filterStr.substring(0, colonIdx).trim(); + String value = filterStr.substring(colonIdx + 1).trim(); + return new SimpleFilterParameter(field, value); + } + return null; + } + public CatalogueSearch(boolean justActive, String itemsListId, String representationsListId, String filesListId, Permissions permissions, boolean startHidden, boolean redirectOnSingleResult) { this(Collections.emptyList(), justActive, itemsListId, representationsListId, filesListId, permissions, startHidden, diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/SearchField.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/SearchField.java index f7fef9703e..108a3f3e58 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/SearchField.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/SearchField.java @@ -22,6 +22,8 @@ public class SearchField implements Serializable { private Tree terms; private String suggestField; private boolean suggestPartial; + private String nestedType; + private String nestedParentType; public SearchField() { super(); @@ -115,10 +117,26 @@ public void setSuggestPartial(boolean suggestPartial) { this.suggestPartial = suggestPartial; } + public String getNestedType() { + return nestedType; + } + + public void setNestedType(String nestedType) { + this.nestedType = nestedType; + } + + public String getNestedParentType() { + return nestedParentType; + } + + public void setNestedParentType(String nestedParentType) { + this.nestedParentType = nestedParentType; + } + @Override public String toString() { return "SearchField [id=" + id + ", searchFields=" + searchFields + ", label=" + label + ", type=" + type + ", fixed=" + fixed + ", terms=" + terms + ", suggestField=" + suggestField + ", suggestPartial=" - + suggestPartial + "]"; + + suggestPartial + ", nestedType=" + nestedType + ", nestedParentType=" + nestedParentType + "]"; } } \ 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/search/SearchPanel.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/SearchPanel.java index 4dce92bee0..8812f4a782 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/SearchPanel.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/SearchPanel.java @@ -9,16 +9,20 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.roda.core.data.common.RodaConstants; import org.roda.core.data.v2.index.IsIndexed; import org.roda.core.data.v2.index.filter.AllFilterParameter; +import org.roda.core.data.v2.index.filter.AndFiltersParameters; 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.FilterParameter; import org.roda.core.data.v2.index.filter.OrFiltersParameters; +import org.roda.core.data.v2.index.filter.ParentWhichFilterParameter; import org.roda.core.data.v2.index.filter.SimpleFilterParameter; import org.roda.core.data.v2.ip.IndexedAIP; import org.roda.wui.client.common.NoAsyncCallback; @@ -197,7 +201,7 @@ private void setupAdvancedSearch(boolean makeAdvancedPanelVisible) { if (advancedSearchEnabled) { searchAdvancedDisclosureButton.addClickHandler(event -> toggleAdvancedSearchPanel()); - advancedSearchFieldsPanel = new AdvancedSearchFieldsPanel(list.getClassToReturn().getSimpleName(), keyCode -> { + advancedSearchFieldsPanel = new AdvancedSearchFieldsPanel(resolveSearchFieldScope(), keyCode -> { if (KeyCodes.KEY_ENTER == keyCode && searchAdvancedGo.isEnabled()) { searchAdvancedGo.click(); } @@ -211,6 +215,12 @@ private void setupAdvancedSearch(boolean makeAdvancedPanelVisible) { } } + private String resolveSearchFieldScope() { + String listId = list.getListId(); + List listIdFields = ConfigurationManager.getStringList(RodaConstants.SEARCH_FIELD_PREFIX, listId); + return !listIdFields.isEmpty() ? listId : list.getClassToReturn().getSimpleName(); + } + /** * Sets the default filter and incremental boolean for the search, shows the * preFilters in the UI if they should be shown according to the constructor @@ -400,6 +410,31 @@ private Filter buildSearchFilter() { } } + // Group nested-type fields into ParentWhichFilterParameter block-join queries + Map> nestedGroups = new LinkedHashMap<>(); + Iterator> it = advancedSearchFilters.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + SearchField sf = advancedSearchFieldsPanel.getSearchField(entry.getKey()); + if (sf != null && sf.getNestedType() != null && sf.getNestedParentType() != null) { + String groupKey = sf.getNestedType() + "\0" + sf.getNestedParentType(); + nestedGroups.computeIfAbsent(groupKey, k -> new ArrayList<>()).add(entry.getValue()); + it.remove(); + } + } + + for (Map.Entry> entry : nestedGroups.entrySet()) { + String[] parts = entry.getKey().split("\0", 2); + String nestedType = parts[0]; + String nestedParentType = parts[1]; + List childFilters = new ArrayList<>(entry.getValue()); + childFilters.add(new SimpleFilterParameter("content_type", nestedType)); + FilterParameter childrenFilter = childFilters.size() == 1 ? childFilters.get(0) + : new AndFiltersParameters(childFilters); + parameters.add(new ParentWhichFilterParameter(new SimpleFilterParameter("content_type", nestedParentType), + childrenFilter)); + } + parameters.addAll(advancedSearchFilters.values()); } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/SearchWrapper.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/SearchWrapper.java index 0070d562f4..4808c827eb 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/SearchWrapper.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/search/SearchWrapper.java @@ -276,6 +276,42 @@ private void attachComponents(String objectClassSimpleName searchPanel.attachSearchPanelSelectionDropdown(searchPanelSelectionDropdown); } + public SearchWrapper createVirtualListAndSearchPanel(ListBuilder listBuilder, + boolean showSaveButton, boolean hideListAfterClear) { + AsyncTableCell list = listBuilder.build(); + String listId = list.getListId(); + + String labelI18nKey = ConfigurationManager.resolveTranslation(RodaConstants.UI_LISTS_PROPERTY, listId, + RodaConstants.UI_LISTS_CATALOGUE_LABEL_I18N_PROPERTY); + if (labelI18nKey == null) { + labelI18nKey = listId; + } + + String icon = ConfigurationManager.getString(RodaConstants.UI_LISTS_PROPERTY, listId, + RodaConstants.UI_LISTS_CATALOGUE_ICON_PROPERTY); + + Filter filter = list.getFilter(); + String allFilter = SearchFilters.searchField(); + boolean incremental = SearchFilters.shouldBeIncremental(filter); + + SearchPanel searchPanel = new SearchPanel<>(list, filter, allFilter, incremental, + listBuilder.getOptions().getSearchPlaceholder(), hasMultipleSearchPanels, showSaveButton, hideListAfterClear); + + initSearchPanelSelectionDropdown(); + searchPanelSelectionDropdown.addItem(labelI18nKey, listId, icon); + searchPanel.setVisible(true); + + components.putVirtual(listId, searchPanel, list); + + if (mainPanel.getWidgetCount() == 0) { + if (preselectedDropdownValue == null || preselectedDropdownValue.equals(listId)) { + attachComponents(listId); + } + } + + return this; + } + /** * Auxiliary manager for inner components (groups of one searchWrapper and one * BasicAsyncTableCell, at least for now) that is used to enforce type coherence @@ -284,6 +320,8 @@ private void attachComponents(String objectClassSimpleName private class Components { private final Map, SearchPanel> searchPanels = new LinkedHashMap<>(); private final Map, AsyncTableCell> lists = new LinkedHashMap<>(); + private final Map> virtualSearchPanels = new LinkedHashMap<>(); + private final Map> virtualLists = new LinkedHashMap<>(); /** * Add a new set of components associated with a class. @@ -302,6 +340,11 @@ public void put(Class objectClass, SearchPanel searc lists.put(objectClass, list); } + void putVirtual(String listId, SearchPanel searchPanel, AsyncTableCell list) { + virtualSearchPanels.put(listId, searchPanel); + virtualLists.put(listId, list); + } + SearchPanel getSearchPanel(Class objectClass) { return (SearchPanel) searchPanels.get(objectClass); } @@ -310,18 +353,26 @@ AsyncTableCell getList(Class objectClass) { return (AsyncTableCell) lists.get(objectClass); } - SearchPanel getSearchPanel(String className) { - return (SearchPanel) searchPanels.get(classForName(className)); + SearchPanel getSearchPanel(String key) { + if (virtualSearchPanels.containsKey(key)) { + return (SearchPanel) virtualSearchPanels.get(key); + } + return (SearchPanel) searchPanels.get(classForName(key)); } - AsyncTableCell getList(String className) { - return (AsyncTableCell) lists.get(classForName(className)); + AsyncTableCell getList(String key) { + if (virtualLists.containsKey(key)) { + return (AsyncTableCell) virtualLists.get(key); + } + return (AsyncTableCell) lists.get(classForName(key)); } void forEachList(Consumer> action) { for (AsyncTableCell value : lists.values()) { - AsyncTableCell list = (AsyncTableCell) value; - action.accept(list); + action.accept((AsyncTableCell) value); + } + for (AsyncTableCell value : virtualLists.values()) { + action.accept((AsyncTableCell) value); } } 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..eb7cae2997 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} @@ -263,6 +263,7 @@ oneOfAObject[org.roda.core.data.v2.disposal.hold.DisposalHold]:Entsorgungssperre oneOfAObject[org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation]:Entsorgungsbestätigung oneOfAObject[org.roda.core.data.v2.disposal.rule.DisposalRule]:Entsorgungsregel selected[\=1]:{0,number} {1} ausgewählt +selected[one]:{0,number} {1} ausgewählt selected:{0,number} {1} ausgewählt inspectTransferredResource:Übertragene Ressource untersuchen identifierNotFound:Indentifikator (nicht gefunden) @@ -841,12 +842,15 @@ representationInformationEditAssociations:Assoziationen bearbeiten representationInformationIntellectualEntities:Es sind {0,number} intellektuellen Entitäten mit dieser Repräsentationsinformation verknüpft representationInformationIntellectualEntities[\=0]:Es gibt keine intellektuellen Entitäten, die mit dieser Repräsentationsinformartion verknüpft sind representationInformationIntellectualEntities[\=1]:Es ist eine intellektuelle Entität mit dieser Repräsentationsinformation verknüpft +representationInformationIntellectualEntities[one]:Es ist eine intellektuelle Entität mit dieser Repräsentationsinformation verknüpft representationInformationRepresentations:Es sind {0,number} Repräsentationen mit dieser Repräsentationsinformation verknüpft representationInformationRepresentations[\=0]:Es gibt keine Repräsentationen, die mit dieser Repräsentationsinformartion verknüpft sind representationInformationRepresentations[\=1]:Es ist eine Repräsentation mit dieser Repräsentationsinformation verknüpft +representationInformationRepresentations[one]:Es ist eine Repräsentation mit dieser Repräsentationsinformation verknüpft representationInformationFiles:Es sind {0,number} Dateien mit dieser Repräsentationsinformation verknüpft representationInformationFiles[\=0]:Es gibt keine Dateien, die mit dieser Repräsentationsinformartion verknüpft sind representationInformationFiles[\=1]:Es ist eine Datei mit dieser Repräsentationsinformation verknüpft +representationInformationFiles[one]:Es ist eine Datei mit dieser Repräsentationsinformation verknüpft # Descriptive Metadata metadataType:Typ @@ -1518,6 +1522,7 @@ clearDisposalHoldDialogMessage[one]:Sind Sie sicher, dass Sie alle Entsorgungssp clearDisposalHoldDialogMessage:Sind Sie sicher, dass Sie alle Entsorgungssperren auf die ausgewählten {0, number} Elemente aufheben möchten? liftDisposalHoldDialogTitle:Kassationsperre aufheben liftDisposalHoldDialogMessage[\=1]:Sind Sie sicher, dass Sie die Kassationsperre aufheben wollen? +liftDisposalHoldDialogMessage[one]:Sind Sie sicher, dass Sie die Kassationsperre aufheben wollen? liftDisposalHoldDialogMessage:Sind Sie sicher, dass Sie die {0, Zahl} ausgewählten Kassationsperren aufheben wollen? disassociateDisposalHoldDialogTitle:Entsorgungssperre aufheben disassociateDisposalHoldDialogMessage[one]:Sind Sie sicher, dass Sie die Entsorgungssperre auf das ausgewählte Element aufheben möchten? @@ -1529,6 +1534,7 @@ disposalHoldAssociatedOn:Verbunden am disposalHoldAssociatedBy:Verbunden von disposalHoldAssociatedFrom:Verbunden von disposalHoldAssociatedFromValue[\=1]:{0} AIP +disposalHoldAssociatedFromValue[one]:{0} AIP disposalHoldAssociatedFromValue:{0} AIPs disposalScheduleAssociationInformationTitle:Aufbewahrugsfrist disposalScheduleAssociationTitle:Entsorgungsplan @@ -1559,12 +1565,16 @@ disposalPolicyRetainPermanently:Dauerhaft erhalten disposalPolicyDestroyedAIPSummary:Vernichtet am {0} retentionPeriod:{0} retentionPeriod[\=1|DAYS]:1 Tag +retentionPeriod[one|DAYS]:1 Tag retentionPeriod[other|DAYS]:{0,number} Tage retentionPeriod[\=1|WEEKS]:1 Woche +retentionPeriod[one|WEEKS]:1 Woche retentionPeriod[other|WEEKS]:{0,number} Wochen retentionPeriod[\=1|MONTHS]:1 Monat +retentionPeriod[one|MONTHS]:1 Monat retentionPeriod[other|MONTHS]:{0,number} Monate retentionPeriod[\=1|YEARS]:1 Jahr +retentionPeriod[one|YEARS]:1 Jahr retentionPeriod[other|YEARS]:{0,number} Jahre retentionPeriod[other|NO_RETENTION_PERIOD]:Keine Aufbewahrugsfrist permanentlyDeleteConfirmDialogTitle:Datensätze dauerhaft aus dem Papierkorb löschen 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..93ec52c6e6 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} @@ -263,6 +263,7 @@ oneOfAObject[org.roda.core.data.v2.disposal.hold.DisposalHold]:Kassationssperre oneOfAObject[org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation]:Kassationsbestätigung oneOfAObject[org.roda.core.data.v2.disposal.rule.DisposalRule]:Kassationsregel selected[\=1]:{0,number} {1} ausgewählt +selected[one]:{0,number} {1} ausgewählt selected:{0,number} {1} ausgewählt inspectTransferredResource:Übertragene Ressource untersuchen identifierNotFound:Indentifikator (nicht gefunden) @@ -841,12 +842,15 @@ representationInformationEditAssociations:Assoziationen bearbeiten representationInformationIntellectualEntities:Es sind {0,number} intellektuellen Entitäten mit dieser Repräsentationsinformation verknüpft representationInformationIntellectualEntities[\=0]:Es gibt keine intellektuellen Entitäten, die mit dieser Repräsentationsinformartion verknüpft sind representationInformationIntellectualEntities[\=1]:Es ist eine intellektuelle Entität mit dieser Repräsentationsinformation verknüpft +representationInformationIntellectualEntities[one]:Es ist eine intellektuelle Entität mit dieser Repräsentationsinformation verknüpft representationInformationRepresentations:Es sind {0,number} Repräsentationen mit dieser Repräsentationsinformation verknüpft representationInformationRepresentations[\=0]:Es gibt keine Repräsentationen, die mit dieser Repräsentationsinformartion verknüpft sind representationInformationRepresentations[\=1]:Es ist eine Repräsentation mit dieser Repräsentationsinformation verknüpft +representationInformationRepresentations[one]:Es ist eine Repräsentation mit dieser Repräsentationsinformation verknüpft representationInformationFiles:Es sind {0,number} Dateien mit dieser Repräsentationsinformation verknüpft representationInformationFiles[\=0]:Es gibt keine Dateien, die mit dieser Repräsentationsinformartion verknüpft sind representationInformationFiles[\=1]:Es ist eine Datei mit dieser Repräsentationsinformation verknüpft +representationInformationFiles[one]:Es ist eine Datei mit dieser Repräsentationsinformation verknüpft # Descriptive Metadata metadataType:Typ @@ -1518,6 +1522,7 @@ clearDisposalHoldDialogMessage[one]:Sind Sie sicher, dass Sie alle Kassationsspe clearDisposalHoldDialogMessage:Sind Sie sicher, dass Sie alle Kassationssperren für die ausgewählten {0, number} Elemente aufheben möchten? liftDisposalHoldDialogTitle:Kassationsperre aufheben liftDisposalHoldDialogMessage[\=1]:Sind Sie sicher, dass Sie die Kassationsperre aufheben wollen? +liftDisposalHoldDialogMessage[one]:Sind Sie sicher, dass Sie die Kassationsperre aufheben wollen? liftDisposalHoldDialogMessage:Sind Sie sicher, dass Sie die {0, Zahl} ausgewählten Kassationsperren aufheben wollen? disassociateDisposalHoldDialogTitle:Kassationssperre aufheben disassociateDisposalHoldDialogMessage[one]:Sind Sie sicher, dass Sie die Kassationssperre für das ausgewählte Element aufheben möchten? @@ -1529,6 +1534,7 @@ disposalHoldAssociatedOn:Assoziiert am disposalHoldAssociatedBy:Assoziiert druch disposalHoldAssociatedFrom:Assoziiert von disposalHoldAssociatedFromValue[\=1]:{0} AIP +disposalHoldAssociatedFromValue[one]:{0} AIP disposalHoldAssociatedFromValue:{0} AIPs disposalScheduleAssociationInformationTitle:Aufbewahrugsfrist disposalScheduleAssociationTitle:Kassationsplan @@ -1559,12 +1565,16 @@ disposalPolicyRetainPermanently:Dauerhaft erhalten disposalPolicyDestroyedAIPSummary:Vernichtet am {0} retentionPeriod:{0} retentionPeriod[\=1|DAYS]:1 Tag +retentionPeriod[one|DAYS]:1 Tag retentionPeriod[other|DAYS]:{0,number} Tage retentionPeriod[\=1|WEEKS]:1 Woche +retentionPeriod[one|WEEKS]:1 Woche retentionPeriod[other|WEEKS]:{0,number} Wochen retentionPeriod[\=1|MONTHS]:1 Monat +retentionPeriod[one|MONTHS]:1 Monat retentionPeriod[other|MONTHS]:{0,number} Monate retentionPeriod[\=1|YEARS]:1 Jahr +retentionPeriod[one|YEARS]:1 Jahr retentionPeriod[other|YEARS]:{0,number} Jahre retentionPeriod[other|NO_RETENTION_PERIOD]:Keine Aufbewahrugsfrist permanentlyDeleteConfirmDialogTitle:Datensätze dauerhaft aus dem Papierkorb löschen 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..b6150334df 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 @@ -1,16 +1,16 @@ moveNoSuchObject:No se puede mover el elemento porque el destino no se encuentra en el repositorio. Detalles\: {0} of:de -ofOver:of over -and:and +ofOver:de más de +and:y # Titles genericTitle:Titulo processTitle:Procesos reportTitle:Informe de procesamiento de paquete de información ingestAppraisalTitle:Evaluación de la Ingesta riskHistoryTitle:Historial de Riesgos -editIncidenceTitle:Edit risk incidence -editIncidencesTitle:Edit risk incidences -showRiskIncidenceTitle:Risk incidence +editIncidenceTitle:Editar incidencia de riesgo +editIncidencesTitle:Editar incidencias de riesgo +showRiskIncidenceTitle:Incidencia de riesgo createRepresentationInformationTitle:Crear información de representación editRepresentationInformationTitle:Editar información de representación showRepresentationInformationTitle:Información de representación @@ -26,123 +26,123 @@ catalogueItemTitle:Entidades intelectuales catalogueRepresentationTitle:Representación catalogueFileTitle:Archivo catalogueDIPTitle:Diseminación -preservationActionsTitle:Preservation actions -ingestProcessTitle:Ingest process -internalProcessTitle:Internal actions +preservationActionsTitle:Acciones de preservación +ingestProcessTitle:Proceso de ingesta +internalProcessTitle:Acciones internas loginTitle:Usuario searchTitle:Buscar -activityLogTitle:Audit logs -showLogEntryTitle:Audit log -notificationsTitle:Notification logs -notificationTitle:Notification log -usersAndGroupsTitle:Users and groups +activityLogTitle:Registros de auditoría +showLogEntryTitle:Registro de auditoría +notificationsTitle:Registros de notificaciones +notificationTitle:Registro de notificaciones +usersAndGroupsTitle:Usuarios y grupos usersAndGroupsSearchPlaceHolder:Buscar usuarios y grupos... logsTitle:Logs changeTypeTitle:Cambiar tipo changeStatusTitle:Cambiar Estado -changeStatusSuccessful:Status changed successfully +changeStatusSuccessful:Estado cambiado correctamente statusLabel:{0} statusLabel[ORIGINAL]:Original -statusLabel[INGESTED]:Ingested +statusLabel[INGESTED]:Ingestado statusLabel[ACCESS]:Accesos -statusLabel[PRESERVATION]:Preservation +statusLabel[PRESERVATION]:Preservación tikaTitles:{0} -tikaTitles[tika_pdf_num3DAnnotations_txt]:Number of 3d Annotations -tikaTitles[tika_pdf_PDFVersion_txt]:Pdf version -tikaTitles[tika_access_permission_can_print_degraded_txt]:Permission to print degraded text -tikaTitles[tika_access_permission_extract_for_accessibility_txt]:Permission to extract for accessibility text -tikaTitles[tika_xmpTPg_NPages_txt]:Number of pages -tikaTitles[tika_pdf_totalUnmappedUnicodeChars_txt]:Number of unmapped unicode characters -tikaTitles[tika_pdf_hasXFA_txt]:Has XFA -tikaTitles[tika_pdf_hasXMP_txt]:Has XMP -tikaTitles[tika_access_permission_extract_content_txt]:Permission to extract content -tikaTitles[tika_access_permission_can_print_txt]:Permission to print -tikaTitles[tika_pdf_overallPercentageUnmappedUnicodeChars_txt]:Overall percentage of unmapped unicode characters -tikaTitles[tika_X-TIKA_Parsed-By_txt]:Tika parsed by -tikaTitles[tika_pdf_docinfo_created_txt]:Document info creation date -tikaTitles[tika_access_permission_modify_annotations_txt]:Permission to modify annotations -tikaTitles[tika_pdf_hasMarkedContent_txt]:Has marked content -tikaTitles[tika_xmp_CreatorTool_txt]:Creator tool -tikaTitles[tika_Content-Type_txt]:Content type -tikaTitles[tika_X-TIKA_Parsed-By-Full-Set_txt]:Parsed by -tikaTitles[tika_pdf_encrypted_txt]:Encrypted -tikaTitles[tika_access_permission_can_modify_txt]: Permission to modify -tikaTitles[tika_pdf_docinfo_creator_tool_txt]:Document info creator tool -tikaTitles[tika_pdf_containsNonEmbeddedFont_txt]:Contains non embedded font -tikaTitles[tika_access_permission_fill_in_form_txt]:Permission to fill in form -tikaTitles[tika_pdf_containsDamagedFont_txt]:Contains damaged font -tikaTitles[tika_dc_format_txt]:DC format -tikaTitles[tika_pdf_charsPerPage_txt]:Characters per page -tikaTitles[tika_pdf_docinfo_producer_txt]:Document info producer -tikaTitles[tika_pdf_hasCollection_txt]:Has collection -tikaTitles[tika_access_permission_assemble_document_txt]:Permission to assemble document -tikaTitles[tika_pdf_producer_txt]:Producer -tikaTitles[tika_pdf_unmappedUnicodeCharsPerPage_txt]:Unmapped unicode characters per page -tikaTitles[tika_dcterms_created_txt]:Terms creation date -tikaTitles[tika_tEXt_tEXtEntry_txt]:Text Entry -tikaTitles[tika_Dimension_PixelAspectRatio_txt]:Pixel aspect ratio dimensions -tikaTitles[tika_Data_PlanarConfiguration_txt]:Data planar configuration -tikaTitles[tika_Transparency_Alpha_txt]:Transpericy alpha +tikaTitles[tika_pdf_num3DAnnotations_txt]:Número de anotaciones 3D +tikaTitles[tika_pdf_PDFVersion_txt]:Versión PDF +tikaTitles[tika_access_permission_can_print_degraded_txt]:Permiso para imprimir con calidad reducida +tikaTitles[tika_access_permission_extract_for_accessibility_txt]:Permiso para extraer contenido con fines de accesibilidad +tikaTitles[tika_xmpTPg_NPages_txt]:Número de páginas +tikaTitles[tika_pdf_totalUnmappedUnicodeChars_txt]:Número total de caracteres Unicode sin asignar +tikaTitles[tika_pdf_hasXFA_txt]:Tiene XFA +tikaTitles[tika_pdf_hasXMP_txt]:Tiene XMP +tikaTitles[tika_access_permission_extract_content_txt]:Permiso para extraer contenido +tikaTitles[tika_access_permission_can_print_txt]:Permiso para imprimir +tikaTitles[tika_pdf_overallPercentageUnmappedUnicodeChars_txt]:Porcentaje global de caracteres Unicode sin asignar +tikaTitles[tika_X-TIKA_Parsed-By_txt]:Analizado por Tika +tikaTitles[tika_pdf_docinfo_created_txt]:Fecha de creación de información del documento +tikaTitles[tika_access_permission_modify_annotations_txt]:Permiso para modificar anotaciones +tikaTitles[tika_pdf_hasMarkedContent_txt]:Tiene contenido marcado +tikaTitles[tika_xmp_CreatorTool_txt]:Herramienta de creación +tikaTitles[tika_Content-Type_txt]:Tipo de contenido +tikaTitles[tika_X-TIKA_Parsed-By-Full-Set_txt]:Analizado por +tikaTitles[tika_pdf_encrypted_txt]:Cifrado +tikaTitles[tika_access_permission_can_modify_txt]: Permiso para modificar +tikaTitles[tika_pdf_docinfo_creator_tool_txt]:Herramienta de creación de información del documento +tikaTitles[tika_pdf_containsNonEmbeddedFont_txt]:Contiene fuente no incrustada +tikaTitles[tika_access_permission_fill_in_form_txt]:Permiso para rellenar formularios +tikaTitles[tika_pdf_containsDamagedFont_txt]:Contiene fuente dañada +tikaTitles[tika_dc_format_txt]:Formato DC +tikaTitles[tika_pdf_charsPerPage_txt]:Caracteres por página +tikaTitles[tika_pdf_docinfo_producer_txt]:Productor de información del documento +tikaTitles[tika_pdf_hasCollection_txt]:Tiene colección +tikaTitles[tika_access_permission_assemble_document_txt]:Permiso para ensamblar documento +tikaTitles[tika_pdf_producer_txt]:Productor +tikaTitles[tika_pdf_unmappedUnicodeCharsPerPage_txt]:Caracteres Unicode sin asignar por página +tikaTitles[tika_dcterms_created_txt]:Fecha de creación de términos DC +tikaTitles[tika_tEXt_tEXtEntry_txt]:Entrada de texto +tikaTitles[tika_Dimension_PixelAspectRatio_txt]:Relación de aspecto de píxel +tikaTitles[tika_Data_PlanarConfiguration_txt]:Configuración planar de datos +tikaTitles[tika_Transparency_Alpha_txt]:Canal alfa de transparencia tikaTitles[tika_sBIT_sBIT_RGBAlpha_txt]:RGB -tikaTitles[tika_Compression_Lossless_b]:Compression lossless -tikaTitles[tika_Compression_CompressionTypeName_txt]:Compression type name -tikaTitles[tika_Chroma_NumChannels_txt]:Chroma number of channels -tikaTitles[tika_Compression_NumProgressiveScans_txt]:Number of progressive scans -tikaTitles[tika_imagereader_NumImages_txt]:Number of images -tikaTitles[tika_Data_SampleFormat_txt]:Sample format -tikaTitles[tika_tiff_ImageWidth_d]:Image width -tikaTitles[tika_tiff_ImageLength_d]:Image length -tikaTitles[tika_Text_TextEntry_txt]:Text entry -tikaTitles[tika_Data_BitsPerSample_txt]:Bits per sample (Data) -tikaTitles[tika_Data_SignificantBitsPerSample_txt]:Significant bits per sample -tikaTitles[tika_tiff_BitsPerSample_txt]:Bits per sample (Tiff) +tikaTitles[tika_Compression_Lossless_b]:Compresión sin pérdida +tikaTitles[tika_Compression_CompressionTypeName_txt]:Nombre del tipo de compresión +tikaTitles[tika_Chroma_NumChannels_txt]:Número de canales de croma +tikaTitles[tika_Compression_NumProgressiveScans_txt]:Número de exploraciones progresivas +tikaTitles[tika_imagereader_NumImages_txt]:Número de imágenes +tikaTitles[tika_Data_SampleFormat_txt]:Formato de muestra +tikaTitles[tika_tiff_ImageWidth_d]:Anchura de imagen +tikaTitles[tika_tiff_ImageLength_d]:Longitud de imagen +tikaTitles[tika_Text_TextEntry_txt]:Entrada de texto +tikaTitles[tika_Data_BitsPerSample_txt]:Bits por muestra (datos) +tikaTitles[tika_Data_SignificantBitsPerSample_txt]:Bits significativos por muestra +tikaTitles[tika_tiff_BitsPerSample_txt]:Bits por muestra (Tiff) tikaTitles[tika_IHDR_txt]:IHDR -tikaTitles[tika_Dimension_ImageOrientation_txt]:Image orientation -tikaTitles[tika_Chroma_ColorSpaceType_txt]:Color space type -tikaTitles[tika_Chroma_BlackIsZero_txt]:Black is zero -otherStatusLabel:Other status: +tikaTitles[tika_Dimension_ImageOrientation_txt]:Orientación de imagen +tikaTitles[tika_Chroma_ColorSpaceType_txt]:Tipo de espacio de color +tikaTitles[tika_Chroma_BlackIsZero_txt]:Negro es cero +otherStatusLabel:Otro estado: otherStatusPlaceholder:Agregar nueva representación de estado -createFolderTitle:Create folder -createFolderPlaceholder:Write folder name -createFolderAlreadyExistsTitle:Cannot create folder -createFolderAlreadyExistsMessage:Folder name already exists -renameFolderAlreadyExistsTitle: Cannot rename folder -renameFolderAlreadyExistsMessage: Folder name already exists -outcomeDetailTitle:Action detail -outcomeDetailPlaceholder:Detail the reason to do it -chooseEntityTitle:Choose entity to relate +createFolderTitle:Crear carpeta +createFolderPlaceholder:Escriba el nombre de la carpeta +createFolderAlreadyExistsTitle:No se puede crear la carpeta +createFolderAlreadyExistsMessage:Ya existe una carpeta con ese nombre +renameFolderAlreadyExistsTitle: No se puede renombrar la carpeta +renameFolderAlreadyExistsMessage: Ya existe una carpeta con ese nombre +outcomeDetailTitle:Detalle de la acción +outcomeDetailPlaceholder:Detalle el motivo +chooseEntityTitle:Elegir entidad a relacionar # Sidebars sidebarFilterDates:Fechas sidebarFoldersFilesTitle:Carpetas y archivos sidebarProcessTitle:Procesos sidebarIngestTitle:Ingesta sidebarActionsTitle:Acciones -sidebarDisposalBinTitle:Disposal bin -sidebarDisposalScheduleTitle:Disposal Schedule -sidebarDisposalConfirmationTitle:Disposal Confirmation -sidebarAppraisalTitle:Assessment +sidebarDisposalBinTitle:Papelera de eliminación +sidebarDisposalScheduleTitle:Calendario de eliminación +sidebarDisposalConfirmationTitle:Confirmación de eliminación +sidebarAppraisalTitle:Evaluación metadataParseError:Error en linea {0}, columna {1}\: {2} notFoundError:No encontrado descriptiveMetadataTransformToHTMLError:Ha ocurrido un error mientras está transformando metadato descriptivo en HTML -preservationEventDetailsTransformToHTMLError:Error transforming preservation event details into HTML +preservationEventDetailsTransformToHTMLError:Error al transformar los detalles del evento de preservación en HTML allCollectionsTitle:Catalogo errorLoadingDescriptiveMetadata:Ha ocurrido un error mientras está cargando metadato descriptivo\: {0} -errorLoadingPreservationEventDetails:Error loading preservation event details: {0} -errorLoadingJobReport:Error loading job report: {0} +errorLoadingPreservationEventDetails:Error al cargar los detalles del evento de preservación: {0} +errorLoadingJobReport:Error al cargar el informe del proceso: {0} titleDatesEmpty: titleDatesNoFinal:Desde {0} titleDatesNoInitial:Hasta {0} -titleDates:Desde {0} to {1} +titleDates:Desde {0} hasta {1} simpleDatesEmpty: simpleDatesNoFinal:{0} simpleDatesNoInitial:a {0} -simpleDates:{0} to {1} +simpleDates:{0} hasta {1} rejectMessage:Mensaje de rechazo rejectQuestion:¿Cuál es la razón de rechazar este item? -renameSIPFailed:Rename of a SIP failed -renameSIPSuccessful:Rename of a SIP was successful -moveSIPFailed:SIP move failed -movingAIP:Moving AIP(s) in background +renameSIPFailed:Error al renombrar el SIP +renameSIPSuccessful:El SIP se renombró correctamente +moveSIPFailed:Error al mover el SIP +movingAIP:Moviendo AIP(s) en segundo plano jobNotFound:El trabajo que ha solicitado no fue encontrado updateIsCurrentlyRunning:Se está ejecutando otra actualización actualmente. Favor intentar luego nuevamente. updatedFilesUnderFolder:Archivos contenidos en carpeta actualizados @@ -152,15 +152,15 @@ otherItem:Otro metadataFileCreated:Archivo metadata descriptiva creado metadataFileSaved:Archivo metadata descriptiva guardado metadataFileRemoved:Archivo metadata descriptiva eliminado -removeMetadataFileTitle:Confirm descriptive metadata removal -removeMetadataFileLabel:Are you sure you want to remove the descriptive metadata of this entity? -updateMetadataFileTitle:Confirm descriptive metadata update -updateMetadataFileLabel:Are you sure you want to update the descriptive metadata of this entity? +removeMetadataFileTitle:Confirmar eliminación de metadatos descriptivos +removeMetadataFileLabel:¿Está seguro de que desea eliminar los metadatos descriptivos de esta entidad? +updateMetadataFileTitle:Confirmar actualización de metadatos descriptivos +updateMetadataFileLabel:¿Está seguro de que desea actualizar los metadatos descriptivos de esta entidad? versionReverted:Versión revertida versionDeleted:Versión eliminada selectUserOrGroupToAdd:Seleccione usuarios y grupos para agregar couldNotFindPreservationEvent:No pudo ser encontrado el evento de preservación -couldNotFindPreservationAgent:Could not find preservation agent +couldNotFindPreservationAgent:No se encontró el agente de preservación intellectualEntity:Entidad intelectual inspectIntellectualEntity:Inspeccionar entidad intelectual inspectRepresentation:Inspeccionar representación @@ -189,81 +189,82 @@ allOfAObject[org.roda.core.data.v2.jobs.IndexedJob]:Procesos allOfAObject[org.roda.core.data.v2.jobs.Report]:Reportes allOfAObject[org.roda.core.data.v2.jobs.IndexedReport]:Reportes allOfAObject[org.roda.core.data.v2.notifications.Notification]:Notificaciones -allOfAObject[org.roda.core.data.v2.log.LogEntry]:All audit logs +allOfAObject[org.roda.core.data.v2.log.LogEntry]:Todos los registros de auditoría allOfAObject[org.roda.core.data.v2.user.RODAMember]:Usuarios y grupos allOfAObject[org.roda.core.data.v2.ip.metadata.DescriptiveMetadata]:Metadatos descriptivos allOfAObject[org.roda.core.data.v2.ip.metadata.PreservationMetadata]:Metadatos de preservación allOfAObject[org.roda.core.data.v2.formats.Format]:Formatos -allOfAObject[org.roda.core.data.v2.ip.metadata.IndexedPreservationEvent]:All preservation events -allOfAObject[org.roda.core.data.v2.ip.metadata.IndexedPreservationAgent]:All preservation agents +allOfAObject[org.roda.core.data.v2.ip.metadata.IndexedPreservationEvent]:Todos los eventos de preservación +allOfAObject[org.roda.core.data.v2.ip.metadata.IndexedPreservationAgent]:Todos los agentes de preservación allOfAObject[org.roda.core.data.v2.Void]:No hay objetos de entrada someOfAObject:{0} -someOfAObject[org.roda.core.data.v2.ip.AIP]:intellectual entities -someOfAObject[org.roda.core.data.v2.ip.IndexedAIP]:intellectual entities -someOfAObject[org.roda.core.data.v2.ip.Representation]:representations -someOfAObject[org.roda.core.data.v2.ip.IndexedRepresentation]:representations -someOfAObject[org.roda.core.data.v2.ip.File]:files -someOfAObject[org.roda.core.data.v2.ip.IndexedFile]:files -someOfAObject[org.roda.core.data.v2.ip.TransferredResource]:transferred resources -someOfAObject[org.roda.core.data.v2.ip.DIP]:disseminations -someOfAObject[org.roda.core.data.v2.ip.IndexedDIP]:disseminations -someOfAObject[org.roda.core.data.v2.ip.DIPFile]:dissemination files -someOfAObject[org.roda.core.data.v2.risks.Risk]:risks -someOfAObject[org.roda.core.data.v2.risks.IndexedRisk]:risks -someOfAObject[org.roda.core.data.v2.risks.RiskIncidence]:incidences -someOfAObject[org.roda.core.data.v2.ri.RepresentationInformation]:representation information -someOfAObject[org.roda.core.data.v2.jobs.Job]:jobs -someOfAObject[org.roda.core.data.v2.jobs.IndexedJob]:jobs -someOfAObject[org.roda.core.data.v2.jobs.Report]:reports -someOfAObject[org.roda.core.data.v2.jobs.IndexedReport]:reports -someOfAObject[org.roda.core.data.v2.notifications.Notification]:notifications -someOfAObject[org.roda.core.data.v2.log.LogEntry]:Audit logs -someOfAObject[org.roda.core.data.v2.user.RODAMember]:users and groups -someOfAObject[org.roda.core.data.v2.ip.metadata.DescriptiveMetadata]:descriptive metadata -someOfAObject[org.roda.core.data.v2.ip.metadata.PreservationMetadata]:preservation metadata -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.ip.AIP]:entidades intelectuales +someOfAObject[org.roda.core.data.v2.ip.IndexedAIP]:entidades intelectuales +someOfAObject[org.roda.core.data.v2.ip.Representation]:representaciones +someOfAObject[org.roda.core.data.v2.ip.IndexedRepresentation]:representaciones +someOfAObject[org.roda.core.data.v2.ip.File]:archivos +someOfAObject[org.roda.core.data.v2.ip.IndexedFile]:archivos +someOfAObject[org.roda.core.data.v2.ip.TransferredResource]:recursos transferidos +someOfAObject[org.roda.core.data.v2.ip.DIP]:diseminaciones +someOfAObject[org.roda.core.data.v2.ip.IndexedDIP]:diseminaciones +someOfAObject[org.roda.core.data.v2.ip.DIPFile]:archivos de diseminación +someOfAObject[org.roda.core.data.v2.risks.Risk]:riesgos +someOfAObject[org.roda.core.data.v2.risks.IndexedRisk]:riesgos +someOfAObject[org.roda.core.data.v2.risks.RiskIncidence]:incidencias +someOfAObject[org.roda.core.data.v2.ri.RepresentationInformation]:información de representación +someOfAObject[org.roda.core.data.v2.jobs.Job]:procesos +someOfAObject[org.roda.core.data.v2.jobs.IndexedJob]:procesos +someOfAObject[org.roda.core.data.v2.jobs.Report]:informes +someOfAObject[org.roda.core.data.v2.jobs.IndexedReport]:informes +someOfAObject[org.roda.core.data.v2.notifications.Notification]:notificaciones +someOfAObject[org.roda.core.data.v2.log.LogEntry]:Registros de auditoría +someOfAObject[org.roda.core.data.v2.user.RODAMember]:usuarios y grupos +someOfAObject[org.roda.core.data.v2.ip.metadata.DescriptiveMetadata]:metadatos descriptivos +someOfAObject[org.roda.core.data.v2.ip.metadata.PreservationMetadata]:metadatos de preservación +someOfAObject[org.roda.core.data.v2.formats.Format]:formatos +someOfAObject[org.roda.core.data.v2.ip.metadata.IndexedPreservationEvent]:eventos de preservación +someOfAObject[org.roda.core.data.v2.ip.metadata.IndexedPreservationAgent]:agentes de preservación 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.synchronization.central.DistributedInstances]:distributed instances -someOfAObject[org.roda.core.data.v2.accessKey.AccessKeys]:access tokens +someOfAObject[org.roda.core.data.v2.disposal.schedule.DisposalSchedule]:calendarios de eliminación +someOfAObject[org.roda.core.data.v2.disposal.hold.DisposalHold]:bloqueos de eliminación +someOfAObject[org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation]:confirmaciones de eliminación +someOfAObject[org.roda.core.data.v2.disposal.rule.DisposalRule]:reglas de eliminación +someOfAObject[org.roda.core.data.v2.synchronization.central.DistributedInstances]:instancias distribuidas +someOfAObject[org.roda.core.data.v2.accessKey.AccessKeys]:tokens de acceso oneOfAObject:{0} -oneOfAObject[org.roda.core.data.v2.ip.AIP]:intellectual entity -oneOfAObject[org.roda.core.data.v2.ip.IndexedAIP]:intellectual entity -oneOfAObject[org.roda.core.data.v2.ip.Representation]:representation -oneOfAObject[org.roda.core.data.v2.ip.IndexedRepresentation]:representation +oneOfAObject[org.roda.core.data.v2.ip.AIP]:entidad intelectual +oneOfAObject[org.roda.core.data.v2.ip.IndexedAIP]:entidad intelectual +oneOfAObject[org.roda.core.data.v2.ip.Representation]:representación +oneOfAObject[org.roda.core.data.v2.ip.IndexedRepresentation]:representación oneOfAObject[org.roda.core.data.v2.ip.File]:archivo oneOfAObject[org.roda.core.data.v2.ip.IndexedFile]:archivo -oneOfAObject[org.roda.core.data.v2.ip.TransferredResource]:transferred resource +oneOfAObject[org.roda.core.data.v2.ip.TransferredResource]:recurso transferido oneOfAObject[org.roda.core.data.v2.ip.DIP]:diseminación oneOfAObject[org.roda.core.data.v2.ip.IndexedDIP]:diseminación -oneOfAObject[org.roda.core.data.v2.ip.DIPFile]:dissemination file -oneOfAObject[org.roda.core.data.v2.risks.Risk]:risk -oneOfAObject[org.roda.core.data.v2.risks.IndexedRisk]:risk -oneOfAObject[org.roda.core.data.v2.risks.RiskIncidence]:incidence -oneOfAObject[org.roda.core.data.v2.ri.RepresentationInformation]:representation information +oneOfAObject[org.roda.core.data.v2.ip.DIPFile]:archivo de diseminación +oneOfAObject[org.roda.core.data.v2.risks.Risk]:riesgo +oneOfAObject[org.roda.core.data.v2.risks.IndexedRisk]:riesgo +oneOfAObject[org.roda.core.data.v2.risks.RiskIncidence]:incidencia +oneOfAObject[org.roda.core.data.v2.ri.RepresentationInformation]:información de representación oneOfAObject[org.roda.core.data.v2.jobs.Job]:trabajo oneOfAObject[org.roda.core.data.v2.jobs.IndexedJob]:trabajo -oneOfAObject[org.roda.core.data.v2.jobs.Report]:report -oneOfAObject[org.roda.core.data.v2.jobs.IndexedReport]:report -oneOfAObject[org.roda.core.data.v2.notifications.Notification]:notification -oneOfAObject[org.roda.core.data.v2.log.LogEntry]:Audit log -oneOfAObject[org.roda.core.data.v2.user.RODAMember]:user or group -oneOfAObject[org.roda.core.data.v2.ip.metadata.DescriptiveMetadata]:descriptive metadata -oneOfAObject[org.roda.core.data.v2.ip.metadata.PreservationMetadata]:preservation metadata -oneOfAObject[org.roda.core.data.v2.formats.Format]:format -oneOfAObject[org.roda.core.data.v2.ip.metadata.IndexedPreservationEvent]:preservation event -oneOfAObject[org.roda.core.data.v2.ip.metadata.IndexedPreservationAgent]:preservation agent -oneOfAObject[org.roda.core.data.v2.disposal.schedule.DisposalSchedule]:disposal schedule -oneOfAObject[org.roda.core.data.v2.disposal.hold.DisposalHold]:disposal hold -oneOfAObject[org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation]:disposal confirmation -oneOfAObject[org.roda.core.data.v2.disposal.rule.DisposalRule]:disposal rule -selected[\=1]:{0,number} {1} selected -selected:{0,number} {1} selected +oneOfAObject[org.roda.core.data.v2.jobs.Report]:informe +oneOfAObject[org.roda.core.data.v2.jobs.IndexedReport]:informe +oneOfAObject[org.roda.core.data.v2.notifications.Notification]:notificación +oneOfAObject[org.roda.core.data.v2.log.LogEntry]:Registro de auditoría +oneOfAObject[org.roda.core.data.v2.user.RODAMember]:usuario o grupo +oneOfAObject[org.roda.core.data.v2.ip.metadata.DescriptiveMetadata]:metadatos descriptivos +oneOfAObject[org.roda.core.data.v2.ip.metadata.PreservationMetadata]:metadatos de preservación +oneOfAObject[org.roda.core.data.v2.formats.Format]:formato +oneOfAObject[org.roda.core.data.v2.ip.metadata.IndexedPreservationEvent]:evento de preservación +oneOfAObject[org.roda.core.data.v2.ip.metadata.IndexedPreservationAgent]:agente de preservación +oneOfAObject[org.roda.core.data.v2.disposal.schedule.DisposalSchedule]:calendario de eliminación +oneOfAObject[org.roda.core.data.v2.disposal.hold.DisposalHold]:bloqueo de eliminación +oneOfAObject[org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation]:confirmación de eliminación +oneOfAObject[org.roda.core.data.v2.disposal.rule.DisposalRule]:regla de eliminación +selected[\=1]:{0,number} {1} seleccionado(s) +selected[one]:{0,number} {1} seleccionado(s) +selected:{0,number} {1} seleccionado(s) inspectTransferredResource:Inspeccionar recursos transferidos identifierNotFound:Identificador (no encontrado) originalRepresentation:original @@ -271,7 +272,7 @@ alternativeRepresentation:alternativo representationStatus:Estado executingTaskMessage:Estamos ejecutando su tarea solicitada, por favor espere... noItemsToDisplay:No hay {0} para desplegar -noItemsToDisplayPreFilters:Could not find any {0} in this context. +noItemsToDisplayPreFilters:No se encontró ningún {0} en este contexto. noItemsToDisplayButFacetsActive:No hay {0} para desplegar mas existen filtros ativos. Para deshabilitarlos, haga resetFacetsLink:clic aquí browserOfflineError:Su navegador esta fuera de linea, por favor revise su conectividad @@ -299,25 +300,25 @@ createJobWorkflow: Flujo de trabajo createJobCategorySelect: Categoría createJobCategoryWorkflow: Categoría: {0,list} createJobCurlCommand: Comando cURL para crear proceso -createJobHighPriorityDescription: High priority processes will start as soon as there is an available slot -createJobMediumPriorityDescription: Medium priority processes will start if there is an available slot and no high priority is on the queue -createJobLowPriorityDescription: Low priority processes will run if there is a slot available and if no other processes are waiting to be started -createJobNormalParallelismDescription: Normal parallelism will be running many processes at the same time -createJobLimitedParallelismDescription: Limited parallelism will limit the amount of processes that are run at the same time -createJobOrchestrationPrioritySeparator: Priority -createJobOrchestrationParallelismSeparator: Parallelism +createJobHighPriorityDescription: Los procesos de alta prioridad comenzarán en cuanto haya un slot disponible +createJobMediumPriorityDescription: Los procesos de prioridad media comenzarán si hay un slot disponible y no hay procesos de alta prioridad en la cola +createJobLowPriorityDescription: Los procesos de baja prioridad se ejecutarán si hay un slot disponible y no hay otros procesos esperando para iniciarse +createJobNormalParallelismDescription: El paralelismo normal ejecutará muchos procesos al mismo tiempo +createJobLimitedParallelismDescription: El paralelismo limitado restringirá el número de procesos que se ejecutan al mismo tiempo +createJobOrchestrationPrioritySeparator: Prioridad +createJobOrchestrationParallelismSeparator: Paralelismo createJobOrchestration: Orquestación copiedToClipboardTitle: Copiado copiedToClipboardMessage: El contenido se ha copiado al clipboard -jobCreatedRedirectTitle: A job was created -removeJobCreatedMessage: Do you want to follow the remove job? -identifyFormatsJobCreatedMessage: Do you want to follow the format identification job? -moveJobCreatedMessage: Do you want to follow the move job? -jobCreatedMessage: Do you want to follow the job? +jobCreatedRedirectTitle: Se ha creado un proceso +removeJobCreatedMessage: ¿Desea seguir el proceso de eliminación? +identifyFormatsJobCreatedMessage: ¿Desea seguir el proceso de identificación de formatos? +moveJobCreatedMessage: ¿Desea seguir el proceso de movimiento? +jobCreatedMessage: ¿Desea seguir el proceso? # Ingest transfer upload ingestTransferUploadTitle:Cargar ingesta transferida -fileUploadTitle:File upload +fileUploadTitle:Carga de archivo ingestTransferUploadDropHere:Borrar archivos aquí ingestTransferUploadBrowseFiles:Seleccionar archivos ingestTransferUploadFinishButton:Hecho @@ -335,13 +336,13 @@ dialogRefresh:Refrescar removeSuccessTitle:Items eliminados removeSuccessMessage:{0} items eliminados con éxito removeConfirmDialogTitle:Confirmar eliminación de items -removeAllConfirmDialogMessageSingle:Are you sure you want to remove the intellectual entity "{0}", including all its nested items? +removeAllConfirmDialogMessageSingle:¿Está seguro de que desea eliminar la entidad intelectual «{0}», incluyendo todos sus elementos anidados? removeSelectedConfirmDialogMessage:¿Seguro que quiere eliminar los items seleccionados {0}? removingSuccessTitle:Eliminando Items -removingSuccessMessage:Executing action to remove {0} items -moveConfirmDialogTitle:Confirm moving of intellectual entities -moveAllConfirmDialogMessageSingle:Are you sure you want to move the intellectual entity "{0}", including all its nested items? -moveSelectedConfirmDialogMessage:Are you sure you want to move the selected {0} items? +removingSuccessMessage:Ejecutando acción para eliminar {0} elementos +moveConfirmDialogTitle:Confirmar traslado de entidades intelectuales +moveAllConfirmDialogMessageSingle:¿Está seguro de que desea mover la entidad intelectual «{0}», incluyendo todos sus elementos anidados? +moveSelectedConfirmDialogMessage:¿Está seguro de que desea mover los {0} elementos seleccionados? # Generic Buttons backButton:Regresar cancelButton:Cancelar @@ -349,20 +350,20 @@ revertButton:Revertir removeButton:Eliminar refreshButton:Refrescar newButton:Nuevo -newRepresentationButton:Create representation +newRepresentationButton:Crear representación editButton:Editar -liftButton:Lift +liftButton:Levantar deactivateButton:DESACTIVAR saveButton:Guardar addButton:Agregar confirmButton:Validar applyAllButton:Aplicar a todas las de abajo -overrideManualAppliedSchedulesButton:Apply rules and override -applyDisposalRulesButton:Apply rules keeping manually associated +overrideManualAppliedSchedulesButton:Aplicar reglas y sobrescribir +applyDisposalRulesButton:Aplicar reglas conservando las asociadas manualmente stopButton:Detener -approveButton:Approve +approveButton:Aprobar rejectButton:Rechazar -listButton:Created packages +listButton:Paquetes creados downloadButton:Descargar renameButton:Rebautizar moveButton:Mover @@ -375,9 +376,9 @@ changeStatusButton:Cambiar Estado clearButton:Limpiar selectAllButton:Todos closeButton:Cerrar -copyAndCloseButton:Copy & Close +copyAndCloseButton:Copiar y cerrar selectButton:Seleccionar -printButton:Print report +printButton:Imprimir informe # Identify formats identifyFormatsButton:Identificar formatos identifyingFormatsTitle:Identificar formatos @@ -388,8 +389,8 @@ viewRepresentationInfoFileButton:Información del archivo viewRepresentationErrorPreview:Ha ocurrido un error mientras intentaba ver el archivo viewRepresentationTooLargeErrorPreview:Este archivo es demasiado grande para abrirlo en el navegador viewRepresentationNotSupportedPreview:previsualización de archivos no es compatible -viewRepresentationNotSupportedPreviewCentralInstance:Synchronized files from distributed instances cannot be accessed via the central instance -viewRepresentationNotSupportedPreviewShallowFile:Could not retrieve the file using the reference to the external storage +viewRepresentationNotSupportedPreviewCentralInstance:No se puede acceder a los archivos sincronizados desde instancias distribuidas a través de la instancia central +viewRepresentationNotSupportedPreviewShallowFile:No se pudo recuperar el archivo utilizando la referencia al almacenamiento externo viewRepresentationFileDisseminationTitle:Diseminación viewRepresentationInfoFilename:Nombre del archivo viewRepresentationInfoSize:Tamaño @@ -397,21 +398,21 @@ viewRepresentationInfoExtension:Extensión viewRepresentationInfoMimetype:Mimetype viewRepresentationInfoFormat:Formato viewRepresentationInfoPronom:PRONOM -viewTechnicalInformation:Technical information -showTechnicalMetadata:Show technical metadata -viewTechnicalMetadata: Technical metadata +viewTechnicalInformation:Información técnica +showTechnicalMetadata:Mostrar metadatos técnicos +viewTechnicalMetadata: Metadatos técnicos viewRepresentationInfoCreatingApplicationName:Creando nombre de la aplicación viewRepresentationInfoCreatingApplicationVersion:Creando versión de la aplicación viewRepresentationInfoDateCreatedByApplication:Fecha creación por la aplicación viewRepresentationInfoHash:Fijación viewRepresentationInfoStoragePath:Ruta almacenamiento -viewRepresentationRemoveFileTitle:Confirm file remove -viewRepresentationRemoveFileMessage:Are you sure you want to remove this file? +viewRepresentationRemoveFileTitle:Confirmar eliminación de archivo +viewRepresentationRemoveFileMessage:¿Está seguro de que desea eliminar este archivo? # New Process processNewDefaultName:Trabajo {0, LocalDateTime, PREDEF\: DATETIME BREVE} pluginLabel:{0} -mandatoryPlugin:Mandatory -optionalPlugin:Optional +mandatoryPlugin:Obligatorio +optionalPlugin:Opcional pluginLabelWithVersion:{0} ({1}) pluginAipIdButton:Seleccionar processNewMissingMandatoryInfoDialogTitle:Falta información obligatoria @@ -430,16 +431,16 @@ showPluginCategories[management]:administración showPluginCategories[dissemination]:diseminación showPluginCategories[reindex]:reindización showPluginCategories[misc]:misceláneos -showPluginCategories[digital_signature]:digital signature -showPluginCategories[risk_assessment]:risk assessment -showPluginCategories[maintenance]:maintenance +showPluginCategories[digital_signature]:firma digital +showPluginCategories[risk_assessment]:evaluación de riesgos +showPluginCategories[maintenance]:mantenimiento showPluginCategories[eArchiving]:eArchiving -showPluginCategories[database_preservation]:database preservation +showPluginCategories[database_preservation]:preservación de bases de datos showJobStatusCreated:esperando showJobStatusStarted:ejecutando showJobStatusCompleted:Hecho -showJobStatusPendingApproval:pending approval -showJobStatusScheduled:scheduled +showJobStatusPendingApproval:pendiente de aprobación +showJobStatusScheduled:programado showJobStatusApprovalRejected:rechazado showJobStatusFailedDuringCreation:Falló inicio showJobStatusFailedToComplete:Falló @@ -461,28 +462,28 @@ showRepresentationExtended:Representación showFileExtended:Archivo showTransferredResourceExtended:Recursos transferidos showJobSourceObjects:Objetos de origen -showAttachments:Attachments -jobStopConfirmDialogTitle:Confirm job stop -jobStopConfirmDialogMessage:Are you sure you want to stop the job? -jobApproveConfirmDialogTitle:Confirm job approval -jobApproveConfirmDialogMessage:Are you sure you want to approve the job? -jobRejectConfirmDialogTitle:Confirm job reject -jobRejectConfirmDialogMessage:Are you sure you want to reject the job? -jobSelectedApproveConfirmDialogMessage: Are you sure you want to approve the selected {0} item(s)? -jobSelectedRejectConfirmDialogMessage: Are you sure you want to delete the selected {0} item(s)? +showAttachments:Adjuntos +jobStopConfirmDialogTitle:Confirmar detención del proceso +jobStopConfirmDialogMessage:¿Está seguro de que desea detener el proceso? +jobApproveConfirmDialogTitle:Confirmar aprobación del proceso +jobApproveConfirmDialogMessage:¿Está seguro de que desea aprobar el proceso? +jobRejectConfirmDialogTitle:Confirmar rechazo del proceso +jobRejectConfirmDialogMessage:¿Está seguro de que desea rechazar el proceso? +jobSelectedApproveConfirmDialogMessage: ¿Está seguro de que desea aprobar los {0} elemento(s) seleccionado(s)? +jobSelectedRejectConfirmDialogMessage: ¿Está seguro de que desea eliminar los {0} elemento(s) seleccionado(s)? # Browse itemId:Identificador del AIP sipId:Identificador del SIP -processIdTitle:Ingest process identifier +processIdTitle:Identificador del proceso de ingesta updateProcessIdTitle:Procesos de Modificación Identificados -dateCreated:Created on {0} by {1} -dateUpdated:Updated on {0} by {1} +dateCreated:Creado el {0} por {1} +dateUpdated:Actualizado el {0} por {1} aipCreated:Creado aipUpdated:Actualizado -dateCreatedOrUpdated:by {1} on {0} -dateCreatedAndUpdated:Created by {1} on {0} and last updated by {3} on {2} -itemInstanceId:Instance identifier +dateCreatedOrUpdated:por {1} el {0} +dateCreatedAndUpdated:Creado por {1} el {0} y última actualización por {3} el {2} +itemInstanceId:Identificador de instancia aipType:Tipo # Search searchDropdownLabels:{0} @@ -490,20 +491,20 @@ searchDropdownLabels[org.roda.core.data.v2.ip.IndexedAIP]:Entidades intelectuale searchDropdownLabels[org.roda.core.data.v2.ip.IndexedRepresentation]:Representaciones searchDropdownLabels[org.roda.core.data.v2.ip.IndexedFile]:Archivos searchDropdownLabels[org.roda.core.data.v2.ip.TransferredResource]:Recursos transferidos -searchDropdownLabels[org.roda.core.data.v2.ip.IndexedPreservationEvent]:disseminations -searchDropdownLabels[org.roda.core.data.v2.ip.IndexedDIP]:disseminations -searchDropdownLabels[org.roda.core.data.v2.ip.DIPFile]:dissemination files +searchDropdownLabels[org.roda.core.data.v2.ip.IndexedPreservationEvent]:Diseminaciones +searchDropdownLabels[org.roda.core.data.v2.ip.IndexedDIP]:Diseminaciones +searchDropdownLabels[org.roda.core.data.v2.ip.DIPFile]:Archivos de diseminación searchDropdownLabels[org.roda.core.data.v2.risks.IndexedRisk]:Riesgos searchDropdownLabels[org.roda.core.data.v2.risks.RiskIncidence]:Incidencias -searchDropdownLabels[org.roda.core.data.v2.ri.RepresentationInformation]:representation information +searchDropdownLabels[org.roda.core.data.v2.ri.RepresentationInformation]:Información de representación searchDropdownLabels[org.roda.core.data.v2.jobs.IndexedJob]:Procesos searchDropdownLabels[org.roda.core.data.v2.jobs.IndexedReport]:Reportes -searchDropdownLabels[org.roda.core.data.v2.notifications.Notification]:notifications -searchDropdownLabels[org.roda.core.data.v2.log.LogEntry]:Audit logs -searchDropdownLabels[org.roda.core.data.v2.user.RODAMember]:users and groups -searchDropdownLabels[org.roda.core.data.v2.ip.metadata.DescriptiveMetadata]:descriptive metadata -searchDropdownLabels[org.roda.core.data.v2.ip.metadata.PreservationMetadata]:preservation metadata -searchDropdownLabels[org.roda.core.data.v2.formats.Format]:formats +searchDropdownLabels[org.roda.core.data.v2.notifications.Notification]:Notificaciones +searchDropdownLabels[org.roda.core.data.v2.log.LogEntry]:Registros de auditoría +searchDropdownLabels[org.roda.core.data.v2.user.RODAMember]:Usuarios y grupos +searchDropdownLabels[org.roda.core.data.v2.ip.metadata.DescriptiveMetadata]:Metadatos descriptivos +searchDropdownLabels[org.roda.core.data.v2.ip.metadata.PreservationMetadata]:Metadatos de preservación +searchDropdownLabels[org.roda.core.data.v2.formats.Format]:Formatos searchDropdownLabels[org.roda.core.data.v2.Void]:No hay objetos de entrada searchListBoxItems:Entidades intelectuales searchListBoxRepresentations:Representaciones @@ -521,9 +522,9 @@ searchButton:Buscar # AIP, Representations, File & Transferred Resources aipGenericTitle:Titulo aipDates:Fechas -aipRiskIncidences:{0} risk incidences -aipEvents:{0} preservation events -aipLogs:{0} log entries +aipRiskIncidences:{0} incidencias de riesgo +aipEvents:{0} eventos de preservación +aipLogs:{0} entradas de registro fileId:Id fileName:Nombre filePath:Ruta @@ -572,13 +573,13 @@ preservationAgentNote:Nota preservationAgentExtension:Extensión # Select dialog selectAipCancelButton:Cancelar -selectAipEmptyParentButton:Move a la raíz +selectAipEmptyParentButton:Mover a la raíz selectAipSelectButton:Seleccionar selectAipSearchResults:Resultado de búsqueda selectRepresentationSearchResults:Resultado de búsqueda selectFileSearchResults:Resultado de búsqueda selectTransferredResourcesSearchResults:Resultado de búsqueda -renameTransferredResourcesDialogTitle:Rename transferred resource +renameTransferredResourcesDialogTitle:Cambiar nombre del recurso transferido # Rename Item renameItemTitle:Cambiar el nombre de item renameSuccessful:Éxito renombrado item! @@ -621,24 +622,24 @@ aipState[CREATED]:Creado aipState[DELETED]:Eliminado aipState[INGEST_PROCESSING]:Ingestando aipState[UNDER_APPRAISAL]:En evaluación -aipState[DESTROYED]:Destroyed +aipState[DESTROYED]:Destruido # Search pre-filters searchPreFilterSimpleFilterParameter:{0}\: {1} searchPreFilterBasicSearchFilterParameter:{0}\: {1} searchPreFilterNotSimpleFilterParameter:NOT {0}\: {1} searchPreFilterEmptyKeyFilterParameter:NO {0} -searchPreFilterLongRangeFilterParameter:{0} is between {1} and {2} -searchPreFilterLongRangeFilterParameterGreaterThan:{0} is greater than {1} -searchPreFilterLongRangeFilterParameterSmallerThan:{0} is smaller than {1} -searchPreFilterOneOfManyFilterParameterWithSize:{0} is one of one of {1} possible values +searchPreFilterLongRangeFilterParameter:{0} está entre {1} y {2} +searchPreFilterLongRangeFilterParameterGreaterThan:{0} es mayor que {1} +searchPreFilterLongRangeFilterParameterSmallerThan:{0} es menor que {1} +searchPreFilterOneOfManyFilterParameterWithSize:{0} es uno de uno de {1} posibles valores searchPreFilterOneOfManyFilterParameterSingle:{0}\: {1} -searchPreFilterOneOfManyFilterParameterWithList:{0} is one of {1,list} -searchPreFilterDateIntervalFilterParameter:{0} is between {1} and {2} -searchPreFilterDateIntervalFilterParameterFrom:{0} is between {1} and now -searchPreFilterDateIntervalFilterParameterTo:{0} is until {1} +searchPreFilterOneOfManyFilterParameterWithList:{0} es uno de {1,list} +searchPreFilterDateIntervalFilterParameter:{0} está entre {1} y {2} +searchPreFilterDateIntervalFilterParameterFrom:{0} está entre {1} y ahora +searchPreFilterDateIntervalFilterParameterTo:{0} hasta {1} searchPreFilterOr: OR  searchPreFilterAnd: AND  -searchPreFilterWhere: where +searchPreFilterWhere: donde searchPreFilterName:{0} searchPreFilterName[state]:estado searchPreFilterName[ingestJobId]:trabajo @@ -648,16 +649,16 @@ searchPreFilterName[ingestSIPIds]:SIP IDs searchPreFilterValue:{0} searchPreFilterValue[UNDER_APPRAISAL]:En evaluación # Risk register -riskRegisterTitle:Risk register -riskIncidenceRegisterTitle:Risk incidence register -riskIncidenceRegisterSearchPlaceHolder:Search risk incidences... +riskRegisterTitle:Registro de riesgos +riskIncidenceRegisterTitle:Registro de incidencias de riesgo +riskIncidenceRegisterSearchPlaceHolder:Buscar incidencias de riesgo... editRiskNotFound:Riesgo no encontrado {0} riskRemoveConfirmDialogTitle:Confirmar eliminación del riesgo riskRemoveSelectedConfirmDialogMessage:Estas seguro de querer eliminar los riesgos {0} seleccionados? riskHistoryRemoveConfirmDialogTitle:Confirmar eliminación del riesgo -riskHistoryRemoveConfirmDialogMessage:Are you sure you want to remove the selected risk history? -riskHistoryRevertConfirmDialogTitle:Confirm revert risk -riskHistoryRevertConfirmDialogMessage:Are you sure you want to revert the risk to the selected history? +riskHistoryRemoveConfirmDialogMessage:¿Está seguro de que desea eliminar el historial de riesgos seleccionado? +riskHistoryRevertConfirmDialogTitle:Confirmar reversión del riesgo +riskHistoryRevertConfirmDialogMessage:¿Está seguro de que desea revertir el riesgo al historial seleccionado? riskRemoveConfirmDialogCancel:No riskRemoveConfirmDialogOk:Si riskRemoveSuccessTitle:Reisgo(s) eliminados @@ -669,16 +670,16 @@ severityLevel[MODERATE]:Moderado severityLevel[HIGH]:Alta getRisksDialogName:Riesgos riskHistoryButton:Historia -editIncidenceNotFound:Incidence {0} not found -editIncidenceFailure:Edit incidence {0} failed -riskIncidenceRemoveConfirmDialogTitle:Confirm remove risk incidence -riskIncidenceRemoveSelectedConfirmDialogMessage:Are you sure you want to remove the selected {0} risk incidence(s)? +editIncidenceNotFound:Incidencia {0} no encontrada +editIncidenceFailure:Error al editar la incidencia {0} +riskIncidenceRemoveConfirmDialogTitle:Confirmar eliminación de incidencia de riesgo +riskIncidenceRemoveSelectedConfirmDialogMessage:¿Está seguro de que desea eliminar las {0} incidencia(s) de riesgo seleccionada(s)? riskIncidenceRemoveConfirmDialogCancel:No riskIncidenceRemoveConfirmDialogOk:Si -riskCreatedTitle:Risk created -riskCreatedMessage: Risk was successfully created +riskCreatedTitle:Riesgo creado +riskCreatedMessage: El riesgo se ha creado correctamente # Representation information register -representationInformationRegisterTitle:Representation network +representationInformationRegisterTitle:Red de representación representationInformationRegisterSearchPlaceHolder:Buscar información de representación editRepresentationInformationNotFound:Información de representación no encontrado {0} representationInformationRemoveFolderConfirmDialogTitle:Confirmar elminación de información de representación @@ -688,14 +689,14 @@ representationInformationRemoveFolderConfirmDialogOk:Si representationInformationRemoveSuccessTitle:Eliminar información de representación representationInformationRemoveSuccessMessage:Información de representación eliminado(s) exitosamente {0} representationInformationListItems:{0} -representationInformationAdditionalInformation:Additional information -representationInformationMissingFieldsTitle:Missing fields -representationInformationMissingFields:There are some mandatory empty fields -noTitleMessage:No title -currentRelationResults:List of current results -createNewRepresentationInformation:Create new -addToExistingRepresentationInformation:Add to existing -atLeastOneOfAbove:At least one of the above +representationInformationAdditionalInformation:Información adicional +representationInformationMissingFieldsTitle:Campos obligatorios +representationInformationMissingFields:Hay campos obligatorios vacíos +noTitleMessage:Sin título +currentRelationResults:Lista de resultados actuales +createNewRepresentationInformation:Crear nuevo +addToExistingRepresentationInformation:Añadir a existente +atLeastOneOfAbove:Al menos uno de los anteriores # Common Messages logParameter:{0}\: {1} windowTitle:RODA - {0} @@ -710,15 +711,15 @@ editGroupFailure:No se pudo editar el grupo {0} debido a un error\: {1}. editUserNotFound:El usuario {0} ya no existe. editUserEmailAlreadyExists:La dirección de correo electrónico {0} ya se utiliza está en los registros. Por favor seleccione otro correo electrónico o recuperar su nombre de usuario y contraseña. editGroupNotFound:El grupo {0} ya no existe. -userRemoveConfirmDialogTitle:User and group remove process -userRemoveConfirmDialogMessage:Do you want to remove the selected users and groups? +userRemoveConfirmDialogTitle:Proceso de eliminación de usuarios y grupos +userRemoveConfirmDialogMessage:¿Desea eliminar los usuarios y grupos seleccionados? userGroups:Grupos addUserButton:Agregar usuario addGroupButton:Agregar grupo userIdentifier:Identificador userFullName:Nombre completo # Job and report -jobInstanceId:Instance identifier +jobInstanceId:Identificador de instancia jobName: Nombre jobCreator: Creador jobParallelismShortBadge: {0} @@ -740,15 +741,15 @@ jobStartDate: Fecha inicio jobEndDate: Fecha fin jobDuration: Duración jobStatus: Estado -jobStateDetails: State details +jobStateDetails: Detalles del estado jobProgress: Progreso jobPlugin: Plugin jobProcessed: Reportes jobProcessedSearchPlaceHolder: Buscar reportes... reportJob: Trabajo reportIngestType: Tipo -newIngestion: New ingestion -ingestionUpdate: Ingest update +newIngestion: Nueva ingesta +ingestionUpdate: Actualización de ingesta reportDateCreated: Feecha de creación reportDateUpdated: Fecha modificación reportDuration: Duración @@ -761,7 +762,7 @@ jobFailureCountMessage: Falla reportLastUpdatedAt: Última modificación en reportLastRunTask: Ejecutar última tarea reportStatus:Estado -reportScheduleInfo:Schedule Info +reportScheduleInfo:Información de calendario reportProgress:Progreso reportOutcome: Resultado reportSource: Fuente @@ -816,19 +817,19 @@ riskIncidenceStatusValue[UNMITIGATED]:No mitigado riskIncidenceStatusValue[MITIGATED]:mitigado riskIncidenceStatusValue[ACCEPT_RISK]:Acepta el riesgo riskIncidenceStatusValue[FALSE_POSITIVE]:Falso positivo -riskNotMitigatedIncidences:Not mitigated +riskNotMitigatedIncidences:No mitigado # Representation information representationInformationIdentifier:Identificador representationInformationName:Nombre representationInformationDescription:Descripción -representationInformationFamily:Family -representationInformationTags:Tags -representationInformationSupport:Support level +representationInformationFamily:Familia +representationInformationTags:Etiquetas +representationInformationSupport:Nivel de soporte representationInformationSupportValue:{0} -representationInformationSupportValue[KNOWN]:Known +representationInformationSupportValue[KNOWN]:Conocido representationInformationSupportValue[SUPPORTED]:Soportado -representationInformationSupportValue[UNSUPPORTED]:Unsupported -representationInformationRelationType:Relation type +representationInformationSupportValue[UNSUPPORTED]:No soportado +representationInformationRelationType:Tipo de relación representationInformationRelationObjectType:Tipo de Objeto representationInformationRelationObjectType[AIP]:Entidad intelectual representationInformationRelationObjectType[REPRESENTATION_INFORMATION]:Información de representación @@ -838,44 +839,47 @@ representationInformationRelationLink:Link representationInformationRelationTitle:Titulo representationInformationAddNewRelation:Add new relation representationInformationEditAssociations:Edit associations -representationInformationIntellectualEntities:There are {0,number} intellectual entities associated with this representation information -representationInformationIntellectualEntities[\=0]:There are no intellectual entities associated with this representation information -representationInformationIntellectualEntities[\=1]:There is one intellectual entity associated with this representation information -representationInformationRepresentations:There are {0,number} representations associated with this representation information -representationInformationRepresentations[\=0]:There are no representations associated with this representation information -representationInformationRepresentations[\=1]:There is one representation associated with this representation information -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 +representationInformationIntellectualEntities:Hay {0,number} entidades intelectuales asociadas a esta información de representación +representationInformationIntellectualEntities[\=0]:No hay entidades intelectuales asociadas a esta información de representación +representationInformationIntellectualEntities[\=1]:Hay una entidad intelectual asociada a esta información de representación +representationInformationIntellectualEntities[one]:Hay una entidad intelectual asociada a esta información de representación +representationInformationRepresentations:Hay {0,number} representaciones asociadas a esta información de representación +representationInformationRepresentations[\=0]:No hay representaciones asociadas a esta información de representación +representationInformationRepresentations[\=1]:Hay una representación asociada a esta información de representación +representationInformationRepresentations[one]:Hay una representación asociada a esta información de representación +representationInformationFiles:Hay {0,number} archivos asociados a esta información de representación +representationInformationFiles[\=0]:No hay archivos asociados a esta información de representación +representationInformationFiles[\=1]:Hay un archivo asociado a esta información de representación +representationInformationFiles[one]:Hay un archivo asociado a esta información de representación # Descriptive Metadata metadataType:Tipo metadataFilename:Nombre archivo #Other Metadata -otherMetadata:other metadata +otherMetadata:otros metadatos # Browse -appraisalTitle:Assessment +appraisalTitle:Evaluación appraisalAccept:Aceptar appraisalReject:Rechazar transferredResourcesTitle:Recursos transferidos newArchivalPackage:Nuevo -newSublevel:Create sublevel +newSublevel:Crear subnivel moveArchivalPackage:Mover archivalPackagePermissions:Permisos archivalPackagePermissionsTitle:Permiso paquete de archivos disseminationPermissions:Permisos removeArchivalPackage:Eliminar -preservationTitle:Preservation +preservationTitle:Preservación newProcessPreservation:Proceso preservationEvents:Eventos preservationRisks:Riesgos downloadDocumentation:Documentación -downloadNoDocumentationTitle:No documentation -downloadNoDocumentationDescription:There are no documentation on this intellectual entity -downloadSubmissions:Download submissions -downloadNoSubmissionsTitle:No submissions -downloadNoSubmissionsDescription:There are no submissions on this intellectual entity +downloadNoDocumentationTitle:Sin documentación +downloadNoDocumentationDescription:No hay documentación en esta entidad intelectual +downloadSubmissions:Descargar envíos +downloadNoSubmissionsTitle:Sin envíos +downloadNoSubmissionsDescription:No hay envíos en esta entidad intelectual addPermission:Agregar permiso permissionAssignedGroups:Asignar grupo permissionAssignedUsers:Asignar usuarios @@ -885,8 +889,8 @@ listOfAIPs:Lista de AIPs listOfRepresentations:Lista de representación listOfDisseminations:Lista de diseminadores unknownAncestorError:Antecesor desconocido -searchPrevious:Previous -searchNext:Next +searchPrevious:Anterior +searchNext:Siguiente searchContext:En este contexto searchAIP:En este paquete de archivo aipPermissionDetails:Permisos @@ -895,8 +899,8 @@ representation:Representación representationListOfFiles:Lista de archivos representationRemoveTitle:Eliminar representación representationRemoveMessage:¿Seguro que desea eliminar toda la representación? -entityTypeAddNew:Add new ... -entityTypeNewLabel:New type +entityTypeAddNew:Agregar nuevo... +entityTypeNewLabel:Nuevo tipo # File file:Archivo folder:Carpeta @@ -905,7 +909,7 @@ selectedFileRemoveMessage:¿Seguro que desea eliminar los archivos seleccionados # Dissemination dissemination:Diseminación # Dissemination File -disseminationFile:Dissemination File +disseminationFile:Archivo de diseminación # Job processes # Login loginUsername:Nombre de usuario @@ -913,29 +917,29 @@ loginPassword:Contraseña loginRecoverPassword:Recuperar clave loginRegister:Regístrate loginLogin:Usuario -loginResendEmail:Resend verification email -loginResendEmailSuccessDialogTitle:Email verification -loginResendEmailSuccessDialogMessage:Please check your inbox for the verification email and follow the link in the message. +loginResendEmail:Reenviar correo de verificación +loginResendEmailSuccessDialogTitle:Verificación de correo electrónico +loginResendEmailSuccessDialogMessage:Por favor, revise su bandeja de entrada para el correo de verificación y siga el enlace del mensaje. loginResendEmailSuccessDialogButton:De acuerdo -loginResendEmailFailureDialogTitle:Email verification -loginResendEmailFailureDialogMessage:It was not possible to send the verification email. Check the username is correct and try again. +loginResendEmailFailureDialogTitle:Verificación de correo electrónico +loginResendEmailFailureDialogMessage:No fue posible enviar el correo de verificación. Compruebe que el nombre de usuario es correcto e inténtelo de nuevo. loginResendEmailFailureDialogButton:De acuerdo -loginResendEmailVerificationFailure:It was not possible to send the verification email. Check the username is correct and try again. +loginResendEmailVerificationFailure:No fue posible enviar el correo de verificación. Compruebe que el nombre de usuario es correcto e inténtelo de nuevo. fillUsernameAndPasswordMessage:Por favor ingresar usuario y clave wrongUsernameAndPasswordMessage:Usuario o clave incorrecto -emailUnverifiedMessage:You must validate your email to login. Please check your inbox for the verification email and follow the link in the message. To receive a new email click the button "Resend verification email" bellow. If you do not receive any e-mail please contact the administrator. -inactiveUserMessage:Your user was deactivated, please contact the administrator. +emailUnverifiedMessage:Debe validar su correo electrónico para iniciar sesión. Por favor, revise su bandeja de entrada para el correo de verificación y siga el enlace del mensaje. Para recibir un nuevo correo, haga clic en el botón "Reenviar correo de verificación". Si no recibe ningún correo, contacte con el administrador. +inactiveUserMessage:Su usuario ha sido desactivado. Por favor, contacte con el administrador. systemCurrentlyUnavailableMessage:Sistema no disponible en este momento -learnMore:Learn More +learnMore:Más información # More Search -searchDuplicateWarningMessage:The {0} field is duplicated. An 'OR' operator will be used. +searchDuplicateWarningMessage:El campo {0} está duplicado. Se utilizará el operador 'OR'. # Activity log logEntryIdentifier:Identificador logEntryComponent:Componente logEntryMethod:Método logEntryAddress:Dirección logEntryDate:Fecha -logEntryDatetime:Datetime +logEntryDatetime:Fecha y hora logEntryDuration:Duración logEntryRelatedObject:Objeto relacionado logEntryUsername:Nombre de usuario @@ -946,8 +950,8 @@ logEntryStateValue:{0} logEntryStateValue[SUCCESS]:Éxito logEntryStateValue[FAILURE]:Fracaso logEntryStateValue[UNAUTHORIZED]:No autorizado -logEntryInstanceId:Instance -relatedAuditLogs: Related audit logs +logEntryInstanceId:Instancia +relatedAuditLogs: Registros de auditoría relacionados # Notifications notificationAck:Reconocido notificationIdentifier:Identificador @@ -980,47 +984,47 @@ title[browse]:Catalogo title[search]:Buscar title[help]:Ayuda title[administration]:Administración -title[administration_actions]:Preservation actions -title[administration_internal_actions]:Internal actions -title[administration_disposal]:Disposal -title[administration_user]:Users and groups +title[administration_actions]:Acciones de preservación +title[administration_internal_actions]:Acciones internas +title[administration_disposal]:Eliminación +title[administration_user]:Usuarios y grupos title[administration_user_users]:Lista de usuarios title[administration_user_groups]:Lista de grupos title[administration_event]:Programador title[administration_event_tasks]:Programador title[administration_event_taskInstances]:Lista title[administration_metadataEditor]:Edición de metadata -title[administration_statistics]:Reporting -title[administration_log]:Audit logs +title[administration_statistics]:Informes +title[administration_log]:Registros de auditoría title[administration_help]:Ayuda title[administration_preferences]:Preferencias -title[administration_notifications]:Notification logs -title[administration_distributed_instances]:Distributed Instances -title[administration_local_instance_configuration]:Local Instance -title[administration_access_keys]:Access Keys -title[administration_monitoring]:Monitoring -title[administration_market_place]:Marketplace -title[disposal]:Disposal -title[disposal_policies]:Disposal policies -title[disposal_confirmations]:Disposal confirmations -title[overdue_actions]:Overdue records -title[disposal_destroyed_records]:Destroyed records +title[administration_notifications]:Registros de notificaciones +title[administration_distributed_instances]:Instancias distribuidas +title[administration_local_instance_configuration]:Instancia local +title[administration_access_keys]:Tokens de acceso +title[administration_monitoring]:Monitorización +title[administration_market_place]:Mercado de aplicaciones +title[disposal]:Eliminación +title[disposal_policies]:Políticas de eliminación +title[disposal_confirmations]:Confirmaciones de eliminación +title[overdue_actions]:Registros vencidos +title[disposal_destroyed_records]:Registros destruidos title[planning]:Planificando title[planning_monitoring]:Monitoreo interno -title[planning_risk]:Risk register -title[planning_representation_information]:Representation network +title[planning_risk]:Registro de riesgos +title[planning_representation_information]:Red de representación title[planning_event]:Eventos de preservación title[planning_agent]:Agentes de preservación title[planning_format]:Formato de Registro title[ingest]:Ingesta title[ingest_preIngest]:Pre-ingesta -title[ingest_transfer]:Transfer +title[ingest_transfer]:Transferencia title[ingest_submit]:Enviar title[ingest_submit_upload]:Enviar paquete title[ingest_submit_create]:Crear paquete title[ingest_list]:Procesos title[ingest_help]:Ayuda -title[ingest_appraisal]:Assessment +title[ingest_appraisal]:Evaluación title[settings]:Configuración title[language]:Lenguaje # Login Panel @@ -1029,16 +1033,16 @@ loginProfile:Perfil loginDialogTitle:Autenticación loginDialogLogin:Usuario loginDialogCancel:Cancelar -loggedIn:You are already logged in as {0}, you need to log out before logging in again. -welcomePage:Welcome page -casInformationText:You can also integrate with various authentication protocols using RODA Enterprise Identity and Access Management. +loggedIn:Ya ha iniciado sesión como {0}. Debe cerrar sesión antes de volver a iniciar sesión. +welcomePage:Página de bienvenida +casInformationText:También puede integrarse con varios protocolos de autenticación mediante RODA Enterprise Identity and Access Management. casTitleText:RODA Enterprise Identity and Access Management # HOME homeTitle:Ir a inicio # Content Panel authorizationDeniedAlert:Autorización Denegada -authorizationDeniedAlertMessageMissingRoles:Do not have permission to do this operation because your user is missing the roles: {0,list} -authorizationDeniedAlertMessageExceptionSimple:You do not have permission to do this operation.
{0} +authorizationDeniedAlertMessageMissingRoles:No tiene permiso para realizar esta operación porque su usuario carece de los siguientes roles: {0,list} +authorizationDeniedAlertMessageExceptionSimple:No tiene permiso para realizar esta operación.
{0} casForwardWarning:Tienes que estar autenticado para acceder a esta página. ¿Se desea autenticar? # Cookies cookiesMessage:Este sitio web utiliza cookies para asegurarse de obtener la mejor experiencia. @@ -1060,10 +1064,10 @@ createUserCreate:GUARDAR # Show User showUserTitle:Usuario showUserStatusLabel:Estado -showUserActivated:Activated -showUserDeactivated:Deactivated -showUserEmptyGroupList:There is no group associated to this user -showUserEmptyPermissions:There is no permission associated to this user +showUserActivated:Activado +showUserDeactivated:Desactivado +showUserEmptyGroupList:No hay ningún grupo asociado a este usuario +showUserEmptyPermissions:No hay ningún permiso asociado a este usuario # Edit User editUserTitle:Editar usuario editUserCancel:Cancelar @@ -1073,7 +1077,7 @@ editUserRemove:Eliminar editUserApply:Guardar editUserAction:Editar # Pre ingest panel -dropFolderInformationText:Did you know you can integrate with production systems for automatic ingest using RODA Enterprise Drop Folders? +dropFolderInformationText:¿Sabía que puede integrar con sistemas de producción para la ingesta automática con RODA Enterprise Drop Folders? # User Data Panel username:Nombre usuario password:Contraseña @@ -1092,9 +1096,9 @@ createGroupTitle:Crear grupo createGroupCreate:GUARDAR createGroupCancel:CANCELAR # Show Group -showGroupTitle:Group -showGroupEmptyUserList:There is no user associated to this group -showGroupEmptyPermissions:There is no permission associated to this group +showGroupTitle:Grupo +showGroupEmptyUserList:No hay ningún usuario asociado a este grupo +showGroupEmptyPermissions:No hay ningún permiso asociado a este grupo # Edit Group editGroupTitle:Editar grupo editGroupCancel:CANCELAR @@ -1112,7 +1116,7 @@ preferencesSubmit:GUARDAR preferencesCancel:CANCELAR # Roles Description role:{0} (no traducido) -role[aip.view]:Retrieve intellectual entities (AIPs) +role[aip.view]:Recuperar entidades intelectuales (AIPs) role[aip.read]:Lista y obtención de entidades intelectuales (AIPs) role[aip.appraisal]:Aceptar o rechazar entidades intelectuales en la evaluación role[aip.create.top]:Crear entidades intelectuales superiores @@ -1120,7 +1124,7 @@ role[aip.create.below]:Crear entidades intelectuales role[aip.update]:Actualizar entidades intelectuales role[aip.delete]:Eliminar entidades intelectuales -role[representation.view]:Retrieve representations and computer files +role[representation.view]:Recuperar representaciones y archivos role[representation.read]:Listar y obtener representaciones y archivos de computadora role[representation.create]:Crear representaciones y archivos de computadora role[representation.update]:Actualizar representaciones y archivos de computadora @@ -1150,30 +1154,30 @@ role[log_entry.create]:Crear entradas de registro role[log_entry.delete]:Eliminar entradas de registro role[risk.read]:Listar y ver los riesgos de preservación role[risk.manage]:Gestionar los riesgos de preservación -role[ri.read]:List and view representation information -role[ri.manage]:Manage representation information +role[ri.read]:Listar y ver información de representación +role[ri.manage]:Gestionar información de representación role[format.read]:Listar y ver la información del formato de archivo role[format.manage]:Administrar la información del formato de archivo role[permission.read]:Leer y consultar sobre los permisos de otros usuarios -role[disposal_rule.read]:List and view disposal rule information -role[disposal_rule.manage]:Manage disposal rule information -role[disposal_schedule.read]:List and view disposal schedule information -role[disposal_schedule.manage]:Manage disposal schedule information -role[disposal_schedule.associate]:Associate or disassociate disposal schedule from intellectual entities (AIPs) -role[disposal_hold.read]:List and view disposal hold information -role[disposal_hold.manage]:Manage disposal hold information -role[disposal_hold.apply]:Apply or lift disposal hold from intellectual entities (AIPs) -role[disposal_confirmation.read]:List and view disposal confirmation information -role[disposal_confirmation.manage]:Manage disposal confirmation information -role[disposal_confirmation.destroy]:Destroy intellectual entities (AIPs) according to the disposal confirmation -role[disposal_confirmation.restore]:Restore destroyed intellectual entities (AIPs) according to the disposal confirmation -role[disposal_confirmation.delete_bin]:Permanently delete destroyed intellectual entities (AIPs) according to the disposal confirmation -role[distributed_instances.read]:List and view distributed instances -role[distributed_instances.manage]:Manage distributed instances -role[local_instance_configuration.read]:View local instance configuration -role[local_instance_configuration.manage]:Manage local instance configuration -role[access_key.read]:List and view access keys for users and distributed instances -role[access_key.manage]:Manage access keys for users and distributed instances +role[disposal_rule.read]:Listar y ver información de reglas de eliminación +role[disposal_rule.manage]:Gestionar información de reglas de eliminación +role[disposal_schedule.read]:Listar y ver información de calendarios de eliminación +role[disposal_schedule.manage]:Gestionar información de calendarios de eliminación +role[disposal_schedule.associate]:Asociar o desasociar calendarios de eliminación de entidades intelectuales (AIPs) +role[disposal_hold.read]:Listar y ver información de bloqueos de eliminación +role[disposal_hold.manage]:Gestionar información de bloqueos de eliminación +role[disposal_hold.apply]:Aplicar o levantar bloqueos de eliminación de entidades intelectuales (AIPs) +role[disposal_confirmation.read]:Listar y ver información de confirmaciones de eliminación +role[disposal_confirmation.manage]:Gestionar información de confirmaciones de eliminación +role[disposal_confirmation.destroy]:Destruir entidades intelectuales (AIPs) según la confirmación de eliminación +role[disposal_confirmation.restore]:Restaurar entidades intelectuales (AIPs) destruidas según la confirmación de eliminación +role[disposal_confirmation.delete_bin]:Eliminar permanentemente entidades intelectuales (AIPs) destruidas según la confirmación de eliminación +role[distributed_instances.read]:Listar y ver instancias distribuidas +role[distributed_instances.manage]:Gestionar instancias distribuidas +role[local_instance_configuration.read]:Ver configuración de instancia local +role[local_instance_configuration.manage]:Gestionar configuración de instancia local +role[access_key.read]:Listar y ver tokens de acceso de usuarios e instancias distribuidas +role[access_key.manage]:Gestionar tokens de acceso de usuarios e instancias distribuidas # Register registerUserDataTitle:Regístrate registerSubmit:REGISTRATE @@ -1200,22 +1204,22 @@ verifyEmailSuccessDialogMessage:La dirección se ha verificado correctamente. verifyEmailSuccessDialogButton:De acuerdo # Recover Login recoverLoginTitle:Recuperar usuario -recoverLoginEmail:E-mail address +recoverLoginEmail:Dirección de correo electrónico recoverLoginSubmit:RECUPERAR recoverLoginCancel:Cancelar recoverLoginCaptchaFailed:Fallado en la prueba de diferenciación entre seres humanos y computadoras, por favor, inténtelo de nuevo. recoverLoginSuccessDialogTitle:Recuperar registro -recoverLoginSuccessDialogMessage:If this username or email belongs to an existing user, an email will be sent to recover the password. +recoverLoginSuccessDialogMessage:Si este nombre de usuario o correo electrónico pertenece a un usuario existente, se enviará un correo para recuperar la contraseña. recoverLoginSuccessDialogButton:De acuerdo # Reset Password resetPasswordTitle:Borra clave -setPasswordTitle:Set password +setPasswordTitle:Establecer contraseña resetPasswordUsername:Nombre de usuario resetPasswordToken:Clave de verificación resetPasswordNewPassword:Nueva clave resetPasswordRepeatPassword:Repetir clave resetPasswordSubmit:CAMBIO -setPasswordSubmit:SET +setPasswordSubmit:ESTABLECER resetPasswordCancel:Cancelar resetPasswordInvalidToken:La contraseña de recuperación no es válida. Por favor, compruebe si ha introducido la contraseña correcta o pedir uno nuevo. resetPasswordNoSuchUser:El nombre de usuario no existe. Por favor, compruebe si ha introducido la correcta. @@ -1240,19 +1244,19 @@ pluginStateMessage[FAILURE]:Fracaso pluginStateMessage[RUNNING]:Corriendo pluginStateMessage[PARTIAL_SUCCESS]:Éxito parcial pluginStateMessage[SKIPPED]:Omitida -pluginNotInstalledLabel:Not installed -pluginLicenseLabel:Package specifications -pluginDocumentationLabel:Documentation +pluginNotInstalledLabel:No instalado +pluginLicenseLabel:Especificaciones del paquete +pluginDocumentationLabel:Documentación pluginHomepageLabel:Inicio pluginLicenseStatus:{0} -pluginLicenseStatus[INTERNAL]:Built-in -pluginLicenseStatus[VERIFIED]:Verified -pluginLicenseStatus[NOT_VERIFIED]:Not verified -pluginNotInstalledMessage:Please select the "Install" button for more information on how to install this action. -pluginInternalMessage:This action is part of RODA. -pluginTrustedMessage:Developed by {0} and licensed to {1} -pluginUntrustedMessage:Publisher is unknown or it cannot be verified. For security reasons this action cannot be run. -pluginVendorLabel:Vendor: +pluginLicenseStatus[INTERNAL]:Integrado +pluginLicenseStatus[VERIFIED]:Verificado +pluginLicenseStatus[NOT_VERIFIED]:No verificado +pluginNotInstalledMessage:Seleccione el botón «Instalar» para obtener más información sobre cómo instalar esta acción. +pluginInternalMessage:Esta acción forma parte de RODA. +pluginTrustedMessage:Desarrollado por {0} y con licencia para {1} +pluginUntrustedMessage:El editor es desconocido o no se puede verificar. Por razones de seguridad, esta acción no se puede ejecutar. +pluginVendorLabel:Proveedor: notificationStateValue:{0} notificationStateValue[CREATED]:Creado notificationStateValue[COMPLETED]:Éxito @@ -1288,30 +1292,30 @@ versionAction[metadata_type_forced]:Forzar tipo de metadatos versionAction[update_from_sip]:Actualización del SIP versionActionBy:{0} por {1} mandatoryField:Campo obligatorio -numberIsRequired:Number is required +numberIsRequired:Se requiere un número isAMandatoryField:{0} es uno campo obligatorio passwordIsTooSmall:La contraseña es demasiado pequeña passwordDoesNotMatchConfirmation:La contraseña y la confirmación no coinciden -emailNotValid:E-mail is not valid +emailNotValid:El correo electrónico no es válido wrongMailFormat:E-Mail con formato errado fileAlreadyExists:Archivo ya existe tableDownloadCSV:Exportar -tableUpdateOn:Updating every 10 seconds, click to pause. -tableUpdating:Updating... -tableUpdatePause:Paused, click to resume auto-update. -tableUpdateErrorConnection:Could not connect to server. Click to retry. +tableUpdateOn:Actualizando cada 10 segundos, haga clic para pausar. +tableUpdating:Actualizando... +tableUpdatePause:En pausa, haga clic para reanudar la actualización automática. +tableUpdateErrorConnection:No se pudo conectar al servidor. Haga clic para reintentar. actionableEmptyHelp:Por favor, seleccione los elementos mediante las casillas de verificación actionableEmptyHelp[NONE]:Por favor, seleccione los elementos mediante las casillas de verificación actionableEmptyHelp[SINGLE]:No hay acciones disponibles para el ítem seleccionado actionableEmptyHelp[MULTIPLE]:No hay acciones disponibles para los ítems seleccionados showMore:Ver más showLess:Ver menos -uriLinkingIdentifierTitle:External URI -browseFileDipEmpty:No entries -browseFileDipDelete:Delete dissemination -browseFileDipOpenedExternalURL:Opened dissemination in another page -browseFileDipRepresentationConfirmTitle:Confirm dissemination remove -browseFileDipRepresentationConfirmMessage:Are you sure you want to remove this dissemination? +uriLinkingIdentifierTitle:URI externo +browseFileDipEmpty:Sin entradas +browseFileDipDelete:Eliminar diseminación +browseFileDipOpenedExternalURL:Diseminación abierta en otra página +browseFileDipRepresentationConfirmTitle:Confirmar eliminación de diseminación +browseFileDipRepresentationConfirmMessage:¿Está seguro de que desea eliminar esta diseminación? jobReportSource:Fuente jobReportSource[org.roda.core.data.v2.ip.AIP]:Entidade intelectual fuente jobReportSource[org.roda.core.data.v2.ip.Representation]:Representacion fuente @@ -1325,7 +1329,7 @@ jobReportSource[org.roda.core.data.v2.ri.RepresentationInformation]:Información jobReportSource[org.roda.core.data.v2.jobs.Job]:Proceso fuente jobReportSource[org.roda.core.data.v2.jobs.Report]:Reporte fuente jobReportSource[org.roda.core.data.v2.notifications.Notification]:Notificacion fuente -jobReportSource[org.roda.core.data.v2.log.LogEntry]:Source audit log +jobReportSource[org.roda.core.data.v2.log.LogEntry]:Registro de auditoría fuente jobReportSource[org.roda.core.data.v2.user.RODAMember]:Miembro fuente jobReportSource[org.roda.core.data.v2.ip.metadata.DescriptiveMetadata]:Metadatos descriptivos fuente jobReportSource[org.roda.core.data.v2.ip.metadata.PreservationMetadata]:Metadatos de preservación fuente @@ -1343,382 +1347,388 @@ jobReportOutcome[org.roda.core.data.v2.ri.RepresentationInformation]:Informació jobReportOutcome[org.roda.core.data.v2.jobs.Job]:Proceso resultado jobReportOutcome[org.roda.core.data.v2.jobs.Report]:Reporte resultado jobReportOutcome[org.roda.core.data.v2.notifications.Notification]:Notificacion resultado -jobReportOutcome[org.roda.core.data.v2.log.LogEntry]:Outcome audit log +jobReportOutcome[org.roda.core.data.v2.log.LogEntry]:Registro de auditoría resultado jobReportOutcome[org.roda.core.data.v2.user.RODAMember]:Miembro resultado jobReportOutcome[org.roda.core.data.v2.ip.metadata.DescriptiveMetadata]:Metadatos descriptivos resultado jobReportOutcome[org.roda.core.data.v2.ip.metadata.PreservationMetadata]:Metadatos de preservación resultado jobReportOutcome[org.roda.core.data.v2.formats.Format]:Formato resultado -sourceObjectList:A manually selected list with {0} {1} +sourceObjectList:Una lista seleccionada manualmente con {0} {1} runningInBackgroundTitle:La acción se está ejecutando en último plano -runningInBackgroundDescription:The progress of the action can be monitored on the internal actions page -exportListTitle:Exporting list -exportListMessage:Exporting list with a maximum limit of {0} items +runningInBackgroundDescription:El progreso de la acción puede monitorizarse en la página de acciones internas +exportListTitle:Exportando lista +exportListMessage:Exportando lista con un límite máximo de {0} elementos representationInformationAssociatedWith: -representationInformationAssociatedWith[AIP]:Associated with intellectual entities where field {0} is {1} -representationInformationAssociatedWith[Representation]:Associated with representations where field {0} is {1} -representationInformationAssociatedWith[File]:Associated with files where field {0} is {1} -representationInformationAssociatedWithDescription:This page presents information that maps {2} where field {0} is {1} into more meaningful concepts, including structure information about how the information is organized, semantic information that further describes the meaning of encoded information, and other information useful for the understanding of the content. Each record of representation information can be connected to other representation information, intellectual entities and external resources (e.g. Web pages or books) creating a representation network that aims to fully describe the meaning of the digital object. -representationInformationAssociatedWithDescription[AIP]:This page presents information that maps intellectual entities where field {0} is {1} into more meaningful concepts, including structure information about how the information is organized, semantic information that further describes the meaning of encoded information, and other information useful for the understanding of the content. Each record of representation information can be connected to other representation information, intellectual entities and external resources (e.g. Web pages or books) creating a representation network that aims to fully describe the meaning of the digital object. -representationInformationAssociatedWithDescription[Representation]:This page presents information that maps representations where field {0} is {1} into more meaningful concepts, including structure information about how the information is organized, semantic information that further describes the meaning of encoded information, and other information useful for the understanding of the content. Each record of representation information can be connected to other representation information, intellectual entities and external resources (e.g. Web pages or books) creating a representation network that aims to fully describe the meaning of the digital object. -representationInformationAssociatedWithDescription[File]:This page presents information that maps files where field {0} is {1} into more meaningful concepts, including structure information about how the information is organized, semantic information that further describes the meaning of encoded information, and other information useful for the understanding of the content. Each record of representation information can be connected to other representation information, intellectual entities and external resources (e.g. Web pages or books) creating a representation network that aims to fully describe the meaning of the digital object. +representationInformationAssociatedWith[AIP]:Asociado con entidades intelectuales donde el campo {0} es {1} +representationInformationAssociatedWith[Representation]:Asociado con representaciones donde el campo {0} es {1} +representationInformationAssociatedWith[File]:Asociado con archivos donde el campo {0} es {1} +representationInformationAssociatedWithDescription:Esta página presenta información que mapea {2} donde el campo {0} es {1} en conceptos más significativos, incluyendo información de estructura sobre cómo está organizada la información, información semántica que describe el significado de la información codificada, y otra información útil para comprender el contenido. Cada registro de información de representación puede conectarse a otra información de representación, entidades intelectuales y recursos externos (p. ej. páginas web o libros) creando una red de representación que busca describir completamente el significado del objeto digital. +representationInformationAssociatedWithDescription[AIP]:Esta página presenta información que mapea entidades intelectuales donde el campo {0} es {1} en conceptos más significativos, incluyendo información de estructura sobre cómo está organizada la información, información semántica que describe el significado de la información codificada, y otra información útil para comprender el contenido. Cada registro de información de representación puede conectarse a otra información de representación, entidades intelectuales y recursos externos (p. ej. páginas web o libros) creando una red de representación que busca describir completamente el significado del objeto digital. +representationInformationAssociatedWithDescription[Representation]:Esta página presenta información que mapea representaciones donde el campo {0} es {1} en conceptos más significativos, incluyendo información de estructura sobre cómo está organizada la información, información semántica que describe el significado de la información codificada, y otra información útil para comprender el contenido. Cada registro de información de representación puede conectarse a otra información de representación, entidades intelectuales y recursos externos (p. ej. páginas web o libros) creando una red de representación que busca describir completamente el significado del objeto digital. +representationInformationAssociatedWithDescription[File]:Esta página presenta información que mapea archivos donde el campo {0} es {1} en conceptos más significativos, incluyendo información de estructura sobre cómo está organizada la información, información semántica que describe el significado de la información codificada, y otra información útil para comprender el contenido. Cada registro de información de representación puede conectarse a otra información de representación, entidades intelectuales y recursos externos (p. ej. páginas web o libros) creando una red de representación que busca describir completamente el significado del objeto digital. representationInformationNameFromAssociation: -representationInformationNameFromAssociation[AIP]:Intellectual entities where field {0} is {1} -representationInformationNameFromAssociation[Representation]:Representations where field {0} is {1} -representationInformationNameFromAssociation[File]:Files where field {0} is {1} -insertImageUrl:Insert a image URL -insertLinkUrl:Insert a URL -editHTMLContent:Edit content via HTML -searchButtonAdvancedSearch:advanced +representationInformationNameFromAssociation[AIP]:Entidades intelectuales donde el campo {0} es {1} +representationInformationNameFromAssociation[Representation]:Representaciones donde el campo {0} es {1} +representationInformationNameFromAssociation[File]:Archivos donde el campo {0} es {1} +insertImageUrl:Insertar una URL de imagen +insertLinkUrl:Insertar una URL +editHTMLContent:Editar contenido mediante HTML +searchButtonAdvancedSearch:avanzado details:Detalles -selectAllPages:Select all pages -selectThisPage:Select this page -dateRangeFieldFrom:from -dateRangeFieldTo:to -inputStorageSizeList:Units -removableTextBox:Removable text box +selectAllPages:Seleccionar todas las páginas +selectThisPage:Seleccionar esta página +dateRangeFieldFrom:desde +dateRangeFieldTo:hasta +inputStorageSizeList:Unidades +removableTextBox:Campo de texto eliminable defaultColumnHeader:{0} -defaultColumnHeader[default_IndexedAIP_level]: Level -defaultColumnHeader[default_IndexedAIP_hasrepresentations]: +defaultColumnHeader[default_IndexedAIP_level]: Nivel +defaultColumnHeader[default_IndexedAIP_hasrepresentations]: defaultColumnHeader[default_IndexedFile_icon]: #Disposal -disposalPolicyTitle:Disposal policies -disposalRulesTitle:Disposal rules -disposalSchedulesTitle:Disposal schedules -disposalHoldsTitle:Disposal holds -disposalDestroyedRecordsTitle:Destroyed records -disposalConfirmationsTitle:Disposal confirmations -createDisposalConfirmationTitle:Create disposal confirmation -createDisposalConfirmationExtraInformationTitle:Disposal confirmation metadata information +disposalPolicyTitle:Políticas de eliminación +disposalRulesTitle:Reglas de eliminación +disposalSchedulesTitle:Calendarios de eliminación +disposalHoldsTitle:Bloqueos de eliminación +disposalDestroyedRecordsTitle:Registros destruidos +disposalConfirmationsTitle:Confirmaciones de eliminación +createDisposalConfirmationTitle:Crear confirmación de eliminación +createDisposalConfirmationExtraInformationTitle:Información de metadatos de la confirmación de eliminación disposalScheduleTitle:Titulo disposalScheduleIdentifier:Identificador disposalScheduleDescription:Descripción -disposalScheduleMandate:Mandate -disposalScheduleNotes:Scope notes -disposalSchedulePeriod:Period +disposalScheduleMandate:Mandato +disposalScheduleNotes:Notas de alcance +disposalSchedulePeriod:Período disposalScheduleActionCol:Acción disposalScheduleAction:{0} -disposalScheduleAction[DESTROY]:Destroy -disposalScheduleAction[REVIEW]:Review -disposalScheduleAction[RETAIN_PERMANENTLY]:Retain permanently -disposalScheduleRetentionTriggerElementId:Retention trigger element identifier -disposalScheduleRetentionPeriodInterval:Retention period interval +disposalScheduleAction[DESTROY]:Destruir +disposalScheduleAction[REVIEW]:Revisión +disposalScheduleAction[RETAIN_PERMANENTLY]:Conservar permanentemente +disposalScheduleRetentionTriggerElementId:Identificador del elemento de activación de retención +disposalScheduleRetentionPeriodInterval:Intervalo del período de retención disposalScheduleRetentionPeriodIntervalValue:{0} -disposalScheduleRetentionPeriodIntervalValue[NO_RETENTION_PERIOD]:No retention period -disposalScheduleRetentionPeriodIntervalValue[DAYS]:Days -disposalScheduleRetentionPeriodIntervalValue[WEEKS]:Weeks -disposalScheduleRetentionPeriodIntervalValue[MONTHS]:Months -disposalScheduleRetentionPeriodIntervalValue[YEARS]:Years -disposalScheduleRetentionPeriodDuration:Retention period -disposalScheduleStateCol:State +disposalScheduleRetentionPeriodIntervalValue[NO_RETENTION_PERIOD]:Sin período de retención +disposalScheduleRetentionPeriodIntervalValue[DAYS]:Días +disposalScheduleRetentionPeriodIntervalValue[WEEKS]:Semanas +disposalScheduleRetentionPeriodIntervalValue[MONTHS]:Meses +disposalScheduleRetentionPeriodIntervalValue[YEARS]:Años +disposalScheduleRetentionPeriodDuration:Período de retención +disposalScheduleStateCol:Estado disposalScheduleState:{0} disposalScheduleState[ACTIVE]:Activo -disposalScheduleState[INACTIVE]:Inactive -disposalScheduleUsedInRule:Is being used in a disposal rule +disposalScheduleState[INACTIVE]:Inactivo +disposalScheduleUsedInRule:Está siendo usado en una regla de eliminación disposalHoldTitle:Titulo disposalHoldIdentifier:Identificador disposalHoldDescription:Descripción -disposalHoldMandate:Mandate -disposalHoldNotes:Scope notes -disposalHoldStateCol:State +disposalHoldMandate:Mandato +disposalHoldNotes:Notas de alcance +disposalHoldStateCol:Estado disposalHoldState:{0} disposalHoldState[ACTIVE]:Activo -disposalHoldState[LIFTED]:Lifted -newDisposalRuleTitle:Create disposal rule -newDisposalScheduleTitle:Create disposal schedule -newDisposalHoldTitle:Create disposal hold -editDisposalRuleTitle:Edit disposal rule -editDisposalScheduleTitle:Edit disposal schedule -editDisposalHoldTitle:Edit disposal hold -editRules:Prioritize rules -applyRules:Apply rules +disposalHoldState[LIFTED]:Levantado +newDisposalRuleTitle:Crear regla de eliminación +newDisposalScheduleTitle:Crear calendario de eliminación +newDisposalHoldTitle:Crear bloqueo de eliminación +editDisposalRuleTitle:Editar regla de eliminación +editDisposalScheduleTitle:Editar calendario de eliminación +editDisposalHoldTitle:Editar bloqueo de eliminación +editRules:Priorizar reglas +applyRules:Aplicar reglas disposalConfirmationTitle:Titulo disposalConfirmationCreationDate:Fecha de creación disposalConfirmationCreationBy:Creador disposalConfirmationStatus:Estado disposalConfirmationAIPs:# AIP -disposalConfirmationSize:Storage size +disposalConfirmationSize:Tamaño de almacenamiento disposalConfirmationState:{0} -disposalConfirmationState[APPROVED]:Approved -disposalConfirmationState[PENDING]:Pending -disposalConfirmationState[RESTORED]:Restored +disposalConfirmationState[APPROVED]:Aprobado +disposalConfirmationState[PENDING]:Pendiente +disposalConfirmationState[RESTORED]:Restaurado disposalConfirmationState[PERMANENTLY_DELETED]:Eliminado -disposalConfirmationState[EXECUTION_FAILED]:Execution Failed -createDisposalRuleFailure:Could not create disposal rule due to an error: {0}. -createDisposalHoldAlreadyExists:There is already a disposal hold with the title {0}, please choose another title. -createDisposalHoldFailure:Could not create disposal hold due to an error: {0}. -showDisposalRuleTitle:Disposal rule -showDisposalHoldTitle:Disposal hold -showDisposalScheduleTitle:Disposal Schedule -applyDisposalScheduleButton:Destroy +disposalConfirmationState[EXECUTION_FAILED]:Ejecución fallida +createDisposalRuleFailure:No se pudo crear la regla de eliminación debido a un error: {0}. +createDisposalHoldAlreadyExists:Ya existe un bloqueo de eliminación con el título {0}. Por favor, elija otro título. +createDisposalHoldFailure:No se pudo crear el bloqueo de eliminación debido a un error: {0}. +showDisposalRuleTitle:Regla de eliminación +showDisposalHoldTitle:Bloqueo de eliminación +showDisposalScheduleTitle:Calendario de eliminación +applyDisposalScheduleButton:Destruir deleteDisposalConfirmationReport:Eliminar -permanentlyDeleteFromBinButton:Permanently delete -reExecuteDisposalDestroyActionButton:Re-execute destruction -recoverDisposalConfirmationExecutionFailedButton:Recover AIPs -restoreFromBinButton:Restore -newDisposalConfirmationButton:New confirmation -disassociateDisposalScheduleButton:Disassociate schedule -disassociateDisposalHoldButton:Disassociate hold -associateDisposalScheduleButton:Associate disposal schedule -createDisposalScheduleButton:Create schedule -disposalScheduleSelectionDialogTitle:Change disposal schedule -changeDisposalScheduleActionTitle:Change disposal schedule -createDisposalConfirmationActionTitle:Create disposal confirmation -dissociateDisposalScheduleDialogTitle:Confirm schedule disassociation of intellectual entities -dissociateDisposalScheduleDialogMessage:Are you sure you want to disassociate the schedule for the selected {0} items? -createDisposalConfirmationReportDialogTitle:Confirming intellectual entities to be destroyed -createDisposalConfirmationReportDialogMessage:Are you sure you want to create a disposal confirmation for the selected {0} items? -associateDisposalScheduleDialogTitle:Confirm schedule association of intellectual entities -associateDisposalScheduleDialogMessage:Are you sure you want to associate the schedule for the selected {0} items? -disposalRetentionStartDateLabel:Retention start date -disposalRetentionDueDateLabel:Retention due date -disposalRetentionPeriodLabel:Retention period -disposalActionLabel:Disposal action -holdStatusLabel:Hold status -disposalOnHoldStatusLabel:On hold -disposalClearStatusLabel:Not on hold -disposalScheduleListAips:Records with this schedule -disposalHoldListAips:Records with this hold -deleteConfirmationReportDialogTitle:Confirm intellectual entities deletion -deleteConfirmationReportDialogMessage:Are you sure you want to withdraw the intellectual entities from this disposal confirmation? -deleteConfirmationReportSuccessTitle:Deleting disposal confirmation -deleteConfirmationReportSuccessMessage:Execute action to delete disposal confirmation +permanentlyDeleteFromBinButton:Eliminar permanentemente +reExecuteDisposalDestroyActionButton:Reejecutar destrucción +recoverDisposalConfirmationExecutionFailedButton:Recuperar AIPs +restoreFromBinButton:Restaurar +newDisposalConfirmationButton:Nueva confirmación +disassociateDisposalScheduleButton:Desasociar calendario +disassociateDisposalHoldButton:Desasociar bloqueo +associateDisposalScheduleButton:Asociar calendario de eliminación +createDisposalScheduleButton:Crear calendario +disposalScheduleSelectionDialogTitle:Cambiar calendario de eliminación +changeDisposalScheduleActionTitle:Cambiar calendario de eliminación +createDisposalConfirmationActionTitle:Crear confirmación de eliminación +dissociateDisposalScheduleDialogTitle:Confirmar disociación de calendario de entidades intelectuales +dissociateDisposalScheduleDialogMessage:¿Está seguro de que desea disociar el calendario para los {0} elementos seleccionados? +createDisposalConfirmationReportDialogTitle:Confirmando entidades intelectuales a destruir +createDisposalConfirmationReportDialogMessage:¿Está seguro de que desea crear una confirmación de eliminación para los {0} elementos seleccionados? +associateDisposalScheduleDialogTitle:Confirmar asociación de calendario de entidades intelectuales +associateDisposalScheduleDialogMessage:¿Está seguro de que desea asociar el calendario para los {0} elementos seleccionados? +disposalRetentionStartDateLabel:Fecha de inicio de retención +disposalRetentionDueDateLabel:Fecha de vencimiento de retención +disposalRetentionPeriodLabel:Período de retención +disposalActionLabel:Acción de eliminación +holdStatusLabel:Estado de bloqueo +disposalOnHoldStatusLabel:En retención +disposalClearStatusLabel:Sin retención +disposalScheduleListAips:Registros con este calendario +disposalHoldListAips:Registros con este bloqueo +deleteConfirmationReportDialogTitle:Confirmar eliminación de entidades intelectuales +deleteConfirmationReportDialogMessage:¿Está seguro de que desea retirar las entidades intelectuales de esta confirmación de eliminación? +deleteConfirmationReportSuccessTitle:Eliminando confirmación de eliminación +deleteConfirmationReportSuccessMessage:Ejecutar acción para eliminar confirmación de eliminación disposalConfirmationDataPanelTitle:Titulo -disposalConfirmationDataNote:(*) Required fields +disposalConfirmationDataNote:(*) Campos obligatorios disposalRuleTitle:Titulo disposalRuleIdentifier:Identificador disposalRuleDescription:Descripción -disposalRuleScheduleName:Schedule -disposalRuleType:Selection method +disposalRuleScheduleName:Calendario +disposalRuleType:Método de selección disposalRuleOrder:# -disposalRuleCondition:Condition -disposalRuleConditionOperator:is +disposalRuleCondition:Condición +disposalRuleConditionOperator:es disposalRuleTypeValue:{0} -disposalRuleTypeValue[IS_CHILD_OF]:Child of -disposalRuleTypeValue[METADATA_FIELD]:Metadata field -disposalRulePreviewAIPListTitle:Preview records that match the selection method criteria -disposalRulePreviewHelpText:Select a selection method and add the criteria. Click Preview to check which intellectual entities will be affected by this disposal rule. -disposalRulePreviewButtonText:Preview -disposalTitle:Disposal -conditionActualParent:Actual parent -editRulesOrder:Edit rules order -confirmChangeRulesOrder:Are you sure you want to change the order of the disposal rules? -deleteDisposalRuleDialogTitle:Remove disposal rule -deleteDisposalRuleDialogMessage:Are you sure you want to remove the disposal rule {0}? -associateDisposalHoldButton:Associate disposal hold -disposalHoldSelectionDialogTitle:Associate disposal hold -applyDisposalHoldDialogTitle:Confirm disposal hold association to intellectual entity -applyDisposalHoldDialogMessage[one]:Are you sure you want to apply the disposal hold to the selected item? -applyDisposalHoldDialogMessage:Are you sure you want to apply the disposal hold to the selected {0, number} items? -applyDisposalHoldButton:Associate disposal hold -createDisposalHoldButton:Create disposal hold -clearDisposalHoldButton:Clear disposal holds -overrideDisposalHoldButton:Override disposal hold -clearDisposalHoldDialogTitle:Clear disposal holds -clearDisposalHoldDialogMessage[one]:Are you sure you want to disassociate all disposal holds from the selected item? -clearDisposalHoldDialogMessage:Are you sure you want to disassociate all disposal holds from the selected {0, number} items? -liftDisposalHoldDialogTitle:Lift disposal hold -liftDisposalHoldDialogMessage[\=1]:Are you sure you want to lift the disposal hold? -liftDisposalHoldDialogMessage:Are you sure you want to lift the {0, number} selected disposal holds? -disassociateDisposalHoldDialogTitle:Disassociate disposal hold -disassociateDisposalHoldDialogMessage[one]:Are you sure you want to disassociate the disposal hold from the selected item? -disassociateDisposalHoldDialogMessage:Are you sure you want to disassociate the disposal hold from the selected {0, number} items? -disassociateDisposalScheduleDialogTitle:Disassociate disposal schedule -disassociateDisposalScheduleDialogMessage[one]:Are you sure you want to disassociate the disposal schedule from the selected item? -disassociateDisposalScheduleDialogMessage:Are you sure you want to disassociate the disposal schedule from the selected {0, number} items? -disposalHoldAssociatedOn:Associated on -disposalHoldAssociatedBy:Associated by -disposalHoldAssociatedFrom:Associated from +disposalRuleTypeValue[IS_CHILD_OF]:Elemento subordinado de +disposalRuleTypeValue[METADATA_FIELD]:Campo de metadatos +disposalRulePreviewAIPListTitle:Vista previa de registros que coinciden con los criterios del método de selección +disposalRulePreviewHelpText:Seleccione un método de selección y añada los criterios. Haga clic en Vista previa para comprobar qué entidades intelectuales se verán afectadas por esta regla de eliminación. +disposalRulePreviewButtonText:Vista previa +disposalTitle:Eliminación +conditionActualParent:Elemento padre actual +editRulesOrder:Editar orden de reglas +confirmChangeRulesOrder:¿Está seguro de que desea cambiar el orden de las reglas de eliminación? +deleteDisposalRuleDialogTitle:Eliminar regla de eliminación +deleteDisposalRuleDialogMessage:¿Está seguro de que desea eliminar la regla de eliminación {0}? +associateDisposalHoldButton:Asociar bloqueo de eliminación +disposalHoldSelectionDialogTitle:Asociar bloqueo de eliminación +applyDisposalHoldDialogTitle:Confirmar asociación de bloqueo de eliminación a entidad intelectual +applyDisposalHoldDialogMessage[one]:¿Está seguro de que desea aplicar el bloqueo de eliminación al elemento seleccionado? +applyDisposalHoldDialogMessage:¿Está seguro de que desea aplicar el bloqueo de eliminación a los {0, number} elementos seleccionados? +applyDisposalHoldButton:Asociar bloqueo de eliminación +createDisposalHoldButton:Crear bloqueo de eliminación +clearDisposalHoldButton:Eliminar bloqueos de eliminación +overrideDisposalHoldButton:Anular bloqueo de eliminación +clearDisposalHoldDialogTitle:Eliminar bloqueos de eliminación +clearDisposalHoldDialogMessage[one]:¿Está seguro de que desea desasociar todos los bloqueos de eliminación del elemento seleccionado? +clearDisposalHoldDialogMessage:¿Está seguro de que desea desasociar todos los bloqueos de eliminación de los {0, number} elementos seleccionados? +liftDisposalHoldDialogTitle:Levantar bloqueo de eliminación +liftDisposalHoldDialogMessage[\=1]:¿Está seguro de que desea levantar el bloqueo de eliminación? +liftDisposalHoldDialogMessage[one]:¿Está seguro de que desea levantar el bloqueo de eliminación? +liftDisposalHoldDialogMessage:¿Está seguro de que desea levantar los {0, number} bloqueos de eliminación seleccionados? +disassociateDisposalHoldDialogTitle:Desasociar bloqueo de eliminación +disassociateDisposalHoldDialogMessage[one]:¿Está seguro de que desea desasociar el bloqueo de eliminación del elemento seleccionado? +disassociateDisposalHoldDialogMessage:¿Está seguro de que desea desasociar el bloqueo de eliminación de los {0, number} elementos seleccionados? +disassociateDisposalScheduleDialogTitle:Desasociar calendario de eliminación +disassociateDisposalScheduleDialogMessage[one]:¿Está seguro de que desea desasociar el calendario de eliminación del elemento seleccionado? +disassociateDisposalScheduleDialogMessage:¿Está seguro de que desea desasociar el calendario de eliminación de los {0, number} elementos seleccionados? +disposalHoldAssociatedOn:Asociado el +disposalHoldAssociatedBy:Asociado por +disposalHoldAssociatedFrom:Asociado desde disposalHoldAssociatedFromValue[\=1]:{0} AIP +disposalHoldAssociatedFromValue[one]:{0} AIP disposalHoldAssociatedFromValue:{0} AIPs -disposalScheduleAssociationInformationTitle:Retention period -disposalScheduleAssociationTitle:Disposal schedule -disposalConfirmationAssociationInformationTitle:Disposal confirmation -disposalConfirmationAssociationTitle:Disposal confirmation -disposalHoldsAssociationInformationTitle:Disposal holds -transitiveDisposalHoldsAssociationInformationTitle:Transitive disposal holds assigned through other intellectual entity +disposalScheduleAssociationInformationTitle:Período de retención +disposalScheduleAssociationTitle:Calendario de eliminación +disposalConfirmationAssociationInformationTitle:Confirmación de eliminación +disposalConfirmationAssociationTitle:Confirmación de eliminación +disposalHoldsAssociationInformationTitle:Bloqueos de eliminación +transitiveDisposalHoldsAssociationInformationTitle:Bloqueos de eliminación transitivos asignados a través de otra entidad intelectual disposalScheduleActionCode:{0} -disposalScheduleActionCode[RETAIN_PERMANENTLY]:Retain permanently -disposalScheduleActionCode[REVIEW]:Review -disposalScheduleActionCode[DESTROY]:Destroy -disposalPolicyScheduleSummary:Due for {0} {1} -disposalPolicyHoldSummary:On hold -disposalPolicyConfirmationSummary:Assigned to a disposal confirmation +disposalScheduleActionCode[RETAIN_PERMANENTLY]:Conservar permanentemente +disposalScheduleActionCode[REVIEW]:Revisión +disposalScheduleActionCode[DESTROY]:Destruir +disposalPolicyScheduleSummary:Vence para {0} {1} +disposalPolicyHoldSummary:En retención +disposalPolicyConfirmationSummary:Asignado a una confirmación de eliminación disposalPolicyActionSummary:{0} -disposalPolicyActionSummary[REVIEW]:revision -disposalPolicyActionSummary[DESTROY]:destruction -disposalPolicyScheduleMonthSummary[one]:in 1 month -disposalPolicyScheduleMonthSummary:in {0,number} months -disposalPolicyScheduleYearSummary[one]:in 1 year -disposalPolicyScheduleYearSummary:in {0,number} years -disposalPolicyScheduleDaySummary[\=0]:today -disposalPolicyScheduleDaySummary[one]:tomorrow -disposalPolicyScheduleDaySummary:in {0,number} days -disposalPolicySummaryReady:Overdue for {0} +disposalPolicyActionSummary[REVIEW]:revisión +disposalPolicyActionSummary[DESTROY]:destrucción +disposalPolicyScheduleMonthSummary[one]:en 1 mes +disposalPolicyScheduleMonthSummary:en {0,number} meses +disposalPolicyScheduleYearSummary[one]:en 1 año +disposalPolicyScheduleYearSummary:en {0,number} años +disposalPolicyScheduleDaySummary[\=0]:hoy +disposalPolicyScheduleDaySummary[one]:mañana +disposalPolicyScheduleDaySummary:en {0,number} días +disposalPolicySummaryReady:Vencido para {0} permanentlyRetained:Nunca -disposalPolicyRetainPermanently:Permanently retained -disposalPolicyDestroyedAIPSummary:Destroyed on {0} +disposalPolicyRetainPermanently:Conservado permanentemente +disposalPolicyDestroyedAIPSummary:Destruido el {0} retentionPeriod:{0} retentionPeriod[\=1|DAYS]:1 día -retentionPeriod[other|DAYS]:{0,number} days -retentionPeriod[\=1|WEEKS]:1 week -retentionPeriod[other|WEEKS]:{0,number} weeks -retentionPeriod[\=1|MONTHS]:1 month -retentionPeriod[other|MONTHS]:{0,number} months -retentionPeriod[\=1|YEARS]:1 year -retentionPeriod[other|YEARS]:{0,number} years -retentionPeriod[other|NO_RETENTION_PERIOD]:No retention period -permanentlyDeleteConfirmDialogTitle:Permanently delete records from disposal bin -permanentlyDeleteConfirmDialogMessage:Are you sure you want to permanently delete the records from disposal bin? This action can not be undone once executed. -restoreDestroyedRecordsConfirmDialogTitle:Restore destroyed records from disposal bin -restoreDestroyedRecordsConfirmDialogMessage:Are you sure you want to restore the records from disposal bin? -restoreDestroyedRecordsSuccessTitle:Restoring records from disposal bin -restoreDestroyedRecordsSuccessMessage:Execute action to restore records -recoverDestroyedRecordsConfirmDialogTitle:Recover destroyed records from a failed execution -recoverDestroyedRecordsConfirmDialogMessage:Are you sure you want to recover the records from this disposal confirmation? -recoverDestroyedRecordsSuccessTitle:Recovering records from a failed execution -recoverDestroyedRecordsSuccessMessage:Execute action to recover records -permanentlyDeleteRecordsSuccessTitle:Permanently delete records from disposal bin -permanentlyDeleteRecordsSuccessMessage:Execute action to permanently delete records -disposalScheduleAssociationTypeLabel:Association type +retentionPeriod[one|DAYS]:1 día +retentionPeriod[other|DAYS]:{0,number} días +retentionPeriod[\=1|WEEKS]:1 semana +retentionPeriod[one|WEEKS]:1 semana +retentionPeriod[other|WEEKS]:{0,number} semanas +retentionPeriod[\=1|MONTHS]:1 mes +retentionPeriod[one|MONTHS]:1 mes +retentionPeriod[other|MONTHS]:{0,number} meses +retentionPeriod[\=1|YEARS]:1 año +retentionPeriod[one|YEARS]:1 año +retentionPeriod[other|YEARS]:{0,number} años +retentionPeriod[other|NO_RETENTION_PERIOD]:Sin período de retención +permanentlyDeleteConfirmDialogTitle:Eliminar permanentemente registros de la papelera de eliminación +permanentlyDeleteConfirmDialogMessage:¿Está seguro de que desea eliminar permanentemente los registros de la papelera de eliminación? Esta acción no se puede deshacer una vez ejecutada. +restoreDestroyedRecordsConfirmDialogTitle:Restaurar registros destruidos de la papelera de eliminación +restoreDestroyedRecordsConfirmDialogMessage:¿Está seguro de que desea restaurar los registros de la papelera de eliminación? +restoreDestroyedRecordsSuccessTitle:Restaurando registros de la papelera de eliminación +restoreDestroyedRecordsSuccessMessage:Ejecutar acción para restaurar registros +recoverDestroyedRecordsConfirmDialogTitle:Recuperar registros destruidos de una ejecución fallida +recoverDestroyedRecordsConfirmDialogMessage:¿Está seguro de que desea recuperar los registros de esta confirmación de eliminación? +recoverDestroyedRecordsSuccessTitle:Recuperando registros de una ejecución fallida +recoverDestroyedRecordsSuccessMessage:Ejecutar acción para recuperar registros +permanentlyDeleteRecordsSuccessTitle:Eliminar permanentemente registros de la papelera de eliminación +permanentlyDeleteRecordsSuccessMessage:Ejecutar acción para eliminar permanentemente los registros +disposalScheduleAssociationTypeLabel:Tipo de asociación disposalScheduleAssociationType:{0} -disposalScheduleAssociationType[MANUAL]:Manually -disposalScheduleAssociationType[RULES]:Via disposal rules -applyDisposalRulesDialogTitle:Apply disposal rules to repository -applyDisposalRulesDialogMessage:Are you sure you want to apply the disposal rules? This may affect all the intellectual entities and their schedules. -applyDisposalRulesDialogExplanation:This process will go over all intellectual entities in the repository and apply a disposal schedule according the disposal rules defined. On apply rules and override, disposal schedules that were manually associated to an intellectual entity may be associated to a different disposal schedule. On apply rules, disposal schedules manually associated will be skipped during the process. -deleteDisposalRuleSuccessTitle:Disposal rule removed -deleteDisposalRuleSuccessMessage:Disposal rule {0} successfully removed -updateDisposalRulesOrderSuccessTitle:Update disposal rules order -updateDisposalRulesOrderSuccessMessage:Disposal rules order successfully updated -confirmEditRuleMessage:The current changes may affect the disposal rules. Do you want to proceed? -disposalPolicyRetentionPeriodCalculationError:Retention period failed to be calculated -disposalConfirmationShowRecordsToDestroy:Show records to destroy -disposalConfirmationShowRecordsToReview:Show records to review -disposalConfirmationShowRecordsRetentionPeriodCalculationError:Retention period errors -updateDisposalHoldMessage:Lifting the disposal hold -deleteDisposalSchedule: Disposal schedule {0} was successfully removed -searchPrefilterDescendantsOf: Descendants of {0} -searchPreFilterInThisPackage: In the package {0} -searchPrefilterTransitiveHolds: Transitive holds -searchPrefilterCreatedByJob: Created by ingest process {0} -genericSearchWithPrefilterTitle: Search with prefilter -saveSearchTitle: Save search -saveSearchDescription: Please provide a title for this search. This will help you better identify what is the search context. -promptDialogErrorMessageForSavedSearchTitle: The symbol / is not allowed -promptDialogEmptyInputError: Please provide a value +disposalScheduleAssociationType[MANUAL]:Manualmente +disposalScheduleAssociationType[RULES]:Mediante reglas de eliminación +applyDisposalRulesDialogTitle:Aplicar reglas de eliminación al repositorio +applyDisposalRulesDialogMessage:¿Está seguro de que desea aplicar las reglas de eliminación? Esto puede afectar a todas las entidades intelectuales y sus calendarios. +applyDisposalRulesDialogExplanation:Este proceso recorrerá todas las entidades intelectuales del repositorio y aplicará un calendario de eliminación según las reglas definidas. Con aplicar reglas y anular, los calendarios asociados manualmente pueden asignarse a un calendario diferente. Con aplicar reglas, los calendarios asociados manualmente se omitirán durante el proceso. +deleteDisposalRuleSuccessTitle:Regla de eliminación eliminada +deleteDisposalRuleSuccessMessage:Regla de eliminación {0} eliminada correctamente +updateDisposalRulesOrderSuccessTitle:Actualizar orden de reglas de eliminación +updateDisposalRulesOrderSuccessMessage:Orden de reglas de eliminación actualizado correctamente +confirmEditRuleMessage:Los cambios actuales pueden afectar a las reglas de eliminación. ¿Desea continuar? +disposalPolicyRetentionPeriodCalculationError:Error al calcular el período de retención +disposalConfirmationShowRecordsToDestroy:Mostrar registros a destruir +disposalConfirmationShowRecordsToReview:Mostrar registros a revisar +disposalConfirmationShowRecordsRetentionPeriodCalculationError:Errores en el período de retención +updateDisposalHoldMessage:Levantando el bloqueo de eliminación +deleteDisposalSchedule: El calendario de eliminación {0} se ha eliminado correctamente +searchPrefilterDescendantsOf: Descendientes de {0} +searchPreFilterInThisPackage: En el paquete {0} +searchPrefilterTransitiveHolds: Bloqueos transitivos +searchPrefilterCreatedByJob: Creado por el proceso de ingesta {0} +genericSearchWithPrefilterTitle: Búsqueda con prefiltro +saveSearchTitle: Guardar búsqueda +saveSearchDescription: Por favor, proporcione un título para esta búsqueda. Esto le ayudará a identificar mejor el contexto de la búsqueda. +promptDialogErrorMessageForSavedSearchTitle: El símbolo / no está permitido +promptDialogEmptyInputError: Por favor, proporcione un valor #Distributed Instances -manageDistributedInstanceTitle:Manage Distributed Instances -createDistributedInstanceTitle:Create Distributed Instances -showDistributedInstanceTitle:Distributed Instance details -editDistributedInstanceTitle:Edit Distributed Instances -removeDistributedInstanceTitle:Remove Distributed Instance -removeDistributedInstanceLabel: Are you sure you want to remove the distributed instance? -distributedInstanceLabel:Instance -distributedInstancesLabel:Instances +manageDistributedInstanceTitle:Gestionar instancias distribuidas +createDistributedInstanceTitle:Crear instancias distribuidas +showDistributedInstanceTitle:Detalles de instancia distribuida +editDistributedInstanceTitle:Editar instancias distribuidas +removeDistributedInstanceTitle:Eliminar instancia distribuida +removeDistributedInstanceLabel: ¿Está seguro de que desea eliminar la instancia distribuida? +distributedInstanceLabel:Instancia +distributedInstancesLabel:Instancias distributedInstanceNameLabel:Nombre distributedInstanceUUIDLabel:UUID distributedInstanceDescriptionLabel:Descripción distributedInstanceIDLabel:Identificador -distributedInstanceLastSyncDateLabel:Last Synchronization date -distributedInstanceLastSyncLabel: Last Syncronization +distributedInstanceLastSyncDateLabel:Fecha de última sincronización +distributedInstanceLastSyncLabel: Última sincronización distributedInstanceStatusLabel:Estado distributedInstanceStatusValue:{0} distributedInstanceStatusValue[CREATED]:Creado distributedInstanceStatusValue[ACTIVE]:Activo -distributedInstanceStatusValue[INACTIVE]:Inactive +distributedInstanceStatusValue[INACTIVE]:Inactivo distributedInstanceStatusButtonActivateLabel:ACTIVAR distributedInstanceStatusButtonDeactivateLabel:DESACTIVAR distributedInstanceStatistics:Estadísticas -distributedInstanceSyncErrorsLabel:Issues -distributedInstanceRemovedEntitiesLabel: Removed -distributedInstanceUpdatedEntitiesLabel: Added/Updated +distributedInstanceSyncErrorsLabel:Problemas +distributedInstanceRemovedEntitiesLabel: Eliminados +distributedInstanceUpdatedEntitiesLabel: Añadidos/Actualizados #Local Instances Configuration -localInstanceIsSubscribedLabel:Subscribed -localInstanceUnsubscribeButton:Unsubscribe -localInstanceLastSyncDateLabel:Last synchronization -subscribeLocalInstanceConfigurationButton:Subscribe -runPluginLocalInstanceConfigurationButton:Run Plugin -synchronizeLocalInstanceConfigurationButton:Synchronize -createLocalInstanceConfigurationTitle:Create Local Instance Configuration -showLocalInstanceConfigurationTitle:Local Instance Configuration -editLocalInstanceConfigurationTitle:Edit Local Instance Configuration -testLocalInstanceConfigurationButton:Test Configuration -testLocalInstanceConfigurationDialogTitle:Test configuration -testLocalInstanceConfigurationDialogMessage:Connection successful.
{0}
-testLocalInstanceConfigurationDialogMessageError:Connection failed.
{0}
+localInstanceIsSubscribedLabel:Suscrito +localInstanceUnsubscribeButton:Cancelar suscripción +localInstanceLastSyncDateLabel:Última sincronización +subscribeLocalInstanceConfigurationButton:Suscribirse +runPluginLocalInstanceConfigurationButton:Ejecutar complemento +synchronizeLocalInstanceConfigurationButton:Sincronizar +createLocalInstanceConfigurationTitle:Crear configuración de instancia local +showLocalInstanceConfigurationTitle:Configuración de instancia local +editLocalInstanceConfigurationTitle:Editar configuración de instancia local +testLocalInstanceConfigurationButton:Probar configuración +testLocalInstanceConfigurationDialogTitle:Probar configuración +testLocalInstanceConfigurationDialogMessage:Conexión establecida correctamente.
{0}
+testLocalInstanceConfigurationDialogMessageError:Error de conexión.
{0}
localInstanceConfigurationIDLabel:Identificador -localInstanceConfigurationSecretLabel:Access Token -localInstanceConfigurationCentralInstanceURLLabel:Central Instance URL -localInstanceConfigurationCentralInstanceURLPlaceholder:e.g. http://localhost:8080 -localInstanceConfigurationInvalidURL:The URL must follow the following format: ://
: (e.g. http://localhost:8080) -synchronizationStatusLabel:Synchronization State -applyInstanceIdToRepository:Apply instance identifier to all repository -applyInstanceIdToRepositoryMessage:You will apply the instance identifier to the all repository. From this moment the system will start to create the entities with the instance identifier as well. -removeLocalConfiguration:Remove Local Instance configuration. -removeLocalConfigurationMessage:You will remove the local configuration. From this moment the you can not be able to synchronize with the Central Repository. -successfullyUnsubscribedTitle:Unsubscribed -successfullyUnsubscribedMessage:Successfully unsubscribed -removeInstanceIdFromRepository:Remove instance identifier from all repository. -removeInstanceIdFromRepositoryMessage:You will remove the instance identifier from all repository. From this moment the system will start to create the entities without the instance identifier. This action may take some time, are you sure you want to remove all identifiers ? +localInstanceConfigurationSecretLabel:Token de acceso +localInstanceConfigurationCentralInstanceURLLabel:URL de instancia central +localInstanceConfigurationCentralInstanceURLPlaceholder:p. ej. http://localhost:8080 +localInstanceConfigurationInvalidURL:La URL debe seguir el siguiente formato: ://
: (p. ej. http://localhost:8080) +synchronizationStatusLabel:Estado de sincronización +applyInstanceIdToRepository:Aplicar identificador de instancia a todo el repositorio +applyInstanceIdToRepositoryMessage:Aplicará el identificador de instancia a todo el repositorio. A partir de este momento, el sistema comenzará a crear las entidades también con el identificador de instancia. +removeLocalConfiguration:Eliminar configuración de instancia local. +removeLocalConfigurationMessage:Eliminará la configuración local. A partir de este momento no podrá sincronizarse con el repositorio central. +successfullyUnsubscribedTitle:Suscripción cancelada +successfullyUnsubscribedMessage:Suscripción cancelada correctamente +removeInstanceIdFromRepository:Eliminar identificador de instancia de todo el repositorio. +removeInstanceIdFromRepositoryMessage:Eliminará el identificador de instancia de todo el repositorio. A partir de este momento el sistema creará las entidades sin el identificador de instancia. Esta acción puede tardar algún tiempo. ¿Está seguro de que desea eliminar todos los identificadores? synchronizingStatus:{0} synchronizingStatus[ACTIVE]:Activo -synchronizingStatus[INACTIVE]:Inactive -synchronizingStatus[APPLYINGIDENTIFIER]:Applying identifier -synchronizingStatus[SYNCHRONIZING]:Synchronizing +synchronizingStatus[INACTIVE]:Inactivo +synchronizingStatus[APPLYINGIDENTIFIER]:Aplicando identificador +synchronizingStatus[SYNCHRONIZING]:Sincronizando #Access Token -addAccessKeyButton:New access Token -manageAccessKeyTitle:Manage Access Tokens -createAccessKeyTitle:Create Access Token -showAccessKeyTitle:Access Token -editAccessKeyTitle:Edit Access Token -accessKeyLabel:Access Token +addAccessKeyButton:Nuevo token de acceso +manageAccessKeyTitle:Gestionar tokens de acceso +createAccessKeyTitle:Crear token de acceso +showAccessKeyTitle:Token de acceso +editAccessKeyTitle:Editar token de acceso +accessKeyLabel:Token de acceso accessKeyNameLabel:Nombre accessKeyIDLabel:ID -accessKeyLastUsageDateLabel:Last Usage -accessKeyExpirationDateLabel:Expiration date +accessKeyLastUsageDateLabel:Último uso +accessKeyExpirationDateLabel:Fecha de expiración accessKeyStatusLabel:Estado accessKeyStatusValue:{0} accessKeyStatusValue[CREATED]:Creado accessKeyStatusValue[ACTIVE]:Activo -accessKeyStatusValue[INACTIVE]:Inactive -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 -accessKeyNeverUsedLabel:Never used +accessKeyStatusValue[INACTIVE]:Inactivo +accessKeyWarningLabel:Para mantener su token de acceso seguro, lo ocultaremos permanentemente al cerrar esta ventana. Por favor, guárdelo en un lugar seguro. Si pierde su token de acceso, puede regenerar uno nuevo en cualquier momento. +accessKeyInfo:Este token y el identificador de instancia permiten el registro de la instancia local. En la instancia local, vaya a Administración -> Instancia local y rellene los campos con los valores correspondientes. +accessKeyEditButton:Editar token de acceso +accessKeyUpdateButton:Actualizar token de acceso +accessKeyDeleteButton:Eliminar token de acceso +accessKeyRegenerateButton:Regenerar token de acceso +accessKeyRevokeButton:Revocar token de acceso +accessKeyNeverUsedLabel:Nunca utilizado accessKeyNotFoundLabel:No encontrado -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:El token de acceso se ha regenerado correctamente +accessKeySuccessfullyRevoked:El token de acceso se ha revocado correctamente +accessKeyDeleteConfirmationMessage:¿Está seguro de que desea eliminar permanentemente este token de acceso? Esta acción no se puede deshacer una vez ejecutada. +accessKeyRevokeConfirmationMessage:¿Está seguro de que desea revocar este token de acceso? +accessKeyRegenerateConfirmationMessage:¿Está seguro de que desea regenerar este token de acceso? #Market marketPluginsActionsTabLabel:Acciones -marketVersionLabel:Version {0} available in the store -marketTabLabel:Marketplace -marketStoreInstallLabel:Install +marketVersionLabel:Versión {0} disponible en la tienda +marketTabLabel:Mercado de aplicaciones +marketStoreInstallLabel:Instalar # Descriptive metatada lock to edit -editDescMetadataLockedTitle: Unable to edit descriptive metadata -editDescMetadataLockedText: Intellectual entity is currently being edited by another user -descriptiveHistoryRemoveConfirmDialogTitle: Confirm remove descriptive metadata history -descriptiveHistoryRemoveConfirmDialogMessage: Are you sure you want to remove the selected descriptive metadata history? -descriptiveHistoryRevertConfirmDialogTitle: Confirm revert descriptive metadata -descriptiveHistoryRevertConfirmDialogMessage: Are you sure you want to revert the descriptive metadata to the selected history? +editDescMetadataLockedTitle: No se pueden editar los metadatos descriptivos +editDescMetadataLockedText: Otra persona está editando actualmente esta entidad intelectual +descriptiveHistoryRemoveConfirmDialogTitle: Confirmar eliminación del historial de metadatos descriptivos +descriptiveHistoryRemoveConfirmDialogMessage: ¿Está seguro de que desea eliminar el historial de metadatos descriptivos seleccionado? +descriptiveHistoryRevertConfirmDialogTitle: Confirmar reversión de metadatos descriptivos +descriptiveHistoryRevertConfirmDialogMessage: ¿Está seguro de que desea revertir los metadatos descriptivos al historial seleccionado? # Conversion plugin -representationTypeTitle: Representation type -representationTypeDescription: Assign a type when creating a new representation -disseminationTitle: Dissemination title -disseminationTitleDescription: This will be the respective dissemination title. -disseminationDescriptionTitle: Dissemination description -disseminationDescriptionDescription: This will be the respective dissemination description. -disseminationTitleDefaultValue: Dissemination title -disseminationDescriptionDefaultValue: Dissemination description -changeRepresentationStatusToPreservationTitle: Change the representation status to Preservation? +representationTypeTitle: Tipo de representación +representationTypeDescription: Asigne un tipo al crear una nueva representación +disseminationTitle: Título de diseminación +disseminationTitleDescription: Este será el título de la diseminación correspondiente. +disseminationDescriptionTitle: Descripción de la diseminación +disseminationDescriptionDescription: Esta será la descripción de la diseminación correspondiente. +disseminationTitleDefaultValue: Título de diseminación +disseminationDescriptionDefaultValue: Descripción de diseminación +changeRepresentationStatusToPreservationTitle: ¿Cambiar el estado de la representación a Preservación? changeRepresentationStatusToPreservationDescription: -conversionProfileTitle: Conversion Profile -conversionProfileDescription: Conversion profile options +conversionProfileTitle: Perfil de conversión +conversionProfileDescription: Opciones del perfil de conversión diff --git a/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_hr.properties b/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_hr.properties index 42a764e1ad..670bac7a9c 100644 --- a/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_hr.properties +++ b/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_hr.properties @@ -200,6 +200,8 @@ oneOfAObject[org.roda.core.data.v2.ip.disposal.DisposalHold]:izlučivanje postav oneOfAObject[org.roda.core.data.v2.ip.disposal.DisposalConfirmation]:potvrda izlučivanja oneOfAObject[org.roda.core.data.v2.ip.disposal.DisposalRule]:pravilo izlučivanja selected[\=1]:{0,number} {1} odabrano +selected[one]:{0,number} {1} odabrano +selected[few]:{0,number} {1} odabrano selected:{0,number} {1} odabrano inspectTransferredResource:Pregledajte preneseni resurs identifierNotFound:Identifikator (nije pronađen) @@ -713,12 +715,18 @@ representationInformationEditAssociations:Uredite udruživanja representationInformationIntellectualEntities:{0, number} intelektualnih entiteta je povezano s ovim pojavnim oblikom representationInformationIntellectualEntities[\=0]:Nema intelektualnih entiteta povezanih s ovim pojavnim oblikom representationInformationIntellectualEntities[\=1]:Jedan intelektualni entitet je povezan s ovim pojavnim oblikom +representationInformationIntellectualEntities[one]:{0, number} intelektualni entitet je povezan s ovim pojavnim oblikom +representationInformationIntellectualEntities[few]:{0, number} intelektualna entiteta su povezana s ovim pojavnim oblikom representationInformationRepresentations:{0, number} prikaza je povezano s ovim pojavnim oblikom representationInformationRepresentations[\=0]:Nema prikaza povezanih s ovim pojavnim oblikom representationInformationRepresentations[\=1]:Jedan prikaz je povezan s ovim pojavnim oblikom +representationInformationRepresentations[one]:{0, number} prikaz je povezan s ovim pojavnim oblikom +representationInformationRepresentations[few]:{0, number} prikaza su povezana s ovim pojavnim oblikom representationInformationFiles:{0, number} datoteka je povezano s ovim pojavnim oblikom representationInformationFiles[\=0]:Nema datoteka povezanih s ovim pojavnim oblikom representationInformationFiles[\=1]:Jedna datoteka je povezana s ovim pojavnim oblikom +representationInformationFiles[one]:{0, number} datoteka je povezana s ovim pojavnim oblikom +representationInformationFiles[few]:{0, number} datoteke su povezane s ovim pojavnim oblikom # Descriptive Metadata metadataType:Vrsta metadataFilename:Naziv datoteke @@ -1327,6 +1335,8 @@ associateDisposalHoldButton:Pridruži izlučivanje postavljeno na čekanje disposalHoldSelectionDialogTitle:Pridruži izlučivanje postavljeno na čekanje applyDisposalHoldDialogTitle:Potvrdite povezanost izlučivanja koje je postavljeno na čekanje s intelektualnim entitetom applyDisposalHoldDialogMessage[\=1]:Jeste li sigurni da želite primijeniti izlučivanje postavljeno na čekanje na odabranu stavku? +applyDisposalHoldDialogMessage[one]:Jeste li sigurni da želite primijeniti izlučivanje postavljeno na čekanje na odabranu stavku? +applyDisposalHoldDialogMessage[few]:Jeste li sigurni da želite primijeniti izlučivanje postavljeno na čekanje na odabranih {0, number} predmeta? applyDisposalHoldDialogMessage:Jeste li sigurni da želite primijeniti izlučivanje postavljeno na čekanje na odabranih {0, number} predmeta? applyDisposalHoldButton:Pridruži izlučivanje postavljeno na čekanje createDisposalHoldButton:Kreiraj izlučivanje koje je postavljeno na čekanje @@ -1334,17 +1344,30 @@ clearDisposalHoldButton:Očisti izlučivanja postavljena na čekanje overrideDisposalHoldButton:Nadjačaj izlučivanje postavljeno na čekanje clearDisposalHoldDialogTitle:Očisti izlučivanja postavljena na čekanje clearDisposalHoldDialogMessage[\=1]:Jeste li sigurni da želite razdvojiti sva izlučivanja postavljena na čekanje od odabrane stavke? +clearDisposalHoldDialogMessage[one]:Jeste li sigurni da želite razdvojiti sva izlučivanja postavljena na čekanje od odabrane stavke? +clearDisposalHoldDialogMessage[few]:Jeste li sigurni da želite razdvojiti sva izlučivanja postavljena na čekanje od odabranih {0, number} stavaka? clearDisposalHoldDialogMessage:Jeste li sigurni da želite razdvojiti sva izlučivanja postavljena na čekanje od odabranih {0, number} stavaka? +liftDisposalHoldDialogTitle:Ukini izlučivanje postavljeno na čekanje +liftDisposalHoldDialogMessage[\=1]:Jeste li sigurni da želite ukinuti izlučivanje postavljeno na čekanje? +liftDisposalHoldDialogMessage[one]:Jeste li sigurni da želite ukinuti izlučivanje postavljeno na čekanje? +liftDisposalHoldDialogMessage[few]:Jeste li sigurni da želite ukinuti {0, number} odabranih izlučivanja postavljenih na čekanje? +liftDisposalHoldDialogMessage:Jeste li sigurni da želite ukinuti {0, number} odabranih izlučivanja postavljenih na čekanje? disassociateDisposalHoldDialogTitle:Razdvoji izlučivanje postavljeno na čekanje disassociateDisposalHoldDialogMessage[\=1]:Jeste li sigurni da želite razdvojiti izlučivanje koje je postavljeno na čekanje od odabrane stavke? +disassociateDisposalHoldDialogMessage[one]:Jeste li sigurni da želite razdvojiti izlučivanje koje je postavljeno na čekanje od odabrane stavke? +disassociateDisposalHoldDialogMessage[few]:Jeste li sigurni da želite razdvojiti izlučivanje koje je postavljeno na čekanje od odabranih {0, number} stavaka? disassociateDisposalHoldDialogMessage:Jeste li sigurni da želite razdvojiti izlučivanje koje je postavljeno na čekanje od odabranih {0, number} stavaka? disassociateDisposalScheduleDialogTitle:Razdvoji raspored izlučivanja disassociateDisposalScheduleDialogMessage[\=1]:Jeste li sigurni da želite razdvojiti raspored izlučivanja od odabrane stavke? +disassociateDisposalScheduleDialogMessage[one]:Jeste li sigurni da želite razdvojiti raspored izlučivanja od odabrane stavke? +disassociateDisposalScheduleDialogMessage[few]:Jeste li sigurni da želite razdvojiti raspored izlučivanja od odabranih {0, number} stavaka? disassociateDisposalScheduleDialogMessage:Jeste li sigurni da želite razdvojiti raspored izlučivanja od odabranih {0, number} stavaka? disposalHoldAssociatedOn:Pridruženo disposalHoldAssociatedBy:Pridružio disposalHoldAssociatedFrom:Pridružen od disposalHoldAssociatedFromValue[\=1]:{0} AIP +disposalHoldAssociatedFromValue[one]:{0} AIP +disposalHoldAssociatedFromValue[few]:{0} AIP-a disposalHoldAssociatedFromValue:{0} AIP-ova disposalScheduleAssociationInformationTitle:Razdoblje čuvanja disposalScheduleAssociationTitle:Raspored izlučivanja @@ -1363,11 +1386,17 @@ disposalPolicyActionSummary:{0} disposalPolicyActionSummary[REVIEW]:revizija disposalPolicyActionSummary[DESTROY]:uništavanje disposalPolicyScheduleMonthSummary[\=1]:za 1 mjesec +disposalPolicyScheduleMonthSummary[one]:za {0,number} mjesec +disposalPolicyScheduleMonthSummary[few]:za {0,number} mjeseca disposalPolicyScheduleMonthSummary:za {0,number} mjeseci disposalPolicyScheduleYearSummary[\=1]:za 1 godinu +disposalPolicyScheduleYearSummary[one]:za {0,number} godinu +disposalPolicyScheduleYearSummary[few]:za {0,number} godine disposalPolicyScheduleYearSummary:za {0,number} godina disposalPolicyScheduleDaySummary[\=0]:danas disposalPolicyScheduleDaySummary[\=1]:sutra +disposalPolicyScheduleDaySummary[one]:za {0, number} dan +disposalPolicyScheduleDaySummary[few]:za {0, number} dana disposalPolicyScheduleDaySummary:za {0, number} dana disposalPolicySummaryReady:Prekoračen rok za {0} permanentlyRetained:Nikada @@ -1375,12 +1404,20 @@ disposalPolicyRetainPermanently:Trajno zadržana disposalPolicyDestroyedAIPSummary:Uništetno {0} retentionPeriod:{0} retentionPeriod[\=1|DAYS]:1 dan +retentionPeriod[one|DAYS]:{0,number} dan +retentionPeriod[few|DAYS]:{0,number} dana retentionPeriod[other|DAYS]:{0,number} dana retentionPeriod[\=1|WEEKS]:1 tjedan +retentionPeriod[one|WEEKS]:{0,number} tjedan +retentionPeriod[few|WEEKS]:{0,number} tjedna retentionPeriod[other|WEEKS]:{0,number} tjedana retentionPeriod[\=1|MONTHS]:1 mjesec +retentionPeriod[one|MONTHS]:{0,number} mjesec +retentionPeriod[few|MONTHS]:{0,number} mjeseca retentionPeriod[other|MONTHS]:{0,number} mjeseci retentionPeriod[\=1|YEARS]:1 godina +retentionPeriod[one|YEARS]:{0,number} godina +retentionPeriod[few|YEARS]:{0,number} godine retentionPeriod[other|YEARS]:{0,number} godina retentionPeriod[other|NO_RETENTION_PERIOD]:Nema razdoblja čuvanja permanentlyDeleteConfirmDialogTitle:Trajno izbrišite zapise iz pretinca obrisanih informacijskih paketa diff --git a/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_hu.properties b/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_hu.properties index 2d1ab0af82..e1ac146b87 100644 --- a/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_hu.properties +++ b/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_hu.properties @@ -1496,20 +1496,20 @@ deleteDisposalRuleDialogMessage:Are you sure you want to remove the disposal rul associateDisposalHoldButton:Associate disposal hold disposalHoldSelectionDialogTitle:Associate disposal hold applyDisposalHoldDialogTitle:Confirm disposal hold association to intellectual entity -applyDisposalHoldDialogMessage[one]:Are you sure you want to apply the disposal hold to the selected item? +applyDisposalHoldDialogMessage[\=1]:Are you sure you want to apply the disposal hold to the selected item? applyDisposalHoldDialogMessage:Are you sure you want to apply the disposal hold to the selected {0, number} items? applyDisposalHoldButton:Associate disposal hold createDisposalHoldButton:Create disposal hold clearDisposalHoldButton:Clear disposal holds overrideDisposalHoldButton:Override disposal hold clearDisposalHoldDialogTitle:Clear disposal holds -clearDisposalHoldDialogMessage[one]:Are you sure you want to disassociate all disposal holds from the selected item? +clearDisposalHoldDialogMessage[\=1]:Are you sure you want to disassociate all disposal holds from the selected item? clearDisposalHoldDialogMessage:Are you sure you want to disassociate all disposal holds from the selected {0, number} items? disassociateDisposalHoldDialogTitle:Disassociate disposal hold -disassociateDisposalHoldDialogMessage[one]:Are you sure you want to disassociate the disposal hold from the selected item? +disassociateDisposalHoldDialogMessage[\=1]:Are you sure you want to disassociate the disposal hold from the selected item? disassociateDisposalHoldDialogMessage:Are you sure you want to disassociate the disposal hold from the selected {0, number} items? disassociateDisposalScheduleDialogTitle:Disassociate disposal schedule -disassociateDisposalScheduleDialogMessage[one]:Are you sure you want to disassociate the disposal schedule from the selected item? +disassociateDisposalScheduleDialogMessage[\=1]:Are you sure you want to disassociate the disposal schedule from the selected item? disassociateDisposalScheduleDialogMessage:Are you sure you want to disassociate the disposal schedule from the selected {0, number} items? disposalHoldAssociatedOn:Társítva disposalHoldAssociatedBy:Associated by @@ -1532,12 +1532,12 @@ disposalPolicyConfirmationSummary:Assigned to a disposal confirmation disposalPolicyActionSummary:{0} disposalPolicyActionSummary[REVIEW]:felülvizsgálat disposalPolicyActionSummary[DESTROY]:megsemmisítés -disposalPolicyScheduleMonthSummary[one]:1 hónap alatt +disposalPolicyScheduleMonthSummary[\=1]:1 hónap alatt disposalPolicyScheduleMonthSummary:{0,number} hónap alatt -disposalPolicyScheduleYearSummary[one]:1 év alatt +disposalPolicyScheduleYearSummary[\=1]:1 év alatt disposalPolicyScheduleYearSummary:{0,number} év alatt disposalPolicyScheduleDaySummary[\=0]:ma -disposalPolicyScheduleDaySummary[one]:holnap +disposalPolicyScheduleDaySummary[\=1]:holnap disposalPolicyScheduleDaySummary:{0,number} nap alatt disposalPolicySummaryReady:Overdue for {0} permanentlyRetained:Soha 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..500d9f5209 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 @@ -266,6 +268,7 @@ oneOfAObject[org.roda.core.data.v2.disposal.hold.DisposalHold]:suspensão de eli oneOfAObject[org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation]:autos de eliminação oneOfAObject[org.roda.core.data.v2.disposal.rule.DisposalRule]:condição de eliminação selected[\=1]:{0,number} {1} selecionados +selected[one]:{0,number} {1} selecionados selected:{0,number} {1} selecionados inspectTransferredResource:Inspecionar recurso transferido identifierNotFound:Identificador (não encontrado) @@ -348,6 +351,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 +526,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 +739,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 @@ -861,12 +872,15 @@ representationInformationEditAssociations:Editar associações representationInformationIntellectualEntities:Existem {0,number} entidades intelectuais relacionadas com esta informação de representação representationInformationIntellectualEntities[\=0]:Não existem entidades intelectuais relacionadas com esta informação de representação representationInformationIntellectualEntities[\=1]:Existe uma entidade intelectual relacionada com esta informação de representação +representationInformationIntellectualEntities[one]:Existe uma entidade intelectual relacionada com esta informação de representação representationInformationRepresentations:Existem {0,number} representações relacionadas com esta informação de representação representationInformationRepresentations[\=0]:Não existem representações relacionadas com esta informação de representação representationInformationRepresentations[\=1]:Existe uma representação relacionada com esta informação de representação +representationInformationRepresentations[one]:Existe uma representação relacionada com esta informação de representação representationInformationFiles:Existem {0,number} ficheiros relacionados com esta informação de representação representationInformationFiles[\=0]:Não existem ficheiros relacionados com esta informação de representação representationInformationFiles[\=1]:Existe um ficheiro relacionado com esta informação de representação +representationInformationFiles[one]:Existe um ficheiro relacionado com esta informação de representação # Descriptive Metadata metadataType:Tipo @@ -1106,7 +1120,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 +1457,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 @@ -1553,6 +1570,7 @@ clearDisposalHoldDialogMessage[one]:Tem a certeza que pretende desassociar todas clearDisposalHoldDialogMessage:Tem a certeza que pretende desassociar todas as suspensões de eliminação dos {0, number} items selecionados? liftDisposalHoldDialogTitle:Levantar suspensão de eliminação liftDisposalHoldDialogMessage[\=1]:Tem a certeza que quer levantar a suspensão de eliminação? +liftDisposalHoldDialogMessage[one]:Tem a certeza que quer levantar a suspensão de eliminação? liftDisposalHoldDialogMessage:Tem a certeza que quer levantar as {0, number} suspensões de eliminação selecionadas? disassociateDisposalHoldDialogTitle:Desassociar suspensão de eliminação disassociateDisposalHoldDialogMessage[one]:Tem a certeza que prentende desassociar a suspensão de eliminação do item selecionado? @@ -1564,6 +1582,7 @@ disposalHoldAssociatedOn:Associado em disposalHoldAssociatedBy:Associado por disposalHoldAssociatedFrom:Associado desde disposalHoldAssociatedFromValue[\=1]:{0} AIP +disposalHoldAssociatedFromValue[one]:{0} AIP disposalHoldAssociatedFromValue:{0} AIPs disposalScheduleAssociationInformationTitle:Prazo de conservação disposalScheduleAssociationTitle:Tabela de seleção @@ -1595,12 +1614,16 @@ disposalPolicyRetainPermanently:Conservado permanentemente disposalPolicyDestroyedAIPSummary:Eliminado em {0} retentionPeriod:{0} retentionPeriod[\=1|DAYS]:1 dia +retentionPeriod[one|DAYS]:1 dia retentionPeriod[other|DAYS]:{0,number} dias retentionPeriod[\=1|WEEKS]:1 semana +retentionPeriod[one|WEEKS]:1 semana retentionPeriod[other|WEEKS]:{0,number} semanas retentionPeriod[\=1|MONTHS]:1 mês +retentionPeriod[one|MONTHS]:1 mês retentionPeriod[other|MONTHS]:{0,number} meses retentionPeriod[\=1|YEARS]:1 ano +retentionPeriod[one|YEARS]:1 ano retentionPeriod[other|YEARS]:{0,number} anos retentionPeriod[other|NO_RETENTION_PERIOD]:Sem prazo de conservação permanentlyDeleteConfirmDialogTitle:Eliminar permanentemente entidades da caixa de eliminação @@ -1784,6 +1807,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 +1825,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/i18n/client/ClientMessages_sv_SE.properties b/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_sv_SE.properties index 4c7a7a23f6..e1bbeb24e8 100644 --- a/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_sv_SE.properties +++ b/roda-ui/roda-wui/src/main/resources/config/i18n/client/ClientMessages_sv_SE.properties @@ -261,6 +261,7 @@ oneOfAObject[org.roda.core.data.v2.ip.disposal.DisposalHold]:gallringsstopp oneOfAObject[org.roda.core.data.v2.ip.disposal.DisposalConfirmation]:gallringsbekräftelse oneOfAObject[org.roda.core.data.v2.ip.disposal.DisposalRule]:gallringsregler selected[\=1]:{0,number} {1} vald +selected[one]:{0,number} {1} vald selected:{0,number} {1} vald inspectTransferredResource:Inspektera överförd resurs identifierNotFound:Identifierare (hittades inte) @@ -833,12 +834,15 @@ representationInformationEditAssociations:Redigera association representationInformationIntellectualEntities:Det finns {0,number} logiska enheter associerade med denna representationsinformation representationInformationIntellectualEntities[\=0]:Det finns inga logiska enheter associerade med denna representationsinformation representationInformationIntellectualEntities[\=1]:Det finns en logisk enhet associerad med denna representationsinformation +representationInformationIntellectualEntities[one]:Det finns en logisk enhet associerad med denna representationsinformation representationInformationRepresentations:Det finns {0,number} representationer associerade till denna representationsinformation representationInformationRepresentations[\=0]:Det finns inga representationer kopplade till denna representationsinformation representationInformationRepresentations[\=1]:Det finns en representation associerad med denna representationsinformation +representationInformationRepresentations[one]:Det finns en representation associerad med denna representationsinformation representationInformationFiles:Det finns {0, number} filer associerade med denna representationsinformation representationInformationFiles[\=0]:Det finns inga filer associerade med denna representationsinformation representationInformationFiles[\=1]:Det finns en fil associerad med denna representationsinformation +representationInformationFiles[one]:Det finns en fil associerad med denna representationsinformation # Descriptive Metadata metadataType:Typ @@ -1505,6 +1509,10 @@ overrideDisposalHoldButton:Åsidosätt gallringsstopp clearDisposalHoldDialogTitle:Rensa bort gallringsstopp clearDisposalHoldDialogMessage[one]:Är du säker på att du vill koppla bort alla gallringsstopp från det valda objektet? clearDisposalHoldDialogMessage:Är du säker på att du vill koppla bort alla gallringsstopp från de valda {0, number} objekten? +liftDisposalHoldDialogTitle:Häv gallringsstopp +liftDisposalHoldDialogMessage[\=1]:Är du säker på att du vill häva gallringsstoppet? +liftDisposalHoldDialogMessage[one]:Är du säker på att du vill häva gallringsstoppet? +liftDisposalHoldDialogMessage:Är du säker på att du vill häva de {0, number} valda gallringsstoppen? disassociateDisposalHoldDialogTitle:Koppla loss gallringsstopp disassociateDisposalHoldDialogMessage[one]:Är du säker på att du vill koppla bort gallringsstoppet från det valda objektet? disassociateDisposalHoldDialogMessage:Är du säker på att du vill koppla bort gallringsstoppet från de valda {0, number} objekten? @@ -1515,6 +1523,7 @@ disposalHoldAssociatedOn:Associerad med disposalHoldAssociatedBy:Associerad av disposalHoldAssociatedFrom:Associerad från disposalHoldAssociatedFromValue[\=1]:{0} AIP +disposalHoldAssociatedFromValue[one]:{0} AIP disposalHoldAssociatedFromValue:{0} AIP:er disposalScheduleAssociationInformationTitle:Gallringsfrist disposalScheduleAssociationTitle:Gallringsschema @@ -1545,12 +1554,16 @@ disposalPolicyRetainPermanently:Behålls permanent disposalPolicyDestroyedAIPSummary:Gallrades {0} retentionPeriod:{0} retentionPeriod[\=1|DAYS]:1 dag +retentionPeriod[one|DAYS]:1 dag retentionPeriod[other|DAYS]:{0,number} dagar retentionPeriod[\=1|WEEKS]:1 vecka +retentionPeriod[one|WEEKS]:1 vecka retentionPeriod[other|WEEKS]:{0,number} veckor retentionPeriod[\=1|MONTHS]:1 månad +retentionPeriod[one|MONTHS]:1 månad retentionPeriod[other|MONTHS]:{0,number} månader retentionPeriod[\=1|YEARS]:1 år +retentionPeriod[one|YEARS]:1 år retentionPeriod[other|YEARS]:{0,number} år retentionPeriod[other|NO_RETENTION_PERIOD]:Ingen gallringsfrist permanentlyDeleteConfirmDialogTitle:Ta permanent bort objekt från papperskorgen 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..a600fece42 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 @@ -75,8 +75,10 @@ ui.sharedProperties.whitelist.configuration.prefix = ui.icons ui.sharedProperties.whitelist.configuration.prefix = ui.lists ui.sharedProperties.whitelist.configuration.prefix = ui.search.fields ui.sharedProperties.whitelist.configuration.property = ui.lists +ui.sharedProperties.whitelist.configuration.prefix = ui.catalogue ui.sharedProperties.whitelist.configuration.prefix = ui.css ui.sharedProperties.whitelist.messages.prefix = ui.facets +ui.sharedProperties.whitelist.messages.prefix = ui.catalogue.virtual ui.sharedProperties.whitelist.messages.prefix = lists.label ui.sharedProperties.whitelist.messages.prefix = ui.search.fields ui.sharedProperties.whitelist.messages.prefix = level @@ -284,14 +286,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 +300,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 @@ -325,6 +327,7 @@ ui.role: access_key.manage ui.browser.metadata.descriptive.types = ead_2002 ui.browser.metadata.descriptive.types = dc_SimpleDC20021212 ui.browser.metadata.descriptive.types = key-value +ui.browser.metadata.descriptive.types = emailarchive #ui.browser.metadata.descriptive.types = ead_3 ########################################################################## @@ -372,6 +375,9 @@ ui.search.fields.IndexedAIP = origination ui.search.fields.IndexedAIP = ingestSIPIds ui.search.fields.IndexedAIP = dates ui.search.fields.IndexedAIP = level +ui.search.fields.IndexedAIP = emailSubject +ui.search.fields.IndexedAIP = emailSender +ui.search.fields.IndexedAIP = emailSentDate ui.search.fields.IndexedAIP.identifier.fields = id ui.search.fields.IndexedAIP.identifier.i18n = ui.search.fields.IndexedAIP.identifier @@ -441,6 +447,24 @@ ui.search.fields.IndexedAIP.type.i18n = ui.search.fields.IndexedAIP.type ui.search.fields.IndexedAIP.type.type = text ui.search.fields.IndexedAIP.type.fixed = true +ui.search.fields.IndexedAIP.emailSubject.fields = subject_txt +ui.search.fields.IndexedAIP.emailSubject.i18n = ui.search.fields.IndexedAIP.emailSubject +ui.search.fields.IndexedAIP.emailSubject.type = text +ui.search.fields.IndexedAIP.emailSubject.nestedType = email +ui.search.fields.IndexedAIP.emailSubject.nestedParentType = emailarchive + +ui.search.fields.IndexedAIP.emailSender.fields = sender_s +ui.search.fields.IndexedAIP.emailSender.i18n = ui.search.fields.IndexedAIP.emailSender +ui.search.fields.IndexedAIP.emailSender.type = text +ui.search.fields.IndexedAIP.emailSender.nestedType = email +ui.search.fields.IndexedAIP.emailSender.nestedParentType = emailarchive + +ui.search.fields.IndexedAIP.emailSentDate.fields = sentDate_dt +ui.search.fields.IndexedAIP.emailSentDate.i18n = ui.search.fields.IndexedAIP.emailSentDate +ui.search.fields.IndexedAIP.emailSentDate.type = date_interval +ui.search.fields.IndexedAIP.emailSentDate.nestedType = email +ui.search.fields.IndexedAIP.emailSentDate.nestedParentType = emailarchive + ui.search.fields.IndexedRepresentation = identifier ui.search.fields.IndexedRepresentation = type ui.search.fields.IndexedRepresentation = original @@ -907,7 +931,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 @@ -1095,6 +1119,114 @@ ui.lists.Search_AIPs.columns[].default_IndexedAIP_hasrepresentations.width = 3.0 ui.lists.Search_AIPs.defaultSortList.columnName = default_IndexedAIP_dates ui.lists.Search_AIPs.defaultSortList.ascending = false +########################################################################## +# Virtual catalogue configuration +# +# ui.catalogue.virtual +# Array of virtual catalogue list IDs to append to the search/catalogue tabs. +# Each entry corresponds to a ui.lists.{listId} block that must define +# either catalogue.filter (type 1: filtered AIP view) or +# catalogue.childOf.filter (type 2: nested child documents). +# +# ui.lists.{listId}.catalogue.label.i18n +# i18n key whose resolved translation is used as the dropdown label. +# +# ui.lists.{listId}.catalogue.icon +# FontAwesome CSS class for the dropdown icon. +# +# ui.lists.{listId}.catalogue.filter = field:value +# Type 1: filters parent AIP documents by a Solr field:value. +# Standard AIP browse (bindOpener) is used on row click. +# +# ui.lists.{listId}.catalogue.childOf.filter = field:value +# Type 2: returns nested child documents whose parent matches field:value. +# Row click navigates to the parent AIP. +########################################################################## +ui.catalogue.virtual = Search_emailarchive +ui.catalogue.virtual = Search_emails + +## Search_emailarchive — Virtual catalogue: Email Archive AIPs (type 1, filtered) +ui.lists.Search_emailarchive.catalogue.label.i18n = ui.catalogue.virtual.Search_emailarchive.label +ui.lists.Search_emailarchive.catalogue.filter = content_type:emailarchive +ui.lists.Search_emailarchive.search.advanced.enabled = true + +ui.lists.Search_emailarchive.columns[] = title +ui.lists.Search_emailarchive.columns[].title.field = title +ui.lists.Search_emailarchive.columns[].title.header = ui.search.fields.IndexedAIP.title +ui.lists.Search_emailarchive.columns[].title.sortBy = title_sort + +ui.lists.Search_emailarchive.columns[] = default_IndexedAIP_dates +ui.lists.Search_emailarchive.columns[].default_IndexedAIP_dates.header = ui.search.fields.IndexedAIP.dates +ui.lists.Search_emailarchive.columns[].default_IndexedAIP_dates.sortBy = dateInitial +ui.lists.Search_emailarchive.columns[].default_IndexedAIP_dates.sortBy = dateFinal +ui.lists.Search_emailarchive.columns[].default_IndexedAIP_dates.width = 14.0 + +ui.lists.Search_emailarchive.facets.parameters = level +ui.lists.Search_emailarchive.facets.parameters = hasRepresentations +ui.lists.Search_emailarchive.facets.parameters.level.type = SimpleFacetParameter +ui.lists.Search_emailarchive.facets.parameters.hasRepresentations.type = SimpleFacetParameter + +ui.search.fields.Search_emailarchive = title +ui.search.fields.Search_emailarchive = description +ui.search.fields.Search_emailarchive = dates + +ui.search.fields.Search_emailarchive.title.fields = title +ui.search.fields.Search_emailarchive.title.i18n = ui.search.fields.IndexedAIP.title +ui.search.fields.Search_emailarchive.title.type = text +ui.search.fields.Search_emailarchive.title.fixed = true + +ui.search.fields.Search_emailarchive.description.fields = description +ui.search.fields.Search_emailarchive.description.i18n = ui.search.fields.IndexedAIP.description +ui.search.fields.Search_emailarchive.description.type = text +ui.search.fields.Search_emailarchive.description.fixed = false + +ui.search.fields.Search_emailarchive.dates.fields = dateInitial,dateFinal +ui.search.fields.Search_emailarchive.dates.i18n = ui.search.fields.IndexedAIP.dates +ui.search.fields.Search_emailarchive.dates.type = date_interval +ui.search.fields.Search_emailarchive.dates.fixed = false + +## Search_emails — Virtual catalogue: Individual Emails (type 2, nested child documents) +ui.lists.Search_emails.catalogue.label.i18n = ui.catalogue.virtual.Search_emails.label +ui.lists.Search_emails.catalogue.childOf.filter = content_type:emailarchive +ui.lists.Search_emails.search.advanced.enabled = true + +ui.lists.Search_emails.columns[] = sender_s +ui.lists.Search_emails.columns[].sender_s.field = sender_s +ui.lists.Search_emails.columns[].sender_s.header = ui.search.fields.IndexedAIP.emailSender +ui.lists.Search_emails.columns[].sender_s.sortBy = sender_s + +ui.lists.Search_emails.columns[] = subject_txt +ui.lists.Search_emails.columns[].subject_txt.field = subject_txt +ui.lists.Search_emails.columns[].subject_txt.header = ui.search.fields.IndexedAIP.emailSubject +ui.lists.Search_emails.columns[].subject_txt.sortBy = subject_txt +ui.lists.Search_emails.columns[].subject_txt.renderingHint = LIST + +ui.lists.Search_emails.columns[] = sentDate_dt +ui.lists.Search_emails.columns[].sentDate_dt.field = sentDate_dt +ui.lists.Search_emails.columns[].sentDate_dt.header = ui.search.fields.IndexedAIP.emailSentDate +ui.lists.Search_emails.columns[].sentDate_dt.sortBy = sentDate_dt +ui.lists.Search_emails.columns[].sentDate_dt.width = 14.0 +ui.lists.Search_emails.columns[].sentDate_dt.renderingHint = DATETIME_FORMAT_SIMPLE + +ui.lists.Search_emails.facets.parameters = sender_s +ui.lists.Search_emails.facets.parameters.sender_s.type = SimpleFacetParameter + +ui.search.fields.Search_emails = emailSender +ui.search.fields.Search_emails = emailSubject +ui.search.fields.Search_emails = emailSentDate + +ui.search.fields.Search_emails.emailSender.fields = sender_s +ui.search.fields.Search_emails.emailSender.i18n = ui.search.fields.IndexedAIP.emailSender +ui.search.fields.Search_emails.emailSender.type = text + +ui.search.fields.Search_emails.emailSubject.fields = subject_txt +ui.search.fields.Search_emails.emailSubject.i18n = ui.search.fields.IndexedAIP.emailSubject +ui.search.fields.Search_emails.emailSubject.type = text + +ui.search.fields.Search_emails.emailSentDate.fields = sentDate_dt +ui.search.fields.Search_emails.emailSentDate.i18n = ui.search.fields.IndexedAIP.emailSentDate +ui.search.fields.Search_emails.emailSentDate.type = date_interval + ## BrowseTop_aip ui.lists.BrowseTop_aip.columns[] = default_IndexedAIP_level ui.lists.BrowseTop_aip.columns[].default_IndexedAIP_level.sortBy = level @@ -2038,6 +2170,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 +2769,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)