Repository: CSCfi/rems
Pull request #3165 (2023-06-16 12:13:46)

Anonymize handling workflow
relates #3130 

Add new option :anonymize-handling to workflow. When enabled, `handling users` are anonymized `for applying users` in:
- application events and attachments
- event emails.

Anonymized user is replaced with "rems-handler" and name/email is set to nil. Other attributes are omitted.

Events contain :event/actor (userid) and :event/actor-attributes, attachments contain :attachment/user. Certain event emails to applying users contain event actor that may be a handling user.

Anonymized userid can be localized in user interface and event emails with :t.roles/anonymized-handler, which uses :t.roles/handler. For example the english default translation is "Handler", and can be safely overridden in extra translations without overriding :t.roles/handler.

Other notable changes:
- rems.application.model/censor-user now returns only `[:userid :name :email]` instead of `[:userid :name :email :organizations :notification-email]`
- updated lambdaisland/kaocha to `1.86.1355` (originally thought something was broken with earlier version, but was actually a case of PEBKAC. however newer version did not break anything so updated anyway)
- workflow type field now has label in admin page.

# Checklist for author

Remove items that aren't applicable, check items that are done.

## Reviewability
- [x] Link to issue
- [x] Consider adding screenshots for ease of review

## Backwards compatibility
- [x] API is backwards compatible
- [x] Feature appears correctly in PDF print

## Documentation
- [x] Update changelog if necessary
- [ ] Update manual/ (if applicable) (owner manual perhaps?) #3197

## Testing
- [x] Complex logic is unit tested
- [x] Valuable features are integration / browser / acceptance tested automatically

## Screenshots

### Admin

