Compare commits

..

27 Commits

Author SHA1 Message Date
Damian Ujma
cb863c646f ACS-7587 Improve permission test [ags][tas] 2024-05-29 13:05:54 +02:00
Damian Ujma
a2d1391aee ACS-7587 Increase timeout 2024-05-29 08:45:09 +02:00
Damian Ujma
818bcb09f3 ACS-7587 Fix intermittent failure 2024-05-27 14:01:49 +02:00
Damian Ujma
72e85076ad ACS-7587 Fix PMD issue 2024-05-27 12:19:43 +02:00
Damian Ujma
a69be867e8 ACS-7587 Fix PMD issues 2024-05-27 10:42:25 +02:00
Damian Ujma
fa6e0ded45 ACS-7587 Improve search query 2024-05-24 18:34:29 +02:00
Damian Ujma
ed0bbb6699 ACS-7587 Refactor code 2024-05-24 12:07:41 +02:00
Damian Ujma
8009a6d6dd ACS-7587 Add test files alternately 2024-05-20 13:17:48 +02:00
Damian Ujma
785bdb72ea ACS-7587 Refactor 2024-05-20 12:28:11 +02:00
Damian Ujma
66aef18862 ACS-7587 Fix PMD isues 2024-05-17 15:24:54 +02:00
Damian Ujma
7b516f24b6 ACS-7587 Fix PMD issues 2024-05-17 15:14:50 +02:00
Damian Ujma
0de1aca0f6 ACS-7587 Reimplement BulkStatusUpdater [ags][tas] 2024-05-16 17:16:47 +02:00
Damian Ujma
592dc35b6d ACS-7587 Change DefaultHoldBulkMonitor [ags][tas] 2024-05-15 16:22:21 +02:00
Damian Ujma
73724a8205 ACS-7587 Tests [ags][tas] 2024-05-15 15:16:54 +02:00
Damian Ujma
c8c1102431 ACS-7587 Refactor [ags] 2024-05-15 14:41:08 +02:00
Damian.Ujma@hyland.com
c31ae1fe33 ACS-7587 Remove merge leftovers 2024-05-15 13:12:10 +02:00
Damian Ujma
82e9f0452d Merge branch 'feature/ACS-7557_design_bulk_api' into feature/ACS-7587_implement_v1_bulk_api_to_add_items
# Conflicts:
#	amps/ags/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml
2024-05-15 13:05:19 +02:00
Damian Ujma
377f546a9b Merge branch 'master' into feature/ACS-7556_bulk_update_in_legal_holds 2024-05-15 12:34:50 +02:00
Damian Ujma
07dcba972f ACS-7557 Refactor code 2024-05-15 12:33:21 +02:00
Damian Ujma
d7f9ed1cf0 ACS-7557 Reimplement task container 2024-05-10 16:33:53 +02:00
Damian Ujma
4eccb77fa8 ACS-7557 Add Legal Holds Bulk v1 API (#2624)
* ACS-7557 Add Legal Holds Bulk v1 API

* ACS-7557 Improve v1 API

* ACS-7557 Replace processId with bulkStatusId
2024-05-10 16:29:54 +02:00
Damian Ujma
07352336c5 ACS-7557 Refactor 2024-05-09 16:39:09 +02:00
Damian Ujma
b5ce847bb1 ACS-7557 Add comments + logging 2024-05-09 16:27:10 +02:00
Damian Ujma
34925b497b ACS-7557 Add IT tests 2024-05-09 15:11:59 +02:00
Damian.Ujma@hyland.com
8986d03a2f ACS-7557 Add permissions checks [ags] 2024-05-06 15:24:27 +02:00
Damian.Ujma@hyland.com
502c996c9e ACS-7557 Fix [ags] 2024-04-29 16:15:03 +02:00
Damian.Ujma@hyland.com
a22e7d23f0 ACS-7557 Add bulk API design 2024-04-29 16:00:40 +02:00
186 changed files with 1439 additions and 7365 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -34,12 +34,12 @@ jobs:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.0.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.0.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.0.0
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2
- name: "Init"
run: bash ./scripts/ci/init.sh
- uses: Alfresco/alfresco-build-tools/.github/actions/configure-git-author@v7.0.0
- uses: Alfresco/alfresco-build-tools/.github/actions/configure-git-author@v1.35.2
with:
username: ${{ env.GIT_USERNAME }}
email: ${{ env.GIT_EMAIL }}
@@ -63,12 +63,12 @@ jobs:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v7.0.0
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v7.0.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v7.0.0
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2
- uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2
- name: "Init"
run: bash ./scripts/ci/init.sh
- uses: Alfresco/alfresco-build-tools/.github/actions/configure-git-author@v7.0.0
- uses: Alfresco/alfresco-build-tools/.github/actions/configure-git-author@v1.35.2
with:
username: ${{ env.GIT_USERNAME }}
email: ${{ env.GIT_EMAIL }}

View File

@@ -2,57 +2,38 @@
[![Build Status](https://github.com/Alfresco/alfresco-community-repo/actions/workflows/master_release.yml/badge.svg?branch=master)](https://github.com/Alfresco/alfresco-community-repo/actions/workflows/master_release.yml)
## Table of Contents
1. [Content](#content)
2. [Artifacts](#artifacts)
3. [Setup](#setting-up-and-building-your-development-environment)
4. [Branches](#branches)
5. [Contributing](#contributing-guide)
6. [Helpful links](#helpful-links)
## Content
Alfresco Community Repository contains following libraries:
### Alfresco Core
Core is a library packaged as a jar file which contains the following:
#### Alfresco Core
Alfresco Core is a library packaged as a jar file which contains the following:
* Various helpers and utils
* Canned queries interface and supporting classes
* Generic encryption supporting classes
### Alfresco Data Model
Data Model is a library packaged as a jar file which contains the following:
#### Alfresco Data Model
Data model is a library packaged as a jar file which contains the following:
* Dictionary, Repository and Search Services interfaces
* Models for data types and Dictionary implementation
* Parsers
### Alfresco Repository
#### Alfresco Repository
Repository is a library packaged as a jar file which contains the following:
* DAOs and SQL scripts
* Various Service implementations
* Utility classes
### Alfresco Remote API
#### Alfresco Remote API
Remote API is a library packaged as a jar file which contains the following:
* REST API framework
* WebScript implementations including [V1 REST APIs](https://hub.alfresco.com/t5/alfresco-content-services-blog/v1-rest-api-10-things-you-should-know/ba-p/287692)
* [OpenCMIS](https://chemistry.apache.org/java/opencmis.html) implementations
## Artifacts
#### Artifacts
The artifacts can be obtained by:
* downloading from [Alfresco maven repository](https://artifacts.alfresco.com/nexus/#browse/browse:public)
* downloading from [Alfresco maven repository](https://artifacts.alfresco.com/nexus/content/groups/public)
* as Maven dependency by adding the dependency to your pom file:
~~~xml
~~~
<dependency>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-core</artifactId>
@@ -83,46 +64,34 @@ The artifacts can be obtained by:
<version>version</version>
<type>war</type>
</dependency>
~~~
and Alfresco maven repository:
~~~xml
~~~
<repository>
<id>alfresco-maven-repo</id>
<url>https://artifacts.alfresco.com/nexus/content/groups/public</url>
</repository>
~~~
The SNAPSHOT versions of the artifact are not published.
## Setting up and building your development environment
See the [**Development Tomcat Environment**](https://github.com/Alfresco/acs-community-packaging/tree/master/dev/README.md)
page which will show you how to try out your repository changes in a local Tomcat instance or using Docker containers.
See the [Development Tomcat Environment](https://github.com/Alfresco/acs-community-packaging/tree/master/dev/README.md)
page which will show you how to try out your repository changes in a local tomcat instance.
If you wish to use Docker images, take a look at the aliases ending in `D` and the docker-compose files in this
project's test modules.
## Branches
This project has a branch for each ACS release. For example the code in ACS 6.2.2 is a
branch called **`release/6.2.2`**. In addition to the original 6.2.2 release it will also contain Hot Fixes
added later. The latest unreleased code is on the **`master`** branch. There are also **`.N`** branches, such as
**`release/7.1.N`** on which we gather unreleased fixes for future service pack releases. They do not indicate
This project has a branch for each ACS release. For example the code in ACS 6.2.1 is a
branch called `releases/6.2.2`. In addition to the original 6.2.2 release it will also contain Hot Fixes
added later. The latest unreleased code is on the `master` branch. There are also `.N` branches, such as
`releases/7.1.N` on which we gather unreleased fixes for future service pack releases. They do not indicate
that one is planned.
For historic reasons the version of artifacts created on each branch do not match the ACS version.
For example artifact in ACS 7.2.0 will be **`14.<something>`**.
For example artifact in ACS 7.2.0 will be `14.<something>`.
The enterprise projects which extend the **`alfresco-community-repo`** use the same branch names and leading
The enterprise projects which extend the `alfresco-community-repo` use the same branch names and leading
artifact version number.
## Contributing guide
Please use [**this guide**](CONTRIBUTING.md) to make a contribution to the project.
## Helpful links
- [Alfresco Content Services Documentation](https://docs.alfresco.com/content-services/latest/)
- [Alfresco Platform](https://www.hyland.com/en/products/alfresco-platform)
### Contributing guide
Please use [this guide](CONTRIBUTING.md) to make a contribution to the project.

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-amps</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<modules>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-parent</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<modules>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-automation-community-repo</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<build>
@@ -98,7 +98,7 @@
<dependency>
<groupId>com.github.docker-java</groupId>
<artifactId>docker-java</artifactId>
<version>3.4.0</version>
<version>3.3.2</version>
<exclusions>
<exclusion>
<groupId>org.bouncycastle</groupId>

View File

@@ -49,7 +49,6 @@ import org.alfresco.rest.rm.community.requests.gscore.api.TransferAPI;
import org.alfresco.rest.rm.community.requests.gscore.api.TransferContainerAPI;
import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledContainerAPI;
import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledRecordFolderAPI;
import org.alfresco.rest.rm.community.requests.gscore.api.RetentionScheduleAPI;
import org.alfresco.utility.data.DataUserAIS;
import org.alfresco.utility.model.RepoTestModel;
import org.alfresco.utility.model.UserModel;
@@ -255,14 +254,4 @@ public class RestAPIFactory
{
return getGSCoreAPI(userModel).usingHoldsAPI();
}
public RetentionScheduleAPI getRetentionScheduleAPI()
{
return getGSCoreAPI(null).usingRetentionScheduleAPI();
}
public RetentionScheduleAPI getRetentionScheduleAPI(UserModel userModel)
{
return getGSCoreAPI(userModel).usingRetentionScheduleAPI();
}
}

View File

@@ -1,41 +0,0 @@
/*-
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.rm.community.model.hold;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BulkBodyCancel
{
private String reason;
}

View File

@@ -31,6 +31,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.alfresco.rest.search.RestRequestQueryModel;
import org.alfresco.utility.model.TestModel;
@@ -40,6 +41,7 @@ import org.alfresco.utility.model.TestModel;
*
* @author Damian Ujma
*/
@EqualsAndHashCode(callSuper = true)
@Builder
@Data
@NoArgsConstructor
@@ -55,5 +57,4 @@ public class HoldBulkOperation extends TestModel
private RestRequestQueryModel query;
@JsonProperty(required = true)
private HoldBulkOperationType op;
}

View File

@@ -57,11 +57,12 @@ public class HoldBulkStatus extends TestModel
private String lastError;
private String status;
private Status status;
private boolean isCancelled;
private String cancellationReason;
private HoldBulkOperation holdBulkOperation;
public enum Status
{
PENDING,
IN_PROGRESS,
DONE
}
}

View File

@@ -1,58 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.rm.community.model.retentionschedule;
import lombok.EqualsAndHashCode;
import org.alfresco.utility.model.TestModel;
import lombok.Data;
import java.util.List;
/**
* retention schedule
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class RetentionSchedule extends TestModel
{
private String id ;
private String parentId;
private String authority;
private String instructions;
private boolean isRecordLevel;
private boolean isUnpublishedUpdates;
private List<RetentionScheduleActionDefinition> actions;
public boolean getIsRecordLevel()
{
return isRecordLevel;
}
public void setIsRecordLevel(boolean recordLevel) {
isRecordLevel = recordLevel;
}
}

View File

@@ -1,50 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.rm.community.model.retentionschedule;
import java.util.List;
import lombok.Data;
/**
* retention schedule action definition
*/
@Data
public class RetentionScheduleActionDefinition
{
private String id;
private String name;
private int periodAmount;
private String period;
private String periodProperty;
private boolean combineRetentionStepConditions;
private List<String> events;
private boolean eligibleOnFirstCompleteEvent;
private String description;
private boolean retainRecordMetadataAfterDestruction;
private String location;
private int index;
}

View File

@@ -1,32 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.rm.community.model.retentionschedule;
import org.alfresco.rest.core.RestModels;
public class RetentionScheduleCollection extends RestModels<RetentionScheduleEntry, RetentionScheduleCollection>
{
}

View File

@@ -1,37 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.rm.community.model.retentionschedule;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import org.alfresco.rest.core.RestModels;
@Data
public class RetentionScheduleEntry extends RestModels<RetentionSchedule, RetentionScheduleEntry>
{
@JsonProperty
private RetentionSchedule entry;
}

View File

@@ -1,33 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.rm.community.model.retentionschedule;
import org.alfresco.rest.core.RestModels;
public class RetentionScheduleStepCollection extends RestModels<RetentionScheduleStepEntry, RetentionScheduleStepCollection>
{
}

View File

@@ -1,38 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.rm.community.model.retentionschedule;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import org.alfresco.rest.core.RestModels;
@Data
public class RetentionScheduleStepEntry extends RestModels<RetentionScheduleActionDefinition, RetentionScheduleStepEntry>
{
@JsonProperty
private RetentionScheduleActionDefinition entry;
}

View File

@@ -47,7 +47,6 @@ import org.alfresco.rest.rm.community.requests.gscore.api.TransferAPI;
import org.alfresco.rest.rm.community.requests.gscore.api.TransferContainerAPI;
import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledContainerAPI;
import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledRecordFolderAPI;
import org.alfresco.rest.rm.community.requests.gscore.api.RetentionScheduleAPI;
/**
* Defines the entire GS Core API
@@ -194,9 +193,4 @@ public class GSCoreAPI extends RMModelRequest
}
public HoldsAPI usingHoldsAPI() { return new HoldsAPI(getRmRestWrapper()); }
public RetentionScheduleAPI usingRetentionScheduleAPI()
{
return new RetentionScheduleAPI(getRmRestWrapper());
}
}

View File

@@ -38,7 +38,6 @@ import static org.springframework.http.HttpMethod.POST;
import static org.springframework.http.HttpMethod.PUT;
import org.alfresco.rest.core.RMRestWrapper;
import org.alfresco.rest.rm.community.model.hold.BulkBodyCancel;
import org.alfresco.rest.rm.community.model.hold.Hold;
import org.alfresco.rest.rm.community.model.hold.HoldBulkOperation;
import org.alfresco.rest.rm.community.model.hold.HoldBulkOperationEntry;
@@ -401,46 +400,4 @@ public class HoldsAPI extends RMModelRequest
return getBulkStatuses(holdId, EMPTY);
}
/**
* Cancels a bulk operation for a hold.
*
* @param holdId The identifier of a hold
* @param bulkStatusId The identifier of a bulk status operation
* @param bulkBodyCancel The bulk body cancel model
* @param parameters The URL parameters to add
* @throws RuntimeException for the following cases:
* <ul>
* <li>{@code holdId}, {@code bulkStatusId} or {@code bulkBodyCancel} is invalid</li>
* <li>authentication fails</li>
* <li>current user does not have permission to cancel the bulk operation for {@code bulkStatusId}</li>
* <li>{@code holdId} or {@code bulkStatusId} does not exist</li>
* </ul>
*/
public void cancelBulkOperation(String holdId, String bulkStatusId, BulkBodyCancel bulkBodyCancel, String parameters)
{
mandatoryString("holdId", holdId);
mandatoryString("bulkStatusId", bulkStatusId);
mandatoryObject("bulkBodyCancel", bulkBodyCancel);
getRmRestWrapper().processEmptyModel(requestWithBody(
POST,
toJson(bulkBodyCancel),
"holds/{holdId}/bulk-statuses/{bulkStatusId}/cancel",
holdId,
bulkStatusId,
parameters
));
}
/**
* See {@link #cancelBulkOperation(String, String, BulkBodyCancel, String)}
*/
public void cancelBulkOperation(String holdId, String bulkStatusId, BulkBodyCancel bulkBodyCancel)
{
mandatoryString("holdId", holdId);
mandatoryString("bulkStatusId", bulkStatusId);
mandatoryObject("bulkBodyCancel", bulkBodyCancel);
cancelBulkOperation(holdId, bulkStatusId, bulkBodyCancel, EMPTY);
}
}

View File

@@ -1,198 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.rm.community.requests.gscore.api;
import org.alfresco.rest.core.RMRestWrapper;
import org.alfresco.rest.rm.community.model.retentionschedule.RetentionSchedule;
import org.alfresco.rest.rm.community.model.retentionschedule.RetentionScheduleActionDefinition;
import org.alfresco.rest.rm.community.model.retentionschedule.RetentionScheduleCollection;
import org.alfresco.rest.rm.community.model.retentionschedule.RetentionScheduleStepCollection;
import org.alfresco.rest.rm.community.requests.RMModelRequest;
import static org.alfresco.rest.core.RestRequest.requestWithBody;
import static org.alfresco.rest.core.RestRequest.simpleRequest;
import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryObject;
import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryString;
import static org.alfresco.rest.rm.community.util.PojoUtility.toJson;
import static org.apache.commons.lang3.StringUtils.EMPTY;
import static org.springframework.http.HttpMethod.GET;
import static org.springframework.http.HttpMethod.POST;
public class RetentionScheduleAPI extends RMModelRequest
{
/**
* @param rmRestWrapper
*/
public RetentionScheduleAPI(RMRestWrapper rmRestWrapper)
{
super(rmRestWrapper);
}
/**
* Creates a retention schedule.
*
* @param retentionScheduleModel The retentionSchedule model
* @param recordCategoryId The identifier of a record category
* @param parameters The URL parameters to add
* @return The created {@link RetentionSchedule}
* @throws RuntimeException for the following cases:
* <ul>
* <li>{@code recordCategoryId} is not a valid format or {@code recordCategoryId} is invalid</li>
* <li>authentication fails</li>
* <li>current user does not have permission to add children to {@code recordCategoryId}</li>
* <li>{@code recordCategoryId} does not exist</li>
* <li>new name clashes with an existing node in the current parent container</li>
* </ul>
*/
public RetentionSchedule createRetentionSchedule(RetentionSchedule retentionScheduleModel, String recordCategoryId, String parameters)
{
mandatoryString("recordCategoryId", recordCategoryId);
mandatoryObject("retentionScheduleModel", retentionScheduleModel);
return getRmRestWrapper().processModel(RetentionSchedule.class, requestWithBody(
POST,
toJson(retentionScheduleModel),
"record-categories/{recordCategoryId}/retention-schedules",
recordCategoryId,
parameters
));
}
/**
* See {@link #createRetentionSchedule(RetentionSchedule, String, String)}
*/
public RetentionSchedule createRetentionSchedule(RetentionSchedule retentionScheduleModel, String recordCategoryId)
{
return createRetentionSchedule(retentionScheduleModel, recordCategoryId, EMPTY);
}
/**
* Gets the retentionSchedule of a record category.
*
* @param recordCategoryId The identifier of a record category
* @param parameters The URL parameters to add
* @return The {@link RetentionSchedule} for the given {@code recordCategoryId}
* @throws RuntimeException for the following cases:
* <ul>
* <li>authentication fails</li>
* <li>current user does not have permission to read {@code recordCategoryId}</li>
* <li>{@code recordCategoryId} does not exist</li>
*</ul>
*/
public RetentionScheduleCollection getRetentionSchedule(String recordCategoryId, String parameters)
{
mandatoryString("recordCategoryId", recordCategoryId);
return getRmRestWrapper().processModels(RetentionScheduleCollection.class, simpleRequest(
GET,
"record-categories/{recordCategoryId}/retention-schedules?{parameters}",
recordCategoryId,
parameters
));
}
/**
* See {@link #getRetentionSchedule(String, String)}
*/
public RetentionScheduleCollection getRetentionSchedule(String recordCategoryId)
{
return getRetentionSchedule(recordCategoryId, EMPTY);
}
/**
* Creates a step in the retention schedule.
*
* @param retentionScheduleActionDefinition The retentionScheduleActionDefinition model
* @param retentionScheduleId The identifier of a retention schedule id
* @param parameters The URL parameters to add
* @return The created {@link RetentionScheduleActionDefinition}
* @throws RuntimeException for the following cases:
* <ul>
* <li>{@code retentionScheduleId} is not a valid format or {@code retentionScheduleId} is invalid</li>
* <li>authentication fails</li>
* <li>current user does not have permission to add children to {@code retentionScheduleId}</li>
* <li>{@code retentionScheduleId} does not exist</li>
* <li>new name clashes with an existing node in the current parent container</li>
* </ul>
*/
public RetentionScheduleActionDefinition createRetentionScheduleStep(RetentionScheduleActionDefinition retentionScheduleActionDefinition, String retentionScheduleId, String parameters)
{
mandatoryString("retentionScheduleId", retentionScheduleId);
mandatoryObject("retentionScheduleActionDefinition", retentionScheduleActionDefinition);
return getRmRestWrapper().processModel(RetentionScheduleActionDefinition.class, requestWithBody(
POST,
toJson(retentionScheduleActionDefinition),
"retention-schedules/{retentionScheduleId}/retention-steps",
retentionScheduleId,
parameters
));
}
/**
* See {@link #createRetentionScheduleStep(RetentionScheduleActionDefinition, String)} (RetentionSchedule, String, String)}
*/
public RetentionScheduleActionDefinition createRetentionScheduleStep(RetentionScheduleActionDefinition retentionScheduleActionDefinition, String retentionScheduleId)
{
return createRetentionScheduleStep(retentionScheduleActionDefinition, retentionScheduleId, EMPTY);
}
/**
* Gets the retentionSchedule of a record category.
*
* @param retentionScheduleId The identifier of a record category
* @param parameters The URL parameters to add
* @return The {@link RetentionScheduleActionDefinition} for the given {@code recordCategoryId}
* @throws RuntimeException for the following cases:
* <ul>
* <li>authentication fails</li>
* <li>current user does not have permission to read {@code recordCategoryId}</li>
* <li>{@code recordCategoryId} does not exist</li>
*</ul>
*/
public RetentionScheduleStepCollection getRetentionScheduleStep(String retentionScheduleId, String parameters)
{
mandatoryString("retentionScheduleId", retentionScheduleId);
return getRmRestWrapper().processModels(RetentionScheduleStepCollection.class, simpleRequest(
GET,
"retention-schedules/{retentionScheduleId}/retention-steps?{parameters}",
retentionScheduleId,
parameters
));
}
/**
* See {@link #getRetentionScheduleStep(String, String)}
*/
public RetentionScheduleStepCollection getRetentionScheduleStep(String recordCategoryId)
{
return getRetentionScheduleStep(recordCategoryId, EMPTY);
}
}

View File

@@ -41,24 +41,22 @@ import static org.springframework.http.HttpStatus.ACCEPTED;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.HttpStatus.UNAUTHORIZED;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import org.alfresco.dataprep.CMISUtil;
import org.alfresco.dataprep.ContentActions;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
import org.alfresco.rest.rm.community.model.hold.BulkBodyCancel;
import org.alfresco.rest.rm.community.model.hold.Hold;
import org.alfresco.rest.rm.community.model.hold.HoldBulkOperation;
import org.alfresco.rest.rm.community.model.hold.HoldBulkOperation.HoldBulkOperationType;
import org.alfresco.rest.rm.community.model.hold.HoldBulkOperationEntry;
import org.alfresco.rest.rm.community.model.hold.HoldBulkStatus;
import org.alfresco.rest.rm.community.model.hold.HoldBulkStatus.Status;
import org.alfresco.rest.rm.community.model.hold.HoldBulkStatusCollection;
import org.alfresco.rest.rm.community.model.hold.HoldBulkStatusEntry;
import org.alfresco.rest.rm.community.model.hold.HoldChild;
@@ -169,13 +167,12 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
STEP("Check the bulk status.");
HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId());
assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, 0, null, holdBulkOperation);
assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, 0, null);
STEP("Check the bulk statuses.");
HoldBulkStatusCollection holdBulkStatusCollection = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatuses(hold.getId());
assertEquals(Arrays.asList(holdBulkStatus),
holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList());
assertEquals(Arrays.asList(holdBulkStatus), holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList());
}
/**
@@ -220,13 +217,12 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
STEP("Check the bulk status.");
HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatus(hold3.getId(), bulkOperationEntry.getBulkStatusId());
assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, 0, null, bulkOperation);
assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, 0, null);
STEP("Check the bulk statuses.");
HoldBulkStatusCollection holdBulkStatusCollection = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatuses(hold3.getId());
assertEquals(List.of(holdBulkStatus),
holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList());
assertEquals(Arrays.asList(holdBulkStatus), holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList());
}
/**
@@ -304,13 +300,12 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
assertStatusCode(ACCEPTED);
await().atMost(20, TimeUnit.SECONDS).until(() ->
Objects.equals(getRestAPIFactory().getHoldsAPI(userWithoutPermission)
.getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId()).getStatus(), "DONE"));
getRestAPIFactory().getHoldsAPI(userWithoutPermission)
.getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId()).getStatus() == Status.DONE);
HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userWithoutPermission)
.getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId());
assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, NUMBER_OF_FILES, ACCESS_DENIED_ERROR_MESSAGE,
holdBulkOperation);
assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, NUMBER_OF_FILES, ACCESS_DENIED_ERROR_MESSAGE);
}
/**
@@ -349,8 +344,7 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
== NUMBER_OF_FILES - 1);
await().atMost(30, TimeUnit.SECONDS).until(
() -> getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatus(hold2.getId(), bulkOperationEntry.getBulkStatusId()).getProcessedItems()
== NUMBER_OF_FILES);
.getBulkStatus(hold2.getId(), bulkOperationEntry.getBulkStatusId()).getProcessedItems() == NUMBER_OF_FILES);
List<String> holdChildrenNodeRefs = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getChildren(hold2.getId()).getEntries().stream().map(HoldChildEntry::getEntry).map(
HoldChild::getId).toList();
@@ -360,13 +354,12 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
STEP("Check the bulk status.");
HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatus(hold2.getId(), bulkOperationEntry.getBulkStatusId());
assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, 1, ACCESS_DENIED_ERROR_MESSAGE, holdBulkOperation);
assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, 1, ACCESS_DENIED_ERROR_MESSAGE);
STEP("Check the bulk statuses.");
HoldBulkStatusCollection holdBulkStatusCollection = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.getBulkStatuses(hold2.getId());
assertEquals(List.of(holdBulkStatus),
holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList());
assertEquals(Arrays.asList(holdBulkStatus), holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList());
// Revert the permissions
contentActions.setPermissionForUser(getAdminUser().getUsername(), getAdminUser().getPassword(),
@@ -496,89 +489,14 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
assertStatusCode(BAD_REQUEST);
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a site to a hold using the bulk API
* And then the user cancels the bulk operation
* Then the user receives OK status code
*/
@Test
public void testBulkProcessCancellationWithAllowedUser()
{
Hold hold4 = getRestAPIFactory().getFilePlansAPI(getAdminUser()).createHold(
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(), FILE_PLAN_ALIAS);
holds.add(hold4);
UserModel userAddHoldPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite,
UserRole.SiteCollaborator, hold4.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
users.add(userAddHoldPermission);
STEP("Add content from the site to the hold using the bulk API.");
HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.startBulkProcess(holdBulkOperation, hold4.getId());
// Verify the status code
assertStatusCode(ACCEPTED);
assertEquals(NUMBER_OF_FILES, bulkOperationEntry.getTotalItems());
STEP("Cancel the bulk operation.");
getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.cancelBulkOperation(hold4.getId(), bulkOperationEntry.getBulkStatusId(), new BulkBodyCancel());
// Verify the status code
assertStatusCode(OK);
}
/**
* Given a user with the add to hold capability and hold filing permission
* When the user adds content from a site to a hold using the bulk API
* And a 2nd user without the add to hold capability cancels the bulk operation
* Then the 2nd user receives access denied error
*/
@Test
public void testBulkProcessCancellationWithUserWithoutAddToHoldCapability()
{
Hold hold5 = getRestAPIFactory().getFilePlansAPI(getAdminUser()).createHold(
Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION)
.reason(HOLD_REASON).build(), FILE_PLAN_ALIAS);
holds.add(hold5);
UserModel userAddHoldPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite,
UserRole.SiteCollaborator, hold5.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
users.add(userAddHoldPermission);
STEP("Add content from the site to the hold using the bulk API.");
HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(userAddHoldPermission)
.startBulkProcess(holdBulkOperation, hold5.getId());
// Verify the status code
assertStatusCode(ACCEPTED);
assertEquals(NUMBER_OF_FILES, bulkOperationEntry.getTotalItems());
UserModel userWithoutAddToHoldCapability = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite,
UserRole
.SiteCollaborator,
hold5.getId(), UserRoles.ROLE_RM_POWER_USER, PERMISSION_FILING);
users.add(userWithoutAddToHoldCapability);
STEP("Cancel the bulk operation.");
getRestAPIFactory().getHoldsAPI(userWithoutAddToHoldCapability)
.cancelBulkOperation(hold5.getId(), bulkOperationEntry.getBulkStatusId(), new BulkBodyCancel());
STEP("Verify the response status code and the error message.");
assertStatusCode(FORBIDDEN);
getRestAPIFactory().getRmRestWrapper().assertLastError().containsSummary(ACCESS_DENIED_ERROR_MESSAGE);
}
private void assertBulkProcessStatus(HoldBulkStatus holdBulkStatus, long expectedProcessedItems,
int expectedErrorsCount, String expectedErrorMessage, HoldBulkOperation holdBulkOperation)
int expectedErrorsCount, String expectedErrorMessage)
{
assertEquals("DONE", holdBulkStatus.getStatus());
assertEquals(Status.DONE, holdBulkStatus.getStatus());
assertEquals(expectedProcessedItems, holdBulkStatus.getTotalItems());
assertEquals(expectedProcessedItems, holdBulkStatus.getProcessedItems());
assertEquals(expectedErrorsCount, holdBulkStatus.getErrorsCount());
assertEquals(holdBulkStatus.getHoldBulkOperation(), holdBulkOperation);
assertNotNull(holdBulkStatus.getStartTime());
assertNotNull(holdBulkStatus.getEndTime());
@@ -611,4 +529,4 @@ public class AddToHoldsBulkV1Tests extends BaseRMRestTest
users.forEach(user -> getDataUser().usingAdmin().deleteUser(user));
holds.forEach(hold -> getRestAPIFactory().getHoldsAPI(getAdminUser()).deleteHold(hold.getId()));
}
}
}

View File

@@ -1,377 +0,0 @@
/*-
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.rm.community.retentionschedule;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory;
import org.alfresco.rest.rm.community.model.retentionschedule.RetentionSchedule;
import org.alfresco.rest.rm.community.model.retentionschedule.RetentionScheduleActionDefinition;
import org.alfresco.rest.rm.community.model.retentionschedule.RetentionScheduleStepCollection;
import org.alfresco.rest.v0.RMRolesAndActionsAPI;
import org.alfresco.utility.model.UserModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.alfresco.rest.core.v0.BaseAPI.RM_SITE_ID;
import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric;
import static org.alfresco.utility.data.RandomData.getRandomName;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CONFLICT;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.HttpStatus.UNAUTHORIZED;
import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY;
import static org.testng.Assert.assertNotNull;
import static org.testng.AssertJUnit.assertEquals;
/**
* Retention schedule step test case
*/
public class RetentionScheduleStepTests extends BaseRMRestTest
{
private RecordCategory recordCategory;
private RetentionSchedule createdRetentionSchedule;
private final RetentionScheduleActionDefinition retentionScheduleActionDefinition = new RetentionScheduleActionDefinition();
private RetentionScheduleActionDefinition createdRetentionActionDefinition;
private UserModel nonRMuser;
private final List<String> recordCategories = new ArrayList<>();
private static final String TEST_USER = "testUser";
private static final String RECORD_CATEGORY = "recordCategory";
private static final String PERIOD_PROPERTY = "cm:created";
private static final String AUTHORITY = "authority";
private static final String INSTRUCTIONS = "instructions";
private static final int PERIOD_AMOUNT = 5;
private static final String PERIOD = "month";
private static final List<String> EVENTS = Arrays.asList("case_closed","abolished");
private static final String TRANSFER_STEP = "transfer";
private static final String RETAIN_STEP = "retain";
private static final String INVALID_PERIOD = "random";
private static final String CUTOFF_STEP = "cutoff";
private static final String DESTROY_STEP = "destroyContent";
private static final String INVALID_PASSWORD = "wrongPassword";
@Autowired
private RMRolesAndActionsAPI rmRolesAndActionsAPI;
@BeforeClass(alwaysRun = true)
public void preconditionForRetentionScheduleStepTests()
{
createRMSiteIfNotExists();
// create a non rm user
nonRMuser = dataUser.createRandomTestUser(TEST_USER);
//Create record category
recordCategory = createRootCategory(getRandomName(RECORD_CATEGORY));
recordCategories.add(recordCategory.getId());
RetentionSchedule retentionSchedule = new RetentionSchedule();
retentionSchedule.setAuthority(AUTHORITY + getRandomAlphanumeric());
retentionSchedule.setInstructions(INSTRUCTIONS + getRandomAlphanumeric());
retentionSchedule.setIsRecordLevel(false);
//Create retention schedule with a valid user
createdRetentionSchedule = getRestAPIFactory().getRetentionScheduleAPI()
.createRetentionSchedule(retentionSchedule, recordCategory.getId());
retentionScheduleActionDefinition.setName(RETAIN_STEP);
retentionScheduleActionDefinition.setDescription(INSTRUCTIONS);
retentionScheduleActionDefinition.setPeriodAmount(PERIOD_AMOUNT);
retentionScheduleActionDefinition.setPeriodProperty(PERIOD_PROPERTY);
retentionScheduleActionDefinition.setPeriod(PERIOD);
retentionScheduleActionDefinition.setCombineRetentionStepConditions(false);
retentionScheduleActionDefinition.setEligibleOnFirstCompleteEvent(true);
retentionScheduleActionDefinition.setEvents(EVENTS);
}
@Test(priority = 1)
public void createRetentionScheduleStepFor422()
{
RetentionScheduleActionDefinition actionDefinition = getRetentionScheduleActionDefinition();
//Creating the first action "transfer" should give 422
actionDefinition.setName(TRANSFER_STEP);
actionDefinition.setLocation("location");
//Create retention schedule action definition
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition,createdRetentionSchedule.getId());
// Verify the status code
assertStatusCode(UNPROCESSABLE_ENTITY);
}
@Test(priority = 2)
public void createRetentionScheduleStepWithInvalidPeriodValue()
{
RetentionScheduleActionDefinition actionDefinition = getRetentionScheduleActionDefinition();
actionDefinition.setName(RETAIN_STEP);
//Invalid period value
actionDefinition.setPeriod(INVALID_PERIOD);
//Create retention schedule action definition
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition,createdRetentionSchedule.getId());
// Verify the status code
assertStatusCode(BAD_REQUEST);
}
@Test(priority = 3)
public void createRetentionScheduleWithInvalidStep()
{
RecordCategory recordCategory = createRootCategory(getRandomName(RECORD_CATEGORY));
recordCategories.add(recordCategory.getId());
RetentionSchedule retentionSchedule = createRetentionSchedule(recordCategory);
RetentionScheduleActionDefinition actionDefinition = getRetentionScheduleActionDefinition();
actionDefinition.setName(RETAIN_STEP);
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition,retentionSchedule.getId());
assertStatusCode(CREATED);
RetentionScheduleActionDefinition actionDefinition1 = getRetentionScheduleActionDefinition();
actionDefinition1.setName(TRANSFER_STEP);
actionDefinition1.setLocation("location");
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition1,retentionSchedule.getId());
assertStatusCode(CREATED);
RetentionScheduleActionDefinition actionDefinition2 = getRetentionScheduleActionDefinition();
actionDefinition2.setName(CUTOFF_STEP);
//Create retention schedule action definition
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition2,retentionSchedule.getId());
// Verify the status code
assertStatusCode(CONFLICT);
}
@Test(priority = 4)
public void createRetentionScheduleWithInvalidStepAfterDestroy()
{
RecordCategory recordCategory = createRootCategory(getRandomName(RECORD_CATEGORY));
recordCategories.add(recordCategory.getId());
RetentionSchedule retentionSchedule = createRetentionSchedule(recordCategory);
RetentionScheduleActionDefinition actionDefinition = getRetentionScheduleActionDefinition();
actionDefinition.setName(RETAIN_STEP);
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition,retentionSchedule.getId());
assertStatusCode(CREATED);
RetentionScheduleActionDefinition actionDefinition1 = getRetentionScheduleActionDefinition();
actionDefinition1.setName(DESTROY_STEP);
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition1,retentionSchedule.getId());
assertStatusCode(CREATED);
RetentionScheduleActionDefinition actionDefinition2 = getRetentionScheduleActionDefinition();
actionDefinition2.setName(CUTOFF_STEP);
//Create retention schedule action definition
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition2,retentionSchedule.getId());
// Verify the status code
assertStatusCode(CONFLICT);
}
@Test(priority = 5)
public void combineRetentionStepConditionsNotValidForNonAccessionStep()
{
RecordCategory recordCategory = createRootCategory(getRandomName(RECORD_CATEGORY));
recordCategories.add(recordCategory.getId());
RetentionSchedule retentionSchedule = createRetentionSchedule(recordCategory);
RetentionScheduleActionDefinition actionDefinition = getRetentionScheduleActionDefinition();
actionDefinition.setName(RETAIN_STEP);
actionDefinition.setCombineRetentionStepConditions(true);
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition,retentionSchedule.getId());
assertStatusCode(BAD_REQUEST);
}
@Test(priority = 6)
public void createRetentionScheduleWithSameStep()
{
RecordCategory recordCategory = createRootCategory(getRandomName(RECORD_CATEGORY));
recordCategories.add(recordCategory.getId());
RetentionSchedule retentionSchedule = createRetentionSchedule(recordCategory);
RetentionScheduleActionDefinition actionDefinition = getRetentionScheduleActionDefinition();
actionDefinition.setName(RETAIN_STEP);
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition,retentionSchedule.getId());
assertStatusCode(CREATED);
RetentionScheduleActionDefinition actionDefinition1 = getRetentionScheduleActionDefinition();
actionDefinition1.setName(RETAIN_STEP);
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition1,retentionSchedule.getId());
// Verify the status code
assertStatusCode(CONFLICT);
}
@Test(priority = 7)
public void createRetentionScheduleWithMultipleTransferStep()
{
RecordCategory recordCategory = createRootCategory(getRandomName(RECORD_CATEGORY));
recordCategories.add(recordCategory.getId());
RetentionSchedule retentionSchedule = createRetentionSchedule(recordCategory);
RetentionScheduleActionDefinition actionDefinition = getRetentionScheduleActionDefinition();
actionDefinition.setName(RETAIN_STEP);
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition,retentionSchedule.getId());
assertStatusCode(CREATED);
RetentionScheduleActionDefinition actionDefinition1 = getRetentionScheduleActionDefinition();
actionDefinition1.setName(TRANSFER_STEP);
actionDefinition1.setLocation("location");
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition1, retentionSchedule.getId());
RetentionScheduleActionDefinition actionDefinition2 = getRetentionScheduleActionDefinition();
actionDefinition2.setName(TRANSFER_STEP);
actionDefinition2.setLocation("location");
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition2, retentionSchedule.getId());
// Verify the status code
assertStatusCode(CREATED);
}
@Test(priority = 8)
public void createRetentionScheduleStepFor201()
{
//Create retention schedule action definition
createdRetentionActionDefinition = getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(retentionScheduleActionDefinition,createdRetentionSchedule.getId());
// Verify the status code
assertStatusCode(CREATED);
// Find this retention schedule is created one or not
assertEquals(createdRetentionActionDefinition.getName(), retentionScheduleActionDefinition.getName());
assertEquals(createdRetentionActionDefinition.getDescription(), retentionScheduleActionDefinition.getDescription());
assertEquals(createdRetentionActionDefinition.getPeriodAmount(), retentionScheduleActionDefinition.getPeriodAmount());
assertEquals(createdRetentionActionDefinition.isCombineRetentionStepConditions(), retentionScheduleActionDefinition.isCombineRetentionStepConditions());
assertEquals(createdRetentionActionDefinition.isEligibleOnFirstCompleteEvent(), retentionScheduleActionDefinition.isEligibleOnFirstCompleteEvent());
}
@Test(priority = 9)
public void createRetentionScheduleStepFor401()
{
//Create retention schedule action definition
getRestAPIFactory().getRetentionScheduleAPI(new UserModel(getAdminUser().getUsername(), INVALID_PASSWORD)).createRetentionScheduleStep(retentionScheduleActionDefinition,createdRetentionSchedule.getId());
// Verify the status code
assertStatusCode(UNAUTHORIZED);
}
@Test(priority = 10)
public void createRetentionScheduleStepFor403()
{
//Create retention schedule action definition
getRestAPIFactory().getRetentionScheduleAPI(nonRMuser).createRetentionScheduleStep(retentionScheduleActionDefinition,createdRetentionSchedule.getId());
// Verify the status code
assertStatusCode(FORBIDDEN);
}
@Test(priority = 11)
public void retentionScheduleStepFor400()
{
getRestAPIFactory().getRetentionScheduleAPI().getRetentionScheduleStep(recordCategory.getId());
// Verify the status code
assertStatusCode(BAD_REQUEST);
}
@Test(priority = 12)
public void createRetentionScheduleStepFor404()
{
//Create retention schedule action definition
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(retentionScheduleActionDefinition,getRandomAlphanumeric());
// Verify the status code
assertStatusCode(NOT_FOUND);
}
@Test(priority = 13)
public void retentionScheduleStepFor403()
{
// Get retention schedule steps with user having no rights
getRestAPIFactory().getRetentionScheduleAPI(nonRMuser).getRetentionScheduleStep(createdRetentionSchedule.getId());
// Verify the status code
assertStatusCode(FORBIDDEN);
}
@Test(priority = 14)
public void retentionScheduleStepFor401()
{
getRestAPIFactory().getRetentionScheduleAPI(new UserModel(getAdminUser().getUsername(), INVALID_PASSWORD)).getRetentionScheduleStep(createdRetentionSchedule.getId());
// Verify the status code
assertStatusCode(UNAUTHORIZED);
}
@Test(priority = 15)
public void retentionScheduleStepWith200()
{
RetentionScheduleStepCollection receiveRetentionStepCollection = getRestAPIFactory().getRetentionScheduleAPI().getRetentionScheduleStep(createdRetentionSchedule.getId());
// Verify the status code
assertStatusCode(OK);
receiveRetentionStepCollection.getEntries().forEach(c ->
{
RetentionScheduleActionDefinition retentionActionDef = c.getEntry();
assertNotNull(retentionActionDef.getId());
// Find this retention schedule is created one or not
assertEquals(createdRetentionActionDefinition.getId(), retentionActionDef.getId());
assertEquals(createdRetentionActionDefinition.getName(), retentionActionDef.getName());
assertEquals(createdRetentionActionDefinition.getDescription(), retentionActionDef.getDescription());
assertEquals(createdRetentionActionDefinition.getPeriod(), retentionActionDef.getPeriod());
assertEquals(createdRetentionActionDefinition.getPeriodAmount(), retentionActionDef.getPeriodAmount());
assertEquals(createdRetentionActionDefinition.isCombineRetentionStepConditions(), retentionActionDef.isCombineRetentionStepConditions());
assertEquals(createdRetentionActionDefinition.isEligibleOnFirstCompleteEvent(), retentionActionDef.isEligibleOnFirstCompleteEvent());
});
}
private RetentionSchedule createRetentionSchedule(RecordCategory recordCategory)
{
RetentionSchedule retentionSchedule = new RetentionSchedule();
retentionSchedule.setAuthority(AUTHORITY + getRandomAlphanumeric());
retentionSchedule.setInstructions(INSTRUCTIONS + getRandomAlphanumeric());
retentionSchedule.setIsRecordLevel(false);
//Create retention schedule with a valid user
retentionSchedule = getRestAPIFactory().getRetentionScheduleAPI()
.createRetentionSchedule(retentionSchedule, recordCategory.getId());
// Verify the status code
assertStatusCode(CREATED);
return retentionSchedule;
}
private static RetentionScheduleActionDefinition getRetentionScheduleActionDefinition()
{
RetentionScheduleActionDefinition actionDefinition = new RetentionScheduleActionDefinition();
actionDefinition.setDescription(INSTRUCTIONS);
actionDefinition.setPeriodAmount(PERIOD_AMOUNT);
actionDefinition.setPeriodProperty(PERIOD_PROPERTY);
actionDefinition.setPeriod(PERIOD);
actionDefinition.setCombineRetentionStepConditions(false);
actionDefinition.setEligibleOnFirstCompleteEvent(true);
actionDefinition.setEvents(EVENTS);
return actionDefinition;
}
@AfterClass(alwaysRun = true)
public void cleanUpRetentionScheduleStepTests()
{
rmRolesAndActionsAPI.deleteAllItemsInContainer(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), RM_SITE_ID, recordCategory.getName());
recordCategories.forEach(this::deleteRecordCategory);
dataUser.deleteUser(nonRMuser);
}
}

