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:
+ *
+ * Docker with Compose V2 ({@code docker compose}) available on {@code PATH}
+ * {@code docker.io/keeps/roda:development} image available locally or pullable
+ *
+ */
+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:
+ *
+ * Testcontainers polls {@code /actuator/health} until Spring Boot reports
+ * {@code "status":"UP"} — this covers RODA startup including Solr and database.
+ * 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).
+ *
+ *
+ * TestNG guarantees this runs exactly once before any test method in the suite,
+ * regardless of how many test classes extend this base.
+ */
+ @BeforeSuite(alwaysRun = true)
+ public void startEnvironment() {
+ LOGGER.info("Starting RODA Docker Compose stack from {}", COMPOSE_FILE.getAbsolutePath());
+
+ COMPOSE = new ComposeContainer(COMPOSE_FILE)
+ .withExposedService(RODA_SERVICE, RODA_PORT)
+ .waitingFor(
+ RODA_SERVICE,
+ Wait.forHttp("/actuator/health")
+ .forStatusCode(200)
+ .forResponsePredicate(body -> body.contains("\"status\":\"UP\""))
+ .withStartupTimeout(Duration.ofMinutes(10)));
+
+ COMPOSE.start();
+
+ String host = COMPOSE.getServiceHost(RODA_SERVICE, RODA_PORT);
+ int mappedPort = COMPOSE.getServicePort(RODA_SERVICE, RODA_PORT);
+
+ LOGGER.info("RODA available at http://{}:{}/api/v2", host, mappedPort);
+
+ RestAssured.baseURI = "http://" + host;
+ RestAssured.port = mappedPort;
+ RestAssured.basePath = "/api/v2";
+ RestAssured.defaultParser = io.restassured.parsing.Parser.JSON;
+
+ // requestSpecification is the most reliable way to set default auth globally
+ // across all given() calls in REST Assured 5.x.
+ RestAssured.requestSpecification = new RequestSpecBuilder()
+ .setAuth(preemptive().basic(ADMIN_USER, ADMIN_PASSWORD))
+ .build();
+
+ // Phase 2: /actuator/health reports UP before LDAP is fully operational.
+ // Poll until the admin user is returned, confirming LDAP auth works.
+ LOGGER.info("Waiting for LDAP authentication to be ready...");
+ pollUntil("LDAP auth", () -> {
+ String name = given().when().get("/members/users/authenticated").then().extract().path("name");
+ return ADMIN_USER.equals(name);
+ });
+
+ }
+
+ private void pollUntil(String label, java.util.function.BooleanSupplier condition) {
+ long deadline = System.currentTimeMillis() + Duration.ofMinutes(5).toMillis();
+ int attempt = 0;
+ while (System.currentTimeMillis() < deadline) {
+ attempt++;
+ try {
+ if (condition.getAsBoolean()) {
+ LOGGER.info("{} ready after {} poll attempts", label, attempt);
+ return;
+ }
+ LOGGER.info("Attempt {}: {} not ready yet", attempt, label);
+ } catch (Exception e) {
+ LOGGER.warn("Attempt {}: {} check failed: {}", attempt, label, e.getMessage());
+ }
+ try {
+ Thread.sleep(10000);
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ return;
+ }
+ }
+ LOGGER.warn("{} readiness timed out after {} attempts — proceeding anyway", label, attempt);
+ }
+
+ /**
+ * Stops the Docker Compose stack after all suite tests have finished.
+ */
+ @AfterSuite(alwaysRun = true)
+ public void stopEnvironment() {
+ if (COMPOSE != null) {
+ LOGGER.info("Stopping RODA Docker Compose stack");
+ COMPOSE.stop();
+ }
+ }
+}
diff --git a/roda-api-tests/src/test/java/org/roda/tests/api/aips/AipApiTest.java b/roda-api-tests/src/test/java/org/roda/tests/api/aips/AipApiTest.java
new file mode 100644
index 0000000000..4de3191e8f
--- /dev/null
+++ b/roda-api-tests/src/test/java/org/roda/tests/api/aips/AipApiTest.java
@@ -0,0 +1,115 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE file at the root of the source
+ * tree and available online at
+ *
+ * https://github.com/keeps/roda
+ */
+package org.roda.tests.api.aips;
+
+import static io.restassured.RestAssured.given;
+
+import org.roda.tests.api.AbstractApiTest;
+import org.roda.tests.api.model.IndexResult;
+import org.roda.tests.api.model.LongResponse;
+import org.roda.tests.api.model.User;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import io.restassured.http.ContentType;
+
+/**
+ * E2E tests for the {@code /api/v2/aips} resource.
+ *
+ *
All tests run against a live RODA instance managed by Testcontainers.
+ * The Docker Compose stack is started once per suite in {@link AbstractApiTest}.
+ *
+ *
A freshly started RODA instance has no AIPs, so count/find assertions
+ * verify structure (non-null, non-negative) rather than exact counts.
+ *
+ *
Filter JSON is built as raw strings because the generated OpenAPI model
+ * uses {@code "@type"} as the Jackson discriminator while RODA expects {@code "type"}.
+ */
+@Test(groups = {"e2e", "aips"})
+public class AipApiTest extends AbstractApiTest {
+
+ private static final String ALL_FILTER_BODY =
+ "{\"filter\":{\"parameters\":[{\"type\":\"AllFilterParameter\"}]}}";
+
+ /**
+ * Sanity check: the authenticated admin user is returned correctly.
+ */
+ @Test
+ public void getAuthenticatedUser_returnsAdminUser() {
+ User user = given()
+ .when()
+ .get("/members/users/authenticated")
+ .then()
+ .statusCode(200)
+ .extract().as(User.class);
+
+ Assert.assertNotNull(user, "Authenticated user must not be null");
+ Assert.assertEquals(user.getName(), ADMIN_USER,
+ "Expected the admin user to be authenticated");
+ }
+
+ /**
+ * {@code POST /aips/count} with an AllFilterParameter must return HTTP 200
+ * and a non-negative count.
+ */
+ @Test
+ public void countAips_withAllFilter_returns200AndNonNegativeCount() {
+ LongResponse response = given()
+ .contentType(ContentType.JSON)
+ .body(ALL_FILTER_BODY)
+ .when()
+ .post("/aips/count")
+ .then()
+ .statusCode(200)
+ .extract().as(LongResponse.class);
+
+ Assert.assertNotNull(response, "Count response must not be null");
+ Assert.assertTrue(response.getResult() >= 0,
+ "AIP count must be non-negative, got: " + response.getResult());
+ }
+
+ /**
+ * {@code POST /aips/find} with an AllFilterParameter and limit=10 must return
+ * HTTP 200 and a structurally valid {@link IndexResult}.
+ */
+ @Test
+ public void findAips_withAllFilter_returns200AndValidIndexResult() {
+ String body =
+ "{\"filter\":{\"parameters\":[{\"type\":\"AllFilterParameter\"}]}," +
+ "\"sublist\":{\"firstElementIndex\":0,\"maximumElementCount\":10}}";
+
+ IndexResult result = given()
+ .contentType(ContentType.JSON)
+ .body(body)
+ .when()
+ .post("/aips/find")
+ .then()
+ .statusCode(200)
+ .extract().as(IndexResult.class);
+
+ Assert.assertNotNull(result, "IndexResult must not be null");
+ Assert.assertTrue(result.getTotalCount() >= 0,
+ "Total count must be non-negative, got: " + result.getTotalCount());
+ // RODA returns results:null (not []) when totalCount == 0
+ if (result.getTotalCount() > 0) {
+ Assert.assertNotNull(result.getResults(), "IndexResult.results must not be null when totalCount > 0");
+ }
+ }
+
+ /**
+ * {@code GET /aips/configuration/types} must return HTTP 200.
+ */
+ @Test
+ public void getAipConfigurationTypes_returns200() {
+ given()
+ .when()
+ .get("/aips/configuration/types")
+ .then()
+ .statusCode(200);
+ }
+}
diff --git a/roda-api-tests/src/test/resources/logback-test.xml b/roda-api-tests/src/test/resources/logback-test.xml
new file mode 100644
index 0000000000..36863c28d1
--- /dev/null
+++ b/roda-api-tests/src/test/resources/logback-test.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/roda-api-tests/src/test/resources/testng.xml b/roda-api-tests/src/test/resources/testng.xml
new file mode 100644
index 0000000000..80f598f327
--- /dev/null
+++ b/roda-api-tests/src/test/resources/testng.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/common/RodaConstants.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/common/RodaConstants.java
index 64715f8c18..1f59483aff 100644
--- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/common/RodaConstants.java
+++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/common/RodaConstants.java
@@ -10,8 +10,12 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Date;
import java.util.List;
+import org.roda.core.data.v2.disposal.schedule.DisposalActionCode;
+import org.roda.core.data.v2.disposal.schedule.DisposalScheduleState;
+import org.roda.core.data.v2.disposal.schedule.RetentionPeriodIntervalCode;
import org.roda.core.data.v2.ip.Permissions.PermissionType;
public final class RodaConstants {
@@ -932,6 +936,9 @@ public enum OrchestratorType {
public static final String INDEX_DIP_FILE = "DIPFile";
public static final String INDEX_REPRESENTATION_INFORMATION = "RepresentationInformation";
public static final String INDEX_DISPOSAL_CONFIRMATION = "DisposalConfirmation";
+ public static final String INDEX_DISPOSAL_SCHEDULE = "DisposalSchedule";
+ public static final String INDEX_DISPOSAL_HOLD = "DisposalHold";
+ public static final String INDEX_DISPOSAL_RULE = "DisposalRule";
/*
* INDEXED CLASSES
@@ -946,7 +953,10 @@ public enum OrchestratorType {
"org.roda.core.data.v2.risks.RiskIncidence", "org.roda.core.data.v2.ri.RepresentationInformation",
"org.roda.core.data.v2.ip.TransferredResource", "org.roda.core.data.v2.user.User",
"org.roda.core.data.v2.user.Group", "org.roda.core.data.v2.user.RODAMember",
- "org.roda.core.data.v2.ip.disposal.DisposalConfirmation", "org.roda.core.data.v2.user.RodaPrincipal",
+ "org.roda.core.data.v2.disposal.hold.DisposalHold",
+ "org.roda.core.data.v2.disposal.schedule.DisposalSchedule",
+ "org.roda.core.data.v2.disposal.rule.DisposalRule",
+ "org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation", "org.roda.core.data.v2.user.RodaPrincipal",
"org.roda.core.data.v2.ip.AIP", "org.roda.core.data.v2.risks.Risk", "org.roda.core.events.pekko.CRDTWrapper",
"org.roda.core.data.v2.ip.DIP", "org.roda.core.data.v2.ip.metadata.DescriptiveMetadata",
"org.roda.core.data.v2.ip.disposal.DisposalConfirmationAIPEntry",
@@ -1307,8 +1317,32 @@ public enum OrchestratorType {
public static final String DISPOSAL_CONFIRMATION_EXTRA_INFO = "extraInformation";
public static final String DISPOSAL_CONFIRMATION_STORAGE_SIZE = "size";
+ public static final String DISPOSAL_SCHEDULE_TITLE = "title";
+ public static final String DISPOSAL_SCHEDULE_DESCRIPTION = "description";
+ public static final String DISPOSAL_SCHEDULE_MANDATE = "mandate";
+ public static final String DISPOSAL_SCHEDULE_SCOPE_NOTES = "scopeNotes";
+ public static final String DISPOSAL_SCHEDULE_STATE = "state";
+ public static final String DISPOSAL_SCHEDULE_ACTION = "action";
+ public static final String DISPOSAL_SCHEDULE_RETENTION_PERIOD_INTERVAL_CODE = "retentionPeriodIntervalCode";
+ public static final String DISPOSAL_SCHEDULE_RETENTION_PERIOD_DURATION = "retentionPeriodDuration";
+
+ public static final String DISPOSAL_HOLD_TITLE = "title";
+ public static final String DISPOSAL_HOLD_DESCRIPTION = "description";
+ public static final String DISPOSAL_HOLD_MANDATE = "mandate";
+ public static final String DISPOSAL_HOLD_SCOPE_NOTES = "scopeNotes";
+ public static final String DISPOSAL_HOLD_STATE = "state";
+
+ public static final String DISPOSAL_RULE_ORDER = "order";
+ public static final String DISPOSAL_RULE_TITLE = "title";
+ public static final String DISPOSAL_RULE_DESCRIPTION = "description";
+ public static final String DISPOSAL_RULE_SELECTION_METHOD = "selectionMethod";
+ public static final String DISPOSAL_RULE_CONDITION_KEY = "conditionKey";
+ public static final String DISPOSAL_RULE_CONDITION_VALUE = "conditionvalue";
+ public static final String DISPOSAL_RULE_SCHEDULE_ID = "scheduleId";
+
/* Disposal related parameters */
public static final String DISPOSAL_HOLD_FILE_EXTENSION = ".json";
+ public static final String DISPOSAL_RULE_FILE_EXTENSION = ".json";
/* Distributed related parameters */
public static final String DISTRIBUTED_INSTANCE_FILE_EXTENSION = ".json";
@@ -2161,11 +2195,15 @@ public enum RODA_TYPE {
public static final String PERMISSION_METHOD_CREATE_GROUP = "org.roda.wui.api.v2.controller.MembersController.createGroup";
public static final String PERMISSION_METHOD_UPDATE_USER = "org.roda.wui.api.v2.controller.MembersController.updateUser";
public static final String PERMISSION_METHOD_DELETE_USER = "org.roda.wui.api.v2.controller.MembersController.deleteUser";
+ public static final String PERMISSION_METHOD_REVOKE_ACCESS_TOKEN = "org.roda.wui.api.v2.controller.MembersController.regenerateAccessKey";
+ public static final String PERMISSION_METHOD_DELETE_ACCESS_TOKEN = "org.roda.wui.api.v2.controller.MembersController.deleteAccessKey";
+ public static final String PERMISSION_METHOD_REGENERATE_ACCESS_TOKEN = "org.roda.wui.api.v2.controller.MembersController.regenerateAccessKey";
public static final String PERMISSION_METHOD_CREATE_ACCESS_KEY = "org.roda.wui.api.v2.controller.MembersController.createAccessKey";
public static final String PERMISSION_METHOD_CREATE_DISPOSAL_RULE = "org.roda.wui.api.v2.controller.DisposalRuleController.createDisposalRule";
public static final String PERMISSION_METHOD_UPDATE_DISPOSAL_RULE = "org.roda.wui.api.v2.controller.DisposalRuleController.updateDisposalRule";
+ public static final String PERMISSION_METHOD_DELETE_DISPOSAL_RULE = "org.roda.wui.api.v2.controller.DisposalRuleController.deleteDisposalRule";
public static final String PERMISSION_METHOD_CREATE_DISPOSAL_SCHEDULE = "org.roda.wui.api.v2.controller.DisposalScheduleController.createDisposalSchedule";
public static final String PERMISSION_METHOD_UPDATE_DISPOSAL_SCHEDULE = "org.roda.wui.api.v2.controller.DisposalScheduleController.updateDisposalSchedule";
diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/common/RODAObjectList.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/common/RODAObjectList.java
index 72144f4da0..4263ac6169 100644
--- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/common/RODAObjectList.java
+++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/common/RODAObjectList.java
@@ -14,9 +14,9 @@
public interface RODAObjectList extends Serializable {
- public List getObjects();
+ List getObjects();
- public void setObjects(List objects);
+ void setObjects(List objects);
- public void addObject(T object);
+ void addObject(T object);
}
diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/hold/DisposalHold.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/hold/DisposalHold.java
index 791823505f..063219f753 100644
--- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/hold/DisposalHold.java
+++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/hold/DisposalHold.java
@@ -7,7 +7,12 @@
*/
package org.roda.core.data.v2.disposal.hold;
+import java.io.Serial;
+import java.util.Arrays;
import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Objects;
import org.roda.core.data.common.RodaConstants;
@@ -15,6 +20,8 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
+import org.roda.core.data.v2.index.IsIndexed;
+import org.roda.core.data.v2.ip.HasId;
/**
* @author Tiago Fraga
@@ -22,8 +29,9 @@
@jakarta.xml.bind.annotation.XmlRootElement(name = RodaConstants.RODA_OBJECT_DISPOSAL_HOLD)
@JsonInclude(JsonInclude.Include.NON_NULL)
-public class DisposalHold implements IsModelObject {
+public class DisposalHold implements IsIndexed, IsModelObject, HasId {
+ @Serial
private static final long serialVersionUID = 8291490773422089586L;
private String id;
@@ -57,6 +65,8 @@ public class DisposalHold implements IsModelObject {
private DisposalHoldState state;
+ private Map fields = new HashMap<>();
+
public DisposalHold() {
super();
this.state = DisposalHoldState.ACTIVE;
@@ -242,4 +252,38 @@ public String toString() {
+ ", liftedBy='" + liftedBy + '\'' + ", firstTimeUsed=" + firstTimeUsed + ", aipCounter=" + aipCounter
+ ", state=" + state + '}';
}
+
+ @JsonIgnore
+ @Override
+ public String getUUID() {
+ return getId();
+ }
+
+ @Override
+ public List toCsvHeaders() {
+ return Arrays.asList("id", "title", "description", "mandate", "scopeNotes", "createOn", "createBy", "updatedOn",
+ "updatedBy", "originatedOn", "originatedBy", "liftedOn", "liftedBy", "firstTimeUsed", "state");
+ }
+
+ @Override
+ public List toCsvValues() {
+ return Arrays.asList(id, title, description, mandate, scopeNotes, createdOn, createdBy, updatedOn, updatedBy,
+ originatedOn, originatedBy, liftedOn, liftedBy, firstTimeUsed, state);
+ }
+
+ @Override
+ public List liteFields() {
+ return List.of();
+ }
+
+ @Override
+ public Map getFields() {
+ return this.fields;
+ }
+
+ @Override
+ public void setFields(Map fields) {
+ fields.entrySet().stream().filter(p -> p.getValue() != null)
+ .forEach(e -> this.fields.put(e.getKey(), e.getValue()));
+ }
}
diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/hold/DisposalHoldState.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/hold/DisposalHoldState.java
index 3227e7854b..c48af2c710 100644
--- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/hold/DisposalHoldState.java
+++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/hold/DisposalHoldState.java
@@ -11,5 +11,9 @@
* @author Tiago Fraga
*/
public enum DisposalHoldState {
- ACTIVE, LIFTED
+ ACTIVE, LIFTED;
+
+ public static DisposalHoldState getDefault() {
+ return ACTIVE;
+ }
}
diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/ChangeOrderRequest.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/ChangeOrderRequest.java
new file mode 100644
index 0000000000..dea7ab9447
--- /dev/null
+++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/ChangeOrderRequest.java
@@ -0,0 +1,53 @@
+package org.roda.core.data.v2.disposal.rule;
+
+import org.roda.core.data.v2.generics.select.SelectedItemsRequest;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * @author Miguel Guimarães
+ */
+
+public class ChangeOrderRequest implements Serializable {
+ @Serial
+ private static final long serialVersionUID = -1604170151297177958L;
+
+ private SelectedItemsRequest items;
+ private OrderPositions position;
+ private int newOrder;
+
+ public ChangeOrderRequest() {
+ position = OrderPositions.TOP;
+ }
+
+ public ChangeOrderRequest(SelectedItemsRequest items, OrderPositions position, int newOrder) {
+ this.items = items;
+ this.position = position;
+ this.newOrder = newOrder;
+ }
+
+ public SelectedItemsRequest getItems() {
+ return items;
+ }
+
+ public void setItems(SelectedItemsRequest items) {
+ this.items = items;
+ }
+
+ public int getNewOrder() {
+ return newOrder;
+ }
+
+ public void setNewOrder(int newOrder) {
+ this.newOrder = newOrder;
+ }
+
+ public OrderPositions getPosition() {
+ return position;
+ }
+
+ public void setPosition(OrderPositions position) {
+ this.position = position;
+ }
+}
diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/ConditionType.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/ConditionType.java
index df4f3b8147..291fd58f9a 100644
--- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/ConditionType.java
+++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/ConditionType.java
@@ -11,5 +11,9 @@
* @author Tiago Fraga
*/
public enum ConditionType {
- IS_CHILD_OF, METADATA_FIELD
+ IS_CHILD_OF, METADATA_FIELD;
+
+ public static ConditionType getDefault() {
+ return IS_CHILD_OF;
+ }
}
diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/DisposalRule.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/DisposalRule.java
index 9ff812252e..9d8fdf8d6a 100644
--- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/DisposalRule.java
+++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/DisposalRule.java
@@ -8,11 +8,15 @@
package org.roda.core.data.v2.disposal.rule;
import java.io.Serial;
+import java.util.Arrays;
import java.util.Date;
+import java.util.List;
+import java.util.Map;
import java.util.Objects;
import org.roda.core.data.common.RodaConstants;
import org.roda.core.data.v2.IsModelObject;
+import org.roda.core.data.v2.index.IsIndexed;
import org.roda.core.data.v2.ip.HasId;
import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -23,7 +27,7 @@
*/
@jakarta.xml.bind.annotation.XmlRootElement(name = RodaConstants.RODA_OBJECT_DISPOSAL_RULE)
@JsonInclude(JsonInclude.Include.NON_NULL)
-public class DisposalRule implements IsModelObject, HasId, Comparable {
+public class DisposalRule implements IsModelObject, IsIndexed, HasId, Comparable {
private static final int VERSION = 1;
@Serial
@@ -50,8 +54,11 @@ public class DisposalRule implements IsModelObject, HasId, Comparable fields;
+
public DisposalRule() {
super();
+ type = ConditionType.IS_CHILD_OF;
}
@Override
@@ -201,4 +208,37 @@ public int getClassVersion() {
public int compareTo(DisposalRule otherRule) {
return Integer.compare(this.getOrder(), otherRule.getOrder());
}
+
+ @JsonIgnore
+ @Override
+ public String getUUID() {
+ return getId();
+ }
+
+ @Override
+ public List toCsvHeaders() {
+ return Arrays.asList("id", "title", "description", "type", "conditionKey", "conditionValue", "disposalScheduleId",
+ "disposalScheduleName", "order", "createdOn", "createdBy", "updatedOn", "updatedBy");
+ }
+
+ @Override
+ public List toCsvValues() {
+ return Arrays.asList(id, title, description, type, conditionKey, conditionValue, disposalScheduleId,
+ disposalScheduleName, order, createdOn, createdBy, updatedOn, updatedBy);
+ }
+
+ @Override
+ public List liteFields() {
+ return List.of();
+ }
+
+ @Override
+ public Map getFields() {
+ return this.fields;
+ }
+
+ @Override
+ public void setFields(Map fields) {
+ this.fields = fields;
+ }
}
diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/DisposalRules.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/DisposalRules.java
index a98fb9e61c..7256972b88 100644
--- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/DisposalRules.java
+++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/DisposalRules.java
@@ -7,6 +7,7 @@
*/
package org.roda.core.data.v2.disposal.rule;
+import java.io.Serial;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -23,6 +24,8 @@
@JsonInclude(JsonInclude.Include.NON_NULL)
public class DisposalRules implements RODAObjectList {
+ @Serial
+ private static final long serialVersionUID = 5273072223178069428L;
private List disposalRuleList;
public DisposalRules() {
diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/OrderPositions.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/OrderPositions.java
new file mode 100644
index 0000000000..fa245a8f5f
--- /dev/null
+++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/rule/OrderPositions.java
@@ -0,0 +1,9 @@
+package org.roda.core.data.v2.disposal.rule;
+
+/**
+ * @author Miguel Guimarães
+ */
+
+public enum OrderPositions {
+ TOP, BOTTOM, POSITION
+}
diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/DisposalSchedule.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/DisposalSchedule.java
index 45aa44af63..2d6bf63812 100644
--- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/DisposalSchedule.java
+++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/DisposalSchedule.java
@@ -8,10 +8,16 @@
package org.roda.core.data.v2.disposal.schedule;
import java.io.Serial;
+import java.util.Arrays;
import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import org.roda.core.data.common.RodaConstants;
import org.roda.core.data.v2.IsModelObject;
+import org.roda.core.data.v2.index.IsIndexed;
import org.roda.core.data.v2.ip.HasId;
import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -21,7 +27,7 @@
* @author Miguel Guimarães
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
-public class DisposalSchedule implements IsModelObject, HasId {
+public class DisposalSchedule implements IsModelObject, IsIndexed, HasId {
@Serial
private static final long serialVersionUID = -2870778207871507847L;
private static final int VERSION = 1;
@@ -54,6 +60,10 @@ public class DisposalSchedule implements IsModelObject, HasId {
private DisposalScheduleState state;
+ private Map fields = new HashMap<>();
+
+ private boolean usedInDisposalRule = false;
+
public DisposalSchedule() {
super();
this.state = DisposalScheduleState.ACTIVE;
@@ -221,6 +231,14 @@ public void setState(DisposalScheduleState state) {
this.state = state;
}
+ public boolean isUsedInDisposalRule() {
+ return usedInDisposalRule;
+ }
+
+ public void setUsedInDisposalRule(boolean usedInDisposalRule) {
+ this.usedInDisposalRule = usedInDisposalRule;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o)
@@ -255,4 +273,39 @@ public String toString() {
+ firstTimeUsed + ", numberOfAIPUnder=" + apiCounter + ", createdOn=" + createdOn + ", createdBy='" + createdBy
+ '\'' + ", updatedOn=" + updatedOn + ", updatedBy='" + updatedBy + '\'' + ", state=" + state + '}';
}
+
+ @JsonIgnore
+ @Override
+ public String getUUID() {
+ return getId();
+ }
+
+ @Override
+ public List toCsvHeaders() {
+ return Arrays.asList("id", "title", "description", "mandate", "scopeNotes", "actionCode",
+ "retentionTriggerElementId", "retentionPeriodIntervalCode", "retentionPeriodDuration", "createdOn", "createdBy",
+ "updatedOn", "updatedBy", "state");
+ }
+
+ @Override
+ public List toCsvValues() {
+ return Arrays.asList(id, title, description, mandate, scopeNotes, actionCode, retentionTriggerElementId,
+ retentionPeriodIntervalCode, retentionPeriodDuration, createdOn, createdBy, updatedOn, updatedBy, state);
+ }
+
+ @Override
+ public List liteFields() {
+ return Arrays.asList(RodaConstants.INDEX_UUID);
+ }
+
+ @Override
+ public Map getFields() {
+ return fields;
+ }
+
+ @Override
+ public void setFields(Map fields) {
+ fields.entrySet().stream().filter(p -> p.getValue() != null)
+ .forEach(e -> this.fields.put(e.getKey(), e.getValue()));
+ }
}
diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/DisposalScheduleState.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/DisposalScheduleState.java
index 53f54b2098..a499448443 100644
--- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/DisposalScheduleState.java
+++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/DisposalScheduleState.java
@@ -11,5 +11,9 @@
* @author Miguel Guimarães
*/
public enum DisposalScheduleState {
- ACTIVE, INACTIVE
+ ACTIVE, INACTIVE;
+
+ public static DisposalScheduleState getDefault() {
+ return ACTIVE;
+ }
}
diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/IndexedDisposalSchedule.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/IndexedDisposalSchedule.java
new file mode 100644
index 0000000000..b5d0ebcbbb
--- /dev/null
+++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/IndexedDisposalSchedule.java
@@ -0,0 +1,47 @@
+package org.roda.core.data.v2.disposal.schedule;
+
+import org.roda.core.data.v2.index.IsIndexed;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Miguel Guimarães
+ */
+
+public class IndexedDisposalSchedule implements IsIndexed {
+ @Override
+ public String getUUID() {
+ return "";
+ }
+
+ @Override
+ public List toCsvHeaders() {
+ return List.of();
+ }
+
+ @Override
+ public List toCsvValues() {
+ return List.of();
+ }
+
+ @Override
+ public List liteFields() {
+ return List.of();
+ }
+
+ @Override
+ public Map getFields() {
+ return Map.of();
+ }
+
+ @Override
+ public void setFields(Map fields) {
+
+ }
+
+ @Override
+ public String getId() {
+ return "";
+ }
+}
diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/RetentionPeriodIntervalCode.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/RetentionPeriodIntervalCode.java
index 84079d53d8..83cc2a18b6 100644
--- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/RetentionPeriodIntervalCode.java
+++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/disposal/schedule/RetentionPeriodIntervalCode.java
@@ -11,5 +11,9 @@
* @author Miguel Guimarães
*/
public enum RetentionPeriodIntervalCode {
- NO_RETENTION_PERIOD, DAYS, WEEKS, MONTHS, YEARS
+ NO_RETENTION_PERIOD, DAYS, WEEKS, MONTHS, YEARS;
+
+ public static RetentionPeriodIntervalCode getDefault() {
+ return NO_RETENTION_PERIOD;
+ }
}
diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/index/select/SelectedItemsList.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/index/select/SelectedItemsList.java
index 534349b270..696a73a47d 100644
--- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/index/select/SelectedItemsList.java
+++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/index/select/SelectedItemsList.java
@@ -14,8 +14,6 @@
import org.roda.core.data.v2.IsRODAObject;
-import com.fasterxml.jackson.annotation.JsonTypeName;
-
public class SelectedItemsList implements SelectedItems {
@Serial
diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/representation/ChangeTypeRequest.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/representation/ChangeTypeRequest.java
index aeb807336d..5e0f78e950 100644
--- a/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/representation/ChangeTypeRequest.java
+++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/v2/representation/ChangeTypeRequest.java
@@ -10,10 +10,6 @@
import java.io.Serial;
import java.io.Serializable;
-import io.swagger.v3.oas.annotations.media.DiscriminatorMapping;
-import io.swagger.v3.oas.annotations.media.Schema;
-import org.roda.core.data.v2.generics.select.SelectedItemsFilterRequest;
-import org.roda.core.data.v2.generics.select.SelectedItemsListRequest;
import org.roda.core.data.v2.generics.select.SelectedItemsRequest;
/**
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/common/CryptographyUtils.java b/roda-core/roda-core/src/main/java/org/roda/core/common/CryptographyUtils.java
new file mode 100644
index 0000000000..bcd994f3ba
--- /dev/null
+++ b/roda-core/roda-core/src/main/java/org/roda/core/common/CryptographyUtils.java
@@ -0,0 +1,40 @@
+package org.roda.core.common;
+
+import org.roda.core.data.exceptions.GenericException;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ *
+ * @author Miguel Guimarães
+ */
+public class CryptographyUtils {
+
+ private CryptographyUtils() {
+ // do nothing
+ }
+
+ public static String hashTokenSHA256(String token) throws GenericException {
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ byte[] encodedHash = digest.digest(token.getBytes(StandardCharsets.UTF_8));
+
+ // Convert the byte array into a hex string for easy storage
+ StringBuilder hexString = new StringBuilder(2 * encodedHash.length);
+ for (byte hash : encodedHash) {
+ String hex = Integer.toHexString(0xff & hash);
+ if (hex.length() == 1) {
+ hexString.append('0');
+ }
+ hexString.append(hex);
+ }
+ return hexString.toString();
+
+ } catch (NoSuchAlgorithmException e) {
+ // Wrap and throw using your application's exception handling
+ throw new GenericException("Error initializing SHA-256 hashing algorithm", e);
+ }
+ }
+}
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/index/IndexModelObserver.java b/roda-core/roda-core/src/main/java/org/roda/core/index/IndexModelObserver.java
index 0647dba256..2c7d68af30 100644
--- a/roda-core/roda-core/src/main/java/org/roda/core/index/IndexModelObserver.java
+++ b/roda-core/roda-core/src/main/java/org/roda/core/index/IndexModelObserver.java
@@ -37,6 +37,8 @@
import org.roda.core.data.v2.LiteRODAObject;
import org.roda.core.data.v2.common.OptionalWithCause;
import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation;
+import org.roda.core.data.v2.disposal.hold.DisposalHold;
+import org.roda.core.data.v2.disposal.rule.DisposalRule;
import org.roda.core.data.v2.disposal.schedule.DisposalSchedule;
import org.roda.core.data.v2.index.IsIndexed;
import org.roda.core.data.v2.index.filter.Filter;
@@ -1509,6 +1511,36 @@ public ReturnWithExceptions disposalConfirmationDeleted(Str
return deleteDocumentFromIndex(DisposalConfirmation.class, confirmationId);
}
+ @Override
+ public ReturnWithExceptions disposalScheduleCreatedOrUpdated(DisposalSchedule schedule) {
+ return SolrUtils.create2(index, model, this, DisposalSchedule.class, schedule);
+ }
+
+ @Override
+ public ReturnWithExceptions disposalScheduleDeleted(String scheduleId, boolean commit) {
+ return deleteDocumentFromIndex(DisposalSchedule.class, scheduleId);
+ }
+
+ @Override
+ public ReturnWithExceptions disposalHoldCreatedOrUpdated(DisposalHold hold) {
+ return SolrUtils.create2(index, model, this, DisposalHold.class, hold);
+ }
+
+ @Override
+ public ReturnWithExceptions disposalHoldDeleted(String holdId, boolean commit) {
+ return deleteDocumentFromIndex(DisposalHold.class, holdId);
+ }
+
+ @Override
+ public ReturnWithExceptions disposalRuleCreatedOrUpdated(DisposalRule rule) {
+ return SolrUtils.create2(index, model, this, DisposalRule.class, rule);
+ }
+
+ @Override
+ public ReturnWithExceptions disposalRuleDeleted(String ruleId, boolean commit) {
+ return deleteDocumentFromIndex(DisposalRule.class, ruleId);
+ }
+
public ReturnWithExceptions liteRODAObjectCreated(LiteRODAObject liteRODAObject) {
ReturnWithExceptions ret = new ReturnWithExceptions<>(this);
OptionalWithCause liteObject = LiteRODAObjectFactory.get(model, liteRODAObject);
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/index/IndexService.java b/roda-core/roda-core/src/main/java/org/roda/core/index/IndexService.java
index 6a109866aa..6864437de0 100644
--- a/roda-core/roda-core/src/main/java/org/roda/core/index/IndexService.java
+++ b/roda-core/roda-core/src/main/java/org/roda/core/index/IndexService.java
@@ -40,6 +40,9 @@
import org.roda.core.data.v2.IsRODAObject;
import org.roda.core.data.v2.common.OptionalWithCause;
import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation;
+import org.roda.core.data.v2.disposal.hold.DisposalHold;
+import org.roda.core.data.v2.disposal.rule.DisposalRule;
+import org.roda.core.data.v2.disposal.schedule.DisposalSchedule;
import org.roda.core.data.v2.index.FindRequest;
import org.roda.core.data.v2.index.IndexResult;
import org.roda.core.data.v2.index.IndexRunnable;
@@ -322,6 +325,30 @@ public ReturnWithExceptions reindexDisposalConfirmation(Dis
return ret;
}
+ public ReturnWithExceptions reindexDisposalSchedule(DisposalSchedule schedule) {
+ ReturnWithExceptions ret = RodaCoreFactory.checkIfWriteIsAllowedAndIfFalseReturn(nodeType);
+ if (ret.isEmpty()) {
+ ret = observer.disposalScheduleCreatedOrUpdated(schedule);
+ }
+ return ret;
+ }
+
+ public ReturnWithExceptions reindexDisposalHold(DisposalHold hold) {
+ ReturnWithExceptions ret = RodaCoreFactory.checkIfWriteIsAllowedAndIfFalseReturn(nodeType);
+ if (ret.isEmpty()) {
+ ret = observer.disposalHoldCreatedOrUpdated(hold);
+ }
+ return ret;
+ }
+
+ public ReturnWithExceptions reindexDisposalRule(DisposalRule rule) {
+ ReturnWithExceptions ret = RodaCoreFactory.checkIfWriteIsAllowedAndIfFalseReturn(nodeType);
+ if (ret.isEmpty()) {
+ ret = observer.disposalRuleCreatedOrUpdated(rule);
+ }
+ return ret;
+ }
+
public ReturnWithExceptions reindexAIPPreservationEvents(AIP aip) {
ReturnWithExceptions ret = RodaCoreFactory.checkIfWriteIsAllowedAndIfFalseReturn(nodeType);
if (ret.isEmpty()) {
@@ -519,6 +546,12 @@ public ReturnWithExceptions reinde
return reindexDIPFile(DIPFile.class.cast(object));
} else if (DisposalConfirmation.class.equals(objectClass)) {
return reindexDisposalConfirmation(DisposalConfirmation.class.cast(object));
+ } else if (DisposalSchedule.class.equals(objectClass)) {
+ return reindexDisposalSchedule(DisposalSchedule.class.cast(object));
+ } else if (DisposalHold.class.equals(objectClass)) {
+ return reindexDisposalHold(DisposalHold.class.cast(object));
+ } else if (DisposalRule.class.equals(objectClass)) {
+ return reindexDisposalRule(DisposalRule.class.cast(object));
} else {
LOGGER.error("Error trying to reindex an unconfigured object class: {}", objectClass.getName());
ReturnWithExceptions exceptions = new ReturnWithExceptions<>();
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/index/schema/SolrCollectionRegistry.java b/roda-core/roda-core/src/main/java/org/roda/core/index/schema/SolrCollectionRegistry.java
index ebd2acba4d..a2262239cd 100644
--- a/roda-core/roda-core/src/main/java/org/roda/core/index/schema/SolrCollectionRegistry.java
+++ b/roda-core/roda-core/src/main/java/org/roda/core/index/schema/SolrCollectionRegistry.java
@@ -29,6 +29,9 @@
import org.roda.core.index.schema.collections.DIPCollection;
import org.roda.core.index.schema.collections.DIPFileCollection;
import org.roda.core.index.schema.collections.DisposalConfirmationCollection;
+import org.roda.core.index.schema.collections.DisposalHoldCollection;
+import org.roda.core.index.schema.collections.DisposalRuleCollection;
+import org.roda.core.index.schema.collections.DisposalScheduleCollection;
import org.roda.core.index.schema.collections.FileCollection;
import org.roda.core.index.schema.collections.JobCollection;
import org.roda.core.index.schema.collections.JobReportCollection;
@@ -46,10 +49,6 @@
public final class SolrCollectionRegistry {
- private SolrCollectionRegistry() {
-
- }
-
private static final Map, SolrCollection extends IsIndexed, ? extends IsModelObject>> REGISTRY = new HashMap<>();
private static final Map, Class extends IsIndexed>> MODEL_TO_INDEX = new HashMap<>();
@@ -79,9 +78,16 @@ private SolrCollectionRegistry() {
register(new RepresentationInformationCollection());
+ register(new DisposalScheduleCollection());
+ register(new DisposalHoldCollection());
+ register(new DisposalRuleCollection());
register(new DisposalConfirmationCollection());
}
+ private SolrCollectionRegistry() {
+
+ }
+
public static void register(SolrCollection collection) {
REGISTRY.put(collection.getIndexClass(), collection);
MODEL_TO_INDEX.put(collection.getModelClass(), collection.getIndexClass());
@@ -138,9 +144,9 @@ public static I fromSolrDocument(Class indexClass, Solr
return fromSolrDocument(indexClass, doc, Collections.emptyList());
}
- public static SolrInputDocument toSolrDocument(Class indexClass, ModelService model,
- M object, IndexingAdditionalInfo utils) throws GenericException, NotSupportedException, RequestNotValidException,
- NotFoundException, AuthorizationDeniedException {
+ public static SolrInputDocument toSolrDocument(Class indexClass,
+ ModelService model, M object, IndexingAdditionalInfo utils) throws GenericException, NotSupportedException,
+ RequestNotValidException, NotFoundException, AuthorizationDeniedException {
SolrCollection solrCollection = get(indexClass);
if (solrCollection != null) {
return solrCollection.toSolrDocument(model, object, utils);
@@ -150,9 +156,9 @@ public static SolrInputDocument t
}
}
- public static SolrInputDocument toSolrDocument(Class indexClass, ModelService model,
- M object) throws GenericException, NotSupportedException, RequestNotValidException, NotFoundException,
- AuthorizationDeniedException {
+ public static SolrInputDocument toSolrDocument(Class indexClass,
+ ModelService model, M object) throws GenericException, NotSupportedException, RequestNotValidException,
+ NotFoundException, AuthorizationDeniedException {
return toSolrDocument(indexClass, model, object, IndexingAdditionalInfo.empty());
}
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/index/schema/collections/DisposalHoldCollection.java b/roda-core/roda-core/src/main/java/org/roda/core/index/schema/collections/DisposalHoldCollection.java
new file mode 100644
index 0000000000..f2b3457c61
--- /dev/null
+++ b/roda-core/roda-core/src/main/java/org/roda/core/index/schema/collections/DisposalHoldCollection.java
@@ -0,0 +1,108 @@
+package org.roda.core.index.schema.collections;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrInputDocument;
+import org.roda.core.data.common.RodaConstants;
+import org.roda.core.data.exceptions.AuthorizationDeniedException;
+import org.roda.core.data.exceptions.GenericException;
+import org.roda.core.data.exceptions.NotFoundException;
+import org.roda.core.data.exceptions.RequestNotValidException;
+import org.roda.core.data.v2.disposal.hold.DisposalHold;
+import org.roda.core.data.v2.disposal.hold.DisposalHoldState;
+import org.roda.core.index.IndexingAdditionalInfo;
+import org.roda.core.index.schema.AbstractSolrCollection;
+import org.roda.core.index.schema.CopyField;
+import org.roda.core.index.schema.Field;
+import org.roda.core.index.schema.SolrCollection;
+import org.roda.core.index.utils.SolrUtils;
+import org.roda.core.model.ModelService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Miguel Guimarães
+ */
+
+public class DisposalHoldCollection extends AbstractSolrCollection {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DisposalHoldCollection.class);
+
+ @Override
+ public Class getIndexClass() {
+ return DisposalHold.class;
+ }
+
+ @Override
+ public Class getModelClass() {
+ return DisposalHold.class;
+ }
+
+ @Override
+ public String getIndexName() {
+ return RodaConstants.INDEX_DISPOSAL_HOLD;
+ }
+
+ @Override
+ public List getCommitIndexNames() {
+ return Arrays.asList(RodaConstants.INDEX_DISPOSAL_HOLD);
+ }
+
+ @Override
+ public String getUniqueId(DisposalHold modelObject) {
+ return modelObject.getUUID();
+ }
+
+ @Override
+ public List getCopyFields() {
+ return Collections.singletonList(SolrCollection.getCopyAllToSearchField());
+ }
+
+ @Override
+ public List getFields() {
+ List fields = new ArrayList<>(super.getFields());
+
+ fields.add(new Field(RodaConstants.DISPOSAL_HOLD_TITLE, Field.TYPE_STRING));
+ fields.add(new Field(RodaConstants.DISPOSAL_HOLD_DESCRIPTION, Field.TYPE_STRING));
+ fields.add(new Field(RodaConstants.DISPOSAL_HOLD_MANDATE, Field.TYPE_STRING));
+ fields.add(new Field(RodaConstants.DISPOSAL_HOLD_SCOPE_NOTES, Field.TYPE_STRING));
+ fields.add(new Field(RodaConstants.DISPOSAL_HOLD_STATE, Field.TYPE_STRING));
+
+ return fields;
+ }
+
+ @Override
+ public SolrInputDocument toSolrDocument(ModelService model, DisposalHold hold, IndexingAdditionalInfo info)
+ throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
+
+ SolrInputDocument doc = super.toSolrDocument(model, hold, info);
+
+ doc.addField(RodaConstants.DISPOSAL_HOLD_TITLE, hold.getTitle());
+ doc.addField(RodaConstants.DISPOSAL_HOLD_DESCRIPTION, hold.getDescription());
+ doc.addField(RodaConstants.DISPOSAL_HOLD_MANDATE, hold.getMandate());
+ doc.addField(RodaConstants.DISPOSAL_HOLD_SCOPE_NOTES, hold.getScopeNotes());
+ doc.addField(RodaConstants.DISPOSAL_HOLD_STATE, hold.getState().toString());
+
+ return doc;
+ }
+
+ @Override
+ public DisposalHold fromSolrDocument(SolrDocument doc, List fieldsToReturn) throws GenericException {
+
+ final DisposalHold hold = super.fromSolrDocument(doc, fieldsToReturn);
+
+ hold.setTitle(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_HOLD_TITLE), null));
+ hold.setDescription(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_HOLD_DESCRIPTION), null));
+ hold.setMandate(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_HOLD_MANDATE), null));
+ hold.setScopeNotes(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_HOLD_SCOPE_NOTES), null));
+ DisposalHoldState disposalHoldState = SolrUtils.objectToEnum(doc.get(RodaConstants.DISPOSAL_HOLD_STATE),
+ DisposalHoldState.class, DisposalHoldState.getDefault());
+ hold.setState(disposalHoldState);
+
+ return hold;
+ }
+}
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/index/schema/collections/DisposalRuleCollection.java b/roda-core/roda-core/src/main/java/org/roda/core/index/schema/collections/DisposalRuleCollection.java
new file mode 100644
index 0000000000..c1344ef71d
--- /dev/null
+++ b/roda-core/roda-core/src/main/java/org/roda/core/index/schema/collections/DisposalRuleCollection.java
@@ -0,0 +1,114 @@
+package org.roda.core.index.schema.collections;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrInputDocument;
+import org.roda.core.data.common.RodaConstants;
+import org.roda.core.data.exceptions.AuthorizationDeniedException;
+import org.roda.core.data.exceptions.GenericException;
+import org.roda.core.data.exceptions.NotFoundException;
+import org.roda.core.data.exceptions.RequestNotValidException;
+import org.roda.core.data.v2.disposal.rule.ConditionType;
+import org.roda.core.data.v2.disposal.rule.DisposalRule;
+import org.roda.core.index.IndexingAdditionalInfo;
+import org.roda.core.index.schema.AbstractSolrCollection;
+import org.roda.core.index.schema.CopyField;
+import org.roda.core.index.schema.Field;
+import org.roda.core.index.schema.SolrCollection;
+import org.roda.core.index.utils.SolrUtils;
+import org.roda.core.model.ModelService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Miguel Guimarães
+ */
+
+public class DisposalRuleCollection extends AbstractSolrCollection {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DisposalRuleCollection.class);
+
+ @Override
+ public Class getIndexClass() {
+ return DisposalRule.class;
+ }
+
+ @Override
+ public Class getModelClass() {
+ return DisposalRule.class;
+ }
+
+ @Override
+ public String getIndexName() {
+ return RodaConstants.INDEX_DISPOSAL_RULE;
+ }
+
+ @Override
+ public List getCommitIndexNames() {
+ return Arrays.asList(RodaConstants.INDEX_DISPOSAL_RULE);
+ }
+
+ @Override
+ public String getUniqueId(DisposalRule modelObject) {
+ return modelObject.getUUID();
+ }
+
+ @Override
+ public List getCopyFields() {
+ return Collections.singletonList(SolrCollection.getCopyAllToSearchField());
+ }
+
+ @Override
+ public List getFields() {
+ List fields = new ArrayList<>(super.getFields());
+
+ fields.add(new Field(RodaConstants.DISPOSAL_RULE_ORDER, Field.TYPE_INT));
+ fields.add(new Field(RodaConstants.DISPOSAL_RULE_TITLE, Field.TYPE_STRING));
+ fields.add(new Field(RodaConstants.DISPOSAL_RULE_DESCRIPTION, Field.TYPE_STRING));
+ fields.add(new Field(RodaConstants.DISPOSAL_RULE_SELECTION_METHOD, Field.TYPE_STRING));
+ fields.add(new Field(RodaConstants.DISPOSAL_RULE_CONDITION_KEY, Field.TYPE_STRING));
+ fields.add(new Field(RodaConstants.DISPOSAL_RULE_CONDITION_VALUE, Field.TYPE_STRING));
+ fields.add(new Field(RodaConstants.DISPOSAL_RULE_SCHEDULE_ID, Field.TYPE_STRING));
+
+ return fields;
+ }
+
+ @Override
+ public SolrInputDocument toSolrDocument(ModelService model, DisposalRule rule, IndexingAdditionalInfo info)
+ throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
+
+ SolrInputDocument doc = super.toSolrDocument(model, rule, info);
+
+ doc.addField(RodaConstants.DISPOSAL_RULE_ORDER, rule.getOrder());
+ doc.addField(RodaConstants.DISPOSAL_RULE_TITLE, rule.getTitle());
+ doc.addField(RodaConstants.DISPOSAL_RULE_DESCRIPTION, rule.getDescription());
+ doc.addField(RodaConstants.DISPOSAL_RULE_SELECTION_METHOD, rule.getType().toString());
+ doc.addField(RodaConstants.DISPOSAL_RULE_CONDITION_KEY, rule.getConditionKey());
+ doc.addField(RodaConstants.DISPOSAL_RULE_CONDITION_VALUE, rule.getConditionValue());
+ doc.addField(RodaConstants.DISPOSAL_RULE_SCHEDULE_ID, rule.getDisposalScheduleId());
+
+ return doc;
+ }
+
+ @Override
+ public DisposalRule fromSolrDocument(SolrDocument doc, List fieldsToReturn) throws GenericException {
+
+ final DisposalRule rule = super.fromSolrDocument(doc, fieldsToReturn);
+
+ rule.setOrder(SolrUtils.objectToInteger(doc.get(RodaConstants.DISPOSAL_RULE_ORDER), null));
+ rule.setTitle(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_RULE_TITLE), null));
+ rule.setDescription(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_RULE_DESCRIPTION), null));
+ rule.setConditionKey(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_RULE_CONDITION_KEY), null));
+ rule.setConditionValue(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_RULE_CONDITION_VALUE), null));
+ rule.setDisposalScheduleId(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_RULE_SCHEDULE_ID), null));
+ ConditionType conditionType = SolrUtils.objectToEnum(doc.get(RodaConstants.DISPOSAL_RULE_SELECTION_METHOD),
+ ConditionType.class, ConditionType.getDefault());
+ rule.setType(conditionType);
+
+ return rule;
+ }
+}
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/index/schema/collections/DisposalScheduleCollection.java b/roda-core/roda-core/src/main/java/org/roda/core/index/schema/collections/DisposalScheduleCollection.java
new file mode 100644
index 0000000000..462e356f22
--- /dev/null
+++ b/roda-core/roda-core/src/main/java/org/roda/core/index/schema/collections/DisposalScheduleCollection.java
@@ -0,0 +1,132 @@
+package org.roda.core.index.schema.collections;
+
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrInputDocument;
+import org.roda.core.data.common.RodaConstants;
+import org.roda.core.data.exceptions.AuthorizationDeniedException;
+import org.roda.core.data.exceptions.GenericException;
+import org.roda.core.data.exceptions.NotFoundException;
+import org.roda.core.data.exceptions.RequestNotValidException;
+import org.roda.core.data.v2.disposal.schedule.DisposalActionCode;
+import org.roda.core.data.v2.disposal.schedule.DisposalSchedule;
+import org.roda.core.data.v2.disposal.schedule.DisposalScheduleState;
+import org.roda.core.data.v2.disposal.schedule.RetentionPeriodIntervalCode;
+import org.roda.core.index.IndexingAdditionalInfo;
+import org.roda.core.index.schema.AbstractSolrCollection;
+import org.roda.core.index.schema.CopyField;
+import org.roda.core.index.schema.Field;
+import org.roda.core.index.schema.SolrCollection;
+import org.roda.core.index.utils.SolrUtils;
+import org.roda.core.model.ModelService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author Miguel Guimarães
+ */
+
+public class DisposalScheduleCollection extends AbstractSolrCollection {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DisposalScheduleCollection.class);
+
+ @Override
+ public Class getIndexClass() {
+ return DisposalSchedule.class;
+ }
+
+ @Override
+ public Class getModelClass() {
+ return DisposalSchedule.class;
+ }
+
+ @Override
+ public String getIndexName() {
+ return RodaConstants.INDEX_DISPOSAL_SCHEDULE;
+ }
+
+ @Override
+ public List getCommitIndexNames() {
+ return Arrays.asList(RodaConstants.INDEX_DISPOSAL_SCHEDULE);
+ }
+
+ @Override
+ public String getUniqueId(DisposalSchedule modelObject) {
+ return modelObject.getUUID();
+ }
+
+ @Override
+ public List getCopyFields() {
+ return Collections.singletonList(SolrCollection.getCopyAllToSearchField());
+ }
+
+ @Override
+ public List getFields() {
+ List fields = new ArrayList<>(super.getFields());
+
+ fields.add(new Field(RodaConstants.DISPOSAL_SCHEDULE_TITLE, Field.TYPE_STRING));
+ fields.add(new Field(RodaConstants.DISPOSAL_SCHEDULE_DESCRIPTION, Field.TYPE_STRING));
+ fields.add(new Field(RodaConstants.DISPOSAL_SCHEDULE_MANDATE, Field.TYPE_STRING));
+ fields.add(new Field(RodaConstants.DISPOSAL_SCHEDULE_SCOPE_NOTES, Field.TYPE_STRING));
+ fields.add(new Field(RodaConstants.DISPOSAL_SCHEDULE_STATE, Field.TYPE_STRING));
+ fields.add(new Field(RodaConstants.DISPOSAL_SCHEDULE_ACTION, Field.TYPE_STRING));
+ fields.add(new Field(RodaConstants.DISPOSAL_SCHEDULE_RETENTION_PERIOD_INTERVAL_CODE, Field.TYPE_STRING));
+ fields.add(new Field(RodaConstants.DISPOSAL_SCHEDULE_RETENTION_PERIOD_DURATION, Field.TYPE_INT));
+
+ return fields;
+ }
+
+ @Override
+ public SolrInputDocument toSolrDocument(ModelService model, DisposalSchedule disposalSchedule,
+ IndexingAdditionalInfo info)
+ throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
+
+ SolrInputDocument doc = super.toSolrDocument(model, disposalSchedule, info);
+
+ doc.addField(RodaConstants.DISPOSAL_SCHEDULE_TITLE, disposalSchedule.getTitle());
+ doc.addField(RodaConstants.DISPOSAL_SCHEDULE_DESCRIPTION, disposalSchedule.getDescription());
+ doc.addField(RodaConstants.DISPOSAL_SCHEDULE_MANDATE, disposalSchedule.getMandate());
+ doc.addField(RodaConstants.DISPOSAL_SCHEDULE_SCOPE_NOTES, disposalSchedule.getScopeNotes());
+ if (!DisposalActionCode.RETAIN_PERMANENTLY.equals(disposalSchedule.getActionCode())) {
+ doc.addField(RodaConstants.DISPOSAL_SCHEDULE_RETENTION_PERIOD_INTERVAL_CODE,
+ disposalSchedule.getRetentionPeriodIntervalCode().toString());
+ doc.addField(RodaConstants.DISPOSAL_SCHEDULE_RETENTION_PERIOD_DURATION,
+ disposalSchedule.getRetentionPeriodDuration());
+ }
+ doc.addField(RodaConstants.DISPOSAL_SCHEDULE_STATE, disposalSchedule.getState().toString());
+ doc.addField(RodaConstants.DISPOSAL_SCHEDULE_ACTION, disposalSchedule.getActionCode().toString());
+
+ return doc;
+ }
+
+ @Override
+ public DisposalSchedule fromSolrDocument(SolrDocument doc, List fieldsToReturn) throws GenericException {
+
+ final DisposalSchedule disposalSchedule = super.fromSolrDocument(doc, fieldsToReturn);
+
+ disposalSchedule.setTitle(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_SCHEDULE_TITLE), null));
+ disposalSchedule
+ .setDescription(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_SCHEDULE_DESCRIPTION), null));
+ disposalSchedule.setMandate(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_SCHEDULE_MANDATE), null));
+ disposalSchedule
+ .setScopeNotes(SolrUtils.objectToString(doc.get(RodaConstants.DISPOSAL_SCHEDULE_SCOPE_NOTES), null));
+ DisposalScheduleState disposalScheduleState = SolrUtils.objectToEnum(doc.get(RodaConstants.DISPOSAL_SCHEDULE_STATE),
+ DisposalScheduleState.class, DisposalScheduleState.getDefault());
+ disposalSchedule.setState(disposalScheduleState);
+ disposalSchedule.setRetentionPeriodDuration(
+ SolrUtils.objectToInteger(doc.get(RodaConstants.DISPOSAL_SCHEDULE_RETENTION_PERIOD_DURATION), null));
+ RetentionPeriodIntervalCode retentionPeriodIntervalCode = SolrUtils.objectToEnum(
+ doc.get(RodaConstants.DISPOSAL_SCHEDULE_RETENTION_PERIOD_INTERVAL_CODE), RetentionPeriodIntervalCode.class,
+ RetentionPeriodIntervalCode.getDefault());
+ disposalSchedule.setRetentionPeriodIntervalCode(retentionPeriodIntervalCode);
+ DisposalActionCode actionCode = SolrUtils.objectToEnum(doc.get(RodaConstants.DISPOSAL_SCHEDULE_ACTION),
+ DisposalActionCode.class, null);
+ disposalSchedule.setActionCode(actionCode);
+
+ return disposalSchedule;
+ }
+}
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/model/DefaultModelService.java b/roda-core/roda-core/src/main/java/org/roda/core/model/DefaultModelService.java
index 7da4af2b04..20116411ad 100644
--- a/roda-core/roda-core/src/main/java/org/roda/core/model/DefaultModelService.java
+++ b/roda-core/roda-core/src/main/java/org/roda/core/model/DefaultModelService.java
@@ -52,6 +52,7 @@
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.roda.core.RodaCoreFactory;
+import org.roda.core.common.CryptographyUtils;
import org.roda.core.common.JwtUtils;
import org.roda.core.common.PremisV3Utils;
import org.roda.core.common.ProvidesInputStream;
@@ -214,6 +215,32 @@ public class DefaultModelService implements ModelService {
private JobRepository jobRepository;
private ReportRepository reportRepository;
+ public DefaultModelService(StorageService storage, EventsManager eventsManager, NodeType nodeType,
+ String instanceId) {
+ this.storage = storage;
+ this.eventsManager = eventsManager;
+ this.nodeType = nodeType;
+ this.instanceId = instanceId;
+ this.observers = new ArrayList<>();
+
+ if (RodaCoreFactory.checkIfWriteIsAllowed(nodeType)) {
+ ensureAllContainersExist();
+ ensureAllDirectoriesExist();
+ }
+ }
+
+ private static void clearSpecificIndexes(IndexService index, Class objectClass,
+ IsModelObject rodaObject) throws AuthorizationDeniedException {
+ if (AIP.class.equals(objectClass)) {
+ List ids = Arrays.asList(rodaObject.getId());
+ index.delete(IndexedRepresentation.class,
+ new Filter(new OneOfManyFilterParameter(RodaConstants.REPRESENTATION_AIP_ID, ids)));
+ index.delete(IndexedFile.class, new Filter(new OneOfManyFilterParameter(RodaConstants.FILE_AIP_ID, ids)));
+ index.delete(IndexedPreservationEvent.class,
+ new Filter(new OneOfManyFilterParameter(RodaConstants.PRESERVATION_EVENT_AIP_ID, ids)));
+ }
+ }
+
/**
* Lazily retrieves the JobRepository bean from Spring context.
*/
@@ -241,32 +268,6 @@ private boolean isJpaAvailable() {
return SpringContext.isContextInitialized();
}
- public DefaultModelService(StorageService storage, EventsManager eventsManager, NodeType nodeType,
- String instanceId) {
- this.storage = storage;
- this.eventsManager = eventsManager;
- this.nodeType = nodeType;
- this.instanceId = instanceId;
- this.observers = new ArrayList<>();
-
- if (RodaCoreFactory.checkIfWriteIsAllowed(nodeType)) {
- ensureAllContainersExist();
- ensureAllDirectoriesExist();
- }
- }
-
- private static void clearSpecificIndexes(IndexService index, Class objectClass,
- IsModelObject rodaObject) throws AuthorizationDeniedException {
- if (AIP.class.equals(objectClass)) {
- List ids = Arrays.asList(rodaObject.getId());
- index.delete(IndexedRepresentation.class,
- new Filter(new OneOfManyFilterParameter(RodaConstants.REPRESENTATION_AIP_ID, ids)));
- index.delete(IndexedFile.class, new Filter(new OneOfManyFilterParameter(RodaConstants.FILE_AIP_ID, ids)));
- index.delete(IndexedPreservationEvent.class,
- new Filter(new OneOfManyFilterParameter(RodaConstants.PRESERVATION_EVENT_AIP_ID, ids)));
- }
- }
-
private void ensureAllContainersExist() {
try {
createContainerIfNotExists(RodaConstants.STORAGE_CONTAINER_AIP);
@@ -2805,7 +2806,40 @@ public Group updateGroup(final Group group, boolean notify, boolean isHandlingEv
} catch (IllegalOperationException e) {
throw new AuthorizationDeniedException("Illegal operation", e);
}
+ }
+
+ @Override
+ public Group updateGroupMembers(String id, Set members, boolean notify, boolean isHandlingEvent)
+ throws AuthorizationDeniedException, NotFoundException, GenericException {
+ boolean writeIsAllowed = RodaCoreFactory.checkIfWriteIsAllowed(nodeType);
+
+ if (!writeIsAllowed && !isHandlingEvent) {
+ RodaCoreFactory.throwExceptionIfWriteIsNotAllowed();
+ }
+ Group group = UserUtility.getLdapUtility().getGroup(id);
+ group.getUsers().addAll(members);
+ for (String member : members) {
+ User user = UserUtility.getLdapUtility().getUser(member);
+ user.getGroups().add(id);
+ notifyUserUpdated(user).failOnError();
+ }
+
+ try {
+ Group updatedGroup = UserUtility.getLdapUtility().modifyGroupMembers(group);
+ if (notify && writeIsAllowed) {
+ notifyGroupUpdated(updatedGroup).failOnError();
+ }
+
+ if (!isHandlingEvent) {
+ // FIXME 20180813 hsilva: group is not the previous state of the group
+ eventsManager.notifyGroupUpdated(this, group, updatedGroup);
+ }
+
+ return updatedGroup;
+ } catch (IllegalOperationException e) {
+ throw new AuthorizationDeniedException("Illegal operation", e);
+ }
}
@Override
@@ -2942,8 +2976,8 @@ public void createOrUpdateJob(Job job)
}
/**
- * Flushes a job and its reports from the database to the file storage.
- * This method is called when a job reaches a final state.
+ * Flushes a job and its reports from the database to the file storage. This
+ * method is called when a job reaches a final state.
*/
private void flushJobToStorage(Job job)
throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
@@ -3008,8 +3042,7 @@ public CloseableIterable> listJobReports(String jobId)
if (jobRepo != null && reportRepo != null && jobRepo.existsById(jobId)) {
// Return reports from database
List dbReports = reportRepo.findByJobId(jobId);
- List> wrappedReports = dbReports.stream()
- .map(OptionalWithCause::of)
+ List> wrappedReports = dbReports.stream().map(OptionalWithCause::of)
.collect(Collectors.toList());
return CloseableIterables.fromList(wrappedReports);
}
@@ -4081,13 +4114,13 @@ public CloseableIterable> list(Cla
} else if (DescriptiveMetadata.class.equals(objectClass)) {
ret = listDescriptiveMetadata();
} else if (Report.class.equals(objectClass)) {
- // Include both DB reports (for running jobs) and storage reports (for completed jobs)
+ // Include both DB reports (for running jobs) and storage reports (for completed
+ // jobs)
CloseableIterable> storageReports = ResourceParseUtils.convert(getStorage(),
listReportResources(), Report.class);
if (isJpaAvailable() && getReportRepository() != null) {
List dbReports = getReportRepository().findAll();
- List> wrappedDbReports = dbReports.stream()
- .map(OptionalWithCause::of)
+ List> wrappedDbReports = dbReports.stream().map(OptionalWithCause::of)
.collect(Collectors.toList());
CloseableIterable> dbIterable = CloseableIterables.fromList(wrappedDbReports);
ret = CloseableIterables.concat(dbIterable, storageReports);
@@ -4102,8 +4135,7 @@ public CloseableIterable> list(Cla
resourcesIterable, Job.class);
if (isJpaAvailable() && getJobRepository() != null) {
List dbJobs = getJobRepository().findAll();
- List> wrappedDbJobs = dbJobs.stream()
- .map(OptionalWithCause::of)
+ List> wrappedDbJobs = dbJobs.stream().map(OptionalWithCause::of)
.collect(Collectors.toList());
CloseableIterable> dbIterable = CloseableIterables.fromList(wrappedDbJobs);
ret = CloseableIterables.concat(dbIterable, storageJobs);
@@ -4144,13 +4176,13 @@ public CloseableIterable> storageReports = ResourceParseUtils.convertLite(getStorage(),
listReportResources(), objectClass);
if (isJpaAvailable() && getReportRepository() != null) {
List dbReports = getReportRepository().findAll();
- List> wrappedDbReports = dbReports.stream()
- .map(OptionalWithCause::of)
+ List> wrappedDbReports = dbReports.stream().map(OptionalWithCause::of)
.collect(Collectors.toList());
CloseableIterable> dbIterable = LiteRODAObjectFactory
.transformIntoLite(CloseableIterables.fromList(wrappedDbReports));
@@ -4171,8 +4203,7 @@ public CloseableIterable dbJobs = getJobRepository().findAll();
- List> wrappedDbJobs = dbJobs.stream()
- .map(OptionalWithCause::of)
+ List> wrappedDbJobs = dbJobs.stream().map(OptionalWithCause::of)
.collect(Collectors.toList());
CloseableIterable> dbIterable = LiteRODAObjectFactory
.transformIntoLite(CloseableIterables.fromList(wrappedDbJobs));
@@ -4479,6 +4510,7 @@ public DisposalHold createDisposalHold(DisposalHold disposalHold, String created
String disposalHoldAsJson = JsonUtils.getJsonFromObject(newDisposalHold);
StoragePath disposalHoldPath = ModelUtils.getDisposalHoldStoragePath(newDisposalHold.getId());
storage.createBinary(disposalHoldPath, new StringContentPayload(disposalHoldAsJson), false);
+ notifyDisposalHoldCreatedOrUpdated(newDisposalHold).failOnError();
return newDisposalHold;
}
@@ -4529,6 +4561,7 @@ public DisposalHold updateDisposalHold(DisposalHold disposalHold, String updated
storage.updateBinaryContent(disposalHoldPath, new StringContentPayload(disposalHoldAsJson), false, true, false,
null);
+ notifyDisposalHoldCreatedOrUpdated(currentDisposalHold).failOnError();
createRepositoryEvent(PreservationEventType.UPDATE, "Update disposal hold", PluginState.SUCCESS, "", details, "",
true, null);
@@ -4546,6 +4579,7 @@ public void deleteDisposalHold(String disposalHoldId) throws RequestNotValidExce
if (disposalHold.getFirstTimeUsed() == null) {
StoragePath disposalHoldPath = ModelUtils.getDisposalHoldStoragePath(disposalHold.getId());
storage.deleteResource(disposalHoldPath);
+ notifyDisposalHoldDeleted(disposalHoldId, false);
} else {
throw new IllegalOperationException("Error deleting disposal hold: " + disposalHold.getId()
+ ". Reason: One or more AIPs where associated under this disposal hold");
@@ -4678,6 +4712,7 @@ public DisposalSchedule createDisposalSchedule(DisposalSchedule disposalSchedule
String disposalScheduleAsJson = JsonUtils.getJsonFromObject(disposalSchedule);
StoragePath disposalSchedulePath = ModelUtils.getDisposalScheduleStoragePath(disposalSchedule.getId());
storage.createBinary(disposalSchedulePath, new StringContentPayload(disposalScheduleAsJson), false);
+ notifyDisposalScheduleCreatedOrUpdated(disposalSchedule).failOnError();
return disposalSchedule;
}
@@ -4723,6 +4758,7 @@ public DisposalSchedule updateDisposalSchedule(DisposalSchedule disposalSchedule
StoragePath disposalSchedulePath = ModelUtils.getDisposalScheduleStoragePath(currentDisposalSchedule.getId());
storage.updateBinaryContent(disposalSchedulePath, new StringContentPayload(disposalScheduleAsJson), false, false,
false, null);
+ notifyDisposalScheduleCreatedOrUpdated(currentDisposalSchedule).failOnError();
return currentDisposalSchedule;
}
@@ -4782,7 +4818,7 @@ public void deleteDisposalSchedule(String disposalScheduleId) throws NotFoundExc
// if so, block the action and keep the disposal schedule
if (retrieveDisposalSchedule(disposalScheduleId).getFirstTimeUsed() != null) {
throw new IllegalOperationException("Error deleting disposal schedule: " + disposalScheduleId
- + ". Reason: One or more AIPs where destroyed under this disposal schedule");
+ + ". Reason: This disposal schedule was already associated to an AIP or used to destroy on ore more AIP");
}
// check if the disposal schedule is being used in a disposal rule
@@ -4800,6 +4836,7 @@ public void deleteDisposalSchedule(String disposalScheduleId) throws NotFoundExc
}
storage.deleteResource(disposalSchedulePath);
+ notifyDisposalScheduleDeleted(disposalScheduleId, false).failOnError();
}
/**********************************
@@ -5048,6 +5085,7 @@ public DisposalRule createDisposalRule(DisposalRule disposalRule, String created
String disposalRuleAsJson = JsonUtils.getJsonFromObject(disposalRule);
StoragePath disposalRulePath = ModelUtils.getDisposalRuleStoragePath(disposalRule.getId());
storage.createBinary(disposalRulePath, new StringContentPayload(disposalRuleAsJson), false);
+ notifyDisposalRuleCreatedOrUpdated(disposalRule).failOnError();
return disposalRule;
}
@@ -5064,6 +5102,7 @@ public DisposalRule updateDisposalRule(DisposalRule disposalRule, String updated
StoragePath disposalRulePath = ModelUtils.getDisposalRuleStoragePath(disposalRule.getId());
storage.updateBinaryContent(disposalRulePath, new StringContentPayload(disposalRuleAsJson), false, false, false,
null);
+ notifyDisposalRuleCreatedOrUpdated(disposalRule).failOnError();
return disposalRule;
}
@@ -5075,6 +5114,7 @@ public void deleteDisposalRule(String disposalRuleId, String updatedBy)
StoragePath disposalRulePath = ModelUtils.getDisposalRuleStoragePath(disposalRuleId);
storage.deleteResource(disposalRulePath);
+ notifyDisposalRuleDeleted(disposalRuleId, false);
DisposalRules disposalRules = listDisposalRules();
int index = 0;
@@ -5266,9 +5306,10 @@ public AccessKey createAccessKey(AccessKey accessKey, String createdBy) throws G
accessKey.setExpirationDate(expirationDate);
}
- String token = JwtUtils.generateToken(accessKey.getUserName(), accessKey.getExpirationDate());
+ String plainTextToken = JwtUtils.generateToken(accessKey.getUserName(), accessKey.getExpirationDate());
- accessKey.setKey(token);
+ String hashedToken = CryptographyUtils.hashTokenSHA256(plainTextToken);
+ accessKey.setKey(hashedToken);
accessKey.setCreatedOn(new Date());
accessKey.setCreatedBy(createdBy);
@@ -5279,6 +5320,7 @@ public AccessKey createAccessKey(AccessKey accessKey, String createdBy) throws G
StoragePath accessKeyPath = ModelUtils.getAccessKeysStoragePath(accessKey.getId());
storage.createBinary(accessKeyPath, new StringContentPayload(accessKeyAsJson), false);
+ accessKey.setKey(plainTextToken);
return accessKey;
}
@@ -5730,6 +5772,36 @@ public ReturnWithExceptionsWrapper notifyDisposalConfirmationDeleted(String disp
return notifyObserversSafely(observer -> observer.disposalConfirmationDeleted(disposalConfirmationId, commit));
}
+ @Override
+ public ReturnWithExceptionsWrapper notifyDisposalScheduleCreatedOrUpdated(DisposalSchedule schedule) {
+ return notifyObserversSafely(observer -> observer.disposalScheduleCreatedOrUpdated(schedule));
+ }
+
+ @Override
+ public ReturnWithExceptionsWrapper notifyDisposalScheduleDeleted(String disposalScheduleId, boolean commit) {
+ return notifyObserversSafely(observer -> observer.disposalScheduleDeleted(disposalScheduleId, commit));
+ }
+
+ @Override
+ public ReturnWithExceptionsWrapper notifyDisposalHoldCreatedOrUpdated(DisposalHold hold) {
+ return notifyObserversSafely(observer -> observer.disposalHoldCreatedOrUpdated(hold));
+ }
+
+ @Override
+ public ReturnWithExceptionsWrapper notifyDisposalHoldDeleted(String disposalHoldId, boolean commit) {
+ return notifyObserversSafely(observer -> observer.disposalHoldDeleted(disposalHoldId, commit));
+ }
+
+ @Override
+ public ReturnWithExceptionsWrapper notifyDisposalRuleCreatedOrUpdated(DisposalRule rule) {
+ return notifyObserversSafely(observer -> observer.disposalRuleCreatedOrUpdated(rule));
+ }
+
+ @Override
+ public ReturnWithExceptionsWrapper notifyDisposalRuleDeleted(String disposalRuleId, boolean commit) {
+ return notifyObserversSafely(observer -> observer.disposalRuleDeleted(disposalRuleId, commit));
+ }
+
/************************************
* Storage Utils
************************************/
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/model/DefaultTransactionalModelService.java b/roda-core/roda-core/src/main/java/org/roda/core/model/DefaultTransactionalModelService.java
index 14c4e31a3d..32ab73b5e7 100644
--- a/roda-core/roda-core/src/main/java/org/roda/core/model/DefaultTransactionalModelService.java
+++ b/roda-core/roda-core/src/main/java/org/roda/core/model/DefaultTransactionalModelService.java
@@ -15,6 +15,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import org.roda.core.common.ReturnWithExceptionsWrapper;
import org.roda.core.common.iterables.CloseableIterable;
@@ -2038,6 +2039,12 @@ public Group updateGroup(Group group, boolean notify, boolean isHandlingEvent)
return mainModelService.updateGroup(group, notify, isHandlingEvent);
}
+ @Override
+ public Group updateGroupMembers(String id, Set members, boolean notify, boolean isHandlingEvent)
+ throws GenericException, NotFoundException, AuthorizationDeniedException {
+ return mainModelService.updateGroupMembers(id, members, notify, isHandlingEvent);
+ }
+
@Override
public void deleteGroup(String id, boolean notify) throws GenericException, AuthorizationDeniedException {
mainModelService.deleteGroup(id, notify);
@@ -4483,6 +4490,36 @@ public ReturnWithExceptionsWrapper notifyDisposalConfirmationDeleted(String disp
return getModelService().notifyDisposalConfirmationDeleted(disposalConfirmationId, commit);
}
+ @Override
+ public ReturnWithExceptionsWrapper notifyDisposalScheduleCreatedOrUpdated(DisposalSchedule schedule) {
+ return getModelService().notifyDisposalScheduleCreatedOrUpdated(schedule);
+ }
+
+ @Override
+ public ReturnWithExceptionsWrapper notifyDisposalScheduleDeleted(String disposalScheduleId, boolean commit) {
+ return getModelService().notifyDisposalScheduleDeleted(disposalScheduleId, commit);
+ }
+
+ @Override
+ public ReturnWithExceptionsWrapper notifyDisposalHoldCreatedOrUpdated(DisposalHold hold) {
+ return getModelService().notifyDisposalHoldCreatedOrUpdated(hold);
+ }
+
+ @Override
+ public ReturnWithExceptionsWrapper notifyDisposalHoldDeleted(String disposalHoldId, boolean commit) {
+ return getModelService().notifyDisposalHoldDeleted(disposalHoldId, commit);
+ }
+
+ @Override
+ public ReturnWithExceptionsWrapper notifyDisposalRuleCreatedOrUpdated(DisposalRule rule) {
+ return getModelService().notifyDisposalRuleCreatedOrUpdated(rule);
+ }
+
+ @Override
+ public ReturnWithExceptionsWrapper notifyDisposalRuleDeleted(String disposalRuleId, boolean commit) {
+ return getModelService().notifyDisposalRuleDeleted(disposalRuleId, commit);
+ }
+
@Override
public void commit() throws RODATransactionException {
for (TransactionalModelOperationLog modelOperation : transactionLogService
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/model/LiteRODAObjectFactory.java b/roda-core/roda-core/src/main/java/org/roda/core/model/LiteRODAObjectFactory.java
index 906fe14ea8..c21b9b4a88 100644
--- a/roda-core/roda-core/src/main/java/org/roda/core/model/LiteRODAObjectFactory.java
+++ b/roda-core/roda-core/src/main/java/org/roda/core/model/LiteRODAObjectFactory.java
@@ -195,6 +195,12 @@ public static Optional get(T object) {
ret = getIndexedPreservationAgent(object);
} else if (object instanceof DisposalHold) {
ret = get(DisposalHold.class, Arrays.asList(object.getId()), false);
+ } else if (object instanceof DisposalSchedule) {
+ ret = get(DisposalSchedule.class, Arrays.asList(object.getId()), false);
+ } else if (object instanceof DisposalHold) {
+ ret = get(DisposalHold.class, Arrays.asList(object.getId()), false);
+ } else if (object instanceof DisposalRule) {
+ ret = get(DisposalRule.class, Arrays.asList(object.getId()), false);
}
if (!ret.isPresent()) {
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/model/ModelObservable.java b/roda-core/roda-core/src/main/java/org/roda/core/model/ModelObservable.java
index bbfb2f6823..5ac3ccf738 100644
--- a/roda-core/roda-core/src/main/java/org/roda/core/model/ModelObservable.java
+++ b/roda-core/roda-core/src/main/java/org/roda/core/model/ModelObservable.java
@@ -9,6 +9,9 @@
import org.roda.core.common.ReturnWithExceptionsWrapper;
import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation;
+import org.roda.core.data.v2.disposal.hold.DisposalHold;
+import org.roda.core.data.v2.disposal.rule.DisposalRule;
+import org.roda.core.data.v2.disposal.schedule.DisposalSchedule;
import org.roda.core.data.v2.ip.AIP;
import org.roda.core.data.v2.ip.DIP;
import org.roda.core.data.v2.ip.DIPFile;
@@ -34,123 +37,133 @@
* @author Gabriel Barros
*/
public interface ModelObservable {
- void addModelObserver(ModelObserver observer);
+ void addModelObserver(ModelObserver observer);
- void removeModelObserver(ModelObserver observer);
+ void removeModelObserver(ModelObserver observer);
- ReturnWithExceptionsWrapper notifyAipCreated(AIP aip);
+ ReturnWithExceptionsWrapper notifyAipCreated(AIP aip);
- ReturnWithExceptionsWrapper notifyAipUpdated(AIP aip);
+ ReturnWithExceptionsWrapper notifyAipUpdated(AIP aip);
- ReturnWithExceptionsWrapper notifyAipUpdatedOnChanged(AIP aip);
+ ReturnWithExceptionsWrapper notifyAipUpdatedOnChanged(AIP aip);
- ReturnWithExceptionsWrapper notifyAipOnHoldStatusUpdated(AIP aip, boolean status);
+ ReturnWithExceptionsWrapper notifyAipOnHoldStatusUpdated(AIP aip, boolean status);
- ReturnWithExceptionsWrapper notifyAipDestroyed(AIP aip);
+ ReturnWithExceptionsWrapper notifyAipDestroyed(AIP aip);
- ReturnWithExceptionsWrapper notifyAipMoved(AIP aip, String oldParentId, String newParentId);
+ ReturnWithExceptionsWrapper notifyAipMoved(AIP aip, String oldParentId, String newParentId);
- ReturnWithExceptionsWrapper notifyAipStateUpdated(AIP aip);
+ ReturnWithExceptionsWrapper notifyAipStateUpdated(AIP aip);
- ReturnWithExceptionsWrapper notifyAipInstanceIdUpdated(AIP aip);
+ ReturnWithExceptionsWrapper notifyAipInstanceIdUpdated(AIP aip);
- ReturnWithExceptionsWrapper notifyAipDeleted(String aipId);
+ ReturnWithExceptionsWrapper notifyAipDeleted(String aipId);
- ReturnWithExceptionsWrapper notifyDescriptiveMetadataCreated(DescriptiveMetadata descriptiveMetadata);
+ ReturnWithExceptionsWrapper notifyDescriptiveMetadataCreated(DescriptiveMetadata descriptiveMetadata);
- ReturnWithExceptionsWrapper notifyDescriptiveMetadataUpdated(DescriptiveMetadata descriptiveMetadata);
+ ReturnWithExceptionsWrapper notifyDescriptiveMetadataUpdated(DescriptiveMetadata descriptiveMetadata);
- ReturnWithExceptionsWrapper notifyDescriptiveMetadataDeleted(String aipId, String representationId,
- String descriptiveMetadataBinaryId);
+ ReturnWithExceptionsWrapper notifyDescriptiveMetadataDeleted(String aipId, String representationId,
+ String descriptiveMetadataBinaryId);
- ReturnWithExceptionsWrapper notifyRepresentationCreated(Representation representation);
+ ReturnWithExceptionsWrapper notifyRepresentationCreated(Representation representation);
- ReturnWithExceptionsWrapper notifyRepresentationUpdated(Representation representation);
+ ReturnWithExceptionsWrapper notifyRepresentationUpdated(Representation representation);
- ReturnWithExceptionsWrapper notifyRepresentationDeleted(String aipId, String representationId);
+ ReturnWithExceptionsWrapper notifyRepresentationDeleted(String aipId, String representationId);
- ReturnWithExceptionsWrapper notifyRepresentationUpdatedOnChanged(Representation representation);
+ ReturnWithExceptionsWrapper notifyRepresentationUpdatedOnChanged(Representation representation);
- ReturnWithExceptionsWrapper notifyFileCreated(File file);
+ ReturnWithExceptionsWrapper notifyFileCreated(File file);
- ReturnWithExceptionsWrapper notifyFileUpdated(File file);
+ ReturnWithExceptionsWrapper notifyFileUpdated(File file);
- ReturnWithExceptionsWrapper notifyFileDeleted(String aipId, String representationId,
- List fileDirectoryPath, String fileId);
+ ReturnWithExceptionsWrapper notifyFileDeleted(String aipId, String representationId, List fileDirectoryPath,
+ String fileId);
- ReturnWithExceptionsWrapper notifyLogEntryCreated(LogEntry entry);
+ ReturnWithExceptionsWrapper notifyLogEntryCreated(LogEntry entry);
- ReturnWithExceptionsWrapper notifyUserCreated(User user);
+ ReturnWithExceptionsWrapper notifyUserCreated(User user);
- ReturnWithExceptionsWrapper notifyUserUpdated(User user);
+ ReturnWithExceptionsWrapper notifyUserUpdated(User user);
- ReturnWithExceptionsWrapper notifyUserDeleted(String userID);
+ ReturnWithExceptionsWrapper notifyUserDeleted(String userID);
- ReturnWithExceptionsWrapper notifyGroupCreated(Group group);
+ ReturnWithExceptionsWrapper notifyGroupCreated(Group group);
- ReturnWithExceptionsWrapper notifyGroupUpdated(Group group);
+ ReturnWithExceptionsWrapper notifyGroupUpdated(Group group);
- ReturnWithExceptionsWrapper notifyGroupDeleted(String groupID);
+ ReturnWithExceptionsWrapper notifyGroupDeleted(String groupID);
- ReturnWithExceptionsWrapper notifyPreservationMetadataCreated(
- PreservationMetadata preservationMetadataBinary);
+ ReturnWithExceptionsWrapper notifyPreservationMetadataCreated(PreservationMetadata preservationMetadataBinary);
- ReturnWithExceptionsWrapper notifyPreservationMetadataUpdated(
- PreservationMetadata preservationMetadataBinary);
+ ReturnWithExceptionsWrapper notifyPreservationMetadataUpdated(PreservationMetadata preservationMetadataBinary);
- ReturnWithExceptionsWrapper notifyPreservationMetadataDeleted(PreservationMetadata pm);
+ ReturnWithExceptionsWrapper notifyPreservationMetadataDeleted(PreservationMetadata pm);
- ReturnWithExceptionsWrapper notifyOtherMetadataCreated(OtherMetadata otherMetadataBinary);
+ ReturnWithExceptionsWrapper notifyOtherMetadataCreated(OtherMetadata otherMetadataBinary);
- ReturnWithExceptionsWrapper notifyJobCreatedOrUpdated(Job job, boolean reindexJobReports);
+ ReturnWithExceptionsWrapper notifyJobCreatedOrUpdated(Job job, boolean reindexJobReports);
- ReturnWithExceptionsWrapper notifyJobDeleted(String jobId);
+ ReturnWithExceptionsWrapper notifyJobDeleted(String jobId);
- ReturnWithExceptionsWrapper notifyJobReportCreatedOrUpdated(Report jobReport, Job cachedJob);
+ ReturnWithExceptionsWrapper notifyJobReportCreatedOrUpdated(Report jobReport, Job cachedJob);
- ReturnWithExceptionsWrapper notifyJobReportCreatedOrUpdated(Report jobReport, IndexedJob indexedJob);
+ ReturnWithExceptionsWrapper notifyJobReportCreatedOrUpdated(Report jobReport, IndexedJob indexedJob);
- ReturnWithExceptionsWrapper notifyJobReportDeleted(String jobReportId);
+ ReturnWithExceptionsWrapper notifyJobReportDeleted(String jobReportId);
- ReturnWithExceptionsWrapper notifyAipPermissionsUpdated(AIP aip);
+ ReturnWithExceptionsWrapper notifyAipPermissionsUpdated(AIP aip);
- ReturnWithExceptionsWrapper notifyDipPermissionsUpdated(DIP dip);
+ ReturnWithExceptionsWrapper notifyDipPermissionsUpdated(DIP dip);
- ReturnWithExceptionsWrapper notifyDipInstanceIdUpdated(DIP dip);
+ ReturnWithExceptionsWrapper notifyDipInstanceIdUpdated(DIP dip);
- ReturnWithExceptionsWrapper notifyTransferredResourceDeleted(String transferredResourceID);
+ ReturnWithExceptionsWrapper notifyTransferredResourceDeleted(String transferredResourceID);
- ReturnWithExceptionsWrapper notifyRiskCreatedOrUpdated(Risk risk, int incidences, boolean commit);
+ ReturnWithExceptionsWrapper notifyRiskCreatedOrUpdated(Risk risk, int incidences, boolean commit);
- ReturnWithExceptionsWrapper notifyRiskDeleted(String riskId, boolean commit);
+ ReturnWithExceptionsWrapper notifyRiskDeleted(String riskId, boolean commit);
- ReturnWithExceptionsWrapper notifyRiskIncidenceCreatedOrUpdated(RiskIncidence riskIncidence, boolean commit);
+ ReturnWithExceptionsWrapper notifyRiskIncidenceCreatedOrUpdated(RiskIncidence riskIncidence, boolean commit);
- ReturnWithExceptionsWrapper notifyRiskIncidenceDeleted(String riskIncidenceId, boolean commit);
+ ReturnWithExceptionsWrapper notifyRiskIncidenceDeleted(String riskIncidenceId, boolean commit);
- ReturnWithExceptionsWrapper notifyRepresentationInformationCreatedOrUpdated(RepresentationInformation ri,
- boolean commit);
+ ReturnWithExceptionsWrapper notifyRepresentationInformationCreatedOrUpdated(RepresentationInformation ri,
+ boolean commit);
- ReturnWithExceptionsWrapper notifyRepresentationInformationDeleted(String representationInformationId,
- boolean commit);
+ ReturnWithExceptionsWrapper notifyRepresentationInformationDeleted(String representationInformationId,
+ boolean commit);
- ReturnWithExceptionsWrapper notifyNotificationCreatedOrUpdated(Notification notification);
+ ReturnWithExceptionsWrapper notifyNotificationCreatedOrUpdated(Notification notification);
- ReturnWithExceptionsWrapper notifyNotificationDeleted(String notificationId);
+ ReturnWithExceptionsWrapper notifyNotificationDeleted(String notificationId);
- ReturnWithExceptionsWrapper notifyDIPCreated(DIP dip, boolean commit);
+ ReturnWithExceptionsWrapper notifyDIPCreated(DIP dip, boolean commit);
- ReturnWithExceptionsWrapper notifyDIPUpdated(DIP dip, boolean commit);
+ ReturnWithExceptionsWrapper notifyDIPUpdated(DIP dip, boolean commit);
- ReturnWithExceptionsWrapper notifyDIPDeleted(String dipId, boolean commit);
+ ReturnWithExceptionsWrapper notifyDIPDeleted(String dipId, boolean commit);
- ReturnWithExceptionsWrapper notifyDIPFileCreated(DIPFile file);
+ ReturnWithExceptionsWrapper notifyDIPFileCreated(DIPFile file);
- ReturnWithExceptionsWrapper notifyDIPFileUpdated(DIPFile file);
+ ReturnWithExceptionsWrapper notifyDIPFileUpdated(DIPFile file);
- ReturnWithExceptionsWrapper notifyDIPFileDeleted(String dipId, List path, String fileId);
+ ReturnWithExceptionsWrapper notifyDIPFileDeleted(String dipId, List path, String fileId);
- ReturnWithExceptionsWrapper notifyDisposalConfirmationCreatedOrUpdated(DisposalConfirmation confirmation);
+ ReturnWithExceptionsWrapper notifyDisposalConfirmationCreatedOrUpdated(DisposalConfirmation confirmation);
- ReturnWithExceptionsWrapper notifyDisposalConfirmationDeleted(String disposalConfirmationId, boolean commit);
+ ReturnWithExceptionsWrapper notifyDisposalConfirmationDeleted(String disposalConfirmationId, boolean commit);
+
+ ReturnWithExceptionsWrapper notifyDisposalScheduleCreatedOrUpdated(DisposalSchedule schedule);
+
+ ReturnWithExceptionsWrapper notifyDisposalScheduleDeleted(String disposalScheduleId, boolean commit);
+
+ ReturnWithExceptionsWrapper notifyDisposalHoldCreatedOrUpdated(DisposalHold hold);
+
+ ReturnWithExceptionsWrapper notifyDisposalHoldDeleted(String disposalHoldId, boolean commit);
+
+ ReturnWithExceptionsWrapper notifyDisposalRuleCreatedOrUpdated(DisposalRule rule);
+
+ ReturnWithExceptionsWrapper notifyDisposalRuleDeleted(String disposalRuleId, boolean commit);
}
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/model/ModelObserver.java b/roda-core/roda-core/src/main/java/org/roda/core/model/ModelObserver.java
index bdd62307e3..9cb01ee8f7 100644
--- a/roda-core/roda-core/src/main/java/org/roda/core/model/ModelObserver.java
+++ b/roda-core/roda-core/src/main/java/org/roda/core/model/ModelObserver.java
@@ -12,6 +12,9 @@
import org.roda.core.data.exceptions.ReturnWithExceptions;
import org.roda.core.data.v2.LiteRODAObject;
import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation;
+import org.roda.core.data.v2.disposal.hold.DisposalHold;
+import org.roda.core.data.v2.disposal.rule.DisposalRule;
+import org.roda.core.data.v2.disposal.schedule.DisposalSchedule;
import org.roda.core.data.v2.ip.AIP;
import org.roda.core.data.v2.ip.DIP;
import org.roda.core.data.v2.ip.DIPFile;
@@ -155,6 +158,18 @@ public ReturnWithExceptions disposalConfirmationCreateOrUpd
public ReturnWithExceptions disposalConfirmationDeleted(String confirmationId, boolean commit);
+ ReturnWithExceptions disposalScheduleCreatedOrUpdated(DisposalSchedule schedule);
+
+ ReturnWithExceptions disposalScheduleDeleted(String scheduleId, boolean commit);
+
+ ReturnWithExceptions disposalHoldCreatedOrUpdated(DisposalHold hold);
+
+ ReturnWithExceptions disposalHoldDeleted(String holdId, boolean commit);
+
+ ReturnWithExceptions disposalRuleCreatedOrUpdated(DisposalRule rule);
+
+ ReturnWithExceptions disposalRuleDeleted(String ruleId, boolean commit);
+
public ReturnWithExceptions liteRODAObjectCreated(LiteRODAObject liteRODAObject);
public ReturnWithExceptions liteRODAObjectUpdated(LiteRODAObject liteRODAObject);
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/model/ModelService.java b/roda-core/roda-core/src/main/java/org/roda/core/model/ModelService.java
index 02c1ae5ce0..0879aec09b 100644
--- a/roda-core/roda-core/src/main/java/org/roda/core/model/ModelService.java
+++ b/roda-core/roda-core/src/main/java/org/roda/core/model/ModelService.java
@@ -14,6 +14,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import org.roda.core.common.iterables.CloseableIterable;
import org.roda.core.common.notifications.NotificationProcessor;
@@ -547,6 +548,8 @@ Group updateGroup(Group group, boolean notify)
Group updateGroup(Group group, boolean notify, boolean isHandlingEvent)
throws GenericException, NotFoundException, AuthorizationDeniedException;
+ Group updateGroupMembers(String id, Set members, boolean notify,boolean isHandlingEvent) throws AuthorizationDeniedException, NotFoundException, GenericException;
+
void deleteGroup(String id, boolean notify) throws GenericException, AuthorizationDeniedException;
void deleteGroup(String id, boolean notify, boolean isHandlingEvent)
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/model/TransactionalModelService.java b/roda-core/roda-core/src/main/java/org/roda/core/model/TransactionalModelService.java
index e014849c21..8bd0260f2e 100644
--- a/roda-core/roda-core/src/main/java/org/roda/core/model/TransactionalModelService.java
+++ b/roda-core/roda-core/src/main/java/org/roda/core/model/TransactionalModelService.java
@@ -7,10 +7,19 @@
*/
package org.roda.core.model;
+import org.roda.core.data.exceptions.AuthorizationDeniedException;
+import org.roda.core.data.exceptions.GenericException;
+import org.roda.core.data.exceptions.NotFoundException;
+import org.roda.core.data.exceptions.RequestNotValidException;
+import org.roda.core.data.v2.jobs.Job;
import org.roda.core.transaction.TransactionalService;
/**
* @author Gabriel Barros
*/
public interface TransactionalModelService extends ModelService, TransactionalService {
+ @Override
+ default void createOrUpdateJob(Job job) throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException {
+
+ }
}
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/model/utils/LdapUtility.java b/roda-core/roda-core/src/main/java/org/roda/core/model/utils/LdapUtility.java
index 980fd4b228..cc94a2c875 100644
--- a/roda-core/roda-core/src/main/java/org/roda/core/model/utils/LdapUtility.java
+++ b/roda-core/roda-core/src/main/java/org/roda/core/model/utils/LdapUtility.java
@@ -709,6 +709,10 @@ public Group modifyGroup(final Group modifiedGroup)
return modifyGroup(modifiedGroup, false);
}
+ public Group modifyGroupMembers(final Group modifiedGroup) throws NotFoundException, IllegalOperationException, GenericException {
+ return modifyGroup(modifiedGroup, true);
+ }
+
/**
* Removes a group.
*
@@ -1809,6 +1813,9 @@ private Name removeBaseDN(Name dn) {
return LdapUtils.removeFirst(dn, LdapUtils.newLdapName(ldapRootDN));
}
+ public boolean isProtectedUser(String username) {
+ return this.ldapProtectedUsers.contains(username);
+ }
public String transformExtra(Set values) {
Handlebars handlebars = new Handlebars();
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/model/utils/ModelUtils.java b/roda-core/roda-core/src/main/java/org/roda/core/model/utils/ModelUtils.java
index 06652ca493..168d5bc67c 100644
--- a/roda-core/roda-core/src/main/java/org/roda/core/model/utils/ModelUtils.java
+++ b/roda-core/roda-core/src/main/java/org/roda/core/model/utils/ModelUtils.java
@@ -32,6 +32,7 @@
import org.roda.core.data.v2.common.OptionalWithCause;
import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation;
import org.roda.core.data.v2.disposal.hold.DisposalHold;
+import org.roda.core.data.v2.disposal.rule.DisposalRule;
import org.roda.core.data.v2.disposal.schedule.DisposalSchedule;
import org.roda.core.data.v2.index.select.SelectedItems;
import org.roda.core.data.v2.index.select.SelectedItemsFilter;
@@ -718,6 +719,18 @@ public static String getJobId(StoragePath jobPath) {
return jobPath.getName().replace(RodaConstants.JOB_FILE_EXTENSION, "");
}
+ public static String getDisposalScheduleId(StoragePath disposalSchedulePath) {
+ return disposalSchedulePath.getName().replace(RodaConstants.DISPOSAL_SCHEDULE_FILE_EXTENSION, "");
+ }
+
+ public static String getDisposalHoldId(StoragePath disposalHoldPath) {
+ return disposalHoldPath.getName().replace(RodaConstants.DISPOSAL_HOLD_FILE_EXTENSION, "");
+ }
+
+ public static String getDisposalRuleId(StoragePath disposalRulePath) {
+ return disposalRulePath.getName().replace(RodaConstants.DISPOSAL_RULE_FILE_EXTENSION, "");
+ }
+
public static StoragePath getJobReportContainerPath() throws RequestNotValidException {
return DefaultStoragePath.parse(RodaConstants.STORAGE_CONTAINER_JOB_REPORT);
}
@@ -1051,6 +1064,8 @@ public static StoragePath getContainerPath(Class cla
return getDisposalScheduleContainerPath();
} else if (clazz.equals(DisposalHold.class)) {
return getDisposalHoldContainerPath();
+ } else if (clazz.equals(DisposalRule.class)) {
+ return getDisposalRuleContainerPath();
} else if (clazz.equals(DisposalConfirmation.class)) {
return getDisposalConfirmationContainerPath();
} else if (clazz.equals(DistributedInstance.class)) {
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/model/utils/ResourceParseUtils.java b/roda-core/roda-core/src/main/java/org/roda/core/model/utils/ResourceParseUtils.java
index 4efe7bf847..f5949b0016 100644
--- a/roda-core/roda-core/src/main/java/org/roda/core/model/utils/ResourceParseUtils.java
+++ b/roda-core/roda-core/src/main/java/org/roda/core/model/utils/ResourceParseUtils.java
@@ -37,6 +37,9 @@
import org.roda.core.data.v2.LiteRODAObject;
import org.roda.core.data.v2.common.OptionalWithCause;
import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation;
+import org.roda.core.data.v2.disposal.hold.DisposalHold;
+import org.roda.core.data.v2.disposal.rule.DisposalRule;
+import org.roda.core.data.v2.disposal.schedule.DisposalSchedule;
import org.roda.core.data.v2.ip.AIP;
import org.roda.core.data.v2.ip.DIP;
import org.roda.core.data.v2.ip.DIPFile;
@@ -387,7 +390,14 @@ public static OptionalWithCause convert
}
ids.add(fileName.replace(RodaConstants.PREMIS_SUFFIX, ""));
ret = OptionalWithCause.of(LiteRODAObjectFactory.get(classToReturn, ids));
- } else {
+ } else if (classToReturn.equals(DisposalSchedule.class)) {
+ ret = OptionalWithCause.of(LiteRODAObjectFactory.get(classToReturn, ModelUtils.getDisposalScheduleId(storagePath)));
+ } else if (classToReturn.equals(DisposalHold.class)) {
+ ret = OptionalWithCause.of(LiteRODAObjectFactory.get(classToReturn, ModelUtils.getDisposalHoldId(storagePath)));
+ } else if (classToReturn.equals(DisposalRule.class)) {
+ ret = OptionalWithCause.of(LiteRODAObjectFactory.get(classToReturn, ModelUtils.getDisposalRuleId(storagePath)));
+ }
+ else {
ret = OptionalWithCause.of(LiteRODAObjectFactory.get(classToReturn, fileName));
}
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/plugins/PluginHelper.java b/roda-core/roda-core/src/main/java/org/roda/core/plugins/PluginHelper.java
index a5826ab416..e07ca01c9c 100644
--- a/roda-core/roda-core/src/main/java/org/roda/core/plugins/PluginHelper.java
+++ b/roda-core/roda-core/src/main/java/org/roda/core/plugins/PluginHelper.java
@@ -54,6 +54,9 @@
import org.roda.core.data.v2.Void;
import org.roda.core.data.v2.common.OptionalWithCause;
import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation;
+import org.roda.core.data.v2.disposal.hold.DisposalHold;
+import org.roda.core.data.v2.disposal.rule.DisposalRule;
+import org.roda.core.data.v2.disposal.schedule.DisposalSchedule;
import org.roda.core.data.v2.index.IndexResult;
import org.roda.core.data.v2.index.facet.Facets;
import org.roda.core.data.v2.index.filter.Filter;
@@ -97,6 +100,9 @@
import org.roda.core.plugins.base.maintenance.reindex.ReindexActionLogPlugin;
import org.roda.core.plugins.base.maintenance.reindex.ReindexDIPPlugin;
import org.roda.core.plugins.base.maintenance.reindex.ReindexDisposalConfirmationPlugin;
+import org.roda.core.plugins.base.maintenance.reindex.ReindexDisposalHoldPlugin;
+import org.roda.core.plugins.base.maintenance.reindex.ReindexDisposalRulePlugin;
+import org.roda.core.plugins.base.maintenance.reindex.ReindexDisposalSchedulePlugin;
import org.roda.core.plugins.base.maintenance.reindex.ReindexIncidencePlugin;
import org.roda.core.plugins.base.maintenance.reindex.ReindexJobPlugin;
import org.roda.core.plugins.base.maintenance.reindex.ReindexNotificationPlugin;
@@ -731,6 +737,9 @@ public static List> getReindexObjectClasses() {
list.add(IndexedPreservationAgent.class);
list.add(DIP.class);
list.add(DisposalConfirmation.class);
+ list.add(DisposalSchedule.class);
+ list.add(DisposalHold.class);
+ list.add(DisposalRule.class);
return list;
}
@@ -1605,6 +1614,12 @@ public static String getReindexPluginName(Class> reindexClass) throws NotFound
return ReindexDIPPlugin.class.getName();
} else if (reindexClass.equals(DisposalConfirmation.class)) {
return ReindexDisposalConfirmationPlugin.class.getName();
+ } else if (reindexClass.equals(DisposalSchedule.class)) {
+ return ReindexDisposalSchedulePlugin.class.getName();
+ } else if (reindexClass.equals(DisposalHold.class)) {
+ return ReindexDisposalHoldPlugin.class.getName();
+ } else if (reindexClass.equals(DisposalRule.class)) {
+ return ReindexDisposalRulePlugin.class.getName();
} else {
throw new NotFoundException("No reindex plugin available");
}
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/disposal/hold/LiftDisposalHoldPlugin.java b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/disposal/hold/LiftDisposalHoldPlugin.java
index 20d7d98840..ad793519f5 100644
--- a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/disposal/hold/LiftDisposalHoldPlugin.java
+++ b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/disposal/hold/LiftDisposalHoldPlugin.java
@@ -25,7 +25,6 @@
import org.roda.core.data.exceptions.RequestNotValidException;
import org.roda.core.data.v2.LiteOptionalWithCause;
import org.roda.core.data.v2.Void;
-import org.roda.core.data.v2.common.Pair;
import org.roda.core.data.v2.disposal.hold.DisposalHold;
import org.roda.core.data.v2.disposal.hold.DisposalHoldState;
import org.roda.core.data.v2.index.filter.Filter;
@@ -195,7 +194,8 @@ private void liftDisposalHold(IndexService index, ModelService model, Report rep
try {
AIP aip = model.retrieveAIP(indexedAIP.getId());
- long children = processTransitiveAIP(model, index, cachedJob, aip.getId(), disposalHoldId, jobPluginInfo, report);
+ long children = processTransitiveAIP(model, index, cachedJob, aip.getId(), disposalHoldId, jobPluginInfo,
+ report);
model.updateAIPOnHoldStatus(aip, model.onDisposalHold(aip.getId()));
jobPluginInfo.incrementObjectsProcessedWithSuccess();
reportItem.setPluginState(state);
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/maintenance/reindex/ReindexDisposalHoldPlugin.java b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/maintenance/reindex/ReindexDisposalHoldPlugin.java
new file mode 100644
index 0000000000..6152088658
--- /dev/null
+++ b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/maintenance/reindex/ReindexDisposalHoldPlugin.java
@@ -0,0 +1,47 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE file at the root of the source
+ * tree and available online at
+ *
+ * https://github.com/keeps/roda
+ */
+package org.roda.core.plugins.base.maintenance.reindex;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.roda.core.data.exceptions.GenericException;
+import org.roda.core.data.exceptions.RequestNotValidException;
+import org.roda.core.data.v2.disposal.hold.DisposalHold;
+import org.roda.core.index.IndexService;
+import org.roda.core.plugins.Plugin;
+
+public class ReindexDisposalHoldPlugin extends ReindexRodaEntityPlugin {
+
+ @Override
+ public String getName() {
+ return "Rebuild disposal hold index";
+ }
+
+ @Override
+ public String getVersionImpl() {
+ return "1.0";
+ }
+
+ @Override
+ public Plugin cloneMe() {
+ return new ReindexDisposalHoldPlugin();
+ }
+
+ @Override
+ public List> getObjectClasses() {
+ return Collections.singletonList(DisposalHold.class);
+ }
+
+ @Override
+ public void clearSpecificIndexes(IndexService index, List ids)
+ throws GenericException, RequestNotValidException {
+ // do nothing
+ }
+
+}
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/maintenance/reindex/ReindexDisposalRulePlugin.java b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/maintenance/reindex/ReindexDisposalRulePlugin.java
new file mode 100644
index 0000000000..506546f37f
--- /dev/null
+++ b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/maintenance/reindex/ReindexDisposalRulePlugin.java
@@ -0,0 +1,47 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE file at the root of the source
+ * tree and available online at
+ *
+ * https://github.com/keeps/roda
+ */
+package org.roda.core.plugins.base.maintenance.reindex;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.roda.core.data.exceptions.GenericException;
+import org.roda.core.data.exceptions.RequestNotValidException;
+import org.roda.core.data.v2.disposal.rule.DisposalRule;
+import org.roda.core.index.IndexService;
+import org.roda.core.plugins.Plugin;
+
+public class ReindexDisposalRulePlugin extends ReindexRodaEntityPlugin {
+
+ @Override
+ public String getName() {
+ return "Rebuild disposal rule index";
+ }
+
+ @Override
+ public String getVersionImpl() {
+ return "1.0";
+ }
+
+ @Override
+ public Plugin cloneMe() {
+ return new ReindexDisposalRulePlugin();
+ }
+
+ @Override
+ public List> getObjectClasses() {
+ return Collections.singletonList(DisposalRule.class);
+ }
+
+ @Override
+ public void clearSpecificIndexes(IndexService index, List ids)
+ throws GenericException, RequestNotValidException {
+ // do nothing
+ }
+
+}
diff --git a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/maintenance/reindex/ReindexDisposalSchedulePlugin.java b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/maintenance/reindex/ReindexDisposalSchedulePlugin.java
new file mode 100644
index 0000000000..5599bfda47
--- /dev/null
+++ b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/maintenance/reindex/ReindexDisposalSchedulePlugin.java
@@ -0,0 +1,47 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE file at the root of the source
+ * tree and available online at
+ *
+ * https://github.com/keeps/roda
+ */
+package org.roda.core.plugins.base.maintenance.reindex;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.roda.core.data.exceptions.GenericException;
+import org.roda.core.data.exceptions.RequestNotValidException;
+import org.roda.core.data.v2.disposal.schedule.DisposalSchedule;
+import org.roda.core.index.IndexService;
+import org.roda.core.plugins.Plugin;
+
+public class ReindexDisposalSchedulePlugin extends ReindexRodaEntityPlugin {
+
+ @Override
+ public String getName() {
+ return "Rebuild disposal schedule index";
+ }
+
+ @Override
+ public String getVersionImpl() {
+ return "1.0";
+ }
+
+ @Override
+ public Plugin cloneMe() {
+ return new ReindexDisposalSchedulePlugin();
+ }
+
+ @Override
+ public List> getObjectClasses() {
+ return Collections.singletonList(DisposalSchedule.class);
+ }
+
+ @Override
+ public void clearSpecificIndexes(IndexService index, List ids)
+ throws GenericException, RequestNotValidException {
+ // do nothing
+ }
+
+}
diff --git a/roda-core/roda-core/src/main/resources/config/roda-roles.properties b/roda-core/roda-core/src/main/resources/config/roda-roles.properties
index 40ee50fc25..4f9b9b786d 100644
--- a/roda-core/roda-core/src/main/resources/config/roda-roles.properties
+++ b/roda-core/roda-core/src/main/resources/config/roda-roles.properties
@@ -94,6 +94,7 @@ core.roles.org.roda.wui.api.v2.controller.JobsController.createJob = job.manage
core.roles.org.roda.wui.api.v2.controller.JobsController.obtainJobCommand = job.manage
# Member roles
+core.roles.org.roda.wui.api.v2.controller.MembersController.getMember = member.read
core.roles.org.roda.wui.api.v2.controller.MembersController.getUser = member.read
core.roles.org.roda.wui.api.v2.controller.MembersController.getGroup = member.read
core.roles.org.roda.wui.api.v2.controller.MembersController.deleteMultipleMembers = member.manage
@@ -113,6 +114,12 @@ core.roles.org.roda.wui.api.v2.controller.MembersController.updateAccessKey = ac
core.roles.org.roda.wui.api.v2.controller.MembersController.getAccessKeysByUser = access_key.read
core.roles.org.roda.wui.api.v2.controller.MembersController.createAccessKey = access_key.manage
core.roles.org.roda.wui.api.v2.controller.MembersController.changeActive = member.manage
+core.roles.org.roda.wui.api.v2.controller.MembersController.addGroupsToUser = member.manage
+core.roles.org.roda.wui.api.v2.controller.MembersController.removeGroupsFromUser = member.manage
+core.roles.org.roda.wui.api.v2.controller.MembersController.getUserGroups = member.read
+core.roles.org.roda.wui.api.v2.controller.MembersController.getGroupMembers = member.read
+core.roles.org.roda.wui.api.v2.controller.MembersController.addMembersToGroup = member.manage
+core.roles.org.roda.wui.api.v2.controller.MembersController.removeMembersFromGroup = member.manage
core.roles.org.roda.wui.api.v2.services.MembersService.retrieveOwnUserExtra = member.read
core.roles.org.roda.wui.api.v2.services.MembersService.retrieveUserExtra = member.read
@@ -175,6 +182,7 @@ core.roles.org.roda.wui.api.v2.controller.DisposalRuleController.retrieveDisposa
core.roles.org.roda.wui.api.v2.controller.DisposalRuleController.updateDisposalRule = disposal_hold.manage
core.roles.org.roda.wui.api.v2.controller.DisposalRuleController.deleteDisposalRule = disposal_rule.manage
core.roles.org.roda.wui.api.v2.controller.DisposalRuleController.applyDisposalRules = disposal_schedule.associate
+core.roles.org.roda.wui.api.v2.controller.DisposalRuleController.changeDisposalRuleOrder = disposal_rule.manage
# Representation information
core.roles.org.roda.wui.api.v2.controller.RepresentationInformationController.retrieveRepresentationInformationFamily = ri.read
@@ -278,7 +286,6 @@ core.roles.org.roda.wui.api.v2.services.IndexService.find(IndexedReport) = job.
core.roles.org.roda.wui.api.v2.services.IndexService.find(IndexedPreservationEvent) = preservation_metadata.read
core.roles.org.roda.wui.api.v2.services.IndexService.find(IndexedPreservationAgent) = preservation_metadata.read
core.roles.org.roda.wui.api.v2.services.IndexService.find(LogEntry) = log_entry.read
-core.roles.org.roda.wui.api.v2.services.IndexService.find(DisposalConfirmation) = disposal_confirmation.read
core.roles.org.roda.wui.api.v2.services.IndexService.find(RepresentationInformation) = ri.read
core.roles.org.roda.wui.api.v2.services.IndexService.find(RODAMember) = member.read
core.roles.org.roda.wui.api.v2.services.IndexService.find(IndexedDIP) = aip.view
@@ -286,6 +293,10 @@ core.roles.org.roda.wui.api.v2.services.IndexService.find(IndexedDIP) = aip.read
core.roles.org.roda.wui.api.v2.services.IndexService.find(IndexedFile) = representation.read
core.roles.org.roda.wui.api.v2.services.IndexService.find(IndexedRepresentation) = representation.read
core.roles.org.roda.wui.api.v2.services.IndexService.find(DIPFile) = aip.read
+core.roles.org.roda.wui.api.v2.services.IndexService.find(DisposalSchedule) = disposal_schedule.read
+core.roles.org.roda.wui.api.v2.services.IndexService.find(DisposalRule) = disposal_rule.read
+core.roles.org.roda.wui.api.v2.services.IndexService.find(DisposalHold) = disposal_hold.read
+core.roles.org.roda.wui.api.v2.services.IndexService.find(DisposalConfirmation) = disposal_confirmation.read
# Generic count roles
core.roles.org.roda.wui.api.v2.services.IndexService.count(TransferredResource) = transfer.read
@@ -311,6 +322,10 @@ core.roles.org.roda.wui.api.v2.services.IndexService.count(IndexedFile) = aip.vi
core.roles.org.roda.wui.api.v2.services.IndexService.count(IndexedFile) = aip.read
core.roles.org.roda.wui.api.v2.services.IndexService.count(DIPFile) = aip.read
core.roles.org.roda.wui.api.v2.services.IndexService.count(IndexedReport) = job.read
+core.roles.org.roda.wui.api.v2.services.IndexService.count(DisposalSchedule) = disposal_schedule.read
+core.roles.org.roda.wui.api.v2.services.IndexService.count(DisposalRule) = disposal_rule.read
+core.roles.org.roda.wui.api.v2.services.IndexService.count(DisposalHold) = disposal_hold.read
+core.roles.org.roda.wui.api.v2.services.IndexService.count(DisposalConfirmation) = disposal_confirmation.read
# Generic suggest roles
core.roles.org.roda.wui.api.v2.services.IndexService.suggest(IndexedAIP) = aip.read
diff --git a/roda-ui/roda-wui/src/main/java/config/i18n/client/ClientMessages.java b/roda-ui/roda-wui/src/main/java/config/i18n/client/ClientMessages.java
index 7847fb8bb5..65974519af 100644
--- a/roda-ui/roda-wui/src/main/java/config/i18n/client/ClientMessages.java
+++ b/roda-ui/roda-wui/src/main/java/config/i18n/client/ClientMessages.java
@@ -417,6 +417,8 @@ public interface ClientMessages extends Messages {
String cancelButton();
+ String updateButton();
+
String revertButton();
String removeButton();
@@ -1016,6 +1018,10 @@ SafeHtml searchPreFilterLongRangeFilterParameterGreaterThan(String searchPreFilt
String detailsCreatedBy();
+ String detailsUpdatedOn();
+
+ String detailsUpdatedBy();
+
String detailsModifiedOn();
String detailsModifiedBy();
@@ -1100,6 +1106,14 @@ SafeHtml searchPreFilterLongRangeFilterParameterGreaterThan(String searchPreFilt
String userRemoveConfirmDialogMessage();
+ String singleUserRemoveConfirmDialogTitle();
+
+ String singleGroupRemoveConfirmDialogTitle();
+
+ String singleUserRemoveConfirmDialogMessage(String username);
+
+ String singleGroupRemoveConfirmDialogMessage(String username);
+
// Member management
String addUserButton();
@@ -1312,10 +1326,15 @@ SafeHtml searchPreFilterLongRangeFilterParameterGreaterThan(String searchPreFilt
String representationInformationIntellectualEntities(@PluralCount int size, String link);
+ String representationInformationIntellectualEntitiesAssociations();
+
String representationInformationRepresentations(@PluralCount int size, String link);
+ String representationInformationRepresentationsAssociations();
+
String representationInformationFiles(@PluralCount int size, String link);
+ String representationInformationFilesAssociations();
/****** Descriptive Metadata ****/
String metadataType();
@@ -1389,7 +1408,7 @@ SafeHtml searchPreFilterLongRangeFilterParameterGreaterThan(String searchPreFilt
String permissionAssignedUsersEmpty();
- String listOfAIPs();
+ String listOfAIPs();
String listOfRepresentations();
@@ -2086,6 +2105,12 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String
String disposalScheduleUsedInRule();
+ String disposalScheduleRemoveConfirmDialogTitle();
+
+ String disposalScheduleRemoveConfirmDialogMessage();
+
+ String disposalScheduleRemovedWithSuccess();
+
String disposalHoldIdentifier();
String disposalHoldTitle();
@@ -2128,6 +2153,8 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String
String showDisposalScheduleTitle();
+ String showDisposalConfirmationTitle();
+
String disposalConfirmationTitle();
String disposalConfirmationCreationDate();
@@ -2499,6 +2526,8 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String
String createAccessKeyTitle();
+ String regenerateAccessKeyTitle();
+
String showAccessKeyTitle();
String editAccessKeyTitle();
@@ -2537,6 +2566,8 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String
String accessKeySuccessfullyRegenerated();
+ String accessKeySuccessfullyDeleted();
+
String accessKeySuccessfullyRevoked();
String accessKeyDeleteConfirmationMessage();
@@ -2545,6 +2576,8 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String
String accessKeyRegenerateConfirmationMessage();
+ String accessKeyExpirationDateInThePast();
+
/** Market **/
String marketPluginsActionsTabLabel();
@@ -2640,6 +2673,63 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String
String reasonCantActOnGroup();
+ String reasonHoldAlreadyLifted();
+
+ // RODA Members - toolbar actions
+ String deactivateUserTitle();
+
+ String deactivateUserConfirmationMessage();
+
+ String activateUserTitle();
+
+ String activateUserConfirmationMessage();
+
+ // RODA Members - Members
+ String membersTabTitle();
+
+ String addNewGroupModalTitle();
+
+ String addToGroupButton();
+
+ String addNewMemberToGroupButton();
+
+ String groupSuccessfullyAdded();
+
+ String memberSuccessfullyAdded();
+
+ String addNewMemberToGroupTitle();
+
+ String addNewMemberAction();
+
+ String removeGroupConfirmationTitle();
+
+ String removeGroupConfirmationMessage(String group);
+
+ String groupSuccessfullyRemoved();
+
+ String removeMemberConfirmationMessage(String member);
+
+ String removeMemberConfirmationTitle();
+
+ String memberSuccessfullyRemoved();
+
+ // RODA Members - Permissions
+ String catalogueAndSearchGroupLabel();
+
+ String ingestPreservationActionsInternalActionsGroupLabel();
+
+ String administrationGroupLabel();
+
+ String planningGroupLabel();
+
+ String disposalGroupLabel();
+
+ String editPermissionsReadOnlyPermissionsText();
+
+ String permissionsUpdateWithSuccess();
+
+ String editPermissionsModalTitle();
+
// ── Email viewer ──────────────────────────────────────────────────────────
String emailViewerSubject();
@@ -2667,4 +2757,40 @@ SafeHtml representationInformationAssociatedWithDescription(String field, String
String emailViewerLoadImagesOnce();
String emailViewerAlwaysTrustSender();
+
+ String disposalHoldSuccessfullyCreated();
+
+ String disposalHoldSuccessfullyUpdated();
+
+ String updateDisposalRuleOrderSuccessMessage();
+
+ String moveToTop();
+
+ String moveToBottom();
+
+ String moveToPositionNumber();
+
+ String disposalRuleChangeOrderMessage();
+
+ String disposalRuleSuccessfullyCreated();
+
+ String disposalRuleSuccessfullyUpdated();
+
+ String userSuccessfullyCreated();
+
+ String userSuccessfullyUpdated();
+
+ String groupSuccessfullyCreated();
+
+ String groupSuccessfullyUpdated();
+
+ String failedToUpdateGroup();
+
+ String failedToUpdateUser();
+
+ String disposalScheduleSuccessfullyCreated();
+
+ String disposalScheduleSuccessfullyUpdated();
+
+ String disposalPolicyOverdueRecordsTitle();
}
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalHoldController.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalHoldController.java
index 432de2aba1..1b74633d2c 100644
--- a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalHoldController.java
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalHoldController.java
@@ -8,7 +8,10 @@
package org.roda.wui.api.v2.controller;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import jakarta.servlet.http.HttpServletRequest;
import org.roda.core.data.common.RodaConstants;
import org.roda.core.data.exceptions.RODAException;
import org.roda.core.data.utils.JsonUtils;
@@ -16,17 +19,25 @@
import org.roda.core.data.v2.disposal.hold.DisposalHolds;
import org.roda.core.data.v2.disposal.metadata.DisposalHoldsAIPMetadata;
import org.roda.core.data.v2.disposal.metadata.DisposalTransitiveHoldsAIPMetadata;
+import org.roda.core.data.v2.generics.LongResponse;
import org.roda.core.data.v2.generics.select.SelectedItemsRequest;
+import org.roda.core.data.v2.index.CountRequest;
+import org.roda.core.data.v2.index.FindRequest;
+import org.roda.core.data.v2.index.IndexResult;
+import org.roda.core.data.v2.index.SuggestRequest;
import org.roda.core.data.v2.ip.IndexedAIP;
import org.roda.core.data.v2.ip.disposalhold.DisassociateDisposalHoldRequest;
import org.roda.core.data.v2.ip.disposalhold.UpdateDisposalHoldRequest;
import org.roda.core.data.v2.jobs.Job;
+import org.roda.core.model.utils.UserUtility;
import org.roda.wui.api.v2.exceptions.RESTException;
import org.roda.wui.api.v2.services.DisposalHoldService;
+import org.roda.wui.api.v2.services.IndexService;
import org.roda.wui.api.v2.utils.CommonServicesUtils;
import org.roda.wui.client.services.DisposalHoldRestService;
import org.roda.wui.common.RequestControllerAssistant;
import org.roda.wui.common.model.RequestContext;
+import org.roda.wui.common.utils.RequestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -40,6 +51,11 @@
@RestController
@RequestMapping(path = "/api/v2/disposal/holds")
public class DisposalHoldController implements DisposalHoldRestService {
+ @Autowired
+ HttpServletRequest request;
+
+ @Autowired
+ IndexService indexService;
@Autowired
DisposalHoldService disposalHoldService;
@@ -189,4 +205,29 @@ public DisposalHoldsAIPMetadata process(RequestContext requestContext,
}
});
}
+
+ @Override
+ public DisposalHold findByUuid(String uuid, String localeString) {
+ return indexService.retrieve(DisposalHold.class, uuid, new ArrayList<>());
+ }
+
+ @Override
+ public IndexResult find(@RequestBody FindRequest findRequest, String localeString) {
+ return indexService.find(DisposalHold.class, findRequest, localeString);
+ }
+
+ @Override
+ public LongResponse count(@RequestBody CountRequest countRequest) {
+ RequestContext requestContext = RequestUtils.parseHTTPRequest(request);
+ if (UserUtility.hasPermissions(requestContext.getUser(), RodaConstants.PERMISSION_METHOD_FIND_FILE)) {
+ return new LongResponse(indexService.count(DisposalHold.class, countRequest));
+ } else {
+ return new LongResponse(-1L);
+ }
+ }
+
+ @Override
+ public List suggest(@RequestBody SuggestRequest suggestRequest) {
+ return indexService.suggest(suggestRequest, DisposalHold.class);
+ }
}
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalRuleController.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalRuleController.java
index b1cbffea16..9ac2beda40 100644
--- a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalRuleController.java
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalRuleController.java
@@ -8,18 +8,32 @@
package org.roda.wui.api.v2.controller;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import jakarta.servlet.http.HttpServletRequest;
import org.roda.core.data.common.RodaConstants;
import org.roda.core.data.exceptions.RODAException;
import org.roda.core.data.utils.JsonUtils;
+import org.roda.core.data.v2.disposal.rule.ChangeOrderRequest;
import org.roda.core.data.v2.disposal.rule.DisposalRule;
import org.roda.core.data.v2.disposal.rule.DisposalRules;
+import org.roda.core.data.v2.disposal.schedule.DisposalSchedule;
+import org.roda.core.data.v2.generics.LongResponse;
+import org.roda.core.data.v2.index.CountRequest;
+import org.roda.core.data.v2.index.FindRequest;
+import org.roda.core.data.v2.index.IndexResult;
+import org.roda.core.data.v2.index.SuggestRequest;
import org.roda.core.data.v2.jobs.Job;
+import org.roda.core.model.utils.UserUtility;
import org.roda.wui.api.v2.exceptions.RESTException;
import org.roda.wui.api.v2.services.DisposalRuleService;
+import org.roda.wui.api.v2.services.DisposalScheduleService;
+import org.roda.wui.api.v2.services.IndexService;
import org.roda.wui.client.services.DisposalRuleRestService;
import org.roda.wui.common.RequestControllerAssistant;
import org.roda.wui.common.model.RequestContext;
+import org.roda.wui.common.utils.RequestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -33,10 +47,18 @@
@RestController
@RequestMapping(path = "/api/v2/disposal/rules")
public class DisposalRuleController implements DisposalRuleRestService {
+ @Autowired
+ HttpServletRequest request;
+
+ @Autowired
+ IndexService indexService;
@Autowired
DisposalRuleService disposalRuleService;
+ @Autowired
+ DisposalScheduleService disposalScheduleService;
+
@Autowired
RequestHandler requestHandler;
@@ -56,12 +78,17 @@ public DisposalRule retrieveDisposalRule(String id) {
return requestHandler.processRequest(new RequestHandler.RequestProcessor() {
@Override
public DisposalRule process(RequestContext requestContext, RequestControllerAssistant controllerAssistant)
- throws RODAException, RESTException, IOException {
+ throws RODAException, RESTException {
// check user permissions
controllerAssistant.setRelatedObjectId(id);
controllerAssistant.setParameters(RodaConstants.DISPOSAL_RULE_ID, id);
- return disposalRuleService.retrieveDisposalHold(id, requestContext.getModelService());
+ DisposalRule disposalRule = disposalRuleService.retrieveDisposalHold(id, requestContext.getModelService());
+ DisposalSchedule disposalSchedule = disposalScheduleService
+ .retrieveDisposalSchedule(requestContext.getModelService(), disposalRule.getDisposalScheduleId());
+ disposalRule.setDisposalScheduleName(disposalSchedule.getTitle());
+
+ return disposalRule;
}
});
}
@@ -138,4 +165,42 @@ public Job process(RequestContext requestContext, RequestControllerAssistant con
}
});
}
+
+ @Override
+ public Void changeDisposalRuleOrder(@RequestBody ChangeOrderRequest request) {
+ return requestHandler.processRequestWithTransaction(new RequestHandler.RequestProcessor() {
+ @Override
+ public Void process(RequestContext requestContext, RequestControllerAssistant controllerAssistant) throws RODAException, RESTException, IOException {
+
+ // delegate action to service
+ disposalRuleService.changeDisposalRuleOrder(requestContext, request);
+ return null;
+ }
+ });
+ }
+
+ @Override
+ public DisposalRule findByUuid(String uuid, String localeString) {
+ return indexService.retrieve(DisposalRule.class, uuid, new ArrayList<>());
+ }
+
+ @Override
+ public IndexResult find(@RequestBody FindRequest findRequest, String localeString) {
+ return indexService.find(DisposalRule.class, findRequest, localeString);
+ }
+
+ @Override
+ public LongResponse count(@RequestBody CountRequest countRequest) {
+ RequestContext requestContext = RequestUtils.parseHTTPRequest(request);
+ if (UserUtility.hasPermissions(requestContext.getUser(), RodaConstants.PERMISSION_METHOD_FIND_FILE)) {
+ return new LongResponse(indexService.count(DisposalRule.class, countRequest));
+ } else {
+ return new LongResponse(-1L);
+ }
+ }
+
+ @Override
+ public List suggest(@RequestBody SuggestRequest suggestRequest) {
+ return indexService.suggest(suggestRequest, DisposalRule.class);
+ }
}
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalScheduleController.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalScheduleController.java
index de7040cd22..fe7395d91c 100644
--- a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalScheduleController.java
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/DisposalScheduleController.java
@@ -8,21 +8,35 @@
package org.roda.wui.api.v2.controller;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import jakarta.servlet.http.HttpServletRequest;
import org.roda.core.data.common.RodaConstants;
import org.roda.core.data.exceptions.RODAException;
import org.roda.core.data.utils.JsonUtils;
+import org.roda.core.data.v2.disposal.rule.DisposalRule;
import org.roda.core.data.v2.disposal.schedule.DisposalSchedule;
import org.roda.core.data.v2.disposal.schedule.DisposalSchedules;
+import org.roda.core.data.v2.generics.LongResponse;
import org.roda.core.data.v2.generics.select.SelectedItemsRequest;
+import org.roda.core.data.v2.index.CountRequest;
+import org.roda.core.data.v2.index.FindRequest;
+import org.roda.core.data.v2.index.IndexResult;
+import org.roda.core.data.v2.index.SuggestRequest;
+import org.roda.core.data.v2.index.filter.Filter;
+import org.roda.core.data.v2.index.filter.SimpleFilterParameter;
import org.roda.core.data.v2.ip.IndexedAIP;
import org.roda.core.data.v2.jobs.Job;
+import org.roda.core.model.utils.UserUtility;
import org.roda.wui.api.v2.exceptions.RESTException;
import org.roda.wui.api.v2.services.DisposalScheduleService;
+import org.roda.wui.api.v2.services.IndexService;
import org.roda.wui.api.v2.utils.CommonServicesUtils;
import org.roda.wui.client.services.DisposalScheduleRestService;
import org.roda.wui.common.RequestControllerAssistant;
import org.roda.wui.common.model.RequestContext;
+import org.roda.wui.common.utils.RequestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -37,6 +51,12 @@
@RequestMapping(path = "/api/v2/disposal/schedules")
public class DisposalScheduleController implements DisposalScheduleRestService {
+ @Autowired
+ HttpServletRequest request;
+
+ @Autowired
+ IndexService indexService;
+
@Autowired
DisposalScheduleService disposalScheduleService;
@@ -64,7 +84,21 @@ public DisposalSchedule process(RequestContext requestContext, RequestController
controllerAssistant.setRelatedObjectId(id);
// delegate action to service
- return disposalScheduleService.retrieveDisposalSchedule(requestContext.getModelService(), id);
+ DisposalSchedule disposalSchedule = disposalScheduleService
+ .retrieveDisposalSchedule(requestContext.getModelService(), id);
+
+ CountRequest countRequest = new CountRequest();
+ SimpleFilterParameter filterParameter = new SimpleFilterParameter(RodaConstants.DISPOSAL_RULE_SCHEDULE_ID, id);
+ Filter filter = new Filter();
+ filter.add(filterParameter);
+
+ countRequest.setFilter(filter);
+ Long count = indexService.count(DisposalRule.class, countRequest);
+ if (count > 0) {
+ disposalSchedule.setUsedInDisposalRule(true);
+ }
+
+ return disposalSchedule;
}
}));
}
@@ -157,4 +191,29 @@ public Job process(RequestContext requestContext, RequestControllerAssistant con
}
});
}
+
+ @Override
+ public DisposalSchedule findByUuid(String uuid, String localeString) {
+ return indexService.retrieve(DisposalSchedule.class, uuid, new ArrayList<>());
+ }
+
+ @Override
+ public IndexResult find(@RequestBody FindRequest findRequest, String localeString) {
+ return indexService.find(DisposalSchedule.class, findRequest, localeString);
+ }
+
+ @Override
+ public LongResponse count(@RequestBody CountRequest countRequest) {
+ RequestContext requestContext = RequestUtils.parseHTTPRequest(request);
+ if (UserUtility.hasPermissions(requestContext.getUser(), RodaConstants.PERMISSION_METHOD_FIND_FILE)) {
+ return new LongResponse(indexService.count(DisposalSchedule.class, countRequest));
+ } else {
+ return new LongResponse(-1L);
+ }
+ }
+
+ @Override
+ public List suggest(@RequestBody SuggestRequest suggestRequest) {
+ return indexService.suggest(suggestRequest, DisposalSchedule.class);
+ }
}
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/MembersController.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/MembersController.java
index 7b20c7f465..45632027a2 100644
--- a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/MembersController.java
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/controller/MembersController.java
@@ -19,6 +19,7 @@
import javax.crypto.SecretKey;
+import io.swagger.v3.oas.annotations.Parameter;
import org.apache.commons.lang3.StringUtils;
import org.apereo.cas.client.authentication.AttributePrincipal;
import org.roda.core.RodaCoreFactory;
@@ -271,6 +272,31 @@ private static Set mapCasGroupstoRODAGroups(Set casGroups) {
return result;
}
+ @Override
+ public RODAMember getMember(String name) {
+ RequestContext requestContext = RequestUtils.parseHTTPRequest(request);
+ ControllerAssistant controllerAssistant = new ControllerAssistant() {};
+ LogEntryState state = LogEntryState.SUCCESS;
+
+ try {
+ // check user permissions
+ controllerAssistant.checkRoles(requestContext.getUser());
+
+ if (name.startsWith("user-")) {
+ return membersService.retrieveUser(name.substring("user-".length()));
+ } else {
+ return membersService.retrieveGroup(name.substring("group-".length()));
+ }
+
+ } catch (RODAException e) {
+ state = LogEntryState.FAILURE;
+ throw new RESTException(e);
+ } finally {
+ // register action
+ controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_USERNAME_PARAM, name);
+ }
+ }
+
@Override
public User getUser(String name) {
RequestContext requestContext = RequestUtils.parseHTTPRequest(request);
@@ -282,7 +308,6 @@ public User getUser(String name) {
controllerAssistant.checkRoles(requestContext.getUser());
return membersService.retrieveUser(name);
-
} catch (RODAException e) {
state = LogEntryState.FAILURE;
throw new RESTException(e);
@@ -611,7 +636,7 @@ public Group createGroup(@RequestBody CreateGroupRequest groupRequest) {
}
@Override
- public Void updateGroup(@RequestBody Group modifiedGroup) {
+ public Group updateGroup(@RequestBody Group modifiedGroup) {
ControllerAssistant controllerAssistant = new ControllerAssistant() {};
RequestContext requestContext = RequestUtils.parseHTTPRequest(request);
LogEntryState state = LogEntryState.SUCCESS;
@@ -620,7 +645,7 @@ public Void updateGroup(@RequestBody Group modifiedGroup) {
// check user permissions
controllerAssistant.checkRoles(requestContext.getUser());
// delegate
- membersService.updateGroup(modifiedGroup);
+ return membersService.updateGroup(modifiedGroup);
} catch (RODAException e) {
state = LogEntryState.FAILURE;
throw new RESTException(e);
@@ -628,7 +653,136 @@ public Void updateGroup(@RequestBody Group modifiedGroup) {
// register action
controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_GROUP_PARAM, modifiedGroup);
}
- return null;
+ }
+
+ @Override
+ public User addGroupsToUser(String id, @RequestBody SelectedItemsRequest groups) {
+ ControllerAssistant controllerAssistant = new ControllerAssistant() {};
+ RequestContext requestContext = RequestUtils.parseHTTPRequest(request);
+
+ LogEntryState state = LogEntryState.SUCCESS;
+
+ try {
+ // check user permissions
+ controllerAssistant.checkRoles(requestContext.getUser());
+ // delegate
+ return membersService.addGroupsToUser(id, groups);
+ } catch (RODAException e) {
+ state = LogEntryState.FAILURE;
+ throw new RESTException(e);
+ } finally {
+ // register action
+ controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_USER_PARAM, id,
+ RodaConstants.CONTROLLER_SELECTED_ITEMS_PARAM, groups);
+ }
+ }
+
+ @Override
+ public Group addMembersToGroup(String id, @RequestBody SelectedItemsRequest members) {
+ ControllerAssistant controllerAssistant = new ControllerAssistant() {};
+ RequestContext requestContext = RequestUtils.parseHTTPRequest(request);
+
+ LogEntryState state = LogEntryState.SUCCESS;
+
+ try {
+ // check user permissions
+ controllerAssistant.checkRoles(requestContext.getUser());
+ // delegate
+ return membersService.addMembersToGroup(id, members);
+ } catch (RODAException e) {
+ state = LogEntryState.FAILURE;
+ throw new RESTException(e);
+ } finally {
+ // register action
+ controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_GROUP_PARAM, id,
+ RodaConstants.CONTROLLER_SELECTED_ITEMS_PARAM, members);
+ }
+ }
+
+ @Override
+ public User removeGroupsFromUser(String id, String groupID) {
+ ControllerAssistant controllerAssistant = new ControllerAssistant() {};
+ RequestContext requestContext = RequestUtils.parseHTTPRequest(request);
+
+ LogEntryState state = LogEntryState.SUCCESS;
+
+ try {
+ // check user permissions
+ controllerAssistant.checkRoles(requestContext.getUser());
+ // delegate
+ return membersService.removeGroupFromUser(id, groupID);
+ } catch (RODAException e) {
+ state = LogEntryState.FAILURE;
+ throw new RESTException(e);
+ } finally {
+ // register action
+ controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_USER_PARAM, id,
+ RodaConstants.CONTROLLER_GROUP_PARAM, groupID);
+ }
+ }
+
+ @Override
+ public Group removeMembersFromGroup(String id, String userId) {
+ ControllerAssistant controllerAssistant = new ControllerAssistant() {};
+ RequestContext requestContext = RequestUtils.parseHTTPRequest(request);
+
+ LogEntryState state = LogEntryState.SUCCESS;
+
+ try {
+ // check user permissions
+ controllerAssistant.checkRoles(requestContext.getUser());
+ // delegate
+ return membersService.removeMemberFromGroup(id, userId);
+ } catch (RODAException e) {
+ state = LogEntryState.FAILURE;
+ throw new RESTException(e);
+ } finally {
+ // register action
+ controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_GROUP_PARAM, id,
+ RodaConstants.CONTROLLER_USER_PARAM, userId);
+ }
+ }
+
+ @Override
+ public Set getUserGroups(String id) {
+ ControllerAssistant controllerAssistant = new ControllerAssistant() {};
+ RequestContext requestContext = RequestUtils.parseHTTPRequest(request);
+
+ LogEntryState state = LogEntryState.SUCCESS;
+
+ try {
+ // check user permissions
+ controllerAssistant.checkRoles(requestContext.getUser());
+ // delegate
+ return membersService.getGroupFromUser(id);
+ } catch (RODAException e) {
+ state = LogEntryState.FAILURE;
+ throw new RESTException(e);
+ } finally {
+ // register action
+ controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_USER_PARAM, id);
+ }
+ }
+
+ @Override
+ public Set getGroupMembers(String id) {
+ ControllerAssistant controllerAssistant = new ControllerAssistant() {};
+ RequestContext requestContext = RequestUtils.parseHTTPRequest(request);
+
+ LogEntryState state = LogEntryState.SUCCESS;
+
+ try {
+ // check user permissions
+ controllerAssistant.checkRoles(requestContext.getUser());
+ // delegate
+ return membersService.getGroupMembers(id);
+ } catch (RODAException e) {
+ state = LogEntryState.FAILURE;
+ throw new RESTException(e);
+ } finally {
+ // register action
+ controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_GROUP_PARAM, id);
+ }
}
@Override
@@ -650,7 +804,7 @@ public User updateUser(@RequestBody UpdateUserRequest userRequest) {
} finally {
// register action
controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_USER_PARAM,
- requestContext.getUser());
+ requestContext.getUser().getFullName());
}
}
@@ -672,7 +826,7 @@ public User updateMyUser(@RequestBody UpdateUserRequest userOperations) {
} finally {
// register action
controllerAssistant.registerAction(requestContext, state, RodaConstants.CONTROLLER_USER_PARAM,
- userOperations.getUser());
+ userOperations.getUser().getFullName());
}
}
@@ -861,7 +1015,7 @@ public LongResponse count(@RequestBody CountRequest countRequest) {
}
@Override
- public List suggest(SuggestRequest suggestRequest) {
+ public List suggest(@RequestBody SuggestRequest suggestRequest) {
return indexService.suggest(suggestRequest, RODAMember.class);
}
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/DisposalRuleService.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/DisposalRuleService.java
index 42daad85df..6a66425597 100644
--- a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/DisposalRuleService.java
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/DisposalRuleService.java
@@ -8,8 +8,13 @@
package org.roda.wui.api.v2.services;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
import org.roda.core.data.common.RodaConstants;
import org.roda.core.data.exceptions.AlreadyExistsException;
@@ -18,6 +23,7 @@
import org.roda.core.data.exceptions.GenericException;
import org.roda.core.data.exceptions.NotFoundException;
import org.roda.core.data.exceptions.RequestNotValidException;
+import org.roda.core.data.v2.disposal.rule.ChangeOrderRequest;
import org.roda.core.data.v2.disposal.rule.ConditionType;
import org.roda.core.data.v2.disposal.rule.DisposalRule;
import org.roda.core.data.v2.disposal.rule.DisposalRules;
@@ -26,7 +32,9 @@
import org.roda.core.data.v2.disposal.schedule.DisposalSchedules;
import org.roda.core.data.v2.index.filter.Filter;
import org.roda.core.data.v2.index.filter.SimpleFilterParameter;
+import org.roda.core.data.v2.index.select.SelectedItems;
import org.roda.core.data.v2.index.select.SelectedItemsFilter;
+import org.roda.core.data.v2.index.select.SelectedItemsList;
import org.roda.core.data.v2.ip.AIPState;
import org.roda.core.data.v2.ip.IndexedAIP;
import org.roda.core.data.v2.jobs.Job;
@@ -81,6 +89,73 @@ public Job applyDisposalRules(User user, boolean overrideManualAssociations)
ApplyDisposalRulesPlugin.class, user, pluginParameters, "Could not execute apply disposal rules to repository");
}
+ public void changeDisposalRuleOrder(RequestContext context, ChangeOrderRequest request)
+ throws AuthorizationDeniedException, RequestNotValidException, IOException, GenericException, NotFoundException {
+ ModelService modelService = context.getModelService();
+ DisposalRules disposalRules = modelService.listDisposalRules();
+
+ SelectedItems disposalRuleSelectedItems = CommonServicesUtils.convertSelectedItems(request.getItems(),
+ DisposalRule.class);
+ if (disposalRuleSelectedItems instanceof SelectedItemsList itemsList) {
+ Set selectedIds = new HashSet<>(itemsList.getIds());
+
+ List selectedRules = new ArrayList<>();
+ List remainingRules = new ArrayList<>();
+
+ for (DisposalRule rule : disposalRules.getObjects()) {
+ if (selectedIds.contains(rule.getId())) {
+ selectedRules.add(rule);
+ } else {
+ remainingRules.add(rule);
+ }
+ }
+
+ // 3. Calculate the 0-based insertion index
+ int targetIndex = 0;
+
+ switch (request.getPosition()) {
+ case TOP:
+ break;
+ case BOTTOM:
+ targetIndex = remainingRules.size();
+ break;
+ case POSITION:
+ // Convert the 1-based UI position to a 0-based list index
+ targetIndex = request.getNewOrder() - 1;
+
+ // Safety bounds checks
+ if (targetIndex < 0) {
+ targetIndex = 0;
+ } else if (targetIndex > remainingRules.size()) {
+ targetIndex = remainingRules.size();
+ }
+ break;
+ }
+
+ // 4. Insert the selected block into the remaining list
+ remainingRules.addAll(targetIndex, selectedRules);
+
+ // 5. Reassign the "order" integer and collect the ones that actually changed
+ List rulesToUpdate = new ArrayList<>();
+
+ for (int i = 0; i < remainingRules.size(); i++) {
+ DisposalRule rule = remainingRules.get(i);
+ int newExpectedOrder = i; // 1-based ordering for the database
+
+ // Only update if the order actually changed to save database/index calls
+ if (rule.getOrder() == null || rule.getOrder() != newExpectedOrder) {
+ rule.setOrder(newExpectedOrder);
+ rulesToUpdate.add(rule);
+ }
+ }
+
+ // 6. Save the updated rules to your IndexService / Database
+ for (DisposalRule rule : rulesToUpdate) {
+ context.getModelService().updateDisposalRule(rule, context.getUser().getName());
+ }
+ }
+ }
+
public void validateDisposalRule(DisposalRule disposalRule, ModelService model)
throws GenericException, AuthorizationDeniedException, RequestNotValidException, IOException {
DisposalSchedules disposalSchedules = model.listDisposalSchedules();
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/DisposalScheduleService.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/DisposalScheduleService.java
index 1c173633a6..28aba79dce 100644
--- a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/DisposalScheduleService.java
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/DisposalScheduleService.java
@@ -50,7 +50,7 @@ public DisposalSchedules getDisposalSchedules(ModelService model)
public DisposalSchedule retrieveDisposalSchedule(ModelService model, String id)
throws AuthorizationDeniedException, RequestNotValidException, NotFoundException, GenericException {
- return model.retrieveDisposalSchedule(id);
+ return model.retrieveDisposalSchedule(id);
}
public DisposalSchedule updateDisposalSchedule(DisposalSchedule schedule, RequestContext requestContext)
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/IndexService.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/IndexService.java
index 898514443e..e0a1a758a0 100644
--- a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/IndexService.java
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/IndexService.java
@@ -23,7 +23,6 @@
import org.roda.core.data.v2.index.SuggestRequest;
import org.roda.core.data.v2.index.sort.Sorter;
import org.roda.core.data.v2.index.sublist.Sublist;
-import org.roda.core.data.v2.user.User;
import org.roda.wui.api.v2.controller.RequestHandler;
import org.roda.wui.api.v2.exceptions.RESTException;
import org.roda.wui.api.v2.stream.FacetsCSVOutputStream;
@@ -153,7 +152,7 @@ public Long count(Class returnClass, CountRequest reque
public Long process(RequestContext requestContext, RequestControllerAssistant controllerAssistant)
throws RODAException, RESTException {
controllerAssistant.setParameters(RodaConstants.CONTROLLER_CLASS_PARAM, returnClass.getSimpleName(),
- RodaConstants.CONTROLLER_FILTER_PARAM, request.getFilter().toString());
+ RodaConstants.CONTROLLER_FILTER_PARAM, String.valueOf(request.getFilter()));
return requestContext.getIndexService().count(returnClass, request.getFilter(), requestContext.getUser(),
request.isOnlyActive());
}
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/MembersService.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/MembersService.java
index 437feef11d..8ef3124a3a 100644
--- a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/MembersService.java
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/v2/services/MembersService.java
@@ -15,11 +15,14 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
import org.apache.commons.io.IOUtils;
+import org.apache.solr.client.solrj.util.ClientUtils;
import org.joda.time.DateTime;
import org.roda.core.RodaCoreFactory;
import org.roda.core.common.Messages;
@@ -41,6 +44,8 @@
import org.roda.core.data.exceptions.RequestNotValidException;
import org.roda.core.data.exceptions.UserAlreadyExistsException;
import org.roda.core.data.v2.generics.MetadataValue;
+import org.roda.core.data.v2.generics.select.SelectedItemsListRequest;
+import org.roda.core.data.v2.generics.select.SelectedItemsRequest;
import org.roda.core.data.v2.index.filter.Filter;
import org.roda.core.data.v2.index.select.SelectedItems;
import org.roda.core.data.v2.index.select.SelectedItemsFilter;
@@ -57,8 +62,11 @@
import org.roda.core.data.v2.validation.ValidationException;
import org.roda.core.index.IndexService;
import org.roda.core.model.ModelService;
+import org.roda.core.model.utils.LdapGroup;
+import org.roda.core.model.utils.LdapUtility;
import org.roda.core.model.utils.UserUtility;
import org.roda.core.plugins.base.maintenance.ChangeRodaMemberStatusPlugin;
+import org.roda.core.repository.ldap.LdapGroupRepository;
import org.roda.core.util.IdUtils;
import org.roda.wui.api.v2.exceptions.RESTException;
import org.roda.wui.api.v2.utils.CommonServicesUtils;
@@ -69,6 +77,8 @@
import org.roda.wui.common.server.ServerTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import jakarta.servlet.http.HttpServletRequest;
@@ -82,6 +92,9 @@ public class MembersService {
private static final Logger LOGGER = LoggerFactory.getLogger(MembersService.class);
private static final String RECAPTCHA_CODE_SECRET_PROPERTY = "ui.google.recaptcha.code.secret";
+ @Autowired
+ LdapGroupRepository ldapGroupRepository;
+
private static List getMemberUuidFromSelectedItems(SelectedItems members)
throws GenericException, RequestNotValidException {
List uuids = new ArrayList<>();
@@ -202,8 +215,8 @@ public void deleteUser(String username) throws GenericException, AuthorizationDe
RodaCoreFactory.getIndexService().commit(true, RODAMember.class);
}
- public Group retrieveGroup(String groupname) throws GenericException, NotFoundException {
- return RodaCoreFactory.getModelService().retrieveGroup(groupname);
+ public Group retrieveGroup(String groupName) throws GenericException, NotFoundException {
+ return RodaCoreFactory.getModelService().retrieveGroup(groupName);
}
public Job changeActiveMembers(SelectedItems selectedItems, boolean active, User user)
@@ -258,9 +271,10 @@ public Group createGroup(Group group) throws GenericException, AlreadyExistsExce
return newGroup;
}
- public void updateGroup(Group group) throws GenericException, NotFoundException, AuthorizationDeniedException {
- RodaCoreFactory.getModelService().updateGroup(group, true);
+ public Group updateGroup(Group group) throws GenericException, NotFoundException, AuthorizationDeniedException {
+ Group updatedGroup = RodaCoreFactory.getModelService().updateGroup(group, true);
RodaCoreFactory.getIndexService().commit(true, RODAMember.class);
+ return updatedGroup;
}
public void deleteGroup(String groupname) throws GenericException, AuthorizationDeniedException {
@@ -297,6 +311,91 @@ public User confirmUserEmail(String username, String email, String emailConfirma
return RodaCoreFactory.getModelService().confirmUserEmail(username, email, emailConfirmationToken, true, true);
}
+ public User addGroupsToUser(String id, SelectedItemsRequest groups)
+ throws GenericException, AuthorizationDeniedException, AlreadyExistsException, NotFoundException {
+ if (groups instanceof SelectedItemsListRequest listRequest) {
+ User user = RodaCoreFactory.getModelService().retrieveUser(id);
+ Set collect = listRequest.getIds().stream().map(m -> m.replace("group-", "")).collect(Collectors.toSet());
+ user.getGroups().addAll(collect);
+ RodaCoreFactory.getModelService().updateUser(user, null, true);
+ RodaCoreFactory.getIndexService().commit(true, RODAMember.class);
+ return user;
+ }
+
+ return new User();
+ }
+
+ public Group addMembersToGroup(String id, SelectedItemsRequest members)
+ throws GenericException, AuthorizationDeniedException, AlreadyExistsException, NotFoundException, IllegalOperationException {
+ if (members instanceof SelectedItemsListRequest listRequest) {
+ Set collect = listRequest.getIds().stream().map(m -> m.replace("user-", "")).collect(Collectors.toSet());
+
+ collect.removeIf(p -> UserUtility.getLdapUtility().isProtectedUser(p));
+
+ if (collect.isEmpty()) {
+ throw new IllegalOperationException("All selected users are protected and cannot be added to the group");
+ }
+
+ Group group = RodaCoreFactory.getModelService().updateGroupMembers(id, collect, true, false);
+ RodaCoreFactory.getIndexService().commit(true, RODAMember.class);
+ return group;
+ }
+
+ return new Group();
+ }
+
+ public Set getGroupFromUser(String id) throws NotFoundException, GenericException {
+ Set groups = new HashSet<>();
+ RodaCoreFactory.getModelService().retrieveUser(id).getGroups().forEach(groupName -> {
+ try {
+ Group group = RodaCoreFactory.getModelService().retrieveGroup(groupName);
+ group.getUsers().clear();
+ groups.add(group);
+ } catch (GenericException | NotFoundException e) {
+ LOGGER.error("Error retrieving group {}", groupName, e);
+ }
+ });
+
+ return groups;
+ }
+
+ public Set getGroupMembers(String id) throws NotFoundException, GenericException {
+ Set members = new HashSet<>();
+ RodaCoreFactory.getModelService().retrieveGroup(id).getUsers().forEach(userId -> {
+ try {
+ User user = RodaCoreFactory.getModelService().retrieveUser(userId);
+ user.getGroups().clear();
+ user.setResetPasswordToken(null);
+ user.setResetPasswordTokenExpirationDate(null);
+ user.setEmailConfirmationToken(null);
+ user.setEmailConfirmationTokenExpirationDate(null);
+ members.add(user);
+ } catch (GenericException e) {
+ LOGGER.error("Error retrieving user {}", userId, e);
+ }
+ });
+
+ return members;
+ }
+
+ public User removeGroupFromUser(String id, String groupID)
+ throws GenericException, AuthorizationDeniedException, AlreadyExistsException, NotFoundException {
+ User user = RodaCoreFactory.getModelService().retrieveUser(id);
+ user.getGroups().remove(groupID);
+ RodaCoreFactory.getModelService().updateUser(user, null, true);
+ RodaCoreFactory.getIndexService().commit(true, RODAMember.class);
+ return user;
+ }
+
+ public Group removeMemberFromGroup(String id, String userId)
+ throws GenericException, AuthorizationDeniedException, AlreadyExistsException, NotFoundException {
+ User user = RodaCoreFactory.getModelService().retrieveUser(userId);
+ user.getGroups().remove(id);
+ RodaCoreFactory.getModelService().updateUser(user, null, true);
+ RodaCoreFactory.getIndexService().commit(true, RODAMember.class);
+ return RodaCoreFactory.getModelService().retrieveGroup(id);
+ }
+
public User updateUser(UpdateUserRequest request) throws GenericException, AlreadyExistsException, NotFoundException,
AuthorizationDeniedException, ValidationException, RequestNotValidException {
ModelService model = RodaCoreFactory.getModelService();
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseAIP.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseAIP.java
index b23c196d24..08e8fc3ef1 100644
--- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseAIP.java
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseAIP.java
@@ -346,7 +346,6 @@ private static void refresh(String id, AsyncCallback callback) {
});
}
});
-
}
private void updateSectionIdentification(BrowseAIPResponse response) {
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseLogEntryTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseLogEntryTabs.java
new file mode 100644
index 0000000000..3ff7cac8db
--- /dev/null
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseLogEntryTabs.java
@@ -0,0 +1,21 @@
+package org.roda.wui.client.browse.tabs;
+
+import com.google.gwt.safehtml.shared.SafeHtmlUtils;
+import com.google.gwt.user.client.ui.Widget;
+import org.roda.core.data.v2.log.LogEntry;
+
+/**
+ *
+ * @author Eduardo Teixeira
+ */
+public class BrowseLogEntryTabs extends Tabs {
+ public void init(LogEntry logEntry) {
+ // Details
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.detailsTab()), new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new DetailsTab(logEntry);
+ }
+ });
+ }
+}
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseNotificationsTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseNotificationsTabs.java
new file mode 100644
index 0000000000..a9c9a172a7
--- /dev/null
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseNotificationsTabs.java
@@ -0,0 +1,20 @@
+package org.roda.wui.client.browse.tabs;
+
+import com.google.gwt.safehtml.shared.SafeHtmlUtils;
+import com.google.gwt.user.client.ui.Widget;
+import org.roda.core.data.v2.notifications.Notification;
+
+/**
+ *
+ * @author Eduardo Teixeira
+ */
+public class BrowseNotificationsTabs extends Tabs {
+ public void init(Notification notification) {
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.detailsTab()), new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new DetailsTab(notification);
+ }
+ });
+ }
+}
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseRepresentationInformationTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseRepresentationInformationTabs.java
new file mode 100644
index 0000000000..5cb7184b55
--- /dev/null
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseRepresentationInformationTabs.java
@@ -0,0 +1,114 @@
+package org.roda.wui.client.browse.tabs;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.roda.core.data.common.RodaConstants;
+import org.roda.core.data.utils.RepresentationInformationUtils;
+import org.roda.core.data.v2.index.IsIndexed;
+import org.roda.core.data.v2.index.filter.Filter;
+import org.roda.core.data.v2.index.filter.FilterParameter;
+import org.roda.core.data.v2.index.filter.OrFiltersParameters;
+import org.roda.core.data.v2.index.filter.SimpleFilterParameter;
+import org.roda.core.data.v2.ip.IndexedAIP;
+import org.roda.core.data.v2.ip.IndexedFile;
+import org.roda.core.data.v2.ip.IndexedRepresentation;
+import org.roda.core.data.v2.ri.RepresentationInformation;
+import org.roda.wui.client.common.lists.utils.AsyncTableCellOptions;
+import org.roda.wui.client.common.lists.utils.ConfigurableAsyncTableCell;
+import org.roda.wui.client.common.lists.utils.ListBuilder;
+import org.roda.wui.client.common.search.SearchWrapper;
+
+import com.google.gwt.safehtml.shared.SafeHtmlUtils;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.SimplePanel;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ *
+ * @author Eduardo Teixeira
+ */
+public class BrowseRepresentationInformationTabs extends Tabs {
+ public void init(RepresentationInformation ri) {
+
+ List aipParams = new ArrayList<>();
+ List representationParams = new ArrayList<>();
+ List fileParams = new ArrayList<>();
+ initEntityFilters(ri, aipParams, representationParams, fileParams);
+
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.detailsTab()), new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new DetailsTab(ri);
+ }
+ });
+
+ createAndAddTab(SafeHtmlUtils.fromString(messages.representationInformationIntellectualEntitiesAssociations()),
+ () -> buildAssociationsTab(IndexedAIP.class, aipParams, "Search_AIPs"));
+
+ createAndAddTab(SafeHtmlUtils.fromString(messages.representationInformationRepresentationsAssociations()),
+ () -> buildAssociationsTab(IndexedRepresentation.class, representationParams, "Search_representations"));
+
+ createAndAddTab(SafeHtmlUtils.fromString(messages.representationInformationFilesAssociations()),
+ () -> buildAssociationsTab(IndexedFile.class, fileParams, "Search_files"));
+ }
+
+ private void initEntityFilters(RepresentationInformation ri, List aipParams,
+ List representationParams, List fileParams) {
+ for (String filter : ri.getFilters()) {
+ String[] parts = filter.split(RepresentationInformationUtils.REPRESENTATION_INFORMATION_FILTER_SEPARATOR);
+ if (parts.length < 3) {
+ continue;
+ }
+
+ if (RodaConstants.INDEX_AIP.equals(parts[0])) {
+ aipParams.add(new SimpleFilterParameter(parts[1], parts[2]));
+ } else if (RodaConstants.INDEX_REPRESENTATION.equals(parts[0])) {
+ representationParams.add(new SimpleFilterParameter(parts[1], parts[2]));
+ } else if (RodaConstants.INDEX_FILE.equals(parts[0])) {
+ fileParams.add(new SimpleFilterParameter(parts[1], parts[2]));
+ }
+ }
+ }
+
+ private Widget buildAssociationsTab(Class clazz, List params,
+ String listId) {
+
+ if (params == null || params.isEmpty()) {
+ if (IndexedAIP.class.equals(clazz)) {
+ return buildEmptyAssociationsCardLikeTab(messages.representationInformationIntellectualEntities(0, ""));
+ }
+ if (IndexedRepresentation.class.equals(clazz)) {
+ return buildEmptyAssociationsCardLikeTab(messages.representationInformationRepresentations(0, ""));
+ }
+ return buildEmptyAssociationsCardLikeTab(messages.representationInformationFiles(0, ""));
+ }
+ Filter filter = new Filter(new OrFiltersParameters(params));
+
+ ListBuilder listBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(),
+ new AsyncTableCellOptions<>(clazz, listId).withFilter(filter).withJustActive(false).bindOpener());
+
+ return new SearchWrapper(false).createListAndSearchPanel(listBuilder);
+ }
+
+ private Widget buildEmptyAssociationsCardLikeTab(String messageHtml) {
+ FlowPanel card = new FlowPanel();
+ card.addStyleName("roda6CardWithHeader");
+ card.addStyleName("wrapper");
+ card.addStyleName("skip_padding");
+
+ FlowPanel body = new FlowPanel();
+ body.addStyleName("cardBody");
+
+ SimplePanel info = new SimplePanel();
+ info.addStyleName("table-empty-inner");
+ HTML label = new HTML(messageHtml);
+ label.addStyleName("table-empty-inner-label");
+ info.setWidget(label);
+
+ body.add(info);
+ card.add(body);
+ return card;
+ }
+}
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseRiskTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseRiskTabs.java
new file mode 100644
index 0000000000..4539c6c530
--- /dev/null
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/BrowseRiskTabs.java
@@ -0,0 +1,46 @@
+package org.roda.wui.client.browse.tabs;
+
+import org.roda.core.data.common.RodaConstants;
+import org.roda.core.data.v2.index.filter.Filter;
+import org.roda.core.data.v2.index.filter.SimpleFilterParameter;
+import org.roda.core.data.v2.risks.IndexedRisk;
+import org.roda.core.data.v2.risks.RiskIncidence;
+import org.roda.wui.client.common.lists.RiskIncidenceList;
+import org.roda.wui.client.common.lists.utils.AsyncTableCellOptions;
+import org.roda.wui.client.common.lists.utils.ListBuilder;
+import org.roda.wui.client.common.search.SearchWrapper;
+
+import com.google.gwt.safehtml.shared.SafeHtmlUtils;
+import com.google.gwt.user.client.ui.Widget;
+
+public class BrowseRiskTabs extends Tabs {
+
+ public void init(IndexedRisk risk) {
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.detailsTab()), new TabContentBuilder() {
+ ;
+ @Override
+ public Widget buildTabWidget() {
+ return new DetailsTab(risk);
+ }
+ });
+
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.riskIncidences()), new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return buildIncidencesSearchWrapper(risk);
+ }
+ });
+ }
+
+ private Widget buildIncidencesSearchWrapper(IndexedRisk risk) {
+ String RISK_INCIDENCE_LIST_ID = "RiskShowPanel_riskIncidences";
+ Filter filter = new Filter(new SimpleFilterParameter(RodaConstants.RISK_INCIDENCE_RISK_ID, risk.getId()));
+
+ ListBuilder listBuilder = new ListBuilder<>(() -> new RiskIncidenceList(),
+ new AsyncTableCellOptions<>(RiskIncidence.class, RISK_INCIDENCE_LIST_ID).withSummary(messages.riskIncidences())
+ .withFilter(filter).bindOpener().withSearchPlaceholder(messages.riskIncidenceRegisterSearchPlaceHolder()));
+
+ return new SearchWrapper(false).createListAndSearchPanel(listBuilder);
+ }
+
+}
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java
index 734718e613..74d79ca634 100644
--- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DetailsTab.java
@@ -11,8 +11,16 @@
import org.roda.core.data.v2.ip.IndexedFile;
import org.roda.core.data.v2.ip.TransferredResource;
+import org.roda.core.data.v2.ri.RepresentationInformation;
+import org.roda.core.data.v2.notifications.Notification;
+import org.roda.core.data.v2.log.LogEntry;
+import org.roda.core.data.v2.risks.IndexedRisk;
+import org.roda.core.data.v2.risks.Risk;
import org.roda.wui.client.common.model.BrowseAIPResponse;
import org.roda.wui.client.common.model.BrowseRepresentationResponse;
+import org.roda.wui.client.planning.DetailsPanelRepresentationInformation;
+import org.roda.wui.client.management.DetailsPanelNotification;
+import org.roda.wui.client.management.DetailsPanelLogEntry;
import org.roda.wui.client.ingest.transfer.DetailsPanelTransferredResource;
import org.roda.wui.client.planning.DetailsPanelAIP;
import org.roda.wui.client.planning.DetailsPanelFile;
@@ -26,6 +34,7 @@
import com.google.gwt.user.client.ui.Widget;
import config.i18n.client.ClientMessages;
+import org.roda.wui.client.planning.DetailsPanelRisk;
/**
* @author Carlos Afonso
@@ -58,6 +67,13 @@ public DetailsTab(IndexedFile file, List riRules) {
content.add(detailsPanel);
}
+ public DetailsTab(RepresentationInformation ri){
+ initWidget(uiBinder.createAndBindUi(this));
+
+ DetailsPanelRepresentationInformation detailsPanel = new DetailsPanelRepresentationInformation(ri);
+ content.add(detailsPanel);
+ }
+
public DetailsTab(TransferredResource resource) {
initWidget(uiBinder.createAndBindUi(this));
@@ -65,6 +81,25 @@ public DetailsTab(TransferredResource resource) {
content.add(detailsPanel);
}
+ public DetailsTab(IndexedRisk risk) {
+ initWidget(uiBinder.createAndBindUi(this));
+
+ DetailsPanelRisk detailsPanel = new DetailsPanelRisk(risk, "RiskShowPanel_riskIncidences");
+ content.add(detailsPanel);
+ }
+
+ public DetailsTab(Notification notification) {
+ initWidget(uiBinder.createAndBindUi(this));
+ DetailsPanelNotification detailsPanel = new DetailsPanelNotification(notification);
+ content.add(detailsPanel);
+ }
+
+ public DetailsTab(LogEntry logEntry) {
+ initWidget(uiBinder.createAndBindUi(this));
+ DetailsPanelLogEntry detailsPanel = new DetailsPanelLogEntry(logEntry);
+ content.add(detailsPanel);
+ }
+
interface MyUiBinder extends UiBinder {
}
}
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalConfirmationTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalConfirmationTabs.java
new file mode 100644
index 0000000000..5ebb9b757d
--- /dev/null
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalConfirmationTabs.java
@@ -0,0 +1,55 @@
+package org.roda.wui.client.browse.tabs;
+
+import org.roda.core.data.common.RodaConstants;
+import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation;
+import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmationState;
+import org.roda.core.data.v2.index.filter.Filter;
+import org.roda.core.data.v2.index.filter.SimpleFilterParameter;
+import org.roda.core.data.v2.ip.AIPState;
+import org.roda.core.data.v2.ip.IndexedAIP;
+import org.roda.wui.client.common.lists.utils.AsyncTableCellOptions;
+import org.roda.wui.client.common.lists.utils.ConfigurableAsyncTableCell;
+import org.roda.wui.client.common.lists.utils.ListBuilder;
+import org.roda.wui.client.common.search.SearchWrapper;
+import org.roda.wui.client.common.utils.PermissionClientUtils;
+import org.roda.wui.client.disposal.confirmations.tabs.DisposalConfirmationDetailsPanel;
+
+import com.google.gwt.safehtml.shared.SafeHtmlUtils;
+import com.google.gwt.user.client.ui.Widget;
+
+public class DisposalConfirmationTabs extends Tabs {
+
+ public void init(DisposalConfirmation confirmation) {
+
+ int activeIndex = this.getSelectedTabIndex();
+
+ this.clear();
+
+ // 1. Clear any existing tabs before building new ones!
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.detailsTab()), new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new DisposalConfirmationDetailsPanel(confirmation);
+ }
+ });
+
+ // Check if user has permissions to see the AIP
+ if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_FIND_AIP) && DisposalConfirmationState.PENDING.equals(confirmation.getState())) {
+ ListBuilder aipsListBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(),
+ new AsyncTableCellOptions<>(IndexedAIP.class, "ShowDisposalSchedule_aips")
+ .withFilter(
+ new Filter(new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_CONFIRMATION_ID, confirmation.getId()),
+ new SimpleFilterParameter(RodaConstants.AIP_STATE, AIPState.ACTIVE.name())))
+ .withSummary(messages.listOfAIPs()).bindOpener());
+
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.allIntellectualEntities()), new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new SearchWrapper(false).createListAndSearchPanel(aipsListBuilder);
+ }
+ });
+ }
+
+ this.selectTabByIndex(activeIndex);
+ }
+}
\ No newline at end of file
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalHoldTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalHoldTabs.java
new file mode 100644
index 0000000000..e31f814f9c
--- /dev/null
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalHoldTabs.java
@@ -0,0 +1,57 @@
+package org.roda.wui.client.browse.tabs;
+
+import org.roda.core.data.common.RodaConstants;
+import org.roda.core.data.v2.disposal.hold.DisposalHold;
+import org.roda.core.data.v2.index.filter.Filter;
+import org.roda.core.data.v2.index.filter.SimpleFilterParameter;
+import org.roda.core.data.v2.ip.AIPState;
+import org.roda.core.data.v2.ip.IndexedAIP;
+import org.roda.wui.client.common.actions.Actionable;
+import org.roda.wui.client.common.actions.IndexedAIPDisposalHoldSearchWrapperActions;
+import org.roda.wui.client.common.lists.utils.AsyncTableCellOptions;
+import org.roda.wui.client.common.lists.utils.ConfigurableAsyncTableCell;
+import org.roda.wui.client.common.lists.utils.ListBuilder;
+import org.roda.wui.client.common.search.SearchWrapper;
+import org.roda.wui.client.common.utils.PermissionClientUtils;
+import org.roda.wui.client.disposal.hold.tabs.DisposalHoldDetailsPanel;
+
+import com.google.gwt.safehtml.shared.SafeHtmlUtils;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.Widget;
+
+public class DisposalHoldTabs extends Tabs {
+
+ public void init(DisposalHold hold, AsyncCallback actionCallback) {
+
+ int activeIndex = this.getSelectedTabIndex();
+
+ this.clear();
+
+ // 1. Clear any existing tabs before building new ones!
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.detailsTab()), new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new DisposalHoldDetailsPanel(hold, actionCallback);
+ }
+ });
+
+ // Check if user has permissions to see the AIP
+ if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_FIND_AIP)) {
+
+ ListBuilder aipsListBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(),
+ new AsyncTableCellOptions<>(IndexedAIP.class, "ShowDisposalSchedule_aips")
+ .withFilter(new Filter(new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_HOLDS_ID, hold.getId()),
+ new SimpleFilterParameter(RodaConstants.AIP_STATE, AIPState.ACTIVE.name())))
+ .withActionable(new IndexedAIPDisposalHoldSearchWrapperActions(hold)).withSummary(messages.listOfAIPs()).bindOpener());
+
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.disposalScheduleListAips()), new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new SearchWrapper(false).createListAndSearchPanel(aipsListBuilder);
+ }
+ });
+ }
+
+ this.selectTabByIndex(activeIndex);
+ }
+}
\ No newline at end of file
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalOverdueRecordsTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalOverdueRecordsTabs.java
new file mode 100644
index 0000000000..c0d7d78720
--- /dev/null
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalOverdueRecordsTabs.java
@@ -0,0 +1,92 @@
+package org.roda.wui.client.browse.tabs;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.safehtml.shared.SafeHtmlUtils;
+import com.google.gwt.user.client.ui.Widget;
+import config.i18n.client.ClientMessages;
+import org.roda.core.data.common.RodaConstants;
+import org.roda.core.data.v2.disposal.schedule.DisposalActionCode;
+import org.roda.core.data.v2.disposal.schedule.RetentionPeriodCalculation;
+import org.roda.core.data.v2.index.filter.DateIntervalFilterParameter;
+import org.roda.core.data.v2.index.filter.EmptyKeyFilterParameter;
+import org.roda.core.data.v2.index.filter.Filter;
+import org.roda.core.data.v2.index.filter.SimpleFilterParameter;
+import org.roda.core.data.v2.ip.AIPState;
+import org.roda.core.data.v2.ip.IndexedAIP;
+import org.roda.wui.client.common.actions.DisposalCreateConfirmationDestroyActions;
+import org.roda.wui.client.common.actions.DisposalCreateConfirmationReviewActions;
+import org.roda.wui.client.common.lists.utils.AsyncTableCellOptions;
+import org.roda.wui.client.common.lists.utils.ConfigurableAsyncTableCell;
+import org.roda.wui.client.common.lists.utils.ListBuilder;
+import org.roda.wui.client.common.search.SearchWrapper;
+
+import java.util.Date;
+
+public class DisposalOverdueRecordsTabs extends Tabs {
+
+ private static final DateTimeFormat formatter = DateTimeFormat.getFormat(RodaConstants.SIMPLE_DATE_FORMATTER);
+
+ private static final Filter SHOW_RECORDS_TO_REVIEW = new Filter(
+ new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_ACTION, DisposalActionCode.REVIEW.name()),
+ new SimpleFilterParameter(RodaConstants.AIP_STATE, AIPState.ACTIVE.name()),
+ new DateIntervalFilterParameter(RodaConstants.AIP_OVERDUE_DATE, RodaConstants.AIP_OVERDUE_DATE, null,
+ formatter.parse(formatter.format(new Date()))),
+ new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_HOLD_STATUS, Boolean.FALSE.toString()),
+ new EmptyKeyFilterParameter(RodaConstants.AIP_DISPOSAL_CONFIRMATION_ID));
+
+ private static final Filter SHOW_RECORDS_TO_DESTROY = new Filter(
+ new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_ACTION, DisposalActionCode.DESTROY.name()),
+ new SimpleFilterParameter(RodaConstants.AIP_STATE, AIPState.ACTIVE.name()),
+ new DateIntervalFilterParameter(RodaConstants.AIP_OVERDUE_DATE, RodaConstants.AIP_OVERDUE_DATE, null,
+ formatter.parse(formatter.format(new Date()))),
+ new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_HOLD_STATUS, Boolean.FALSE.toString()),
+ new EmptyKeyFilterParameter(RodaConstants.AIP_DISPOSAL_CONFIRMATION_ID));
+
+ private static final Filter SHOW_RECORDS_WITH_RETENTION_PERIOD_ERRORS = new Filter(new SimpleFilterParameter(
+ RodaConstants.AIP_DISPOSAL_RETENTION_PERIOD_CALCULATION, RetentionPeriodCalculation.ERROR.name()));
+
+ private static final ClientMessages messages = GWT.create(ClientMessages.class);
+
+ public void init() {
+
+ ListBuilder overdueRecordsListBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(),
+ new AsyncTableCellOptions<>(IndexedAIP.class, "DisposalOverdueRecords_aip").withSummary(messages.listOfAIPs())
+ .withFilter(SHOW_RECORDS_TO_DESTROY).withActionable(DisposalCreateConfirmationDestroyActions.get())
+ .withJustActive(true).bindOpener());
+
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.disposalConfirmationShowRecordsToDestroy()),
+ new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new SearchWrapper(false).createListAndSearchPanel(overdueRecordsListBuilder);
+ }
+ });
+
+ ListBuilder overdueRecordsToReviewListBuilder = new ListBuilder<>(
+ () -> new ConfigurableAsyncTableCell<>(),
+ new AsyncTableCellOptions<>(IndexedAIP.class, "DisposalOverdueRecords_aip").withSummary(messages.listOfAIPs())
+ .withFilter(SHOW_RECORDS_TO_REVIEW).withActionable(DisposalCreateConfirmationReviewActions.get())
+ .withJustActive(true).bindOpener());
+
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.disposalConfirmationShowRecordsToReview()),
+ new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new SearchWrapper(false).createListAndSearchPanel(overdueRecordsToReviewListBuilder);
+ }
+ });
+
+ ListBuilder recordsWithErrorListBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(),
+ new AsyncTableCellOptions<>(IndexedAIP.class, "DisposalOverdueRecords_aip").withSummary(messages.listOfAIPs())
+ .withFilter(SHOW_RECORDS_WITH_RETENTION_PERIOD_ERRORS).withJustActive(true).bindOpener());
+
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.disposalConfirmationShowRecordsRetentionPeriodCalculationError()), new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new SearchWrapper(false).createListAndSearchPanel(recordsWithErrorListBuilder);
+ }
+ });
+
+ }
+}
\ No newline at end of file
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalPolicyTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalPolicyTabs.java
new file mode 100644
index 0000000000..505f803569
--- /dev/null
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalPolicyTabs.java
@@ -0,0 +1,68 @@
+package org.roda.wui.client.browse.tabs;
+
+import java.util.List;
+
+import org.roda.core.data.common.RodaConstants;
+import org.roda.core.data.v2.disposal.hold.DisposalHold;
+import org.roda.core.data.v2.disposal.rule.DisposalRule;
+import org.roda.core.data.v2.disposal.schedule.DisposalSchedule;
+import org.roda.wui.client.common.actions.DisposalHoldSearchWrapperActions;
+import org.roda.wui.client.common.actions.DisposalRuleSearchWrapperActions;
+import org.roda.wui.client.common.actions.DisposalScheduleSearchWrapperActions;
+import org.roda.wui.client.common.lists.DisposalHoldList;
+import org.roda.wui.client.common.lists.DisposalRuleList;
+import org.roda.wui.client.common.lists.DisposalScheduleList;
+import org.roda.wui.client.common.lists.utils.AsyncTableCellOptions;
+import org.roda.wui.client.common.lists.utils.ListBuilder;
+import org.roda.wui.client.common.search.SearchWrapper;
+import org.roda.wui.client.common.utils.PermissionClientUtils;
+
+import com.google.gwt.safehtml.shared.SafeHtmlUtils;
+import com.google.gwt.user.client.ui.Widget;
+
+public class DisposalPolicyTabs extends Tabs {
+
+ public void init() {
+
+ if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_LIST_DISPOSAL_SCHEDULES)) {
+ ListBuilder scheduleListBuilder = new ListBuilder<>(() -> new DisposalScheduleList(),
+ new AsyncTableCellOptions<>(DisposalSchedule.class, "DisposalPolicyPage_disposalSchedules")
+ .withActionable(DisposalScheduleSearchWrapperActions.get()).withCsvDownloadButtonVisibility(false)
+ .bindOpener());
+
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.disposalSchedulesTitle()), new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new SearchWrapper(false).createListAndSearchPanel(scheduleListBuilder);
+ }
+ });
+ }
+
+ if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_LIST_DISPOSAL_HOLDS)) {
+ ListBuilder holdListBuilder = new ListBuilder<>(() -> new DisposalHoldList(),
+ new AsyncTableCellOptions<>(DisposalHold.class, "DisposalPolicyPage_disposalHolds")
+ .withActionable(DisposalHoldSearchWrapperActions.get()).withCsvDownloadButtonVisibility(false).bindOpener());
+
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.disposalHoldsTitle()), new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new SearchWrapper(false).createListAndSearchPanel(holdListBuilder);
+ }
+ });
+ }
+
+ if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_LIST_DISPOSAL_RULES)) {
+ ListBuilder ruleListBuilder = new ListBuilder<>(() -> new DisposalRuleList(),
+ new AsyncTableCellOptions<>(DisposalRule.class, "DisposalPolicyPage_disposalRules")
+ .withActionable(DisposalRuleSearchWrapperActions.get())
+ .withCsvDownloadButtonVisibility(false).bindOpener());
+
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.disposalRulesTitle()), new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new SearchWrapper(false).createListAndSearchPanel(ruleListBuilder);
+ }
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalRuleTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalRuleTabs.java
new file mode 100644
index 0000000000..cea2581211
--- /dev/null
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalRuleTabs.java
@@ -0,0 +1,23 @@
+package org.roda.wui.client.browse.tabs;
+
+import org.roda.core.data.v2.disposal.rule.DisposalRule;
+import org.roda.wui.client.common.actions.Actionable;
+import org.roda.wui.client.disposal.rule.tabs.DisposalRuleDetailsPanel;
+
+import com.google.gwt.safehtml.shared.SafeHtmlUtils;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.Widget;
+
+public class DisposalRuleTabs extends Tabs {
+
+ public void init(DisposalRule rule, AsyncCallback actionCallback) {
+ this.clear();
+
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.detailsTab()), new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new DisposalRuleDetailsPanel(rule, actionCallback);
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalScheduleTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalScheduleTabs.java
new file mode 100644
index 0000000000..9b92b8665c
--- /dev/null
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/DisposalScheduleTabs.java
@@ -0,0 +1,57 @@
+package org.roda.wui.client.browse.tabs;
+
+import org.roda.core.data.common.RodaConstants;
+import org.roda.core.data.v2.disposal.schedule.DisposalSchedule;
+import org.roda.core.data.v2.index.filter.Filter;
+import org.roda.core.data.v2.index.filter.SimpleFilterParameter;
+import org.roda.core.data.v2.ip.AIPState;
+import org.roda.core.data.v2.ip.IndexedAIP;
+import org.roda.wui.client.common.actions.Actionable;
+import org.roda.wui.client.common.actions.IndexedAIPDisposalScheduleSearchWrapperActions;
+import org.roda.wui.client.common.lists.utils.AsyncTableCellOptions;
+import org.roda.wui.client.common.lists.utils.ConfigurableAsyncTableCell;
+import org.roda.wui.client.common.lists.utils.ListBuilder;
+import org.roda.wui.client.common.search.SearchWrapper;
+import org.roda.wui.client.common.utils.PermissionClientUtils;
+import org.roda.wui.client.disposal.schedule.tabs.DisposalScheduleDetailsPanel;
+
+import com.google.gwt.safehtml.shared.SafeHtmlUtils;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.Widget;
+
+public class DisposalScheduleTabs extends Tabs {
+
+ public void init(DisposalSchedule schedule, AsyncCallback actionCallback) {
+
+ int activeIndex = this.getSelectedTabIndex();
+
+ this.clear();
+
+ // 1. Clear any existing tabs before building new ones!
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.detailsTab()), new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new DisposalScheduleDetailsPanel(schedule, actionCallback);
+ }
+ });
+
+ // Check if user has permissions to see the AIP
+ if (PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_FIND_AIP)) {
+
+ ListBuilder aipsListBuilder = new ListBuilder<>(() -> new ConfigurableAsyncTableCell<>(),
+ new AsyncTableCellOptions<>(IndexedAIP.class, "ShowDisposalSchedule_aips")
+ .withFilter(new Filter(new SimpleFilterParameter(RodaConstants.AIP_DISPOSAL_SCHEDULE_ID, schedule.getId()),
+ new SimpleFilterParameter(RodaConstants.AIP_STATE, AIPState.ACTIVE.name())))
+ .withActionable(IndexedAIPDisposalScheduleSearchWrapperActions.get()).withSummary(messages.listOfAIPs()).bindOpener());
+
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.disposalScheduleListAips()), new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new SearchWrapper(false).createListAndSearchPanel(aipsListBuilder);
+ }
+ });
+ }
+
+ this.selectTabByIndex(activeIndex);
+ }
+}
\ No newline at end of file
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/RODAMemberTabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/RODAMemberTabs.java
new file mode 100644
index 0000000000..53a51775df
--- /dev/null
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/RODAMemberTabs.java
@@ -0,0 +1,58 @@
+package org.roda.wui.client.browse.tabs;
+
+import com.google.gwt.safehtml.shared.SafeHtmlUtils;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.Widget;
+import org.roda.core.data.common.RodaConstants;
+import org.roda.core.data.v2.user.RODAMember;
+import org.roda.wui.client.common.actions.Actionable;
+import org.roda.wui.client.common.utils.PermissionClientUtils;
+import org.roda.wui.client.management.members.tabs.AccessKeysTab;
+import org.roda.wui.client.management.members.tabs.RODAMemberDetailsPanel;
+import org.roda.wui.client.management.members.tabs.RODAMemberGroupsTab;
+import org.roda.wui.client.management.members.tabs.RODAMemberPermissionsTab;
+
+public class RODAMemberTabs extends Tabs {
+
+ public void init(RODAMember member, AsyncCallback actionCallback) {
+
+ int activeIndex = this.getSelectedTabIndex();
+
+ this.clear();
+
+ // 1. Clear any existing tabs before building new ones!
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.detailsTab()), new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new RODAMemberDetailsPanel(member, actionCallback);
+ }
+ });
+
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(member.isUser() ? messages.groups() : messages.membersTabTitle()),
+ new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new RODAMemberGroupsTab(member, actionCallback);
+ }
+ });
+
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.permissionsTab()), new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new RODAMemberPermissionsTab(member, actionCallback);
+ }
+ });
+
+ if (member.isUser() && PermissionClientUtils.hasPermissions(RodaConstants.PERMISSION_METHOD_REVOKE_ACCESS_TOKEN,
+ RodaConstants.PERMISSION_METHOD_REGENERATE_ACCESS_TOKEN, RodaConstants.PERMISSION_METHOD_DELETE_ACCESS_TOKEN)) {
+ createAndAddTab(SafeHtmlUtils.fromSafeConstant(messages.showAccessKeyTitle()), new TabContentBuilder() {
+ @Override
+ public Widget buildTabWidget() {
+ return new AccessKeysTab(member, actionCallback);
+ }
+ });
+ }
+
+ this.selectTabByIndex(activeIndex);
+ }
+}
\ No newline at end of file
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/Tabs.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/Tabs.java
index 75f04f1dfe..3103c39022 100644
--- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/Tabs.java
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/tabs/Tabs.java
@@ -22,6 +22,7 @@
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
+import com.google.gwt.storage.client.Storage;
import config.i18n.client.ClientMessages;
@@ -32,15 +33,14 @@
public class Tabs extends Composite {
protected static final ClientMessages messages = GWT.create(ClientMessages.class);
private static Tabs.MyUiBinder uiBinder = GWT.create(Tabs.MyUiBinder.class);
-
+ private final Map tabs;
@UiField
FlowPanel tabButtons;
@UiField
SimplePanel tabContentWrapper;
-
private Widget selectedTab;
- private final Map tabs;
private TabContentBuilder defaultContent;
+ private String storageKey = null;
public Tabs() {
initWidget(uiBinder.createAndBindUi(this));
@@ -55,6 +55,24 @@ public Widget buildTabWidget() {
selectTab(null);
}
+ public void setStorageKey(String storageKey) {
+ this.storageKey = storageKey;
+
+ // Immediately attempt to restore the saved tab index
+ Storage sessionStorage = Storage.getSessionStorageIfSupported();
+ if (sessionStorage != null) {
+ String savedIndexStr = sessionStorage.getItem(storageKey);
+ if (savedIndexStr != null) {
+ try {
+ int activeIndex = Integer.parseInt(savedIndexStr);
+ selectTabByIndex(activeIndex);
+ } catch (NumberFormatException e) {
+ // If parsing fails, it safely ignores and keeps the default tab
+ }
+ }
+ }
+ }
+
public void setDefaultContent(TabContentBuilder tabContentBuilder) {
defaultContent = tabContentBuilder;
}
@@ -110,6 +128,14 @@ protected void selectTab(Widget tabButtonContainer) {
selectedTab = tabButtonContainer;
tab.getKey().addStyleName("tabSelected");
tabContentWrapper.setWidget(tab.getValue().buildTabWidget());
+
+ if (storageKey != null) {
+ Storage sessionStorage = Storage.getSessionStorageIfSupported();
+ if (sessionStorage != null) {
+ sessionStorage.setItem(storageKey, String.valueOf(getSelectedTabIndex()));
+ }
+ }
+
} else {
tab.getKey().removeStyleName("tabSelected");
}
@@ -120,6 +146,57 @@ protected void selectTab(Widget tabButtonContainer) {
}
}
+ public void clear() {
+ tabButtons.clear();
+ tabContentWrapper.clear();
+ tabs.clear();
+ selectedTab = null;
+ }
+
+ public int getSelectedTabIndex() {
+ if (selectedTab == null) {
+ return 0; // Default to first tab
+ }
+ return tabButtons.getWidgetIndex(selectedTab);
+ }
+
+ public void selectTabByIndex(int index) {
+ if (index >= 0 && index < tabButtons.getWidgetCount()) {
+ Widget tabToSelect = tabButtons.getWidget(index);
+ selectTab(tabToSelect);
+ }
+ }
+
+ // --- NEW: Add the onLoad method ---
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+
+ // 1. Dynamically generate a unique key based on the child class (e.g.,
+ // "org.roda...DisposalPolicyTabs_ActiveIndex")
+ if (this.storageKey == null) {
+ this.storageKey = this.getClass().getName() + "_ActiveIndex";
+ }
+
+ // 2. Restore the saved index now that the tabs are built and the widget is
+ // attached
+ Storage sessionStorage = Storage.getSessionStorageIfSupported();
+ if (sessionStorage != null) {
+ String savedIndexStr = sessionStorage.getItem(storageKey);
+ if (savedIndexStr != null) {
+ try {
+ int activeIndex = Integer.parseInt(savedIndexStr);
+ // Only select if the saved index is valid for the current number of tabs
+ if (activeIndex >= 0 && activeIndex < tabButtons.getWidgetCount()) {
+ selectTabByIndex(activeIndex);
+ }
+ } catch (NumberFormatException e) {
+ // Defaults gracefully if parsing fails
+ }
+ }
+ }
+ }
+
public interface TabContentBuilder {
public Widget buildTabWidget();
}
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/BrowseRepresentationInformationActionsToolbar.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/BrowseRepresentationInformationActionsToolbar.java
new file mode 100644
index 0000000000..08d48a1839
--- /dev/null
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/BrowseRepresentationInformationActionsToolbar.java
@@ -0,0 +1,33 @@
+package org.roda.wui.client.common;
+
+import java.util.List;
+
+import org.roda.core.data.v2.ri.RepresentationInformation;
+import org.roda.wui.client.common.actions.RepresentationInformationActions;
+import org.roda.wui.client.common.actions.model.ActionableObject;
+import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder;
+
+/**
+ *
+ * @author Eduardo Teixeira
+ */
+public class BrowseRepresentationInformationActionsToolbar
+ extends BrowseObjectActionsToolbar {
+ public void buildIcon() {
+ setIcon(null);
+ }
+
+ public void buildTags() {
+ // do nothing
+ }
+
+ public void buildActions() {
+ this.actions.clear();
+ RepresentationInformationActions representationInformationActions;
+ representationInformationActions = RepresentationInformationActions.get();
+ this.actions.add(new ActionableWidgetBuilder(representationInformationActions)
+ .buildGroupedListWithObjects(new ActionableObject<>(object), List.of(),
+ List.of(RepresentationInformationActions.RepresentationInformationAction.START_PROCESS)));
+ }
+
+}
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/BrowseRiskActionsToolbar.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/BrowseRiskActionsToolbar.java
new file mode 100644
index 0000000000..86a6717002
--- /dev/null
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/BrowseRiskActionsToolbar.java
@@ -0,0 +1,39 @@
+package org.roda.wui.client.common;
+
+import java.util.List;
+
+import org.roda.core.data.v2.risks.IndexedRisk;
+import org.roda.wui.client.common.actions.RiskActions;
+import org.roda.wui.client.common.actions.model.ActionableObject;
+import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder;
+
+/**
+ *
+ * @author Eduardo Teixeira
+ */
+public class BrowseRiskActionsToolbar extends BrowseObjectActionsToolbar {
+ public void buildIcon() {
+ setIcon("fa-solid fa-triangle-exclamation");
+ }
+
+ public void buildTags() {
+ // do nothing
+ }
+
+ public void buildActions() {
+ this.actions.clear();
+ if (object == null)
+ return;
+
+ RiskActions riskActions = RiskActions.get();
+ if (object.hasVersions()) {
+ riskActions = RiskActions.getWithHistory();
+ }
+ this.actions
+ .add(new ActionableWidgetBuilder(riskActions).buildGroupedListWithObjects(
+ new ActionableObject<>(object), List.of(RiskActions.IndexedRiskAction.EDIT,
+ RiskActions.IndexedRiskAction.REMOVE, RiskActions.IndexedRiskAction.START_PROCESS),
+ List.of(RiskActions.IndexedRiskAction.START_PROCESS)));
+ }
+
+}
diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/DisposalConfirmationActionsToolbar.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/DisposalConfirmationActionsToolbar.java
new file mode 100644
index 0000000000..41a6e77d32
--- /dev/null
+++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/DisposalConfirmationActionsToolbar.java
@@ -0,0 +1,74 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE file at the root of the source
+ * tree and available online at
+ *
+ * https://github.com/keeps/roda
+ */
+package org.roda.wui.client.common;
+
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+import config.i18n.client.ClientMessages;
+import org.roda.core.data.v2.disposal.confirmation.DisposalConfirmation;
+import org.roda.wui.client.common.actions.DisposalConfirmationToolbarActions;
+import org.roda.wui.client.common.actions.model.ActionableObject;
+import org.roda.wui.client.common.actions.widgets.ActionableWidgetBuilder;
+import org.roda.wui.client.common.labels.Tag;
+
+/**
+ *
+ * @author Miguel Guimarães