diff --git a/config/sync/editor.editor.content_editor.yml b/config/sync/editor.editor.content_editor.yml
index ba8db5640e5e1b93e9e8ad4669208ffc9169b1c5..e79ab58f33624fc65bde4a1663a362d34ad76fa2 100644
--- a/config/sync/editor.editor.content_editor.yml
+++ b/config/sync/editor.editor.content_editor.yml
@@ -36,6 +36,7 @@ settings:
           items:
             - DrupalLink
             - DrupalUnlink
+            - contactus
         -
           name: Lists
           items:
diff --git a/config/sync/filter.format.content_editor.yml b/config/sync/filter.format.content_editor.yml
index 20bfdb418146950aca9403ff16552f78f6245dbc..3e84cacc67c56d81d0bd2c13caf424d6bff3ccce 100644
--- a/config/sync/filter.format.content_editor.yml
+++ b/config/sync/filter.format.content_editor.yml
@@ -30,7 +30,7 @@ filters:
     status: true
     weight: -49
     settings:
-      allowed_html: '<em> <br> <strong> <cite> <blockquote cite> <code> <q> <ul type> <ol type start> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id> <p> <h1> <pre> <img src alt data-entity-type data-entity-uuid data-align data-caption data-image-style usemap width height> <table> <caption> <tbody> <thead> <tfoot> <th> <td> <tr> <a hreflang href data-entity-type data-entity-uuid !href accesskey id rel target title> <iframe src allowfullscreen mozallowfullscreen webkitallowfullscreen width height frameborder> <map name> <area alt coords href hreflang rel shape>'
+      allowed_html: '<em> <br> <strong> <cite> <blockquote cite> <code> <q> <ul type> <ol type start> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id> <p> <h1> <pre> <img src alt data-entity-type data-entity-uuid data-align data-caption data-image-style usemap width height> <table> <caption> <tbody> <thead> <tfoot> <th> <td> <tr> <a hreflang href data-entity-type data-entity-uuid data-joinup-contact-information data-joinup-contact-information-href data-joinup-contact-information-login !href accesskey id rel target title> <iframe src allowfullscreen mozallowfullscreen webkitallowfullscreen width height frameborder> <map name> <area alt coords href hreflang rel shape>'
       filter_html_help: true
       filter_html_nofollow: false
   filter_htmlcorrector:
diff --git a/tests/features/collection/collection.edit.feature b/tests/features/collection/collection.edit.feature
index 05cab1703f86cd4d0f5fe5dc56b4d58301800231..6c1675b730b832fa62455b72f5eb127d36632bc0 100644
--- a/tests/features/collection/collection.edit.feature
+++ b/tests/features/collection/collection.edit.feature
@@ -24,7 +24,7 @@ Feature: Editing collections
     And the following fields should not be present "Langcode, Translation, Affiliates, Enable the search field, Query presets, Limit"
     And I should see "Short description text of the collection. Appears on the Overview page. (Leave blank to use the trimmed value of the Description field.)"
     And I should see "Add a country name relevant to the content of this collection."
-    And the "Description" wysiwyg editor should have the buttons "HTML block format, Bold, Italic, Remove format, Link, Unlink, Bullet list, Numbered list, Outdent, Indent, Blockquote, Image, File, Table, Video Embed, Cut, Copy, Paste, Paste Text, Undo, Redo, Source code"
+    And the "Description" wysiwyg editor should have the buttons "HTML block format, Bold, Italic, Remove format, Link, Unlink, Contact us, Bullet list, Numbered list, Outdent, Indent, Blockquote, Image, File, Table, Video Embed, Cut, Copy, Paste, Paste Text, Undo, Redo, Source code"
     And the "Abstract" wysiwyg editor should have the buttons "HTML block format, Bold, Italic, Remove format, Link, Unlink, Outdent, Indent, Source code"
 
     # Query builder is disabled in collections.
diff --git a/tests/features/collection/propose.feature b/tests/features/collection/propose.feature
index 03786f2d68e3c2cc0ac637d59352c4caa0389156..d04ba830ec6df5e4f4178951a3de8f9ade72a7c0 100644
--- a/tests/features/collection/propose.feature
+++ b/tests/features/collection/propose.feature
@@ -295,6 +295,11 @@ Feature: Proposing a collection
       | signature_required | no                                                                                                                            |
       | html               | no                                                                                                                            |
 
