diff --git a/app/assets/javascripts/admin/appstack.js b/app/assets/javascripts/admin/appstack.js
index aa96e4785e2122a1d5a8f8f4f5c96192ec2041f6..288469caaf16052c44adc410d92efad1a25c718e 100644
--- a/app/assets/javascripts/admin/appstack.js
+++ b/app/assets/javascripts/admin/appstack.js
@@ -12,6 +12,7 @@
 //= require summernote/summernote-bs5
 //= require slug/slug
 //= require cocoon
+//= require codemirror/lib/codemirror
 //= require_self
 //= require_tree ./commons
 //= require_tree ../application/plugins
diff --git a/app/assets/javascripts/admin/plugins/codemirror.js b/app/assets/javascripts/admin/plugins/codemirror.js
new file mode 100644
index 0000000000000000000000000000000000000000..553e9afd748b24c9bf3dee73f9bd49c39d0b5d11
--- /dev/null
+++ b/app/assets/javascripts/admin/plugins/codemirror.js
@@ -0,0 +1,53 @@
+//= require codemirror/mode/xml/xml
+//= require codemirror/mode/javascript/javascript
+//= require codemirror/mode/css/css
+//= require codemirror/mode/htmlmixed/htmlmixed
+//= require codemirror/mode/sass/sass
+
+/*global CodeMirror */
+
+// Add [data-provider="codemirror"] to textarea.
+// You need to set [data-codemirror-mode="<language>"] to set the language.
+// You can use set [data-codemirror-indentation="n"] to set the indentation level (2 by default).
+window.codemirrorManager = {
+    init: function () {
+        'use strict';
+        var i;
+        this.textareas = document.querySelectorAll('textarea[data-provider="codemirror"]');
+        this.instances = [];
+        for (i = 0; i < this.textareas.length; i += 1) {
+            this.instances.push(this.createInstance(this.textareas[i]));
+        }
+    },
+
+    createInstance: function (textarea) {
+        'use strict';
+        var mode = textarea.getAttribute('data-codemirror-mode'),
+            indentationLevel = window.parseInt(textarea.getAttribute('data-codemirror-indentation'));
+
+        if (isNaN(indentationLevel)) {
+            indentationLevel = 2;
+        }
+
+        return CodeMirror.fromTextArea(textarea, {
+            lineNumbers: true,
+            matchBrackets: true,
+            styleActiveLine: true,
+            indentUnit: indentationLevel,
+            viewportMargin: Infinity,
+            mode: mode
+        });
+    },
+
+    invoke: function () {
+        'use strict';
+        return {
+            init: this.init.bind(this)
+        };
+    }
+}.invoke();
+
+window.addEventListener('DOMContentLoaded', function () {
+    'use strict';
+    window.codemirrorManager.init();
+});
diff --git a/app/assets/javascripts/admin/pure.js b/app/assets/javascripts/admin/pure.js
index ce84c279a86652825ff124a99876d8cf2f5339c1..94017fb5f5845850521b0e158949325b6794e6f7 100644
--- a/app/assets/javascripts/admin/pure.js
+++ b/app/assets/javascripts/admin/pure.js
@@ -13,6 +13,7 @@
 //= require summernote/summernote-bs5
 //= require slug/slug
 //= require cocoon
+//= require codemirror/lib/codemirror
 //= require_self
 //= require_tree ./commons
 //= require_tree ../application/plugins
diff --git a/app/assets/stylesheets/admin/appstack.sass b/app/assets/stylesheets/admin/appstack.sass
index 06e674993f421ad82117422b524ea86c91d90846..3413526ba440914d25ccfa8aea3f2e44d45ddf3e 100644
--- a/app/assets/stylesheets/admin/appstack.sass
+++ b/app/assets/stylesheets/admin/appstack.sass
@@ -7,6 +7,7 @@
 @import 'summernote-bs5'
 @import 'cropperjs/dist/cropper'
 @import 'gdpr/cookie_consent'
+@import 'codemirror/lib/codemirror'
 @import '../commons/*'
 @import 'commons/*'
 @import 'appstack/*'
