Repository: CSCfi/rems
Pull request #1605 (2019-09-10 07:19:30)

Use application title and user commonName in emails
groundwork for #1366

# Definition of Done / Review checklist

## Reviewability
- [ ] link to issue
- [ ] note if PR is on top of other PR
- [ ] note if related change in rems-deploy repo
- [ ] consider adding screenshots for ease of review

## API
- [ ] API is documented and shows up in Swagger UI
- [ ] API is backwards compatible or completely new
- [ ] Events are backwards compatible

## Documentation
- [ ] update changelog if necessary
- [ ] add or update docstrings for namespaces and functions
- [ ] components are added to guide page
- [ ] documentation _at least_ for config options (i.e. docs folder)
- [ ] ADR for major architectural decisions or experiments

## Different installations
- [ ] new configuration options added to rems-deploy repository
- [ ] instance specific translations (i.e. LBR kielivara)

## Testing
- [ ] complex logic is unit tested
- [ ] valuable features are integration / browser / acceptance tested automatically

## Accessibility
- [ ] all icons have the aria-label attribute
- [ ] all fields have a label
- [ ] errors are linked to fields with aria-describedby
- [ ] contrast is checked
- [ ] conscious decision about where to move focus after an action

## Follow-up
- [ ] new tasks are created for pending or remaining tasks
- [ ] no critical TODOs left to implement



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 037762491..236eb7e21 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -67,10 +67,11 @@ Changes since v2.4
 - PDFs are now downloaded as \<application-id\>.pdf instead of pdf.pdf
 - Improved table performance: added a "show all rows" button for long tables
 - Modal popups have been replaced with flash messages (#1469)
+- Email messages now use the application title and full names of users
 
 ### Fixes
 - Entitlement API
-- Search on the catalogue and admin pages did not support multiple search terms (#1541) 
+- Search on the catalogue and admin pages did not support multiple search terms (#1541)
 
 ## v2.5 "Maarintie" 2019-07-18
 
diff --git a/resources/translations/en.edn b/resources/translations/en.edn
index cdf762001..7ea7b6989 100644
--- a/resources/translations/en.edn
+++ b/resources/translations/en.edn
@@ -270,7 +270,7 @@
          :submit "Send application"
          :success "Success"
          :upload "Upload an attachment"
-         :validation {:errors "Submission failed. Check the following errors."
+         :validation {:errors "Submission failed. Check the following fields."
                       :invalid-user "Invalid user."
                       :invalid-value "Invalid value."
                       :required "Field \"%1\" is required."
diff --git a/resources/translations/fi.edn b/resources/translations/fi.edn
index 1ac78512d..46a37b06e 100644
--- a/resources/translations/fi.edn
+++ b/resources/translations/fi.edn
@@ -268,7 +268,7 @@
          :submit "Lähetä hakemus"
          :success "Onnistui"
          :upload "Lisää liitetiedosto"
-         :validation {:errors "Lähettäminen epäonnistui. Tarkasta seuraavat virheet."
+         :validation {:errors "Lähettäminen epäonnistui. Tarkasta seuraavat kentät."
                       :invalid-user "Käyttäjä ei kelpaa."
                       :invalid-value "Arvo ei kelpaa."
                       :required "Kenttä \"%1\" on pakollinen."
diff --git a/src/clj/rems/poller/email.clj b/src/clj/rems/poller/email.clj
index 6f11fa9c9..7f9851e30 100644
--- a/src/clj/rems/poller/email.clj
+++ b/src/clj/rems/poller/email.clj
@@ -5,6 +5,7 @@
             [clojure.tools.logging :as log]
             [mount.core :as mount]
             [postal.core :as postal]
+            [rems.common-util :as common-util]
             [rems.config :refer [env]]
             [rems.context :as context]
             [rems.db.applications :as applications]
@@ -30,10 +31,19 @@
 (defn- invitation-link [token]
   (str (:public-url env) "accept-invitation?token=" token))
 
-(defn- application-id-for-email [application]
-  (case (util/getx env :application-id-column)
-    :external-id (:application/external-id application)
-    :id (:application/id application)))
+(defn- format-application-for-email [application]
+  (str
+   (case (util/getx env :application-id-column)
+     :external-id (:application/external-id application)
+     :id (:application/id application))
+   (when-not (empty? (:application/description application))
+     (str ", \"" (:application/description application) "\""))))
+
+(defn- user-for-email [user]
+  (let [user-attributes (users/get-user-attributes user)]
+    (or (:commonName user-attributes)
+        (:eppn user-attributes)
+        user)))
 
 (defn- resources-for-email [application]
   (->> (:application/resources application)
@@ -66,18 +76,18 @@
        (fn []
          {:to-user recipient
           :subject (text-format subject-text
-                                recipient
-                                (:event/actor event)
-                                (application-id-for-email application)
-                                (:application/applicant application)
+                                (user-for-email recipient)
+                                (user-for-email (:event/actor event))
+                                (format-application-for-email application)
+                                (user-for-email (:application/applicant application))
                                 (resources-for-email application)
                                 (link-to-application (:application/id event)))
           :body (str
                  (text-format body-text
-                              recipient
-                              (:event/actor event)
-                              (application-id-for-email application)
-                              (:application/applicant application)
+                              (user-for-email recipient)
+                              (user-for-email (:event/actor event))
+                              (format-application-for-email application)
+                              (user-for-email (:application/applicant application))
                               (resources-for-email application)
                               (link-to-application (:application/id event)))
                  (text :t.email/footer))})))))
diff --git a/src/cljc/rems/common_util.cljc b/src/cljc/rems/common_util.cljc
index f067620de..e89ddf76c 100644
--- a/src/cljc/rems/common_util.cljc
+++ b/src/cljc/rems/common_util.cljc
@@ -55,15 +55,14 @@
       (is (= "(2)" (andstr "(" (:foo x) ")"))))))
 
 (defn deep-merge [a b]
-  (cond (and (sequential? a) (sequential? b))
-        (let [max-length (max (count a) (count b))
-              a (take max-length (concat a (repeat nil)))
-              b (take max-length (concat b (repeat nil)))]
-          (doall (map deep-merge a b)))
-
-        (map? a)
-        (merge-with deep-merge a b)
-
+  (cond (and (sequential? a)
+             (sequential? b)) (let [max-length (max (count a) (count b))
+                                    a (take max-length (concat a (repeat nil)))
+                                    b (take max-length (concat b (repeat nil)))]
+                                (->> (interleave a b)
+                                     (partition 2)
+                                     (map (fn [[x y]] (deep-merge x y)))))
+        (map? a) (merge-with deep-merge a b)
         :else b))
 
 (deftest test-deep-merge
diff --git a/src/cljs/rems/application.cljs b/src/cljs/rems/application.cljs
index 8fd3af1f9..7bf8e3edd 100644
--- a/src/cljs/rems/application.cljs
+++ b/src/cljs/rems/application.cljs
@@ -48,7 +48,7 @@
                                             (:catalogue-item/expired %)
                                             (:catalogue-item/archived %)))
                                seq)]