+    # Contact us page should return access denied,
+    # because contact info emails are not validated.
+    When I go to "/collection/some-email-validation/contact"
+    Then I should get an access denied error
+
     # As some entities were created via UI, we should explicitly delete them.
     And I delete the "Contact person" contact information
     And I delete the "Contact Rapazoglou" contact information
diff --git a/tests/features/solution/about.feature b/tests/features/solution/about.feature
index 38a3421b69d0c8b7c3e6e51ad5f2dd0015bf05f9..ff0477cf4f2059045277157574684afa97ce2a64 100644
--- a/tests/features/solution/about.feature
+++ b/tests/features/solution/about.feature
@@ -311,6 +311,49 @@ Feature: About this solution
     And I press "Publish"
     Then I should not see the success message "A new confirmation message has been sent to elise@nova.com."
 
+    When I go to "/collection/antibody-development/solution/six-exclusion-advantages/contact"
+    Then I should see the heading "Contact us"
+
+    When I go to the about page of "Six exclusion advantages"
+    Then I open the plus button menu
+    And I click "Add news"
+    When I fill in the following:
+      | Headline    | Some contact test |
+      | Short title | Some contact test |
+    And I select "Supplier exchange" from "Topic"
+    And I press the button "Contact us" in the "Content" wysiwyg editor
+    Then I press "Publish"
+
+    When I go to the "Some contact test" news
+    Then I should see the heading "Some contact test"
+    When I click "Contact us"
+    Then I should see the heading "Contact us"
+    Then I fill in "Your name" with "Lina Example"
+    And I fill in "Your e-mail address" with "linak@example.pt"
+    And I fill in "Subject" with "New reclamation about Owner"
+    And I fill in "Message" with "This is reclamation test."
+    When I press "Send e-mail"
+    And I should see the success message "Your message has been sent."
+    Then I should see the heading "Some contact test"
+    And I should be on "/collection/antibody-development/solution/six-exclusion-advantages/news/some-contact-test"
+
+    When I am an anonymous user
+    Then I go to the "Some contact test" news
+    And I click "Contact us"
+    And I should see the heading "Sign in to continue"
+
+    When I fill in "E-mail address" with "iodine@ankh.am"
+    And I fill in "Password" with "10d1ne"
+    And I press "Log in"
+    And I should see the heading "Contact us"
+    Then I fill in "Your name" with "V Example"
+    And I fill in "Your e-mail address" with "vnak@example.pt"
+    And I fill in "Subject" with "v2 New reclamation about Owner"
+    And I fill in "Message" with "This is v2 reclamation test."
+    When I press "Send e-mail"
+    Then I should see the success message "Your message has been sent."
+    And I should see the heading "Some contact test"
+
     # These contact information have been created via UI.
     But I delete the "Invisible Cat" contact information
     And I delete the "Elise Anna Lucile" contact information
diff --git a/web/modules/custom/collection/src/Entity/Collection.php b/web/modules/custom/collection/src/Entity/Collection.php
index 6a10cafa85c61a41fd234b3f026a8c1246f89b44..1a0816c739e1bdb54f33ba84758d27821fe18895 100644
--- a/web/modules/custom/collection/src/Entity/Collection.php
+++ b/web/modules/custom/collection/src/Entity/Collection.php
@@ -7,6 +7,7 @@
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\contact_information\Traits\ContactInformationTrait;
 use Drupal\joinup_bundle_class\DescriptionTrait;
 use Drupal\joinup_bundle_class\JoinupBundleClassFieldAccessTrait;
 use Drupal\joinup_bundle_class\JoinupBundleClassMetaEntityTrait;