![Screenshot 2023-10-13 at 14 58 14](https://github.com/CSCfi/rems/assets/794087/482643c7-d5be-4d3c-b740-9f7f36bafb2f)

<img width="1030" alt="Screenshot 2023-09-19 at 23 57 07" src="https://github.com/CSCfi/rems/assets/794087/d71ebc8f-7665-40b9-aad6-11c9ba8cd0f3">

### Application as handler
<img width="923" alt="Screenshot 2023-09-20 at 0 07 58" src="https://github.com/CSCfi/rems/assets/794087/7d7b9bc9-3035-401f-b53d-5c28e54602b3">

### PDF as handler
<img width="480" alt="Screenshot 2023-09-20 at 0 02 31" src="https://github.com/CSCfi/rems/assets/794087/e6aed473-e95f-46ec-83d4-0d625d3d3b65">

### Application as applicant
<img width="927" alt="Screenshot 2023-09-20 at 0 07 29" src="https://github.com/CSCfi/rems/assets/794087/3a987f46-adfa-4a6f-8852-d5c9ebbfa6e6">

### PDF as applicant
<img width="446" alt="Screenshot 2023-09-20 at 0 02 10" src="https://github.com/CSCfi/rems/assets/794087/00d13c4f-7b4a-4a40-9955-190c4d6e9997">



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dbb15702e..51487090b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,7 @@ Changes since v2.33
 - Cache reloading can be configured using the new `:buzy-hours` config. REMS will then try to avoid reloading during the specified time spans. (#3194)
 - The handling users can now see whether an event is shown to the applicant from a small eye icon in the event history. (#3156)
 - Event now shows which attachments were redacted when viewing as handling user. (#3190)
+- Workflow has new option to anonymize handling users. When enabled, applying users may only see "Handler" in events where handling user's name would appear. Event emails to applying users similarly show only anonymized name where handling user's name would appear. Application API also returns only anonymized name to applying users. Anonymized name is customizable with translation key `:t.roles/anonymous-handler`, which defaults to `:t.roles/handler`.
 
 ### Fixes
 - Email template parameters for `:application-expiration-notification` event are now documented. The parameters are different from standard event email parameters, which may have caused confusion.
diff --git a/project.clj b/project.clj
index 2e803fc9b..ce208d5de 100644
--- a/project.clj
+++ b/project.clj
@@ -119,7 +119,7 @@
    :project/dev {:dependencies [[binaryage/devtools "1.0.6"]
                                 [com.clojure-goes-fast/clj-memory-meter "0.2.1"]
                                 [criterium "0.4.6"]
-                                [lambdaisland/kaocha "1.80.1274"]
+                                [lambdaisland/kaocha "1.86.1355"]
                                 [lambdaisland/kaocha-junit-xml "1.17.101"]
                                 [etaoin "1.0.39"]
                                 [ring/ring-mock "0.4.0" :exclusions [cheshire]]
diff --git a/resources/translations/da.edn b/resources/translations/da.edn
index 58cdd76ef..21284e9c4 100644
--- a/resources/translations/da.edn
+++ b/resources/translations/da.edn
@@ -77,6 +77,7 @@
                    :add "Tilføj"
                    :added-at "Tilføjet"
                    :added-by "Tilføjet af"
+                   :anonymize-handling nil
                    :application-state "Ansøgningsstatus"
                    :archive "Arkiver"
                    :back "Tilbage"
@@ -139,6 +140,7 @@
                    :name "Navn"
                    :no-categories "Ingen kategorier"
                    :no-form "Ingen blanket"
+                   :no-forms nil
                    :no-licenses "Ingen brugsvilkår"
                    :no-voting "Ingen afstemning"
                    :optional "(valgfrit)"
@@ -167,7 +169,9 @@
                    :voting "Afstemning"
                    :workflow "Arbejdsgang"
                    :workflows "Arbejdsgange"
-                   :workflow-disabled-commands-explanation [:p "Denne arbejdsgang deaktiverer funktioner for brugere når vilkår er opfyldt. Funktioner, ansøgningsstatus og brugerroller kan findes på " [:a {:href "https://github.com/CSCfi/rems/blob/master/docs/application-permissions.md" :target :_blank} "REMS Github-side."]]}
+                   :workflow-anonymize-handling-explanation nil
+                   :workflow-disabled-commands-explanation [:p "Denne arbejdsgang deaktiverer funktioner for brugere når vilkår er opfyldt. Funktioner, ansøgningsstatus og brugerroller kan findes på " [:a {:href "https://github.com/CSCfi/rems/blob/master/docs/application-permissions.md" :target :_blank} "REMS Github-side."]]
+                   :workflow-type nil}
   :applicant-info {:applicant "Ansøger"
                    :applicants "Ansøgere"
                    :email "E-mail"
@@ -636,7 +640,8 @@
             :settings "Indstillinger"
             :warning-about-missing-email [:<> "Du er nødt til at " [:a {:href "/profile"} "tilføje en e-mailadresse til dine indstillinger"] ", så du kan modtage notifikationer om dine ansøgninger."]
             :your-details "Dine brugerdetaljer"}
-  :roles {:applicant "Ansøger"
+  :roles {:anonymous-handler :da.t.roles/handler
+          :applicant "Ansøger"
           :decider "Beslutningstager"
           :handler "Behandler"
           :member "Deltager"
diff --git a/resources/translations/en.edn b/resources/translations/en.edn
index c42dcf47a..d7527257c 100644
--- a/resources/translations/en.edn
+++ b/resources/translations/en.edn
@@ -77,6 +77,7 @@
                    :add "Add"
                    :added-at "Added at"
                    :added-by "Added by"
+                   :anonymize-handling "Anonymized handling"
                    :application-state "Application state"
                    :archive "Archive"
                    :back "Back"
@@ -139,6 +140,7 @@
                    :name "Name"
                    :no-categories "No categories"
                    :no-form "No form"
+                   :no-forms "No forms"
                    :no-licenses "No licenses"
                    :no-voting "No voting"
                    :optional "(optional)"
@@ -167,7 +169,9 @@
                    :voting "Voting"
                    :workflow "Workflow"
                    :workflows "Workflows"
-                   :workflow-disabled-commands-explanation [:p "This workflow disables actions for users when conditions apply. Actions, application states and user roles can be found on " [:a {:href "https://github.com/CSCfi/rems/blob/master/docs/application-permissions.md" :target :_blank} "REMS Github page."]]}
+                   :workflow-anonymize-handling-explanation "This workflow hides handling users personally identifiable information (e.g. name and email) from applying users."
+                   :workflow-disabled-commands-explanation [:p "This workflow disables actions for users when conditions apply. Actions, application states and user roles can be found on " [:a {:href "https://github.com/CSCfi/rems/blob/master/docs/application-permissions.md" :target :_blank} "REMS Github page."]]
+                   :workflow-type "Workflow type"}
   :applicant-info {:applicant "Applicant"
                    :applicants "Applicants"
                    :email "Email"
@@ -638,7 +642,8 @@
             :settings "Settings"
             :warning-about-missing-email [:<> "You will need to " [:a {:href "/profile"} "add an email address to your settings"] ", so that you can receive notifications about your applications."]
             :your-details "Your user details"}
-  :roles {:applicant "Applicant"
+  :roles {:anonymous-handler :en.t.roles/handler ; use handler as default translation for workflow anonymized handlers
+          :applicant "Applicant"
           :decider "Decider"
           :handler "Handler"
           :member "Member"
diff --git a/resources/translations/fi.edn b/resources/translations/fi.edn
index 05aa4b3ff..d2b65803c 100644
--- a/resources/translations/fi.edn
+++ b/resources/translations/fi.edn
@@ -75,6 +75,7 @@
                    :add "Lisää"
                    :added-at "Lisätty"
                    :added-by "Lisääjä"
+                   :anonymize-handling "Anonymisoitu käsittely"
                    :application-state "Hakemuksen tila"
                    :archive "Arkistoi"
                    :back "Takaisin"
@@ -136,6 +137,7 @@
                    :name "Nimi"
                    :no-categories "Ei kategorioita"
                    :no-form "Ei lomaketta"
+                   :no-forms "Ei lomakkeita"
                    :no-licenses "Ei lisenssejä"
                    :no-voting "Ei äänestystä"
                    :optional "(valinnainen)"
@@ -163,7 +165,9 @@
                    :voting "Äänestys"
                    :workflow "Työvuo"
                    :workflows "Työvuot"
-                   :workflow-disabled-commands-explanation [:p "Tämä työvuo poistaa toimintoja käyttäjiltä ehtojen täyttyessä. Toiminnot, hakemusten tilat ja käyttäjien roolit löytyvät " [:a {:href "https://github.com/CSCfi/rems/blob/master/docs/application-permissions.md" :target :_blank} "REMSin Github-sivulta."]]}
+                   :workflow-anonymize-handling-explanation "Tämä työvuo piilottaa käsittelijöiden henkilökohtaiset tunnistetiedot (kuten nimen ja sähköpostiosoitteen) hakijoilta."
+                   :workflow-disabled-commands-explanation [:p "Tämä työvuo poistaa toimintoja käyttäjiltä ehtojen täyttyessä. Toiminnot, hakemusten tilat ja käyttäjien roolit löytyvät " [:a {:href "https://github.com/CSCfi/rems/blob/master/docs/application-permissions.md" :target :_blank} "REMSin Github-sivulta."]]
+                   :workflow-type "Työvuon tyyppi"}
   :applicant-info {:applicant "Hakija"
                    :applicants "Hakijat"
                    :email "Sähköposti"
@@ -589,7 +593,8 @@
             :settings "Asetukset"
             :warning-about-missing-email [:<> "Sinun on " [:a {:href "/profile"} "lisättävä sähköpostiosoite asetuksiisi"] ", jotta voisit vastaanottaa ilmoituksia hakemuksistasi."]
             :your-details "Käyttäjätietosi"}
-  :roles {:applicant "Hakija"
+  :roles {:anonymous-handler :fi.t.roles/handler
+          :applicant "Hakija"
           :decider "Päättäjä"
           :handler "Käsittelijä"
           :member "Jäsen"
diff --git a/resources/translations/sv.edn b/resources/translations/sv.edn
index 4731fb2a3..aea323879 100644
--- a/resources/translations/sv.edn
+++ b/resources/translations/sv.edn
@@ -75,6 +75,7 @@
                    :add "Lägg till"
                    :added-at "Tillagd"
                    :added-by "Tillagd av"
+                   :anonymize-handling "Anonymiserad hantering"
                    :application-state "Ansökningsstatus"
                    :archive "Arkivera"
                    :back "Tillbaka"
@@ -136,6 +137,7 @@
                    :name "Namn"
                    :no-categories "Inga kategorier"
                    :no-form "Ingen blankett"
+                   :no-forms "Inga blanketter"
                    :no-licenses "Inga licenser"
                    :no-voting "Inga omröstning"
                    :optional "(valfritt)"
@@ -163,7 +165,9 @@
                    :voting "Omröstning"
                    :workflow "Arbetsflöde"
                    :workflows "Arbetsflöden"
-                   :workflow-disabled-commands-explanation [:p "Detta arbetsflöde avaktiverar funktioner för användarna när villkoren uppfylls. Funktioner, ansökningar statusar och användarroller hittas på " [:a {:href "https://github.com/CSCfi/rems/blob/master/docs/application-permissions.md" :target :_blank} "REMS Github-sidan."]]}
+                   :workflow-anonymize-handling-explanation "Detta arbetsflöde döljer handläggarens personligt identifierbar information (t.ex. namn och e-postaddress) för sökande."
+                   :workflow-disabled-commands-explanation [:p "Detta arbetsflöde avaktiverar funktioner för användarna när villkoren uppfylls. Funktioner, ansökningar statusar och användarroller hittas på " [:a {:href "https://github.com/CSCfi/rems/blob/master/docs/application-permissions.md" :target :_blank} "REMS Github-sidan."]]
+                   :workflow-type "Arbetsflödets typ"}
   :applicant-info {:applicant "Sökande"
                    :applicants "Sökande"
                    :email "E-postadress"
@@ -589,7 +593,8 @@
             :settings "Inställningar"
             :warning-about-missing-email [:<> "Du måste " [:a {:href "/profile"} "lägga till din e-postadress i dina inställningar"] ", för att kunna ta emot meddelanden om din ansökan."]
             :your-details "Dina användardetaljer"} ; TODO check
-  :roles {:applicant "Sökande"
+  :roles {:anonymous-handler :sv.t.roles/handler
+          :applicant "Sökande"
           :decider "Beslutare"
           :handler "Handläggare"
           :member "Deltagare"
diff --git a/src/clj/rems/api/schema.clj b/src/clj/rems/api/schema.clj
index db0b2573f..3ff7d9301 100644
--- a/src/clj/rems/api/schema.clj
+++ b/src/clj/rems/api/schema.clj
@@ -258,7 +258,8 @@
    :application/workflow {:workflow/id s/Int
                           :workflow/type s/Keyword
                           (s/optional-key :workflow.dynamic/handlers) [Handler]
-                          (s/optional-key :workflow/voting) schema-base/WorkflowVoting}
+                          (s/optional-key :workflow/voting) schema-base/WorkflowVoting
+                          (s/optional-key :workflow/anonymize-handling) s/Bool}
    :application/roles #{s/Keyword}
    :application/permissions Permissions
    :application/attachments [ApplicationAttachment]
diff --git a/src/clj/rems/api/workflows.clj b/src/clj/rems/api/workflows.clj
index 75f53b0e8..3d9357887 100644
--- a/src/clj/rems/api/workflows.clj
+++ b/src/clj/rems/api/workflows.clj
@@ -6,7 +6,7 @@
             [rems.application.events :as events]
             [rems.common.roles :refer [+admin-read-roles+ +admin-write-roles+]]
             [rems.schema-base :as schema-base]
-            [ring.util.http-response :refer :all]
+            [ring.util.http-response :refer [ok]]
             [schema.core :as s]))
 
 (s/defschema CreateWorkflowCommand
@@ -17,7 +17,8 @@
    (s/optional-key :handlers) [schema-base/UserId]
    (s/optional-key :licenses) [schema-base/LicenseId]
    (s/optional-key :disable-commands) [schema-base/DisableCommandRule]
-   (s/optional-key :voting) (s/maybe schema-base/WorkflowVoting)})
+   (s/optional-key :voting) (s/maybe schema-base/WorkflowVoting)
+   (s/optional-key :anonymize-handling) (s/maybe s/Bool)})
 
 (s/defschema EditWorkflowCommand
   {:id s/Int
@@ -26,7 +27,8 @@
    (s/optional-key :title) s/Str
    (s/optional-key :handlers) [schema-base/UserId]
    (s/optional-key :disable-commands) [schema-base/DisableCommandRule]
-   (s/optional-key :voting) (s/maybe schema-base/WorkflowVoting)})
+   (s/optional-key :voting) (s/maybe schema-base/WorkflowVoting)
+   (s/optional-key :anonymize-handling) (s/maybe s/Bool)})
 
 (s/defschema CreateWorkflowResponse
   {:success s/Bool
diff --git a/src/clj/rems/application/model.clj b/src/clj/rems/application/model.clj
index d70e57040..dffdc19d5 100644
--- a/src/clj/rems/application/model.clj
+++ b/src/clj/rems/application/model.clj
@@ -2,11 +2,12 @@
   (:require [clojure.set :as set]
             [clojure.test :refer [deftest is testing]]
             [com.rpl.specter :refer [ALL transform select]]
-            [medley.core :refer [distinct-by find-first map-vals update-existing update-existing-in]]
+            [medley.core :refer [dissoc-in distinct-by filter-vals find-first map-vals update-existing update-existing-in]]
             [rems.application.events :as events]
             [rems.application.master-workflow :as master-workflow]
             [rems.common.application-util :refer [applicant-and-members can-redact-attachment? is-applying-user?]]
             [rems.common.form :as form]
+            [rems.common.roles :refer [+handling-roles+]]
             [rems.common.util :refer [assoc-some-in conj-vec getx getx-in into-vec]]
             [rems.permissions :as permissions]
             [rems.json :as json]
@@ -51,8 +52,7 @@
   (-> application
       (assoc :application/modified (:event/time event))
       (assoc ::draft-answers (:application/field-values event))
-      (assoc-some-in [:application/duo :duo/codes] (when (:enable-duo rems.config/env)
-                                                     (:application/duo-codes event)))))
+      (assoc-some-in [:application/duo :duo/codes] (:application/duo-codes event))))
 
 (defmethod application-base-view :application.event/licenses-accepted
   [application event]
@@ -511,8 +511,8 @@
                                               :duo/restrictions (:restrictions dataset-code)}]
                nil)}))
 