View File

@@ -1,270 +0,0 @@
/*-
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.rm.community.retentionschedule;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory;
import org.alfresco.rest.rm.community.model.retentionschedule.RetentionSchedule;
import org.alfresco.rest.rm.community.model.retentionschedule.RetentionScheduleCollection;
import org.alfresco.rest.v0.RMRolesAndActionsAPI;
import org.alfresco.utility.model.UserModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.alfresco.rest.core.v0.BaseAPI.RM_SITE_ID;
import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric;
import static org.alfresco.utility.data.RandomData.getRandomName;
import static org.springframework.http.HttpStatus.CONFLICT;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.HttpStatus.UNAUTHORIZED;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.AssertJUnit.assertEquals;
/**
* This class contains the tests for the Retention Schedule CRUD V1 API
*/
public class RetentionScheduleTests extends BaseRMRestTest
{
private RecordCategory recordCategory;
private RetentionSchedule createdRetentionSchedule;
private UserModel nonRMuser;
@Autowired
private RMRolesAndActionsAPI rmRolesAndActionsAPI;
@BeforeClass(alwaysRun = true)
public void preconditionForRetentionScheduleTests()
{
createRMSiteIfNotExists();
// create a non rm user
nonRMuser = dataUser.createRandomTestUser("testUser");
//Create record category
recordCategory = createRootCategory(getRandomName("recordCategory"));
}
/**
* <pre>
* Given that a record category exists
* When I ask the API to create a retention schedule with a user having no rights
* Then it will give 403 as status code
* </pre>
*/
@Test(priority = 1)
public void createRetentionScheduleFor403()
{
RetentionSchedule retentionSchedule = new RetentionSchedule();
// Create retention schedule with user having no rights
getRestAPIFactory().getRetentionScheduleAPI(nonRMuser).createRetentionSchedule(retentionSchedule, recordCategory.getId());
// Verify the status code
assertStatusCode(FORBIDDEN);
}
/**
* <pre>
* Given that a record category does not exists
* When I ask the API to create a retention schedule on a category Id
* Then it will give 404 as a status code
* </pre>
*/
@Test(priority = 2)
public void createRetentionScheduleFor404()
{
RetentionSchedule retentionSchedule = new RetentionSchedule();
//Create retention schedule with category id not exist
getRestAPIFactory().getRetentionScheduleAPI().createRetentionSchedule(retentionSchedule, getRandomAlphanumeric());
// Verify the status code
assertStatusCode(NOT_FOUND);
}
/**
* <pre>
* Given that a record category exists
* When I ask the API to create a retention schedule on a category id with a user having unauthorized access
* Then it will give 401 as a status code
* </pre>
*/
@Test(priority = 3)
public void createRetentionScheduleFor401()
{
RetentionSchedule retentionSchedule = new RetentionSchedule();
//Create retention schedule with a user with unauthorized access
createdRetentionSchedule = getRestAPIFactory().getRetentionScheduleAPI(new UserModel(getAdminUser().getUsername(), "wrongPassword")).createRetentionSchedule(retentionSchedule, recordCategory.getId());
// Verify the status code
assertStatusCode(UNAUTHORIZED);
}
/**
* <pre>
* Given that a record category exists
* When I ask the API to create a retention schedule with a user having access
* Then it is created with a 201 status code
* </pre>
*/
@Test(priority = 4)
public void createRetentionScheduleFor201()
{
RetentionSchedule retentionSchedule = new RetentionSchedule();
String authority = "authority" + getRandomAlphanumeric();
String instructions = "instructions" + getRandomAlphanumeric();
boolean isRecordLevel = false;
retentionSchedule.setAuthority(authority);
retentionSchedule.setInstructions(instructions);
retentionSchedule.setIsRecordLevel(isRecordLevel);
//Create retention schedule with a valid user
createdRetentionSchedule = getRestAPIFactory().getRetentionScheduleAPI()
.createRetentionSchedule(retentionSchedule, recordCategory.getId());
// Verify the status code
assertStatusCode(CREATED);
assertEquals(createdRetentionSchedule.getAuthority(), authority);
assertEquals(createdRetentionSchedule.getInstructions(), instructions);
assertFalse(createdRetentionSchedule.getIsRecordLevel());
assertNotNull(createdRetentionSchedule.getId());
}
/**
* <pre>
* Given that a record category exists
* When I ask the API to create a retention schedule on a category id having retention schedule already
* Then it will give 409 as a status code
* </pre>
*/
@Test(priority = 5)
public void createRetentionScheduleFor409()
{
RetentionSchedule retentionSchedule = new RetentionSchedule();
//Create retention schedule on a category with already having retention schedule
getRestAPIFactory().getRetentionScheduleAPI()
.createRetentionSchedule(retentionSchedule, recordCategory.getId());
assertStatusCode(CONFLICT);
}
/**
* <pre>
* Given that a record category exists
* When I ask the API to get a retention schedule on a given categoryId with a user having no rights
* Then it will give 403
* </pre>
*/
@Test(priority = 6)
public void retentionScheduleWith403()
{
//Get retention schedule with user having no rights
getRestAPIFactory().getRetentionScheduleAPI(nonRMuser).getRetentionSchedule(recordCategory.getId());
// Verify the status code
assertStatusCode(FORBIDDEN);
}
/**
* <pre>
* Given that a record category does not exists
* When I ask the API to get a retention schedule on a category Id
* Then it will give 404 as a status code
* </pre>
*/
@Test(priority = 7)
public void retentionScheduleWith404()
{
//Get retention schedule with category id that does not exist
getRestAPIFactory().getRetentionScheduleAPI().getRetentionSchedule(getRandomAlphanumeric());
// Verify the status code
assertStatusCode(NOT_FOUND);
}
/**
* <pre>
* Given that a record category exists
* When I ask the API to get a retention schedule on a categoryId with a user having unauthorized access
* Then it will give 401 as a status code
* </pre>
*/
@Test(priority = 8)
public void retentionScheduleWith401()
{
//Create retention schedule with a user with unauthorized access
getRestAPIFactory().getRetentionScheduleAPI(new UserModel(getAdminUser().getUsername(), "wrongPassword")).getRetentionSchedule(recordCategory.getId());
// Verify the status code
assertStatusCode(UNAUTHORIZED);
}
/**
* <pre>
* Given that a record category exists
* When I ask the API to get a retention schedule on a categoryId with a user having access
* Then it will give retentionSchedule with 200 as a status code
* </pre>
*/
@Test(priority = 9)
public void retentionScheduleWith200()
{
RetentionScheduleCollection retentionScheduleCollection = getRestAPIFactory().getRetentionScheduleAPI().getRetentionSchedule(recordCategory.getId());
// Verify the status code
assertStatusCode(OK);
retentionScheduleCollection.getEntries().forEach(c ->
{
RetentionSchedule retentionSchedule = c.getEntry();
String retentionScheduleId = retentionSchedule.getId();
assertNotNull(retentionScheduleId);
logger.info("Checking retention schedule " + retentionScheduleId);
// Find this retention schedule is created one or not
assertEquals(createdRetentionSchedule.getId(), retentionScheduleId);
assertEquals(createdRetentionSchedule.getParentId(),retentionSchedule.getParentId());
assertEquals(createdRetentionSchedule.getAuthority(), retentionSchedule.getAuthority());
assertEquals(createdRetentionSchedule.getInstructions(), retentionSchedule.getInstructions());
assertEquals(createdRetentionSchedule.getIsRecordLevel(), retentionSchedule.getIsRecordLevel());
assertEquals(createdRetentionSchedule.isUnpublishedUpdates(), retentionSchedule.isUnpublishedUpdates());
});
}
@AfterClass(alwaysRun = true)
public void cleanUpRetentionScheduleTests()
{
rmRolesAndActionsAPI.deleteAllItemsInContainer(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), RM_SITE_ID, recordCategory.getName());
deleteRecordCategory(recordCategory.getId());
dataUser.deleteUser(nonRMuser);
}
}

View File

@@ -45,9 +45,9 @@ serverHealth.showTenants=false
# testManagement.username=<username>
# testManagement.apiKey=<api-key>
# testManagement.project=<id-of-your-project
# testManagement.testRun=<test-run-name>
# testManagement.testRun=<test-run-name>
# testManagement.includeOnlyTestCasesExecuted=true #if you want to include in your run ONLY the test cases that you run, then set this value to false
# testManagement.rateLimitInSeconds=1 #is the default rate limit after what minimum time, should we upload the next request. http://docs.gurock.com/testrail-api2/introduction #Rate Limit
# testManagement.rateLimitInSeconds=1 #is the default rate limit after what minimum time, should we upload the next request. http://docs.gurock.com/testrail-api2/introduction #Rate Limit
# testManagement.suiteId=23 (the id of the Master suite)
# ------------------------------------------------------
testManagement.enabled=false
@@ -72,7 +72,7 @@ reports.path=./target/reports
#
# MySQL:
# db.url = jdbc:mysql://${alfresco.server}:3306/alfresco
#
#
# PostgreSQL:
# db.url = jdbc:postgresql://<your-DB-IP>:3306/alfresco
#

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-parent</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<modules>

View File

@@ -1,3 +1,3 @@
SOLR6_TAG=2.0.11
SOLR6_TAG=2.0.10
POSTGRES_TAG=15.4
ACTIVEMQ_TAG=5.18.3-jre17-rockylinux8

View File

@@ -153,12 +153,7 @@ rm.hold.bulk.batchSize=100
rm.hold.bulk.logging.interval=100
# The number of entries we process at a time in a transaction.
rm.hold.bulk.itemsPerTransaction=1
# The maximum number of bulk requests we can process in parallel.
rm.hold.bulk.maxParallelRequests=10
cache.bulkHoldStatusCache.cluster.type=fully-distributed
cache.bulkHoldStatusCache.timeToLiveSeconds=2592000
cache.bulkHoldRegistryCache.cluster.type=fully-distributed
cache.bulkHoldRegistryCache.timeToLiveSeconds=2592000
cache.bulkCancellationsCache.cluster.type=fully-distributed
cache.bulkCancellationsCache.timeToLiveSeconds=2592000