diff --git a/app/assets/stylesheets/admin/commons/codemirror.sass b/app/assets/stylesheets/admin/commons/codemirror.sass
new file mode 100644
index 0000000000000000000000000000000000000000..f7c3a66762077a977db4ae333af3814473903be8
--- /dev/null
+++ b/app/assets/stylesheets/admin/commons/codemirror.sass
@@ -0,0 +1,2 @@
+.CodeMirror
+    border: 1px solid #CED4DA
\ No newline at end of file
diff --git a/app/assets/stylesheets/admin/pure.sass b/app/assets/stylesheets/admin/pure.sass
index a769401d2472d51dab00ff4d314da975acb0c262..3a2248c6d08bd76b573ece1fce347666a5d0c63a 100644
--- a/app/assets/stylesheets/admin/pure.sass
+++ b/app/assets/stylesheets/admin/pure.sass
@@ -10,6 +10,7 @@
 @import 'summernote-bs5'
 @import 'cropperjs/dist/cropper'
 @import 'gdpr/cookie_consent'
+@import 'codemirror/lib/codemirror'
 @import '../commons/*'
 @import 'commons/*'
 @import 'pure/buttons'
diff --git a/app/controllers/extranet/pages_controller.rb b/app/controllers/extranet/pages_controller.rb
index 1cb2242cc14d3f32c33f1aeac241531f59861786..a3bc8035e5d7d9c40a26063051c6c4832affdcb5 100644
--- a/app/controllers/extranet/pages_controller.rb
+++ b/app/controllers/extranet/pages_controller.rb
@@ -17,14 +17,21 @@ class Extranet::PagesController < Extranet::ApplicationController
   end
 
   def data
-    @metrics = [
-      { value: current_extranet.alumni.count, name: University::Person::Alumnus.model_name.human(count: 2) },
-      { value: current_extranet.users.count, name: User.model_name.human(count: 2) },
-      { value: current_extranet.experiences.count, name: University::Person::Experience.model_name.human(count: 2) },
-      { value: current_extranet.academic_years.count, name: Education::AcademicYear.model_name.human(count: 2) },
-      { value: current_extranet.cohorts.count, name: Education::Cohort.model_name.human(count: 2) },
-      { value: current_extranet.organizations.count, name: University::Organization.model_name.human(count: 2) },
-    ]
+    @metrics = []
+    if current_extranet.has_feature?(:alumni)
+      @metrics.concat [
+        { value: current_extranet.alumni.count, name: University::Person::Alumnus.model_name.human(count: 2) },
+        { value: current_extranet.academic_years.count, name: Education::AcademicYear.model_name.human(count: 2) },
+        { value: current_extranet.cohorts.count, name: Education::Cohort.model_name.human(count: 2) }
+      ]
+    end
+    if current_extranet.has_feature?(:alumni) || current_extranet.has_feature?(:contacts)
+      @metrics.concat [
+        { value: current_extranet.users.count, name: User.model_name.human(count: 2) },
+        { value: current_extranet.experiences.count, name: University::Person::Experience.model_name.human(count: 2) },
+        { value: current_extranet.organizations.count, name: University::Organization.model_name.human(count: 2) }
+      ]
+    end
     breadcrumb
     add_breadcrumb t('extranet.data')
   end
diff --git a/app/controllers/extranet/style_controller.rb b/app/controllers/extranet/style_controller.rb
index dea8f4d4b07d08303b30e5c00abfabf28fc330eb..3eb7c8587a2876c04a9be5b355261ab86f509bf7 100644
--- a/app/controllers/extranet/style_controller.rb
+++ b/app/controllers/extranet/style_controller.rb
@@ -1,5 +1,7 @@
 class Extranet::StyleController < Extranet::ApplicationController
+  skip_before_action :authenticate_user!, :authorize_extranet_access!
+  
   def index
     render body: current_extranet.css, content_type: 'text/css'
   end
-end
\ No newline at end of file
+end
diff --git a/app/models/communication/extranet.rb b/app/models/communication/extranet.rb
index faabfae4280d7d6a74fa33af518642da3104aaf4..6f227728cd853220b83e747ab96e673f3f656c62 100644
--- a/app/models/communication/extranet.rb
+++ b/app/models/communication/extranet.rb
@@ -104,11 +104,19 @@ class Communication::Extranet < ApplicationRecord
   alias academic_years years
 
   def organizations
-    about&.alumni_organizations
+    if about.present? && about.respond_to?(:alumni_organizations)
+      about.alumni_organizations
+    else
+      connected_organizations
+    end
   end
 
   def experiences