-(defn- enrich-application-duo-matches [application]
-  (if-not (:enable-duo rems.config/env)
+(defn- enrich-application-duo-matches [application get-config]
+  (if-not (:enable-duo (get-config))
     application
     (let [duos (->> application :application/duo :duo/codes)
           matches (for [resource (:application/resources application)
@@ -789,6 +789,13 @@
        (transform [:application/attachments ALL] #(update % :attachment/user get-user))
        (transform [:application/attachments ALL] #(assoc % :attachment/redact-roles (allowed-redact-roles application %)))))
 
+(defn- enrich-workflow-anonymize-handling [application get-workflow]
+  (let [workflow-id (get-in application [:application/workflow :workflow/id])
+        workflow (get-workflow workflow-id)
+        anonymize-handling (get-in workflow [:workflow :anonymize-handling])]
+    (-> application
+        (assoc-some-in [:application/workflow :workflow/anonymize-handling] anonymize-handling))))
+
 (defn enrich-with-injections
   [application {:keys [blacklisted?
                        get-form-template
@@ -806,7 +813,7 @@
       enrich-field-visible ; uses enriched answers
       set-application-description ; uses enriched answers
       (update :application/resources enrich-resources get-catalogue-item)
-      enrich-application-duo-matches ; uses enriched resources
+      (enrich-application-duo-matches get-config) ; uses enriched resources
       (update-existing-in [:application/duo :duo/codes] duo/enrich-duo-codes)
       (enrich-workflow-licenses get-workflow)
       (update :application/licenses enrich-licenses get-license)
@@ -816,6 +823,7 @@
       (enrich-user-attributes get-user)
       (enrich-blacklist blacklisted?) ; uses enriched users
       (enrich-workflow-handlers get-workflow)
+      (enrich-workflow-anonymize-handling get-workflow)
       (enrich-deadline get-config)
       (enrich-super-users get-users-with-role)
       (enrich-workflow-disable-commands get-config get-workflow)
@@ -833,7 +841,7 @@
        (filterv #(= :visibility/public (:event/visibility %)))))
 
 (defn- censor-user [user]
-  (select-keys user [:userid :name :email :organizations :notification-email]))
+  (select-keys user [:userid :name :email]))
 
 (defn- censor-users-in-event [event]
   (-> event
@@ -857,13 +865,52 @@
       (update :application/events (partial mapv censor-users-in-event))
       (update :application/attachments (partial mapv #(update % :attachment/user censor-user)))))
 
+(def anonymous-handler {:userid "rems-handler"
+                        :name nil
+                        :email nil})
+
+(defn anonymize-users-in-attachments [attachments userids]
+  (vec
+   (for [attachment attachments
+         :let [userid (get-in attachment [:attachment/user :userid])]]
+     (if (some #{userid} userids)
+       (assoc attachment :attachment/user anonymous-handler)
+       attachment))))
+
+(defn anonymize-users-in-events [events userids]
+  (vec
+   (for [event events
+         :let [userid (:event/actor event)]]
+     (if (some #{userid} userids)
+       (-> event
+           (assoc :event/actor (:userid anonymous-handler))
+           (assoc :event/actor-attributes anonymous-handler))
+       event))))
+
+(defn get-handling-users [application]
+  (let [get-handling-roles #(clojure.set/intersection +handling-roles+ (set %))]
+    (set
+     (some->> (:application/user-roles application)
+              (filter-vals (comp seq get-handling-roles))
+              keys))))
+
+(defn apply-workflow-anonymization [application]
+  (if (get-in application [:application/workflow :workflow/anonymize-handling])
+    (let [handling-users (get-handling-users application)]
+      (-> application
+          (update :application/attachments anonymize-users-in-attachments handling-users)
+          (update :application/events anonymize-users-in-events handling-users)))
+    application))
+
 (defn- hide-sensitive-information [application]
   (-> application
       (dissoc :application/blacklist)
-      (update :application/events hide-sensitive-events) ; uses :event/public :event/visibility
+      (update :application/events hide-sensitive-events)
       (update :application/events (partial mapv #(dissoc % :event/public :event/visibility)))
-      (update :application/workflow dissoc :workflow.dynamic/handlers)
-      (update :application/workflow dissoc :workflow/voting)
+      apply-workflow-anonymization
+      (dissoc-in [:application/workflow :workflow.dynamic/handlers]
+                 [:application/workflow :workflow/voting]
+                 [:application/workflow :workflow/anonymize-handling])
       (dissoc :application/votes)
       hide-extra-user-attributes))
 
diff --git a/src/clj/rems/db/workflow.clj b/src/clj/rems/db/workflow.clj
index a587cbdb1..36fec7058 100644
--- a/src/clj/rems/db/workflow.clj
+++ b/src/clj/rems/db/workflow.clj
@@ -15,7 +15,8 @@
    (s/optional-key :forms) [{:form/id s/Num}]
    (s/optional-key :licenses) [s/Int]
    (s/optional-key :disable-commands) [schema-base/DisableCommandRule]
-   (s/optional-key :voting) (s/maybe schema-base/WorkflowVoting)})
+   (s/optional-key :voting) (s/maybe schema-base/WorkflowVoting)
+   (s/optional-key :anonymize-handling) s/Bool})
 
 (def ^:private coerce-workflow-body
   (coerce/coercer! WorkflowBody coerce/string-coercion-matcher))
@@ -23,13 +24,14 @@
 (def ^:private validate-workflow-body
   (s/validator WorkflowBody))
 
-(defn create-workflow! [{:keys [organization type title handlers forms licenses disable-commands voting]}]
+(defn create-workflow! [{:keys [organization type title handlers forms licenses disable-commands voting anonymize-handling]}]
   (let [body (cond-> {:type type
                       :handlers handlers
                       :forms forms
                       :licenses licenses}
                (seq disable-commands) (assoc :disable-commands disable-commands)
-               voting (assoc :voting voting))]
+               voting (assoc :voting voting)
+               anonymize-handling (assoc :anonymize-handling anonymize-handling))]
     (:id (db/create-workflow! {:organization (:organization/id organization)
                                :title title
                                :workflow (json/generate-string
@@ -62,12 +64,13 @@
       (update-existing-in [:workflow :handlers] #(map :userid %))
       (update-existing-in [:workflow :licenses] #(map :license/id %))))
 
-(defn edit-workflow! [{:keys [id organization title handlers disable-commands voting]}]
+(defn edit-workflow! [{:keys [id organization title handlers disable-commands voting anonymize-handling]}]
   (let [workflow (unrich-workflow (get-workflow id))
         workflow-body (cond-> (:workflow workflow)
                         handlers (assoc :handlers handlers)
                         disable-commands (assoc :disable-commands disable-commands)
-                        voting (assoc :voting voting))]
+                        voting (assoc :voting voting)
+                        (some? anonymize-handling) (assoc :anonymize-handling anonymize-handling))]
     (db/edit-workflow! {:id (or id (:id workflow))
                         :title (or title (:title workflow))
                         :organization (or (:organization/id organization)
diff --git a/src/clj/rems/email/template.clj b/src/clj/rems/email/template.clj
index 31fc7a5c7..466e31805 100644
--- a/src/clj/rems/email/template.clj
+++ b/src/clj/rems/email/template.clj
@@ -1,11 +1,12 @@
 (ns rems.email.template
   (:require [clojure.string :as str]
             [rems.common.application-util :as application-util]
-            [rems.application.model]
+            [rems.application.model :as model]
             [rems.config :refer [env]]
             [rems.context :as context]
             [rems.db.user-settings :as user-settings]
-            [rems.text :refer [localize-utc-date text text-no-fallback text-format with-language]]
+            [rems.permissions :as permissions]
+            [rems.text :refer [localize-user localize-utc-date text text-no-fallback text-format with-language]]
             [rems.util :as util]))
 
 ;;; Mapping events to emails
@@ -41,6 +42,19 @@
 (defn- other-handlers [event application]
   (filter #(not= (:userid %) (:event/actor event)) (handlers application)))
 
+(defn- apply-event-privacy [event application userid]
+  (cond
+    (contains? (permissions/user-permissions application userid) :see-everything)
+    event
+
+    (get-in application [:application/workflow :workflow/anonymize-handling])
+    (-> [event]
+        (model/anonymize-users-in-events (model/get-handling-users application))
+        first)
+
+    :else
+    event))
+
 (defmulti event-to-emails
   (fn [event _application] (:event/type event)))
 
@@ -52,24 +66,25 @@
    (for [recipient recipients
          :let [email (with-language (:language (user-settings/get-user-settings (:userid recipient)))
                        (when (and body-text (not (str/blank? (text-no-fallback body-text))))
-                         {:to-user (:userid recipient)
-                          :subject (text-format subject-text
+                         (let [event (apply-event-privacy event application (:userid recipient))]
+                           {:to-user (:userid recipient)
+                            :subject (text-format subject-text
+                                                  (application-util/get-member-name recipient)
+                                                  (localize-user (:event/actor-attributes event))
+                                                  (format-application-for-email application)
+                                                  (application-util/get-applicant-name application)
+                                                  (resources-for-email application)
+                                                  (link-to-application (:application/id event)))
+                            :body (str
+                                   (text-format body-text
                                                 (application-util/get-member-name recipient)
-                                                (application-util/get-member-name (:event/actor-attributes event))
+                                                (localize-user (:event/actor-attributes event))
                                                 (format-application-for-email application)
                                                 (application-util/get-applicant-name application)
                                                 (resources-for-email application)
                                                 (link-to-application (:application/id event)))
-                          :body (str
-                                 (text-format body-text
-                                              (application-util/get-member-name recipient)
-                                              (application-util/get-member-name (:event/actor-attributes event))
-                                              (format-application-for-email application)
-                                              (application-util/get-applicant-name application)
-                                              (resources-for-email application)
-                                              (link-to-application (:application/id event)))
-                                 (text :t.email/regards)
-                                 (text :t.email/footer))}))]
+                                   (text :t.email/regards)
+                                   (text :t.email/footer))})))]
          :when email]
      email)))
 
@@ -158,7 +173,7 @@
                         :t.email.reviewed/message))
 
 (defmethod event-to-emails :application.event/remarked [event application]
-  (emails-to-recipients (concat (handlers application)
+  (emails-to-recipients (concat (other-handlers event application)
                                 (when (:event/public event)
                                   ;; no need to email members on non-actionable things
                                   [(:application/applicant application)]))
diff --git a/src/clj/rems/service/workflow.clj b/src/clj/rems/service/workflow.clj
index 2774e4df0..d85b384da 100644
--- a/src/clj/rems/service/workflow.clj
+++ b/src/clj/rems/service/workflow.clj
@@ -34,7 +34,7 @@
      :errors [{:type :invalid-disable-commands
                :commands invalid}]}))
 
-(defn create-workflow! [{:keys [organization handlers licenses forms disable-commands] :as cmd}]
+(defn create-workflow! [{:keys [organization handlers licenses forms disable-commands voting anonymize-handling] :as cmd}]
   (util/check-allowed-organization! organization)
   (or (invalid-users-error handlers)
       (invalid-forms-error forms)
@@ -45,7 +45,7 @@
         {:success (not (nil? id))
          :id id})))
 
-(defn edit-workflow! [{:keys [id organization handlers disable-commands] :as cmd}]
+(defn edit-workflow! [{:keys [id organization handlers disable-commands voting anonymize-handling] :as cmd}]
   (let [workflow (workflow/get-workflow id)]
     (util/check-allowed-organization! (:organization workflow))
     (when organization
diff --git a/src/cljc/rems/common/roles.cljc b/src/cljc/rems/common/roles.cljc
index 6c669c31d..e453b3576 100644
--- a/src/cljc/rems/common/roles.cljc
+++ b/src/cljc/rems/common/roles.cljc
@@ -5,6 +5,7 @@
 
 (def +admin-write-roles+ #{:organization-owner :owner})
 (def +admin-read-roles+ #{:owner :organization-owner :handler :reporter})
+(def +handling-roles+ #{:handler :reviewer :decider :past-reviewer :past-decider})
 
 (defn is-logged-in? [roles]
   (some #{:logged-in} roles))
@@ -15,9 +16,6 @@
 (defn show-all-applications? [roles]
   (some #{:reporter} roles))
 
-(defn show-reviews? [roles]
-  (some #{:handler :reviewer :decider :past-reviewer :past-decider} roles))
-
 (defn show-admin-pages? [roles]
   (some +admin-read-roles+ roles))
 
diff --git a/src/cljc/rems/text.cljc b/src/cljc/rems/text.cljc
index 4154ff4ce..eaf405ee2 100644
--- a/src/cljc/rems/text.cljc
+++ b/src/cljc/rems/text.cljc
@@ -182,6 +182,13 @@
    :application.event/submitted :t.applications.events/submitted
    :application.event/voted :t.applications.events/voted})
 
+(defn localize-user
+  "Returns localization for special user if possible. Otherwise returns formatted user."
+  [user]
+  (case (:userid user)
+    "rems-handler" (text :t.roles/anonymous-handler)
+    (application-util/get-member-name user)))
+
 (defn localize-decision [event]
   (when-let [decision (:application/decision event)]
     (text-format
@@ -189,7 +196,7 @@
        :approved :t.applications.events/approved
        :rejected :t.applications.events/rejected
        :t.applications.events/unknown)
-     (application-util/get-member-name (:event/actor-attributes event)))))
+     (localize-user (:event/actor-attributes event)))))
 
 (defn localize-invitation [{:keys [name email]}]
   (str name " <" email ">"))
@@ -199,14 +206,14 @@
     (str
      (text-format
       (get event-types event-type :t.applications.events/unknown)
-      (application-util/get-member-name (:event/actor-attributes event))
+      (localize-user (:event/actor-attributes event))
       (case event-type
         :application.event/review-requested
-        (str/join ", " (mapv application-util/get-member-name
+        (str/join ", " (mapv localize-user
                              (:application/reviewers event)))
 
         :application.event/decision-requested
-        (str/join ", " (mapv application-util/get-member-name
+        (str/join ", " (mapv localize-user
                              (:application/deciders event)))
 
         :application.event/created
@@ -217,10 +224,10 @@
 
         (:application.event/member-added
          :application.event/member-removed)
-        (application-util/get-member-name (:application/member event))
+        (localize-user (:application/member event))
 
         :application.event/applicant-changed
-        (application-util/get-member-name (:application/applicant event))
+        (localize-user (:application/applicant event))
 
         (:application.event/member-invited
          :application.event/member-uninvited)
diff --git a/src/cljs/rems/administration/create_workflow.cljs b/src/cljs/rems/administration/create_workflow.cljs
index f682c943e..1a8573d52 100644
--- a/src/cljs/rems/administration/create_workflow.cljs
+++ b/src/cljs/rems/administration/create_workflow.cljs
@@ -5,7 +5,7 @@
             [rems.administration.administration :as administration]
             [rems.administration.components :refer [organization-field radio-button-group text-field]]
             [rems.administration.items :as items]
-            [rems.atoms :as atoms :refer [enrich-user document-title]]
+            [rems.atoms :as atoms :refer [checkbox enrich-user document-title]]
             [rems.collapsible :as collapsible]
             [rems.config :as config]
             [rems.common.application-util :as application-util]
@@ -55,7 +55,8 @@
                                             :licenses (->> (:licenses workflow)
                                                            (mapv #(replace-key % :license/id :id)))
                                             :disable-commands (get workflow :disable-commands)
-                                            :voting (get workflow :voting)})))
+                                            :voting (get workflow :voting)
+                                            :anonymize-handling (:anonymize-handling workflow)})))
 
 (fetcher/reg-fetcher ::workflow "/api/workflows/:id" {:path-params (fn [db] {:id (::workflow-id db)})
                                                       :on-success #(rf/dispatch [::fetch-workflow-success %])})
@@ -64,6 +65,8 @@
 
 (def workflow-types #{:workflow/default :workflow/decider :workflow/master})
 
+(rf/reg-sub ::workflow-type (fn [db _] (get-in db [::form :type])))
+
 (defn needs-handlers? [type]
   (contains? #{:workflow/default :workflow/decider :workflow/master} type))
 
@@ -87,9 +90,10 @@
                   :disable-commands (->> (:disable-commands form)
                                          (mapv #(update-existing % :when/state vec))
                                          (mapv #(update-existing % :when/role vec)))
-                  :voting (:voting form)}
+                  :voting (:voting form)
+                  :anonymize-handling (:anonymize-handling form false)}
                  (when (needs-handlers? (:type form))
-                   {:handlers (map :userid (:handlers form))}))]
+                   {:handlers (mapv :userid (:handlers form))}))]
     (when (valid-create-request? request)
       request)))
 
@@ -104,11 +108,12 @@
   (let [request {:organization {:organization/id (get-in form [:organization :organization/id])}
                  :id id
                  :title (:title form)
-                 :handlers (map :userid (:handlers form))
+                 :handlers (mapv :userid (:handlers form))
                  :disable-commands (->> (:disable-commands form)
                                         (mapv #(update-existing % :when/state vec))
                                         (mapv #(update-existing % :when/role vec)))
-                 :voting (:voting form)}]
+                 :voting (:voting form)
+                 :anonymize-handling (:anonymize-handling form false)}]
     (when (valid-edit-request? request)
       request)))
 
@@ -155,6 +160,9 @@
                              :application.command/create
                              :application.command/send-expiration-notifications}))))
 
+(rf/reg-sub ::workflow-anonymize-handling (fn [db _] (get-in db [::form :anonymize-handling])))
+(rf/reg-event-db ::toggle-anonymize-handling (fn [db [_]] (update-in db [::form :anonymize-handling] not)))
+
 ;;;; UI
 
 (def ^:private context
@@ -200,18 +208,27 @@
          :on-change #(rf/dispatch [::set-licenses %])}])]))
 
 (defn- workflow-type-field []
-  [radio-button-group context {:id :workflow-type
-                               :keys [:type]
-                               :readonly @(rf/subscribe [::editing?])
-                               :orientation :horizontal
-                               :options (concat
-                                         [{:value :workflow/default
-                                           :label (text :t.create-workflow/default-workflow)}
-                                          {:value :workflow/decider
-                                           :label (text :t.create-workflow/decider-workflow)}]
-                                         (when (config/dev-environment?)
-                                           [{:value :workflow/master
-                                             :label (text :t.create-workflow/master-workflow)}]))}])
+  [:div.form-group
+   [:label.administration-field-label {:for :workflow-type}
+    (text :t.administration/workflow-type)]
+   [radio-button-group context
+    {:id :workflow-type
+     :keys [:type]
+     :readonly @(rf/subscribe [::editing?])
+     :orientation :horizontal
+     :options (concat
+               [{:value :workflow/default
+                 :label (text :t.create-workflow/default-workflow)}
+                {:value :workflow/decider
+                 :label (text :t.create-workflow/decider-workflow)}]
+               (when (config/dev-environment?)
+                 [{:value :workflow/master
+                   :label (text :t.create-workflow/master-workflow)}]))}]
+   [:div.alert.alert-info
+    (case @(rf/subscribe [::workflow-type])
+      :workflow/default (text :t.create-workflow/default-workflow-description)
+      :workflow/decider (text :t.create-workflow/decider-workflow-description)
+      :workflow/master (text :t.create-workflow/master-workflow-description))]])
 
 (defn- save-workflow-button []
   (let [form @(rf/subscribe [::form])
@@ -235,9 +252,6 @@
    (str "/administration/workflows" (andstr "/" @(rf/subscribe [::workflow-id])))
    (text :t.administration/cancel)])
 
-(defn workflow-type-description [description]
-  [:div.alert.alert-info description])
-
 (defn- create-rule [{:keys [db]} [_]]
   {:db (update-in db [::form :disable-commands] conj-vec {})
    :rems.focus/scroll-into-view [".disable-commands" {:block :end}]})
@@ -400,28 +414,22 @@
          :disabled? @(rf/subscribe [::editing?])
          :on-change #(rf/dispatch [::set-forms %])}])]))
 
-(defn default-workflow-form []
-  [:div
-   [workflow-type-description (text :t.create-workflow/default-workflow-description)]
-   [workflow-handlers-field]
-   [workflow-forms-field]])
-
-(defn decider-workflow-form []
-  [:div
-   [workflow-type-description (text :t.create-workflow/decider-workflow-description)]
-   [workflow-handlers-field]
-   [workflow-forms-field]])
-
-(defn master-workflow-form []
-  [:div
-   [workflow-type-description (text :t.create-workflow/master-workflow-description)]
-   [workflow-handlers-field]
-   [workflow-forms-field]])
+(defn- workflow-anonymize-handling-field []
+  (let [anonymize-handling @(rf/subscribe [::workflow-anonymize-handling])
+        on-change #(rf/dispatch [::toggle-anonymize-handling])]
+    [:div.form-group.anonymize-handling
+     [:div.form-group.form-check.form-check-inline.pointer
+      [checkbox {:id :anonymize-handling
+                 :class :form-check-input
+                 :value anonymize-handling
+                 :on-change on-change}]
+      [:label.form-check-label {:for :anonymize-handling :on-click on-change}
+       (text :t.administration/anonymize-handling)]]
+     (when anonymize-handling
+       [:div.alert.alert-info (text :t.administration/workflow-anonymize-handling-explanation)])]))
 
 (defn create-workflow-page []
   (let [config @(rf/subscribe [:rems.config/config])
-        form @(rf/subscribe [::form])
-        workflow-type (:type form)
         loading? (or @(rf/subscribe [::actors :fetching?])
                      @(rf/subscribe [::workflow :fetching?]))
         editing? @(rf/subscribe [::editing?])
@@ -440,15 +448,11 @@
                  [:div#workflow-editor.fields
                   [workflow-organization-field]
                   [workflow-title-field]
-
                   [workflow-type-field]
-                  (case workflow-type
-                    :workflow/default [default-workflow-form]
-                    :workflow/decider [decider-workflow-form]
-                    :workflow/master [master-workflow-form])
-
+                  [workflow-anonymize-handling-field]
+                  [workflow-handlers-field]
+                  [workflow-forms-field]
                   [workflow-licenses-field]
-
                   ;; optional extra stuff
                   ;; XXX: could use collapsible too
                   [:div.spaced-vertically-5.mt-5
diff --git a/src/cljs/rems/administration/workflow.cljs b/src/cljs/rems/administration/workflow.cljs
index 72481f408..ee73b6c03 100644
--- a/src/cljs/rems/administration/workflow.cljs
+++ b/src/cljs/rems/administration/workflow.cljs
@@ -110,6 +110,8 @@
     {:id "workflow"
      :title [:span (andstr (get-in workflow [:organization :organization/short-name language]) "/") (:title workflow)]
      :always [:div
+              (when (get-in workflow [:workflow :anonymize-handling])
+                [:div.alert.alert-info (text :t.administration/workflow-anonymize-handling-explanation)])
               [inline-info-field (text :t.administration/organization) (get-in workflow [:organization :organization/name language])]
               [inline-info-field (text :t.administration/title) (:title workflow)]
               [inline-info-field (text :t.administration/type) (text (get workflow-types
@@ -119,26 +121,27 @@
                                                                          (map enrich-user)
                                                                          (map :display)
                                                                          (str/join ", "))]
+              (when (get-in workflow [:workflow :anonymize-handling])
+                [inline-info-field (text :t.administration/anonymize-handling) [readonly-checkbox {:value true}]])
               [inline-info-field (text :t.administration/active) [readonly-checkbox {:value (status-flags/active? workflow)}]]
               [inline-info-field (text :t.administration/forms)
-               (->> (for [form (get-in workflow [:workflow :forms])
-                          :let [uri (str "/administration/forms/" (:form/id form))
-                                title (:form/internal-name form)]]
-                      [atoms/link nil uri title])
-                    (interpose ", ")
-                    (into [:<>]))]
+               (if-some [forms (seq (for [form (get-in workflow [:workflow :forms])
+                                          :let [uri (str "/administration/forms/" (:form/id form))
+                                                title (:form/internal-name form)]]
+                                      [atoms/link nil uri title]))]
+                 (into [:<>] (interpose ", ") forms)
+                 (text :t.administration/no-forms))]
               [inline-info-field (text :t.administration/licenses)
-               (->> (for [license (get-in workflow [:workflow :licenses])
-                          :let [uri (str "/administration/licenses/" (:license/id license))
-                                title (:title (localized (:localizations license)))]]
-                      [atoms/link nil uri title])
-                    (interpose ", ")
-                    (into [:<>]))]
+               (if-some [licenses (seq (for [license (get-in workflow [:workflow :licenses])
+                                             :let [uri (str "/administration/licenses/" (:license/id license))
+                                                   title (:title (localized (:localizations license)))]]
+                                         [atoms/link nil uri title]))]
+                 (into [:<>] (interpose ", ") licenses)
+                 (text :t.administration/no-licenses))]
               (when-let [voting (get-in workflow [:workflow :voting])]
                 [inline-info-field
                  (text :t.administration/voting)
                  (text (keyword (str "t" ".administration") (:type voting)))])]}]
-
    (when (seq (get-in workflow [:workflow :disable-commands]))
      [collapsible/component
       {:id "workflow-disabled-commands"
diff --git a/src/cljs/rems/application.cljs b/src/cljs/rems/application.cljs
index d56b14b87..bd4a41181 100644
--- a/src/cljs/rems/application.cljs
+++ b/src/cljs/rems/application.cljs
@@ -714,7 +714,7 @@
       [:div.event-description.col-form-label
        [event-description event]]
       (when decision
-        [:div.event-decision decision])
+        [:div.event-decision.mb-2 decision])
       (when comment
         [:div.form-control.event-comment comment])
       [event-attachments attachments redacted-attachments]]]))
@@ -1576,6 +1576,11 @@
                                 :event/type :application.event/remarked
                                 :event/actor-attributes {:name "Hannah Handler"}
                                 :application/comment (str lipsum "\n\nA final line.")}]))
+   (example "event with anonymized user"
+            [event-view nil {:event/time #inst "2020-01-01T08:35"
+                             :event/type :application.event/remarked
+                             :event/actor-attributes {:userid "rems-handler"}
+                             :application/comment "I could be reviewer 1 or reviewer 2, but you will never know."}])
    (example "event that redacts and replaces attachments"
             (let [opts {:attachments [{:attachment/filename "new_image.jpeg"}
                                       {:attachment/filename "new_document.pdf"}]
diff --git a/src/cljs/rems/navbar.cljs b/src/cljs/rems/navbar.cljs
index 7a29f8b48..fa25c1840 100644
--- a/src/cljs/rems/navbar.cljs
+++ b/src/cljs/rems/navbar.cljs
@@ -89,7 +89,7 @@
                 [nav-link "/catalogue" (text :t.navigation/catalogue)])
               (when (roles/show-applications? roles)
                 [nav-link "/applications" (text :t.navigation/applications)])
-              (when (roles/show-reviews? roles)
+              (when (some roles/+handling-roles+ roles)
                 [nav-link "/actions" (text :t.navigation/actions)])
               (when (roles/show-admin-pages? roles)
                 [nav-link "/administration" (text :t.navigation/administration)])
diff --git a/src/cljs/rems/spa.cljs b/src/cljs/rems/spa.cljs
index 0cf74eab6..a0bd1c4b4 100644
--- a/src/cljs/rems/spa.cljs
+++ b/src/cljs/rems/spa.cljs
@@ -179,7 +179,7 @@
        (println "Selecting landing page based on roles" roles)
        (.removeItem js/sessionStorage "rems-redirect-url")
        (cond
-         (roles/show-reviews? roles) (navigate! "/actions")
+         (some roles/+handling-roles+ roles) (navigate! "/actions")
          (roles/show-admin-pages? roles) (navigate! "/administration")
          :else (navigate! "/catalogue"))
        {})


Test patch:
diff --git a/src/clj/rems/db/test_data_helpers.clj b/src/clj/rems/db/test_data_helpers.clj
index daf2f9ee7..8358f71d3 100644
--- a/src/clj/rems/db/test_data_helpers.clj
+++ b/src/clj/rems/db/test_data_helpers.clj
@@ -177,7 +177,7 @@
     (assert (:success result) {:command command :result result})
     (:id result)))
 
-(defn create-workflow! [{:keys [actor organization title type handlers forms licenses disable-commands voting]
+(defn create-workflow! [{:keys [actor organization title type handlers forms licenses disable-commands voting anonymize-handling]
                          :as command}]
   (let [actor (or actor (create-owner!))
         result (with-user actor
@@ -191,7 +191,8 @@
                                      ["developer"]))
                    :licenses (mapv (fn [id] {:license/id id}) licenses)
                    :disable-commands disable-commands
-                   :voting voting}))]
+                   :voting voting
+                   :anonymize-handling anonymize-handling}))]
     (assert (:success result) {:command command :result result})
     (:id result)))
 
diff --git a/src/clj/rems/service/test_data.clj b/src/clj/rems/service/test_data.clj
index 882853666..b50d6beef 100644
--- a/src/clj/rems/service/test_data.clj
+++ b/src/clj/rems/service/test_data.clj
@@ -317,23 +317,24 @@
                                             :title "EGA workflow, a variant of default"
                                             :type :workflow/default
                                             :handlers handlers})
-        disabled-commands (test-helpers/create-workflow! {:actor owner
-                                                          :organization {:organization/id "nbn"}
-                                                          :title "Restricted workflow"
-                                                          :type :workflow/default
-                                                          :handlers handlers
-                                                          :licenses [link text]
-                                                          :disable-commands [{:command :application.command/invite-member}
-                                                                             {:command :application.command/close
-                                                                              :when/state [:application.state/returned]
-                                                                              :when/role [:applicant]}]})]
+        restricted (test-helpers/create-workflow! {:actor owner
+                                                   :organization {:organization/id "nbn"}
+                                                   :title "Restricted workflow"
+                                                   :type :workflow/default
+                                                   :handlers handlers
+                                                   :licenses [link text]
+                                                   :disable-commands [{:command :application.command/invite-member}
+                                                                      {:command :application.command/close
+                                                                       :when/state [:application.state/returned]
+                                                                       :when/role [:applicant]}]
+                                                   :anonymize-handling true})]
     {:default default
      :ega ega
      :decider decider
      :master master
      :auto-approve auto-approve
      :organization-owner organization-owner
-     :disabled-commands disabled-commands}))
+     :restricted restricted}))
 
 (defn- create-bona-fide-catalogue-item! [users]
   (let [owner (:owner users)
@@ -384,7 +385,9 @@
 (defn- create-applications! [catid catid-restricted users]
   (let [applicant (users :applicant1)
         approver (users :approver1)
-        reviewer (users :reviewer)]
+        approver2 (users :approver2)
+        reviewer (users :reviewer)
+        decider (users :decider)]
 
     (test-helpers/create-draft! applicant [catid] "draft application")
 
@@ -431,10 +434,44 @@
       (test-helpers/command! {:type :application.command/submit
                               :application-id app-id
                               :actor applicant})
-      (test-helpers/command! {:type :application.command/return
+      (test-helpers/command! {:type :application.command/remark
                               :application-id app-id
                               :actor approver
-                              :comment "Need more details"}))
+                              :comment "processing is due now"
+                              :public true})
+      (test-helpers/command! {:type :application.command/request-review
+                              :application-id app-id
+                              :actor approver2
+                              :reviewers [reviewer]
+                              :comment "please have a look"})
+      (test-helpers/command! {:type :application.command/remark
+                              :application-id app-id
+                              :actor reviewer
+                              :comment "application is missing key purpose"
+                              :public true})
+      (test-helpers/command! {:type :application.command/review
+                              :application-id app-id
+                              :actor reviewer
+                              :comment "not looking good in my opinion"})
+      (test-helpers/command! {:type :application.command/request-decision
+                              :application-id app-id
+                              :actor approver2
+                              :comment "please decide"
+                              :deciders [decider]})
+      (test-helpers/command! {:type :application.command/remark
+                              :application-id app-id
+                              :actor decider
+                              :comment "i agree with previous remarker"
+                              :public true})
+      (test-helpers/command! {:type :application.command/decide
+                              :application-id app-id
+                              :actor decider
+                              :decision :rejected
+                              :comment "unacceptable in current state"})
+      (test-helpers/command! {:type :application.command/return
+                              :application-id app-id
+                              :actor approver2
+                              :comment "need more details"}))
 
     (let [app-id (test-helpers/create-draft! applicant [catid] "approved & closed")]
       (test-helpers/command! {:type :application.command/submit
@@ -841,7 +878,7 @@
           catid (test-helpers/create-catalogue-item! cat)
           catid-restricted (test-helpers/create-catalogue-item!
                             (assoc cat
-                                   :workflow-id (:disabled-commands workflows)
+                                   :workflow-id (:restricted workflows)
                                    :title {:en "Default workflow (restricted)"
                                            :fi "Oletustyövuo (rajoitettu)"
                                            :sv "Standard arbetsflöde (begränsad)"}))]
diff --git a/test/clj/rems/api/test_applications.clj b/test/clj/rems/api/test_applications.clj
index eb366e3da..2902a9b5b 100644
--- a/test/clj/rems/api/test_applications.clj
+++ b/test/clj/rems/api/test_applications.clj
@@ -87,10 +87,12 @@
        :application/attachments
        (sort-by :attachment/id)))
 
-(defn- get-last-event [application-id user-id]
-  (-> (get-application-for-user application-id user-id)
-      :application/events
-      last))
+(defn- get-events [app-id user-id]
+  (-> (get-application-for-user app-id user-id)
+      :application/events))
+
+(defn- get-last-event [app-id user-id]
+  (last (get-events app-id user-id)))
 
 (def testfile (io/file "./test-data/test.txt"))
 
@@ -1437,8 +1439,7 @@
                                     api-key user)]
           (is (= {:userid "alice"
                   :name "Alice Applicant"
-                  :email "alice@example.com"
-                  :organizations [{:organization/id "default"}]}
+                  :email "alice@example.com"}
                  (:application/applicant application)
                  (get-in application [:application/events 0 :event/actor-attributes])))
           (is (= {:userid "developer"
@@ -2821,3 +2822,150 @@
                            api-key reporter)
                  ;; event ids are unpredictable
                  (update :application/events (partial map #(update % :event/id (constantly 100))))))))))
+
+(deftest test-application-handler-anonymization
+  (let [applicant "alice"
+        member "malice"
+        reviewer "carl"
+        decider (test-helpers/create-user! {:userid "decider"
+                                            :email "decider@example.com"
+                                            :name "David Decider"
+                                            :organizations [{:organization/id "default"}]})
+        handler-id (test-helpers/create-user! {:userid "special-handler"
+                                               :email "special-handler@example.com"
+                                               :name "Heather Handler"
+                                               :organizations [{:organization/id "default"}]
+                                               :nickname "Edge case"})
+        _ (api-call :put "/api/user-settings/edit" {:language :fi :notification-email "special-handler-notifications@example.com"} +test-api-key+ handler-id)
+        workflow-id (test-helpers/create-workflow! {:title "Restricted workflow"
+                                                    :handlers [handler-id]
+                                                    :type :workflow/decider
+                                                    :anonymize-handling true})
+        cat-item-id (test-helpers/create-catalogue-item! {:workflow-id workflow-id})
+        app-id (test-helpers/create-application! {:catalogue-item-ids [cat-item-id]
+                                                  :actor applicant})]
+    (testing "test setup"
+      (is (= {:language "fi" :notification-email "special-handler-notifications@example.com"}
+             (api-call :get "/api/user-settings" nil +test-api-key+ handler-id))
+          "handler user has extra attributes set")
+      (is (= {:success true}
+             (send-command applicant {:type :application.command/save-draft
+                                      :application-id app-id
+                                      :field-values []}))
+          "applicant can save draft")
+      (is (= {:success true}
+             (send-command applicant {:type :application.command/submit
+                                      :application-id app-id}))
+          "applicant can submit application")
+      (is (= {:success true}
+             (send-command handler-id {:type :application.command/add-member
+                                       :application-id app-id
+                                       :member {:userid member}}))
+          "handler can add member")
+      (is (= {:success true}
+             (send-command handler-id {:type :application.command/remark
+                                       :public true
+                                       :application-id app-id
+                                       :comment "see attachment for details"
+                                       :attachments [{:attachment/id (upload-api-attachment-ok app-id handler-id "remark.txt")}]}))
+          "handler can remark with attachment")
+      (is (= {:success true}
+             (send-command handler-id {:type :application.command/request-review
+                                       :application-id app-id
+                                       :reviewers [reviewer]
+                                       :comment "please give your review"}))
+          "handler can request review")
+      (is (= {:success true}
+             (send-command reviewer {:type :application.command/remark
+                                     :public true ; XXX: should reviewer be able to make public remark?
+                                     :application-id app-id
+                                     :comment "not sure"}))
+          "reviewer can make a public remark")
+      (is (= {:success true}
+             (send-command handler-id {:type :application.command/request-decision
+                                       :application-id app-id
+                                       :deciders [decider]
+                                       :comment "please give your decision"}))
+          "handler can request decision")
+      (is (= {:success true}
+             (send-command decider {:type :application.command/remark
+                                    :public true ; XXX: should decider be able to make public remark?
+                                    :application-id app-id
+                                    :comment "i will make final decision soon"}))
+          "decider can make a public remark"))
+
+    (testing "attachments"
+      (let [get-attachments (fn [userid]
+                              (vec (for [att (get-api-attachments app-id userid)]
+                                     (select-keys att [:attachment/user]))))]
+        (is (= [{:attachment/user {:userid "special-handler" :email "special-handler@example.com" :name "Heather Handler" :nickname "Edge case" :organizations [{:organization/id "default"}] :notification-email "special-handler-notifications@example.com"}}]
+               (get-attachments handler-id)
+               (get-attachments reviewer))
+            "handler and reviewer can see all user attributes in attachments")
+        (is (= [{:attachment/user {:userid "rems-handler" :email nil :name nil}}]
+               (get-attachments applicant)
+               (get-attachments member))
+            "applicant and member can see only anonymized handler in attachments")))
+
+    (testing "events"
+      (let [select-event-columns #(select-keys % [:event/actor :event/actor-attributes :event/public :event/visibility])
+            get-app-events (fn [userid]
+                             (->> (get-events app-id userid)
+                                  (mapv (juxt :event/type select-event-columns))))
+            applicant-full {:userid "alice" :email "alice@example.com" :name "Alice Applicant" :nickname "In Wonderland" :organizations [{:organization/id "default"}] :researcher-status-by "so"}
+            handler-full {:userid "special-handler" :email "special-handler@example.com" :name "Heather Handler" :nickname "Edge case" :organizations [{:organization/id "default"}] :notification-email "special-handler-notifications@example.com"}
+            reviewer-full {:userid "carl" :email "carl@example.com" :name "Carl Reviewer"}
+            decider-full {:userid "decider" :email "decider@example.com" :name "David Decider" :organizations [{:organization/id "default"}]}]
+
+        (testing "as handling users"
+          (is (= [["application.event/created"            {:event/actor "alice" :event/actor-attributes applicant-full :event/visibility "visibility/public"}]
+                  ["application.event/draft-saved"        {:event/actor "alice" :event/actor-attributes applicant-full :event/visibility "visibility/public"}]
+                  ["application.event/submitted"          {:event/actor "alice" :event/actor-attributes applicant-full :event/visibility "visibility/public"}]
+                  ["application.event/member-added"       {:event/actor "special-handler" :event/actor-attributes handler-full :event/visibility "visibility/public"}]
+                  ["application.event/remarked"           {:event/actor "special-handler" :event/actor-attributes handler-full :event/visibility "visibility/public" :event/public true}]
+                  ["application.event/review-requested"   {:event/actor "special-handler" :event/actor-attributes handler-full :event/visibility "visibility/handling-users"}]
+                  ["application.event/remarked"           {:event/actor "carl" :event/actor-attributes reviewer-full :event/visibility "visibility/public" :event/public true}]
+                  ["application.event/decision-requested" {:event/actor "special-handler" :event/actor-attributes handler-full :event/visibility "visibility/handling-users"}]
+                  ["application.event/remarked"           {:event/actor "decider" :event/actor-attributes decider-full :event/visibility "visibility/public" :event/public true}]]
+                 (get-app-events handler-id)
+                 (get-app-events reviewer)
+                 (get-app-events decider)
+                 (get-app-events "reporter"))
+              "handling users see full event actor attributes and both :event/public and :event/visibility attributes"))
+
+        (testing "as applying users"
+          (let [applicant-short {:userid "alice" :email "alice@example.com" :name "Alice Applicant"}
+                anonymous {:userid "rems-handler" :name nil :email nil}]
+
+            (is (= [["application.event/created"              {:event/actor "alice" :event/actor-attributes applicant-short}]
+                    ["application.event/draft-saved"          {:event/actor "alice" :event/actor-attributes applicant-short}]
+                    ["application.event/submitted"            {:event/actor "alice" :event/actor-attributes applicant-short}]
+                    ["application.event/member-added"         {:event/actor "rems-handler" :event/actor-attributes anonymous}]
+                    ["application.event/remarked"             {:event/actor "rems-handler" :event/actor-attributes anonymous}]
+                    #_["application.event/review-requested"   {:event/actor "special-handler" :event/actor-attributes handler-attributes}]
+                    ["application.event/remarked"             {:event/actor "rems-handler" :event/actor-attributes anonymous}]
+                    #_["application.event/decision-requested" {:event/actor "special-handler" :event/actor-attributes handler-attributes}]
+                    ["application.event/remarked"             {:event/actor "rems-handler" :event/actor-attributes anonymous}]]
+                   (get-app-events applicant)
+                   (get-app-events member))
+                "applying users see anonymized handling users and shortened actor attributes, and neither :event/public nor :event/visibility are visible")))))
+
+    (testing "workflow"
+      (is (= {:workflow/id workflow-id
+              :workflow/type "workflow/decider"
+              :workflow/anonymize-handling true
+              :workflow.dynamic/handlers [{:userid "special-handler"
+                                           :name "Heather Handler"
+                                           :email "special-handler@example.com"
+                                           :organizations [{:organization/id "default"}]
+                                           :notification-email "special-handler-notifications@example.com"
+                                           :nickname "Edge case"
+                                           :handler/active? true}]}
+             (:application/workflow (get-application-for-user app-id handler-id))
+             (:application/workflow (get-application-for-user app-id reviewer)))
+          "handler and reviewer can see anonymize handling attributes in workflow")
+      (is (= {:workflow/id workflow-id
+              :workflow/type "workflow/decider"}
+             (:application/workflow (get-application-for-user app-id applicant))
+             (:application/workflow (get-application-for-user app-id member)))
+          "applicant and member do not see anonymize handling attributes in workflow"))))
diff --git a/test/clj/rems/api/test_workflows.clj b/test/clj/rems/api/test_workflows.clj
index da0ff6858..51d054961 100644
--- a/test/clj/rems/api/test_workflows.clj
+++ b/test/clj/rems/api/test_workflows.clj
@@ -1,18 +1,16 @@
 (ns ^:integration rems.api.test-workflows
-  (:require [clojure.test :refer :all]
+  (:require [clojure.test :refer [deftest is testing use-fixtures]]
             [rems.service.licenses :as licenses]
             [rems.service.workflow :as workflows]
-            [rems.api.testing :refer :all]
+            [rems.api.testing :refer [api-call api-fixture api-response assert-response-is-ok assert-success authenticate coll-is-not-empty? read-body read-ok-body response-is-forbidden? response-is-not-found? response-is-unauthorized?]]
             [rems.common.util :refer [replace-key]]
             [rems.db.applications :as applications]
-            [rems.db.core :as db]
             [rems.db.test-data-users :refer [+fake-user-data+]]
             [rems.db.test-data-helpers :as test-helpers]
             [rems.db.testing :refer [owners-fixture +test-api-key+]]
-            [rems.db.workflow :as workflow]
             [rems.handler :refer [handler]]
             [rems.testing-util :refer [with-user]]
-            [ring.mock.request :refer :all]))
+            [ring.mock.request :refer [json-body request]]))
 
 (use-fixtures :each
   api-fixture
@@ -46,13 +44,12 @@
 
 (deftest workflows-api-test
   (create-handlers!)
-  (let [create-workflow (fn [user-id organization type forms]
+  (let [create-workflow (fn [user-id organization workflow]
                           (api-call :post "/api/workflows/create"
-                                    {:organization {:organization/id organization}
-                                     :title "workflow title"
-                                     :type type
-                                     :forms forms
-                                     :handlers ["handler" "carl"]}
+                                    (merge {:organization {:organization/id organization}
+                                            :title "workflow title"
+                                            :handlers ["handler" "carl"]}
+                                           workflow)
                                     +test-api-key+ user-id))]
     (doseq [user-id ["owner" "organization-owner1"]]
       (testing user-id
@@ -74,7 +71,8 @@
                                                     :form/fields [{:field/type :text
                                                                    :field/title {:fi "fi" :sv "sv" :en "en"}
                                                                    :field/optional true}]})
-                body (create-workflow user-id "organization1" :workflow/default [{:form/id form-id}])
+                body (create-workflow user-id "organization1" {:type :workflow/default
+                                                               :forms [{:form/id form-id}]})
                 id (:id body)]
             (is (number? id))
             (testing "and fetch"
@@ -88,7 +86,8 @@
         (testing "create default workflow with invalid form"
           (is (= {:success false
                   :errors [{:type "invalid-form" :forms [{:form/id 999999}]}]}
-                 (create-workflow user-id "organization1" :workflow/default [{:form/id 999999}]))))
+                 (create-workflow user-id "organization1" {:type :workflow/default
+                                                           :forms [{:form/id 999999}]}))))
 
         (testing "create default workflow with invalid handlers"
           (is (= {:success false
@@ -101,12 +100,14 @@
                            +test-api-key+ user-id))))
 
         (testing "create decider workflow"
-          (let [body (create-workflow user-id "organization1" :workflow/decider [])
+          (let [body (create-workflow user-id "organization1" {:type :workflow/decider
+                                                               :anonymize-handling true})
                 id (:id body)]
             (is (< 0 id))
             (testing "and fetch"
               (is (= (-> expected
-                         (assoc-in [:workflow :type] "workflow/decider"))
+                         (update :workflow merge {:type "workflow/decider"
+                                                  :anonymize-handling true}))
                      (fetch +test-api-key+ user-id id))))))))
 
     (testing "create as organization-owner with incorrect organization"
@@ -273,7 +274,22 @@
     (testing "application is updated when handlers are changed"
       (let [app (applications/get-application app-id)]
         (is (= #{"owner" "alice"}
-               (application->handler-user-ids app)))))))
+               (application->handler-user-ids app)))))
+
+    (testing "change anonymize handling"
+      (let [get-anonymize-handling #(-> % :workflow (select-keys [:anonymize-handling]))
+            edit-wf #(api-call :put "/api/workflows/edit"
+                               (merge {:id wfid} %)
+                               +test-api-key+ user-id)]
+
+        (is (true? (:success (edit-wf {:anonymize-handling true}))))
+        (is (= {:anonymize-handling true} (get-anonymize-handling (fetch +test-api-key+ user-id wfid))))
+
+        (is (true? (:success (edit-wf {}))))
+        (is (= {:anonymize-handling true} (get-anonymize-handling (fetch +test-api-key+ user-id wfid))))
+
+        (is (true? (:success (edit-wf {:anonymize-handling false}))))
+        (is (= {:anonymize-handling false} (get-anonymize-handling (fetch +test-api-key+ user-id wfid))))))))
 
 (deftest workflows-api-filtering-test
   (let [enabled-wf (test-helpers/create-workflow! {})
diff --git a/test/clj/rems/email/test_template.clj b/test/clj/rems/email/test_template.clj
index 07cded975..459de900d 100644
--- a/test/clj/rems/email/test_template.clj
+++ b/test/clj/rems/email/test_template.clj
@@ -1,5 +1,5 @@
 (ns rems.email.test-template
-  (:require [clojure.test :refer :all]
+  (:require [clojure.test :refer [deftest is testing use-fixtures]]
             [clj-time.core :as time]
             [mount.core :as mount]
             [rems.application.model :as model]
@@ -49,7 +49,14 @@
                              :email "handler@example.com"}
                             {:userid "assistant"
                              :name "Amber Assistant"
-                             :email "assistant@example.com"}]}}})
+                             :email "assistant@example.com"}]}}
+   6 {:workflow {:handlers [{:userid "handler"
+                             :name "Hannah Handler"
+                             :email "handler@example.com"}
+                            {:userid "assistant"
+                             :name "Amber Assistant"
+                             :email "assistant@example.com"}]
+                 :anonymize-handling true}}})
 
 (def ^:private get-form-template
   (constantly {:form/id 40
@@ -121,20 +128,21 @@
   ([base-events event]
    (emails :en base-events event)))
 
-(def created-events [{:application/id 7
-                      :application/external-id "2001/3"
-                      :event/type :application.event/created
-                      :event/actor "applicant"
-                      :application/resources [{:catalogue-item/id 10
-                                               :resource/ext-id "urn:11"}
-                                              {:catalogue-item/id 20
-                                               :resource/ext-id "urn:21"}]
-                      :application/forms [{:form/id 40}]
-                      :workflow/id 5
-                      :workflow/type :workflow/default}
-                     {:application/id 7
-                      :event/type :application.event/draft-saved
-                      :application/field-values [{:form 40 :field "1" :value "Application title"}]}])
+(def create-event {:application/id 7
+                   :application/external-id "2001/3"
+                   :event/type :application.event/created
+                   :event/actor "applicant"
+                   :application/resources [{:catalogue-item/id 10
+                                            :resource/ext-id "urn:11"}
+                                           {:catalogue-item/id 20
+                                            :resource/ext-id "urn:21"}]
+                   :application/forms [{:form/id 40}]
+                   :workflow/id 5
+                   :workflow/type :workflow/default})
+(def save-draft-event {:application/id 7
+                       :event/type :application.event/draft-saved
+                       :application/field-values [{:form 40 :field "1" :value "Application title"}]})
+(def created-events [create-event save-draft-event])
 
 (def submit-event {:application/id 7
                    :event/type :application.event/submitted
@@ -142,6 +150,9 @@
                    :event/time 13})
 
 (def base-events (conj created-events submit-event))
+(def anonymous-wf-events [(assoc create-event :workflow/id 6)
+                          save-draft-event
+                          submit-event])
 
 (deftest test-submitted
   (let [mails (emails created-events submit-event)]
@@ -211,7 +222,22 @@
     (is (= {:to-user "applicant"
             :subject "(2001/3, \"Application title\") Application has been commented"
             :body "Dear Alice Applicant,\n\nRandom Remarker has commented your application 2001/3, \"Application title\", submitted by Alice Applicant.\n\nYou can view the application and the comment at http://example.com/application/7"}
-           (email-to "applicant" mails)))))
+           (email-to "applicant" mails))))
+  (testing "remarked anonymize handling workflow"
+    (let [mails (emails anonymous-wf-events {:application/id 7
+                                             :event/type :application.event/remarked
+                                             :event/actor "handler"
+                                             :event/public true
+                                             :application/comment "remark!"})]
+      (is (= #{"assistant" "applicant"} (email-recipients mails)))
+      (is (= {:to-user "applicant"
+              :subject "(2001/3, \"Application title\") Application has been commented"
+              :body "Dear Alice Applicant,\n\nHandler has commented your application 2001/3, \"Application title\", submitted by Alice Applicant.\n\nYou can view the application and the comment at http://example.com/application/7"}
+             (email-to "applicant" mails)))
+      (is (= {:to-user "assistant"
+              :subject "(2001/3, \"Application title\") Application has been commented"
+              :body "Dear Amber Assistant,\n\nHannah Handler has commented your application 2001/3, \"Application title\", submitted by Alice Applicant.\n\nYou can view the application and the comment at http://example.com/application/7"}
+             (email-to "assistant" mails))))))
 
 (deftest test-members-licenses-approved-closed
   (let [add-member {:application/id 7
@@ -237,6 +263,17 @@
                 :subject "Your application's 2001/3, \"Application title\" terms of use have changed"
                 :body "Dear Alice Applicant,\n\nHannah Handler has requested your approval for changed terms of use to application 2001/3, \"Application title\".\n\nYou can view the application and approve the changed terms of use at http://example.com/application/7"}
                (email-to "applicant" mails)))))
+    (testing "licenses-added anonymize handling workflow"
+      (let [mails (emails (conj anonymous-wf-events add-member join)
+                          {:application/id 7
+                           :event/type :application.event/licenses-added
+                           :event/actor "handler"
+                           :application/licenses [{:license/id 1234}]})]
+        (is (= #{"applicant" "member" "somebody"} (email-recipients mails)))
+        (is (= {:to-user "applicant"
+                :subject "Your application's 2001/3, \"Application title\" terms of use have changed"
+                :body "Dear Alice Applicant,\n\nHandler has requested your approval for changed terms of use to application 2001/3, \"Application title\".\n\nYou can view the application and approve the changed terms of use at http://example.com/application/7"}
+               (email-to "applicant" mails)))))
     (testing "approved"
       (let [mails (emails member-events {:application/id 7
                                          :event/type :application.event/approved
@@ -417,7 +454,22 @@
     (is (= {:to-user "handler"
             :subject "(2001/3, \"Application title\") Applicant changed"
             :body "Dear Hannah Handler,\n\nThe applicant for application 2001/3, \"Application title\" has been changed to Bob Boss by Amber Assistant.\n\nYou can view the application at http://example.com/application/7."}
-           (email-to "handler" mails)))))
+           (email-to "handler" mails)))
+    (testing "change-applicant anonymize handling workflow"
+      (let [mails (emails anonymous-wf-events change)]
+        (is (= #{"applicant" "handler" "bob"} (email-recipients mails)))
+        (is (= {:to-user "applicant"
+                :subject "Applicant for application 2001/3, \"Application title\" changed"
+                :body "Dear Alice Applicant,\n\nThe applicant for application 2001/3, \"Application title\" has been changed to Bob Boss by Handler.\n\nYou can view the application at http://example.com/application/7."}
+               (email-to "applicant" mails)))
+        (is (= {:to-user "bob"
+                :subject "Applicant for application 2001/3, \"Application title\" changed"
+                :body "Dear Bob Boss,\n\nThe applicant for application 2001/3, \"Application title\" has been changed to Bob Boss by Handler.\n\nYou can view the application at http://example.com/application/7."}
+               (email-to "bob" mails)))
+        (is (= {:to-user "handler"
+                :subject "(2001/3, \"Application title\") Applicant changed"
+                :body "Dear Hannah Handler,\n\nThe applicant for application 2001/3, \"Application title\" has been changed to Bob Boss by Amber Assistant.\n\nYou can view the application at http://example.com/application/7."}
+               (email-to "handler" mails)))))))
 
 (deftest test-finnish-emails
   ;; only one test case so far, more of a smoke test
diff --git a/test/clj/rems/test_browser.clj b/test/clj/rems/test_browser.clj
index e25aecd48..c578dfe9f 100644
--- a/test/clj/rems/test_browser.clj
+++ b/test/clj/rems/test_browser.clj
@@ -1046,8 +1046,8 @@
                 "Type" "Master workflow"
                 "Handlers" "Invited Person Name (invite-person-id@example.com)"
                 "Active" true
-                "Forms" ""
-                "Licenses" ""}
+                "Forms" "No forms"
+                "Licenses" "No licenses"}
                (slurp-fields :workflow)))))))
 
 (deftest test-invite-reviewer
@@ -1351,8 +1351,8 @@
               "Title" (btu/context-getx :workflow-name)
               "Type" "Default workflow"
               "Handlers" "Hannah Handler (handler@example.com)"
-              "Forms" ""
-              "Licenses" ""
+              "Forms" "No forms"
+              "Licenses" "No licenses"
               "Active" true}
              (slurp-fields :workflow)))
       (go-to-admin "Workflows")