-      [:div.alert.alert-danger
+      [:div.alert.alert-warning
        (text :t.form/alert-disabled-resources)
        (into [:ul]
              (for [resource resources]
@@ -599,12 +599,12 @@
   (let [commands-and-actions [:application.command/save-draft [save-button]
                               :application.command/submit [submit-button]
                               :application.command/return [return-action-button]
-                              :application.command/request-comment [request-review-action-button]
-                              :application.command/comment [review-action-button]
                               :application.command/request-decision [request-decision-action-button]
                               :application.command/decide [decide-action-button]
-                              :application.command/add-licenses [add-licenses-action-button]
+                              :application.command/request-comment [request-review-action-button]
+                              :application.command/comment [review-action-button]
                               :application.command/remark [remark-action-button]
+                              :application.command/add-licenses [add-licenses-action-button]
                               :application.command/approve [approve-reject-action-button]
                               :application.command/reject [approve-reject-action-button]
                               :application.command/close [close-action-button]


Test patch:
diff --git a/src/clj/rems/db/test_data.clj b/src/clj/rems/db/test_data.clj
index 8c9812a76..461ab4c15 100644
--- a/src/clj/rems/db/test_data.clj
+++ b/src/clj/rems/db/test_data.clj
@@ -793,7 +793,7 @@
                                                     :form-id form
                                                     :workflow-id (:dynamic workflows)})]
       (create-disabled-applications! dynamic-disabled
-                                     (+fake-users+ :applicant2)
+                                     (+fake-users+ :approver1) ; TODO: this should probably be :applicant1
                                      (+fake-users+ :approver1))
       (db/set-catalogue-item-enabled! {:id dynamic-disabled :enabled false}))
     (let [dynamic-expired (create-catalogue-item! {:title {:en "Dynamic workflow (expired)"
@@ -835,6 +835,6 @@
                                                     :form-id form
                                                     :workflow-id (:dynamic workflows)})]
       (create-disabled-applications! dynamic-disabled
-                                     (+demo-users+ :applicant2)
+                                     (+demo-users+ :approver1) ; TODO: this should probably be :applicant1
                                      (+demo-users+ :approver1))
       (db/set-catalogue-item-enabled! {:id dynamic-disabled :enabled false}))))
diff --git a/test/clj/rems/api/test_entitlements.clj b/test/clj/rems/api/test_entitlements.clj
index 699f62ca9..d21a6526d 100644
--- a/test/clj/rems/api/test_entitlements.clj
+++ b/test/clj/rems/api/test_entitlements.clj
@@ -36,12 +36,12 @@
                                                  (dissoc x :start :end :application-id)))
                                           (is (valid-date? (:start x)))
                                           (is (valid-date? (:end x))))