-    about&.alumni_experiences
+    if about.present? && about.respond_to?(:alumni_experiences)
+      about.alumni_experiences
+    else
+      experiences_through_connections
+    end
   end
 
   def url
diff --git a/app/models/communication/extranet/document.rb b/app/models/communication/extranet/document.rb
index 30a353f9f0e1bc72338072f57042b31978c816b4..081f39cbb290caf496bc1cac72d3051b878208c4 100644
--- a/app/models/communication/extranet/document.rb
+++ b/app/models/communication/extranet/document.rb
@@ -8,24 +8,18 @@
 #  published_at  :datetime
 #  created_at    :datetime         not null
 #  updated_at    :datetime         not null
-#  category_id   :uuid             indexed
 #  extranet_id   :uuid             not null, indexed
-#  kind_id       :uuid             indexed
 #  university_id :uuid             not null, indexed
 #
 # Indexes
 #
-#  extranet_document_categories                             (category_id)
 #  index_communication_extranet_documents_on_extranet_id    (extranet_id)
 #  index_communication_extranet_documents_on_university_id  (university_id)
-#  index_extranet_document_kinds                            (kind_id)
 #
 # Foreign Keys
 #
 #  fk_rails_1272fd263c  (extranet_id => communication_extranets.id)
-#  fk_rails_51f7db2f66  (kind_id => communication_extranet_document_kinds.id)
 #  fk_rails_af877a8c0c  (university_id => universities.id)
-#  fk_rails_eb351dc444  (category_id => communication_extranet_document_categories.id)
 #
 class Communication::Extranet::Document < ApplicationRecord
   include Sanitizable
diff --git a/app/models/communication/extranet/with_connections.rb b/app/models/communication/extranet/with_connections.rb
index 67841e9235ab6d571a83acd50aa0c5dbb50ac225..1325bfcec6c8751760fa94ad125c951a992930e5 100644
--- a/app/models/communication/extranet/with_connections.rb
+++ b/app/models/communication/extranet/with_connections.rb
@@ -27,4 +27,8 @@ module Communication::Extranet::WithConnections
     University::Person.where(id: ids)
   end
 
+  def experiences_through_connections
+    University::Person::Experience.where(person_id: connected_persons, organization_id: connected_organizations)
+  end
+
 end
\ No newline at end of file
diff --git a/app/views/admin/communication/blocks/components/_edit.html.erb b/app/views/admin/communication/blocks/components/_edit.html.erb
index b2bc336a1db1ad86780cdde67f3b6f1f2e460177..f3c6a8352944be21b27cfb764eca6860d68614e1 100644
--- a/app/views/admin/communication/blocks/components/_edit.html.erb
+++ b/app/views/admin/communication/blocks/components/_edit.html.erb
@@ -19,6 +19,7 @@ placeholder ||= t "#{i18n_component}.placeholder", default: ''
 hint ||= t "#{i18n_component}.hint", default: ''
 none ||= t "#{i18n_component}.none", default: ''
 summernote_config ||= "mini-list"
+codemirror_mode ||= "htmlmixed"
 partial = "admin/communication/blocks/components/#{component.kind}/edit"
 
 local_assigns[:template] = template
@@ -31,5 +32,6 @@ local_assigns[:placeholder] = placeholder
 local_assigns[:hint] = hint
 local_assigns[:none] ||= t "#{i18n_component}.none", default: ''
 local_assigns[:summernote_config] ||= summernote_config
+local_assigns[:codemirror_mode] ||= codemirror_mode
 %>
 <%= render partial, **local_assigns %>
diff --git a/app/views/admin/communication/blocks/components/code/_edit.html.erb b/app/views/admin/communication/blocks/components/code/_edit.html.erb
index 378a26d41bc751c05ee349f1ddeca7895b1df33d..c21538631ba9c501802a04f93d723a9f2a6c109b 100644
--- a/app/views/admin/communication/blocks/components/code/_edit.html.erb
+++ b/app/views/admin/communication/blocks/components/code/_edit.html.erb
@@ -4,7 +4,8 @@
   <%= label %>
 </label>
 <textarea :id="<%= dom_id.html_safe %>"
-          class="form-control mb-3"
+          class="form-control mb-3 codemirror-vue"
+          data-codemirror-mode="<%= codemirror_mode %>"
           rows="<%= rows %>"
           v-model="<%= model %>.<%= property %>"
           placeholder="<%= placeholder %>"></textarea>
