diff --git a/Gemfile b/Gemfile
index 0b4b758b8df39549bd388050d24eaebc1cad2b53..b01ab29ad62464c6031fec4e4bd16c404928a6fa 100644
--- a/Gemfile
+++ b/Gemfile
@@ -33,7 +33,8 @@ gem 'kamifusen'#, path: '../kamifusen'
 gem 'kaminari'
 gem 'mini_magick'
 gem 'octokit'
-gem 'omniauth-saml'
+gem 'omniauth-rails_csrf_protection', '~> 1.0'
+gem 'omniauth-saml', '~> 2.0'
 gem 'pg', '~> 1.1'
 gem 'puma'
 gem 'rails', '~> 6.1'
diff --git a/Gemfile.lock b/Gemfile.lock
index 92baf75634214df93fd5990bd3491f2a072575f2..92c4809d69efee91c96d0dd265033cdeab118141 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -320,6 +320,9 @@ GEM
       hashie (>= 3.4.6)
       rack (>= 2.2.3)
       rack-protection
+    omniauth-rails_csrf_protection (1.0.1)
+      actionpack (>= 4.2)
+      omniauth (~> 2.0)
     omniauth-saml (2.1.0)
       omniauth (~> 2.0)
       ruby-saml (~> 1.12)
@@ -511,7 +514,8 @@ DEPENDENCIES
   listen (~> 3.3)
   mini_magick
   octokit
-  omniauth-saml
+  omniauth-rails_csrf_protection (~> 1.0)
+  omniauth-saml (~> 2.0)
   pg (~> 1.1)
   puma
   rack-mini-profiler (~> 2.0)
diff --git a/app/assets/stylesheets/admin/treeview.sass b/app/assets/stylesheets/admin/treeview.sass
index b77719ec75f72eac129591189104c62f3e22c9e8..a873aebfaaf3b5848df9459c684012961e4c882f 100644
--- a/app/assets/stylesheets/admin/treeview.sass
+++ b/app/assets/stylesheets/admin/treeview.sass
@@ -1,5 +1,8 @@
 .treeview
     &__element
+        .show-on-hover
+            display: none
+
         & > .treeview__children .treeview__empty
             display: none
 
@@ -70,3 +73,5 @@
                     & > a .open_text,
                     & > a .close_text
                         opacity: 1
+                .show-on-hover
+                    display: inline
diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb
index be8f1f99dfc7cd5b53c57a3c380f90d5ab0fdea8..47b36fc4d6bb9823d4f47ddda8486fe7f53d70e1 100644
--- a/app/controllers/users/omniauth_callbacks_controller.rb
+++ b/app/controllers/users/omniauth_callbacks_controller.rb
@@ -32,12 +32,12 @@ class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
 
   def manage_user(user_infos)
     @user = User.from_omniauth(current_university, user_infos)
-    
+
     if @user&.persisted?
       @user.remember_me = true
       sign_in_and_redirect @user, event: :authentication
     else
-      flash[:notice] = tt('devise.omniauth_callbacks.failure')
+      flash[:notice] = t('devise.omniauth_callbacks.failure')
       redirect_to new_user_session_url
     end
   end
diff --git a/app/models/communication/block/template/page.rb b/app/models/communication/block/template/page.rb
index 2a80625d176fdff00822cc720ebed7f651a3af98..dd60b571e3c046c63e06f055ddbece03b4b1b140 100644
--- a/app/models/communication/block/template/page.rb
+++ b/app/models/communication/block/template/page.rb
@@ -1,18 +1,19 @@
 class Communication::Block::Template::Page < Communication::Block::Template
   def build_git_dependencies
-    # add_dependency category unless category.nil?
-    # add_dependency selected_posts
-    # selected_posts.each do |post|
-    #   add_dependency post.active_storage_blobs
-    #   if post.author.present?category.nil? ? free_posts : category_posts
-    #     add_dependency [post.author, post.author.author]
-    #     add_dependency post.author.active_storage_blobs
-    #   end
-    # end
+    add_dependency main_page
+    selected_pages.each do |hash|
+      page = hash.page
+      add_dependency page
+      add_dependency page.active_storage_blobs
+    end
   end
 
   def selected_pages
-    @selected_pages ||= free_pages
+    @selected_pages ||= elements.map { |element|
+      p = page(element['id'])
+      next if p.nil?
+      hash_from_page(p, element)
+    }.compact
   end
 
   def main_page
@@ -29,19 +30,19 @@ class Communication::Block::Template::Page < Communication::Block::Template
 
   protected
 
-  def free_pages
-    elements.map { |element| 
-                  {
-                    page: page(element['id']),
-                    show_description: element['show_description'] || false,
-                    show_image: element['show_image'] || false
-                  }.to_dot
-                }
-                .compact
+  def hash_from_page(page, element)
+    {
+      page: page,
+      show_description: element['show_description'] || false,
+      show_image: element['show_image'] || false
+    }.to_dot
   end
 
   def page(id)
     return if id.blank?
-    page = block.about&.website.pages.find_by id: id
+    page = block.about&.website
+                       .pages
+                       .published
+                       .find_by(id: id)
   end
 end
diff --git a/app/models/communication/block/template/post.rb b/app/models/communication/block/template/post.rb
index aa4143e902a6f1ce6381973061d73b5013146d0e..90df81731bc1dc28b994b73cc4540f5fddf9ef44 100644
--- a/app/models/communication/block/template/post.rb
+++ b/app/models/communication/block/template/post.rb
@@ -12,11 +12,13 @@ class Communication::Block::Template::Post < Communication::Block::Template
   end
 
   def category
-    @category ||= block.about&.website.categories.find_by(id: data['category_id'])
+    @category ||= block.about&.website
+                              .categories
+                              .find_by(id: data['category_id'])
   end
 
   def selected_posts
-    # kind could be : selection, category, or all
+    # kind could be: selection, category, or all
     @selected_posts ||= send "selected_posts_#{kind}"
   end
 