-        check-malice-entitlement (fn [x]
-                                   (is (= {:resource "urn:nbn:fi:lb-201403262"
-                                           :end nil
-                                           :mail "malice@example.com"}
-                                          (dissoc x :start :application-id)))
-                                   (is (valid-date? (:start x))))]
+        check-developer-entitlement (fn [x]
+                                      (is (= {:resource "urn:nbn:fi:lb-201403262"
+                                              :end nil
+                                              :mail "developer@example.com"}
+                                             (dissoc x :start :application-id)))
+                                      (is (valid-date? (:start x))))]
     (testing "all"
       (let [data (-> (request :get "/api/entitlements")
                      (authenticate api-key "developer")
@@ -49,7 +49,7 @@
                      read-body)]
         (is (= 2 (count data)))
         (check-alice-entitlement (first data))
-        (check-malice-entitlement (second data))))
+        (check-developer-entitlement (second data))))
 
     (doseq [userid ["developer" "owner" "reporter"]]
       (testing (str "all as " userid)
@@ -59,7 +59,7 @@
                        read-body)]
           (is (= 2 (count data)))
           (check-alice-entitlement (first data))
-          (check-malice-entitlement (second data)))))
+          (check-developer-entitlement (second data)))))
 
     (testing "just for alice as a developer"
       (let [data (-> (request :get "/api/entitlements?user=alice")
@@ -85,7 +85,7 @@
         (is (= 3 (count data)))
         (check-alice-entitlement (first data))
         (check-alice-expired-entitlement (second data))
-        (check-malice-entitlement (nth data 2))))
+        (check-developer-entitlement (nth data 2))))
 
     (testing "just for alice as a reporter"
       (let [data (-> (request :get "/api/entitlements?user=alice")
diff --git a/test/clj/rems/poller/test_email.clj b/test/clj/rems/poller/test_email.clj
index 3f5f5719c..8716f2829 100644
--- a/test/clj/rems/poller/test_email.clj
+++ b/test/clj/rems/poller/test_email.clj
@@ -60,19 +60,33 @@
                              :name "Assistant"
                              :email "assistant@example.com"}]}}})
 
+(def ^:private get-form-template
+  (constantly {:form/id 40
+               :form/fields [{:field/id 1
+                              :field/title {:en "en title" :fi "fi title"}
+                              :field/optional false
+                              :field/type :description}]}))
+
 (defn ^:private get-nothing [& _]
   nil)
 
+(def ^:private get-user-attributes
+  {"applicant" {:commonName "Alice Applicant"
+                :email "alice@applicant.com"}
+   "handler" {:commonName "Hannah Handler"
+              :email "hannah@handler.com"}})
+
 (defn events-to-emails [events]
   (let [application (-> (reduce model/application-view nil events)
                         (model/enrich-with-injections {:get-workflow get-workflow
                                                        :get-catalogue-item get-catalogue-item
-                                                       :get-form-template get-nothing
+                                                       :get-form-template get-form-template
                                                        :get-license get-nothing
                                                        :get-user get-nothing
                                                        :get-users-with-role get-nothing
                                                        :get-attachments-for-application get-nothing}))]
     (with-redefs [rems.config/env (assoc rems.config/env :public-url "http://example.com/")
+                  rems.db.users/get-user-attributes get-user-attributes
                   user-settings/get-user-settings (constantly {:language :en})]
       (mapv #(sort-emails (#'rems.poller.email/event-to-emails-impl % application)) events))))
 
@@ -87,6 +101,9 @@
                                                :resource/ext-id "urn:21"}]
                       :workflow/id 5
                       :workflow/type :workflow/dynamic}
+                     {:application/id 7
+                      :event/type :application.event/draft-saved
+                      :application/field-values {1 "Application title"}}
                      {:application/id 7
                       :event/type :application.event/submitted
                       :event/actor "applicant"}]]