View File

@@ -31,11 +31,6 @@
<cm:description>Configuration information for the Records Management application.</cm:description>
</view:properties>
<view:aspects>
<sys:undeletable/>
<sys:unmovable/>
</view:aspects>
<view:associations>
<cm:contains>

View File

@@ -125,7 +125,7 @@
parent="declarativeCapability">
<property name="name" value="DeleteRecordFolder"/>
<property name="private" value="true"/>
<property name="permission" value="DeleteRecords"/>
<property name="permission" value="CreateModifyDestroyFolders"/>
<property name="kinds">
<list>
<value>RECORD_FOLDER</value>

View File

@@ -32,15 +32,11 @@
<property name="itemsPerTransaction">
<value>${rm.hold.bulk.itemsPerTransaction}</value>
</property>
<property name="maxParallelRequests">
<value>${rm.hold.bulk.maxParallelRequests}</value>
</property>
</bean>
<bean id="holdBulkMonitor" class="org.alfresco.module.org_alfresco_module_rm.bulk.hold.DefaultHoldBulkMonitor">
<property name="holdProgressCache" ref="holdProgressCache" />
<property name="holdProcessRegistry" ref="holdProcessRegistry" />
<property name="bulkCancellationsCache" ref="bulkCancellationsCache" />
</bean>
@@ -52,8 +48,4 @@
<constructor-arg value="cache.bulkHoldRegistryCache" />
</bean>
<bean name="bulkCancellationsCache" factory-bean="cacheFactory" factory-method="createCache">
<constructor-arg value="cache.bulkCancellationsCache" />
</bean>
</beans>

View File

@@ -31,7 +31,6 @@
<property name="personService" ref="PersonService"/>
<property name="dispositionService" ref="DispositionService"/>
<property name="serviceRegistry" ref="ServiceRegistry"/>
<property name="recordsManagementServiceRegistry" ref="RecordsManagementServiceRegistry"/>
</bean>
<bean id="searchTypesFactory" class="org.alfresco.rm.rest.api.impl.SearchTypesFactory">
@@ -89,7 +88,6 @@
<bean class="org.alfresco.rm.rest.api.holds.HoldsBulkStatusesRelation" >
<property name="holdBulkMonitor" ref="holdBulkMonitor" />
<property name="holdBulkService" ref="holdBulkService" />
<property name="apiUtils" ref="apiUtils" />
<property name="permissionService" ref="PermissionService" />
</bean>
@@ -148,20 +146,6 @@
<property name="transactionService" ref="transactionService" />
</bean>
<bean class="org.alfresco.rm.rest.api.retentionschedule.RetentionScheduleRelation">
<property name="apiUtils" ref="apiUtils" />
<property name="nodesModelFactory" ref="nodesModelFactory" />
<property name="dispositionService" ref="DispositionService"/>
<property name="nodeService" ref="NodeService"/>
</bean>
<bean class="org.alfresco.rm.rest.api.retentionschedule.RetentionScheduleActionRelation">
<property name="apiUtils" ref="apiUtils" />
<property name="nodesModelFactory" ref="nodesModelFactory" />
<property name="nodeService" ref="NodeService"/>
<property name="recordsManagementServiceRegistry" ref="RecordsManagementServiceRegistry"/>
</bean>
<bean class="org.alfresco.rm.rest.api.recordfolders.RecordFolderEntityResource">
<property name="apiUtils" ref="apiUtils" />
<property name="fileFolderService" ref="FileFolderService" />

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<properties>
@@ -155,12 +155,6 @@
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>${dependency.awaitility.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@@ -26,12 +26,7 @@
*/
package org.alfresco.module.org_alfresco_module_rm.bulk;
import static java.util.concurrent.Executors.newFixedThreadPool;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.alfresco.repo.batch.BatchProcessWorkProvider;
import org.alfresco.repo.batch.BatchProcessor;
@@ -55,7 +50,7 @@ import org.springframework.beans.factory.InitializingBean;
public abstract class BulkBaseService<T> implements InitializingBean
{
private static final Log LOG = LogFactory.getLog(BulkBaseService.class);
protected ExecutorService executorService;
protected ServiceRegistry serviceRegistry;
protected SearchService searchService;
protected TransactionService transactionService;
@@ -67,13 +62,11 @@ public abstract class BulkBaseService<T> implements InitializingBean
protected int itemsPerTransaction;
protected int maxItems;
protected int loggingInterval;
protected int maxParallelRequests;
@Override
public void afterPropertiesSet() throws Exception
{
this.searchService = serviceRegistry.getSearchService();
this.executorService = newFixedThreadPool(maxParallelRequests);
}
/**
@@ -98,17 +91,15 @@ public abstract class BulkBaseService<T> implements InitializingBean
T initBulkStatus = getInitBulkStatus(processId, totalItems);
bulkMonitor.updateBulkStatus(initBulkStatus);
bulkMonitor.registerProcess(nodeRef, processId, bulkOperation);
bulkMonitor.registerProcess(nodeRef, processId);
BulkProgress bulkProgress = new BulkProgress(totalItems, processId, new AtomicBoolean(false),
new AtomicInteger(0));
BatchProcessWorker<NodeRef> batchProcessWorker = getWorkerProvider(nodeRef, bulkOperation, bulkProgress);
BatchProcessWorker<NodeRef> batchProcessWorker = getWorkerProvider(nodeRef, bulkOperation);
BulkStatusUpdater bulkStatusUpdater = getBulkStatusUpdater();
BatchProcessor<NodeRef> batchProcessor = new BatchProcessor<>(
processId,
transactionService.getRetryingTransactionHelper(),
getWorkProvider(bulkOperation, bulkStatusUpdater, bulkProgress),
getWorkProvider(bulkOperation, totalItems, bulkStatusUpdater),
threadCount,
itemsPerTransaction,
bulkStatusUpdater,
@@ -148,7 +139,8 @@ public abstract class BulkBaseService<T> implements InitializingBean
}
};
executorService.submit(backgroundLogic);
Thread backgroundThread = new Thread(backgroundLogic, "BulkBaseService-BackgroundThread");
backgroundThread.start();
}
/**
@@ -171,23 +163,21 @@ public abstract class BulkBaseService<T> implements InitializingBean
* Get work provider
*
* @param bulkOperation bulk operation
* @param totalItems total items
* @param bulkStatusUpdater bulk status updater
* @param bulkProgress bulk progress
* @return work provider
*/
protected abstract BatchProcessWorkProvider<NodeRef> getWorkProvider(BulkOperation bulkOperation,
BulkStatusUpdater bulkStatusUpdater, BulkProgress bulkProgress);
protected abstract BatchProcessWorkProvider<NodeRef> getWorkProvider(BulkOperation bulkOperation, long totalItems,
BulkStatusUpdater bulkStatusUpdater);
/**
* Get worker provider
*
* @param nodeRef node reference
* @param bulkOperation bulk operation
* @param bulkProgress bulk progress
* @return worker provider
*/
protected abstract BatchProcessWorker<NodeRef> getWorkerProvider(NodeRef nodeRef, BulkOperation bulkOperation,
BulkProgress bulkProgress);
protected abstract BatchProcessWorker<NodeRef> getWorkerProvider(NodeRef nodeRef, BulkOperation bulkOperation);
/**
* Check permissions
@@ -204,7 +194,6 @@ public abstract class BulkBaseService<T> implements InitializingBean
searchMapper.fromQuery(searchParams, searchQuery);
searchParams.setSkipCount(skipCount);
searchParams.setMaxItems(1);
searchParams.setLimit(1);
return searchService.query(searchParams);
}
@@ -257,9 +246,4 @@ public abstract class BulkBaseService<T> implements InitializingBean
{
this.itemsPerTransaction = itemsPerTransaction;
}
public void setMaxParallelRequests(int maxParallelRequests)
{
this.maxParallelRequests = maxParallelRequests;
}
}

View File

@@ -1,34 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.module.org_alfresco_module_rm.bulk;
/**
* An immutable POJO to represent a bulk cancellation request
*/
public record BulkCancellationRequest(String reason)
{
}

View File

@@ -43,11 +43,10 @@ public interface BulkMonitor<T>
/**
* Register a process
*
* @param nodeRef the node reference
* @param processId the process id
* @param bulkOperation the bulk operation
* @param nodeRef the node reference
* @param processId the process id
*/
void registerProcess(NodeRef nodeRef, String processId, BulkOperation bulkOperation);
void registerProcess(NodeRef nodeRef, String processId);
/**
* Get the bulk status
@@ -56,28 +55,4 @@ public interface BulkMonitor<T>
* @return the bulk status
*/
T getBulkStatus(String bulkStatusId);
/**
* Cancel a bulk operation
*
* @param bulkStatusId
* @param bulkCancellationRequest
*/
void cancelBulkOperation(String bulkStatusId, BulkCancellationRequest bulkCancellationRequest);
/**
* Check if a bulk operation is cancelled
*
* @param bulkStatusId
* @return true if the bulk operation is cancelled
*/
boolean isCancelled(String bulkStatusId);
/**
* Get the bulk cancellation request
*
* @param bulkStatusId
* @return cancellation reason
*/
BulkCancellationRequest getBulkCancellationRequest(String bulkStatusId);
}

View File

@@ -26,14 +26,12 @@
*/
package org.alfresco.module.org_alfresco_module_rm.bulk;
import java.io.Serializable;
import org.alfresco.rest.api.search.model.Query;
/**
* An immutable POJO to represent a bulk operation
*/
public record BulkOperation(Query searchQuery, String operationType) implements Serializable
public record BulkOperation(Query searchQuery, String operationType)
{
public BulkOperation
{

View File

@@ -1,37 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.module.org_alfresco_module_rm.bulk;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
* An immutable POJO to represent the progress of a bulk operation
*/
public record BulkProgress(long totalItems, String processId, AtomicBoolean cancelled, AtomicInteger currentNodeNumber)
{
}

View File

@@ -26,18 +26,16 @@
*/
package org.alfresco.module.org_alfresco_module_rm.bulk.hold;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import org.alfresco.module.org_alfresco_module_rm.bulk.BulkCancellationRequest;
import org.alfresco.module.org_alfresco_module_rm.bulk.BulkOperation;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.rm.rest.api.model.HoldBulkStatus;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.Pair;
import org.springframework.context.ApplicationEvent;
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
@@ -47,8 +45,7 @@ import org.springframework.extensions.surf.util.AbstractLifecycleBean;
public class DefaultHoldBulkMonitor extends AbstractLifecycleBean implements HoldBulkMonitor
{
protected SimpleCache<String, HoldBulkStatus> holdProgressCache;
protected SimpleCache<String, BulkCancellationRequest> bulkCancellationsCache;
protected SimpleCache<Pair<String, String>, HoldBulkProcessDetails> holdProcessRegistry;
protected SimpleCache<String, List<HoldBulkProcessDetails>> holdProcessRegistry;
@Override
public void updateBulkStatus(HoldBulkStatus holdBulkStatus)
@@ -57,13 +54,12 @@ public class DefaultHoldBulkMonitor extends AbstractLifecycleBean implements Hol
}
@Override
public void registerProcess(NodeRef holdRef, String processId, BulkOperation bulkOperation)
public void registerProcess(NodeRef holdRef, String processId)
{
if (holdRef != null && processId != null)
{
holdProcessRegistry.put(new Pair<>(holdRef.getId(), processId),
new HoldBulkProcessDetails(processId, getCurrentInstanceDetails(), bulkOperation));
}
List<HoldBulkProcessDetails> processIds = Optional.ofNullable(holdProcessRegistry.get(holdRef.getId()))
.orElse(new ArrayList<>());
processIds.add(new HoldBulkProcessDetails(processId, null));
holdProcessRegistry.put(holdRef.getId(), processIds);
}
@Override
@@ -73,64 +69,18 @@ public class DefaultHoldBulkMonitor extends AbstractLifecycleBean implements Hol
}
@Override
public void cancelBulkOperation(String bulkStatusId, BulkCancellationRequest bulkCancellationRequest)
public List<HoldBulkStatus> getBulkStatusesForHold(String holdId)
{
bulkCancellationsCache.put(bulkStatusId, bulkCancellationRequest);
}
@Override
public boolean isCancelled(String bulkStatusId)
{
return bulkCancellationsCache.contains(bulkStatusId);
}
@Override
public BulkCancellationRequest getBulkCancellationRequest(String bulkStatusId)
{
return bulkCancellationsCache.get(bulkStatusId);
}
@Override
public List<HoldBulkStatusAndProcessDetails> getBulkStatusesWithProcessDetails(String holdId)
{
return holdProcessRegistry.getKeys().stream()
.filter(holdIdAndBulkStatusId -> holdId.equals(holdIdAndBulkStatusId.getFirst()))
.map(holdIdAndBulkStatusId -> holdProcessRegistry.get(holdIdAndBulkStatusId))
.filter(Objects::nonNull)
.map(createHoldBulkStatusAndProcessDetails())
.filter(statusAndProcess -> Objects.nonNull(statusAndProcess.holdBulkStatus()))
.sorted(sortBulkStatuses())
.toList();
}
@Override
public HoldBulkStatusAndProcessDetails getBulkStatusWithProcessDetails(String holdId, String bulkStatusId)
{
return Optional.ofNullable(holdProcessRegistry.get(new Pair<>(holdId, bulkStatusId)))
.map(createHoldBulkStatusAndProcessDetails())
.filter(statusAndProcess -> Objects.nonNull(statusAndProcess.holdBulkStatus()))
.orElse(null);
}
protected String getCurrentInstanceDetails()
{
return null;
}
protected Function<HoldBulkProcessDetails, HoldBulkStatusAndProcessDetails> createHoldBulkStatusAndProcessDetails()
{
return bulkProcessDetails -> new HoldBulkStatusAndProcessDetails(
getBulkStatus(bulkProcessDetails.bulkStatusId()), bulkProcessDetails);
}
protected static Comparator<HoldBulkStatusAndProcessDetails> sortBulkStatuses()
{
return Comparator.<HoldBulkStatusAndProcessDetails, Date>comparing(
statusAndProcess -> statusAndProcess.holdBulkStatus().endTime(),
Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(statusAndProcess -> statusAndProcess.holdBulkStatus().startTime(),
Comparator.nullsLast(Comparator.naturalOrder()))
.reversed();
return Optional.ofNullable(holdProcessRegistry.get(holdId))
.map(bulkProcessDetailsList -> bulkProcessDetailsList.stream()
.map(HoldBulkProcessDetails::bulkStatusId)
.map(this::getBulkStatus)
.filter(Objects::nonNull)
.sorted(Comparator.comparing(HoldBulkStatus::endTime, Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(HoldBulkStatus::startTime, Comparator.nullsLast(Comparator.naturalOrder()))
.reversed())
.toList())
.orElse(Collections.emptyList());
}
public void setHoldProgressCache(
@@ -140,17 +90,11 @@ public class DefaultHoldBulkMonitor extends AbstractLifecycleBean implements Hol
}
public void setHoldProcessRegistry(
SimpleCache<Pair<String, String>, HoldBulkProcessDetails> holdProcessRegistry)
SimpleCache<String, List<HoldBulkProcessDetails>> holdProcessRegistry)
{
this.holdProcessRegistry = holdProcessRegistry;
}
public void setBulkCancellationsCache(
SimpleCache<String, BulkCancellationRequest> bulkCancellationsCache)
{
this.bulkCancellationsCache = bulkCancellationsCache;
}
@Override
protected void onBootstrap(ApplicationEvent applicationEvent)
{

View File

@@ -29,6 +29,7 @@ package org.alfresco.module.org_alfresco_module_rm.bulk.hold;
import java.util.List;
import org.alfresco.module.org_alfresco_module_rm.bulk.BulkMonitor;
import org.alfresco.rm.rest.api.model.HoldBulkStatus;
/**
* An interface for monitoring the progress of a bulk hold operation
@@ -36,19 +37,10 @@ import org.alfresco.module.org_alfresco_module_rm.bulk.BulkMonitor;
public interface HoldBulkMonitor extends BulkMonitor<HoldBulkStatus>
{
/**
* Get the bulk statuses with process details for a hold
* Get the bulk statuses for a hold
*
* @param holdId the hold id
* @return the bulk statuses with process details
* @return the bulk statuses
*/
List<HoldBulkStatusAndProcessDetails> getBulkStatusesWithProcessDetails(String holdId);
/**
* Get the bulk status with process details
*
* @param holdId the hold id
* @param bulkStatusId the bulk status id
* @return the bulk status with process details
*/
HoldBulkStatusAndProcessDetails getBulkStatusWithProcessDetails(String holdId, String bulkStatusId);
List<HoldBulkStatus> getBulkStatusesForHold(String holdId);
}

View File

@@ -28,11 +28,9 @@ package org.alfresco.module.org_alfresco_module_rm.bulk.hold;
import java.io.Serializable;
import org.alfresco.module.org_alfresco_module_rm.bulk.BulkOperation;
/**
* A simple immutable POJO to hold the details of a bulk process
* A simple immutable POJO to hold the details of a bulk hold process
*/
public record HoldBulkProcessDetails(String bulkStatusId, String creatorInstance, BulkOperation bulkOperation) implements Serializable
public record HoldBulkProcessDetails(String bulkStatusId, String creatorInstance) implements Serializable
{
}

View File

@@ -26,8 +26,8 @@
*/
package org.alfresco.module.org_alfresco_module_rm.bulk.hold;
import org.alfresco.module.org_alfresco_module_rm.bulk.BulkCancellationRequest;
import org.alfresco.module.org_alfresco_module_rm.bulk.BulkOperation;
import org.alfresco.rm.rest.api.model.HoldBulkStatus;
import org.alfresco.service.cmr.repository.NodeRef;
/**
@@ -40,16 +40,6 @@ public interface HoldBulkService
*
* @param holdRef The hold reference
* @param bulkOperation The bulk operation
* @return The initial status of the bulk operation
*/
HoldBulkStatus execute(NodeRef holdRef, BulkOperation bulkOperation);
/**
* Cancels a bulk operation.
*
* @param holdRef The hold reference
* @param bulkStatusId The bulk status id
* @param bulkCancellationRequest The bulk cancellation request
*/
void cancelBulkOperation(NodeRef holdRef, String bulkStatusId, BulkCancellationRequest bulkCancellationRequest);
}

View File

@@ -32,13 +32,11 @@ import static org.alfresco.rm.rest.api.model.HoldBulkOperationType.ADD;
import java.util.Collection;
import java.util.Collections;
import java.util.Locale;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.bulk.BulkBaseService;
import org.alfresco.module.org_alfresco_module_rm.bulk.BulkCancellationRequest;
import org.alfresco.module.org_alfresco_module_rm.bulk.BulkOperation;
import org.alfresco.module.org_alfresco_module_rm.bulk.BulkProgress;
import org.alfresco.module.org_alfresco_module_rm.bulk.BulkStatusUpdater;
import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService;
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
@@ -50,6 +48,7 @@ import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.rest.api.search.model.Query;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rm.rest.api.model.HoldBulkOperationType;
import org.alfresco.rm.rest.api.model.HoldBulkStatus;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.ResultSet;
@@ -67,9 +66,10 @@ import org.springframework.extensions.surf.util.I18NUtil;
public class HoldBulkServiceImpl extends BulkBaseService<HoldBulkStatus> implements HoldBulkService
{
private static final Logger LOGGER = LoggerFactory.getLogger(HoldBulkServiceImpl.class);
private static final String MSG_ERR_ACCESS_DENIED = "permissions.err_access_denied";
private HoldService holdService;
private static final String MSG_ERR_ACCESS_DENIED = "permissions.err_access_denied";
private CapabilityService capabilityService;
private PermissionService permissionService;
private NodeService nodeService;
@@ -77,7 +77,7 @@ public class HoldBulkServiceImpl extends BulkBaseService<HoldBulkStatus> impleme
@Override
protected HoldBulkStatus getInitBulkStatus(String processId, long totalItems)
{
return new HoldBulkStatus(processId, null, null, 0, 0, totalItems, null, false, null);
return new HoldBulkStatus(processId, null, null, 0, 0, totalItems, null);
}
@Override
@@ -87,16 +87,14 @@ public class HoldBulkServiceImpl extends BulkBaseService<HoldBulkStatus> impleme
}
@Override
protected BatchProcessWorkProvider<NodeRef> getWorkProvider(BulkOperation bulkOperation,
BulkStatusUpdater bulkStatusUpdater, BulkProgress bulkProgress)
protected BatchProcessWorkProvider<NodeRef> getWorkProvider(BulkOperation bulkOperation, long totalItems,
BulkStatusUpdater bulkStatusUpdater)
{
return new AddToHoldWorkerProvider(bulkOperation, bulkStatusUpdater, bulkProgress,
(HoldBulkMonitor) bulkMonitor);
return new AddToHoldWorkerProvider(new AtomicInteger(0), bulkOperation, totalItems, bulkStatusUpdater);
}
@Override
protected BatchProcessWorker<NodeRef> getWorkerProvider(NodeRef nodeRef, BulkOperation bulkOperation,
BulkProgress bulkProgress)
protected BatchProcessWorker<NodeRef> getWorkerProvider(NodeRef nodeRef, BulkOperation bulkOperation)
{
try
{
@@ -104,7 +102,7 @@ public class HoldBulkServiceImpl extends BulkBaseService<HoldBulkStatus> impleme
.toUpperCase(Locale.ENGLISH));
return switch (holdBulkOperationType)
{
case ADD -> new AddToHoldWorkerBatch(nodeRef, bulkProgress);
case ADD -> new AddToHoldWorkerBatch(nodeRef);
};
}
catch (IllegalArgumentException e)
@@ -135,32 +133,14 @@ public class HoldBulkServiceImpl extends BulkBaseService<HoldBulkStatus> impleme
}
@Override
public void cancelBulkOperation(NodeRef holdRef, String bulkStatusId, BulkCancellationRequest cancellationRequest)
{
if (bulkMonitor instanceof HoldBulkMonitor holdBulkMonitor)
{
HoldBulkStatusAndProcessDetails statusAndProcessDetails = holdBulkMonitor.getBulkStatusWithProcessDetails(
holdRef.getId(), bulkStatusId);
Optional.ofNullable(statusAndProcessDetails).map(HoldBulkStatusAndProcessDetails::holdBulkProcessDetails)
.map(HoldBulkProcessDetails::bulkOperation).ifPresent(bulkOperation -> {
checkPermissions(holdRef, bulkOperation);
holdBulkMonitor.cancelBulkOperation(bulkStatusId, cancellationRequest);
});
}
}
private class AddToHoldWorkerBatch implements BatchProcessWorker<NodeRef>
{
private final NodeRef holdRef;
private final String currentUser;
private final BulkProgress bulkProgress;
public AddToHoldWorkerBatch(NodeRef holdRef, BulkProgress bulkProgress)
public AddToHoldWorkerBatch(NodeRef holdRef)
{
this.holdRef = holdRef;
this.bulkProgress = bulkProgress;
currentUser = AuthenticationUtil.getFullyAuthenticatedUser();
}
@@ -179,11 +159,8 @@ public class HoldBulkServiceImpl extends BulkBaseService<HoldBulkStatus> impleme
@Override
public void process(NodeRef entry) throws Throwable
{
if (!bulkProgress.cancelled().get())
{
AuthenticationUtil.setFullyAuthenticatedUser(currentUser);
holdService.addToHold(holdRef, entry);
}
AuthenticationUtil.setFullyAuthenticatedUser(currentUser);
holdService.addToHold(holdRef, entry);
}
@Override
@@ -195,32 +172,32 @@ public class HoldBulkServiceImpl extends BulkBaseService<HoldBulkStatus> impleme
private class AddToHoldWorkerProvider implements BatchProcessWorkProvider<NodeRef>
{
private final HoldBulkMonitor holdBulkMonitor;
private final AtomicInteger currentNodeNumber;
private final Query searchQuery;
private final String currentUser;
private final BulkProgress bulkProgress;
private final long totalItems;
private final BulkStatusUpdater bulkStatusUpdater;
public AddToHoldWorkerProvider(BulkOperation bulkOperation,
BulkStatusUpdater bulkStatusUpdater, BulkProgress bulkProgress, HoldBulkMonitor holdBulkMonitor)
public AddToHoldWorkerProvider(AtomicInteger currentNodeNumber, BulkOperation bulkOperation, long totalItems,
BulkStatusUpdater bulkStatusUpdater)
{
this.currentNodeNumber = currentNodeNumber;
this.searchQuery = bulkOperation.searchQuery();
this.bulkProgress = bulkProgress;
this.totalItems = totalItems;
this.bulkStatusUpdater = bulkStatusUpdater;
this.holdBulkMonitor = holdBulkMonitor;
currentUser = AuthenticationUtil.getFullyAuthenticatedUser();
}
@Override
public int getTotalEstimatedWorkSize()
{
return (int) bulkProgress.totalItems();
return (int) totalItems;
}
@Override
public long getTotalEstimatedWorkSizeLong()
{
return bulkProgress.totalItems();
return totalItems;
}
@Override
@@ -228,11 +205,6 @@ public class HoldBulkServiceImpl extends BulkBaseService<HoldBulkStatus> impleme
{
AuthenticationUtil.pushAuthentication();
AuthenticationUtil.setFullyAuthenticatedUser(currentUser);
if (holdBulkMonitor.isCancelled(bulkProgress.processId()))
{
bulkProgress.cancelled().set(true);
return Collections.emptyList();
}
SearchParameters searchParams = getNextPageParameters();
ResultSet result = searchService.query(searchParams);
if (result.getNodeRefs().isEmpty())
@@ -245,7 +217,7 @@ public class HoldBulkServiceImpl extends BulkBaseService<HoldBulkStatus> impleme
LOGGER.debug("Processing the next work for the batch processor, skipCount={}, size={}",
searchParams.getSkipCount(), result.getNumberFound());
}
bulkProgress.currentNodeNumber().addAndGet(batchSize);
currentNodeNumber.addAndGet(batchSize);
bulkStatusUpdater.update();
return result.getNodeRefs();
}
@@ -255,7 +227,7 @@ public class HoldBulkServiceImpl extends BulkBaseService<HoldBulkStatus> impleme
SearchParameters searchParams = new SearchParameters();
searchMapper.setDefaults(searchParams);
searchMapper.fromQuery(searchParams, searchQuery);
searchParams.setSkipCount(bulkProgress.currentNodeNumber().get());
searchParams.setSkipCount(currentNodeNumber.get());
searchParams.setMaxItems(batchSize);
searchParams.setLimit(batchSize);
searchParams.addSort("@" + ContentModel.PROP_CREATED, true);

View File

@@ -1,35 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.module.org_alfresco_module_rm.bulk.hold;
/**
* An immutable POJO that contains the status of a hold bulk operation and the details of the process
*/
public record HoldBulkStatusAndProcessDetails(HoldBulkStatus holdBulkStatus,
HoldBulkProcessDetails holdBulkProcessDetails)
{
}

View File

@@ -26,12 +26,10 @@
*/
package org.alfresco.module.org_alfresco_module_rm.bulk.hold;
import java.util.Optional;
import org.alfresco.module.org_alfresco_module_rm.bulk.BulkCancellationRequest;
import org.alfresco.module.org_alfresco_module_rm.bulk.BulkStatusUpdater;
import org.alfresco.repo.batch.BatchMonitor;
import org.alfresco.repo.batch.BatchMonitorEvent;
import org.alfresco.rm.rest.api.model.HoldBulkStatus;
/**
* An implementation of {@link BulkStatusUpdater} for the hold bulk operation
@@ -44,17 +42,11 @@ public class HoldBulkStatusUpdater implements BulkStatusUpdater
public HoldBulkStatusUpdater(HoldBulkMonitor holdBulkMonitor)
{
this.task = () -> holdBulkMonitor.updateBulkStatus(
new HoldBulkStatus(batchMonitor.getProcessName(),
batchMonitor.getStartTime(),
new HoldBulkStatus(batchMonitor.getProcessName(), batchMonitor.getStartTime(),
batchMonitor.getEndTime(),
batchMonitor.getSuccessfullyProcessedEntriesLong() + batchMonitor.getTotalErrorsLong(),
batchMonitor.getTotalErrorsLong(),
batchMonitor.getTotalResultsLong(),
batchMonitor.getLastError(),
holdBulkMonitor.isCancelled(batchMonitor.getProcessName()),
Optional.ofNullable(holdBulkMonitor.getBulkCancellationRequest(batchMonitor.getProcessName()))
.map(BulkCancellationRequest::reason)
.orElse(null)));
batchMonitor.getTotalErrorsLong(), batchMonitor.getTotalResultsLong(),
batchMonitor.getLastError()));
}
@Override

View File

@@ -1,69 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.module.org_alfresco_module_rm.bulk.hold;
import org.alfresco.module.org_alfresco_module_rm.bulk.BulkOperation;
import org.alfresco.rest.api.search.model.Query;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rm.rest.api.model.HoldBulkOperation;
import org.alfresco.rm.rest.api.model.HoldBulkOperationType;
import org.alfresco.rm.rest.api.model.HoldBulkStatusEntry;
/**
* Utility class for hold bulk operations
*/
@SuppressWarnings("PMD.PreserveStackTrace")
public final class HoldBulkUtils
{
private HoldBulkUtils()
{
}
public static HoldBulkStatusEntry toHoldBulkStatusEntry(
HoldBulkStatusAndProcessDetails holdBulkStatusAndProcessDetails)
{
HoldBulkStatus bulkStatus = holdBulkStatusAndProcessDetails.holdBulkStatus();
BulkOperation bulkOperation = holdBulkStatusAndProcessDetails.holdBulkProcessDetails().bulkOperation();
try
{
HoldBulkOperation holdBulkOperation = new HoldBulkOperation(
new Query(bulkOperation.searchQuery().getLanguage(),
bulkOperation.searchQuery().getQuery(), bulkOperation.searchQuery().getUserQuery()),
HoldBulkOperationType.valueOf(bulkOperation.operationType()));
return new HoldBulkStatusEntry(bulkStatus.bulkStatusId(), bulkStatus.startTime(),
bulkStatus.endTime(), bulkStatus.processedItems(), bulkStatus.errorsCount(),
bulkStatus.totalItems(), bulkStatus.lastError(), bulkStatus.getStatus(),
bulkStatus.cancellationReason(), holdBulkOperation);
}
catch (IllegalArgumentException e)
{
String errorMsg = "Unsupported action type in the bulk operation: ";
throw new InvalidArgumentException(errorMsg + bulkOperation.operationType());
}
}
}

View File

@@ -31,6 +31,7 @@ import java.util.HashMap;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
import org.alfresco.module.org_alfresco_module_rm.capability.declarative.DeclarativeCapability;
import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService;
@@ -75,7 +76,7 @@ public class CreateCapability extends DeclarativeCapability
@Override
public int evaluate(NodeRef nodeRef)
{
return evaluate(nodeRef, null, null, null);
return evaluate(nodeRef, null, null);
}
/**
@@ -84,10 +85,9 @@ public class CreateCapability extends DeclarativeCapability
* @param destination destination node reference
* @param linkee linkee node reference, can be null
* @param assocType association type, can be null
* @param recordType record type, can be null
* @return
*/
public int evaluate(NodeRef destination, NodeRef linkee, QName assocType, QName recordType)
public int evaluate(NodeRef destination, NodeRef linkee, QName assocType)
{
if (linkee != null)
{
@@ -105,7 +105,7 @@ public class CreateCapability extends DeclarativeCapability
{
if (recordService.isRecord(destination) &&
!recordService.isDeclared(destination) &&
permissionService.hasPermission(destination, FILE_RECORDS) == AccessStatus.ALLOWED)
permissionService.hasPermission(destination, RMPermissionModel.FILE_RECORDS) == AccessStatus.ALLOWED)
{
return AccessDecisionVoter.ACCESS_GRANTED;
}
@@ -115,7 +115,7 @@ public class CreateCapability extends DeclarativeCapability
if (recordService.isRecord(linkee) &&
recordService.isRecord(destination) &&
!recordService.isDeclared(destination) &&
permissionService.hasPermission(destination, FILE_RECORDS) == AccessStatus.ALLOWED)
permissionService.hasPermission(destination, RMPermissionModel.FILE_RECORDS) == AccessStatus.ALLOWED)
{
return AccessDecisionVoter.ACCESS_GRANTED;
}
@@ -132,15 +132,14 @@ public class CreateCapability extends DeclarativeCapability
// if the destination folder is not a record folder and the user has filling capability on it, grant access to create the record
if (checkConditions(destination, conditions) &&
!recordFolderService.isRecordFolder(destination) &&
permissionService.hasPermission(destination, CREATE_MODIFY_DESTROY_FILEPLAN_METADATA) == AccessStatus.ALLOWED)
!recordFolderService.isRecordFolder(destination) )
{
return AccessDecisionVoter.ACCESS_GRANTED;
}
if (checkConditions(destination, conditions) &&
recordFolderService.isRecordFolder(destination) &&
permissionService.hasPermission(destination, FILE_RECORDS) == AccessStatus.ALLOWED)
permissionService.hasPermission(destination, RMPermissionModel.FILE_RECORDS) == AccessStatus.ALLOWED)
{
return AccessDecisionVoter.ACCESS_GRANTED;
}
@@ -148,7 +147,7 @@ public class CreateCapability extends DeclarativeCapability
conditions.put("capabilityCondition.closed", Boolean.TRUE);
if (checkConditions(destination, conditions) &&
recordFolderService.isRecordFolder(destination) &&
permissionService.hasPermission(getFilePlanService().getFilePlan(destination), DECLARE_RECORDS_IN_CLOSED_FOLDERS) == AccessStatus.ALLOWED)
permissionService.hasPermission(getFilePlanService().getFilePlan(destination), RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS) == AccessStatus.ALLOWED)
{
return AccessDecisionVoter.ACCESS_GRANTED;
}
@@ -157,32 +156,32 @@ public class CreateCapability extends DeclarativeCapability
conditions.put("capabilityCondition.cutoff", Boolean.TRUE);
if (checkConditions(destination, conditions) &&
recordFolderService.isRecordFolder(destination) &&
permissionService.hasPermission(getFilePlanService().getFilePlan(destination), CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS) == AccessStatus.ALLOWED)
permissionService.hasPermission(getFilePlanService().getFilePlan(destination), RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS) == AccessStatus.ALLOWED)
{
return AccessDecisionVoter.ACCESS_GRANTED;
}
}
if (null != recordType && recordType.equals(TYPE_RECORD_FOLDER) && capabilityService.getCapability(CREATE_MODIFY_DESTROY_FOLDERS).evaluate(destination) == AccessDecisionVoter.ACCESS_GRANTED)
if (capabilityService.getCapability(RMPermissionModel.CREATE_MODIFY_DESTROY_FOLDERS).evaluate(destination) == AccessDecisionVoter.ACCESS_GRANTED)
{
return AccessDecisionVoter.ACCESS_GRANTED;
}
if (capabilityService.getCapability(DECLARE_RECORDS_IN_CLOSED_FOLDERS).evaluate(destination) == AccessDecisionVoter.ACCESS_GRANTED)
if (capabilityService.getCapability(RMPermissionModel.DECLARE_RECORDS_IN_CLOSED_FOLDERS).evaluate(destination) == AccessDecisionVoter.ACCESS_GRANTED)
{
return AccessDecisionVoter.ACCESS_GRANTED;
}
if (capabilityService.getCapability(CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS).evaluate(destination) == AccessDecisionVoter.ACCESS_GRANTED)
if (capabilityService.getCapability(RMPermissionModel.CREATE_MODIFY_RECORDS_IN_CUTOFF_FOLDERS).evaluate(destination) == AccessDecisionVoter.ACCESS_GRANTED)
{
return AccessDecisionVoter.ACCESS_GRANTED;
}
if (capabilityService.getCapability(CREATE_MODIFY_DESTROY_FILEPLAN_METADATA).evaluate(destination) == AccessDecisionVoter.ACCESS_GRANTED)
if (capabilityService.getCapability(RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA).evaluate(destination) == AccessDecisionVoter.ACCESS_GRANTED)
{
return AccessDecisionVoter.ACCESS_GRANTED;
}
if (capabilityService.getCapability(CREATE_HOLD).evaluate(destination) == AccessDecisionVoter.ACCESS_GRANTED)
if (capabilityService.getCapability(RMPermissionModel.CREATE_HOLD).evaluate(destination) == AccessDecisionVoter.ACCESS_GRANTED)
{
return AccessDecisionVoter.ACCESS_GRANTED;
}
if (((ChangeOrDeleteReferencesCapability)capabilityService.getCapability(CHANGE_OR_DELETE_REFERENCES)).evaluate(destination, linkee) == AccessDecisionVoter.ACCESS_GRANTED)
if (((ChangeOrDeleteReferencesCapability)capabilityService.getCapability(RMPermissionModel.CHANGE_OR_DELETE_REFERENCES)).evaluate(destination, linkee) == AccessDecisionVoter.ACCESS_GRANTED)
{
return AccessDecisionVoter.ACCESS_GRANTED;
}