@@ -28,7 +30,11 @@ class Communication::Block::Template::Post < Communication::Block::Template
 
   def selected_posts_all
     quantity = data['posts_quantity'] || 3
-    block.about&.website.posts.ordered.limit(quantity)
+    block.about&.website
+                .posts
+                .published
+                .ordered
+                .limit(quantity)
   end
 
   def selected_posts_category
@@ -37,6 +43,7 @@ class Communication::Block::Template::Post < Communication::Block::Template
     university.communication_website_posts.joins(:categories)
                                           .where(categories: { id: category_ids })
                                           .distinct
+                                          .published
                                           .ordered
                                           .limit(quantity)
   end
@@ -48,6 +55,9 @@ class Communication::Block::Template::Post < Communication::Block::Template
 
   def post(id)
     return if id.blank?
-    block.about&.website.posts.find_by id: id
+    block.about&.website
+                .posts
+                .published
+                .find_by(id: id)
   end
 end
diff --git a/app/models/communication/website/index_page.rb b/app/models/communication/website/index_page.rb
deleted file mode 100644
index 4c80742baaf0e5c20f062c70e072ae5a84e4a355..0000000000000000000000000000000000000000
--- a/app/models/communication/website/index_page.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-# == Schema Information
-#
-# Table name: communication_website_index_pages
-#
-#  id                       :uuid             not null, primary key
-#  breadcrumb_title         :string
-#  description              :text
-#  featured_image_alt       :string
-#  header_text              :text
-#  kind                     :integer
-#  path                     :string
-#  text                     :text
-#  title                    :string
-#  created_at               :datetime         not null
-#  updated_at               :datetime         not null
-#  communication_website_id :uuid             not null, indexed
-#  university_id            :uuid             not null, indexed
-#
-# Indexes
-#
-#  idx_comm_website_index_page_on_communication_website_id   (communication_website_id)
-#  index_communication_website_index_pages_on_university_id  (university_id)
-#
-# Foreign Keys
-#
-#  fk_rails_5cd2482227  (communication_website_id => communication_websites.id)
-#  fk_rails_7eb45227ae  (university_id => universities.id)
-#
-class Communication::Website::IndexPage < ApplicationRecord
-  include WithUniversity
-  include Sanitizable
-  include WithFeaturedImage
-  include WithBlobs
-
-  enum kind: {
-    home: 0,
-    communication_posts: 10,
-    education_programs: 20,
-    research_articles: 30,
-    research_volumes: 32,
-    legal_terms: 80,
-      sitemap: 81,
-      privacy_policy: 82,
-    organizations: 90,
-    persons: 100,
-      administrators: 110,
-      authors: 120,
-      researchers: 130,
-      teachers: 140
-  }
-
-  belongs_to :website, foreign_key: :communication_website_id
-
-  has_summernote :header_text
-  has_summernote :text
-
-  validates :title, presence: true
-  validates :path, presence: true, unless: Proc.new { |p| p.home? }
-
-  def to_s
-    "#{title}"
-  end
-
-end
diff --git a/app/models/communication/website/menu/item.rb b/app/models/communication/website/menu/item.rb
index cc4355371cf5b0138b3ccaa49798e84dcd783d81..5c0cebf91d28576e6837d746a9d55fd93344e758 100644
--- a/app/models/communication/website/menu/item.rb
+++ b/app/models/communication/website/menu/item.rb
@@ -122,7 +122,7 @@ class Communication::Website::Menu::Item < ApplicationRecord
   end
 
   def sync_menu
-    menu.sync_with_git if menu
+    menu.sync_with_git if menu && !menu.destroyed?
   end
 
   def siblings
diff --git a/app/models/communication/website/page.rb b/app/models/communication/website/page.rb
index 9b359f9af8ed67e8ce6a5f1a869c2af23bd3bef8..121a75e1d55994fd6b5c4e206f7c320176be9943 100644
--- a/app/models/communication/website/page.rb
+++ b/app/models/communication/website/page.rb
@@ -75,9 +75,16 @@ class Communication::Website::Page < ApplicationRecord
 
   validates :title, presence: true
 
-  validates :slug, presence: true, unless: :kind_home?
-  validate :slug_must_be_unique
-  validates :slug, format: { with: /\A[a-z0-9\-]+\z/, message: I18n.t('slug_error') }, unless: :kind_home?
+  validates :slug,
+            presence: true,
+            unless: :kind_home?
+  validate  :slug_must_be_unique
+  validates :slug,
+            format: {
+              with: /\A[a-z0-9\-]+\z/,
+              message: I18n.t('slug_error')
+            },
+            unless: :kind_home?
 
   before_validation :check_slug, :make_path
   after_save :update_children_paths, if: :saved_change_to_path?
@@ -90,7 +97,9 @@ class Communication::Website::Page < ApplicationRecord
   end
 
   def path_without_language
-    if parent_id.present?
+    if kind_home?
+      "/"
+    elsif parent_id.present?
       "#{parent&.path_without_language}#{slug}/".gsub(/\/+/, '/')
     else
       "/#{slug}/".gsub(/\/+/, '/')
diff --git a/app/models/communication/website/with_special_pages.rb b/app/models/communication/website/with_special_pages.rb
index 9b898c81fa6bc1ffc53c67360a89cf0acfd2717c..4b01e1a9dceef3b4b743ab306330a559615abd4c 100644
--- a/app/models/communication/website/with_special_pages.rb
+++ b/app/models/communication/website/with_special_pages.rb
@@ -38,42 +38,14 @@ module Communication::Website::WithSpecialPages
 
   def create_special_page(kind, parent_id = nil)
     i18n_key = "communication.website.pages.defaults.#{kind}"