diff --git a/test/cljs/rems/administration/test_create_workflow.cljs b/test/cljs/rems/administration/test_create_workflow.cljs
index c57c1c7df..4235a0784 100644
--- a/test/cljs/rems/administration/test_create_workflow.cljs
+++ b/test/cljs/rems/administration/test_create_workflow.cljs
@@ -10,11 +10,13 @@
                 :forms [{:form/id 123}]
                 :handlers ["bob"]
                 :licenses [{:id 1}]
-                :disable-commands [{:command :application.command/close}]}]
+                :disable-commands [{:command :application.command/close}]
+                :anonymize-handling true}]
       (is (not (nil? (build-create-request form))))
       (is (not (nil? (build-create-request (dissoc form :forms :licenses)))))
-      (is (= {:licenses [{:license/id 1}]}
-             (select-keys (build-create-request form) [:licenses])))
+      (is (= {:anonymize-handling true
+              :licenses [{:license/id 1}]}
+             (select-keys (build-create-request form) [:anonymize-handling :licenses])))
       (testing "missing organization"
         (is (nil? (build-create-request (assoc form :organization nil)))))
       (testing "missing title"
@@ -40,7 +42,8 @@
                 :handlers ["bob" "carl"]
                 :licenses []
                 :disable-commands []
-                :voting nil}
+                :voting nil
+                :anonymize-handling false}
                (build-create-request form))))
       (testing "missing handlers"
         (is (nil? (build-create-request (assoc-in form [:handlers] [])))))))