@@ -130,64 +147,65 @@
                     :event/type :application.event/closed
                     :event/actor "assistant"}])]
       (is (= [[]
+              []
               [{:to-user "assistant",
-                :subject "Application submitted (applicant: 2001/3)",
-                :body "Dear assistant,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
+                :subject "Application submitted (Alice Applicant: 2001/3, \"Application title\")",
+                :body "Dear assistant,\n\nAlice Applicant has submitted an application (2001/3, \"Application title\"): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "handler",
-                :subject "Application submitted (applicant: 2001/3)",
-                :body "Dear handler,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
+                :subject "Application submitted (Alice Applicant: 2001/3, \"Application title\")",
+                :body "Dear Hannah Handler,\n\nAlice Applicant has submitted an application (2001/3, \"Application title\"): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
               [{:to "somebody@example.com",
                 :subject "Invitation to participate in an application",
                 :body "Hello,\n\nThis email address (somebody@example.com) has been invited to participate in an application.\n\nParticipate: http://example.com/accept-invitation?token=abc\n\nPlease don't reply to this automatically generated message."}]
               [{:to-user "commenter1",
-                :subject "Review requested (handler: 2001/3)",
-                :body "Dear commenter1,\n\nhandler has requested a review on application 2001/3.\n\nComment: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
+                :subject "Review requested (Hannah Handler: 2001/3, \"Application title\")",
+                :body "Dear commenter1,\n\nHannah Handler has requested a review on application 2001/3, \"Application title\".\n\nComment: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "commenter2",
-                :subject "Review requested (handler: 2001/3)",
-                :body "Dear commenter2,\n\nhandler has requested a review on application 2001/3.\n\nComment: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
+                :subject "Review requested (Hannah Handler: 2001/3, \"Application title\")",
+                :body "Dear commenter2,\n\nHannah Handler has requested a review on application 2001/3, \"Application title\".\n\nComment: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
               []
               [{:to-user "assistant",
-                :subject "Review added (commenter2: 2001/3)",
-                :body "Dear assistant,\n\ncommenter2 has reviewed application 2001/3.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
+                :subject "Review added (commenter2: 2001/3, \"Application title\")",
+                :body "Dear assistant,\n\ncommenter2 has reviewed application 2001/3, \"Application title\".\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "handler",
-                :subject "Review added (commenter2: 2001/3)",
-                :body "Dear handler,\n\ncommenter2 has reviewed application 2001/3.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
+                :subject "Review added (commenter2: 2001/3, \"Application title\")",
+                :body "Dear Hannah Handler,\n\ncommenter2 has reviewed application 2001/3, \"Application title\".\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
               [{:to-user "member",
-                :subject "Added as a member of an application (2001/3)",
-                :body "Dear member,\n\nYou've been added as a member of application 2001/3.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
+                :subject "Added as a member of an application (2001/3, \"Application title\")",
+                :body "Dear member,\n\nYou've been added as a member of application 2001/3, \"Application title\".\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
               [{:to-user "decider",
-                :subject "Decision requested (assistant: 2001/3)",
-                :body "Dear decider,\n\nassistant has requested your decision on application 2001/3.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
+                :subject "Decision requested (assistant: 2001/3, \"Application title\")",
+                :body "Dear decider,\n\nassistant has requested your decision on application 2001/3, \"Application title\".\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
               [{:to-user "assistant",
-                :subject "Decision made (decider: 2001/3)",
-                :body "Dear assistant,\n\ndecider has made a decision on application 2001/3.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
+                :subject "Decision made (decider: 2001/3, \"Application title\")",
+                :body "Dear assistant,\n\ndecider has made a decision on application 2001/3, \"Application title\".\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "handler",
-                :subject "Decision made (decider: 2001/3)",
-                :body "Dear handler,\n\ndecider has made a decision on application 2001/3.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
-              [{:to-user "applicant",
-                :subject "Application approved (2001/3)",
-                :body "Dear applicant,\n\nYour application 2001/3 has been approved.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
+                :subject "Decision made (decider: 2001/3, \"Application title\")",
+                :body "Dear Hannah Handler,\n\ndecider has made a decision on application 2001/3, \"Application title\".\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
+              [{:to-user "applicant"
+                :subject "Application approved (2001/3, \"Application title\")",
+                :body "Dear Alice Applicant,\n\nYour application 2001/3, \"Application title\" has been approved.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "assistant"
-                :subject "Application approved (2001/3)"
-                :body "Dear assistant,\n\nhandler has approved the application 2001/3 from applicant.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
+                :subject "Application approved (2001/3, \"Application title\")"
+                :body "Dear assistant,\n\nHannah Handler has approved the application 2001/3, \"Application title\" from Alice Applicant.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "member",
-                :subject "Application approved (2001/3)",
-                :body "Dear member,\n\nYour application 2001/3 has been approved.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
+                :subject "Application approved (2001/3, \"Application title\")",
+                :body "Dear member,\n\nYour application 2001/3, \"Application title\" has been approved.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "somebody",
-                :subject "Application approved (2001/3)",
-                :body "Dear somebody,\n\nYour application 2001/3 has been approved.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
-              [{:to-user "applicant",
-                :subject "Application closed (2001/3)",
-                :body "Dear applicant,\n\nYour application 2001/3 has been closed.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
+                :subject "Application approved (2001/3, \"Application title\")",
+                :body "Dear somebody,\n\nYour application 2001/3, \"Application title\" has been approved.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
+              [{:to-user "applicant"
+                :subject "Application closed (2001/3, \"Application title\")",
+                :body "Dear Alice Applicant,\n\nYour application 2001/3, \"Application title\" has been closed.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "handler"
-                :subject "Application closed (2001/3)"
-                :body "Dear handler,\n\nassistant has closed the application 2001/3 from applicant.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
+                :subject "Application closed (2001/3, \"Application title\")"
+                :body "Dear Hannah Handler,\n\nassistant has closed the application 2001/3, \"Application title\" from Alice Applicant.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "member",
-                :subject "Application closed (2001/3)",
-                :body "Dear member,\n\nYour application 2001/3 has been closed.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
+                :subject "Application closed (2001/3, \"Application title\")",
+                :body "Dear member,\n\nYour application 2001/3, \"Application title\" has been closed.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                {:to-user "somebody",
-                :subject "Application closed (2001/3)",
-                :body "Dear somebody,\n\nYour application 2001/3 has been closed.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]]
+                :subject "Application closed (2001/3, \"Application title\")",
+                :body "Dear somebody,\n\nYour application 2001/3, \"Application title\" has been closed.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]]
              (events-to-emails events))))
     (testing "application rejected"
       (let [events (conj base-events
@@ -195,29 +213,50 @@
                           :event/type :application.event/rejected
                           :event/actor "handler"})]
         (is (= [[]
+                []
                 [{:to-user "assistant",
-                  :subject "Application submitted (applicant: 2001/3)",
-                  :body "Dear assistant,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
+                  :subject "Application submitted (Alice Applicant: 2001/3, \"Application title\")",
+                  :body "Dear assistant,\n\nAlice Applicant has submitted an application (2001/3, \"Application title\"): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                  {:to-user "handler",
-                  :subject "Application submitted (applicant: 2001/3)",
-                  :body "Dear handler,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
-                [{:subject "Application rejected (2001/3)",
-                  :body "Dear applicant,\n\nYour application 2001/3 has been rejected.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message.",
+                  :subject "Application submitted (Alice Applicant: 2001/3, \"Application title\")",
+                  :body "Dear Hannah Handler,\n\nAlice Applicant has submitted an application (2001/3, \"Application title\"): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
+                [{:subject "Application rejected (2001/3, \"Application title\")",
+                  :body "Dear Alice Applicant,\n\nYour application 2001/3, \"Application title\" has been rejected.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message.",
                   :to-user "applicant"}
-                 {:subject "Application rejected (2001/3)"
-                  :body "Dear assistant,\n\nhandler has rejected the application 2001/3 from applicant.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."
+                 {:subject "Application rejected (2001/3, \"Application title\")"
+                  :body "Dear assistant,\n\nHannah Handler has rejected the application 2001/3, \"Application title\" from Alice Applicant.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."
                   :to-user "assistant"}]]
                (events-to-emails events)))))
     (testing "id field can be overrided"
       (with-redefs [rems.config/env (assoc rems.config/env :application-id-column :id)]
         (is (= [[]
+                []
                 [{:to-user "assistant"
-                  :subject "Application submitted (applicant: 7)"
-                  :body "Dear assistant,\n\napplicant has submitted an application (7): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
+                  :subject "Application submitted (Alice Applicant: 7, \"Application title\")"
+                  :body "Dear assistant,\n\nAlice Applicant has submitted an application (7, \"Application title\"): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                  {:to-user "handler"
-                  :subject "Application submitted (applicant: 7)"
-                  :body "Dear handler,\n\napplicant has submitted an application (7): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]]
+                  :subject "Application submitted (Alice Applicant: 7, \"Application title\")"
+                  :body "Dear Hannah Handler,\n\nAlice Applicant has submitted an application (7, \"Application title\"): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]]
                (events-to-emails base-events)))))
+    (testing "application title is optional"
+      (is (= [[]
+              [{:to-user "assistant",
+                :subject "Application submitted (Alice Applicant: 2001/3)",
+                :body "Dear assistant,\n\nAlice Applicant has submitted an application (2001/3): en title 11.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
+               {:to-user "handler",
+                :subject "Application submitted (Alice Applicant: 2001/3)",
+                :body "Dear Hannah Handler,\n\nAlice Applicant has submitted an application (2001/3): en title 11.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]]
+             (events-to-emails [{: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"}]
+                                 :workflow/id 5
+                                 :workflow/type :workflow/dynamic}
+                                {:application/id 7
+                                 :event/type :application.event/submitted
+                                 :event/actor "applicant"}]))))
     (testing "returning application to applicant and re-submitting"
       (let [events (conj base-events
                          {:application/id 7
@@ -228,22 +267,23 @@
                           :event/type :application.event/submitted
                           :event/actor "applicant"})]
         (is (= [[]
+                []
                 [{:to-user "assistant",
-                  :subject "Application submitted (applicant: 2001/3)",
-                  :body "Dear assistant,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
+                  :subject "Application submitted (Alice Applicant: 2001/3, \"Application title\")",
+                  :body "Dear assistant,\n\nAlice Applicant has submitted an application (2001/3, \"Application title\"): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                  {:to-user "handler",
-                  :subject "Application submitted (applicant: 2001/3)",
-                  :body "Dear handler,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
+                  :subject "Application submitted (Alice Applicant: 2001/3, \"Application title\")",
+                  :body "Dear Hannah Handler,\n\nAlice Applicant has submitted an application (2001/3, \"Application title\"): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
                 [{:to-user "applicant",
-                  :subject "Application returned (2001/3)",
-                  :body "Dear applicant,\n\nYour application 2001/3 has been returned.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
+                  :subject "Application returned (2001/3, \"Application title\")",
+                  :body "Dear Alice Applicant,\n\nYour application 2001/3, \"Application title\" has been returned.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                  {:to-user "assistant",
-                  :subject "Application returned (2001/3)",
-                  :body "Dear assistant,\n\nhandler has returned the application 2001/3 from applicant.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
+                  :subject "Application returned (2001/3, \"Application title\")",
+                  :body "Dear assistant,\n\nHannah Handler has returned the application 2001/3, \"Application title\" from Alice Applicant.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]
                 [{:to-user "assistant",
-                  :subject "Application submitted (applicant: 2001/3)",
-                  :body "Dear assistant,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
+                  :subject "Application submitted (Alice Applicant: 2001/3, \"Application title\")",
+                  :body "Dear assistant,\n\nAlice Applicant has submitted an application (2001/3, \"Application title\"): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}
                  {:to-user "handler",
-                  :subject "Application submitted (applicant: 2001/3)",
-                  :body "Dear handler,\n\napplicant has submitted an application (2001/3): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]]
+                  :subject "Application submitted (Alice Applicant: 2001/3, \"Application title\")",
+                  :body "Dear Hannah Handler,\n\nAlice Applicant has submitted an application (2001/3, \"Application title\"): en title 11, en title 21.\n\nView application: http://example.com/#/application/7\n\nPlease don't reply to this automatically generated message."}]]
                (events-to-emails events)))))))