-    # TODO: remove legacy after migrations
-    legacy_index_page = Communication::Website::IndexPage.where(communication_website_id: id, kind: kind).first
-    if legacy_index_page.present?
-      page = pages.where(kind: kind).first
-      unless page.present?
-        page = pages.create(
-          kind: kind,
-          title: legacy_index_page.title,
-          slug: legacy_index_page.path,
-          description_short: legacy_index_page.description,
-          parent_id: parent_id,
-          published: true,
-          university_id: university_id,
-          breadcrumb_title: legacy_index_page.breadcrumb_title,
-          featured_image_alt: legacy_index_page.featured_image_alt,
-          header_text: legacy_index_page.header_text,
-          text: legacy_index_page.text
-        )
-        if legacy_index_page.featured_image.attached?
-          blob_to_duplicate = legacy_index_page.featured_image.blob
-          page.featured_image.attach(
-            io: URI.open(blob_to_duplicate.url),
-            filename: blob_to_duplicate.filename.to_s
-          )
-        end
-      end
-    else
-      page = pages.where(kind: kind).first_or_create(
-        title: I18n.t("#{i18n_key}.title"),
-        slug: I18n.t("#{i18n_key}.slug"),
-        description_short: I18n.t("#{i18n_key}.description_short"),
-        parent_id: parent_id,
-        published: true,
-        university_id: university_id
-      )
-    end
+    page = pages.where(kind: kind).first_or_create(
+      title: I18n.t("#{i18n_key}.title"),
+      slug: I18n.t("#{i18n_key}.slug"),
+      description_short: I18n.t("#{i18n_key}.description_short"),
+      parent_id: parent_id,
+      published: true,
+      university_id: university_id
+    )
     page
   end
 
diff --git a/app/models/university/with_sso.rb b/app/models/university/with_sso.rb
index 08dec1a53fc213d78f7a0515ba35688a90cd5e20..91ef0d463e0143666a8bdaf6deff7b936dc8b30e 100644
--- a/app/models/university/with_sso.rb
+++ b/app/models/university/with_sso.rb
@@ -5,7 +5,7 @@ module University::WithSso
     enum sso_provider: { saml: 0 }, _prefix: :with_sso_via
 
     validates :sso_cert, :sso_name_identifier_format, :sso_target_url, presence: true, if: :has_sso?
-
+    validate :sso_mapping_should_have_email, if: :has_sso?
   end
 
   # Setter to serialize data as JSON
@@ -18,4 +18,7 @@ module University::WithSso
     super(value)
   end
 
+  def sso_mapping_should_have_email
+    errors.add(:sso_mapping, :missing_email) unless (sso_mapping || []).detect { |sso_item| sso_item['internal_key'] == 'email' }
+  end
 end
diff --git a/app/models/user.rb b/app/models/user.rb
index 97c8b7767781e08d02385cc1a8f3b73b6c7b82e7..0b7266b7eee47105b9edd65fa93bd81db33a373d 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -22,6 +22,7 @@
 #  last_sign_in_ip               :string
 #  locked_at                     :datetime
 #  mobile_phone                  :string
+#  picture_url                   :string
 #  remember_created_at           :datetime
 #  reset_password_sent_at        :datetime
 #  reset_password_token          :string           indexed
@@ -53,7 +54,7 @@
 #  fk_rails_bd6f7212a9  (university_id => universities.id)
 #
 class User < ApplicationRecord
-  has_one_attached_deletable :picture  # In this order, "resize avatar" callback will be fired after the others.
+  include WithAvatar
   include WithUniversity
   include WithAuthentication
   include WithOmniauth
diff --git a/app/models/user/with_avatar.rb b/app/models/user/with_avatar.rb
new file mode 100644
index 0000000000000000000000000000000000000000..863b3d0a237145865bfcec2217b67d65d5f1cdec
--- /dev/null
+++ b/app/models/user/with_avatar.rb
@@ -0,0 +1,37 @@
+module User::WithAvatar
+  extend ActiveSupport::Concern
+
+  included do
+    has_one_attached_deletable :picture # Nota: user has a picture_url property for SSO mapping. If picture_url is set it will use the url to change the picture
+
+    before_save :update_picture, if: :will_save_change_to_picture_url?
+    after_save :update_picture_url
+
+    private
+
+    def update_picture
+      if picture_url.blank?
+        do_purge_picture
+      else
+        do_update_picture
+      end
+    end
+
+    def update_picture_url
+      if picture_url.present? && !picture.attached?
+        self.update_column(:picture_url, nil)
+      end
+    end
+
+    def do_purge_picture
+      self.picture.purge if self.picture.attached?
+    end
+
+    def do_update_picture
+      downloaded_image = open(picture_url)
+      content_type = downloaded_image.content_type
+      extension = content_type.split('/').last
+      self.picture.attach(io: downloaded_image, filename: "avatar.#{extension}")
+    end
+  end
+end
diff --git a/app/models/user/with_omniauth.rb b/app/models/user/with_omniauth.rb
index 24d8361368f2e7c1cdceb2bdd808f89df7b45fa1..64ba7ddc5433e77746467b4deb1b35c2d82f0089 100644
--- a/app/models/user/with_omniauth.rb
+++ b/app/models/user/with_omniauth.rb
@@ -4,16 +4,13 @@ module User::WithOmniauth
   included do
 
     def self.from_omniauth(university, attributes)
-      mapping = university.sso_mapping
+      mapping = university.sso_mapping || []
 
       # first step: we find the email (we are supposed to have an email mapping)
-      email_sso_key = mapping.select { |elmt| elmt['internal_key'] == 'email' }&.first&.dig('sso_key')
-      email = attributes.dig(email_sso_key)
+      email = get_email_from_mapping(mapping, attributes)
       return unless email
-      email = email.first if email.is_a?(Array)
-      email = email.downcase
 
-      user = User.where(university: university, email: email).first_or_create do |u|
+      user = User.where(university: university, email: email.downcase).first_or_create do |u|
         u.password = "#{Devise.friendly_token[0,20]}!" # meets password complexity requirements
       end
 
@@ -28,6 +25,13 @@ module User::WithOmniauth
 
     protected
 