@@ -41,6 +42,7 @@ class Collection extends Rdf implements CollectionInterface {
   use StringTranslationTrait;
   use TopicReferencingEntityTrait;
   use DescriptionTrait;
+  use ContactInformationTrait;
 
   /**
    * {@inheritdoc}
diff --git a/web/modules/custom/contact_information/contact_information.libraries.yml b/web/modules/custom/contact_information/contact_information.libraries.yml
index 28e87f62a88dd68a6a26ca700bcd930c52f32cab..855cc151e783f04d53777fe38d3a90dccc84a200 100644
--- a/web/modules/custom/contact_information/contact_information.libraries.yml
+++ b/web/modules/custom/contact_information/contact_information.libraries.yml
@@ -5,3 +5,12 @@ contact-us:
   dependencies:
     - core/drupal
     - core/js-cookie
+
+contact-wysiwyg:
+  version: VERSION
+  js:
+    js/contact-wysiwyg.js: {}
+  dependencies:
+    - core/drupal
+    - core/drupalSettings
+    - core/once
diff --git a/web/modules/custom/contact_information/contact_information.routing.yml b/web/modules/custom/contact_information/contact_information.routing.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b85404a3c7147db5d6e71b53ea26663dd99a092b
--- /dev/null
+++ b/web/modules/custom/contact_information/contact_information.routing.yml
@@ -0,0 +1,14 @@
+entity.rdf_entity.contact:
+  path: '/rdf_entity/{rdf_entity}/contact'
+  defaults:
+    _form: \Drupal\contact_information\Form\ContactForm
+    _title: 'Contact us'
+  requirements:
+    _custom_access: \Drupal\contact_information\Form\ContactForm::access
+  options:
+    parameters:
+      rdf_entity:
+        type: entity:rdf_entity
+        bundle:
+          - collection
+          - solution
diff --git a/web/modules/custom/contact_information/js/contact-wysiwyg.js b/web/modules/custom/contact_information/js/contact-wysiwyg.js
new file mode 100644
index 0000000000000000000000000000000000000000..e4ce5d2106cbfde62108eff5916015f181c928cc
--- /dev/null
+++ b/web/modules/custom/contact_information/js/contact-wysiwyg.js
@@ -0,0 +1,28 @@
+/**
+ * @file
+ * Add current path into contact information wysiwyg.
+ */
+
+((Drupal, once, drupalSettings) => {
+  Drupal.behaviors.contactInformationWysiwyg = {
+    attach(context) {
+      once(
+        "myContactInformation",
+        "a[data-joinup-contact-information]",
+        context
+      ).forEach((element) => {
+        let href;
+        if (drupalSettings.user.uid !== 0) {
+          href = element.getAttribute("data-joinup-contact-information-href");
+          href += drupalSettings.path.currentPath;
+        } else {
+          href = element.getAttribute("data-joinup-contact-information-login");
+          href += `?destination=${drupalSettings.path.currentPath}`;
+        }
+        element.removeAttribute("data-joinup-contact-information-href");
+        element.removeAttribute("data-joinup-contact-information-login");
+        element.href = href;
+      });
+    },
+  };
+})(Drupal, once, drupalSettings);
diff --git a/web/modules/custom/contact_information/js/plugins/contact.png b/web/modules/custom/contact_information/js/plugins/contact.png
new file mode 100644
index 0000000000000000000000000000000000000000..2153217a76d8446f83b380c2fe9880750c1e6a6a
--- /dev/null
+++ b/web/modules/custom/contact_information/js/plugins/contact.png
@@ -0,0 +1,10 @@
+‰PNG
+
+���
IHDR���������\r¨f��ZIDATx^íO¨\WÇw¡­îüC¦PQÛÔ… yµX²kâJÜdºu“j󢮺0ÍÂu^覸IêFj ¸°ŠBSÿ ˆêÂ…Ú Rwõ÷-3ôñœ¹÷ÎÜßïÌùó9p(ÍÜû»ç÷¹¿óygfÞ;÷ø1 Ð,ãÍfNâ€À1@@ a á›OêÈA�_²ÛðU럳þaëà¶@ bÿ°ÜÞ°þKë/XŸí2×]	à=–ô׬?cý£»Àµ!°c²ëï[ÿ±õ·SexŸ%ù돥N–ëA c?µ±}ÅúSŽ1µ�Þ¿Xò|:e’\…ø•óËÖÿj¼)p¿%õs돤JŽë@ @¿°1?žj%R�×,©i7„!C 5gí‚ßJqÑTø˜%óGëúðtÐç�돕J�?²D¾ñ!PïZ.?ˆÎ'•�ô½ç‡z’ùž½þ¼õ¿D'M|ìÀÃví©õo÷ŒáU{ýóÑãL!�-eîö$ò
{ý‡ÑÉøŽåû=ãy¯½þVä˜Sà3–À¯;’ø§½ÆoÿEÞebçJà?60MòuíãöŸ#ŸB�ú^ógIÌì5~)(ò.;WúZüƒÓׁ/G>…�ž°^êHâ–½¦chh€~0êäºv¦gîŒæ…�F#$�¶&€�+€­ë‡'€�@á%ÌðÇ@�`Lýpná�(¼„þ�Œ©Î-œ�@�…—0ÃC� €1õSí¹Xf—¬Ÿ\dxÓþ{u‹l÷ìœóÖ'Öß´~ݺbmÚ¦‹8:onýòâ¿›Æ9z<@�ck¨ºó5Y_³.	nwìNm­&­ö 8Ú$’‹Ä‘0ž<r¼drÎúlƒ8«E�`d	Õwº&Õé5ié'ïþ€”'vÌ*‰,O•H$”¾¶N":oný¡¾�=¯#�0²„ê;½kgÜÛ–îÞ€”ÏÚ17:Ž*’‹¡·ëÚP‘¬; €åÜÎ!Zöÿ«#Ý×íµåç]Tº~r뼡Xµü?|]ýÛlÄíA�`DùÔyêÜÒ:±&5}ˆ§ÉÝ×$	½X×ôþ}ȇú¬àJGœ±Kƒ�@_-7÷úžeüÊŠ¬ïÙ¿ibKCÚsvÐ…}¡Sµ"Ñg«„4tÑ5V€�†ÔrsÇH‡&ž&­~ùàî0¬ýÅyÚ’^Mß�èßô)þÐ&	H&ËÏ$"ÅпmÀò×€c˨Þó5ù6™¬ëHäg9N€�ꝽdÖK� €Þ"á€z	 �Pou“Y/€�z‹„ê%€�@½ÕMf½�è-¨—�@�õV7™õ@� ·H8 ^�Ô[ÝdÖK� €Þ"á€z	 �Pou“Y/&Ð÷pPýý÷o{Qq�ê#ðˆ¥ô`GZzf¦þV&¬ý{æ!ë{8萁	4ñpÐo,9C`0„Ç@ R ÒKZB�¡Ä1¨”�¨ôÆ’†@�C(q*%P…�ú~à7vó¾Yé
$-txÖ^ülÇMü�›‚2IZ%ð’%®I¾®U±èûE Ðjù“7°@�L„V	 �Ðjí“·@�€‰Ð0€�.RG�€YÐ0€�.RG�€YÐ0€�.RG�€YÐ0€�.RG�€YÐ0€�.RG�€YÐ0€�.RG�€YÐ0€�.RG�€YÐ0€�.RG�€YÐ0€�.RG�€YÐ0Ѐ�°/Y?Ùp¡M}n^·>zBáÇ!€Ê0±ü^³.	ІxÊ=~x±G"€ÊpÓò{²ØòÜÝÀß´KŸ²®AÍ
T.€·k®ÞàÜÎY|	´æ†�@Íõ=*·§íìçFEÈÿdP¹�´„=‘f9B½¸“åÈü…�*Àžå÷Š_½4IßLÈT.�Õ°$pÀJ`Ðt¾·Xöï:ºüƒ@X–©¾
+äwÖOZ}ò_û’ÿhö !”ÿóŠ¼	 �à]SÄ+ˆ�@�•+Cõ&€�€wM¯ �T®Õ›�@�Þ5E¼‚ �PP¹2To�x×ñ
+"€�@AåÊP½	 �à]SÄ+ˆ�@�•+Cõ&€�€wM¯ �T®Õ›�H(€‰]‹Í9¼K¸žxúSäԍˆ�‚ I¯-¹ÏZggÞz&kd&Ú‡ðªõYäE±@ �.XìÚ÷”KP£Í^BÐÆ¤Ú§ ª!€ hâK�4Œ! ·J�@Ëýcî:çBà­$ˆ†�œ ÷ùzÏ$ân³Y€DàÝ€³�¦ïš÷]"^ó^4ZYz7à,�Åå]¢Ä[8€8@Ë´Ó7ê¯öÚ<àF²|]u£ì"T‚� ê½\ùåO}?<"j �f^&@ЍxÂâÊtëÚ-{AÇx´]ÜDqc÷vQ;¬�Xì¾òÁ;@P!°KXWÀç»Á@Xº@�®8@NÂ@�A`Y%¬+àŠ“@NÂ@�A`Y%¬+àŠ“@NÂ@�A`Y%¬+àŠ“@NÂ@�A`Y%¬+àŠ“@NÂ@�A`Y%¬+àŠ“@NÂ@�A`Y%¬+m&{²#"‚l‰;'Ì-uÐnÒ]“_Ç>P;ì`P=79°xç©m`SÐ-¡¦\ðP-o§u`[ð’R�¦–ø<xÄ
ãÔÿ#ñþ_á-€ó[�AeÀö$pÝ‚M=Š…� ¾­_	ºi„m‡Àë–êžõ¨'#€ ¨Dem=%øþvê•L	\µXû“_CE�`}½£Õ€dÀç޳£ÒP÷,/=^îÀú,AŽ X�GïáÄþA£RLø£×D�‰@ÙC '�äTŒ%1€�——ˉ�@�9Õ#cIL� €Ä%Çår"€�@NõÈX@� qÉq¹œ �S=2–Ä�H\r\.'�äTŒ%1€�——ˉ�H$�í÷ö¨õINwŸ±dG`f#ºpT X�ÚpŸ‰Ÿ°¤ë¸”þŒü²õ¨}�–”@�ôgÀ׬kw ¶! É¯­ÀîlsòÀs@�úöxx8¬qÑ@�Ð’ÿRã…Kú~æJψhÀY�‹w7âN³iOYö€³�Ø4 J	ùÎç�§8 �gÌ,Þé€EHðd -k åƒAú =ÞõžŽ£´zìÚA:âá ¬�¯�"n"S©}?<"j €:fOY € ›˜Ó[€‹a#lb 8KXWÀç»Á@Xº@�®8@NÂ@�A`Y%¬+àŠ“@NÂ@�A`Y%¬+àŠ“@NÂ@�A`Y%¬+àŠ“@NÂ@�A`Y%¬+àŠ“@NÂ@�A`Y%¬+àŠ“@NÂ@�A`Y%¬+àŠ3Ï@ÔæŽA蛈€ž#¡­ä'׋øSr61à·¬k•àÑú,îq
b´IàAKÛûIAÀY�z¤Ó…6듬	ܳØZ%x7à,�=TK9<	h3Ù©gÀE,à,�qåm@@¥6ROš0@�Ð*@èÚâ9à^²RO[^zkÑ@€�t£ö¬ßD5ÛT̨¥ÿ"€�k% só¤ ¦æ¬K²úÐoºø!âpM(€%s­Î.„0±ÿžˆ¼£Ä.’€&¼žÿ§¯ù´rT÷þÊo@�EV$ƒn‚�@�M:I®&€��s£a�4\þ¤Ž��³ a�4\þ¤Ž��³ a�4\þ¤Ž��³ a�4\þ¤Ž��³ a�4\þ¤Ž��³ a�4WþÚ[ï¼uý…fŽmnƒšY×^�Ñ
 €èË*¾6lÝ·±Á¦w¢Á9ëú3ᨆ�@Tme÷šhšÝ¨º¤=$­"@�u•]L-÷od7ªa’´)hÄ!�«Âºk㟜CÔÞ€�<-†
½äŸþËõÓ_Oòn�x×TvñömD—²ÕæŠx6�@�›WbagÔ"�ºeá¥|<ø–Cä´@}ÐûëƒÀë
­-äí8%yä8°%¸JNëÀeËSÇìºÍl�]ϐ@�[Þ!°%¸JNC�ëo$Ÿ›[Ö%	Z�8&Ó­k Î‰¿Ì
+ �P÷ïÌ �€�ÖàCÀõÅqÆ^êZ=.«ã£#ôàCÀ~F5Á
+€�+€šgxOn� �À[€øРð-@Ýv`À
+€@Ýs|Ô·�s;[}×í¤
 k§"~pË;ć€[‚«ä´¾@)i"€-ïØ\%§M-mVz‹øÆŒÏ�ø ôyÑ;þ‰¡Jn/Úà#v1F� äy1xìv¤¶/µE,ÿÅ €RçÄFãÖ‡kÚ^ûÄFgåqpÔ~€`qù0B…Þ
+Ü´ÞµéFô69ù�Ø´‹?^+‹Ö§™¯nÛøö­Ï‚‰ó€·�Á%–ox­Ôskz«ñ€Uy"�[ý3ž„�HXn\*7�äV“Œ'!€�–—ʍ�@�¹Õ$ãIH� €„寥r#€�@n5Éx@� a¹q©Ü �[M2ž„�HXn\*7�äV“Œ'!€�–—ʍ�@�¹Õ$ãIH� €„寥r#€�@n5Éx@� a¹q©Ü �[M2ž„�HXn\*7�äV“Œ'!€�–—ʍ�@�¹Õ$ãIH� €„寥r#€�@n5Éx@�	as)”FàŒ
X’k<>:ؾǃ‡%G`N�~>Æ@�cèq.
+'P…�>i7á÷…߆]ø”]ô‘Nñ€Æÿ7ëŒL„بŒÀ–ÏG¢sJ%�>Œ¾“įÀã–ÐËÑI¥€òø¢õ+Ö?aý¾èĈ	¼ecþõ‹Ö_M1þ”H‘×€�6 €�6€Å¡¨�¨íŽ’6 €�6€Å¡¨Àÿ�ƒ¯HLØ7Œ����IEND®B`‚
\ No newline at end of file
diff --git a/web/modules/custom/contact_information/js/plugins/plugin.js b/web/modules/custom/contact_information/js/plugins/plugin.js
new file mode 100644
index 0000000000000000000000000000000000000000..f10e3180293e7ddeeb579a2026e08e00d3906e42
--- /dev/null
+++ b/web/modules/custom/contact_information/js/plugins/plugin.js
@@ -0,0 +1,47 @@
+/**
+ * @file
+ * Contact us plugin definition.
+ */
+
+/**
+ * Get HTML of a selection.
+ * @param {string} selection selected text.
+ * @return {html} The selected html with text.
+ */
+const getSelectionHtml = (selection) => {
+  const ranges = selection.getRanges();
+  let html = "";
+  for (let i = 0; i < ranges.length; i++) {
+    const content = ranges[i].extractContents();
+    html += content.getHtml();
+  }
+  return html;
+};
+
+CKEDITOR.plugins.add("contactus", {
+  init(editor) {
+    editor.addCommand("contactus_template", {
+      exec() {
+        let selectedHtml = "";
+        const selection = editor.getSelection();
+        if (selection) {
+          selectedHtml = getSelectionHtml(selection);
+        }
+        if (!selectedHtml) {
+          selectedHtml = "Contact us";
+        }
+        editor.insertHtml(
+          `<a data-joinup-contact-information data-joinup-contact-information-href="${editor.config.contactInformation_url}" data-joinup-contact-information-login="${editor.config.contactInformation_login}" href="">${selectedHtml}</a>`
+        );
+      },
+    });
+    if (editor.config.contactInformation_disable) {
+      editor.ui.addButton("contactus", {
+        label: "Contact us",
+        toolbar: "insert",
+        command: "contactus_template",
+        icon: `${this.path}contact.png`,
+      });
+    }
+  },
+});
diff --git a/web/modules/custom/contact_information/src/Form/ContactForm.php b/web/modules/custom/contact_information/src/Form/ContactForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..2e7d1eba07d78ccc1bfaa4c0304ed76fbbfd22a2
--- /dev/null
+++ b/web/modules/custom/contact_information/src/Form/ContactForm.php
@@ -0,0 +1,92 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\contact_information\Form;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Access\AccessResultInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\email_contact\Form\ContactForm as OriginalContactForm;
+use Drupal\email_contact\Plugin\Field\FieldFormatter\EmailContactLinkFormatter;
+use Drupal\rdf_entity\RdfInterface;
+
+/**
+ * Extend email contact form.
+ */
+class ContactForm extends OriginalContactForm {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, ?RdfInterface $rdf_entity = NULL): array {
+    $user = $this->currentUser();
+    $form['emails'] = [
+      '#type' => 'value',
+      '#value' => serialize($rdf_entity->getValidatedContactInformation()),
+    ];
+
+    $form['name'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Your name'),
+      '#maxlength' => 255,
+      '#default_value' => $user->id() ? $user->getDisplayName() : '',
+      '#required' => TRUE,
+    ];
+
+    $form['mail'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Your e-mail address'),
+      '#maxlength' => 255,
+      '#default_value' => $user->id() ? $user->getEmail() : '',
+      '#required' => TRUE,
+    ];
+
+    $form['subject'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Subject'),
+      '#maxlength' => 255,
+      '#required' => TRUE,
+    ];
+
+    $form['message'] = [
+      '#type' => 'textarea',
+      '#title' => $this->t('Message'),
+      '#required' => TRUE,
+    ];
+
+    if (!$form_state->get('settings')) {
+      $settings = EmailContactLinkFormatter::defaultSettings();
+      $settings['default_message'] = '[current-user:name] sent a message using the contact form at [current-page:url].';
+      $form_state->set('settings', $settings);
+    }
+
+    $form['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Send e-mail'),
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state): void {
+    $this->sendMessage($form, $form_state);
+  }
+
+  /**
+   * Access check for the form.
+   *
+   * @param \Drupal\rdf_entity\RdfInterface $rdf_entity
+   *   The collection for which to check access.
+   *
+   * @return \Drupal\Core\Access\AccessResultInterface
+   *   The access result.
+   */
+  public function access(RdfInterface $rdf_entity): AccessResultInterface {
+    return AccessResult::allowedIf($this->currentUser()->isAuthenticated() && !empty($rdf_entity->getValidatedContactInformation()));
+  }
+
+}
diff --git a/web/modules/custom/contact_information/src/Plugin/CKEditorPlugin/ContactInformation.php b/web/modules/custom/contact_information/src/Plugin/CKEditorPlugin/ContactInformation.php
new file mode 100644
index 0000000000000000000000000000000000000000..3e70c779e2696ce2776791fe22b39458809664ea
--- /dev/null
+++ b/web/modules/custom/contact_information/src/Plugin/CKEditorPlugin/ContactInformation.php
@@ -0,0 +1,101 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\contact_information\Plugin\CKEditorPlugin;
+
+use Drupal\Core\Url;
+use Drupal\ckeditor\CKEditorPluginBase;
+use Drupal\collection\Entity\CollectionInterface;
+use Drupal\editor\Entity\Editor;
+use Drupal\joinup_community_content\Entity\CommunityContentInterface;
+use Drupal\joinup_licence\Entity\LicenceInterface;
+use Drupal\solution\Entity\SolutionInterface;
+
+/**
+ * Defines the "Contact us" plugin.
+ *
+ * @CKEditorPlugin(
+ *   id = "contactus",
+ *   label = @Translation("Contact us"),
+ *   module = "ckeditor_contactus"
+ * )
+ */
+class ContactInformation extends CKEditorPluginBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFile(): string {
+    return \Drupal::service('extension.list.module')->getPath('contact_information') . '/js/plugins/plugin.js';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDependencies(Editor $editor): array {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLibraries(Editor $editor): array {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isInternal(): bool {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getButtons(): array {
+    return [
+      'contactus' => [
+        'label' => $this->t('Contact us'),
+        'image' => \Drupal::service('extension.list.module')->getPath('contact_information') . '/js/plugins/contact.png',
+      ],
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfig(Editor $editor): array {
+    $group = \Drupal::routeMatch()->getParameter('rdf_entity');
+    if (!$group) {
+      $node = \Drupal::routeMatch()->getParameter('node');
+      if ($node instanceof CommunityContentInterface) {
+        $group = $node->getGroup();
+      }
+    }
+
+    $login = $url = NULL;
+    $disable = FALSE;
+    if ($group && !$group instanceof CollectionInterface && !$group instanceof SolutionInterface) {
+      $group = !$group instanceof LicenceInterface && $group->hasGroup() ? $group->getCollection() : NULL;
+    }
+
+    if ($group) {
+      $url = Url::fromRoute('entity.rdf_entity.contact', ['rdf_entity' => $group->id()])->setOption('query', [
+        'destination' => '',
+      ])->toString();
+      $login = Url::fromRoute('cas.login')->setOption('query', [
+        'destination' => Url::fromRoute('entity.rdf_entity.contact', ['rdf_entity' => $group->id()])->toString(),
+      ])->setAbsolute()->toString();
+      $disable = !empty($group->getValidatedContactInformation());
+    }
+
+    return [
+      'contactInformation_disable' => $disable,
+      'contactInformation_url' => $url,
+      'contactInformation_login' => $login,
+    ];
+  }
+
+}
diff --git a/web/modules/custom/contact_information/src/Traits/ContactInformationTrait.php b/web/modules/custom/contact_information/src/Traits/ContactInformationTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..791660ffba53b9e6de0a524805cc256a3b0a960e
--- /dev/null
+++ b/web/modules/custom/contact_information/src/Traits/ContactInformationTrait.php
@@ -0,0 +1,37 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\contact_information\Traits;
+
+use Drupal\joinup_group\Entity\GroupInterface;
+
+/**
+ * Reusable methods for contact information.
+ */
+trait ContactInformationTrait {
+
+  /**
+   * Returns validated emails from collection/solution.
+   */
+  public function getValidatedContactInformation(): array {
+    assert(is_subclass_of($this, GroupInterface::class), __TRAIT__ . ' is intended to be used in bundle classes for group entities.');
+    assert(method_exists($this, 'getReferencedEntityIds'), __TRAIT__ . ' depends on JoinupBundleClassFieldAccessTrait. Please include it in your class.');
+    $ids = $this->getReferencedEntityIds($this->getContactInformationFieldName())['rdf_entity'] ?? [];
+    if (empty($ids)) {
+      return $ids;
+    }
+    $contacts = $this->entityTypeManager()->getStorage('rdf_entity')->loadMultiple($ids);
+    $emails = [];
+    /** @var \Drupal\email_confirmer\EmailConfirmerManagerInterface $email_confirmer */
+    $email_confirmer = \Drupal::service('email_confirmer');
+    foreach ($contacts as $contact) {
+      $confirmation = $email_confirmer->getConfirmation($contact->get('field_ci_email')->value);
+      if ($confirmation && $confirmation->isConfirmed()) {
+        $emails[] = $contact->get('field_ci_email')->value;
+      }
+    }
+    return $emails;
+  }
+
+}
diff --git a/web/modules/custom/solution/src/Entity/Solution.php b/web/modules/custom/solution/src/Entity/Solution.php
index cea0942632c2f08f4b32093144fa1c58498cb0e3..de9431fa4b4b82b16e1d01c0341514e868c4538c 100644
--- a/web/modules/custom/solution/src/Entity/Solution.php
+++ b/web/modules/custom/solution/src/Entity/Solution.php
@@ -11,6 +11,7 @@
 use Drupal\asset_release\Entity\AssetReleaseInterface;
 use Drupal\collection\Entity\CollectionInterface;
 use Drupal\collection\Exception\MissingCollectionException;
+use Drupal\contact_information\Traits\ContactInformationTrait;
 use Drupal\joinup_bundle_class\DescriptionTrait;
 use Drupal\joinup_bundle_class\JoinupBundleClassFieldAccessTrait;
 use Drupal\joinup_bundle_class\JoinupBundleClassMetaEntityTrait;
@@ -46,6 +47,7 @@ class Solution extends Rdf implements SolutionInterface {
   use StringTranslationTrait;
   use TopicReferencingEntityTrait;
   use DescriptionTrait;
+  use ContactInformationTrait;
 
   /**
    * {@inheritdoc}
diff --git a/web/themes/joinup/joinup_theme.libraries.yml b/web/themes/joinup/joinup_theme.libraries.yml
index ca3c8e3361a8354d1c0c1fb2a7c9087557a7241b..31b7178a5b76729ac61cadc56bd627f80296cfac 100644
--- a/web/themes/joinup/joinup_theme.libraries.yml
+++ b/web/themes/joinup/joinup_theme.libraries.yml
@@ -14,6 +14,7 @@ global-components:
     js/script.js: {}
   dependencies:
     - core/jquery
+    - contact_information/contact-wysiwyg
 
 global-search:
   version: 1.x
diff --git a/web/themes/ventuno/ventuno.libraries.yml b/web/themes/ventuno/ventuno.libraries.yml
index 296f2b4e7e2393fec37fdbd57a684cd4471a38c7..ec34a420dc570047b2dd567c0b03c9d3ed1fe078 100644
--- a/web/themes/ventuno/ventuno.libraries.yml
+++ b/web/themes/ventuno/ventuno.libraries.yml
@@ -17,6 +17,7 @@ components:
       assets/css/components.css: {}
   dependencies:
     - ventuno/components.js
+    - contact_information/contact-wysiwyg
 
 components.js:
   version: VERSION