diff --git a/app/views/admin/communication/blocks/edit.html.erb b/app/views/admin/communication/blocks/edit.html.erb
index d57c344baacdba41e8e45f2cabe0122919169d07..e4813e15a33e370cab21e7dfe3c010a6b4dec19a 100644
--- a/app/views/admin/communication/blocks/edit.html.erb
+++ b/app/views/admin/communication/blocks/edit.html.erb
@@ -71,9 +71,9 @@
         this.uploadFile(event.target, files[0], object, key);
       },
       uploadFile(input, file, object, key) {
-        var size = Math.round(file.size / 1024 / 1024), 
+        var size = Math.round(file.size / 1024 / 1024),
             sizeLimit = <%= Communication::Block::IMAGE_MAX_SIZE %>,
-            sizeLimitMo = 0, 
+            sizeLimitMo = 0,
             controller;
         if (input.hasAttribute('data-size-limit')) {
           sizeLimit = input.getAttribute('data-size-limit');
@@ -107,6 +107,13 @@
           this.initSummernote($summernoteElements.get(index));
         }.bind(this));
       },
+      handleCodemirrors() {
+        var $codemirrorElements = $('.codemirror-vue');
+
+        $codemirrorElements.each(function(index){
+          this.initCodemirror($codemirrorElements.get(index));
+        }.bind(this));
+      },
       initSummernote(element) {
         var config = element.getAttribute('data-summernote-config') || 'default';
         $(element).summernote({
@@ -121,10 +128,21 @@
             }.bind(this)
           }
         });
+      },
+      initCodemirror(element) {
+        CodeMirror.fromTextArea(element, {
+            lineNumbers: true,
+            styleActiveLine: true,
+            indentUnit: 2,
+            viewportMargin: Infinity,
+            mode: element.getAttribute('data-codemirror-mode')
+        });
       }
     },
     mounted: function() {
       this.handleSummernotes();
+      // Use Timeout to prevent display issues
+      setTimeout(this.handleCodemirrors.bind(this), 0);
     }
   });
 
diff --git a/app/views/admin/communication/extranets/_form.html.erb b/app/views/admin/communication/extranets/_form.html.erb
index eef002e8e81176d5512e22bc0b363c067fc711ec..c286e5734e615db02d6ccd4412b81f6a3e1bc557 100644
--- a/app/views/admin/communication/extranets/_form.html.erb
+++ b/app/views/admin/communication/extranets/_form.html.erb
@@ -23,7 +23,14 @@
                     direct_upload: true %>
         <%= f.input :color, as: :color %>
         <%= f.input :home_sentence %>
-        <%= f.input :sass %>
+        <%= f.input :sass,
+                    input_html: {
+                      data: {
+                        provider: 'codemirror',
+                        "codemirror-mode": "sass",
+                        "codemirror-indentation": 4
+                      }
+                    } %>
       <% end %>
     </div>
     <div class="col-xl-6">
diff --git a/app/views/admin/university/organizations/static.html.erb b/app/views/admin/university/organizations/static.html.erb
index 60054f0d4fbeacd0eb5c737974fdfedd7db117d9..ac035815dde3defe990bba3c4ff006073013dd31 100644
--- a/app/views/admin/university/organizations/static.html.erb
+++ b/app/views/admin/university/organizations/static.html.erb
@@ -16,8 +16,6 @@ kind:
   :zipcode,
   :city,
   :country,
-  :latitude,
-  :longitude,
   :phone,
   :email,
   :twitter,
@@ -43,6 +41,9 @@ contact_details:
 <%= render 'admin/application/static/contact_detail', variable: :mastodon, data: @about.mastodon, kind: ContactDetails::Mastodon %>
 <%= render 'admin/application/static/contact_detail', variable: :phone, data: @about.phone, kind: ContactDetails::Phone %>
 <%= render 'admin/application/static/contact_detail', variable: :email, data: @about.email, kind: ContactDetails::Email %>
+  geolocation:
+    latitude: <%= @about.latitude %>
+    longitude: <%= @about.longitude %>
 <% if @about.logo.attached? %>
 logo: "<%= @about.logo.blob.id %>"
 <% end %>