+    def self.get_email_from_mapping(mapping, attributes)
+      email_sso_key = mapping.detect { |elmt| elmt['internal_key'] == 'email' }&.dig('sso_key')
+      email = attributes.dig(email_sso_key)
+      email = email.first if email.is_a?(Array)
+      email
+    end
+
     def self.update_data_for_mapping_element(user, mapping_element, attributes)
       sso_key = mapping_element['sso_key']
       return user if attributes[sso_key].nil? # if not provided by sso, just return
@@ -39,7 +43,7 @@ module User::WithOmniauth
     def self.update_data_for_mapping_element_standard(user, mapping_element, sso_value)
       case mapping_element['internal_key']
       when 'language'
-        user = self.set_best_id_for(user, mapping_element['type'], sso_value.first)
+        user = self.set_best_id_for(user, 'language', sso_value.first)
       when 'role'
         value = mapping_element['roles'].select { |key, val| val == sso_value.first }.first&.first
         user['role'] = value if value
@@ -55,7 +59,7 @@ module User::WithOmniauth
     end
 
     def self.set_best_id_for(user, type, iso)
-      element_id = eval(type.classify).find_by(iso_code: iso)&.id
+      element_id = type.classify.safe_constantize.find_by(iso_code: iso)&.id
       user["#{type}_id"] = element_id unless element_id.nil?
       user
     end
diff --git a/app/views/admin/communication/blocks/_preview.html.erb b/app/views/admin/communication/blocks/_preview.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..72d57e3ae7ad6244f32e791ac6204ec3af38cd3a
--- /dev/null
+++ b/app/views/admin/communication/blocks/_preview.html.erb
@@ -0,0 +1,6 @@
+<h2 class="h4 mt-5"><%= block.title %></h2>
+<%
+@block = block
+@preview = true
+%>
+<%= render "admin/communication/blocks/templates/#{@block.template_kind}/show" %>
diff --git a/app/views/admin/communication/blocks/templates/call_to_action/_edit.html.erb b/app/views/admin/communication/blocks/templates/call_to_action/_edit.html.erb
index db8501c429ddb80d89d16d221a164b7d57c0e136..74caee4a0f55716c0f7d1eb925d5abbd42cbc358 100644
--- a/app/views/admin/communication/blocks/templates/call_to_action/_edit.html.erb
+++ b/app/views/admin/communication/blocks/templates/call_to_action/_edit.html.erb
@@ -1,6 +1,5 @@
-<div class="row">
+<div class="row mb-5">
   <div class="col-xxl-4 col-md-6">
-    <h3>Contenu</h3>
     <label  class="form-label"
             for="text">
       <%= t '.text_label' %>
@@ -12,88 +11,86 @@
               data-summernote-config="mini"
               placeholder="<%= t '.text_placeholder' %>"></textarea>
     </div>
-    <h3 class="mt-4"><%= t '.image_title' %></h3>
-    <div class="row">
-      <div class="col-md-6">
-        <div v-if="!data.image">
-          <%# TODO : create a uploader vue3 component %>
-          <label  class="form-label"
-                  for="image">
-            <%= t '.image_label' %>
-          </label>
-          <input  class="form-control mb-2"
-                  type="file"
-                  accept="image/*"
-                  @change="onFileImageChange( $event, data, 'image' )"
-                  id="image">
-        </div>
-        <div v-if="data.image">
-          <img :src="getImageUrl(data.image)"
-                class="img-fluid"
-                style="max-height: 80px"
-                />
-          <br>
-          <a  class="btn btn-sm btn-danger mt-2"
-              v-on:click="data.image=null">
-              <i class="fas fa-times"></i>
-              <%= t '.remove_image' %>
-          </a>
-        </div>
-      </div>
-      <div class="col-md-6">
-        <label  class="form-label"
-                for="image_alt">
-          <%= t '.image_alt_label' %>
-        </label>
-        <input id="image_alt"
-                type="text"
-                class="form-control"
-                v-model="data.image_alt"
-                placeholder="<%= t '.image_alt_placeholder' %>" />
-      </div>
-    </div>
   </div>
   <div class="col-xxl-4 col-md-6">
-    <h3>Bouton principal</h3>
+    <div v-if="!data.image">
+      <%# TODO : create a uploader vue3 component %>
+      <label  class="form-label"
+              for="image">
+        <%= t '.image_title' %>
+      </label>
+      <input  class="form-control mb-2"
+              type="file"
+              accept="image/*"
+              @change="onFileImageChange( $event, data, 'image' )"
+              id="image">
+    </div>
+    <div v-if="data.image">
+      <img :src="getImageUrl(data.image)"
+            class="img-fluid"
+            style="max-height: 80px"
+            />
+      <br>
+      <a  class="btn btn-sm btn-danger mt-2"
+          v-on:click="data.image=null">
+          <i class="fas fa-times"></i>
+          <%= t '.remove_image' %>
+      </a>
+    </div>
     <label  class="form-label"
-            for="url">
-      <%= t '.url_label' %>
+            for="image_alt">
+      <%= t '.image_alt_label' %>
     </label>
-    <input id="url"
-            type="url"
+    <input id="image_alt"
+            type="text"
             class="form-control"
-            v-model="data.url"
-            placeholder="<%= t '.url_placeholder' %>" />
+            v-model="data.image_alt"
+            placeholder="<%= t '.image_alt_placeholder' %>" />
+  </div>
+</div>
+<h2><%= t '.buttons' %></h2>
+<div class="row">
+  <div class="col-xxl-4 col-md-6">
+    <h3 class="h4"><%= t '.button_1' %></h3>
 
-    <label  class="form-label mt-3"
-            for="button">
+    <label class="form-label" for="button">
       <%= t '.button_label' %>
     </label>
-    <input id="button"
+    <input  id="button"
             type="text"
             class="form-control"
             v-model="data.button"
             placeholder="<%= t '.button_placeholder' %>" />
 