View File

@@ -28,7 +28,6 @@
package org.alfresco.module.org_alfresco_module_rm.capability.policy;
import org.alfresco.module.org_alfresco_module_rm.capability.impl.CreateCapability;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.aopalliance.intercept.MethodInvocation;
@@ -43,18 +42,10 @@ public class CreatePolicy extends AbstractBasePolicy
{
NodeRef linkee = null;
QName assocType = null;
QName recordType = null;
// get the destination node
NodeRef destination = getTestNode(invocation, params, cad.getParameters().get(0), cad.isParent());
//get the recordType
for (Object qname : invocation.getArguments()) {
if (qname != null && (qname.equals(RecordsManagementModel.TYPE_RECORD_FOLDER) || qname.equals(RecordsManagementModel.TYPE_RECORD_CATEGORY))) {
recordType = (QName) qname;
}
}
if (cad.getParameters().size() > 1)
{
// get the linkee when present
@@ -67,7 +58,7 @@ public class CreatePolicy extends AbstractBasePolicy
}
}
return ((CreateCapability) getCapabilityService().getCapability("Create")).evaluate(destination, linkee, assocType, recordType);
return ((CreateCapability) getCapabilityService().getCapability("Create")).evaluate(destination, linkee, assocType);
}
}

View File

@@ -59,9 +59,6 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.rest.framework.core.exceptions.ConstraintViolatedException;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -201,7 +198,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
/**
* Behavior to initialize the disposition schedule of a newly filed record.
*
* @see RecordsManagementPolicies.OnFileRecord#onFileRecord(NodeRef)
* @see org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnFileRecord#onFileRecord(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
@Behaviour(kind=BehaviourKind.CLASS, type="rma:record")
@@ -219,7 +216,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
/**
* @see DispositionService#refreshDispositionAction(NodeRef)
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#refreshDispositionAction(NodeRef)
*/
@Override
public void refreshDispositionAction(NodeRef nodeRef)
@@ -245,7 +242,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
/** ========= Disposition Property Methods ========= */
/**
* @see DispositionService#registerDispositionProperty(DispositionProperty)
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#registerDispositionProperty(org.alfresco.module.org_alfresco_module_rm.disposition.property.DispositionProperty)
*/
@Override
public void registerDispositionProperty(DispositionProperty dispositionProperty)
@@ -254,7 +251,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
/**
* @see DispositionService#getDispositionProperties(boolean, String)
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#getDispositionProperties(boolean, java.lang.String)
*/
@Override
public Collection<DispositionProperty> getDispositionProperties(boolean isRecordLevel, String dispositionAction)
@@ -273,7 +270,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
/**
* @see DispositionService#getDispositionProperties()
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#getDispositionProperties()
*/
@Override
public Collection<DispositionProperty> getDispositionProperties()
@@ -284,11 +281,12 @@ public class DispositionServiceImpl extends ServiceBaseImpl
/** ========= Disposition Schedule Methods ========= */
/**
* @see DispositionService#getDispositionSchedule(NodeRef)
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#getDispositionSchedule(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
public DispositionSchedule getDispositionSchedule(final NodeRef nodeRef)
{
DispositionSchedule ds = null;
NodeRef dsNodeRef = null;
if (isRecord(nodeRef))
{
@@ -313,33 +311,36 @@ public class DispositionServiceImpl extends ServiceBaseImpl
if (dsNextAction != null)
{
final NodeRef action = dsNextAction.getNextActionNodeRef();
if (isNotTrue((Boolean)nodeService.getProperty(action, PROP_MANUALLY_SET_AS_OF)) && !dsNextAction.getWriteMode().equals(WriteMode.READ_ONLY))
if (isNotTrue((Boolean)nodeService.getProperty(action, PROP_MANUALLY_SET_AS_OF)))
{
final String dispositionActionName = dsNextAction.getNextActionName();
final Date dispositionActionDate = dsNextAction.getNextActionDateAsOf();
RunAsWork<Void> runAsWork = () -> {
nodeService.setProperty(action, PROP_DISPOSITION_AS_OF, dispositionActionDate);
return null;
};
// if the current transaction is READ ONLY set the property on the node
// in a READ WRITE transaction
if (AlfrescoTransactionSupport.getTransactionReadState().equals(TxnReadState.TXN_READ_ONLY))
if (!dsNextAction.getWriteMode().equals(WriteMode.READ_ONLY))
{
transactionService.getRetryingTransactionHelper().doInTransaction((RetryingTransactionCallback<Void>) () -> {
AuthenticationUtil.runAsSystem(runAsWork);
final String dispositionActionName = dsNextAction.getNextActionName();
final Date dispositionActionDate = dsNextAction.getNextActionDateAsOf();
RunAsWork<Void> runAsWork = () -> {
nodeService.setProperty(action, PROP_DISPOSITION_AS_OF, dispositionActionDate);
return null;
}, false, true);
}
else
{
AuthenticationUtil.runAsSystem(runAsWork);
}
};
if (dsNextAction.getWriteMode().equals(WriteMode.DATE_AND_NAME))
{
nodeService.setProperty(action, PROP_DISPOSITION_ACTION_NAME, dispositionActionName);
// if the current transaction is READ ONLY set the property on the node
// in a READ WRITE transaction
if (AlfrescoTransactionSupport.getTransactionReadState().equals(TxnReadState.TXN_READ_ONLY))
{
transactionService.getRetryingTransactionHelper().doInTransaction((RetryingTransactionCallback<Void>) () -> {
AuthenticationUtil.runAsSystem(runAsWork);
return null;
}, false, true);
}
else
{
AuthenticationUtil.runAsSystem(runAsWork);
}
if (dsNextAction.getWriteMode().equals(WriteMode.DATE_AND_NAME))
{
nodeService.setProperty(action, PROP_DISPOSITION_ACTION_NAME, dispositionActionName);
}
}
}
@@ -351,7 +352,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
// Get the disposition instructions for the node reference provided
dsNodeRef = getDispositionScheduleImpl(nodeRef);
}
DispositionSchedule ds = null;
if (dsNodeRef != null)
{
ds = new DispositionScheduleImpl(serviceRegistry, nodeService, dsNodeRef);
@@ -381,8 +382,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
return result;
}
@Override
public DispositionSchedule getOriginDispositionSchedule(NodeRef nodeRef)
{
NodeRef parent = this.nodeService.getPrimaryParent(nodeRef).getParentRef();
@@ -406,7 +406,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
/**
* @see DispositionService#getAssociatedDispositionSchedule(NodeRef)
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#getAssociatedDispositionSchedule(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
public DispositionSchedule getAssociatedDispositionSchedule(NodeRef nodeRef)
@@ -437,6 +437,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
*/
private NodeRef getAssociatedDispositionScheduleImpl(NodeRef nodeRef)
{
NodeRef result = null;
ParameterCheck.mandatory("nodeRef", nodeRef);
// Make sure we are dealing with an RM node
@@ -444,7 +445,6 @@ public class DispositionServiceImpl extends ServiceBaseImpl
{
throw new AlfrescoRuntimeException("Can not find the associated retention schedule for a non records management component. (nodeRef=" + nodeRef.toString() + ")");
}
NodeRef result = null;
if (getInternalNodeService().hasAspect(nodeRef, ASPECT_SCHEDULED))
{
List<ChildAssociationRef> childAssocs = getInternalNodeService().getChildAssocs(nodeRef, ASSOC_DISPOSITION_SCHEDULE, RegexQNamePattern.MATCH_ALL);
@@ -459,7 +459,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
/**
* @see DispositionService#getAssociatedRecordsManagementContainer(DispositionSchedule)
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#getAssociatedRecordsManagementContainer(org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule)
*/
@Override
public NodeRef getAssociatedRecordsManagementContainer(DispositionSchedule dispositionSchedule)
@@ -477,9 +477,12 @@ public class DispositionServiceImpl extends ServiceBaseImpl
{
// TODO in the future we should be able to support disposition schedule reuse, but for now just warn that
// only the first disposition schedule will be considered
LOGGER.atWarn().log("Retention schedule has more than one associated records management container. " +
"This is not currently supported so only the first container will be considered. " +
"(dispositionScheduleNodeRef={})", dispositionSchedule.getNodeRef());
if (LOGGER.isWarnEnabled())
{
LOGGER.warn("Retention schedule has more than one associated records management container. " +
"This is not currently supported so only the first container will be considered. " +
"(dispositionScheduleNodeRef=" + dispositionSchedule.getNodeRef().toString() + ")");
}
}
// Get the container reference
@@ -492,7 +495,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
/**
* @see DispositionService#hasDisposableItems(DispositionSchedule)
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#hasDisposableItems(org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule)
*/
@Override
public boolean hasDisposableItems(DispositionSchedule dispositionSchdule)
@@ -534,16 +537,19 @@ public class DispositionServiceImpl extends ServiceBaseImpl
return true;
}
}
else if (filePlanService.isRecordCategory(item) && getAssociatedDispositionScheduleImpl(item) == null && hasDisposableItemsImpl(isRecordLevelDisposition, item))
else if (filePlanService.isRecordCategory(item) && getAssociatedDispositionScheduleImpl(item) == null)
{
return true;
if (hasDisposableItemsImpl(isRecordLevelDisposition, item));
{
return true;
}
}
}
return false;
}
/**
* @see DispositionService#getDisposableItems(DispositionSchedule)
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#getDisposableItems(org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule)
*/
@Override
public List<NodeRef> getDisposableItems(DispositionSchedule dispositionSchedule)
@@ -558,7 +564,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
/**
* @see DispositionService#isDisposableItem(NodeRef)
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#isDisposableItem(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
public boolean isDisposableItem(NodeRef nodeRef)
@@ -598,18 +604,20 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
/**
* @see DispositionService#createDispositionSchedule(NodeRef, Map)
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#createDispositionSchedule(org.alfresco.service.cmr.repository.NodeRef, java.util.Map)
*/
@Override
public DispositionSchedule createDispositionSchedule(NodeRef nodeRef, Map<QName, Serializable> props)
{
NodeRef dsNodeRef = null;
// Check mandatory parameters
ParameterCheck.mandatory("nodeRef", nodeRef);
// Check exists
if (!nodeService.exists(nodeRef))
{
throw new EntityNotFoundException(nodeRef.getId());
throw new AlfrescoRuntimeException("Unable to create retention schedule, because node does not exist. (nodeRef=" + nodeRef.toString() + ")");
}
// Check is sub-type of rm:recordCategory
@@ -617,12 +625,10 @@ public class DispositionServiceImpl extends ServiceBaseImpl
if (!TYPE_RECORD_CATEGORY.equals(nodeRefType) &&
!dictionaryService.isSubClass(nodeRefType, TYPE_RECORD_CATEGORY))
{
throw new InvalidArgumentException("The given id:'" + nodeRef.getId() + "' (nodeType:" + nodeRef
+ ") is not valid. Expected nodeType is:" + TYPE_RECORD_CATEGORY);
throw new AlfrescoRuntimeException("Unable to create retention schedule on a node that is not a records management container.");
}
behaviourFilter.disableBehaviour(nodeRef, ASPECT_SCHEDULED);
NodeRef dsNodeRef = null;
try
{
// Add the schedules aspect if required
@@ -656,7 +662,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
else
{
// Error since the node already has a disposition schedule set
throw new ConstraintViolatedException("Unable to create retention schedule on node that already has a retention schedule.");
throw new AlfrescoRuntimeException("Unable to create retention schedule on node that already has a retention schedule.");
}
}
finally
@@ -680,7 +686,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
{
// make sure at least a name has been defined
String name = (String)actionDefinitionParams.get(PROP_DISPOSITION_ACTION_NAME);
if (name == null || name.isEmpty())
if (name == null || name.length() == 0)
{
throw new IllegalArgumentException("'name' parameter is mandatory when creating a disposition action definition");
}
@@ -689,10 +695,10 @@ public class DispositionServiceImpl extends ServiceBaseImpl
// create the child association from the schedule to the action definition
NodeRef actionNodeRef = this.nodeService.createNode(schedule.getNodeRef(),
ASSOC_DISPOSITION_ACTION_DEFINITIONS,
RecordsManagementModel.ASSOC_DISPOSITION_ACTION_DEFINITIONS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,
QName.createValidLocalName(name)),
TYPE_DISPOSITION_ACTION_DEFINITION, actionDefinitionParams).getChildRef();
RecordsManagementModel.TYPE_DISPOSITION_ACTION_DEFINITION, actionDefinitionParams).getChildRef();
// get the updated disposition schedule and retrieve the new action definition
NodeRef scheduleParent = this.nodeService.getPrimaryParent(schedule.getNodeRef()).getParentRef();
@@ -701,7 +707,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
/**
* @see DispositionService#removeDispositionActionDefinition(DispositionSchedule, DispositionActionDefinition)
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#removeDispositionActionDefinition(org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule, org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition)
*/
@Override
public void removeDispositionActionDefinition(DispositionSchedule schedule, DispositionActionDefinition actionDefinition)
@@ -771,12 +777,16 @@ public class DispositionServiceImpl extends ServiceBaseImpl
DispositionAction da;
// check if current transaction is a READ ONLY one and if true create the node in a READ WRITE transaction
if (AlfrescoTransactionSupport.getTransactionReadState().equals(TxnReadState.TXN_READ_ONLY)) {
da = transactionService.getRetryingTransactionHelper().doInTransaction(
() -> createDispositionAction(nodeRef, props),
false,
true
);
if (AlfrescoTransactionSupport.getTransactionReadState().equals(TxnReadState.TXN_READ_ONLY))
{
da =
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<DispositionAction>()
{
public DispositionAction execute() throws Throwable
{
return createDispositionAction(nodeRef, props);
}
}, false, true);
}
else
{
@@ -826,13 +836,13 @@ public class DispositionServiceImpl extends ServiceBaseImpl
Period period = dispositionActionDefinition.getPeriod();
if (period != null)
{
Date contextDate;
Date contextDate = null;
// Get the period properties value
QName periodProperty = dispositionActionDefinition.getPeriodProperty();
if (periodProperty != null)
{
if (PROP_DISPOSITION_AS_OF.equals(periodProperty))
if (RecordsManagementModel.PROP_DISPOSITION_AS_OF.equals(periodProperty))
{
DispositionAction lastCompletedDispositionAction = getLastCompletedDispostionAction(nodeRef);
if (lastCompletedDispositionAction != null)
@@ -876,7 +886,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
/**
* @see DispositionService#isNextDispositionActionEligible(NodeRef)
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#isNextDispositionActionEligible(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
public boolean isNextDispositionActionEligible(NodeRef nodeRef)
@@ -930,7 +940,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
{
NodeRef eventExecution = assoc.getChildRef();
Boolean isCompleteValue = (Boolean) getInternalNodeService().getProperty(eventExecution, PROP_EVENT_EXECUTION_COMPLETE);
boolean isComplete;
boolean isComplete = false;
if (isCompleteValue != null)
{
isComplete = isCompleteValue.booleanValue();
@@ -977,7 +987,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
/**
* @see DispositionService#getNextDispositionAction(NodeRef)
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#getNextDispositionAction(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
public DispositionAction getNextDispositionAction(NodeRef nodeRef)
@@ -996,7 +1006,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
/** ========= Disposition Action History Methods ========= */
/**
* @see DispositionService#getCompletedDispositionActions(NodeRef)
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#getCompletedDispositionActions(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
public List<DispositionAction> getCompletedDispositionActions(NodeRef nodeRef)
@@ -1012,7 +1022,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
/**
* @see DispositionService#getLastCompletedDispostionAction(NodeRef)
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#getLastCompletedDispostionAction(org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
public DispositionAction getLastCompletedDispostionAction(NodeRef nodeRef)
@@ -1028,7 +1038,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
/**
* @see DispositionService#isDisposableItemCutoff(NodeRef)
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#isDisposableItemCutoff(NodeRef)
*/
@Override
public boolean isDisposableItemCutoff(NodeRef nodeRef)
@@ -1038,7 +1048,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
/**
* @see DispositionService#updateNextDispositionAction(NodeRef)
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#updateNextDispositionAction(NodeRef)
*/
@Override
public void updateNextDispositionAction(final NodeRef nodeRef)
@@ -1048,7 +1058,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
RunAsWork<Void> runAsWork = new RunAsWork<Void>()
{
/**
* @see RunAsWork#doWork()
* @see org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork#doWork()
*/
@Override
public Void doWork()
@@ -1067,7 +1077,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
/**
* @see DispositionService#updateNextDispositionAction(NodeRef)
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#updateNextDispositionAction(NodeRef)
*/
@Override
public void updateNextDispositionAction(final NodeRef nodeRef, final DispositionSchedule dispositionSchedule)
@@ -1077,7 +1087,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
RunAsWork<Void> runAsWork = new RunAsWork<Void>()
{
/**
* @see RunAsWork#doWork()
* @see org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork#doWork()
*/
@Override
public Void doWork()
@@ -1103,13 +1113,16 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
List<DispositionActionDefinition> dispositionActionDefinitions = dispositionSchedule.getDispositionActionDefinitions();
DispositionActionDefinition currentDispositionActionDefinition;
DispositionActionDefinition currentDispositionActionDefinition = null;
DispositionActionDefinition nextDispositionActionDefinition = null;
if (currentDispositionAction == null && !dispositionActionDefinitions.isEmpty())
if (currentDispositionAction == null)
{
// The next disposition action is the first action
nextDispositionActionDefinition = dispositionActionDefinitions.get(0);
if (!dispositionActionDefinitions.isEmpty())
{
// The next disposition action is the first action
nextDispositionActionDefinition = dispositionActionDefinitions.get(0);
}
}
else
{
@@ -1154,7 +1167,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
/**
* @see DispositionService#cutoffDisposableItem(NodeRef)
* @see org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService#cutoffDisposableItem(NodeRef)
*/
@Override
public void cutoffDisposableItem(final NodeRef nodeRef)
@@ -1192,7 +1205,6 @@ public class DispositionServiceImpl extends ServiceBaseImpl
// runAs system so that we can close a record that has already been cutoff
authenticationUtil.runAsSystem(new RunAsWork<Void>()
{
@Override
public Void doWork() throws Exception
{
recordFolderService.closeRecordFolder(nodeRef);
@@ -1212,7 +1224,6 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
}
@Override
public Date getDispositionActionDate(NodeRef record, NodeRef dispositionSchedule, String dispositionActionName)
{
DispositionSchedule ds = new DispositionScheduleImpl(serviceRegistry, nodeService, dispositionSchedule);
@@ -1232,8 +1243,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
}
return null;
}
@Override
public void recalculateNextDispositionStep(NodeRef record)
{
List<NodeRef> recordFolders = recordFolderService.getRecordFolders(record);
@@ -1374,7 +1384,14 @@ public class DispositionServiceImpl extends ServiceBaseImpl
Date calculatedDate = (nextDispositionActionDate != null ? nextDispositionActionDate : maxDate);
// We only need to update the date if the current one is too early.
return recordDate.before(calculatedDate) ? WriteMode.DATE_ONLY : WriteMode.READ_ONLY;
if (recordDate.before(calculatedDate))
{
return WriteMode.DATE_ONLY;
}
else
{
return WriteMode.READ_ONLY;
}
}
/**
@@ -1397,7 +1414,7 @@ public class DispositionServiceImpl extends ServiceBaseImpl
DispositionSchedule ds = new DispositionScheduleImpl(serviceRegistry, nodeService, folderDS);
List<DispositionActionDefinition> dispositionActionDefinitions = ds.getDispositionActionDefinitions();
if (dispositionActionDefinitions != null && !dispositionActionDefinitions.isEmpty())
if (dispositionActionDefinitions != null && dispositionActionDefinitions.size() > 0)
{
DispositionActionDefinition firstDispositionActionDef = dispositionActionDefinitions.get(0);
dispositionNodeRef = folderDS;

View File

@@ -34,27 +34,17 @@ import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import jakarta.servlet.http.HttpServletResponse;
import org.alfresco.module.org_alfresco_module_rm.bulk.BulkCancellationRequest;
import org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkMonitor;
import org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkService;
import org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkStatusAndProcessDetails;
import org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkUtils;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.rest.framework.Operation;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.NotFoundException;
import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException;
import org.alfresco.rest.framework.core.exceptions.RelationshipResourceNotFoundException;
import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rest.framework.webscripts.WithResponse;
import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
import org.alfresco.rm.rest.api.model.BulkCancellationEntry;
import org.alfresco.rm.rest.api.model.HoldBulkStatusEntry;
import org.alfresco.rm.rest.api.model.HoldBulkStatus;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
@@ -62,16 +52,14 @@ import org.springframework.extensions.surf.util.I18NUtil;
@RelationshipResource(name = "bulk-statuses", entityResource = HoldsEntityResource.class, title = "Bulk statuses of a hold")
public class HoldsBulkStatusesRelation
implements RelationshipResourceAction.Read<HoldBulkStatusEntry>,
RelationshipResourceAction.ReadById<HoldBulkStatusEntry>
implements RelationshipResourceAction.Read<HoldBulkStatus>, RelationshipResourceAction.ReadById<HoldBulkStatus>
{
private HoldBulkMonitor holdBulkMonitor;
private HoldBulkService holdBulkService;
private FilePlanComponentsApiUtils apiUtils;
private PermissionService permissionService;
@Override
public CollectionWithPagingInfo<HoldBulkStatusEntry> readAll(String holdId, Parameters parameters)
public CollectionWithPagingInfo<HoldBulkStatus> readAll(String holdId, Parameters parameters)
{
// validate parameters
checkNotBlank("holdId", holdId);
@@ -81,9 +69,8 @@ public class HoldsBulkStatusesRelation
checkReadPermissions(holdRef);
List<HoldBulkStatusAndProcessDetails> statuses = holdBulkMonitor.getBulkStatusesWithProcessDetails(holdId);
List<HoldBulkStatusEntry> page = statuses.stream()
.map(HoldBulkUtils::toHoldBulkStatusEntry)
List<HoldBulkStatus> statuses = holdBulkMonitor.getBulkStatusesForHold(holdId);
List<HoldBulkStatus> page = statuses.stream()
.skip(parameters.getPaging().getSkipCount())
.limit(parameters.getPaging().getMaxItems())
.collect(Collectors.toCollection(LinkedList::new));
@@ -94,7 +81,7 @@ public class HoldsBulkStatusesRelation
}
@Override
public HoldBulkStatusEntry readById(String holdId, String bulkStatusId, Parameters parameters)
public HoldBulkStatus readById(String holdId, String bulkStatusId, Parameters parameters)
throws RelationshipResourceNotFoundException
{
checkNotBlank("holdId", holdId);
@@ -105,32 +92,7 @@ public class HoldsBulkStatusesRelation
checkReadPermissions(holdRef);
return Optional.ofNullable(holdBulkMonitor.getBulkStatusWithProcessDetails(holdId, bulkStatusId))
.map(HoldBulkUtils::toHoldBulkStatusEntry)
.orElseThrow(() -> new EntityNotFoundException(bulkStatusId));
}
@Operation("cancel")
@WebApiDescription(title = "Cancel a bulk operation",
successStatus = HttpServletResponse.SC_OK)
public void cancelBulkOperation(String holdId, String bulkStatusId, BulkCancellationEntry bulkCancellationEntry,
Parameters parameters,
WithResponse withResponse)
{
checkNotBlank("holdId", holdId);
checkNotBlank("bulkStatusId", bulkStatusId);
mandatory("parameters", parameters);
NodeRef holdRef = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD);
checkReadPermissions(holdRef);
if (holdBulkMonitor.getBulkStatus(bulkStatusId) == null)
{
throw new NotFoundException("Bulk status not found");
}
holdBulkService.cancelBulkOperation(holdRef, bulkStatusId, new BulkCancellationRequest(bulkCancellationEntry.reason()));
return Optional.ofNullable(holdBulkMonitor.getBulkStatus(bulkStatusId)).orElseThrow(() -> new EntityNotFoundException(bulkStatusId));
}
private void checkReadPermissions(NodeRef holdRef)
@@ -155,9 +117,4 @@ public class HoldsBulkStatusesRelation
{
this.permissionService = permissionService;
}
public void setHoldBulkService(HoldBulkService holdBulkService)
{
this.holdBulkService = holdBulkService;
}
}

View File

@@ -46,7 +46,7 @@ import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
import org.alfresco.rm.rest.api.model.HoldBulkOperation;
import org.alfresco.rm.rest.api.model.HoldBulkOperationEntry;
import org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkStatus;
import org.alfresco.rm.rest.api.model.HoldBulkStatus;
import org.alfresco.rm.rest.api.model.HoldDeletionReason;
import org.alfresco.rm.rest.api.model.HoldModel;
import org.alfresco.service.cmr.model.FileFolderService;

View File

@@ -34,16 +34,10 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinitionImpl;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService;
import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.model.AssocChild;
@@ -59,8 +53,6 @@ import org.alfresco.rm.rest.api.model.Record;
import org.alfresco.rm.rest.api.model.RecordCategory;
import org.alfresco.rm.rest.api.model.RecordCategoryChild;
import org.alfresco.rm.rest.api.model.RecordFolder;
import org.alfresco.rm.rest.api.model.RetentionPeriod;
import org.alfresco.rm.rest.api.model.RetentionSteps;
import org.alfresco.rm.rest.api.model.Transfer;
import org.alfresco.rm.rest.api.model.TransferChild;
import org.alfresco.rm.rest.api.model.TransferContainer;
@@ -69,8 +61,6 @@ import org.alfresco.rm.rest.api.model.UnfiledContainer;
import org.alfresco.rm.rest.api.model.UnfiledContainerChild;
import org.alfresco.rm.rest.api.model.UnfiledRecordFolder;
import org.alfresco.rm.rest.api.model.UnfiledRecordFolderChild;
import org.alfresco.rm.rest.api.model.RetentionSchedule;
import org.alfresco.rm.rest.api.model.RetentionScheduleActionDefinition;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
@@ -80,9 +70,6 @@ import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility class containing Alfresco and RM java services required by the API
@@ -94,9 +81,6 @@ import org.slf4j.LoggerFactory;
public class ApiNodesModelFactory
{
/** Logger */
private static final Logger LOGGER = LoggerFactory.getLogger(ApiNodesModelFactory.class);
// excluded namespaces (aspects, properties, assoc types)
public static final List<String> EXCLUDED_NS = Arrays.asList(NamespaceService.SYSTEM_MODEL_1_0_URI);
@@ -118,7 +102,6 @@ public class ApiNodesModelFactory
private PersonService personService;
private DispositionService dispositionService;
private ServiceRegistry serviceRegistry;
private RecordsManagementServiceRegistry services;
public NodeService getNodeService()
{
@@ -170,11 +153,6 @@ public class ApiNodesModelFactory
this.serviceRegistry = serviceRegistry;
}
public void setRecordsManagementServiceRegistry(RecordsManagementServiceRegistry services)
{
this.services = services;
}
/**
* Helper method that sets the basic information for most of the node types.
*
@@ -526,15 +504,15 @@ public class ApiNodesModelFactory
}
if(RecordsManagementModel.TYPE_RECORD_FOLDER.equals(info.getType()))
{
if (isRecordFolder(isMinimalInfo, propertyFilter, includeParam))
if((!isMinimalInfo && propertyFilter.isAllowed(RecordCategoryChild.PARAM_IS_RECORD_FOLDER)) || (isMinimalInfo && includeParam.contains(RecordCategoryChild.PARAM_IS_RECORD_FOLDER)))
{
recordCategoryChild.setIsRecordFolder(true);
}
if (isRecordCategory(isMinimalInfo, propertyFilter, includeParam))
if((!isMinimalInfo && propertyFilter.isAllowed(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY)) || (isMinimalInfo && includeParam.contains(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY)))
{
recordCategoryChild.setIsRecordCategory(false);
}
if (isRecordCategoryChildClosed(isMinimalInfo, propertyFilter, includeParam))
if((!isMinimalInfo && propertyFilter.isAllowed(RMNode.PARAM_IS_CLOSED)) || (isMinimalInfo && includeParam.contains(RMNode.PARAM_IS_CLOSED)))
{
recordCategoryChild.setIsClosed((Boolean) nodeService.getProperty(info.getNodeRef(), RecordsManagementModel.PROP_IS_CLOSED));
}
@@ -545,11 +523,11 @@ public class ApiNodesModelFactory
}
else
{
if (isRecordFolder(isMinimalInfo, propertyFilter, includeParam))
if((!isMinimalInfo && propertyFilter.isAllowed(RecordCategoryChild.PARAM_IS_RECORD_FOLDER)) || (isMinimalInfo && includeParam.contains(RecordCategoryChild.PARAM_IS_RECORD_FOLDER)))
{
recordCategoryChild.setIsRecordFolder(false);
}
if (isRecordCategory(isMinimalInfo, propertyFilter, includeParam))
if((!isMinimalInfo && propertyFilter.isAllowed(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY)) || (isMinimalInfo && includeParam.contains(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY)))
{
recordCategoryChild.setIsRecordCategory(true);
}
@@ -558,28 +536,13 @@ public class ApiNodesModelFactory
DispositionSchedule ds = dispositionService.getDispositionSchedule(info.getNodeRef());
recordCategoryChild.setHasRetentionSchedule(ds != null);
}
if (isRecordCategoryChildClosed(isMinimalInfo, propertyFilter, includeParam))
if((!isMinimalInfo && propertyFilter.isAllowed(RMNode.PARAM_IS_CLOSED)) || (isMinimalInfo && includeParam.contains(RMNode.PARAM_IS_CLOSED)))
{
recordCategoryChild.setIsClosed(null);
}
}
}
private boolean isRecordCategoryChildClosed(boolean isMinimalInfo, BeanPropertiesFilter propertyFilter, List<String> includeParam)
{
return (!isMinimalInfo && propertyFilter.isAllowed(RMNode.PARAM_IS_CLOSED)) || (isMinimalInfo && includeParam.contains(RMNode.PARAM_IS_CLOSED));
}
private boolean isRecordCategory(boolean isMinimalInfo, BeanPropertiesFilter propertyFilter, List<String> includeParam)
{
return (!isMinimalInfo && propertyFilter.isAllowed(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY)) || (isMinimalInfo && includeParam.contains(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY));
}
private boolean isRecordFolder(boolean isMinimalInfo, BeanPropertiesFilter propertyFilter, List<String> includeParam)
{
return (!isMinimalInfo && propertyFilter.isAllowed(RecordCategoryChild.PARAM_IS_RECORD_FOLDER)) || (isMinimalInfo && includeParam.contains(RecordCategoryChild.PARAM_IS_RECORD_FOLDER));
}
/**
* Utility method that maps record specific fields
@@ -602,8 +565,7 @@ public class ApiNodesModelFactory
{
Serializable val = info.getProperties().get(ContentModel.PROP_CONTENT);
if (val instanceof ContentData)
{
if ((val != null) && (val instanceof ContentData)) {
ContentData cd = (ContentData)val;
String mimeType = cd.getMimetype();
String mimeTypeName = serviceRegistry.getMimetypeService().getDisplaysByMimetype().get(mimeType);
@@ -929,238 +891,4 @@ public class ApiNodesModelFactory
mapAssociations(record, info, parameters.getInclude());
return record;
}
/**
* Helper method that sets the information for the retention schedule type.
* @param dispositionSchedule
* @return RetentionSchedule
*/
public RetentionSchedule mapRetentionScheduleData(DispositionSchedule dispositionSchedule)
{
RetentionSchedule retentionSchedule = new RetentionSchedule();
retentionSchedule.setId(dispositionSchedule.getNodeRef().getId());
if (dispositionSchedule.getNodeRef() != null) {
NodeRef parent = this.nodeService.getPrimaryParent(dispositionSchedule.getNodeRef()).getParentRef();
retentionSchedule.setParentId(parent.getId());
}
retentionSchedule.setInstructions(dispositionSchedule.getDispositionInstructions());
retentionSchedule.setAuthority(dispositionSchedule.getDispositionAuthority());
retentionSchedule.setIsRecordLevel(dispositionSchedule.isRecordLevelDisposition());
boolean unpublishedUpdates = dispositionSchedule.getDispositionActionDefinitions().stream()
.map(DispositionActionDefinition::getNodeRef)
.anyMatch(actionDefNodeRef -> nodeService.hasAspect(actionDefNodeRef, RecordsManagementModel.ASPECT_UNPUBLISHED_UPDATE));
retentionSchedule.setUnpublishedUpdates(unpublishedUpdates);
return retentionSchedule;
}
/**
* Helper method that sets the information for the retention schedule action definition type.
* @param dispositionActionDefinition
* @return RetentionScheduleActionDefinition
*/
public RetentionScheduleActionDefinition mapRetentionScheduleActionDefData(DispositionActionDefinition dispositionActionDefinition)
{
RetentionScheduleActionDefinition retentionScheduleActionDefinition = new RetentionScheduleActionDefinition();
// Mapping basic properties
mapRetentionActionProperties(dispositionActionDefinition, retentionScheduleActionDefinition);
// Mapping period and period amount
mapPeriodProperties(dispositionActionDefinition, retentionScheduleActionDefinition);
// Mapping events properties
mapEventsProperties(dispositionActionDefinition, retentionScheduleActionDefinition);
return retentionScheduleActionDefinition;
}
/**
* Helper method that sets core information for the retention schedule action definition type.
* @param dispositionActionDefinition
* @param retentionScheduleActionDefinition
*/
private void mapRetentionActionProperties(DispositionActionDefinition dispositionActionDefinition, RetentionScheduleActionDefinition retentionScheduleActionDefinition)
{
retentionScheduleActionDefinition.setId(dispositionActionDefinition.getId());
retentionScheduleActionDefinition.setName(dispositionActionDefinition.getName());
retentionScheduleActionDefinition.setDescription(dispositionActionDefinition.getDescription());
retentionScheduleActionDefinition.setEligibleOnFirstCompleteEvent(dispositionActionDefinition.eligibleOnFirstCompleteEvent());
if (nodeService.getProperty(dispositionActionDefinition.getNodeRef(), RecordsManagementModel.PROP_COMBINE_DISPOSITION_STEP_CONDITIONS) != null)
{
retentionScheduleActionDefinition.setCombineRetentionStepConditions((Boolean) nodeService.getProperty(dispositionActionDefinition.getNodeRef(), RecordsManagementModel.PROP_COMBINE_DISPOSITION_STEP_CONDITIONS));
}
retentionScheduleActionDefinition.setLocation(dispositionActionDefinition.getLocation());
if (dispositionActionDefinition.getGhostOnDestroy() != null)
{
retentionScheduleActionDefinition.setRetainRecordMetadataAfterDestruction(dispositionActionDefinition.getGhostOnDestroy().equals("ghost"));
}
retentionScheduleActionDefinition.setIndex(dispositionActionDefinition.getIndex());
}
/**
* Helper method that sets the period-related information for the retention schedule action definition type.
* @param dispositionActionDefinition
* @param retentionScheduleActionDefinition
*/
private void mapPeriodProperties(DispositionActionDefinition dispositionActionDefinition, RetentionScheduleActionDefinition retentionScheduleActionDefinition)
{
if (dispositionActionDefinition.getPeriodProperty() != null)
{
retentionScheduleActionDefinition.setPeriodProperty(dispositionActionDefinition.getPeriodProperty().toPrefixString(namespaceService));
}
String period = dispositionActionDefinition.getPeriod().toString();
if (!period.isEmpty())
{
// In rest api we are splitting `period` property into `period` and `periodAmount`.
// so we need to split the period into two properties.
// ex. period -> 'month|10' so the split properties would be like below
// period -> 'month'
// periodAmount -> 10
String[] periodArray = period.split("\\|");
if (periodArray.length > 0)
{
retentionScheduleActionDefinition.setPeriod(periodArray[0]);
}
if (periodArray.length > 1)
{
try
{
retentionScheduleActionDefinition.setPeriodAmount(Integer.parseInt(periodArray[1]));
}
catch (NumberFormatException numberFormatException)
{
LOGGER.error("Error parsing period amount: {}{}", numberFormatException.getMessage(), periodArray[1], numberFormatException);
throw numberFormatException;
}
}
}
}
/**
* Helper method that sets the events information for the retention schedule action definition type.
* @param dispositionActionDefinition
* @param retentionScheduleActionDefinition
*/
private void mapEventsProperties(DispositionActionDefinition dispositionActionDefinition, RetentionScheduleActionDefinition retentionScheduleActionDefinition)
{
List<RecordsManagementEvent> events = dispositionActionDefinition.getEvents();
if (events != null && !events.isEmpty())
{
List<String> eventNames = events.stream()
.map(RecordsManagementEvent::getName)
.collect(Collectors.toList());
retentionScheduleActionDefinition.setEvents(eventNames);
}
}
/**
* Helper method that sets the optional information for the retention schedule type.
* @param retentionSchedule
* @param schedule
* @param includeParam
*/
public void mapRetentionScheduleOptionalInfo(RetentionSchedule retentionSchedule, DispositionSchedule schedule, List<String> includeParam)
{
if (includeParam != null && !includeParam.isEmpty() && includeParam.contains("actions"))
{
List<RetentionScheduleActionDefinition> actions = schedule.getDispositionActionDefinitions().stream()
.map(this::mapRetentionScheduleActionDefData)
.collect(Collectors.toList());
retentionSchedule.setActions(actions);
}
}
/**
* this method is used for creation of retention schedule action definition params
* @param nodeInfo retention schedule action definition
* @return Map<QName, Serializable>
*/
public Map<QName, Serializable> createRetentionActionDefinitionParams(RetentionScheduleActionDefinition nodeInfo)
{
Map<QName, Serializable> actionDefinitionParams= new HashMap<>();
String retentionActionName = nodeInfo.getName();
if (nodeInfo.getName().equals(RetentionSteps.DESTROY_NODE.stepName) ||
nodeInfo.getName().equals(RetentionSteps.DESTROY_CONTENT.stepName))
{
retentionActionName = "destroy";
}
actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, retentionActionName);
actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, nodeInfo.getDescription());
StringBuilder retentionPeriod = new StringBuilder(nodeInfo.getPeriod()).append("|");
if (isPeriodAmountApplicable(nodeInfo.getPeriod()))
{
retentionPeriod.append(nodeInfo.getPeriodAmount());
}
actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, retentionPeriod.toString());
QName periodProperty = QName.createQName(nodeInfo.getPeriodProperty(), namespaceService);
actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD_PROPERTY, periodProperty);
actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_EVENT_COMBINATION,
nodeInfo.isEligibleOnFirstCompleteEvent());
boolean combineConditions = nodeInfo.getName().equals(RetentionSteps.ACCESSION.stepName) && nodeInfo.isCombineRetentionStepConditions();
actionDefinitionParams.put(RecordsManagementModel.PROP_COMBINE_DISPOSITION_STEP_CONDITIONS, combineConditions);
if(nodeInfo.getLocation() != null && nodeInfo.getName().equals(RetentionSteps.TRANSFER.stepName))
{
actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_LOCATION,
nodeInfo.getLocation());
}
List<String> inputEvents = nodeInfo.getEvents();
actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_EVENT, (Serializable) inputEvents);
if (RetentionSteps.DESTROY_CONTENT.stepName.equals(nodeInfo.getName()))
{
actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_GHOST_ON_DESTROY, "ghost");
}
else if (RetentionSteps.DESTROY_NODE.stepName.equals(nodeInfo.getName()))
{
actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_GHOST_ON_DESTROY, "delete");
}
return actionDefinitionParams;
}
/**
* this method is used retrieve retention schedule action details
* @param retentionScheduleNodeRef nodeRef
* @return List<DispositionActionDefinition>
*/
public List<DispositionActionDefinition> getRetentionActions(NodeRef retentionScheduleNodeRef)
{
List<ChildAssociationRef> assocs = nodeService.getChildAssocs(
retentionScheduleNodeRef,
RecordsManagementModel.ASSOC_DISPOSITION_ACTION_DEFINITIONS,
RegexQNamePattern.MATCH_ALL);
// we are getting disposition action definitions based on retention schedule child association.
// setting the index value for each action.
List<DispositionActionDefinition> actions;
actions = IntStream.range(0, assocs.size())
.mapToObj(index ->
{
ChildAssociationRef assoc = assocs.get(index);
return new DispositionActionDefinitionImpl(
services.getRecordsManagementEventService(),
services.getRecordsManagementActionService(),
nodeService,
assoc.getChildRef(),
index);
})
.collect(Collectors.toList());
return actions;
}
/**
* this method is used to check period amount applicable or not for particular period
* @param period period
* @return boolean
*/
private boolean isPeriodAmountApplicable(String period)
{
// periodAmount property only applicable for following periods
// day, week, month, quarter, year and duration
return period.equals(RetentionPeriod.DAY.periodName) || period.equals(RetentionPeriod.MONTH.periodName) || period.equals(RetentionPeriod.QUARTER.periodName)
|| period.equals(RetentionPeriod.WEEK.periodName) || period.equals(RetentionPeriod.XML_DURATION.periodName) || period.equals(RetentionPeriod.YEAR.periodName);
}
}
}