diff --git a/app/views/extranet/layouts/devise.html.erb b/app/views/extranet/layouts/devise.html.erb
index b8ebe5083ad89910bc9b58d2daccbed966e33421..554c13eca866b3d294a681f8506f3d266933cbce 100644
--- a/app/views/extranet/layouts/devise.html.erb
+++ b/app/views/extranet/layouts/devise.html.erb
@@ -31,5 +31,6 @@
     </main>
     <%= render 'extranet/gdpr/cookie_consent' %>
     <%= render 'bugsnag' %>
+    <%= javascript_include_tag 'extranet' %>
   </body>
 </html>
diff --git a/db/schema.rb b/db/schema.rb
index a22638c875fdcf348e49f48ff14b1b897ccfa479..c3b3ab0a1e1bce6a6dd4c41a447021c8df8a8725 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -214,7 +214,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_03_09_131421) do
     t.index ["communication_website_post_id", "communication_website_category_id"], name: "post_category"
   end
 
-  create_table "communication_website_git_files", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_git_files", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
     t.string "previous_path"
     t.string "about_type", null: false
     t.uuid "about_id", null: false
diff --git a/package.json b/package.json
index bea2adeadb53d5f70e63ae09c35ceb6b12cb0879..11690b06ba4a455b22621bafeea7231ffad8c7e0 100644
--- a/package.json
+++ b/package.json
@@ -2,6 +2,7 @@
   "dependencies": {
     "bootstrap-icons": "^1.9.1",
     "bootstrap-print-css": "^1.0.1",
+    "codemirror": "5",
     "cropperjs": "^1.5.12",
     "jquery-cropper": "^1.0.1",
     "notyf": "^3.10.0",
diff --git a/test/fixtures/communication/extranet/documents.yml b/test/fixtures/communication/extranet/documents.yml
index fb4fa23733ee89bef580ed42a2418d63d9d1b4cd..be1d0d6ab57326b590356596a4609a335ddd6e69 100644
--- a/test/fixtures/communication/extranet/documents.yml
+++ b/test/fixtures/communication/extranet/documents.yml
@@ -8,24 +8,18 @@
 #  published_at  :datetime
 #  created_at    :datetime         not null
 #  updated_at    :datetime         not null
-#  category_id   :uuid             indexed
 #  extranet_id   :uuid             not null, indexed
-#  kind_id       :uuid             indexed
 #  university_id :uuid             not null, indexed
 #
 # Indexes
 #
-#  extranet_document_categories                             (category_id)
 #  index_communication_extranet_documents_on_extranet_id    (extranet_id)
 #  index_communication_extranet_documents_on_university_id  (university_id)
-#  index_extranet_document_kinds                            (kind_id)
 #
 # Foreign Keys
 #
 #  fk_rails_1272fd263c  (extranet_id => communication_extranets.id)
-#  fk_rails_51f7db2f66  (kind_id => communication_extranet_document_kinds.id)
 #  fk_rails_af877a8c0c  (university_id => universities.id)
-#  fk_rails_eb351dc444  (category_id => communication_extranet_document_categories.id)
 #
 
 one:
diff --git a/yarn.lock b/yarn.lock
index a8431a395052b1f0405556a0c582ccce88c64c1d..c7d515077ce9a861f931e17189c3cecd9862a22a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -107,6 +107,11 @@ bootstrap-print-css@^1.0.1:
   resolved "https://registry.yarnpkg.com/bootstrap-print-css/-/bootstrap-print-css-1.0.1.tgz#acc0264388caebbad0805e60c869d42cd6fe55bf"
   integrity sha512-I73Cw87BaxCccTjo3qEbvn7KRb55msMxTuT7mpkAAY4Obq8iG9xCybdxnJqq+RrykLD79O3092AiJwaKiEex7w==
 
+codemirror@5:
+  version "5.65.12"
+  resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.12.tgz#294fdf097d10ac5b56a9e011a91eff252afc73ae"
+  integrity sha512-z2jlHBocElRnPYysN2HAuhXbO3DNB0bcSKmNz3hcWR2Js2Dkhc1bEOxG93Z3DeUrnm+qx56XOY5wQmbP5KY0sw==
+
 cropperjs@^1.5.12:
   version "1.5.12"
   resolved "https://registry.yarnpkg.com/cropperjs/-/cropperjs-1.5.12.tgz#d9c0db2bfb8c0d769d51739e8f916bbc44e10f50"