-    <h3 class="mt-4">Bouton secondaire</h3>
-    <label  class="form-label"
-            for="url_secondary">
+    <label class="form-label mt-3" for="url">
       <%= t '.url_label' %>
     </label>
-    <input id="url_secondary"
+    <input id="url"
             type="url"
             class="form-control"
-            v-model="data.url_secondary"
+            v-model="data.url"
             placeholder="<%= t '.url_placeholder' %>" />
-    <label  class="form-label mt-3"
-            for="button">
+
+  </div>
+  <div class="col-xxl-4 col-md-6">
+    <h3 class="h4"><%= t '.button_2' %></h3>
+
+    <label class="form-label" for="button">
       <%= t '.button_label' %>
     </label>
-    <input id="button_secondary"
+    <input  id="button_secondary"
             type="text"
             class="form-control"
             v-model="data.button_secondary"
             placeholder="<%= t '.button_placeholder' %>" />
 
+    <label class="form-label mt-3" for="url_secondary">
+      <%= t '.url_label' %>
+    </label>
+    <input  id="url_secondary"
+            type="url"
+            class="form-control"
+            v-model="data.url_secondary"
+            placeholder="<%= t '.url_placeholder' %>" />
   </div>
-</div>
\ No newline at end of file
+</div>
diff --git a/app/views/admin/communication/blocks/templates/call_to_action/_show.html.erb b/app/views/admin/communication/blocks/templates/call_to_action/_show.html.erb
index 6acdd62e330833956c01b7b1109f75ea3f4ed947..bb36af7503343bb9e10a3630df0e96522018e057 100644
--- a/app/views/admin/communication/blocks/templates/call_to_action/_show.html.erb
+++ b/app/views/admin/communication/blocks/templates/call_to_action/_show.html.erb
@@ -1,23 +1,35 @@
-<div class="col-md-8 col-xl-6">
-  <div class="card">
-    <div class="card-body">
-      <% if @block.template.image  %>
-        <div style= "max-width: 200px;" class="me-3">
-          <%= kamifusen_tag @block.template.image.blob,
-                          width: 200,
-                          alt: @block.template.image.alt,
-                          class: 'img-fluid' %>
-          <caption><%= @block.template.image.alt %></caption>
-        </div>
-      <% end %>
-      <hr>
-      <%= @block.template.text.html_safe %>
-      <a href="<%= @block.template.url %>" class="btn btn-primary" target="_blank" rel="noopener">
-          <%= @block.template.button %>
-      </a>
-      <a href="<%= @block.template.url_secondary %>" class="btn btn-secondary" target="_blank" rel="noopener">
-          <%= @block.template.button_secondary %>
-      </a>
+<div class="<%= 'row' unless @preview %>">
+  <div class="<%= 'col-md-8 col-xl-6' unless @preview %>">
+    <div class="card">
+      <div class="card-body">
+        <% if @block.template.image  %>
+          <div style= "max-width: 200px;" class="me-3">
+            <%= kamifusen_tag @block.template.image.blob,
+                            width: 200,
+                            alt: @block.template.image.alt,
+                            class: 'img-fluid' %>
+            <caption><%= @block.template.image.alt %></caption>
+          </div>
+          <hr>
+        <% end %>
+        <%= @block.template.text.html_safe %>
+        <% unless @block.template.url.blank? %>
+        <a  href="<%= @block.template.url %>"
+            class="btn btn-primary"
+            target="_blank"
+            rel="noopener">
+            <%= @block.template.button %>
+        </a>
+        <% end %>
+        <% unless @block.template.url_secondary.blank? %>
+          <a  href="<%= @block.template.url_secondary %>"
+              class="btn btn-secondary"
+              target="_blank"
+              rel="noopener">
+              <%= @block.template.button_secondary %>
+          </a>
+        <% end %>
+      </div>
     </div>
   </div>
 </div>
diff --git a/app/views/admin/communication/blocks/templates/chapter/_edit.html.erb b/app/views/admin/communication/blocks/templates/chapter/_edit.html.erb
index 0f570a940377bc1cb0e1c86608d893742bdc86f7..f9af3e9f1f779e9f5545b946b570b0abc9d331da 100644
--- a/app/views/admin/communication/blocks/templates/chapter/_edit.html.erb
+++ b/app/views/admin/communication/blocks/templates/chapter/_edit.html.erb
@@ -1,5 +1,5 @@
-<div class="row">
-  <div class="col-xxl-4 col-md-6">
+<div class="row mb-4">
+  <div class="col-md-6">
     <label  class="form-label"
             for="text">
       <%= t '.text_label' %>
@@ -14,7 +14,7 @@
   </div>
 </div>
 <div class="row">
-  <div class="col-xxl-4 col-md-6">
+  <div class="col-md-6">
     <label  class="form-label"
             for="notes">
       <%= t '.notes_label' %>
diff --git a/app/views/admin/communication/blocks/templates/gallery/_show.html.erb b/app/views/admin/communication/blocks/templates/gallery/_show.html.erb
index 0317b5ab85c4ec3ae2e2434148c27e6d5fb6164e..85b3ef064ef15f41b44eeade08097b24746e3299 100644
--- a/app/views/admin/communication/blocks/templates/gallery/_show.html.erb
+++ b/app/views/admin/communication/blocks/templates/gallery/_show.html.erb
@@ -1,6 +1,6 @@
-<div class="row">
+<div class="<%= 'row' unless @preview %>">
   <% @block.template.images_with_alt.each do |image| %>
-    <div class="col-xxl-2 col-xl-3 col-6">
+    <div class="<%= 'col-xxl-2 col-xl-3 col-6' unless @preview %>">
       <article class="card">
         <% if image.blob %>
           <%= kamifusen_tag image.blob, width: 500, class: 'img-fluid mb-2' %>