View File

@@ -92,7 +92,7 @@ public class SearchTypesFactory
boolean includeRecords = false;
boolean includeSubTypes = false;
if (q != null && q.getTree() != null)
if (q != null)
{
// filtering via "where" clause
MapBasedQueryWalker propertyWalker = new MapBasedQueryWalker(listFolderChildrenEqualsQueryProperties, null);
@@ -101,11 +101,11 @@ public class SearchTypesFactory
Boolean isUnfiledRecordFolder = propertyWalker.getProperty(UnfiledChild.PARAM_IS_UNFILED_RECORD_FOLDER,
WhereClauseParser.EQUALS, Boolean.class);
Boolean isRecord = propertyWalker.getProperty(UnfiledChild.PARAM_IS_RECORD, WhereClauseParser.EQUALS, Boolean.class);
if (checkIncludeUnfiledRecordFolders(isUnfiledRecordFolder, isRecord))
if ((isUnfiledRecordFolder != null && isUnfiledRecordFolder.booleanValue()) || (isRecord != null && !isRecord.booleanValue()))
{
includeUnfiledRecordFolders = true;
}
else if (checkIncludeRecords(isUnfiledRecordFolder, isRecord))
else if ((isUnfiledRecordFolder != null && !isUnfiledRecordFolder.booleanValue()) || (isRecord != null && isRecord.booleanValue()))
{
includeRecords = true;
}
@@ -199,11 +199,11 @@ public class SearchTypesFactory
WhereClauseParser.EQUALS, Boolean.class);
Boolean isRecordCategory = propertyWalker.getProperty(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY, WhereClauseParser.EQUALS, Boolean.class);
if (checkIncludeUnfiledRecordFolders(isRecordFolder, isRecordCategory))
if ((isRecordFolder != null && isRecordFolder.booleanValue()) || (isRecordCategory != null && !isRecordCategory.booleanValue()))
{
includeRecordFolders = true;
}
else if (checkIncludeRecords(isRecordFolder, isRecordCategory))
else if ((isRecordFolder != null && !isRecordFolder.booleanValue()) || (isRecordCategory != null && isRecordCategory.booleanValue()))
{
includeRecordCategories = true;
}
@@ -291,16 +291,4 @@ public class SearchTypesFactory
return new Pair<>(filterNodeTypeQName, filterIncludeSubTypes);
}
private static boolean checkIncludeRecords(Boolean isUnfiledRecordFolder, Boolean isRecord)
{
return (isUnfiledRecordFolder != null && !isUnfiledRecordFolder.booleanValue()) || (isRecord != null
&& isRecord.booleanValue());
}
private static boolean checkIncludeUnfiledRecordFolders(Boolean isUnfiledRecordFolder, Boolean isRecord)
{
return (isUnfiledRecordFolder != null && isUnfiledRecordFolder.booleanValue()) || (isRecord != null
&& !isRecord.booleanValue());
}
}

View File

@@ -1,29 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.model;
public record BulkCancellationEntry(String reason) {}

View File

