Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ Taken als publiek formulier beschikbaar stellen.
## Documentation

- [Getting Started](documentation/getting-started.md) — setup, running, and development instructions
- [Plugin Documentation](documentation/plugin.md) — plugin details and configuration
- [Plugin Documentation](documentation/plugin.md) — configuring the plugin in Valtimo
- [Implementation notes](documentation/developer.md) — process wiring, templates, application settings
- [Release notes](documentation/release-notes.md) — versiegeschiedenis en wijzigingen

## Contact
Expand Down
1 change: 1 addition & 0 deletions backend/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ dependencies {

implementation("com.ritense.valtimo:valtimo-dependencies:$valtimoVersion")
implementation("com.ritense.valtimo:local-mail:$valtimoVersion")
implementation("com.ritense.valtimo:s3-resource:$valtimoVersion")

implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.postgresql:postgresql")
Expand Down
9 changes: 9 additions & 0 deletions backend/app/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ services:
volumes:
- plugin-database-data:/var/lib/postgres # persist data even if container shuts down

plugin-localstack:
image: localstack/localstack:3.7
container_name: plugin-docker-compose-plugin-localstack
ports:
- "4566:4566"
environment:
SERVICES: s3
AWS_DEFAULT_REGION: eu-west-1

plugin-rabbitmq:
image: rabbitmq:4.1.0-management
container_name: plugin-docker-compose-plugin-rabbitmq
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2026 Ritense BV, the Netherlands.
*
* Licensed under EUPL, Version 1.2 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.ritense.plugin.sandbox

import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.CommandLineRunner
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
import software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider
import software.amazon.awssdk.services.s3.S3Client
import software.amazon.awssdk.services.s3.model.CORSConfiguration
import software.amazon.awssdk.services.s3.model.CORSRule

// Credentials and a bucket for the LocalStack container. A real deployment has both already: dev only.
@Configuration
@Profile("dev")
class LocalStackS3Configuration {
/** LocalStack accepts any credentials; this keeps the SDK from looking for real ones. */
@Bean
fun valtimoAwsCredentialsProviderChain(): AwsCredentialsProviderChain =
AwsCredentialsProviderChain
.builder()
.addCredentialsProvider(
StaticCredentialsProvider.create(AwsBasicCredentials.create("test", "test")),
).build()

/** LocalStack starts empty. The CORS rule is what lets the browser upload with a pre-signed URL. */
@Bean
fun localStackS3BucketInitializer(
s3Client: S3Client,
@Value("\${aws.s3.bucketName}") bucketName: String,
) = CommandLineRunner {
if (s3Client.listBuckets().buckets().none { it.name() == bucketName }) {
s3Client.createBucket { it.bucket(bucketName) }
}
s3Client.putBucketCors { request ->
request
.bucket(bucketName)
.corsConfiguration(
CORSConfiguration
.builder()
.corsRules(
CORSRule
.builder()
.allowedHeaders("*")
.allowedMethods("GET", "PUT", "POST", "DELETE", "HEAD")
.allowedOrigins("*")
.exposeHeaders("ETag")
.build(),
).build(),
)
}
}
}
17 changes: 17 additions & 0 deletions backend/app/src/main/resources/config/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ spring:
jackson:
date-format: yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
time-zone: UTC
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: org.postgresql.Driver
Expand Down Expand Up @@ -118,6 +122,19 @@ valtimo:
accepted-packages:
- com.ritense
includeDocumentContentInResponse: true
resource:
s3:
temp-upload-listener:
# Stores a submitted upload in S3 and attaches it to the case.
enabled: true

# Points at the LocalStack container of docker-compose.yml. A real deployment leaves the endpoint empty.
aws:
region: ${AWS_REGION:eu-west-1}
s3:
bucketRegion: ${AWS_S3_BUCKET_REGION:eu-west-1}
bucketName: ${AWS_S3_BUCKET_NAME:publictask-uploads}
endpoint: ${AWS_S3_ENDPOINT:http://localhost:4566}

spring-actuator:
username: admin
Expand Down
Original file line number Diff line number Diff line change
@@ -1,67 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:operaton="http://operaton.org/schema/1.0/bpmn" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.40.0">
<bpmn:process id="create-public-task-url" name="Create Public Task URL" processType="None" isClosed="false" isExecutable="true" operaton:versionTag="CD:example:1.0.0">
<bpmn:sequenceFlow id="Flow_03ct38e" sourceRef="StartEvent_1" targetRef="st-get-assignee-candiate" />
<bpmn:serviceTask id="st-create-public-task-url" name="Create public task URL" implementation="##WebService" camunda:expression="" operaton:asyncAfter="true" operaton:expression="${null}">
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
xmlns:camunda="http://camunda.org/schema/1.0/bpmn"
xmlns:operaton="http://operaton.org/schema/1.0/bpmn" id="Definitions_1"
targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.40.0">
<bpmn:process id="create-public-task-url" name="Create Public Task URL" processType="None" isClosed="false"
isExecutable="true" operaton:versionTag="CD:example:1.0.0">
<bpmn:sequenceFlow id="Flow_03ct38e" sourceRef="StartEvent_1" targetRef="st-get-assignee-candiate"/>
<bpmn:serviceTask id="st-create-public-task-url" name="Create public task URL" implementation="##WebService"
camunda:expression="" operaton:asyncAfter="true" operaton:expression="${null}">
<bpmn:incoming>Flow_0gwjvqr</bpmn:incoming>
<bpmn:outgoing>Flow_19ga70g</bpmn:outgoing>
</bpmn:serviceTask>
<bpmn:task id="Activity_0ckkujf" />
<bpmn:sequenceFlow id="Flow_19ga70g" sourceRef="st-create-public-task-url" targetRef="ut-communicate-public-task-url" />
<bpmn:task id="Activity_0ckkujf"/>
<bpmn:sequenceFlow id="Flow_19ga70g" sourceRef="st-create-public-task-url"
targetRef="ut-communicate-public-task-url"/>
<bpmn:endEvent id="Event_022xmo8">
<bpmn:incoming>Flow_1lf6z8q</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_1lf6z8q" sourceRef="ut-communicate-public-task-url" targetRef="Event_022xmo8" />
<bpmn:userTask id="ut-communicate-public-task-url" name="Communicate Public Task URL" implementation="##unspecified">
<bpmn:sequenceFlow id="Flow_1lf6z8q" sourceRef="ut-communicate-public-task-url" targetRef="Event_022xmo8"/>
<bpmn:userTask id="ut-communicate-public-task-url" name="Communicate Public Task URL"
implementation="##unspecified">
<bpmn:incoming>Flow_19ga70g</bpmn:incoming>
<bpmn:outgoing>Flow_1lf6z8q</bpmn:outgoing>
</bpmn:userTask>
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_03ct38e</bpmn:outgoing>
<bpmn:messageEventDefinition id="MessageEventDefinition_10dm0zn" messageRef="Message_2g87hsd" />
<bpmn:messageEventDefinition id="MessageEventDefinition_10dm0zn" messageRef="Message_2g87hsd"/>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_0gwjvqr" sourceRef="st-get-assignee-candiate" targetRef="st-create-public-task-url" />
<bpmn:serviceTask id="st-get-assignee-candiate" name="Get assignee candidate" implementation="##unspecified" camunda:expression="${execution.setVariable(&#39;assigneeCandidate&#39;, valueResolverDelegateService.resolveValue(execution, &#39;doc:publicTaskAssignee&#39;))}">
<bpmn:sequenceFlow id="Flow_0gwjvqr" sourceRef="st-get-assignee-candiate" targetRef="st-create-public-task-url"/>
<bpmn:serviceTask id="st-get-assignee-candiate" name="Get assignee candidate" implementation="##unspecified"
camunda:expression="${execution.setVariable(&#39;assigneeCandidate&#39;, valueResolverDelegateService.resolveValue(execution, &#39;doc:publicTaskAssignee&#39;))}">
<bpmn:incoming>Flow_03ct38e</bpmn:incoming>
<bpmn:outgoing>Flow_0gwjvqr</bpmn:outgoing>
</bpmn:serviceTask>
</bpmn:process>
<bpmn:message id="Message_2g87hsd" name="startNotifyAssigneeMessage" />
<bpmn:message id="Message_2g87hsd" name="startNotifyAssigneeMessage"/>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="create-public-task-url">
<bpmndi:BPMNShape id="Activity_1ubur3d_di" bpmnElement="st-create-public-task-url">
<dc:Bounds x="360" y="80" width="100" height="80" />
<bpmndi:BPMNLabel />
<dc:Bounds x="360" y="80" width="100" height="80"/>
<bpmndi:BPMNLabel/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_0q634cj_di" bpmnElement="StartEvent_1">
<dc:Bounds x="132" y="102" width="36" height="36" />
<dc:Bounds x="132" y="102" width="36" height="36"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0thshwr_di" bpmnElement="st-get-assignee-candiate">
<dc:Bounds x="220" y="80" width="100" height="80" />
<bpmndi:BPMNLabel />
<dc:Bounds x="220" y="80" width="100" height="80"/>
<bpmndi:BPMNLabel/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_07n0dto_di" bpmnElement="ut-communicate-public-task-url">
<dc:Bounds x="510" y="80" width="100" height="80" />
<bpmndi:BPMNLabel />
<dc:Bounds x="510" y="80" width="100" height="80"/>
<bpmndi:BPMNLabel/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_022xmo8_di" bpmnElement="Event_022xmo8">
<dc:Bounds x="662" y="102" width="36" height="36" />
<dc:Bounds x="662" y="102" width="36" height="36"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_03ct38e_di" bpmnElement="Flow_03ct38e">
<di:waypoint x="168" y="120" />
<di:waypoint x="220" y="120" />
<di:waypoint x="168" y="120"/>
<di:waypoint x="220" y="120"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_19ga70g_di" bpmnElement="Flow_19ga70g">
<di:waypoint x="460" y="120" />
<di:waypoint x="510" y="120" />
<di:waypoint x="460" y="120"/>
<di:waypoint x="510" y="120"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1lf6z8q_di" bpmnElement="Flow_1lf6z8q">
<di:waypoint x="610" y="120" />
<di:waypoint x="662" y="120" />
<di:waypoint x="610" y="120"/>
<di:waypoint x="662" y="120"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0gwjvqr_di" bpmnElement="Flow_0gwjvqr">
<di:waypoint x="320" y="120" />
<di:waypoint x="360" y="120" />
<di:waypoint x="320" y="120"/>
<di:waypoint x="360" y="120"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,82 +1,90 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:operaton="http://operaton.org/schema/1.0/bpmn" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" typeLanguage="http://www.w3.org/2001/XMLSchema">

<bpmn:process id="public-task" isClosed="false" isExecutable="true" name="Publieke taak" operaton:versionTag="CD:example:1.0.0" processType="None">

<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:operaton="http://operaton.org/schema/1.0/bpmn"
expressionLanguage="http://www.w3.org/1999/XPath" id="Definitions_1"
targetNamespace="http://bpmn.io/schema/bpmn" typeLanguage="http://www.w3.org/2001/XMLSchema">

<bpmn:process id="public-task" isClosed="false" isExecutable="true" name="Publieke taak"
operaton:versionTag="CD:example:1.0.0" processType="None">

<bpmn:startEvent id="StartEvent_1" isInterrupting="true" parallelMultiple="false">

<bpmn:outgoing>Flow_1io4evr</bpmn:outgoing>

</bpmn:startEvent>

<bpmn:sequenceFlow id="Flow_1io4evr" sourceRef="StartEvent_1" targetRef="ut-public-task"/>

<bpmn:endEvent id="Event_1hkudac">

<bpmn:incoming>Flow_12em9k5</bpmn:incoming>

</bpmn:endEvent>

<bpmn:sequenceFlow id="Flow_12em9k5" sourceRef="ut-public-task" targetRef="Event_1hkudac"/>

<bpmn:userTask completionQuantity="1" id="ut-public-task" implementation="##unspecified" isForCompensation="false" name="Public Task" startQuantity="1">


<bpmn:userTask completionQuantity="1" id="ut-public-task" implementation="##unspecified" isForCompensation="false"
name="Public Task" startQuantity="1">

<bpmn:extensionElements>

<camunda:taskListener event="create" expression="${publicTaskService.startNotifyAssigneeCandidateProcess(task)}"/>


<camunda:taskListener event="create"
expression="${publicTaskService.startNotifyAssigneeCandidateProcess(task)}"/>

</bpmn:extensionElements>

<bpmn:incoming>Flow_1io4evr</bpmn:incoming>

<bpmn:outgoing>Flow_12em9k5</bpmn:outgoing>

</bpmn:userTask>

</bpmn:process>

<bpmndi:BPMNDiagram id="BPMNDiagram_1">

<bpmndi:BPMNPlane bpmnElement="public-task" id="BPMNPlane_1">

<bpmndi:BPMNShape bpmnElement="StartEvent_1" id="_BPMNShape_StartEvent_2">

<dc:Bounds height="36" width="36" x="173" y="102"/>

</bpmndi:BPMNShape>

<bpmndi:BPMNShape bpmnElement="Event_1hkudac" id="Event_1hkudac_di">

<dc:Bounds height="36" width="36" x="412" y="102"/>

</bpmndi:BPMNShape>

<bpmndi:BPMNShape bpmnElement="ut-public-task" id="Activity_0bkn31k_di">

<dc:Bounds height="80" width="100" x="260" y="80"/>

<bpmndi:BPMNLabel/>

</bpmndi:BPMNShape>

<bpmndi:BPMNEdge bpmnElement="Flow_1io4evr" id="Flow_1io4evr_di">

<di:waypoint x="209" y="120"/>

<di:waypoint x="260" y="120"/>

</bpmndi:BPMNEdge>

<bpmndi:BPMNEdge bpmnElement="Flow_12em9k5" id="Flow_12em9k5_di">

<di:waypoint x="360" y="120"/>

<di:waypoint x="412" y="120"/>

</bpmndi:BPMNEdge>

</bpmndi:BPMNPlane>

</bpmndi:BPMNDiagram>

</bpmn:definitions>
Loading
Loading