diff --git a/app/views/admin/communication/blocks/templates/organization_chart/_show.html.erb b/app/views/admin/communication/blocks/templates/organization_chart/_show.html.erb
index d3b94cf8375e02c1c775c1d7deace62577bb91a6..f0181c8f801110e335636fdf6937065c9b3c0b5c 100644
--- a/app/views/admin/communication/blocks/templates/organization_chart/_show.html.erb
+++ b/app/views/admin/communication/blocks/templates/organization_chart/_show.html.erb
@@ -1,7 +1,7 @@
-<div class="row">
+<div class="<%= 'row' unless @preview %>">
   <% @block.template.persons_with_role.each do |person_with_role|
         person = person_with_role.person %>
-    <div class="col-md-3">
+    <div class="<%= 'col-md-3' unless @preview %>">
       <article class="card">
         <div class="card-body">
           <% if person.best_picture.attached? %>
diff --git a/app/views/admin/communication/blocks/templates/pages/_show.html.erb b/app/views/admin/communication/blocks/templates/pages/_show.html.erb
index 4ea468c6f75c783707c3d7db3edb64c6ce2ac8b8..2175cc87a7b1a018eda00daa0059ecf2c3d2a79f 100644
--- a/app/views/admin/communication/blocks/templates/pages/_show.html.erb
+++ b/app/views/admin/communication/blocks/templates/pages/_show.html.erb
@@ -3,9 +3,9 @@
     <h2 class="mb-4"><%= @block.template.main_page.slug %></h2>
   <% end %>
   <% if @block.template.selected_pages.any? %>
-    <div class="row">
+    <div class="<%= 'row' unless @preview %>">
       <% @block.template.selected_pages.each do |element| %>
-        <div class="col-md-4">
+        <div class="<%= 'col-md-4' unless @preview %>">
           <div class="card">
             <div class="card-header">
               <h3 class="card-title h5"><%= element.page %></h3>
diff --git a/app/views/admin/communication/blocks/templates/partners/_show.html.erb b/app/views/admin/communication/blocks/templates/partners/_show.html.erb
index 5417072ca991d5276224c96957e1acd61ae51039..6c9a10ff598b736478c2963eee42186f3c0a2194 100644
--- a/app/views/admin/communication/blocks/templates/partners/_show.html.erb
+++ b/app/views/admin/communication/blocks/templates/partners/_show.html.erb
@@ -1,6 +1,6 @@
-<div class="row">
+<div class="<%= 'row' unless @preview %>">
   <% @block.template.partners.each do |partner| %>
-    <div class="col-md-2">
+    <div class="<%= 'col-md-2' unless @preview %>">
       <article class="card">
         <%= kamifusen_tag partner.blob,
                           width: 300,
diff --git a/app/views/admin/communication/blocks/templates/posts/_show.html.erb b/app/views/admin/communication/blocks/templates/posts/_show.html.erb
index 06a38ef4d9c3781774271dd075a20b6fa58ad964..7f6d68cf166042ca1dddb307bacb87b7910d598e 100644
--- a/app/views/admin/communication/blocks/templates/posts/_show.html.erb
+++ b/app/views/admin/communication/blocks/templates/posts/_show.html.erb
@@ -3,9 +3,9 @@
     <h2 class="mb-4"><%= @block.template.category %></h2>
   <% end %>
   <% if @block.template.selected_posts.any? %>
-    <div class="row">
+    <div class="<%= 'row' unless @preview %>">
       <% @block.template.selected_posts.each do |post| %>
-        <div class="col-md-4">
+        <div class="<%= 'col-md-4' unless @preview %>">
           <div class="card">
             <div class="card-header">
               <h3 class="card-title h5"><%= post %></h3>
diff --git a/app/views/admin/communication/blocks/templates/testimonials/_show.html.erb b/app/views/admin/communication/blocks/templates/testimonials/_show.html.erb
index 6d8007f766d71ce160ad4534ad0882239456c845..1237e4918d078c790a58ec14957646e7a873242d 100644
--- a/app/views/admin/communication/blocks/templates/testimonials/_show.html.erb
+++ b/app/views/admin/communication/blocks/templates/testimonials/_show.html.erb
@@ -1,6 +1,6 @@
-<div class="row">
+<div class="<%= 'row' unless @preview %>">
   <% @block.template.testimonials.each do |testimonial| %>
-      <div class="col-xxl-4 col-xl-6">
+      <div class="<%= 'col-xxl-4 col-xl-6' unless @preview %>">
         <article class="card">
           <div class="card-body">
             <p class="lead">
diff --git a/app/views/admin/communication/blocks/templates/timeline/_show.html.erb b/app/views/admin/communication/blocks/templates/timeline/_show.html.erb
index 461a8243f8eaabd4a1e9dc28fdc4d4718fe1c337..0bbdf7c177564249e2b38b7bf99ccb84bce72848 100644
--- a/app/views/admin/communication/blocks/templates/timeline/_show.html.erb
+++ b/app/views/admin/communication/blocks/templates/timeline/_show.html.erb
@@ -1,7 +1,7 @@
 <p><%= @block.template.description %></p>
-<div class="row">
+<div class="<%= 'row' unless @preview %>">
   <% @block.template.events.each do |event| %>
-      <div class="col-xxl-4 col-xl-6">
+      <div class="<%= 'col-xxl-4 col-xl-6' unless @preview %>">
         <article class="card">
           <div class="card-body">
             <p class="lead"><%= event.title %></p>
diff --git a/app/views/admin/communication/website/pages/_treebranch.html.erb b/app/views/admin/communication/website/pages/_treebranch.html.erb
index 2958456fd3882d3b4a6a5f63f06d3dc2efc861ba..e8a68621861420735d600bf3bf6aeaeede374857 100644
--- a/app/views/admin/communication/website/pages/_treebranch.html.erb
+++ b/app/views/admin/communication/website/pages/_treebranch.html.erb
@@ -23,7 +23,10 @@
         <span class="open_text"><%= t 'folder.open' %></span>
         <span class="close_text"><%= t 'folder.close' %></span>
       <% end %>