@@ -24,24 +24,19 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.module.org_alfresco_module_rm.bulk.hold;
package org.alfresco.rm.rest.api.model;
import java.io.Serializable;
import java.util.Date;
/**
* An immutable POJO that contains the status of a hold bulk operation
*/
public record HoldBulkStatus(String bulkStatusId, Date startTime, Date endTime, long processedItems, long errorsCount,
long totalItems, String lastError, boolean isCancelled, String cancellationReason)
implements Serializable
long totalItems, String lastError) implements Serializable
{
public enum Status
{
PENDING("PENDING"),
IN_PROGRESS("IN PROGRESS"),
DONE("DONE"),
CANCELLED("CANCELLED");
DONE("DONE");
private final String value;
@@ -58,11 +53,7 @@ public record HoldBulkStatus(String bulkStatusId, Date startTime, Date endTime,
public String getStatus()
{
if (isCancelled)
{
return Status.CANCELLED.getValue();
}
else if (startTime == null && endTime == null)
if (startTime == null && endTime == null)
{
return Status.PENDING.getValue();
}

View File

@@ -1,33 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.model;
import java.util.Date;
public record HoldBulkStatusEntry(String bulkStatusId, Date startTime, Date endTime, long processedItems, long errorsCount,
long totalItems, String lastError, String status, String cancellationReason, HoldBulkOperation holdBulkOperation) {
}

View File

@@ -1,56 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.model;
/**
* Retention event values
*/
public enum RetentionEvents
{
CASE_CLOSED("case_closed"),
ABOLISHED("abolished"),
RE_DESIGNATED("re_designated"),
NO_LONGER_NEEDED("no_longer_needed"),
SUPERSEDED("superseded"),
VERSIONED("versioned"),
STUDY_COMPLETE("study_complete"),
TRAINING_COMPLETE("training_complete"),
TRANSFERRED_INACTIVE_STORAGE("related_record_trasfered_inactive_storage"),
OBSOLETE("obsolete"),
ALLOWANCES_GRANTED_TERMINATED("all_allowances_granted_are_terminated"),
WGI_ACTION_COMPLETE("WGI_action_complete"),
SEPARATION("separation"),
CASE_COMPLETE("case_complete"),
DECLASSIFICATION_REVIEW("declassification_review");
public final String eventName;
RetentionEvents(String eventName)
{
this.eventName = eventName;
}
}

View File

@@ -1,55 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.model;
/**
* Retention period values
*/
public enum RetentionPeriod
{
DAY("day"),
END_OF_FINANCIAL_MONTH("fmend"),
END_OF_FINANCIAL_QUARTER("fqend"),
END_OF_FINANCIAL_YEAR("fyend"),
IMMEDIATELY("immediately"),
END_OF_MONTH("monthend"),
END_OF_QUARTER("quarterend"),
END_OF_YEAR("yearend"),
MONTH("month"),
NONE("none"),
QUARTER("quarter"),
WEEK("week"),
XML_DURATION("duration"),
YEAR("year");
public final String periodName;
RetentionPeriod(String periodName)
{
this.periodName = periodName;
}
}

View File

@@ -1,56 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.model;
import lombok.Data;
import java.util.List;
/**
* retention schedule
*/
@Data
public class RetentionSchedule
{
private String id ;
private String parentId;
private String authority;
private String instructions;
private boolean isRecordLevel;
private boolean isUnpublishedUpdates;
private List<RetentionScheduleActionDefinition> actions;
public boolean getIsRecordLevel()
{
return isRecordLevel;
}
public void setIsRecordLevel(boolean recordLevel)
{
isRecordLevel = recordLevel;
}
}

View File

@@ -1,51 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.model;
import java.util.List;
import lombok.Data;
/**
* retention schedule action definition
*/
@Data
public class RetentionScheduleActionDefinition
{
private String id;
private String name;
private String description;
private int periodAmount;
private String period;
private String periodProperty;
private boolean combineRetentionStepConditions;
private List<String> events;
private boolean eligibleOnFirstCompleteEvent;
private boolean retainRecordMetadataAfterDestruction;
private String location;
private int index;
}

View File

@@ -1,47 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.model;
/**
* Retention steps values
*/
public enum RetentionSteps
{
RETAIN("retain"),
CUTOFF("cutoff"),
TRANSFER("transfer"),
ACCESSION("accession"),
DESTROY_CONTENT("destroyContent"),
DESTROY_NODE("destroyNode");
public final String stepName;
RetentionSteps(String stepName)
{
this.stepName = stepName;
}
}

View File

@@ -1,284 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.retentionschedule;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionScheduleImpl;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.core.exceptions.ConstraintViolatedException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.core.exceptions.UnprocessableContentException;
import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
import org.alfresco.rm.rest.api.model.RetentionEvents;
import org.alfresco.rm.rest.api.model.RetentionPeriod;
import org.alfresco.rm.rest.api.model.RetentionScheduleActionDefinition;
import org.alfresco.rm.rest.api.model.RetentionSteps;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
import static org.alfresco.util.ParameterCheck.mandatory;
/**
* Retention schedule action relation is used to perform the retention schedule step operations.
*/
@RelationshipResource(name = "retention-steps", entityResource = RetentionScheduleEntityResource.class, title = "Retention Schedule Action")
public class RetentionScheduleActionRelation implements RelationshipResourceAction.Read<RetentionScheduleActionDefinition>,
RelationshipResourceAction.Create<RetentionScheduleActionDefinition>
{
private FilePlanComponentsApiUtils apiUtils;
protected NodeService nodeService;
private RecordsManagementServiceRegistry service;
private ApiNodesModelFactory nodesModelFactory;
public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
{
this.apiUtils = apiUtils;
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
{
this.nodesModelFactory = nodesModelFactory;
}
public void setRecordsManagementServiceRegistry(RecordsManagementServiceRegistry service)
{
this.service = service;
}
@Override
@WebApiDescription(title="Create a retention schedule step for the particular retention schedule using the 'retentionScheduleId'")
public List<RetentionScheduleActionDefinition> create(String retentionScheduleId, List<RetentionScheduleActionDefinition> nodeInfos, Parameters parameters)
{
checkNotBlank("retentionScheduleId", retentionScheduleId);
mandatory("entity", nodeInfos);
mandatory("parameters", parameters);
NodeRef retentionScheduleNodeRef = apiUtils.lookupAndValidateNodeType(retentionScheduleId, RecordsManagementModel.TYPE_DISPOSITION_SCHEDULE);
// validation for the order of the step
retentionScheduleStepValidation(retentionScheduleNodeRef, nodeInfos.get(0));
// request property validation
retentionScheduleRequestValidation(nodeInfos.get(0));
// create the parameters for the action definition
Map<QName, Serializable> actionDefinitionParams = nodesModelFactory.createRetentionActionDefinitionParams(nodeInfos.get(0));
// create the child association from the schedule to the action definition
NodeRef actionNodeRef = this.nodeService.createNode(retentionScheduleNodeRef,
RecordsManagementModel.ASSOC_DISPOSITION_ACTION_DEFINITIONS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,
QName.createValidLocalName(nodeInfos.get(0).getName())),
RecordsManagementModel.TYPE_DISPOSITION_ACTION_DEFINITION, actionDefinitionParams).getChildRef();
DispositionSchedule dispositionSchedule = new DispositionScheduleImpl(service, nodeService, retentionScheduleNodeRef);
DispositionActionDefinition dispositionActionDefinition = dispositionSchedule.getDispositionActionDefinition(actionNodeRef.getId());
List<RetentionScheduleActionDefinition> responseActions = new ArrayList<>();
if (dispositionActionDefinition != null)
{
responseActions.add(nodesModelFactory.mapRetentionScheduleActionDefData(dispositionActionDefinition));
}
return responseActions;
}
@Override
@WebApiDescription(title = "Return a paged list of retention schedule action definition based on the 'retentionScheduleId'")
public CollectionWithPagingInfo<RetentionScheduleActionDefinition> readAll(String retentionScheduleId, Parameters parameters)
{
checkNotBlank("retentionScheduleId", retentionScheduleId);
mandatory("parameters", parameters);
NodeRef retentionScheduleNodeRef = apiUtils.lookupAndValidateNodeType(retentionScheduleId, RecordsManagementModel.TYPE_DISPOSITION_SCHEDULE);
List<DispositionActionDefinition> actions = nodesModelFactory.getRetentionActions(retentionScheduleNodeRef);
List<RetentionScheduleActionDefinition> actionDefinitionList = actions.stream()
.map(nodesModelFactory::mapRetentionScheduleActionDefData)
.collect(Collectors.toList());
return CollectionWithPagingInfo.asPaged(parameters.getPaging(), actionDefinitionList, false,
actionDefinitionList.size());
}
/**
* this method is used to validate the order of the retention schedule step
* @param retentionScheduleNodeRef nodeRef
* @param retentionScheduleActionDefinition retention schedule action definition
*/
private void retentionScheduleStepValidation(NodeRef retentionScheduleNodeRef, RetentionScheduleActionDefinition retentionScheduleActionDefinition)
{
if (checkStepNameIsEmpty(retentionScheduleActionDefinition.getName()))
{
throw new IllegalArgumentException("'name' parameter is mandatory when creating a disposition action definition");
}
List<DispositionActionDefinition> actions = nodesModelFactory.getRetentionActions(retentionScheduleNodeRef);
Set<String> completedActions = new HashSet<>();
if (!actions.isEmpty())
{
completedActions = actions.stream()
.map(DispositionActionDefinition::getName)
.collect(Collectors.toSet());
}
if (completedActions.contains("destroy"))
{
throw new ConstraintViolatedException("Invalid Step - destroy action is already added. No other action is allowed after Destroy.");
}
if (checkStepAlreadyExists(completedActions, retentionScheduleActionDefinition.getName()))
{
throw new ConstraintViolatedException("Invalid Step - This step already exists. You cant create it again. Only transfer action is allowed multiple times.");
}
if (firstStepValidation(actions, retentionScheduleActionDefinition.getName()))
{
throw new UnprocessableContentException("Invalid Step - cutoff or retain should be the first step");
}
if (isCutOffStepAllowed(completedActions, retentionScheduleActionDefinition.getName()))
{
throw new ConstraintViolatedException("Invalid Step - Can't use cutoff after transfer or accession");
}
}
private boolean checkStepNameIsEmpty(String name)
{
return name == null || name.isEmpty();
}
/**
* this method is used to validate the request of the retention schedule
* @param retentionScheduleActionDefinition retention schedule action definition
*/
private void retentionScheduleRequestValidation(RetentionScheduleActionDefinition retentionScheduleActionDefinition)
{
// step name validation
if (invalidStepNameCheck(retentionScheduleActionDefinition.getName()))
{
throw new InvalidArgumentException("name value is invalid : " +retentionScheduleActionDefinition.getName());
}
validatePeriodAndPeriodProperty(retentionScheduleActionDefinition);
// event name validation
if (invalidEventNameCheck(retentionScheduleActionDefinition.getEvents()))
{
throw new InvalidArgumentException("event value is invalid: " + retentionScheduleActionDefinition.getEvents());
}
if (validateCombineRetentionStepConditionsForNonAccessionStep(retentionScheduleActionDefinition))
{
throw new IllegalArgumentException("combineRetentionStepConditions property is only valid for accession step. Not valid for :" + retentionScheduleActionDefinition.getName());
}
if (validateLocationForNonTransferStep(retentionScheduleActionDefinition))
{
throw new IllegalArgumentException("location property is only valid for transfer step. Not valid for :" + retentionScheduleActionDefinition.getName());
}
}
private void validatePeriodAndPeriodProperty(RetentionScheduleActionDefinition retentionScheduleActionDefinition)
{
// period value validation
if (invalidPeriodCheck(retentionScheduleActionDefinition.getPeriod()))
{
throw new InvalidArgumentException("period value is invalid : " +retentionScheduleActionDefinition.getPeriod());
}
// periodProperty validation
List<String> validPeriodProperties = Arrays.asList("cm:created", "rma:cutOffDate", "rma:dispositionAsOf");
if (validPeriodProperties.stream().noneMatch(retentionScheduleActionDefinition.getPeriodProperty()::equals))
{
throw new InvalidArgumentException("periodProperty value is invalid: " + retentionScheduleActionDefinition.getPeriodProperty());
}
}
private boolean validateCombineRetentionStepConditionsForNonAccessionStep(RetentionScheduleActionDefinition retentionScheduleActionDefinition)
{
return !retentionScheduleActionDefinition.getName().equals(RetentionSteps.ACCESSION.stepName)
&& retentionScheduleActionDefinition.isCombineRetentionStepConditions();
}
private boolean validateLocationForNonTransferStep(RetentionScheduleActionDefinition retentionScheduleActionDefinition)
{
return retentionScheduleActionDefinition.getLocation() != null
&& !retentionScheduleActionDefinition.getName().equals(RetentionSteps.TRANSFER.stepName)
&& !retentionScheduleActionDefinition.getLocation().isEmpty();
}
private boolean checkStepAlreadyExists(Set<String> completedActions, String stepName)
{
return completedActions.contains(stepName) && !stepName.equals(RetentionSteps.TRANSFER.stepName);
}
private boolean firstStepValidation(List<DispositionActionDefinition> actions, String stepName)
{
return actions.isEmpty()
&& !stepName.equals(RetentionSteps.CUTOFF.stepName) && (!stepName.equals(RetentionSteps.RETAIN.stepName));
}
private boolean isCutOffStepAllowed(Set<String> completedActions, String stepName)
{
return (completedActions.contains(RetentionSteps.TRANSFER.stepName) || completedActions.contains(RetentionSteps.ACCESSION.stepName))
&& stepName.equals(RetentionSteps.CUTOFF.stepName);
}
private boolean invalidStepNameCheck(String stepName)
{
return stepName != null && Arrays.stream(RetentionSteps.values())
.noneMatch(retentionStep -> retentionStep.stepName.equals(stepName));
}
private boolean invalidPeriodCheck(String period)
{
return period != null && Arrays.stream(RetentionPeriod.values())
.noneMatch(retentionPeriod -> retentionPeriod.periodName.equals(period));
}
private boolean invalidEventNameCheck(List<String> events)
{
return !events.isEmpty() && events.stream()
.anyMatch(event -> Arrays.stream(RetentionEvents.values())
.noneMatch(retentionEvent -> retentionEvent.eventName.equals(event)));
}
}

View File

@@ -1,38 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.retentionschedule;
import org.alfresco.rest.framework.resource.EntityResource;
/**
* Retention schedule entity resource
*/
@EntityResource(name="retention-schedules", title = "Retention Schedule")
public class RetentionScheduleEntityResource
{
}

View File

@@ -1,144 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.retentionschedule;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.core.exceptions.UnprocessableContentException;
import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
import org.alfresco.rm.rest.api.model.RetentionSchedule;
import org.alfresco.rm.rest.api.recordcategories.RecordCategoriesEntityResource;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.PROP_DISPOSITION_AUTHORITY;
import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.PROP_DISPOSITION_INSTRUCTIONS;
import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.PROP_RECORD_LEVEL_DISPOSITION;
import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.TYPE_RECORD_CATEGORY;
import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
import static org.alfresco.util.ParameterCheck.mandatory;
/**
* Retention schedule relation is used perform retention schedule operation for a record category.
*/
@RelationshipResource(name = "retention-schedules", entityResource = RecordCategoriesEntityResource.class, title = "Retention Schedule")
public class RetentionScheduleRelation implements RelationshipResourceAction.Read<RetentionSchedule>,
RelationshipResourceAction.Create<RetentionSchedule>
{
private FilePlanComponentsApiUtils apiUtils;
private ApiNodesModelFactory nodesModelFactory;
private DispositionService dispositionService;
protected NodeService nodeService;
public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
{
this.apiUtils = apiUtils;
}
public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
{
this.nodesModelFactory = nodesModelFactory;
}
public void setDispositionService(DispositionService dispositionService)
{
this.dispositionService = dispositionService;
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
@Override
@WebApiDescription(title="Create a retention schedule for the particular record category using the 'recordCategoryId'")
public List<RetentionSchedule> create(String recordCategoryId, List<RetentionSchedule> nodeInfos, Parameters parameters)
{
checkNotBlank("recordCategoryId", recordCategoryId);
mandatory("entity", nodeInfos);
mandatory("parameters", parameters);
NodeRef parentNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, recordCategoryId);
if (checkCategoryHasAssocFolder(parentNodeRef) && nodeInfos.get(0).getIsRecordLevel())
{
throw new UnprocessableContentException("Record level retention schedule cannot be created for a record category having folder associated.");
}
List<RetentionSchedule> result = new ArrayList<>();
// Create the disposition schedule
Map<QName, Serializable> dsProps = new HashMap<>();
dsProps.put(PROP_DISPOSITION_AUTHORITY, nodeInfos.get(0).getAuthority());
dsProps.put(PROP_DISPOSITION_INSTRUCTIONS, nodeInfos.get(0).getInstructions());
dsProps.put(PROP_RECORD_LEVEL_DISPOSITION, nodeInfos.get(0).getIsRecordLevel());
DispositionSchedule dispositionSchedule = dispositionService.createDispositionSchedule(parentNodeRef, dsProps);
RetentionSchedule retentionSchedule = nodesModelFactory.mapRetentionScheduleData(dispositionSchedule);
result.add(retentionSchedule);
return result;
}
private boolean checkCategoryHasAssocFolder(NodeRef nodeRef)
{
List<ChildAssociationRef> assocs = nodeService.getChildAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
return assocs.stream()
.map(assoc -> nodeService.getType(assoc.getChildRef()))
.anyMatch(nodeType -> nodeType.equals(RecordsManagementModel.TYPE_RECORD_FOLDER));
}
@Override
@WebApiDescription(title = "Return a paged list of retention schedule based on the 'recordCategoryId'")
public CollectionWithPagingInfo<RetentionSchedule> readAll(String recordCategoryId, Parameters parameters)
{
checkNotBlank("recordCategoryId", recordCategoryId);
mandatory("parameters", parameters);
NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(recordCategoryId, TYPE_RECORD_CATEGORY);
DispositionSchedule schedule = dispositionService.getDispositionSchedule(parentNodeRef);
RetentionSchedule retentionSchedule = nodesModelFactory.mapRetentionScheduleData(schedule);
List<RetentionSchedule> retentionScheduleList = new ArrayList<>();
nodesModelFactory.mapRetentionScheduleOptionalInfo(retentionSchedule, schedule, parameters.getInclude());
retentionScheduleList.add(retentionSchedule);
return CollectionWithPagingInfo.asPaged(parameters.getPaging(), retentionScheduleList, false,
retentionScheduleList.size());
}
}

View File

@@ -1,34 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
/**
* Package info that defines the Information Governance Retention Schedule REST API
*/
@WebApi(name="gs", scope=Api.SCOPE.PUBLIC, version=1)
package org.alfresco.rm.rest.api.retentionschedule;
import org.alfresco.rest.framework.Api;
import org.alfresco.rest.framework.WebApi;

View File

@@ -1,306 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.module.org_alfresco_module_rm.test.integration.bulk.hold;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.awaitility.Awaitility.await;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.bulk.BulkCancellationRequest;
import org.alfresco.module.org_alfresco_module_rm.bulk.BulkOperation;
import org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkMonitor;
import org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkServiceImpl;
import org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkStatus;
import org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkStatus.Status;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
import org.alfresco.rest.api.search.model.Query;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.mockito.Mockito;
import org.mockito.stubbing.Answer;
import org.springframework.extensions.webscripts.GUID;
/**
* Hold bulk service integration test.
*/
@SuppressWarnings({ "PMD.TestClassWithoutTestCases", "PMD.JUnit4TestShouldUseTestAnnotation" })
public class HoldBulkServiceTest extends BaseRMTestCase
{
private static final int RECORD_COUNT = 10;
private final SearchService searchServiceMock = mock(SearchService.class);
private final ResultSet resultSet = mock(ResultSet.class);
private HoldBulkServiceImpl holdBulkService;
private HoldBulkMonitor holdBulkMonitor;
@Override
protected void initServices()
{
super.initServices();
holdBulkMonitor = (HoldBulkMonitor) applicationContext.getBean("holdBulkMonitor");
holdBulkService = (HoldBulkServiceImpl) applicationContext.getBean("holdBulkService");
holdBulkService.setSearchService(searchServiceMock);
Mockito.when(searchServiceMock.query(any(SearchParameters.class))).thenReturn(resultSet);
}
public void testCancelBulkOperation()
{
doBehaviourDrivenTest(new BehaviourDrivenTest()
{
private NodeRef hold;
private HoldBulkStatus holdBulkStatus;
private final ResultSet resultSet = mock(ResultSet.class);
public void given()
{
Mockito.when(resultSet.getNumberFound()).thenReturn(4L);
Mockito.when(resultSet.hasMore()).thenReturn(false).thenReturn(true).thenReturn(false);
Mockito.when(resultSet.getNodeRefs())
.thenAnswer((Answer<List<NodeRef>>) invocationOnMock -> {
await().pollDelay(1, SECONDS).until(() -> true);
return List.of(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, GUID.generate()),
new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, GUID.generate()));
});
// create a hold
hold = holdService.createHold(filePlan, GUID.generate(), GUID.generate(), GUID.generate());
}
public void when()
{
BulkOperation bulkOperation = new BulkOperation(new Query("afts", "*", ""), "ADD");
// execute the bulk operation
holdBulkStatus = holdBulkService.execute(hold, bulkOperation);
// cancel the bulk operation
holdBulkMonitor.cancelBulkOperation(holdBulkStatus.bulkStatusId(),
new BulkCancellationRequest("No reason"));
await().atMost(10, SECONDS)
.until(() -> Objects.equals(
holdBulkMonitor.getBulkStatus(holdBulkStatus.bulkStatusId()).getStatus(),
Status.CANCELLED.getValue()));
}
public void then()
{
holdBulkStatus = holdBulkMonitor.getBulkStatus(holdBulkStatus.bulkStatusId());
assertNotNull(holdBulkStatus.startTime());
assertNotNull(holdBulkStatus.endTime());
assertEquals(holdBulkMonitor.getBulkStatus(holdBulkStatus.bulkStatusId()).getStatus(),
HoldBulkStatus.Status.CANCELLED.getValue());
assertEquals(holdBulkMonitor.getBulkStatus(holdBulkStatus.bulkStatusId()).cancellationReason(),
"No reason");
}
});
}
public void testAddRecordsToHoldViaBulk()
{
doBehaviourDrivenTest(new BehaviourDrivenTest()
{
private NodeRef hold;
private NodeRef recordFolder;
private HoldBulkStatus holdBulkStatus;
private final List<NodeRef> records = new ArrayList<>(RECORD_COUNT);
public void given()
{
Mockito.when(resultSet.getNumberFound()).thenReturn(Long.valueOf(RECORD_COUNT));
Mockito.when(resultSet.hasMore()).thenReturn(false).thenReturn(false);
// create a hold
hold = holdService.createHold(filePlan, GUID.generate(), GUID.generate(), GUID.generate());
// create a record folder that contains records
NodeRef recordCategory = filePlanService.createRecordCategory(filePlan, GUID.generate());
recordFolder = recordFolderService.createRecordFolder(recordCategory, GUID.generate());
for (int i = 0; i < RECORD_COUNT; i++)
{
records.add(
recordService.createRecordFromContent(recordFolder, GUID.generate(), ContentModel.TYPE_CONTENT,
null, null));
}
Mockito.when(resultSet.getNodeRefs()).thenReturn(records).thenReturn(records)
.thenReturn(Collections.emptyList());
// assert current states
assertFalse(freezeService.isFrozen(recordFolder));
assertFalse(freezeService.hasFrozenChildren(recordFolder));
for (NodeRef record : records)
{
assertFalse(freezeService.isFrozen(record));
}
// additional check for child held caching
assertTrue(nodeService.hasAspect(recordFolder, ASPECT_HELD_CHILDREN));
assertEquals(0, nodeService.getProperty(recordFolder, PROP_HELD_CHILDREN_COUNT));
}
public void when()
{
BulkOperation bulkOperation = new BulkOperation(new Query("afts", "*", ""), "ADD");
// execute the bulk operation
holdBulkStatus = holdBulkService.execute(hold, bulkOperation);
await().atMost(10, SECONDS)
.until(() -> Objects.equals(
holdBulkMonitor.getBulkStatus(holdBulkStatus.bulkStatusId()).getStatus(),
Status.DONE.getValue()));
}
public void then()
{
holdBulkStatus = holdBulkMonitor.getBulkStatus(holdBulkStatus.bulkStatusId());
assertNotNull(holdBulkStatus.startTime());
assertNotNull(holdBulkStatus.endTime());
assertEquals(RECORD_COUNT, holdBulkStatus.totalItems());
assertEquals(RECORD_COUNT, holdBulkStatus.processedItems());
assertEquals(0, holdBulkStatus.errorsCount());
assertEquals(holdBulkMonitor.getBulkStatus(holdBulkStatus.bulkStatusId()).getStatus(),
HoldBulkStatus.Status.DONE.getValue());
// record is held
for (NodeRef record : records)
{
assertTrue(freezeService.isFrozen(record));
}
// record folder has frozen children
assertFalse(freezeService.isFrozen(recordFolder));
assertTrue(freezeService.hasFrozenChildren(recordFolder));
// record folder is not held
assertFalse(holdService.getHeld(hold).contains(recordFolder));
assertFalse(holdService.heldBy(recordFolder, true).contains(hold));
for (NodeRef record : records)
{
// hold contains record
assertTrue(holdService.getHeld(hold).contains(record));
assertTrue(holdService.heldBy(record, true).contains(hold));
}
// additional check for child held caching
assertTrue(nodeService.hasAspect(recordFolder, ASPECT_HELD_CHILDREN));
assertEquals(RECORD_COUNT, nodeService.getProperty(recordFolder, PROP_HELD_CHILDREN_COUNT));
}
});
}
public void testAddRecordFolderToHoldViaBulk()
{
doBehaviourDrivenTest(new BehaviourDrivenTest()
{
private NodeRef hold;
private NodeRef recordFolder;
private final List<NodeRef> records = new ArrayList<>(RECORD_COUNT);
private HoldBulkStatus holdBulkStatus;
public void given()
{
Mockito.when(resultSet.getNumberFound()).thenReturn(1L);
Mockito.when(resultSet.hasMore()).thenReturn(false).thenReturn(false);
// create a hold
hold = holdService.createHold(filePlan, GUID.generate(), GUID.generate(), GUID.generate());
// create a record folder that contains records
NodeRef recordCategory = filePlanService.createRecordCategory(filePlan, GUID.generate());
recordFolder = recordFolderService.createRecordFolder(recordCategory, GUID.generate());
for (int i = 0; i < RECORD_COUNT; i++)
{
records.add(
recordService.createRecordFromContent(recordFolder, GUID.generate(), ContentModel.TYPE_CONTENT,
null, null));
}
Mockito.when(resultSet.getNodeRefs()).thenReturn(Collections.singletonList(recordFolder))
.thenReturn(Collections.singletonList(recordFolder)).thenReturn(Collections.emptyList());
// assert current states
assertFalse(freezeService.isFrozen(recordFolder));
assertFalse(freezeService.hasFrozenChildren(recordFolder));
for (NodeRef record : records)
{
assertFalse(freezeService.isFrozen(record));
}
// additional check for child held caching
assertTrue(nodeService.hasAspect(recordFolder, ASPECT_HELD_CHILDREN));
assertEquals(0, nodeService.getProperty(recordFolder, PROP_HELD_CHILDREN_COUNT));
}
public void when()
{
BulkOperation bulkOperation = new BulkOperation(new Query("afts", "*", ""), "ADD");
// execute the bulk operation
holdBulkStatus = holdBulkService.execute(hold, bulkOperation);
await().atMost(10, SECONDS)
.until(() -> Objects.equals(
holdBulkMonitor.getBulkStatus(holdBulkStatus.bulkStatusId()).getStatus(),
Status.DONE.getValue()));
}
public void then()
{
holdBulkStatus = holdBulkMonitor.getBulkStatus(holdBulkStatus.bulkStatusId());
assertNotNull(holdBulkStatus.startTime());
assertNotNull(holdBulkStatus.endTime());
assertEquals(1, holdBulkStatus.totalItems());
assertEquals(1, holdBulkStatus.processedItems());
assertEquals(0, holdBulkStatus.errorsCount());
assertEquals(holdBulkMonitor.getBulkStatus(holdBulkStatus.bulkStatusId()).getStatus(),
HoldBulkStatus.Status.DONE.getValue());
for (NodeRef record : records)
{
// record is held
assertTrue(freezeService.isFrozen(record));
assertFalse(holdService.getHeld(hold).contains(record));
assertTrue(holdService.heldBy(record, true).contains(hold));
}
// record folder has frozen children
assertTrue(freezeService.isFrozen(recordFolder));
assertTrue(freezeService.hasFrozenChildren(recordFolder));
// hold contains record folder
assertTrue(holdService.getHeld(hold).contains(recordFolder));
assertTrue(holdService.heldBy(recordFolder, true).contains(hold));
// additional check for child held caching
assertTrue(nodeService.hasAspect(recordFolder, ASPECT_HELD_CHILDREN));
assertEquals(RECORD_COUNT, nodeService.getProperty(recordFolder, PROP_HELD_CHILDREN_COUNT));
}
});
}
}

View File

@@ -180,7 +180,6 @@ public class CreateRecordTest extends BaseRMTestCase
Set<Capability> capabilities = new HashSet<>(2);
capabilities.add(capabilityService.getCapability("ViewRecords"));
capabilities.add(capabilityService.getCapability("CreateRecords"));
capabilities.add(capabilityService.getCapability("CreateModifyDestroyFileplanMetadata"));
filePlanRoleService.createRole(filePlan, roleName, roleName, capabilities);
@@ -190,7 +189,6 @@ public class CreateRecordTest extends BaseRMTestCase
//give read and file permission to user on unfiled records container
filePlanPermissionService.setPermission(unfiledContainer , user, RMPermissionModel.FILING);
filePlanPermissionService.setPermission(unfiledContainer , user, RMPermissionModel.CREATE_MODIFY_DESTROY_FILEPLAN_METADATA);
}
public void when()

View File

@@ -51,8 +51,6 @@ import org.alfresco.module.org_alfresco_module_rm.job.publish.PublishExecutorReg
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
import org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils;
import org.alfresco.rest.framework.core.exceptions.ConstraintViolatedException;
import org.junit.Assert;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
@@ -439,12 +437,19 @@ public class DispositionServiceImplTest extends BaseRMTestCase
// Check the disposition schedule
checkDispositionSchedule(ds, "testCreateDispositionSchedule", "testCreateDispositionSchedule", false);
}
});
// Failure: create disposition schedule on container with existing disposition schedule
Assert.assertThrows(ConstraintViolatedException.class,
() -> {
utils.createBasicDispositionSchedule(rmContainer);
});
// Failure: create disposition schedule on container with existing disposition schedule
doTestInTransaction(new FailureTest
(
"Can not create a disposition schedule on a container with an existing disposition schedule"
)
{
@Override
public void run()
{
utils.createBasicDispositionSchedule(rmContainer);
}
});
}
@@ -487,12 +492,19 @@ public class DispositionServiceImplTest extends BaseRMTestCase
// Check the disposition schedule
checkDispositionSchedule(testA, "testA", "testA", false);
checkDispositionSchedule(testB, "testB", "testB", false);
}
});
// Failure: create disposition schedule on container with existing disposition schedule
Assert.assertThrows(ConstraintViolatedException.class,
() -> {
utils.createBasicDispositionSchedule(rmContainer);
});
// Failure: create disposition schedule on container with existing disposition schedule
doTestInTransaction(new FailureTest
(
"Can not create a disposition schedule on container with an existing disposition schedule"
)
{
@Override
public void run()
{
utils.createBasicDispositionSchedule(mhContainer11);
}
});