@@ -59,7 +62,8 @@
                 :handlers ["bob" "carl"]
                 :licenses []
                 :disable-commands []
-                :voting nil}
+                :voting nil
+                :anonymize-handling false}
                (build-create-request form))))
       (testing "missing handlers"
         (is (nil? (build-create-request (assoc-in form [:handlers] [])))))))
@@ -77,23 +81,40 @@
                 :handlers ["bob" "carl"]
                 :licenses []
                 :disable-commands []
-                :voting nil}
+                :voting nil
+                :anonymize-handling false}
                (build-create-request form))))
       (testing "missing handlers"
         (is (nil? (build-create-request (assoc-in form [:handlers] []))))))))
 
 (deftest build-edit-request-test
-  (is (= {:id 3
-          :organization {:organization/id "o"}
-          :title "t"
-          :handlers ["a" "b"]
-          :disable-commands [{:command :application.command/close}]
-          :voting nil}
-         (build-edit-request 3 {:organization {:organization/id "o"}
-                                :title "t"
-                                :handlers [{:userid "a"} {:userid "b"}]
-                                :licenses [{:id 1}] ; licenses should not be mapped
-                                :disable-commands [{:command :application.command/close}]})))
+  (testing "form defaults"
+    (is (= {:id 3
+            :organization {:organization/id "o"}
+            :title "t"
+            :handlers ["a" "b"]
+            :disable-commands []
+            :voting nil
+            :anonymize-handling false}
+           (build-edit-request 3 {:organization {:organization/id "o"}
+                                  :title "t"
+                                  :handlers [{:userid "a"} {:userid "b"}]}))))
+  (testing "extra options"
+    (is (= {:id 3
+            :organization {:organization/id "o"}
+            :title "t"
+            :handlers ["a" "b"]
+            :disable-commands [{:command :application.command/close}]
+            :voting :handlers-vote
+            :anonymize-handling true}
+           (build-edit-request 3 {:organization {:organization/id "o"}
+                                  :title "t"
+                                  :handlers [{:userid "a"} {:userid "b"}]
+                                  :licenses [{:id 1}] ; licenses should not be mapped
+                                  :disable-commands [{:command :application.command/close}]
+                                  :voting :handlers-vote
+                                  :anonymize-handling true}))))
+
   (is (nil? (build-edit-request nil {:title "t" :handlers [{:userid "a"} {:userid "b"}]})))
   (is (nil? (build-edit-request 3 {:title "t" :handlers [{:userid "a"} {:userid "b"}]})))
   (is (nil? (build-edit-request 3 {:organization {:organization/id "o"} :title "" :handlers [{:userid "a"} {:userid "b"}]})))