-      <div class="btn-group ms-auto" role="group">
+      <div class="btn-group ms-auto align-items-center" role="group">
+        <% if page.is_special_page? %>
+          <span class="me-3 show-on-hover"><%= t("communication.website.pages.defaults.#{page.kind}.admin_description") %></span>
+        <% end %>
         <%= edit_link page %>
         <%= destroy_link page, confirm_message: page.children.any? ? t('please_confirm_with_children') : t('please_confirm') if page.is_regular_page? %>
       </div>
diff --git a/app/views/admin/communication/website/pages/show.html.erb b/app/views/admin/communication/website/pages/show.html.erb
index 571e40b0339ffd603c8019eecbbff9e6c1ac5910..c9de26166a1876c6cc903232e370c3e59b8f7bbe 100644
--- a/app/views/admin/communication/website/pages/show.html.erb
+++ b/app/views/admin/communication/website/pages/show.html.erb
@@ -113,6 +113,9 @@
 
 <% content_for :preview do %>
   <%= @page.text.to_s %>
+  <% @page.blocks.ordered.each do |block| %>
+    <%= render 'admin/communication/blocks/preview', block: block %>
+  <% end %>
 <% end %>
 
 <% content_for :action_bar_left do %>
diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb
index f22889e4730e48e6ae0633f5f47641eb51bf69c3..d1a68f946cbd72e5b546544b3566553940e47928 100644
--- a/app/views/devise/sessions/new.html.erb
+++ b/app/views/devise/sessions/new.html.erb
@@ -11,7 +11,7 @@
   <div class="col-md-6">
     <h2 class="mb-4"><%= t('login.already_registered') %></h2>
     <% if current_university.has_sso? %>
-      <p><%= link_to t('login.sign_in_with_sso'), omniauth_authorize_path(resource_name, current_university.sso_provider), class: 'btn btn-primary' %></p>
+      <p><%= link_to t('login.sign_in_with_sso'), omniauth_authorize_path(resource_name, current_university.sso_provider), method: :post, class: 'btn btn-primary' %></p>
       <p><%= t('login.or') %></p>
       <a href="#collapseLoginForm" class="btn btn-primary mb-3" data-bs-toggle="collapse"><%= t('login.sign_in_with_credentials') %></a>
     <% end %>
diff --git a/app/views/server/universities/_form.html.erb b/app/views/server/universities/_form.html.erb
index 5ef1508748f0d071e6782559b6453803da92fbbb..480d423f6eb1fe44660534be8d31013f30f6aca7 100644
--- a/app/views/server/universities/_form.html.erb
+++ b/app/views/server/universities/_form.html.erb
@@ -1,4 +1,6 @@
 <%= simple_form_for [:server, university] do |f| %>
+  <%= f.error_notification %>
+
   <div class="row">
     <div class="col-md-4">
       <%= f.input :name %>
@@ -40,6 +42,7 @@
     </div>
     <div class="col-md-6">
       <h4 class="mb-4"><%= University.human_attribute_name('sso_mapping') %></h4>
+      <%= f.error_notification message: f.object.errors[:sso_mapping].to_sentence if f.object.errors[:sso_mapping].present? %>
       <%= render 'sso_mapping', university: university %>
     </div>
   </div>
diff --git a/app/views/server/universities/_sso_mapping.html.erb b/app/views/server/universities/_sso_mapping.html.erb
index a1fc7362a035003a0e4e5cc843a494d284017d58..7bdb320c2cdcf00863c2af89c69f1ae31dd29bae 100644
--- a/app/views/server/universities/_sso_mapping.html.erb
+++ b/app/views/server/universities/_sso_mapping.html.erb
@@ -9,13 +9,17 @@
   </div>
 
   <div class="app-form">
-    <draggable :list="fields">
+    <draggable :list="fields" handle=".dragHandle" >
       <div v-for="(field, index) in fields">
         <div class="card">
           <div class="card-header d-flex justify-content-between">
-            <a data-bs-toggle="collapse" :href="'#sso_mapping_collapse_' + index ">
-              {{index + 1}}. {{ field.sso_key }} -> {{ keys[field.internal_key]}}
-            </a>
+            <div>
+              <i class="fas fa-arrows-alt dragHandle"></i>
+              &nbsp;
+              <a data-bs-toggle="collapse" :href="'#sso_mapping_collapse_' + index ">
+                {{index + 1}}. {{ field.sso_key }} -> {{ keys[field.internal_key]}}
+              </a>
+            </div>
             <a
               v-on:click="fields.splice(fields.indexOf(field), 1)"
               title="Remove field">
diff --git a/config/locales/communication/en.yml b/config/locales/communication/en.yml
index b65e067d5d008bc4ec0f484a659f44bafa300202..8c7e55dd054111c2369e58cfb05888e27b468411 100644
--- a/config/locales/communication/en.yml
+++ b/config/locales/communication/en.yml
@@ -188,12 +188,15 @@ en:
               text_placeholder: Enter text here
           call_to_action:
             edit:
-              text_label: Texte
-              text_placeholder: Entrer le texte ici
+              text_label: Text
+              text_placeholder: Enter text here
               url_label: Lien
-              url_placeholder: Entrer le lien ici avec "https://..."
-              button_label: Bouton
-              button_placeholder: Entrer le texte du bouton ici
+              url_placeholder: Enter the URL with "https://..."
+              buttons: Action buttons
+              button_1: Primary button
+              button_2: Secondary button
+              button_label: Text
+              button_placeholder: Enter the button text here
               image_title: Image
               image_label: Fichier
               remove_image: Supprimer l'image
diff --git a/config/locales/communication/fr.yml b/config/locales/communication/fr.yml
index f98b5ee6414e03bfbbcc53ab6846fbf6628747c1..d4784d66599ad50eb9fb350869b0a958b3c7183d 100644
--- a/config/locales/communication/fr.yml
+++ b/config/locales/communication/fr.yml
@@ -194,7 +194,10 @@ fr:
               text_placeholder: Entrer le texte ici
               url_label: Lien
               url_placeholder: Entrer le lien ici avec "https://..."