View File

@@ -4,7 +4,7 @@
# Version label
version.major=23
version.minor=4
version.minor=3
version.revision=0
version.label=

View File

@@ -27,21 +27,18 @@
package org.alfresco.module.org_alfresco_module_rm.bulk;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.alfresco.module.org_alfresco_module_rm.bulk.hold.DefaultHoldBulkMonitor;
import org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkProcessDetails;
import org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkStatus;
import org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkStatusAndProcessDetails;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.rm.rest.api.model.HoldBulkStatus;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.Pair;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
@@ -55,7 +52,7 @@ public class DefaultHoldBulkMonitorUnitTest
private SimpleCache<String, HoldBulkStatus> holdProgressCache;
@Mock
private SimpleCache<Pair<String, String>, HoldBulkProcessDetails> holdProcessRegistry;
private SimpleCache<String, List<HoldBulkProcessDetails>> holdProcessRegistry;
private DefaultHoldBulkMonitor holdBulkMonitor;
@@ -71,7 +68,7 @@ public class DefaultHoldBulkMonitorUnitTest
@Test
public void testUpdateBulkStatus()
{
HoldBulkStatus status = new HoldBulkStatus("bulkStatusId", null, null, 0L, 0L, 0L, null, false, null);
HoldBulkStatus status = new HoldBulkStatus("bulkStatusId", null, null, 0L, 0L, 0L, null);
holdBulkMonitor.updateBulkStatus(status);
@@ -83,82 +80,40 @@ public class DefaultHoldBulkMonitorUnitTest
{
NodeRef holdRef = new NodeRef("workspace://SpacesStore/holdId");
String processId = "processId";
when(holdProcessRegistry.get(new Pair<>(holdRef.getId(), processId))).thenReturn(null);
when(holdProcessRegistry.get(holdRef.getId())).thenReturn(null);
holdBulkMonitor.registerProcess(holdRef, processId, null);
holdBulkMonitor.registerProcess(holdRef, processId);
Mockito.verify(holdProcessRegistry)
.put(new Pair<>(holdRef.getId(), processId), new HoldBulkProcessDetails(processId, null, null));
.put(holdRef.getId(), Arrays.asList(new HoldBulkProcessDetails(processId, null)));
}
@Test
public void testGetBulkStatusesWithProcessDetailsReturnsEmptyListWhenNoProcessesWithProcessDetails()
public void testGetBulkStatusesForHoldReturnsEmptyListWhenNoProcesses()
{
when(holdProcessRegistry.getKeys()).thenReturn(Collections.emptyList());
assertEquals(Collections.emptyList(), holdBulkMonitor.getBulkStatusesWithProcessDetails("holdId"));
when(holdProcessRegistry.get("holdId")).thenReturn(null);
assertEquals(Collections.emptyList(), holdBulkMonitor.getBulkStatusesForHold("holdId"));
}
@Test
public void testGetBulkStatus()
public void testGetBulkStatusesForHoldReturnsSortedStatuses()
{
BulkOperation bulkOperation = mock(BulkOperation.class);
HoldBulkStatus status1 = new HoldBulkStatus("process1", new Date(1000), new Date(2000), 0L, 0L, 0L, null, false,
null);
when(holdProcessRegistry.get(new Pair<>("holdId", "process1"))).thenReturn(
new HoldBulkProcessDetails("process1", null, bulkOperation));
when(holdProgressCache.get("process1")).thenReturn(status1);
HoldBulkStatus status1 = new HoldBulkStatus(null, new Date(1000), new Date(2000), 0L, 0L, 0L, null);
HoldBulkStatus status2 = new HoldBulkStatus(null, new Date(3000), null, 0L, 0L, 0L, null);
HoldBulkStatus status3 = new HoldBulkStatus(null, new Date(4000), null, 0L, 0L, 0L, null);
HoldBulkStatus status4 = new HoldBulkStatus(null, new Date(500), new Date(800), 0L, 0L, 0L, null);
HoldBulkStatus status5 = new HoldBulkStatus(null, null, null, 0L, 0L, 0L, null);
assertEquals(new HoldBulkStatusAndProcessDetails(status1,
new HoldBulkProcessDetails(status1.bulkStatusId(), null, bulkOperation)),
holdBulkMonitor.getBulkStatusWithProcessDetails("holdId", "process1"));
}
@Test
public void testGetNonExistingBulkStatus()
{
BulkOperation bulkOperation = mock(BulkOperation.class);
when(holdProcessRegistry.get(new Pair<>("holdId", "process1"))).thenReturn(
new HoldBulkProcessDetails("process1", null, bulkOperation));
when(holdProgressCache.get("process1")).thenReturn(null);
assertNull(holdBulkMonitor.getBulkStatusWithProcessDetails("holdId", "process1"));
}
@Test
public void testGetBulkStatusesForHoldReturnsSortedStatusesWithProcessDetails()
{
BulkOperation bulkOperation = mock(BulkOperation.class);
HoldBulkStatus status1 = new HoldBulkStatus("process1", new Date(1000), new Date(2000), 0L, 0L, 0L, null, false,
null);
HoldBulkStatus status2 = new HoldBulkStatus("process2", new Date(3000), null, 0L, 0L, 0L, null, false, null);
HoldBulkStatus status3 = new HoldBulkStatus("process3", new Date(4000), null, 0L, 0L, 0L, null, false, null);
HoldBulkStatus status4 = new HoldBulkStatus("process4", new Date(500), new Date(800), 0L, 0L, 0L, null, false,
null);
HoldBulkStatus status5 = new HoldBulkStatus("process5", null, null, 0L, 0L, 0L, null, false, null);
when(holdProcessRegistry.getKeys()).thenReturn(
Arrays.asList(new Pair<>("holdId", "process1"), new Pair<>("holdId", "process2"),
new Pair<>("holdId", "process3"), new Pair<>("holdId", "process4"), new Pair<>("holdId", "process5"))
);
when(holdProcessRegistry.get(new Pair<>("holdId", "process1"))).thenReturn(
new HoldBulkProcessDetails("process1", null, bulkOperation));
when(holdProcessRegistry.get(new Pair<>("holdId", "process2"))).thenReturn(
new HoldBulkProcessDetails("process2", null, bulkOperation));
when(holdProcessRegistry.get(new Pair<>("holdId", "process3"))).thenReturn(
new HoldBulkProcessDetails("process3", null, bulkOperation));
when(holdProcessRegistry.get(new Pair<>("holdId", "process4"))).thenReturn(
new HoldBulkProcessDetails("process4", null, bulkOperation));
when(holdProcessRegistry.get(new Pair<>("holdId", "process5"))).thenReturn(
new HoldBulkProcessDetails("process5", null, bulkOperation));
when(holdProcessRegistry.get("holdId")).thenReturn(
Arrays.asList("process1", "process2", "process3", "process4", "process5")
.stream().map(bulkStatusId -> new HoldBulkProcessDetails(bulkStatusId, null)).toList());
when(holdProgressCache.get("process1")).thenReturn(status1);
when(holdProgressCache.get("process2")).thenReturn(status2);
when(holdProgressCache.get("process3")).thenReturn(status3);
when(holdProgressCache.get("process4")).thenReturn(status4);
when(holdProgressCache.get("process5")).thenReturn(status5);
assertEquals(Arrays.asList(status5, status3, status2, status1, status4).stream().map(
status -> new HoldBulkStatusAndProcessDetails(status,
new HoldBulkProcessDetails(status.bulkStatusId(), null, bulkOperation))).toList(),
holdBulkMonitor.getBulkStatusesWithProcessDetails("holdId"));
assertEquals(Arrays.asList(status5, status3, status2, status1, status4),
holdBulkMonitor.getBulkStatusesForHold("holdId"));
}
}

View File

@@ -1,126 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.module.org_alfresco_module_rm.disposition;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseUnitTest;
import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
import org.alfresco.rm.rest.api.model.RetentionSchedule;
import org.alfresco.rm.rest.api.model.RetentionScheduleActionDefinition;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.Period;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
/**
* Retention schedule model unit test
*/
public class RetentionScheduleModelUnitTest extends BaseUnitTest
{
private static final String AUTHORITY = "authority";
private static final String INSTRUCTIONS = "instructions";
private static final String RETAIN_STEP = "retain";
@InjectMocks
private ApiNodesModelFactory apiNodesModelFactory;
@Mock
DispositionSchedule dispositionSchedule;
@Mock
DispositionActionDefinition dispositionActionDefinition;
@Test
public void mapRetentionScheduleDataTest()
{
// Mock data
NodeRef nodeRef = generateNodeRef(RecordsManagementModel.TYPE_DISPOSITION_SCHEDULE, true);
ChildAssociationRef childAssociationRef = generateChildAssociationRef(filePlan, record);
when(dispositionSchedule.getDispositionAuthority()).thenReturn(AUTHORITY);
when(dispositionSchedule.getDispositionInstructions()).thenReturn(INSTRUCTIONS);
when(dispositionSchedule.getNodeRef()).thenReturn(nodeRef);
when(dispositionSchedule.isRecordLevelDisposition()).thenReturn(false);
when(mockedNodeService.getPrimaryParent(nodeRef)).thenReturn(childAssociationRef);
// Call the method
RetentionSchedule actualResult = apiNodesModelFactory.mapRetentionScheduleData(dispositionSchedule);
//Expected Result
RetentionSchedule expectedResult = new RetentionSchedule();
expectedResult.setId(nodeRef.getId());
expectedResult.setParentId(filePlan.getId());
expectedResult.setAuthority(AUTHORITY);
expectedResult.setInstructions(INSTRUCTIONS);
// Assertions
assertEquals(expectedResult, actualResult);
}
@Test
public void mapRetentionScheduleActionDefDataTest()
{
// Mock data
NodeRef nodeRef = generateNodeRef(RecordsManagementModel.TYPE_DISPOSITION_SCHEDULE, true);
String period = "month|10";
ChildAssociationRef childAssociationRef = generateChildAssociationRef(filePlan, record);
when(dispositionActionDefinition.getNodeRef()).thenReturn(nodeRef);
when(dispositionActionDefinition.getName()).thenReturn(RETAIN_STEP);
when(dispositionActionDefinition.getDescription()).thenReturn("Description");
when(dispositionActionDefinition.getIndex()).thenReturn(1);
when(dispositionActionDefinition.getGhostOnDestroy()).thenReturn("ghost");
when(dispositionActionDefinition.getPeriod()).thenReturn(new Period(period));
when(dispositionActionDefinition.getLocation()).thenReturn("location");
when(dispositionActionDefinition.getId()).thenReturn(nodeRef.getId());
when(mockedNodeService.getPrimaryParent(nodeRef)).thenReturn(childAssociationRef);
// Call the method
RetentionScheduleActionDefinition actualResult = apiNodesModelFactory.mapRetentionScheduleActionDefData(dispositionActionDefinition);
//Expected Result
RetentionScheduleActionDefinition expectedResult = getRetentionScheduleActionDefinition(nodeRef);
// Assertion
assertEquals(expectedResult, actualResult);
}
private static RetentionScheduleActionDefinition getRetentionScheduleActionDefinition(NodeRef nodeRef)
{
RetentionScheduleActionDefinition expectedResult = new RetentionScheduleActionDefinition();
expectedResult.setId(nodeRef.getId());
expectedResult.setName(RETAIN_STEP);
expectedResult.setDescription("Description");
expectedResult.setIndex(1);
expectedResult.setLocation("location");
expectedResult.setPeriod("month");
expectedResult.setPeriodAmount(10);
expectedResult.setRetainRecordMetadataAfterDestruction(true);
return expectedResult;
}
}

View File

@@ -93,15 +93,15 @@ public class RMv33HoldAuditEntryValuesPatchUnitTest
verify(mockedRecordsManagementQueryDAO, times(1)).updatePropertyStringValueEntity(deleteHoldPropertyStringValueEntity);
assertEquals("Add To Hold", addToHoldPropertyStringValueEntity.getStringValue());
assertEquals("add to hold", addToHoldPropertyStringValueEntity.getStringLower());
assertEquals("add to hold", addToHoldPropertyStringValueEntity.getStringEndLower());
assertEquals(Long.valueOf(770_786_109L), addToHoldPropertyStringValueEntity.getStringCrc());
assertEquals("Remove From Hold", removeFromHoldPropertyStringValueEntity.getStringValue());
assertEquals("remove from hold", removeFromHoldPropertyStringValueEntity.getStringLower());
assertEquals("remove from hold", removeFromHoldPropertyStringValueEntity.getStringEndLower());
assertEquals(Long.valueOf(2_967_613_012L), removeFromHoldPropertyStringValueEntity.getStringCrc());
assertEquals("Delete Hold", deleteHoldPropertyStringValueEntity.getStringValue());
assertEquals("delete hold", deleteHoldPropertyStringValueEntity.getStringLower());
assertEquals("delete hold", deleteHoldPropertyStringValueEntity.getStringEndLower());
assertEquals(Long.valueOf(132_640_810L), deleteHoldPropertyStringValueEntity.getStringCrc());
}

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<build>

View File

@@ -40,8 +40,6 @@ tags:
description: Retrieve and manage unfiled record folders
- name: holds
description: Retrieve and manage holds
- name: retention-schedules
description: Perform retention schedule specific operations
paths:
## GS sites
@@ -2375,39 +2373,6 @@ paths:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
'/holds/{holdId}/bulk-statuses/{bulkStatusId}/cancel':
post:
tags:
- holds
operationId: cancelBulkStatus
summary: Cancel the bulk operation
description: |
Cancels the bulk operation specified by **bulkStatusId** for **holdId**.
parameters:
- $ref: '#/parameters/holdIdParam'
- $ref: '#/parameters/bulkStatusId'
- in: body
name: cancelReason
description: Cancel reason.
required: false
schema:
$ref: '#/definitions/BulkBodyCancel'
responses:
'200':
description: Successful response
'400':
description: |
Invalid parameter: **holdId** or **bulkStatusId** is not a valid format
'401':
description: Authentication failed
'403':
description: Current user does not have permission to cancel the bulk process for **holdId** and **bulkStatusId**
'404':
description: "**holdId** or **bulkStatusId** does not exist"
default:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
'/holds/{holdId}/bulk':
post:
tags:
@@ -2636,196 +2601,7 @@ paths:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
##retention-schedule
'/record-categories/{recordCategoryId}/retention-schedules':
post:
tags:
- retention-schedules
summary: Create a retention schedule
description: |
Create a retention schedule.
For example, using the following JSON body will create a retention schedule:
```JSON
{
"authority": "Retention Authority",
"instructions": "Retention Instructions",
"isRecordLevel": false
}
```
operationId: createRetentionSchedule
parameters:
- $ref: '#/parameters/recordCategoryIdParam'
- in: body
name: retentionNodeBodyCreate
description: |
The retention schedule information to create.
schema:
$ref: '#/definitions/RetentionNodeBodyCreate'
consumes:
- application/json
produces:
- application/json
responses:
'201':
description: Successful response
schema:
$ref: '#/definitions/RetentionScheduleResponse'
'400':
description: |
Invalid parameter: value of recordCategoryId is invalid
'401':
description: Authentication failed
'403':
description: Current user does not have permission to create retention schedule
'404':
description: recordCategoryId does not exist
'409':
description: Retention schedule already exist for the given recordCategoryId
'422':
description: Record level retention schedule cannot be created for a record category having folder associated
default:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
get:
tags:
- retention-schedules
summary: Get the retention schedule for a record category
description: |
Get the retention schedule for a record category.
You can use the **include** parameter (include=actions) to return additional information.
operationId: getRetentionScheduleList
parameters:
- $ref: '#/parameters/recordCategoryIdParam'
- $ref: '#/parameters/retentionScheduleIncludeParam'
- $ref: '#/parameters/skipCountParam'
- $ref: '#/parameters/maxItemsParam'
consumes:
- application/json
produces:
- application/json
responses:
'200':
description: Successful response
schema:
$ref: '#/definitions/RetentionScheduleResponseList'
'400':
description: |
Invalid parameter: value of recordCategoryId is invalid
'401':
description: Authentication failed
'403':
description: Current user does not have permission to get retention schedule
'404':
description: recordCategoryId does not exist
default:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
'/retention-schedules/{retentionScheduleId}/retention-steps':
post:
tags:
- retention-schedules
summary: Create a step in the retention schedule
description: |
Create a step in the retention schedule.
Order of steps:
* "**retain**" or "**cutoff**" should be first
* can't use "**cutoff**" after "**transfer**" or "**accession**"
* only the "**transfer**" action is allowed multiple times
* no steps are allowed after "**destroy**"
For example, the following JSON body will create a step in the retention schedule:
```JSON
{
"name":"accession",
"description":"Step Description",
"periodAmount": 2,
"period":"month",
"periodProperty":"cm:created",
"combineRetentionStepConditions": false,
"events":["versioned"],
"eligibleOnFirstCompleteEvent": true
}
```
operationId: createRetentionScheduleAction
parameters:
- $ref: '#/parameters/retentionScheduleIdParam'
- in: body
name: nodeBodyCreate
description: |
The retention schedule steps information to create.
required: true
schema:
$ref: '#/definitions/RetentionStepNodeBodyCreate'
consumes:
- application/json
produces:
- application/json
responses:
'201':
description: Successful response
schema:
$ref: '#/definitions/RetentionStepNodeBodyResponse'
'400':
description: |
Invalid parameter: value of retentionScheduleId is invalid
Invalid parameter (e.g. event, period, periodProperty)
'401':
description: Authentication failed
'403':
description: Current user does not have permission to create retention schedule step
'404':
description: retentionScheduleId does not exist
'409':
description: |
* Invalid Step - Can't use Cut Off after Transfer or Accession
* Invalid Step - Destroy action already completed. Can't do any other Action
* Invalid Step - This step already exists. You cant create this step [Transfer action is allowed many times]
'422':
description: Cut Off or Retain should be the first step
default:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
get:
tags:
- retention-schedules
summary: Get the list of steps in the retention schedule
description: |
Get the list of steps in the retention schedule.
operationId: getRetentionScheduleActionList
parameters:
- $ref: '#/parameters/retentionScheduleIdParam'
- $ref: '#/parameters/skipCountParam'
- $ref: '#/parameters/maxItemsParam'
consumes:
- application/json
produces:
- application/json
responses:
'200':
description: Successful response
schema:
$ref: '#/definitions/RetentionStepsNodeBodyResponse'
'400':
description: |
Invalid parameter: value of retentionScheduleId is invalid
'401':
description: Authentication failed
'403':
description: Current user does not have permission to get retention schedule steps
'404':
description: retentionScheduleId does not exist
default:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
parameters:
## File plans
filePlanEntryIncludeParam:
@@ -3291,22 +3067,6 @@ parameters:
If true, then a name clash will cause an attempt to auto rename by finding a unique name using an integer suffix.
required: false
type: boolean
## RetentionSchedule
retentionScheduleIdParam:
name: retentionScheduleId
in: path
description:
The identifier of a retention schedule.
required: true
type: string
retentionScheduleIncludeParam:
name: include
in: query
description: |
Returns additional information about the retention schedule actions. Any optional field from the response model can be requested. For example:
* actions
required: false
type: string
definitions:
FilePlanComponentBodyUpdate:
type: object
@@ -4408,11 +4168,6 @@ definitions:
totalItems:
type: integer
format: int64
BulkBodyCancel:
type: object
properties:
reason:
type: string
HoldBulkStatus:
type: object
properties:
@@ -4441,11 +4196,6 @@ definitions:
- PENDING
- IN PROGRESS
- DONE
- CANCELLED
cancellationReason:
type: string
holdBulkOperation:
$ref: '#/definitions/HoldBulkOperation'
HoldBulkStatusEntry:
type: object
required:
@@ -4465,228 +4215,6 @@ definitions:
type: array
items:
$ref: '#/definitions/HoldBulkStatusEntry'
RetentionNodeBodyCreate:
type: object
properties:
authority:
type: string
description: |
Authority name for the retention schedule.
instructions:
type: string
description: |
Required instructions for the retention schedule.
isRecordLevel:
type: boolean
default: false
description: |
This field is used to specify whether the retention schedule needs to be applied in the folder level or record level.
True will cause the the retention schedule to apply to records and false will cause the retention schedule to apply to record folders.
This cannot be changed once items start being managed by the retention schedule.
RetentionScheduleResponse:
type: object
properties:
id:
type: string
parentId:
type: string
authority:
type: string
instructions:
type: string
isRecordLevel:
type: boolean
unpublishedUpdates:
type: boolean
RetentionScheduleResponseList:
type: object
properties:
list:
type: object
properties:
pagination:
$ref: '#/definitions/Pagination'
entries:
type: array
items:
$ref: '#/definitions/FullRetentionScheduleResponse'
FullRetentionScheduleResponse:
type: object
properties:
id:
type: string
parentId:
type: string
authority:
type: string
instructions:
type: string
isRecordLevel:
type: boolean
unpublishedUpdates:
type: boolean
actions:
type: array
items:
$ref: '#/definitions/Actions'
Actions:
type: object
properties:
id:
type: string
name:
type: string
description:
type: string
periodAmount:
type: integer
period:
type: string
periodProperty:
type: string
combineRetentionStepConditions:
type: boolean
default: false
eligibleOnFirstCompleteEvent:
type: boolean
default: true
retainRecordMetadataAfterDestruction:
type: boolean
location:
type: string
events:
type: array
items:
type: string
index:
type: integer
RetentionStepNodeBodyCreate:
type: object
required:
- name
- description
properties:
name:
type: string
description: |
The valid names are:
* retain
* cutoff
* accession
* transfer
* destroyContent
* destroyNode
destroyNode step can be used to destroy content along with record metadata.
In case, record metadata needs to be retained, then destroyContent step should be used.
description:
type: string
description: |
This property is used to provide the step description.
periodAmount:
type: integer
description: |
This property is only applicable for the following period values.
* day
* month
* quarter
* week
* duration
* year
period:
type: string
description: |
Valid values for the period.
* day = Day
* fmend = End Of Financial Month
* fqend = End Of Financial Quarter
* fyend = End Of Financial Year
* immediately = Immediately
* monthend = End Of Month
* quarterend = End Of Quarter
* yearend = End Of Year
* month = Month
* none = None
* quarter = Quarter
* week = Week
* duration = XML Duration
* year = Year
If you provide XML Duration for the period value, you need to specify a time interval using XML syntax.
The syntax should take the form of:
P = Period (required)
nY = Number of years
nM = Number of months
nD = Number of days
T = Start time of a time section (required if specifying hours, minutes, or seconds)
nH = Number of hours
nM = Number of minutes
nS = Number of seconds
For example, P2M10D represents two months and ten days.
periodProperty:
type: string
default: cm:created
description: |
Valid values for the periodProperty property
* cm:created = Created Date (defult value)
* rma:cutOffDate = Cut Off Date
* rma:dispositionAsOf = Retention Action
combineRetentionStepConditions:
type: boolean
description: |
This property is only valid for **accession** step.
This is used to specify whether to combine the period condition and events for the step execution or only consider one of them.
For example:
**periodCondition**: After a period of 2 months
**eventsCondition**: Case Closed event
This flag can be used to consider only (**periodCondition** or **eventsCondition**) or both of them at once.
events:
type: array
items:
type: string
description: |
Valid values for the events property
* case_closed = Case Closed
* abolished = Abolished
* re_designated = Redesignated
* no_longer_needed = No Longer Needed
* superseded = Superseded
* versioned = Versioned
* study_complete = Study Complete
* training_complete = Training Complete
* related_record_trasfered_inactive_storage = Related Record Transferred to Inactive Storage
* obsolete = Obsolete
* all_allowances_granted_are_terminated = All Allowances Granted are Terminated
* WGI_action_complete = WGI Action Complete
* separation = Separation
* case_complete = Case Complete
* declassification_review = Declassification Review
eligibleOnFirstCompleteEvent:
type: boolean
description: |
* false = When all events have happened
* true = Whichever event is earlier
location:
type: string
description: |
This property is only valid for transfer step
RetentionStepNodeBodyResponse:
type: object
properties:
actions:
$ref: '#/definitions/Actions'
RetentionStepsNodeBodyResponse:
type: object
properties:
list:
type: object
properties:
pagination:
$ref: '#/definitions/Pagination'
entries:
type: array
items:
$ref: '#/definitions/RetentionStepNodeBodyResponse'
##
RequestBodyFile:
type: object

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<modules>

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-amps</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<properties>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<dependencies>
@@ -47,11 +47,11 @@
<artifactId>commons-math3</artifactId>
<version>3.6.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.uuid/java-uuid-generator -->
<dependency>
<groupId>com.fasterxml.uuid</groupId>
<artifactId>java-uuid-generator</artifactId>
<version>5.1.0</version>
<groupId>org.safehaus.jug</groupId>
<artifactId>jug</artifactId>
<version>2.0.0</version>
<classifier>asl</classifier>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>

View File

@@ -21,7 +21,7 @@ package org.alfresco.util;
import java.security.SecureRandom;
import java.util.Random;
import com.fasterxml.uuid.Generators;
import org.safehaus.uuid.UUIDGenerator;
import org.alfresco.api.AlfrescoPublicApi;
/**
@@ -69,7 +69,7 @@ public final class GUID
public static String generate()
{
int randomInt = RANDOM.nextInt(SECURE_RANDOM_POOL_MAX_ITEMS);
return Generators.randomBasedGenerator(SECURE_RANDOM_POOL[randomInt]).generate().toString();
return UUIDGenerator.getInstance().generateRandomBasedUUID(SECURE_RANDOM_POOL[randomInt]).toString();
}
// == Not sure if we need this functionality again (derekh) ==

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2024 Alfresco Software Limited.
* Copyright (C) 2005-2023 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -67,14 +67,4 @@ public interface TransactionListener
* be used only for cleaning up resources after a rollback has occurred.
*/
void afterRollback();
/**
* Allows to provide a custom listener's order.
* See {@link org.alfresco.repo.transaction.AlfrescoTransactionSupport#COMMIT_ORDER_NORMAL}
* @return custom order or null for the default one
*/
default Integer getCustomOrder()
{
return null;
}
}

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<properties>
@@ -132,7 +132,7 @@
<dependency>
<groupId>com.fasterxml.woodstox</groupId>
<artifactId>woodstox-core</artifactId>
<version>7.0.0</version>
<version>6.5.1</version>
</dependency>
<!-- the cxf libs were updated, see dependencyManagement section -->