-              button_label: Bouton
+              buttons: Boutons d'action
+              button_1: Bouton principal
+              button_2: Bouton secondaire
+              button_label: Texte
               button_placeholder: Entrer le texte du bouton ici
               image_title: Image
               image_label: Fichier
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 49071105af0eb9cde4fe42461b5a176d54940a54..413c3e8cf60e353600c964f9647c803282cc5671 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -74,6 +74,8 @@ en:
       two_factor_authentication_code:
         subject: "Two-factor authentication code"
         text_html: "Your two-factor authentication code for %{university} is: <br><br><b>%{code}</b><br><br>It will expire in 5 minutes."
+    omniauth_callbacks:
+      failure: "Failed to sign in."
     sessions:
       signed_in: ''
     shared:
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index ab257fa73827bc04141c59c0edb70995dd5fcf76..1e8778ab47e657036fd405f8e669a9cf92f14fe9 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -74,6 +74,8 @@ fr:
       two_factor_authentication_code:
         subject: "Code d'authentification à deux facteurs"
         text_html: "Votre code d'authentification pour %{university} est :<br><br><b>%{code}</b><br><br>Il expirera dans 5 minutes."
+    omniauth_callbacks:
+      failure: "Échec de l'authentification."
     sessions:
       signed_in: ''
     shared:
diff --git a/config/locales/university/en.yml b/config/locales/university/en.yml
index 2e9ea4d69c0ae389d611c9f297dfe4f64dfc5fcd..821184b444454959fbb0fc717e722cf5888a6c48 100644
--- a/config/locales/university/en.yml
+++ b/config/locales/university/en.yml
@@ -81,6 +81,12 @@ en:
       university/role:
         description: Description
         people: People
+    errors:
+      models:
+        university:
+          attributes:
+            sso_mapping:
+              missing_email: doesn't handle the email
     models:
       university:
         one: University
diff --git a/config/locales/university/fr.yml b/config/locales/university/fr.yml
index bd2215aba23d0f3d6f36c0be7c102f408c688426..11e6e050ef25099db72514b2bfc904e0fe9ba2e5 100644
--- a/config/locales/university/fr.yml
+++ b/config/locales/university/fr.yml
@@ -81,6 +81,12 @@ fr:
       university/role:
         description: Description
         people: Personnes
+    errors:
+      models:
+        university:
+          attributes:
+            sso_mapping:
+              missing_email: ne gère pas l'adresse email
     models:
       university:
         one: Université
diff --git a/db/migrate/20220505105709_add_picture_url_to_users.rb b/db/migrate/20220505105709_add_picture_url_to_users.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9570a3173110bf0246236ad68a91b9e1d02a3508
--- /dev/null
+++ b/db/migrate/20220505105709_add_picture_url_to_users.rb
@@ -0,0 +1,5 @@
+class AddPictureUrlToUsers < ActiveRecord::Migration[6.1]
+  def change
+    add_column :users, :picture_url, :string
+  end
+end
diff --git a/db/migrate/20220505131539_drop_communication_website_index_page.rb b/db/migrate/20220505131539_drop_communication_website_index_page.rb
new file mode 100644
index 0000000000000000000000000000000000000000..43b1d29b9195be3cfaedde8e1adad02ab8f643d9
--- /dev/null
+++ b/db/migrate/20220505131539_drop_communication_website_index_page.rb
@@ -0,0 +1,5 @@
+class DropCommunicationWebsiteIndexPage < ActiveRecord::Migration[6.1]
+  def change
+    drop_table :communication_website_index_pages
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 88997f48bf471b7e18acbeb12b71f8693f09e2c3..4c5f75e298ca6a282c02fd174577cd38ba7d4d40 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 2022_05_05_101823) do
+ActiveRecord::Schema.define(version: 2022_05_05_131539) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "pgcrypto"
@@ -255,23 +255,6 @@ ActiveRecord::Schema.define(version: 2022_05_05_101823) do
     t.index ["website_id"], name: "index_communication_website_imported_websites_on_website_id"
   end
 
-  create_table "communication_website_index_pages", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
-    t.uuid "university_id", null: false
-    t.uuid "communication_website_id", null: false
-    t.string "title"
-    t.string "path"
-    t.text "description"
-    t.text "text"
-    t.string "featured_image_alt"
-    t.integer "kind"
-    t.datetime "created_at", precision: 6, null: false
-    t.datetime "updated_at", precision: 6, null: false
-    t.string "breadcrumb_title"
-    t.text "header_text"
-    t.index ["communication_website_id"], name: "idx_comm_website_index_page_on_communication_website_id"
-    t.index ["university_id"], name: "index_communication_website_index_pages_on_university_id"
-  end
-
   create_table "communication_website_menu_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "website_id", null: false
@@ -804,6 +787,7 @@ ActiveRecord::Schema.define(version: 2022_05_05_101823) do
     t.datetime "direct_otp_sent_at"
     t.datetime "totp_timestamp"
     t.string "session_token"
+    t.string "picture_url"
     t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
     t.index ["email", "university_id"], name: "index_users_on_email_and_university_id", unique: true
     t.index ["encrypted_otp_secret_key"], name: "index_users_on_encrypted_otp_secret_key", unique: true
@@ -841,8 +825,6 @@ ActiveRecord::Schema.define(version: 2022_05_05_101823) do
   add_foreign_key "communication_website_imported_posts", "universities"
   add_foreign_key "communication_website_imported_websites", "communication_websites", column: "website_id"
   add_foreign_key "communication_website_imported_websites", "universities"
-  add_foreign_key "communication_website_index_pages", "communication_websites"
-  add_foreign_key "communication_website_index_pages", "universities"
   add_foreign_key "communication_website_menu_items", "communication_website_menu_items", column: "parent_id"
   add_foreign_key "communication_website_menu_items", "communication_website_menus", column: "menu_id"
   add_foreign_key "communication_website_menu_items", "communication_websites", column: "website_id"