View File

@@ -33,13 +33,13 @@ import java.util.HashSet;
import java.util.Set;
import java.util.zip.CRC32;
import com.fasterxml.uuid.Generators;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.service.cmr.repository.datatype.Duration;
import org.alfresco.util.GUID;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.safehaus.uuid.UUIDGenerator;
import org.alfresco.util.ParameterCheck;
/**
@@ -497,7 +497,7 @@ public class InMemoryTicketComponentImpl implements TicketComponent
this.userName = userName;
this.validDuration = validDuration;
this.testDuration = validDuration.divide(2);
final String guid = Generators.randomBasedGenerator().generate().toString();
final String guid = UUIDGenerator.getInstance().generateRandomBasedUUID().toString();
this.ticketId = computeTicketId(expires, expiryDate, userName, guid);

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Data model classes
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -78,28 +78,6 @@ public interface PermissionService
*/
public static final String GUEST_AUTHORITY = "ROLE_GUEST";
/**
* The dynamic authority for the Admin service account.
*/
String ADMIN_SVC_AUTHORITY = "ROLE_ADMIN_SERVICE_ACCOUNT";
/**
* The dynamic authority for the Collaborator service account.
*/
String COLLABORATOR_SVC_AUTHORITY = "ROLE_COLLABORATOR_SERVICE_ACCOUNT";
/**
* The dynamic authority for the Editor service account.
*/
String EDITOR_SVC_AUTHORITY = "ROLE_EDITOR_SERVICE_ACCOUNT";
/**
* A convenient set of service account authorities to simplify checks
* for whether a given authority is a service account authority or not.
*/
Set<String> SVC_AUTHORITIES_SET = Set.of(ADMIN_SVC_AUTHORITY, COLLABORATOR_SVC_AUTHORITY,
EDITOR_SVC_AUTHORITY);
/**
* The permission for all - not defined in the model. Repsected in the code.
*/

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<dependencies>
@@ -84,7 +84,7 @@
<include>org.alfresco:alfresco-core</include>
<include>org.alfresco:alfresco-repository</include>
<include>org.apache.commons:commons-compress</include>
<include>com.fasterxml.uuid:java-uuid-generator</include>
<include>org.safehaus.jug:jug</include>
<include>org.alfresco.surf:spring-surf-core</include>
<include>org.tukaani:xz</include>
<include>org.apache.maven:maven-artifact</include>

View File

@@ -25,7 +25,6 @@
*/
package org.alfresco.repo.module.tool;
import com.fasterxml.uuid.Generators;
import de.schlichtherle.truezip.file.*;
import de.schlichtherle.truezip.fs.FsSyncException;
import de.schlichtherle.truezip.fs.archive.zip.JarDriver;
@@ -35,6 +34,7 @@ import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.module.ModuleVersionNumber;
import org.alfresco.service.cmr.module.ModuleDetails;
import org.alfresco.service.cmr.module.ModuleInstallState;
import org.safehaus.uuid.UUIDGenerator;
import java.io.BufferedInputStream;
import java.io.IOException;
@@ -916,7 +916,7 @@ public class ModuleManagementTool implements LogOutput
*/
private static String generateGuid()
{
return Generators.timeBasedGenerator().generate().toString();
return UUIDGenerator.getInstance().generateTimeBasedUUID().toString();
}
/**

View File

@@ -9,6 +9,6 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
</project>

View File

@@ -1,5 +1,6 @@
# Fetch image based on Tomcat 9.0, Java 17 and Rocky Linux 8
# More infos about this image: https://github.com/Alfresco/alfresco-docker-base-tomcat
FROM alfresco/alfresco-base-tomcat:tomcat10-jre17-rockylinux9@sha256:395664f9d9be0c9f73d3b722a58fd559ee7231609b263dfe19502617652740e3
FROM alfresco/alfresco-base-tomcat:tomcat10-jre17-rockylinux8-202306291245
# Set default docker_context.
ARG resource_path=target
@@ -13,9 +14,6 @@ ARG USERID=33000
# Set default environment args
ARG TOMCAT_DIR=/usr/local/tomcat
# Needed for installation but make sure another USER directive is added after
# this with a non-root user
USER root
# Create prerequisite to store tools and properties
RUN mkdir -p ${TOMCAT_DIR}/shared/classes/alfresco/extension/mimetypes && \
@@ -63,7 +61,13 @@ RUN sed -i -e "s_appender.rolling.fileName\=alfresco.log_appender.rolling.fileNa
sed -i -e "\$a\grant\ codeBase\ \"file:\$\{catalina.base\}\/webapps\/alfresco\/-\" \{\n\ permission\ java.security.AllPermission\;\n\};\ngrant\ codeBase\ \"file:\$\{catalina.base\}\/webapps\/_vti_bin\/-\" \{\n\ permission\ java.security.AllPermission\;\n\};\ngrant\ codeBase\ \"file:\$\{catalina.base\}\/webapps\/ROOT\/-\" \{\n\ permission org.apache.catalina.security.DeployXmlPermission \"ROOT\";\n\};" ${TOMCAT_DIR}/conf/catalina.policy
# fontconfig is required by Activiti worflow diagram generator
RUN yum install -y fontconfig-2.14.0-2.el9_1 && \
# installing pinned dependencies as well
RUN yum install -y fontconfig-2.13.1-4.el8 \
dejavu-fonts-common-2.35-7.el8 \
fontpackages-filesystem-1.44-22.el8 \
freetype-2.9.1-9.el8 \
libpng-1.6.34-5.el8 \
dejavu-sans-fonts-2.35-7.el8 && \
yum clean all
# The standard configuration is to have all Tomcat files owned by root with group GROUPNAME and whilst owner has read/write privileges,

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<properties>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<modules>

View File

@@ -1,3 +1,3 @@
SOLR6_TAG=2.0.11
SOLR6_TAG=2.0.10
POSTGRES_TAG=15.4
ACTIVEMQ_TAG=5.18.3-jre17-rockylinux8

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<modules>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<organization>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<developers>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<developers>

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<properties>
@@ -17,7 +17,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<rest.api.explorer.branch>master</rest.api.explorer.branch>
<httpclient-osgi-version>4.5.6</httpclient-osgi-version>
<commons-lang3.version>3.17.0</commons-lang3.version>
<commons-lang3.version>3.13.0</commons-lang3.version>
<scribejava-apis.version>8.3.3</scribejava-apis.version>
<java.version>17</java.version>
</properties>
@@ -171,14 +171,14 @@
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>3.0.22</version>
<version>3.0.19</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.codehaus.groovy/groovy-json-->
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-json</artifactId>
<version>3.0.22</version>
<version>3.0.19</version>
</dependency>
<dependency>

View File

@@ -2,7 +2,7 @@
* #%L
* alfresco-tas-restapi
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -25,8 +25,6 @@
*/
package org.alfresco.rest.search;
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.alfresco.rest.core.IRestModel;
@@ -95,28 +93,6 @@ public class RestRequestQueryModel extends TestModel implements IRestModel<RestR
{
this.query = query;
}
@Override
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if (o == null || getClass() != o.getClass())
{
return false;
}
RestRequestQueryModel that = (RestRequestQueryModel) o;
return Objects.equals(model, that.model) && Objects.equals(getLanguage(), that.getLanguage())
&& Objects.equals(getUserQuery(), that.getUserQuery()) && Objects.equals(getQuery(),
that.getQuery());
}
@Override
public int hashCode()
{
return Objects.hash(model, getLanguage(), getUserQuery(), getQuery());
}
}

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<developers>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<properties>
@@ -110,11 +110,6 @@
<artifactId>mysql-connector-java</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.owasp.encoder</groupId>
<artifactId>encoder</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
<build>
@@ -140,7 +135,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
<version>3.2.1</version>
<version>3.2.0</version>
<executions>
<execution>
<phase>validate</phase>

View File

@@ -34,7 +34,6 @@
<%@ page import="org.alfresco.service.cmr.module.ModuleDetails" %>
<%@ page import="org.alfresco.service.cmr.module.ModuleInstallState" %>
<%@ page import="java.util.Calendar" %>
<%@ page import="org.owasp.encoder.Encode" %>
<!-- Enterprise index-jsp placeholder -->
<%
@@ -89,7 +88,7 @@ ModuleDetails shareServicesModule = moduleService.getModule("alfresco-share-serv
<p></p>
<p><a href="./s/index">Alfresco WebScripts Home</a> (admin only - INTERNAL)</p>
<p></p>
<p><a href="<%=Encode.forHtmlAttribute(UrlUtil.getApiExplorerUrl(sysAdminParams, request.getRequestURL().toString(), request.getRequestURI()))%>">Alfresco API Explorer</a></p>
<p><a href="<%=UrlUtil.getApiExplorerUrl(sysAdminParams, request.getRequestURL().toString(), request.getRequestURI())%>">Alfresco API Explorer</a></p>
<%
if (descriptorService.getLicenseDescriptor() == null && transactionService.isReadOnly())
{

146
pom.xml
View File

@@ -2,7 +2,7 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>alfresco-community-repo</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Alfresco Community Repo Parent</name>
@@ -24,7 +24,7 @@
<properties>
<acs.version.major>23</acs.version.major>
<acs.version.minor>4</acs.version.minor>
<acs.version.minor>3</acs.version.minor>
<acs.version.revision>0</acs.version.revision>
<acs.version.label />
<amp.min.version>${acs.version.major}.0.0</amp.min.version>
@@ -51,81 +51,80 @@
<dependency.alfresco-server-root.version>7.0.1</dependency.alfresco-server-root.version>
<dependency.activiti-engine.version>5.23.0</dependency.activiti-engine.version>
<dependency.activiti.version>5.23.0</dependency.activiti.version>
<dependency.alfresco-transform-core.version>5.1.4</dependency.alfresco-transform-core.version>
<dependency.alfresco-transform-service.version>4.1.4</dependency.alfresco-transform-service.version>
<dependency.alfresco-transform-core.version>5.1.2-A1</dependency.alfresco-transform-core.version>
<dependency.alfresco-transform-service.version>4.1.2-A1</dependency.alfresco-transform-service.version>
<dependency.alfresco-greenmail.version>7.0</dependency.alfresco-greenmail.version>
<dependency.acs-event-model.version>0.0.27</dependency.acs-event-model.version>
<dependency.aspectj.version>1.9.22.1</dependency.aspectj.version>
<dependency.spring.version>6.1.13</dependency.spring.version>
<dependency.spring-security.version>6.3.1</dependency.spring-security.version>
<dependency.aspectj.version>1.9.20.1</dependency.aspectj.version>
<dependency.spring.version>6.0.19</dependency.spring.version>
<dependency.spring-security.version>6.2.2</dependency.spring-security.version>
<dependency.antlr.version>3.5.3</dependency.antlr.version>
<dependency.jackson.version>2.17.2</dependency.jackson.version>
<dependency.cxf.version>4.0.5</dependency.cxf.version>
<dependency.jackson.version>2.15.2</dependency.jackson.version>
<dependency.cxf.version>4.0.2</dependency.cxf.version>
<dependency.opencmis.version>1.0.0-jakarta-1</dependency.opencmis.version>
<dependency.webscripts.version>9.2</dependency.webscripts.version>
<dependency.webscripts.version>9.0</dependency.webscripts.version>
<dependency.bouncycastle.version>1.78.1</dependency.bouncycastle.version>
<dependency.mockito-core.version>5.13.0</dependency.mockito-core.version>
<dependency.assertj.version>3.26.3</dependency.assertj.version>
<dependency.org-json.version>20240303</dependency.org-json.version>
<dependency.commons-dbcp.version>2.12.0</dependency.commons-dbcp.version>
<dependency.commons-io.version>2.16.1</dependency.commons-io.version>
<dependency.gson.version>2.11.0</dependency.gson.version>
<dependency.guava.version>33.3.0-jre</dependency.guava.version>
<dependency.bouncycastle.version>1.77</dependency.bouncycastle.version>
<dependency.mockito-core.version>5.4.0</dependency.mockito-core.version>
<dependency.assertj.version>3.24.2</dependency.assertj.version>
<dependency.org-json.version>20231013</dependency.org-json.version>
<dependency.commons-dbcp.version>2.9.0</dependency.commons-dbcp.version>
<dependency.commons-io.version>2.14.0</dependency.commons-io.version>
<dependency.gson.version>2.10.1</dependency.gson.version>
<dependency.guava.version>32.1.2-jre</dependency.guava.version>
<dependency.httpclient.version>4.5.14</dependency.httpclient.version>
<dependency.httpcore.version>4.4.16</dependency.httpcore.version>
<dependency.httpcomponents-httpclient5.version>5.2.1</dependency.httpcomponents-httpclient5.version>
<dependency.httpcomponents-httpcore5.version>5.3</dependency.httpcomponents-httpcore5.version>
<dependency.httpcomponents-httpcore5.version>5.2.3</dependency.httpcomponents-httpcore5.version>
<dependency.commons-httpclient.version>3.1-HTTPCLIENT-1265</dependency.commons-httpclient.version>
<dependency.xercesImpl.version>2.12.2</dependency.xercesImpl.version>
<dependency.slf4j.version>2.0.16</dependency.slf4j.version>
<dependency.log4j.version>2.24.0</dependency.log4j.version>
<dependency.groovy.version>3.0.22</dependency.groovy.version>
<dependency.slf4j.version>2.0.9</dependency.slf4j.version>
<dependency.log4j.version>2.20.0</dependency.log4j.version>
<dependency.groovy.version>3.0.19</dependency.groovy.version>
<dependency.tika.version>2.9.2</dependency.tika.version>
<dependency.truezip.version>7.7.10</dependency.truezip.version>
<dependency.poi.version>5.3.0</dependency.poi.version>
<dependency.poi.version>5.2.5</dependency.poi.version>
<dependency.jboss.logging.version>3.5.0.Final</dependency.jboss.logging.version>
<dependency.camel.version>4.6.0</dependency.camel.version> <!-- when bumping this version, please keep track/sync with included netty.io dependencies -->
<dependency.netty.version>4.1.113.Final</dependency.netty.version> <!-- must be in sync with camels transitive dependencies, e.g.: netty-common -->
<dependency.camel.version>4.0.0</dependency.camel.version> <!-- when bumping this version, please keep track/sync with included netty.io dependencies -->
<dependency.netty.version>4.1.96.Final</dependency.netty.version> <!-- must be in sync with camels transitive dependencies, e.g.: netty-common -->
<dependency.activemq.version>5.18.3</dependency.activemq.version>
<dependency.apache-compress.version>1.27.1</dependency.apache-compress.version>
<dependency.awaitility.version>4.2.2</dependency.awaitility.version>
<dependency.apache-compress.version>1.26.0</dependency.apache-compress.version>
<dependency.awaitility.version>4.2.0</dependency.awaitility.version>
<dependency.swagger-ui.version>4.1.3</dependency.swagger-ui.version>
<dependency.swagger-parser.version>1.0.71</dependency.swagger-parser.version>
<dependency.swagger-parser.version>1.0.67</dependency.swagger-parser.version>
<dependency.maven-filtering.version>3.1.1</dependency.maven-filtering.version>
<dependency.maven-artifact.version>3.8.6</dependency.maven-artifact.version>
<dependency.jdom2.version>2.0.6.1</dependency.jdom2.version>
<dependency.pooled-jms.version>3.1.6</dependency.pooled-jms.version>
<dependency.jakarta-ee-jaxb-api.version>4.0.2</dependency.jakarta-ee-jaxb-api.version>
<dependency.jakarta-ee-jaxb-impl.version>4.0.5</dependency.jakarta-ee-jaxb-impl.version>
<dependency.jakarta-ee-jaxb-api.version>4.0.0</dependency.jakarta-ee-jaxb-api.version>
<dependency.jakarta-ee-jaxb-impl.version>4.0.3</dependency.jakarta-ee-jaxb-impl.version>
<dependency.jakarta-ws-api.version>3.0.1</dependency.jakarta-ws-api.version>
<dependency.jakarta-soap-api.version>3.0.2</dependency.jakarta-soap-api.version>
<dependency.jakarta-annotation-api.version>3.0.0</dependency.jakarta-annotation-api.version>
<dependency.jakarta-soap-api.version>2.0.1</dependency.jakarta-soap-api.version>
<dependency.jakarta-annotation-api.version>2.1.1</dependency.jakarta-annotation-api.version>
<dependency.jakarta-transaction-api.version>2.0.1</dependency.jakarta-transaction-api.version>
<dependency.jakarta-jws-api.version>3.0.0</dependency.jakarta-jws-api.version>
<dependency.jakarta-ee-mail.version>2.0.1</dependency.jakarta-ee-mail.version>
<dependency.jakarta-ee-activation.version>2.0.1</dependency.jakarta-ee-activation.version>
<dependency.jakarta-ee-jms.version>3.1.0</dependency.jakarta-ee-jms.version>
<dependency.jakarta-ee-jms.version>3.0.0</dependency.jakarta-ee-jms.version>
<dependency.java-ee-activation.version>1.2.0</dependency.java-ee-activation.version>
<dependency.jakarta-ee-json-api.version>2.1.3</dependency.jakarta-ee-json-api.version>
<dependency.jakarta-ee-json-impl.version>1.1.7</dependency.jakarta-ee-json-impl.version>
<dependency.jakarta-ee-json-api.version>2.1.2</dependency.jakarta-ee-json-api.version>
<dependency.jakarta-ee-json-impl.version>1.1.4</dependency.jakarta-ee-json-impl.version>
<dependency.jakarta-json-path.version>2.9.0</dependency.jakarta-json-path.version>
<dependency.json-smart.version>2.5.1</dependency.json-smart.version>
<dependency.json-smart.version>2.5.0</dependency.json-smart.version>
<alfresco.googledrive.version>4.1.0</alfresco.googledrive.version>
<alfresco.aos-module.version>3.1.0</alfresco.aos-module.version>
<alfresco.api-explorer.version>23.3.0</alfresco.api-explorer.version> <!-- Also in alfresco-enterprise-share -->
<alfresco.aos-module.version>3.0.0</alfresco.aos-module.version>
<alfresco.api-explorer.version>23.2.0</alfresco.api-explorer.version> <!-- Also in alfresco-enterprise-share -->
<alfresco.maven-plugin.version>2.2.0</alfresco.maven-plugin.version>
<license-maven-plugin.version>2.4.0</license-maven-plugin.version>
<license-maven-plugin.version>2.0.1</license-maven-plugin.version>
<dependency.postgresql.version>42.7.4</dependency.postgresql.version>
<dependency.postgresql.version>42.6.0</dependency.postgresql.version>
<dependency.mysql.version>8.0.30</dependency.mysql.version>
<dependency.mysql-image.version>8</dependency.mysql-image.version>
<dependency.mariadb.version>2.7.4</dependency.mariadb.version>
<dependency.tas-utility.version>5.0.1</dependency.tas-utility.version>
<dependency.rest-assured.version>5.5.0</dependency.rest-assured.version>
<dependency.tas-utility.version>5.0.0</dependency.tas-utility.version>
<dependency.rest-assured.version>5.3.2</dependency.rest-assured.version>
<dependency.tas-email.version>2.0.0</dependency.tas-email.version>
<dependency.tas-webdav.version>1.21</dependency.tas-webdav.version>
<dependency.tas-ftp.version>1.19</dependency.tas-ftp.version>
@@ -152,7 +151,7 @@
<connection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</connection>
<developerConnection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</developerConnection>
<url>https://github.com/Alfresco/alfresco-community-repo</url>
<tag>23.4.0.13</tag>
<tag>HEAD</tag>
</scm>
<distributionManagement>
@@ -402,7 +401,7 @@
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.3.3</version>
<version>1.2</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
@@ -412,7 +411,7 @@
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.17.1</version>
<version>1.17.0</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
@@ -443,7 +442,7 @@
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.11.1</version>
<version>3.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
@@ -463,7 +462,7 @@
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
<version>5.2.1</version>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
@@ -574,7 +573,7 @@
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>2.3</version>
<version>2.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
@@ -622,7 +621,7 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-email</artifactId>
<version>1.6.0</version>
<version>1.5</version>
</dependency>
<dependency>
<groupId>org.alfresco.aos-module</groupId>
@@ -690,7 +689,7 @@
<dependency>
<groupId>com.drewnoakes</groupId>
<artifactId>metadata-extractor</artifactId>
<version>2.19.0</version>
<version>2.18.0</version>
</dependency>
<!-- upgrade dependency from TIKA -->
<dependency>
@@ -701,13 +700,13 @@
<dependency>
<groupId>com.networknt</groupId>
<artifactId>json-schema-validator</artifactId>
<version>1.5.1</version>
<version>1.0.86</version>
</dependency>
<!-- upgrade dependency from TIKA -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.18.1</version>
<version>1.16.1</version>
</dependency>
<!-- upgrade dependency from TIKA -->
<dependency>
@@ -791,14 +790,14 @@
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.12.7</version>
<version>2.12.5</version>
</dependency>
<!-- provided dependencies -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0</version>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -852,17 +851,6 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>${dependency.commons-dbcp.version}</version>
<exclusions>
<!-- Exclude jakarta.transaction-api so that other repositories importing
the alfresco-repository as a dependency, not having community-repo as
a parent, and therefore not sharing the dependencyManagement with it,
won't inherit the outdated jakarta.transaction-api version coming from
this library -->
<exclusion>
<groupId>jakarta.transaction</groupId>
<artifactId>jakarta.transaction-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
@@ -948,7 +936,7 @@
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -987,7 +975,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<version>3.11.0</version>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
@@ -1005,7 +993,7 @@
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.45.0</version>
<version>0.43.4</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
@@ -1017,16 +1005,16 @@
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.4.2</version>
<version>3.3.0</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.4.0</version>
<version>3.3.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.7.0</version>
<version>3.5.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -1036,11 +1024,11 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.7.1</version>
<version>3.6.0</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.7.1</version>
<version>3.6.0</version>
</plugin>
<plugin>
<groupId>org.alfresco.maven.plugin</groupId>
@@ -1086,12 +1074,12 @@
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven3-plugin</artifactId>
<version>1.10.14</version>
<version>1.10.9</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.5.0</version>
<version>3.3.0</version>
<executions>
<execution>
<id>enforce-banned-dependencies</id>
@@ -1135,10 +1123,10 @@
<exclude>com.sun.xml.bind</exclude>
</excludes>
<includes>
<include>org.bouncycastle:bcprov-jdk18on:[1.78.1,)</include>
<include>org.bouncycastle:bcmail-jdk18on:[1.78.1,)</include>
<include>org.bouncycastle:bcpkix-jdk18on:[1.78.1,)</include>
<include>org.bouncycastle:bcutil-jdk18on:[1.78.1,)</include>
<include>org.bouncycastle:bcprov-jdk18on:[1.77,)</include>
<include>org.bouncycastle:bcmail-jdk18on:[1.77,)</include>
<include>org.bouncycastle:bcpkix-jdk18on:[1.77,)</include>
<include>org.bouncycastle:bcutil-jdk18on:[1.77,)</include>
</includes>
</bannedDependencies>
</rules>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>23.4.0.13</version>
<version>23.3.0.38-SNAPSHOT</version>
</parent>
<dependencies>
@@ -45,7 +45,7 @@
<dependency>
<groupId>org.apache.santuario</groupId>
<artifactId>xmlsec</artifactId>
<version>4.0.2</version>
<version>3.0.3</version>
</dependency>
<!-- newer version, see REPO-3133 -->
<dependency>
@@ -137,7 +137,7 @@
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>11.0.24</version>
<version>11.0.16</version>
<scope>test</scope>
</dependency>
<dependency>

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -909,14 +909,16 @@ public class AuditImpl implements Audit
public int getAuditEntriesCountByAppAndProperties(AuditService.AuditApplication auditApplication, AuditEntryQueryWalker propertyWalker)
{
final String applicationName = auditApplication.getKey().substring(1);
AuditQueryParameters parameters = new AuditQueryParameters();
parameters.setApplicationName(auditApplication.getName());
parameters.setApplicationName(applicationName);
parameters.setFromTime(propertyWalker.getFromTime());
parameters.setToTime(propertyWalker.getToTime());
parameters.setFromId(propertyWalker.getFromId());
parameters.setToId(propertyWalker.getToId());
parameters.setUser(propertyWalker.getCreatedByUser());
return auditService.getAuditEntriesCountByAppAndProperties(parameters);
return auditService.getAuditEntriesCountByAppAndProperties(applicationName, parameters);
}
}

View File

@@ -125,7 +125,7 @@ public class PeopleImpl implements People
protected ResetPasswordService resetPasswordService;
protected UserRegistrySynchronizer userRegistrySynchronizer;
protected Renditions renditions;
private Boolean allowImmutableEnabledUpdate;
private final static Map<String, QName> sort_params_to_qnames;
static
@@ -202,11 +202,6 @@ public class PeopleImpl implements People
this.userRegistrySynchronizer = userRegistrySynchronizer;
}
public void setAllowImmutableEnabledUpdate(Boolean allowImmutableEnabledUpdate)
{
this.allowImmutableEnabledUpdate = allowImmutableEnabledUpdate;
}
/**
* Validate, perform -me- substitution and canonicalize the person ID.
*
@@ -713,26 +708,16 @@ public class PeopleImpl implements People
// if requested, update password
updatePassword(isAdmin, personIdToUpdate, person);
Set<QName> immutableProperties = userRegistrySynchronizer.getPersonMappedProperties(personIdToUpdate);
Boolean isEnabled = person.isEnabled();
if (isEnabled != null)
if (person.isEnabled() != null)
{
if (isAdminAuthority(personIdToUpdate))
{
throw new PermissionDeniedException("Admin authority cannot be disabled.");
}
if (allowImmutableEnabledStatusUpdate(personIdToUpdate, isAdmin, immutableProperties))
{
LOGGER.info("User " + personIdToUpdate + " is immutable but enabled status will be set to: " + isEnabled);
}
else
{
// note: if current user is not an admin then permission denied exception is thrown
MutableAuthenticationService mutableAuthenticationService = (MutableAuthenticationService) authenticationService;
mutableAuthenticationService.setAuthenticationEnabled(personIdToUpdate, person.isEnabled());
}
// note: if current user is not an admin then permission denied exception is thrown
MutableAuthenticationService mutableAuthenticationService = (MutableAuthenticationService) authenticationService;
mutableAuthenticationService.setAuthenticationEnabled(personIdToUpdate, person.isEnabled());
}
NodeRef personNodeRef = personService.getPerson(personIdToUpdate, false);
@@ -757,7 +742,9 @@ public class PeopleImpl implements People
properties.putAll(nodes.mapToNodeProperties(customProps));
}
// MNT-21150 LDAP synced attributes can't be changed using REST API
// MNT-21150 LDAP synced attributes can be changed using REST API
Set<QName> immutableProperties = userRegistrySynchronizer.getPersonMappedProperties(personIdToUpdate);
immutableProperties.forEach(immutableProperty -> {
if (properties.containsKey(immutableProperty))
{
@@ -781,28 +768,6 @@ public class PeopleImpl implements People
return getPerson(personId);
}
private boolean allowImmutableEnabledStatusUpdate(String userId, boolean isAdmin, Set<QName> immutableProperties)
{
if (allowImmutableEnabledUpdate)
{
boolean containLdapUserAccountStatus = false;
QName propertyNameToCheck = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "userAccountStatusProperty");
for (QName immutableProperty : immutableProperties)
{
if (immutableProperty.equals(propertyNameToCheck))
{
containLdapUserAccountStatus = true;
break;
}
}
return isAdmin && !containLdapUserAccountStatus && !isMutableAuthority(userId);
}
return false;
}
private boolean checkCurrentUserOrAdmin(String personId)
{
boolean isAdmin = isAdminAuthority();

Some files were not shown because too many files have changed in this diff Show More