diff --git a/Gemfile b/Gemfile
index b2bea4e6b13109568039410b6e5863f48a4da884..4a42a43119ec930d1f161e54c5f2af473dad0a8e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -3,49 +3,55 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
 
 ruby '2.7.5'
 
-gem 'angularjs-rails'
+# Infrastructure
 gem 'aws-sdk-s3'
+gem 'bugsnag'
+gem 'gitlab'
+gem 'image_processing'
+gem 'mini_magick'
+gem 'octokit'
+gem 'pg', '~> 1.1'
+gem 'puma'
+
+# Back-end
+gem 'cancancan'
 gem 'bootsnap', '>= 1.4.4', require: false
+gem 'curation'#, path: '../../arnaudlevy/curation'
+gem 'delayed_job_active_record'
+gem 'delayed_job_web'
+gem 'faceted_search'#, path: '../faceted_search'
+gem 'has_scope', '~> 0.8.0'
+gem 'hash_dot'
+gem 'rails', '~> 6.1'
+gem 'rails-i18n'
+gem 'sanitize'
+gem 'sib-api-v3-sdk'
+gem 'two_factor_authentication', git: 'https://github.com/noesya/two_factor_authentication.git'
+# gem 'two_factor_authentication', path: '../two_factor_authentication'
+
+# Front-end
+gem 'angularjs-rails'
 gem 'bootstrap'
 gem 'bootstrap5-kaminari-views'
 gem 'breadcrumbs_on_rails'
-gem 'bugsnag'
-gem 'cancancan'
 gem 'cocoon', '~> 1.2'
 gem 'country_select'
-gem 'curation'#, path: '../../arnaudlevy/curation'
-gem 'delayed_job_active_record'
-gem 'delayed_job_web'
 gem 'devise'
 gem 'devise-i18n'
 gem 'enum_help'
 gem 'front_matter_parser'
 gem 'gdpr'
-gem 'gitlab'
-gem 'has_scope', '~> 0.8.0'
-gem 'hash_dot'
-gem 'image_processing'
 gem 'jbuilder'
 gem 'jquery-rails'
 gem 'kamifusen'#, path: '../kamifusen'
 gem 'kaminari'
-gem 'mini_magick'
-gem 'octokit'
-gem 'pg', '~> 1.1'
-gem 'puma'
-gem 'rails', '~> 6.1'
-gem 'rails-i18n'
-gem 'sanitize'
 gem 'sassc-rails'
-gem 'sib-api-v3-sdk'
 gem 'simple_form'
 gem 'simple_form_bs5_file_input'#, path: '../simple_form_bs5_file_input'
 gem 'simple_form_password_with_hints'#, path: '../simple_form_password_with_hints'
 gem 'simple-navigation'
 gem 'summernote-rails', git: 'https://github.com/noesya/summernote-rails.git', branch: 'activestorage'
 # gem 'summernote-rails', path: '../summernote-rails'
-gem 'two_factor_authentication', git: 'https://github.com/noesya/two_factor_authentication.git'
-# gem 'two_factor_authentication', path: '../two_factor_authentication'
 
 group :development, :test do
   gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
diff --git a/Gemfile.lock b/Gemfile.lock
index a1cbe2c603b75fd3c9aab9f6f94cb99720852fb2..106b97d221040a40d7d50ad3ec093bc5b3b43faa 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -20,60 +20,60 @@ GIT
 GEM
   remote: https://rubygems.org/
   specs:
-    actioncable (6.1.5)
-      actionpack (= 6.1.5)
-      activesupport (= 6.1.5)
+    actioncable (6.1.5.1)
+      actionpack (= 6.1.5.1)
+      activesupport (= 6.1.5.1)
       nio4r (~> 2.0)
       websocket-driver (>= 0.6.1)
-    actionmailbox (6.1.5)
-      actionpack (= 6.1.5)
-      activejob (= 6.1.5)
-      activerecord (= 6.1.5)
-      activestorage (= 6.1.5)
-      activesupport (= 6.1.5)
+    actionmailbox (6.1.5.1)
+      actionpack (= 6.1.5.1)
+      activejob (= 6.1.5.1)
+      activerecord (= 6.1.5.1)
+      activestorage (= 6.1.5.1)
+      activesupport (= 6.1.5.1)
       mail (>= 2.7.1)
-    actionmailer (6.1.5)
-      actionpack (= 6.1.5)
-      actionview (= 6.1.5)
-      activejob (= 6.1.5)
-      activesupport (= 6.1.5)
+    actionmailer (6.1.5.1)
+      actionpack (= 6.1.5.1)
+      actionview (= 6.1.5.1)
+      activejob (= 6.1.5.1)
+      activesupport (= 6.1.5.1)
       mail (~> 2.5, >= 2.5.4)
       rails-dom-testing (~> 2.0)
-    actionpack (6.1.5)
-      actionview (= 6.1.5)
-      activesupport (= 6.1.5)
+    actionpack (6.1.5.1)
+      actionview (= 6.1.5.1)
+      activesupport (= 6.1.5.1)
       rack (~> 2.0, >= 2.0.9)
       rack-test (>= 0.6.3)
       rails-dom-testing (~> 2.0)
       rails-html-sanitizer (~> 1.0, >= 1.2.0)
-    actiontext (6.1.5)
-      actionpack (= 6.1.5)
-      activerecord (= 6.1.5)
-      activestorage (= 6.1.5)
-      activesupport (= 6.1.5)
+    actiontext (6.1.5.1)
+      actionpack (= 6.1.5.1)
+      activerecord (= 6.1.5.1)
+      activestorage (= 6.1.5.1)
+      activesupport (= 6.1.5.1)
       nokogiri (>= 1.8.5)
-    actionview (6.1.5)
-      activesupport (= 6.1.5)
+    actionview (6.1.5.1)
+      activesupport (= 6.1.5.1)
       builder (~> 3.1)
       erubi (~> 1.4)
       rails-dom-testing (~> 2.0)
       rails-html-sanitizer (~> 1.1, >= 1.2.0)
-    activejob (6.1.5)
-      activesupport (= 6.1.5)
+    activejob (6.1.5.1)
+      activesupport (= 6.1.5.1)
       globalid (>= 0.3.6)
-    activemodel (6.1.5)
-      activesupport (= 6.1.5)
-    activerecord (6.1.5)
-      activemodel (= 6.1.5)
-      activesupport (= 6.1.5)
-    activestorage (6.1.5)
-      actionpack (= 6.1.5)
-      activejob (= 6.1.5)
-      activerecord (= 6.1.5)
-      activesupport (= 6.1.5)
+    activemodel (6.1.5.1)
+      activesupport (= 6.1.5.1)
+    activerecord (6.1.5.1)
+      activemodel (= 6.1.5.1)
+      activesupport (= 6.1.5.1)
+    activestorage (6.1.5.1)
+      actionpack (= 6.1.5.1)
+      activejob (= 6.1.5.1)
+      activerecord (= 6.1.5.1)
+      activesupport (= 6.1.5.1)
       marcel (~> 1.0)
       mini_mime (>= 1.1.0)
-    activesupport (6.1.5)
+    activesupport (6.1.5.1)
       concurrent-ruby (~> 1.0, >= 1.0.2)
       i18n (>= 1.6, < 2)
       minitest (>= 5.1)
@@ -88,20 +88,20 @@ GEM
     autoprefixer-rails (10.4.2.0)
       execjs (~> 2)
     aws-eventstream (1.2.0)
-    aws-partitions (1.576.0)
-    aws-sdk-core (3.130.1)
+    aws-partitions (1.580.0)
+    aws-sdk-core (3.130.2)
       aws-eventstream (~> 1, >= 1.0.2)
       aws-partitions (~> 1, >= 1.525.0)
       aws-sigv4 (~> 1.1)
       jmespath (~> 1.0)
-    aws-sdk-kms (1.55.0)
+    aws-sdk-kms (1.56.0)
       aws-sdk-core (~> 3, >= 3.127.0)
       aws-sigv4 (~> 1.1)
-    aws-sdk-s3 (1.113.0)
+    aws-sdk-s3 (1.113.2)
       aws-sdk-core (~> 3, >= 3.127.0)
       aws-sdk-kms (~> 1)
       aws-sigv4 (~> 1.4)
-    aws-sigv4 (1.4.0)
+    aws-sigv4 (1.5.0)
       aws-eventstream (~> 1, >= 1.0.2)
     bcrypt (3.1.17)
     bindex (0.8.1)
@@ -172,6 +172,9 @@ GEM
     ethon (0.15.0)
       ffi (>= 1.15.0)
     execjs (2.8.1)
+    faceted_search (3.5.13)
+      font-awesome-sass
+      rails (>= 5.2.0, < 7)
     faraday (1.10.0)
       faraday-em_http (~> 1.0)
       faraday-em_synchrony (~> 1.0)
@@ -208,6 +211,8 @@ GEM
     ffi (1.15.5)
     figaro (1.2.0)
       thor (>= 0.14.0, < 2)
+    font-awesome-sass (6.1.1)
+      sassc (~> 2.0)
     front_matter_parser (1.0.1)
     gdpr (1.2.3)
       js_cookie_rails
@@ -316,20 +321,20 @@ GEM
       rack
     rack-test (1.1.0)
       rack (>= 1.0, < 3)
-    rails (6.1.5)
-      actioncable (= 6.1.5)
-      actionmailbox (= 6.1.5)
-      actionmailer (= 6.1.5)
-      actionpack (= 6.1.5)
-      actiontext (= 6.1.5)
-      actionview (= 6.1.5)
-      activejob (= 6.1.5)
-      activemodel (= 6.1.5)
-      activerecord (= 6.1.5)
-      activestorage (= 6.1.5)
-      activesupport (= 6.1.5)
+    rails (6.1.5.1)
+      actioncable (= 6.1.5.1)
+      actionmailbox (= 6.1.5.1)
+      actionmailer (= 6.1.5.1)
+      actionpack (= 6.1.5.1)
+      actiontext (= 6.1.5.1)
+      actionview (= 6.1.5.1)
+      activejob (= 6.1.5.1)
+      activemodel (= 6.1.5.1)
+      activerecord (= 6.1.5.1)
+      activestorage (= 6.1.5.1)
+      activesupport (= 6.1.5.1)
       bundler (>= 1.15.0)
-      railties (= 6.1.5)
+      railties (= 6.1.5.1)
       sprockets-rails (>= 2.0.0)
     rails-dom-testing (2.0.3)
       activesupport (>= 4.2.0)
@@ -339,9 +344,9 @@ GEM
     rails-i18n (7.0.3)
       i18n (>= 0.7, < 2)
       railties (>= 6.0.0, < 8)
-    railties (6.1.5)
-      actionpack (= 6.1.5)
-      activesupport (= 6.1.5)
+    railties (6.1.5.1)
+      actionpack (= 6.1.5.1)
+      activesupport (= 6.1.5.1)
       method_source
       rake (>= 12.2)
       thor (~> 1.0)
@@ -350,7 +355,7 @@ GEM
     rb-fsevent (0.11.1)
     rb-inotify (0.10.1)
       ffi (~> 1.0)
-    regexp_parser (2.3.0)
+    regexp_parser (2.3.1)
     responders (3.0.1)
       actionpack (>= 5.0)
       railties (>= 5.0)
@@ -469,6 +474,7 @@ DEPENDENCIES
   devise
   devise-i18n
   enum_help
+  faceted_search
   figaro
   front_matter_parser
   gdpr
diff --git a/app/assets/javascripts/admin/commons/batch-selectable.js b/app/assets/javascripts/admin/commons/batch-selectable.js
new file mode 100644
index 0000000000000000000000000000000000000000..09f9f4f6c896865bf5639443f4c6acc33a2d39a3
--- /dev/null
+++ b/app/assets/javascripts/admin/commons/batch-selectable.js
@@ -0,0 +1,35 @@
+/* global */
+window.osuny.BatchSelectable = function BatchSelectable (element) {
+    'use strict';
+    this.element = element;
+    this.selectAllInput = this.element.querySelector('[data-batch-selectable-role="select-all"]');
+    this.selectSingleInputs = this.element.querySelectorAll('[data-batch-selectable-role="select-single"]');
+    this.initEvents();
+};
+
+window.osuny.BatchSelectable.prototype.initEvents = function () {
+    'use strict';
+    if (this.selectAllInput === null) {
+        return;
+    }
+    this.selectAllInput.addEventListener('change', function () {
+        this.toggleSingleInputs(this.selectAllInput.checked);
+    }.bind(this));
+};
+
+window.osuny.BatchSelectable.prototype.toggleSingleInputs = function (checked) {
+    'use strict';
+    var i;
+    for (i = 0; i < this.selectSingleInputs.length; i += 1) {
+        this.selectSingleInputs[i].checked = checked;
+    }
+};
+
+window.addEventListener('DOMContentLoaded', function () {
+    'use strict';
+    var elements = document.querySelectorAll('[data-batch-selectable]'),
+        i;
+    for (i = 0; i < elements.length; i += 1) {
+        new window.osuny.BatchSelectable(elements[i]);
+    }
+});
diff --git a/app/assets/stylesheets/application.sass b/app/assets/stylesheets/application.sass
index 1d71576b129df81fc868efe53a4d0f83414091a8..cbdad2dd5161fd3e130203aabb7e325e41f39451 100644
--- a/app/assets/stylesheets/application.sass
+++ b/app/assets/stylesheets/application.sass
@@ -4,5 +4,6 @@
 @import 'simple_form_bs5_file_input'
 @import 'cropperjs/dist/cropper'
 @import 'gdpr/cookie_consent'
+@import 'faceted_search'
 @import 'commons/*'
 @import 'application/*'
diff --git a/app/assets/stylesheets/application/faceted_search.sass b/app/assets/stylesheets/application/faceted_search.sass
new file mode 100644
index 0000000000000000000000000000000000000000..d3d8bb19432f4bc79f47c1c1fdc63c46c989f821
--- /dev/null
+++ b/app/assets/stylesheets/application/faceted_search.sass
@@ -0,0 +1,7 @@
+.faceted
+    &__facet-selected
+        a
+            text-decoration: none
+    &__facet__list
+        .faceted__facet__list__value
+            display: inline-block
diff --git a/app/controllers/admin/application_controller.rb b/app/controllers/admin/application_controller.rb
index 921a46d7ae157ee5413edfc7767c95ae8065efa0..084644e1d0aac2dc59bcc6cd28a4eac79cfcb840 100644
--- a/app/controllers/admin/application_controller.rb
+++ b/app/controllers/admin/application_controller.rb
@@ -1,6 +1,8 @@
 class Admin::ApplicationController < ApplicationController
   layout 'admin/layouts/application'
 
+  before_action :load_filters, only: :index
+
   around_action :switch_locale
 
   protected
@@ -25,4 +27,12 @@ class Admin::ApplicationController < ApplicationController
     locale = LocaleService.locale(current_user, request.env['HTTP_ACCEPT_LANGUAGE'])
     I18n.with_locale(locale, &action)
   end
+
+  def load_filters
+    @filters = []
+    filter_class_name = "::Filters::#{self.class.to_s.gsub('Controller', '')}"
+    # filter_class will be nil if filter does not exist
+    filter_class = filter_class_name.safe_constantize
+    @filters = filter_class.new(current_user).list unless filter_class.nil?
+  end
 end
diff --git a/app/controllers/admin/communication/extranets_controller.rb b/app/controllers/admin/communication/extranets_controller.rb
index 0cfc44d68431688b8ccd159d35f342cf2cdb369f..6cd178308d3b24fc6208e2454e2cabfce2a9ba4b 100644
--- a/app/controllers/admin/communication/extranets_controller.rb
+++ b/app/controllers/admin/communication/extranets_controller.rb
@@ -8,9 +8,11 @@ class Admin::Communication::ExtranetsController < Admin::Communication::Applicat
   end
 
   def show
-    @alumni = @extranet.about&.alumni
-    @cohorts = @extranet.about&.cohorts
-    @years = @extranet.about&.academic_years
+    @about = @extranet.about
+    @alumni = @about&.alumni
+    @cohorts = @about&.cohorts
+    @years = @about&.academic_years
+    @organizations = @about&.alumni_organizations
     breadcrumb
   end
 
diff --git a/app/controllers/admin/communication/website/posts_controller.rb b/app/controllers/admin/communication/website/posts_controller.rb
index f9d074f007b6d1e127660e85fab7984400ee1115..ef64565104f4a3bcaac82da9439e1401a002ea04 100644
--- a/app/controllers/admin/communication/website/posts_controller.rb
+++ b/app/controllers/admin/communication/website/posts_controller.rb
@@ -6,12 +6,26 @@ class Admin::Communication::Website::PostsController < Admin::Communication::Web
     breadcrumb
   end
 
+  def publish
+    ids = params[:ids] || []
+    target_posts = @website.posts.where(id: ids)
+    if params[:published] == "true"
+      target_posts.update(published: true)
+    elsif params[:published] == "false"
+      target_posts.update(published: false)
+    end
+    @website.sync_objects_with_git(target_posts) if target_posts.any?
+    redirect_back fallback_location: admin_communication_website_posts_path,
+                  notice: t('communication.website.posts.successful_batch_update')
+  end
+
   def show
     breadcrumb
   end
 
   def new
     @post.website = @website
+    @post.author_id = current_user.person&.id
     breadcrumb
   end
 
diff --git a/app/controllers/admin/university/organizations_controller.rb b/app/controllers/admin/university/organizations_controller.rb
index 3aadaf0ddef5521d6092b070ea1853feab56a3ec..4a168b2bec9da1be9d9972cafdd31958c34ce92b 100644
--- a/app/controllers/admin/university/organizations_controller.rb
+++ b/app/controllers/admin/university/organizations_controller.rb
@@ -62,7 +62,7 @@ class Admin::University::OrganizationsController < Admin::University::Applicatio
           .permit(
             :name, :long_name, :slug, :description, :active, :siren, :kind,
             :address, :zipcode, :city, :country, :text,
-            :url, :phone, :email, :logo, :logo_delete
+            :url, :phone, :email, :logo, :logo_delete, :logo_infos
           )
   end
 end
diff --git a/app/controllers/admin/university/people_controller.rb b/app/controllers/admin/university/people_controller.rb
index 73b048763c7f2422e60ada101928405974cedd30..bdc9ee2d24cb6797568ca27be11c62683b902ccf 100644
--- a/app/controllers/admin/university/people_controller.rb
+++ b/app/controllers/admin/university/people_controller.rb
@@ -3,8 +3,12 @@ class Admin::University::PeopleController < Admin::University::ApplicationContro
                               through: :current_university,
                               through_association: :people
 
+
+  has_scope :for_search_term
+  has_scope :for_role
+
   def index
-    @people = @people.ordered.page(params[:page])
+    @people = apply_scopes(@people).ordered.page(params[:page])
     breadcrumb
   end
 
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 1cdadc2ae8ac07723f3ae7f42c6a330b06c38052..ddb1f9b14892952864005cc7807c0495be6273cc 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -5,7 +5,6 @@ class Admin::UsersController < Admin::ApplicationController
   has_scope :for_search_term
 
   def index
-    @filters = ::Filters::User.new(current_user).list
     @users = apply_scopes(@users).ordered.page(params[:page])
     breadcrumb
   end
diff --git a/app/controllers/extranet/home_controller.rb b/app/controllers/extranet/home_controller.rb
index ec6a69a8ee2e6e746f6314817be13f4884ddc324..6f189dd8b0cff76d40b497875cd8ea06c1b091ed 100644
--- a/app/controllers/extranet/home_controller.rb
+++ b/app/controllers/extranet/home_controller.rb
@@ -1,7 +1,8 @@
 class Extranet::HomeController < Extranet::ApplicationController
   def index
     return redirect_to admin_root_path unless current_extranet
-    @cohorts = current_extranet.about&.cohorts || current_university.education_cohorts
-    @cohorts = @cohorts.ordered.limit(5)
+    @about = current_extranet.about || current_university
+    @cohorts = @about&.cohorts.ordered.limit(5)
+    @experiences = @about&.experiences.ordered.limit(10)
   end
 end
diff --git a/app/controllers/extranet/organizations_controller.rb b/app/controllers/extranet/organizations_controller.rb
index d91329fb8279c04f91c6a3b8cbc040e21114cb7b..ee06ab51e823f369a0a2571ea27bb2839b246edb 100644
--- a/app/controllers/extranet/organizations_controller.rb
+++ b/app/controllers/extranet/organizations_controller.rb
@@ -4,7 +4,9 @@ class Extranet::OrganizationsController < Extranet::ApplicationController
                               through_association: :organizations
 
   def index
+    @organizations = current_extranet.about&.alumni_organizations || @organizations
     @organizations = @organizations.ordered.page(params[:page])
+    @count = @organizations.total_count
     breadcrumb
   end
 
diff --git a/app/controllers/extranet/persons_controller.rb b/app/controllers/extranet/persons_controller.rb
index 172fc4473cb9dcea27c733b9b95c8d3c81b44b4a..e3975a7a7afe0f01c3a25506c8d5b567a25fc03c 100644
--- a/app/controllers/extranet/persons_controller.rb
+++ b/app/controllers/extranet/persons_controller.rb
@@ -4,7 +4,16 @@ class Extranet::PersonsController < Extranet::ApplicationController
                               through_association: :people
 
   def index
-    @people = current_extranet.about&.alumni || @people.alumni
+    alumni = current_extranet.about&.alumni || @people.alumni
+    @facets = University::Person::Alumnus::Facets.new params[:facets], {
+      model: alumni,
+      about: current_extranet.about
+    }
+    @people = @facets.results
+                     .ordered
+                     .page(params[:page])
+                     .per(60)
+    @count = @people.total_count
     breadcrumb
   end
 
diff --git a/app/helpers/admin/application_helper.rb b/app/helpers/admin/application_helper.rb
index 8b213f3fe0b60082881b34c6762a5a6a7bef92f5..4ecd3416cb65e5531acd9fa4fc63320da5254f19 100644
--- a/app/helpers/admin/application_helper.rb
+++ b/app/helpers/admin/application_helper.rb
@@ -131,6 +131,7 @@ module Admin::ApplicationHelper
       'communication.website.menu.item.kind.research_volumes' => 'fas fa-flask',
       'communication.website.menu.item.kind.research_volume' => 'fas fa-flask',
       'communication.website.menu.item.kind.researchers' => 'fas fa-user',
+      'communication.website.menu.item.kind.organizations' => 'fas fa-building',
       'communication.website.menu.item.kind.staff' => 'fas fa-user',
       'communication.website.menu.item.kind.teachers' => 'fas fa-user',
       'communication.website.menu.item.kind.url' => 'fas fa-globe',
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 1aaad8c6c88e82b40d6cb44c6c9a345dadfe18a1..643651eb8a158dceea2e76a56f473c5fa8444739 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -30,6 +30,19 @@ class Ability
     can :read, Communication::Block, university_id: @user.university_id
   end
 
+  def contributor
+    managed_websites_ids = @user.websites_to_manage.pluck(:communication_website_id)
+    can :read, Communication::Website, university_id: @user.university_id, id: managed_websites_ids
+    can :manage, Communication::Website::Post, university_id: @user.university_id, communication_website_id: managed_websites_ids, author_id: @user.person&.id
+    cannot :publish, Communication::Website::Post
+  end
+
+  def author
+    managed_websites_ids = @user.websites_to_manage.pluck(:communication_website_id)
+    can :read, Communication::Website, university_id: @user.university_id, id: managed_websites_ids
+    can :manage, Communication::Website::Post, university_id: @user.university_id, communication_website_id: managed_websites_ids, author_id: @user.person&.id
+  end
+
   def teacher
     can :manage, University::Person, user_id: @user.id
     cannot :create, University::Person
diff --git a/app/models/communication/block/template.rb b/app/models/communication/block/template.rb
index b759d8326836f8ecbd05283ddb811c3745c57775..2eed4643d11775c3ae08abde14ce3cf2f5c03790 100644
--- a/app/models/communication/block/template.rb
+++ b/app/models/communication/block/template.rb
@@ -7,7 +7,7 @@ class Communication::Block::Template
 
   def git_dependencies
     unless @git_dependencies
-      @git_dependencies = active_storage_blobs
+      @git_dependencies = []
       build_git_dependencies
       @git_dependencies.uniq!
     end
diff --git a/app/models/communication/block/template/call_to_action.rb b/app/models/communication/block/template/call_to_action.rb
index 8123bb6b918f9fa7b0ee7134e65b9ece92f18a9f..c2592194b24a0a3cda6c8d8e8f63886fdb27c2b4 100644
--- a/app/models/communication/block/template/call_to_action.rb
+++ b/app/models/communication/block/template/call_to_action.rb
@@ -1,6 +1,6 @@
 class Communication::Block::Template::CallToAction < Communication::Block::Template
   def build_git_dependencies
-    # image à déclarer
+    add_dependency image&.blob
   end
 
   def text
diff --git a/app/models/communication/block/template/gallery.rb b/app/models/communication/block/template/gallery.rb
index da9c83d520d0f87f1e10ef11afdd7088ca3a0089..834169a4de18ae5a13948d1a58bfaaea25a188c0 100644
--- a/app/models/communication/block/template/gallery.rb
+++ b/app/models/communication/block/template/gallery.rb
@@ -1,6 +1,6 @@
 class Communication::Block::Template::Gallery < Communication::Block::Template
   def build_git_dependencies
-    # Blobs already added in Communication::Block::Template#git_dependencies
+    add_dependency active_storage_blobs
   end
 
   def images_with_alt
diff --git a/app/models/communication/block/template/partner.rb b/app/models/communication/block/template/partner.rb
index 2bc364e062b11cb94e01d96ebe761d74eb2052ca..930e858e956f48b73502fcf49a0186adcf221962 100644
--- a/app/models/communication/block/template/partner.rb
+++ b/app/models/communication/block/template/partner.rb
@@ -1,7 +1,10 @@
 class Communication::Block::Template::Partner < Communication::Block::Template
   def build_git_dependencies
-    # Blobs already added in Communication::Block::Template#git_dependencies
+    add_dependency active_storage_blobs
     add_dependency organizations
+    organizations.each do |organization|
+      add_dependency organization.active_storage_blobs
+    end
   end
 
   def partners
diff --git a/app/models/communication/block/template/testimonial.rb b/app/models/communication/block/template/testimonial.rb
index b72a81aa67e474fb8aef108ec58917a4833a82e6..85a231cddf44da4c1df77e7be6ae896aecb7abfa 100644
--- a/app/models/communication/block/template/testimonial.rb
+++ b/app/models/communication/block/template/testimonial.rb
@@ -1,6 +1,6 @@
 class Communication::Block::Template::Testimonial < Communication::Block::Template
   def build_git_dependencies
-    # Blobs already added in Communication::Block::Template#git_dependencies
+    add_dependency active_storage_blobs
   end
 
   def testimonials
diff --git a/app/models/communication/website.rb b/app/models/communication/website.rb
index 266ba4915a11adbe47316297d0f7512e325ffc06..339043d19ea0cb5d8397d0bd9cd54cc4a2ad01d9 100644
--- a/app/models/communication/website.rb
+++ b/app/models/communication/website.rb
@@ -62,6 +62,7 @@ class Communication::Website < ApplicationRecord
     dependencies += pages + pages.map(&:active_storage_blobs).flatten
     dependencies += posts + posts.map(&:active_storage_blobs).flatten if has_communication_posts?
     dependencies += people_with_facets + people.map(&:active_storage_blobs).flatten if has_persons?
+    dependencies += organizations_in_blocks + organizations_in_blocks.map(&:active_storage_blobs).flatten if has_organizations_in_blocks?
     dependencies += [categories] if has_communication_categories?
     dependencies += about.git_dependencies(website) if about.present?
     dependencies
diff --git a/app/models/communication/website/index_page.rb b/app/models/communication/website/index_page.rb
index 2e11d4a2317138fb6e54aea3c8946a29ebb80a61..4c80742baaf0e5c20f062c70e072ae5a84e4a355 100644
--- a/app/models/communication/website/index_page.rb
+++ b/app/models/communication/website/index_page.rb
@@ -41,6 +41,7 @@ class Communication::Website::IndexPage < ApplicationRecord
     legal_terms: 80,
       sitemap: 81,
       privacy_policy: 82,
+    organizations: 90,
     persons: 100,
       administrators: 110,
       authors: 120,
diff --git a/app/models/communication/website/menu/item.rb b/app/models/communication/website/menu/item.rb
index 4f84f3ebddf63b8241b907e2b7ff9f5028ae8bd2..cc4355371cf5b0138b3ccaa49798e84dcd783d81 100644
--- a/app/models/communication/website/menu/item.rb
+++ b/app/models/communication/website/menu/item.rb
@@ -58,6 +58,7 @@ class Communication::Website::Menu::Item < ApplicationRecord
     news: 40,
     news_category: 41,
     news_article: 42,
+    organizations: 45,
     staff: 50,
     administrators: 51,
     authors: 52,
diff --git a/app/models/communication/website/menu/item/with_targets.rb b/app/models/communication/website/menu/item/with_targets.rb
index 2e83c12458be98cd70bc7e7ee4db2f6480f7b2db..f93175d2f0a2c701b4f5eb9aa98939d0612e1447 100644
--- a/app/models/communication/website/menu/item/with_targets.rb
+++ b/app/models/communication/website/menu/item/with_targets.rb
@@ -35,6 +35,10 @@ module Communication::Website::Menu::Item::WithTargets
     "#{website.special_page(:communication_posts).path}#{about.path}".gsub("//", '/') if about
   end
 
+  def target_for_organizations
+    "#{website.special_page(:organizations).path}"
+  end
+
   def target_for_staff
     "#{website.special_page(:persons).path}"
   end
diff --git a/app/models/communication/website/page.rb b/app/models/communication/website/page.rb
index a940b3508bb240434c14b4a76e3a1d7ea9448e13..a6446e5da0db183f8f25e06a789c8d2d68c785b5 100644
--- a/app/models/communication/website/page.rb
+++ b/app/models/communication/website/page.rb
@@ -82,11 +82,20 @@ class Communication::Website::Page < ApplicationRecord
   after_save :update_children_paths, if: :saved_change_to_path?
 
   scope :recent, -> { order(updated_at: :desc).limit(5) }
+  scope :published, -> { where(published: true) }
 
   def generated_path
     "#{parent&.path}#{slug}/".gsub(/\/+/, '/')
   end
 
+  def path_without_language
+    if parent_id.present?
+      "#{parent&.path_without_language}#{slug}/".gsub(/\/+/, '/')
+    else
+      "/#{slug}/".gsub(/\/+/, '/')
+    end
+  end
+
   def git_path(website)
     return unless published
     if kind_home?
@@ -106,6 +115,7 @@ class Communication::Website::Page < ApplicationRecord
                     siblings +
                     git_block_dependencies
     dependencies += website.education_programs if kind_education_programs?
+    dependencies += [parent] if has_parent?
     dependencies
   end
 
diff --git a/app/models/communication/website/page/with_kind.rb b/app/models/communication/website/page/with_kind.rb
index 667926934fe5380a9498a4679e9ac6ed815ec718..640d60e836e42258f44e7954a7305978ca99daaf 100644
--- a/app/models/communication/website/page/with_kind.rb
+++ b/app/models/communication/website/page/with_kind.rb
@@ -12,6 +12,7 @@ module Communication::Website::Page::WithKind
       legal_terms: 80,
         sitemap: 81,
         privacy_policy: 82,
+      organizations: 90,
       persons: 100,
         administrators: 110,
         authors: 120,
@@ -24,6 +25,7 @@ module Communication::Website::Page::WithKind
       'education_programs',
       'research_articles',
       'research_volumes',
+      'organizations',
       'persons',
       'administrators',
       'authors',
diff --git a/app/models/communication/website/post.rb b/app/models/communication/website/post.rb
index 71ca1ad6ee362b58d6d0744b936b825bbb9c8a3e..14cda1e87c58e99446f1e9268fecb73820bcf977 100644
--- a/app/models/communication/website/post.rb
+++ b/app/models/communication/website/post.rb
@@ -63,6 +63,7 @@ class Communication::Website::Post < ApplicationRecord
   validates :title, presence: true
 
   before_validation :set_published_at, if: :published_changed?
+  after_save_commit :update_authors_statuses!, if: :saved_change_to_author_id?
 
   scope :published, -> { where(published: true) }
   scope :ordered, -> { order(published_at: :desc, created_at: :desc) }
@@ -123,4 +124,12 @@ class Communication::Website::Post < ApplicationRecord
   def inherited_blob_ids
     [best_featured_image&.blob_id]
   end
+
+  def update_authors_statuses!
+    old_author = University::Person.find_by(id: author_id_before_last_save)
+    if old_author && old_author.communication_website_posts.none?
+      old_author.update_and_sync(is_author: false)
+    end
+    author.update_and_sync(is_author: true) if author_id
+  end
 end
diff --git a/app/models/communication/website/with_dependencies.rb b/app/models/communication/website/with_dependencies.rb
index 2ee6c344a312b7e5a99ec6d68f09eeabbb82bc9e..c5158c6095581eb50cab223a93a6fb8f3e5a84fa 100644
--- a/app/models/communication/website/with_dependencies.rb
+++ b/app/models/communication/website/with_dependencies.rb
@@ -61,6 +61,10 @@ module Communication::Website::WithDependencies
     blocks_dependencies.reject { |dependency| !dependency.is_a? University::Person }
   end
 
+  def organizations_in_blocks
+    blocks_dependencies.reject { |dependency| !dependency.is_a? University::Organization }
+  end
+
   def people_with_facets_in_blocks
     blocks_dependencies.reject { |dependency| !dependency.class.to_s.start_with?('University::Person') }
   end
@@ -114,6 +118,10 @@ module Communication::Website::WithDependencies
     categories.any?
   end
 
+  def has_organizations?
+    has_organizations_in_blocks?
+  end
+
   def has_authors?
     authors.compact.any?
   end
@@ -122,6 +130,10 @@ module Communication::Website::WithDependencies
     people_in_blocks.compact.any?
   end
 
+  def has_organizations_in_blocks?
+    organizations_in_blocks.compact.any?
+  end
+
   def has_persons?
     has_authors? || has_administrators? || has_researchers? || has_teachers? || has_people_in_blocks?
   end
diff --git a/app/models/communication/website/with_git_repository.rb b/app/models/communication/website/with_git_repository.rb
index 436b1f64ef663f438eff86b1efabc1e93b935e83..1339143e00a5abb1cb3b51e66101999934732bd1 100644
--- a/app/models/communication/website/with_git_repository.rb
+++ b/app/models/communication/website/with_git_repository.rb
@@ -10,4 +10,18 @@ module Communication::Website::WithGitRepository
   def git_repository
     @git_repository ||= Git::Repository.new self
   end
+
+  def sync_objects_with_git(objects)
+    touch
+    return unless git_repository.valid?
+    objects.each do |object|
+      next unless object.has_website_for_self?(self)
+      dependencies = object.git_dependencies(self).to_a.flatten.uniq.compact
+      dependencies.each do |dependency|
+        Communication::Website::GitFile.sync self, dependency
+      end
+    end
+    git_repository.sync!
+  end
+  handle_asynchronously :sync_objects_with_git, queue: 'default'
 end
diff --git a/app/models/communication/website/with_menu_items.rb b/app/models/communication/website/with_menu_items.rb
index 51818072c81d59137a0d1f35bc47f1c359479524..3221927e0af25cf732b530ccf8edc6b3c347083c 100644
--- a/app/models/communication/website/with_menu_items.rb
+++ b/app/models/communication/website/with_menu_items.rb
@@ -40,6 +40,10 @@ module Communication::Website::WithMenuItems
     has_communication_posts?
   end
 
+  def menu_item_kind_organizations?
+    has_organizations?
+  end
+
   def menu_item_kind_staff?
     has_persons?
   end
diff --git a/app/models/communication/website/with_special_pages.rb b/app/models/communication/website/with_special_pages.rb
index 2b25ff672980cb19bb32673fef4d96ea150135b1..9b898c81fa6bc1ffc53c67360a89cf0acfd2717c 100644
--- a/app/models/communication/website/with_special_pages.rb
+++ b/app/models/communication/website/with_special_pages.rb
@@ -13,7 +13,7 @@ module Communication::Website::WithSpecialPages
     def create_missing_special_pages
       homepage = create_special_page('home')
       # first level pages with test
-      ['legal_terms', 'sitemap', 'privacy_policy', 'communication_posts', 'education_programs', 'research_articles', 'research_volumes'].each do |kind|
+      ['legal_terms', 'sitemap', 'privacy_policy', 'communication_posts', 'education_programs', 'research_articles', 'research_volumes', 'organizations'].each do |kind|
         create_special_page(kind, homepage.id) if public_send("has_#{kind}?")
       end
       # team pages
diff --git a/app/models/concerns/importable.rb b/app/models/concerns/importable.rb
index 6bc569eef9894ed0082e55ff57a6ceb95c9a7774..e2976a1a2c9b7db5f8a6668628923a241e5333cd 100644
--- a/app/models/concerns/importable.rb
+++ b/app/models/concerns/importable.rb
@@ -6,7 +6,7 @@ module Importable
 
     has_one_attached :file
 
-    after_save :parse_async
+    after_commit :parse_async
   end
 
   def lines
diff --git a/app/models/concerns/with_git.rb b/app/models/concerns/with_git.rb
index 174e5b227789f34b95b0bd1d5f1baf3ec1646eca..0b6845c2e1381f48f5c50a4c527cc665a2e28ce0 100644
--- a/app/models/concerns/with_git.rb
+++ b/app/models/concerns/with_git.rb
@@ -64,6 +64,18 @@ module WithGit
     end
   end
 
+  def has_website_for_self?(website)
+    websites_for_self.include?(website)
+  end
+
+  def git_dependencies(website = nil)
+    [self]
+  end
+
+  def git_destroy_dependencies(website = nil)
+    [self]
+  end
+
   protected
 
   def in_block_dependencies?(website)
@@ -83,12 +95,4 @@ module WithGit
       []
     end
   end
-
-  def git_dependencies(website = nil)
-    [self]
-  end
-
-  def git_destroy_dependencies(website = nil)
-    [self]
-  end
 end
diff --git a/app/models/education/academic_year.rb b/app/models/education/academic_year.rb
index 42878678e309e949e9123a3fd43c8270ae39c61d..ac89369971fb7286c5a8a2b490707b66089b7be3 100644
--- a/app/models/education/academic_year.rb
+++ b/app/models/education/academic_year.rb
@@ -21,6 +21,11 @@ class Education::AcademicYear < ApplicationRecord
 
   has_many :cohorts, class_name: 'Education::Cohort'
 
+  # Dénormalisation des alumni pour le faceted search
+  has_and_belongs_to_many   :university_people,
+                            class_name: 'University::Person',
+                            foreign_key: 'education_academic_year_id',
+                            association_foreign_key: 'university_person_id'
   has_many :people,
            class_name: 'University::Person',
            through: :cohorts
diff --git a/app/models/education/program.rb b/app/models/education/program.rb
index 77e3ae32d26ff26481939a0dae5cdb59d3f3d4bb..ef9465eacc3b327140cc2a88e74bc047f0d8cd9f 100644
--- a/app/models/education/program.rb
+++ b/app/models/education/program.rb
@@ -107,16 +107,39 @@ class Education::Program < ApplicationRecord
                           join_table: 'education_programs_schools',
                           foreign_key: 'education_program_id',
                           association_foreign_key: 'education_school_id'
+  has_many   :websites,
+             -> { distinct },
+             through: :schools
+
   has_many   :cohorts,
              class_name: 'Education::Cohort'
-  has_many   :websites, -> { distinct },
-             through: :schools
+
   has_many   :alumni,
              through: :cohorts,
              source: :people
+
+  has_many   :alumni_experiences,
+             -> { distinct },
+             class_name: 'University::Person::Experience',
+             through: :alumni,
+             source: :experiences
+  alias_attribute :experiences, :alumni_experiences
+
+  has_many   :alumni_organizations,
+             -> { distinct },
+             class_name: 'University::Organization',
+             through: :alumni_experiences,
+             source: :organization
+
   has_many   :academic_years,
              through: :cohorts
 
+   # Dénormalisation des alumni pour le faceted search
+   has_and_belongs_to_many   :university_people,
+                             class_name: 'University::Person',
+                             foreign_key: 'education_program_id',
+                             association_foreign_key: 'university_person_id'
+
   accepts_nested_attributes_for :university_person_involvements, reject_if: :all_blank, allow_destroy: true
 
   enum level: {
diff --git a/app/models/education/school.rb b/app/models/education/school.rb
index b5150fba513b7feeb0d2a5f36f469faa8f38cb7f..7149a637cd8a0e01c63cf649c937a507b9114acd 100644
--- a/app/models/education/school.rb
+++ b/app/models/education/school.rb
@@ -69,6 +69,18 @@ class Education::School < ApplicationRecord
   has_many    :alumni,
               -> { distinct },
               through: :programs
+  has_many    :alumni_experiences,
+              -> { distinct },
+              class_name: 'University::Person::Experience',
+              through: :alumni,
+              source: :experiences
+  alias_attribute :experiences, :alumni_experiences
+
+  has_many    :alumni_organizations,
+              -> { distinct },
+              class_name: 'University::Organization',
+              through: :alumni_experiences,
+              source: :organization
   has_many    :academic_years,
               -> { distinct },
               through: :programs
diff --git a/app/models/university/organization.rb b/app/models/university/organization.rb
index 7dd4201622409c85aed1ef46a1dfea5d035422b2..84a53aad21506c275584084e02c0f77aad848d52 100644
--- a/app/models/university/organization.rb
+++ b/app/models/university/organization.rb
@@ -54,6 +54,16 @@ class University::Organization < ApplicationRecord
     government: 30
   }
 
+  def git_dependencies(website)
+    dependencies = []
+    if for_website?(website)
+      dependencies << self
+      dependencies.concat active_storage_blobs
+    end
+    dependencies += website.menus.to_a
+    dependencies
+  end
+
   def websites
     university.communication_websites
   end
diff --git a/app/models/university/person.rb b/app/models/university/person.rb
index acc1f2dd895d59c7d5de7ae81e64c93e1cb05fec..df746435d0c942f8e57ccaa90a477fb3029f96c6 100644
--- a/app/models/university/person.rb
+++ b/app/models/university/person.rb
@@ -11,10 +11,12 @@
 #  habilitation      :boolean          default(FALSE)
 #  is_administration :boolean
 #  is_alumnus        :boolean          default(FALSE)
+#  is_author         :boolean
 #  is_researcher     :boolean
 #  is_teacher        :boolean
 #  last_name         :string
 #  linkedin          :string
+#  name              :string
 #  phone             :string
 #  slug              :string
 #  tenure            :boolean          default(FALSE)
@@ -44,6 +46,14 @@ class University::Person < ApplicationRecord
   include WithPicture
   include WithEducation
 
+  LIST_OF_ROLES = [
+    :administration,
+    :teacher,
+    :researcher,
+    :alumnus,
+    :author
+  ].freeze
+
   has_summernote :biography
 
   belongs_to :user, optional: true
@@ -87,8 +97,6 @@ class University::Person < ApplicationRecord
                           through: :education_programs,
                           source: :websites
 
-  has_many                :experiences
-
   accepts_nested_attributes_for :involvements
 
   validates_presence_of   :first_name, :last_name
@@ -101,29 +109,41 @@ class University::Person < ApplicationRecord
                           allow_blank: true,
                           if: :will_save_change_to_email?
 
-  before_validation :sanitize_email
+  before_validation :sanitize_email, :prepare_name
 
   scope :ordered,         -> { order(:last_name, :first_name) }
   scope :administration,  -> { where(is_administration: true) }
   scope :teachers,        -> { where(is_teacher: true) }
   scope :researchers,     -> { where(is_researcher: true) }
   scope :alumni,          -> { where(is_alumnus: true) }
+  scope :for_role, -> (role) { where("is_#{role}": true) }
+
+  scope :for_search_term, -> (term) {
+    where("
+      unaccent(concat(university_people.first_name, ' ', university_people.last_name)) ILIKE unaccent(:term) OR
+      unaccent(concat(university_people.last_name, ' ', university_people.first_name)) ILIKE unaccent(:term) OR
+      unaccent(university_people.first_name) ILIKE unaccent(:term) OR
+      unaccent(university_people.last_name) ILIKE unaccent(:term) OR
+      unaccent(university_people.email) ILIKE unaccent(:term) OR
+      unaccent(university_people.phone) ILIKE unaccent(:term) OR
+      unaccent(university_people.biography) ILIKE unaccent(:term) OR
+      unaccent(university_people.description) ILIKE unaccent(:term) OR
+      unaccent(university_people.description_short) ILIKE unaccent(:term) OR
+      unaccent(university_people.twitter) ILIKE unaccent(:term) OR
+      unaccent(university_people.url) ILIKE unaccent(:term)
+    ", term: "%#{sanitize_sql_like(term)}%")
+  }
 
   def to_s
     "#{first_name} #{last_name}"
   end
 
   def roles
-    [:administration, :teacher, :researcher, :alumnus, :author].reject do |role|
+    LIST_OF_ROLES.reject do |role|
       ! send "is_#{role}"
     end
   end
 
-  # TODO denormalize
-  def is_author
-    communication_website_posts.any?
-  end
-
   def websites
     university.communication_websites
   end
@@ -133,7 +153,7 @@ class University::Person < ApplicationRecord
   end
 
   def git_dependencies(website)
-    dependencies = website.menus.to_a
+    dependencies = []
     if for_website?(website)
       dependencies << self
       dependencies.concat active_storage_blobs
@@ -142,6 +162,7 @@ class University::Person < ApplicationRecord
     dependencies << author if author.for_website?(website)
     dependencies << researcher if researcher.for_website?(website)
     dependencies << teacher if teacher.for_website?(website)
+    dependencies += website.menus.to_a
     dependencies
   end
 
@@ -182,4 +203,8 @@ class University::Person < ApplicationRecord
   def sanitize_email
     self.email = self.email.to_s.downcase.strip
   end
+
+  def prepare_name
+    self.name = to_s
+  end
 end
diff --git a/app/models/university/person/administrator.rb b/app/models/university/person/administrator.rb
index f5fda080ad57e31d179660161ba2567f1bf4b526..387c93a640f1446247ac545493cbc6ab290b2d42 100644
--- a/app/models/university/person/administrator.rb
+++ b/app/models/university/person/administrator.rb
@@ -11,10 +11,12 @@
 #  habilitation      :boolean          default(FALSE)
 #  is_administration :boolean
 #  is_alumnus        :boolean          default(FALSE)
+#  is_author         :boolean
 #  is_researcher     :boolean
 #  is_teacher        :boolean
 #  last_name         :string
 #  linkedin          :string
+#  name              :string
 #  phone             :string
 #  slug              :string
 #  tenure            :boolean          default(FALSE)
diff --git a/app/models/university/person/alumnus.rb b/app/models/university/person/alumnus.rb
index 06c70f24cd3fd7a02f2ecd63a47a35dd48ed660a..2e140c8c5a3c4c64f2172aea733483431d94cc74 100644
--- a/app/models/university/person/alumnus.rb
+++ b/app/models/university/person/alumnus.rb
@@ -11,10 +11,12 @@
 #  habilitation      :boolean          default(FALSE)
 #  is_administration :boolean
 #  is_alumnus        :boolean          default(FALSE)
+#  is_author         :boolean
 #  is_researcher     :boolean
 #  is_teacher        :boolean
 #  last_name         :string
 #  linkedin          :string
+#  name              :string
 #  phone             :string
 #  slug              :string
 #  tenure            :boolean          default(FALSE)
diff --git a/app/models/university/person/alumnus/facets.rb b/app/models/university/person/alumnus/facets.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cd98815f4885f7ebaea5ab3608f4cb379c0080a3
--- /dev/null
+++ b/app/models/university/person/alumnus/facets.rb
@@ -0,0 +1,24 @@
+class University::Person::Alumnus::Facets < FacetedSearch::Facets
+  def initialize(params, options)
+    super params
+
+    @model = options[:model]
+    @about = options[:about]
+
+    filter_with_text :name, {
+      title: University::Person.human_attribute_name('name')
+    }
+
+    filter_with_list :diploma_years, {
+      source: @about.academic_years.ordered,
+      title: Education::AcademicYear.model_name.human(count: 2),
+      habtm: true
+    }
+
+    filter_with_list :diploma_programs, {
+      source: @about.programs.ordered,
+      title: Education::Program.model_name.human(count: 2),
+      habtm: true
+    } unless @about.is_a? Education::Program
+  end
+end
diff --git a/app/models/university/person/alumnus/import.rb b/app/models/university/person/alumnus/import.rb
index f8a05b767bb3d6168450dff2c1e1dbc205ba63c5..6da4f7198ebb804489d97287313c3f89bc247bba 100644
--- a/app/models/university/person/alumnus/import.rb
+++ b/app/models/university/person/alumnus/import.rb
@@ -87,7 +87,7 @@ class University::Person::Alumnus::Import < ApplicationRecord
       person.phone ||= row['phone_professional']
       byebug unless person.valid?
       person.save
-      cohort.people << person unless person.in?(cohort.people)
+      person.add_to_cohort cohort
       add_picture person, row['photo']
 
       company_name = clean_encoding row['company_name']
diff --git a/app/models/university/person/author.rb b/app/models/university/person/author.rb
index 67dda6a6e289d65603bd5184ad345b06e76283db..a51eadb10d87ba126e899071f130495f7b315e13 100644
--- a/app/models/university/person/author.rb
+++ b/app/models/university/person/author.rb
@@ -11,10 +11,12 @@
 #  habilitation      :boolean          default(FALSE)
 #  is_administration :boolean
 #  is_alumnus        :boolean          default(FALSE)
+#  is_author         :boolean
 #  is_researcher     :boolean
 #  is_teacher        :boolean
 #  last_name         :string
 #  linkedin          :string
+#  name              :string
 #  phone             :string
 #  slug              :string
 #  tenure            :boolean          default(FALSE)
diff --git a/app/models/university/person/researcher.rb b/app/models/university/person/researcher.rb
index 8fffe668f473cf48fa7237a2d68500b78c507ec1..44616e8492facc2bcb1908e8519bdcccff2d8bac 100644
--- a/app/models/university/person/researcher.rb
+++ b/app/models/university/person/researcher.rb
@@ -11,10 +11,12 @@
 #  habilitation      :boolean          default(FALSE)
 #  is_administration :boolean
 #  is_alumnus        :boolean          default(FALSE)
+#  is_author         :boolean
 #  is_researcher     :boolean
 #  is_teacher        :boolean
 #  last_name         :string
 #  linkedin          :string
+#  name              :string
 #  phone             :string
 #  slug              :string
 #  tenure            :boolean          default(FALSE)
diff --git a/app/models/university/person/teacher.rb b/app/models/university/person/teacher.rb
index df1e88803e668e76624ea44562d02825c1e7ccad..37140ba7b1d670cda457627f9b6feeccffbceb18 100644
--- a/app/models/university/person/teacher.rb
+++ b/app/models/university/person/teacher.rb
@@ -11,10 +11,12 @@
 #  habilitation      :boolean          default(FALSE)
 #  is_administration :boolean
 #  is_alumnus        :boolean          default(FALSE)
+#  is_author         :boolean
 #  is_researcher     :boolean
 #  is_teacher        :boolean
 #  last_name         :string
 #  linkedin          :string
+#  name              :string
 #  phone             :string
 #  slug              :string
 #  tenure            :boolean          default(FALSE)
diff --git a/app/models/university/person/with_education.rb b/app/models/university/person/with_education.rb
index 049d08f97bfa94e462c09995152b1430e9f8b52c..7ceb01ad43e2e20804028b45194de4c4d33be261 100644
--- a/app/models/university/person/with_education.rb
+++ b/app/models/university/person/with_education.rb
@@ -2,20 +2,32 @@ module University::Person::WithEducation
   extend ActiveSupport::Concern
 
   included do
-    has_many  :involvements_as_teacher,
-              -> { where(kind: 'teacher') },
-              class_name: 'University::Person::Involvement',
-              dependent: :destroy
+    has_many                :involvements_as_teacher,
+                            -> { where(kind: 'teacher') },
+                            class_name: 'University::Person::Involvement',
+                            dependent: :destroy
 
-    has_many  :education_programs_as_teacher,
-              through: :involvements_as_teacher,
-              source: :target,
-              source_type: "Education::Program"
+    has_many                :education_programs_as_teacher,
+                            through: :involvements_as_teacher,
+                            source: :target,
+                            source_type: "Education::Program"
+
+    has_many                :experiences
 
     has_and_belongs_to_many :cohorts,
                             class_name: 'Education::Cohort',
                             foreign_key: 'university_person_id',
                             association_foreign_key: 'education_cohort_id'
+
+    # Dénormalisation des liens via cohorts, pour la recherche par facettes
+    has_and_belongs_to_many :diploma_years,
+                            class_name: 'Education::AcademicYear',
+                            foreign_key: 'university_person_id',
+                            association_foreign_key: 'education_academic_year_id'
+    has_and_belongs_to_many :diploma_programs,
+                            class_name: 'Education::Program',
+                            foreign_key: 'university_person_id',
+                            association_foreign_key: 'education_program_id'
   end
 
   def education_programs_as_administrator
@@ -24,4 +36,10 @@ module University::Person::WithEducation
               .where(university_person_involvements: { person_id: id })
               .distinct
   end
+
+  def add_to_cohort(cohort)
+    cohorts << cohort unless cohort.in?(cohorts)
+    diploma_years << cohort.academic_year unless cohort.academic_year.in? diploma_years
+    diploma_programs << cohort.program unless cohort.program.in? diploma_programs
+  end
 end
diff --git a/app/models/university/with_communication.rb b/app/models/university/with_communication.rb
index ccf765d5ec5650ef9e4e1b17c7f0806e43a16f26..c43441eb8296b3480d6994078aacc5c6eba5e12e 100644
--- a/app/models/university/with_communication.rb
+++ b/app/models/university/with_communication.rb
@@ -2,8 +2,19 @@ module University::WithCommunication
   extend ActiveSupport::Concern
 
   included do
-    has_many :communication_extranets, class_name: 'Communication::Extranet', dependent: :destroy
-    has_many :communication_websites, class_name: 'Communication::Website', dependent: :destroy
-    has_many :communication_blocks, class_name: 'Communication::Block', dependent: :destroy
+    has_many  :communication_extranets,
+              class_name: 'Communication::Extranet',
+              dependent: :destroy
+    alias_attribute :extranets, :communication_extranets
+
+    has_many  :communication_websites,
+              class_name: 'Communication::Website',
+              dependent: :destroy
+    alias_attribute :websites, :communication_websites
+
+    has_many  :communication_blocks,
+              class_name: 'Communication::Block',
+              dependent: :destroy
+    alias_attribute :blocks, :communication_blocks
   end
 end
diff --git a/app/models/university/with_education.rb b/app/models/university/with_education.rb
index eab20006b33a912ae68713ef3a0d3892eb1fa1c8..95b2d605b02dcbfa2fc723b00573f8804211766d 100644
--- a/app/models/university/with_education.rb
+++ b/app/models/university/with_education.rb
@@ -2,9 +2,24 @@ module University::WithEducation
   extend ActiveSupport::Concern
 
   included do
-    has_many :education_cohorts, class_name: 'Education::Cohort', dependent: :destroy
-    has_many :education_programs, class_name: 'Education::Program', dependent: :destroy
-    has_many :education_schools, class_name: 'Education::School', dependent: :destroy
-    has_many :academic_years, class_name: 'Education::AcademicYear', dependent: :destroy
+    has_many  :education_cohorts,
+              class_name: 'Education::Cohort',
+              dependent: :destroy
+    alias_attribute :cohorts, :education_cohorts
+
+    has_many  :education_programs,
+              class_name: 'Education::Program',
+              dependent: :destroy
+    alias_attribute :programs, :education_programs
+
+    has_many  :education_schools,
+              class_name: 'Education::School',
+              dependent: :destroy
+    alias_attribute :schools, :education_schools
+
+    has_many  :education_academic_years,
+              class_name: 'Education::AcademicYear',
+              dependent: :destroy
+    alias_attribute :academic_years, :education_academic_years
   end
 end
diff --git a/app/models/university/with_people_and_organizations.rb b/app/models/university/with_people_and_organizations.rb
index ad270f63a8638aaef1549a029877ca523464715c..ad18670f9ec33a65633aeeababacedfa66545254 100644
--- a/app/models/university/with_people_and_organizations.rb
+++ b/app/models/university/with_people_and_organizations.rb
@@ -2,17 +2,25 @@ module University::WithPeopleAndOrganizations
   extend ActiveSupport::Concern
 
   included do
-    has_many  :people,
+    has_many  :university_people,
               class_name: 'University::Person',
               dependent: :destroy
-    has_many  :organizations,
+    alias_attribute :people, :university_people
+
+    has_many  :university_organizations,
               class_name: 'University::Organization',
               dependent: :destroy
-    has_many  :organization_imports,
+    alias_attribute :organizations, :university_organizations
+
+    has_many  :university_organization_imports,
               class_name: 'University::Organization::Import',
               dependent: :destroy
-    has_many :person_alumnus_imports,
+    alias_attribute :organization_imports, :university_organization_imports
+
+    has_many :university_person_alumnus_imports,
               class_name: 'University::Person::Alumnus::Import',
               dependent: :destroy
+    alias_attribute :person_alumnus_imports, :university_person_alumnus_imports
+    alias_attribute :alumnus_imports, :university_person_alumnus_imports
   end
 end
diff --git a/app/models/user/with_roles.rb b/app/models/user/with_roles.rb
index d4c44a698838bf3c44b776ac95bb1e68663401e6..bd1ef525c6855098e33165289fe5546817a311dc 100644
--- a/app/models/user/with_roles.rb
+++ b/app/models/user/with_roles.rb
@@ -4,7 +4,7 @@ module User::WithRoles
   included do
     attr_accessor :modified_by
 
-    enum role: { visitor: 0, teacher: 10, program_manager: 12, website_manager: 15, admin: 20, server_admin: 30 }
+    enum role: { visitor: 0, contributor: 4, author: 5, teacher: 10, program_manager: 12, website_manager: 15, admin: 20, server_admin: 30 }
 
     has_and_belongs_to_many :programs_to_manage,
                             class_name: 'Education::Program',
@@ -22,7 +22,7 @@ module User::WithRoles
     before_validation :check_modifier_role
 
     def self.roles_with_access_to_global_menu
-      roles.keys - ["website_manager"]
+      roles.keys - ['contributor', 'author', 'website_manager']
     end
 
     def managed_roles
diff --git a/app/services/filters/admin/university/people.rb b/app/services/filters/admin/university/people.rb
new file mode 100644
index 0000000000000000000000000000000000000000..515697eafa3b0dc4ec161cd5d6715c283f5d9e02
--- /dev/null
+++ b/app/services/filters/admin/university/people.rb
@@ -0,0 +1,9 @@
+module Filters
+  class Admin::University::People < Filters::Base
+    def initialize(user)
+      super
+      add_search
+      add :for_role, ::University::Person::LIST_OF_ROLES.map { |r| { to_s: I18n.t("activerecord.attributes.university/person.#{r}"), id: r } }, I18n.t('filters.attributes.role')
+    end
+  end
+end
diff --git a/app/services/filters/admin/users.rb b/app/services/filters/admin/users.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bc431fa8eccedc0ecb727251214081bd884eeb65
--- /dev/null
+++ b/app/services/filters/admin/users.rb
@@ -0,0 +1,9 @@
+module Filters
+  class Admin::Users < Filters::Base
+    def initialize(user)
+      super
+      add_search
+      add :for_role, ::User.roles.keys.map { |r| { to_s: I18n.t("activerecord.attributes.user.roles.#{r}"), id: r } }, I18n.t('filters.attributes.role')
+    end
+  end
+end
diff --git a/app/services/filters/user.rb b/app/services/filters/user.rb
deleted file mode 100644
index a71e11effad3718dc0f3e890188e92e6ec83c9e1..0000000000000000000000000000000000000000
--- a/app/services/filters/user.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module Filters
-  class User < Base
-    def initialize(user)
-      super
-      add_search
-      add :for_role, ::User.roles.keys.map { |r| { to_s: r.humanize, id: r } }, I18n.t('filters.attributes.role')
-    end
-  end
-end
diff --git a/app/views/admin/communication/extranets/show.html.erb b/app/views/admin/communication/extranets/show.html.erb
index 23351aae9e712411b746a634e6a4ca210a92f79d..1a270a1963698411e4cee1326a69b7202cba0cf3 100644
--- a/app/views/admin/communication/extranets/show.html.erb
+++ b/app/views/admin/communication/extranets/show.html.erb
@@ -28,6 +28,12 @@
     <%= Education::AcademicYear.model_name.human(count: @years.count).downcase %>
   </p>
 <% end %>
+<% if @organizations %>
+  <p>
+    <%= @organizations.count %>
+    <%= University::Organization.model_name.human(count: @organizations.count).downcase %>
+  </p>
+<% end %>
 
 <% content_for :action_bar_right do %>
   <%= edit_link @extranet %>
diff --git a/app/views/admin/communication/website/configs/permalinks/static.html.erb b/app/views/admin/communication/website/configs/permalinks/static.html.erb
index 9a5be71d01c728a12f20c386e8a90ca75bafee5d..ea940c61321255d1a68a1ae7fc885a70290eddc9 100644
--- a/app/views/admin/communication/website/configs/permalinks/static.html.erb
+++ b/app/views/admin/communication/website/configs/permalinks/static.html.erb
@@ -1,22 +1,22 @@
 <% if @website.has_communication_posts? %>
-posts:          <%= @website.special_page(:communication_posts).path %>/:year/:month/:day/:slug/
+posts:          <%= @website.special_page(:communication_posts).path_without_language %>:year/:month/:day/:slug/
 <% end %>
 <% if @website.has_communication_categories? %>
-categories:     <%= @website.special_page(:communication_posts).path %>/:slug/
+categories:     <%= @website.special_page(:communication_posts).path_without_language %>:slug/
 <% end %>
 <% if @website.has_persons? %>
-persons:        <%= @website.special_page(:persons).path %>/:slug/
+persons:        <%= @website.special_page(:persons).path_without_language %>:slug/
 <% end %>
 <% if @website.has_authors? %>
-authors:        <%= @website.special_page(:persons).path %>/:slug/<%= @website.special_page(:communication_posts).slug %>/
+authors:        <%= @website.special_page(:persons).path_without_language %>:slug/<%= @website.special_page(:communication_posts).slug %>/
 <% end %>
-<%# ces paths complémentaires sont nécessairesà Hugo mais on ne les utilise pas %>
+<%# ces paths complémentaires sont nécessaires à Hugo mais on ne les utilise pas %>
 <% if @website.has_administrators? %>
-administrators: <%= @website.special_page(:persons).path %>/:slug/roles/
+administrators: <%= @website.special_page(:persons).path_without_language %>:slug/roles/
 <% end %>
 <% if @website.has_teachers? %>
-teachers:       <%= @website.special_page(:persons).path %>/:slug/programs/
+teachers:       <%= @website.special_page(:persons).path_without_language %>:slug/programs/
 <% end %>
 <% if @website.has_researchers? %>
-researchers:    <%= @website.special_page(:persons).path %>/:slug/articles/
+researchers:    <%= @website.special_page(:persons).path_without_language %>:slug/articles/
 <% end %>
diff --git a/app/views/admin/communication/website/pages/_form.html.erb b/app/views/admin/communication/website/pages/_form.html.erb
index 778a032fcc13267e60cbcf9b1b7b7a4ddfbb2adf..16ce0f956b5a3c56da1e761e41ecd0c02f534e4c 100644
--- a/app/views/admin/communication/website/pages/_form.html.erb
+++ b/app/views/admin/communication/website/pages/_form.html.erb
@@ -8,7 +8,7 @@
         <div class="card-body">
           <%= f.input :title %>
           <%= f.input :breadcrumb_title %>
-          <%= f.input :description_short %>
+          <%= f.input :description_short, input_html: { value: page.description_short&.gsub('&amp;', '&') } %>
           <%= f.input :header_text,
                       as: :summernote,
                       input_html: {
diff --git a/app/views/admin/communication/website/pages/show.html.erb b/app/views/admin/communication/website/pages/show.html.erb
index c29dbc0f40a4c4da54ff7353c37eeb89435d9907..c21247fd9894e5bd6375e7431b857d2a463e67d0 100644
--- a/app/views/admin/communication/website/pages/show.html.erb
+++ b/app/views/admin/communication/website/pages/show.html.erb
@@ -4,6 +4,16 @@
 
   <div class="row">
     <div class="col-md-8">
+
+      <% if @page.is_special_page? %>
+        <div class="card text-white bg-secondary flex-fill w-100">
+          <div class="card-body">
+            <%= t('admin.communication.website.pages.is_special_page') %>
+            <b class="text-white"><%= t("communication.website.pages.defaults.#{@page.kind}.admin_description") %></b>
+          </div>
+        </div>
+      <% end %>
+
       <div class="card flex-fill w-100">
         <div class="card-header">
           <h2 class="card-title mb-0 h5"><%= t('content') %></h2>
diff --git a/app/views/admin/communication/website/pages/static.html.erb b/app/views/admin/communication/website/pages/static.html.erb
index 305f406207362ad68d420b4111e466a8fa50cab9..4d2c9f2f194ee2dce4bd7c91a775615558f3a377 100644
--- a/app/views/admin/communication/website/pages/static.html.erb
+++ b/app/views/admin/communication/website/pages/static.html.erb
@@ -9,6 +9,12 @@ bodyclass: <%= @about.best_bodyclass %>
 image: "<%= @about.best_featured_image.blob.id %>"
 image_alt: "<%= @about.featured_image_alt %>"
 <% end %>
+<% if @about.children.published.any? %>
+children:
+<% @about.children.published.ordered.each do |child| %>
+  - <%= child.path %>
+<% end %>
+<% end %>
 <% if @about.related_category %>
 category: "<%= @about.related_category.path %>"
 <% end %>
diff --git a/app/views/admin/communication/website/posts/_form.html.erb b/app/views/admin/communication/website/posts/_form.html.erb
index 32ffdeb4543b4dca0ebb82ff8efb1092f74fcfc0..711e59170816faf4cf6451a67b2a07468456eb87 100644
--- a/app/views/admin/communication/website/posts/_form.html.erb
+++ b/app/views/admin/communication/website/posts/_form.html.erb
@@ -29,10 +29,16 @@
           <% elsif @website.languages.any? %>
             <%= f.input :language_id, as: :hidden, input_html: { value: @website.languages.first.id }, wrapper: false %>
           <% end %>
-          <%= f.input :published %>
-          <%= f.input :published_at, html5: true %>
+          <% if can? :publish, post %>
+            <%= f.input :published %>
+            <%= f.input :published_at, html5: true %>
+          <% end %>
           <%= f.input :pinned %>
-          <%= f.association :author, collection: current_university.people.ordered %>
+          <% if current_user.author? || current_user.contributor? %>
+            <%= f.input :author_id, as: :hidden, input_html: { value: current_user.person&.id }, wrapper: false %>
+          <% else %>
+            <%= f.association :author, collection: current_university.people.ordered %>
+          <% end %>
         </div>
       </div>
       <div class="card flex-fill w-100">
diff --git a/app/views/admin/communication/website/posts/_list.html.erb b/app/views/admin/communication/website/posts/_list.html.erb
index 61f78aa6ced46a4de5dcf6c09a939a47100357da..8ebc3a67791990b8cd12e8238044fc6772047791 100644
--- a/app/views/admin/communication/website/posts/_list.html.erb
+++ b/app/views/admin/communication/website/posts/_list.html.erb
@@ -1,10 +1,16 @@
 <%
   hide_author |= false
   hide_category |= false
+  selectable |= false
 %>
-<table class="<%= table_classes %>">
+<table class="<%= table_classes %>" <%= "data-batch-selectable" if selectable %>>
   <thead>
     <tr>
+      <% if selectable %>
+        <th>
+          <%= check_box_tag nil, nil, false, data: { batch_selectable_role: "select-all" } %>
+        </th>
+      <% end %>
       <th><%= Communication::Website::Post.human_attribute_name('title') %></th>
       <th><%= Communication::Website::Post.human_attribute_name('featured_image') %></th>
       <% unless hide_author %>
@@ -20,6 +26,11 @@
   <tbody>
     <% posts.each do |post| %>
       <tr>
+        <% if selectable %>
+          <td>
+            <%= check_box_tag "ids[]", post.id, false, data: { batch_selectable_role: "select-single" } %>
+          </td>
+        <% end %>
         <td><%= link_to post,
                         admin_communication_website_post_path(website_id: post.website.id, id: post.id),
                         class: "#{'opacity-50' unless post.published?}" %></td>
diff --git a/app/views/admin/communication/website/posts/index.html.erb b/app/views/admin/communication/website/posts/index.html.erb
index 814a431bf51ab1da6c16ae00eb1bec4d3504f962..7b70ee606cf1881796fadd9d3dadcde245614b32 100644
--- a/app/views/admin/communication/website/posts/index.html.erb
+++ b/app/views/admin/communication/website/posts/index.html.erb
@@ -2,10 +2,29 @@
 
 <%= render 'admin/communication/websites/sidebar' do %>
   <div class="card">
-    <%= render 'admin/communication/website/posts/list', posts: @posts, hide_author: true %>
-    <% if @posts.total_pages > 1 %>
+    <%= form_tag publish_admin_communication_website_posts_path do %>
+      <input type="hidden" name="ids[]" value="">
+      <%= render 'admin/communication/website/posts/list', posts: @posts, selectable: true %>
       <div class="card-footer">
-        <%= paginate @posts, theme: 'bootstrap-5' %>
+        <% if @posts.total_pages > 1 %>
+          <div class="float-end">
+            <%= paginate @posts, theme: 'bootstrap-5' %>
+          </div>
+        <% end %>
+        <div class="row align-items-center">
+          <div class="col-auto">
+            Modifier la sélection
+          </div>
+          <div class="col-auto">
+            <select name="published" class="form-select">
+              <option value="false">Non publiée</option>
+              <option value="true">Publiée</option>
+            </select>
+          </div>
+          <div class="col-auto">
+            <input type="submit" value="Enregistrer" class="btn btn-primary">
+          </div>
+        </div>
       </div>
     <% end %>
   </div>
diff --git a/app/views/admin/communication/websites/index.html.erb b/app/views/admin/communication/websites/index.html.erb
index b4ebe5d3fca6bd2f4b51d1d6521c6ba2ad9c4dd0..a4b8e7879ad34aafd194cf122331daf8d6595a95 100644
--- a/app/views/admin/communication/websites/index.html.erb
+++ b/app/views/admin/communication/websites/index.html.erb
@@ -16,7 +16,7 @@
       <td><%= link_to website, [:admin, website] %></td>
       <td><%= link_to website.url, website.url, target: :_blank %></td>
       <td><%= I18n.t("activerecord.attributes.communication/website.about_#{website.about_type}") %></td>
-      <td><%= link_to website.about, [:admin, website.about] if website.about %></td>
+      <td><%= link_to_if can?(:read, website.about), website.about, [:admin, website.about] if website.about %></td>
       <td class="text-end">
         <div class="btn-group" role="group">
           <%= edit_link website %>
diff --git a/app/views/admin/communication/websites/show.html.erb b/app/views/admin/communication/websites/show.html.erb
index 5057c724143e0766d113becb013237861bc4dbdc..89747b56bdd697807c6271e9d0733543232323a1 100644
--- a/app/views/admin/communication/websites/show.html.erb
+++ b/app/views/admin/communication/websites/show.html.erb
@@ -6,7 +6,7 @@
   <% end %>
   <%= I18n.t("activerecord.attributes.communication/website.about_#{@website.about_type}") %>
   <% if @website.about %>
-    (<%= link_to @website.about, [:admin, @website.about] unless @website.about.nil? %>)
+    (<%= link_to_if can?(:read, @website.about), @website.about, [:admin, @website.about] unless @website.about.nil? %>)
   <% end %>
 <% end %>
 
diff --git a/app/views/admin/dashboard/index.html.erb b/app/views/admin/dashboard/index.html.erb
index 4df287ce4de9a1685862a8367b2e03b4cc29e664..3ce8510ebcbc25717e780df003f37052023eb669 100644
--- a/app/views/admin/dashboard/index.html.erb
+++ b/app/views/admin/dashboard/index.html.erb
@@ -52,3 +52,22 @@
     <% end %>
   </div>
 <% end %>
+
+<% if current_university.communication_extranets.any? && can?(:read, Communication::Extranet) %>
+  <h2 class="h4 my-4"><%= Communication::Extranet.model_name.human(count: 2) %></h2>
+  <div class="row">
+    <% current_university.communication_extranets.each do |extranet| %>
+      <% next unless can?(:read, extranet) %>
+      <div class="col-md-4">
+        <div class="card">
+          <div class="card-body">
+            <span class="float-end"><i class="fas fa-project-diagram fa-2x"></i></span>
+            <h4><%= extranet %></h4>
+            <p class="small"><%= extranet.url %></p>
+            <%= link_to t('show'), [:admin, extranet], class: button_classes('stretched-link') %>
+          </div>
+        </div>
+      </div>
+    <% end %>
+  </div>
+<% end %>
diff --git a/app/views/admin/education/programs/_form.html.erb b/app/views/admin/education/programs/_form.html.erb
index 5ee17c146b10ad150a34501f63751edd210a3004..50138adea6e35c294c1a15f9fa8a32c7a55cbf07 100644
--- a/app/views/admin/education/programs/_form.html.erb
+++ b/app/views/admin/education/programs/_form.html.erb
@@ -58,26 +58,18 @@
         </div>
         <div class="card-body">
           <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :presentation %>
-          <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :registration %>
-          <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :pricing %>
-          <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :duration %>
-          <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :accessibility %>
-          <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :other %>
-        </div>
-      </div>
-
-      <div class="card flex-fill w-100">
-        <div class="card-header">
-          <h5 class="card-title mb-0"><%= t('education.program.educational_informations') %></h5>
-        </div>
-        <div class="card-body">
           <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :objectives %>
+          <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :opportunities %>
+          <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :results %>
+          <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :accessibility %>
+          <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :duration %>
+          <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :pricing %>
           <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :content %>
-          <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :prerequisites %>
           <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :pedagogy %>
           <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :evaluation %>
-          <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :opportunities %>
-          <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :results %>
+          <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :prerequisites %>
+          <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :registration %>
+          <%= render 'admin/education/programs/forms/input_with_inheritance', f: f, property: :other %>
         </div>
       </div>
 
diff --git a/app/views/admin/education/programs/show.html.erb b/app/views/admin/education/programs/show.html.erb
index 51064ef1ccb42ed7cfff872bca6f4495356a52f9..906b1a681597d70ac49514cf43195e2d143ca60f 100644
--- a/app/views/admin/education/programs/show.html.erb
+++ b/app/views/admin/education/programs/show.html.erb
@@ -58,35 +58,23 @@
       <div class="card-body">
         <% [
               :presentation,
-              :registration,
-              :pricing,
-              :duration,
-              :contacts,
+              :objectives,
+              :opportunities,
+              :results,
               :accessibility,
+              :duration,
+              :pricing,
+              :content,
+              :pedagogy,
+              :evaluation,
+              :prerequisites,
+              :registration,
               :other
             ].each_with_index do |property, index| %>
             <%= render 'admin/application/property/text', object: @program, property: property %>
         <% end %>
       </div>
     </div>
-    <div class="card flex-fill w-100">
-      <div class="card-header">
-        <h5 class="card-title mb-0"><%= t('education.program.educational_informations') %></h5>
-      </div>
-      <div class="card-body">
-        <%  [
-        :objectives,
-        :content,
-        :prerequisites,
-        :pedagogy,
-        :evaluation,
-        :opportunities,
-        :results
-        ].each do |property| %>
-        <%= render 'admin/application/property/text', object: @program, property: property %>
-        <% end %>
-      </div>
-    </div>
 
     <%= render 'admin/communication/blocks/list', about: @program %>
 
diff --git a/app/views/admin/university/people/index.html.erb b/app/views/admin/university/people/index.html.erb
index b95e8aa322d9c79d5f98203b273e6acaa580aea7..df601c13f7ed7424fe37ef9a2f4e46cd067630ee 100644
--- a/app/views/admin/university/people/index.html.erb
+++ b/app/views/admin/university/people/index.html.erb
@@ -1,5 +1,7 @@
 <% content_for :title, "#{University::Person.model_name.human(count: 2)} (#{@people.total_count})" %>
 
+<%= render 'filters', current_path: admin_university_people_path, filters: @filters if @filters.any?  %>
+
 <%= render 'admin/university/people/list', people: @people %>
 <%= paginate @people, theme: 'bootstrap-5' %>
 
diff --git a/app/views/admin/users/_form.html.erb b/app/views/admin/users/_form.html.erb
index 6a025d1642892f9c768bfd832951d3f08ad7a777..c2e732e90cea492564749b7fff0bedbecb807356 100644
--- a/app/views/admin/users/_form.html.erb
+++ b/app/views/admin/users/_form.html.erb
@@ -37,7 +37,7 @@
                               label_method: ->(p) { sanitize p[:label] },
                               value_method: ->(p) { p[:id] } %>
           </div>
-          <div data-show-for-roles="website_manager">
+          <div data-show-for-roles="author,contributor,website_manager">
             <%= f.association :websites_to_manage,
                               as: :check_boxes,
                               collection: current_university.communication_websites.ordered
diff --git a/app/views/admin/users/index.html.erb b/app/views/admin/users/index.html.erb
index af80ff5b3d6d1520db414931e45d74790689756d..1e1ee9d58e0331d332c45fd08d547ff9c80ddef1 100644
--- a/app/views/admin/users/index.html.erb
+++ b/app/views/admin/users/index.html.erb
@@ -21,7 +21,7 @@
         <td><%= link_to user.email, [:admin, user] %></td>
         <td><%= user.first_name %></td>
         <td><%= user.last_name %></td>
-        <td><%= user.role.humanize %></td>
+        <td><%= t("activerecord.attributes.user.roles.#{user.role}") %></td>
         <td><%= user.language %></td>
         <td class="text-end">
           <div class="btn-group" role="group">
diff --git a/app/views/extranet/home/index.html.erb b/app/views/extranet/home/index.html.erb
index 3a6a045cbdabf5e569b57de4a9afd5061302f7d7..aa3714a7af09b02bc46eaa44c985e84362a29448 100644
--- a/app/views/extranet/home/index.html.erb
+++ b/app/views/extranet/home/index.html.erb
@@ -2,7 +2,16 @@
 
 <div class="row">
   <div class="col-md-8">
-
+    <h2>Mouvements récents</h2>
+    <% @experiences.each do |experience| %>
+      <article>
+        <%= link_to experience.person, experience.person %>
+        <%= experience.description %>
+        <%= experience.from_year %>
+        <%= experience.to_year %>
+        <%= link_to experience.organization, experience.organization %>
+      </article>
+    <% end %>
   </div>
   <div class="col-md-4">
     <h2>Promotions actuelles</h2>
diff --git a/app/views/extranet/organizations/index.html.erb b/app/views/extranet/organizations/index.html.erb
index 5796718de6abe4e3aee343cff839fdb3ac81f74a..b1229875b7627de2122e7d4ec855f4e31c3bd2b2 100644
--- a/app/views/extranet/organizations/index.html.erb
+++ b/app/views/extranet/organizations/index.html.erb
@@ -2,6 +2,10 @@
 
 <header class="mb-5">
   <h1><%= University::Organization.model_name.human(count: 2) %></h1>
+  <p>
+    <%= @count %>
+    <%= University::Organization.model_name.human(count: @count).downcase %>
+  </p>
 </header>
 
 <table class="<%= table_classes %>">
diff --git a/app/views/extranet/persons/_list.html.erb b/app/views/extranet/persons/_list.html.erb
index 7922cd391743569fccc61271a420850d8171c7c8..064c370335f8f7eb06794db58c6ebe770b772ba0 100644
--- a/app/views/extranet/persons/_list.html.erb
+++ b/app/views/extranet/persons/_list.html.erb
@@ -2,19 +2,7 @@
 <div class="row">
   <% people_paged.each do |person| %>
     <div class="col-xxl-2 col-md-3">
-      <article class="mb-4 position-relative">
-        <% if person.picture.attached? %>
-          <%= kamifusen_tag person.picture, width: 400, class: 'img-fluid' %>
-        <% else %>
-          <%= image_tag 'extranet/avatar.png', width: 400, class: 'img-fluid' %>
-        <% end %>
-        <%= link_to person, class: 'stretched-link' do %>
-          <%= person.first_name %>
-          <b>
-            <%= person.last_name %>
-          </b>
-        <% end %>
-      </article>
+      <%= render 'extranet/persons/person', person: person %>
     </div>
   <% end %>
 </div>
diff --git a/app/views/extranet/persons/_person.html.erb b/app/views/extranet/persons/_person.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..f8151bdac4973a6d5355874e2e713a73ded36928
--- /dev/null
+++ b/app/views/extranet/persons/_person.html.erb
@@ -0,0 +1,11 @@
+<article class="mb-4 position-relative">
+  <% if person.picture.attached? %>
+    <%= kamifusen_tag person.picture, width: 400, class: 'img-fluid' %>
+  <% else %>
+    <%= image_tag 'extranet/avatar.png', width: 400, class: 'img-fluid' %>
+  <% end %>
+  <%= link_to person, class: 'stretched-link' do %>
+    <%= person.first_name %>
+    <b><%= person.last_name %></b>
+  <% end %>
+</article>
diff --git a/app/views/extranet/persons/index.html.erb b/app/views/extranet/persons/index.html.erb
index f12ba91324d414923362034d30c58f500749eb0c..11b59fcc05a3b300839db66b7ab245544a15c998 100644
--- a/app/views/extranet/persons/index.html.erb
+++ b/app/views/extranet/persons/index.html.erb
@@ -7,11 +7,26 @@
     </div>
     <div class="col-md-3 text-end">
       <p>
-        <%= @people.count %>
-        <%= University::Person::Alumnus.model_name.human(count: @people.count).downcase %>
+        <%= @count %>
+        <%= University::Person::Alumnus.model_name.human(count: @count).downcase %>
       </p>
     </div>
   </div>
 </header>
 
-<%= render 'extranet/persons/list', people: @people %>
+<div class="row">
+  <div class="col-lg-3">
+    <%= render 'faceted_search/facets', facets: @facets %>
+  </div>
+  <div class="offset-lg-1 col-lg-8">
+    <div class="row">
+      <% @people.each do |person| %>
+        <div class="col-xxl-3 col-md-4 col-sm-6">
+          <%= render 'extranet/persons/person', person: person %>
+        </div>
+      <% end %>
+    </div>
+    <%= paginate @people, theme: 'bootstrap-5' %>
+
+  </div>
+</div>
diff --git a/config/admin_navigation.rb b/config/admin_navigation.rb
index b2d084377f0c423b281c766a43c531a1d87449ed..4fccef35596f4b3505cad976350d55b20d6c6fdd 100644
--- a/config/admin_navigation.rb
+++ b/config/admin_navigation.rb
@@ -34,7 +34,7 @@ SimpleNavigation::Configuration.run do |navigation|
     if can?(:read, Communication::Website)
       primary.item :communication, Communication.model_name.human, nil, { kind: :header }
       primary.item :communication_websites, Communication::Website.model_name.human(count: 2), admin_communication_websites_path, { icon: 'sitemap' } if can?(:read, Communication::Website)
-      primary.item :communication_extranets, Communication::Extranet.model_name.human(count: 2), admin_communication_extranets_path, { icon: 'project-diagram' }
+      primary.item :communication_extranets, Communication::Extranet.model_name.human(count: 2), admin_communication_extranets_path, { icon: 'project-diagram' } if can?(:read, Communication::Extranet)
       primary.item :communication_newsletters, 'Lettres d\'information', nil, { icon: 'envelope' }
     end
 
diff --git a/config/initializers/active_storage.rb b/config/initializers/active_storage.rb
index 09d7980d77629f465a5144e2a5da55eca12d60dd..89b6ff7a9e5abf6fae93de9a005c937736b1cd79 100644
--- a/config/initializers/active_storage.rb
+++ b/config/initializers/active_storage.rb
@@ -33,22 +33,23 @@ ActiveStorage::Filename.class_eval do
   end
 end
 
-module ActiveStorageGitPathStatic
-  extend ActiveSupport::Concern
+# https://stackoverflow.com/questions/8895103/how-can-i-keep-my-initializer-configuration-from-being-lost-in-development-mode
+Rails.application.config.to_prepare do
+  module ActiveStorageGitPathStatic
+    extend ActiveSupport::Concern
 
-  included do
-    has_many :git_files, class_name: "Communication::Website::GitFile", as: :about, dependent: :destroy
-  end
+    included do
+      has_many :git_files, class_name: "Communication::Website::GitFile", as: :about, dependent: :destroy
+    end
 
-  def git_path(website)
-    "data/media/#{id[0..1]}/#{id}.yml"
-  end
+    def git_path(website)
+      "data/media/#{id[0..1]}/#{id}.yml"
+    end
 
-  def before_git_sync
-    analyze unless analyzed?
+    def before_git_sync
+      analyze unless analyzed?
+    end
   end
-end
 
-ActiveSupport::Reloader.to_prepare do
   ActiveStorage::Blob.include ActiveStorageGitPathStatic
 end
diff --git a/config/locales/communication/en.yml b/config/locales/communication/en.yml
index c30154843ee00dee1e5a0e5cd87dfd963e647f4e..abc8d02429e9f58c9aa1d78ffc061aff0291ebc7 100644
--- a/config/locales/communication/en.yml
+++ b/config/locales/communication/en.yml
@@ -200,6 +200,7 @@ en:
       website:
         pages:
           delete_special_page_notice: Can't delete this page
+          is_special_page: "This is a page with a special behaviour:"
           structure: Structure
   communication:
     authors:
@@ -228,54 +229,72 @@ en:
       pages:
         defaults:
           administrators:
+            admin_description: list of members in the administrative team
             description_short: List of members in the administrative team
             slug: administrative-team
             title: Administrative team
           authors:
+            admin_description: list of members in the editorial team
             description_short: List of members in the editorial team
             slug: editorial-team
             title: Editorial team
           communication_posts:
+            admin_description: list of posts
             description_short: List of posts
             slug: posts
             title: Posts
           education_programs:
+            admin_description: list of available programs
             description_short: List of available programs
             slug: programs
             title: Programs
           home:
+            admin_description: homepage
             description_short: ''
             slug: ''
             title: Home
           legal_terms:
+            admin_description: legal terms page
             description_short: ''
             slug: legal-terms
             title: Legal terms
+          organizations:
+            admin_description: third parties page
+            description_short: List of third parties
+            slug: third-parties
+            title: Third parties
           persons:
+            admin_description: list of members in the team
             description_short: List of members in the team
             slug: team
             title: Team
           privacy_policy:
+            admin_description: privacy policy page
             description_short: ''
             slug: privacy-policy
             title: Privacy policy
           research_articles:
+            admin_description: list of articles
             description_short: List of articles
             slug: articles
             title: Articles
           research_volumes:
+            admin_description: list of volumes
             description_short: List of volumes
             slug: volumes
             title: Volumes
           researchers:
+            admin_description: list of members in the research team
             description_short: List of members in the research team
             slug: research-team
             title: Research team
           sitemap:
+            admin_description: sitemap page
             description_short: ''
             slug: sitemap
             title: Sitemap
           teachers:
+            admin_description: list of members in the educational team
             description_short: List of members in the educational team
             slug: educational-team
             title: Educational team
@@ -306,6 +325,7 @@ en:
               news: News index
               news_article: Specific news
               news_category: News category
+              organizations: Third parties index
               page: Specific Page
               program: Specific program
               programs: Programs index
diff --git a/config/locales/communication/fr.yml b/config/locales/communication/fr.yml
index bfe0320142a52322982ce96afc50a62a73c11e71..95e7e7433936da3b83b4242f8c7120dd733ed574 100644
--- a/config/locales/communication/fr.yml
+++ b/config/locales/communication/fr.yml
@@ -202,6 +202,7 @@ fr:
       website:
         pages:
           delete_special_page_notice: Impossible de supprimer cette page
+          is_special_page: "Ceci est une page avec un comportement spécial :"
           structure: Arborescence
   communication:
     authors:
@@ -230,59 +231,78 @@ fr:
       pages:
         defaults:
           administrators:
+            admin_description: liste des membres de l'équipe administrative
             description_short: Liste des membres de l'équipe administrative
             slug: equipe-administrative
             title: Équipe administrative
           authors:
+            admin_description: liste des membres de l'équipe éditoriale
             description_short: Liste des membres de l'équipe éditoriale
             slug: equipe-editoriale
             title: Équipe éditoriale
           communication_posts:
+            admin_description: liste des actualités
             description_short: Liste des actualités
             slug: actualites
             title: Actualités
           education_programs:
+            admin_description: liste des formations proposées
             description_short: Liste des formations proposées
             slug: offre-de-formation
             title: "L'offre de formation"
           home:
+            admin_description: page d'accueil du site
             description_short: ''
             slug: ''
             title: Accueil
           legal_terms:
+            admin_description: page des mentions légales
             description_short: ''
             slug: mentions-legales
             title: Mentions légales
+          organizations:
+            admin_description: page des tierces parties
+            description_short: Liste des tierces parties
+            slug: tierces-parties
+            title: Tierces parties
           persons:
+            admin_description: liste des membres de l'équipe
             description_short: Liste des membres de l'équipe
             slug: equipe
             title: Équipe
           privacy_policy:
+            admin_description: page de la politique de confidentialité
             description_short: ''
             slug: politique-de-confidentialite
             title: Politique de confidentialité
           research_articles:
+            admin_description: liste des articles
             description_short: Liste des articles
             slug: articles
             title: Articles
           research_volumes:
+            admin_description: liste des volumes
             description_short: Liste des volumes
             slug: volumes
             title: Volumes
           researchers:
+            admin_description: liste des membres de l'équipe de recherche
             description_short: Liste des membres de l'équipe de recherche
             slug: equipe-de-recherche
             title: Équipe de recherche
           sitemap:
+            admin_description: page plan du site
             description_short: ''
             slug: plan-du-site
             title: Plan du site
           teachers:
+            admin_description: liste des membres de l'équipe pédagogique
             description_short: Liste des membres de l'équipe pédagogique
             slug: equipe-pedagogique
             title: Équipe pédagogique
       posts:
         new_curation: Nouvelle curation
+        successful_batch_update: Les actualités ont bien été mises à jour
       see_all: Voir la liste complète (%{number} éléments)
   enums:
     communication:
@@ -308,6 +328,7 @@ fr:
               news: Liste des actualités
               news_article: Actualité spécifique
               news_category: Catégorie d'actualités
+              organizations: Liste des tierces parties
               page: Page spécifique
               program: Formation spécifique
               programs: Liste des formations
diff --git a/config/locales/en.yml b/config/locales/en.yml
index a07167520f99ab724299d056b8e36c63660e76e4..49586fa67c5b553e343b068df01bb7d30bb144ed 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -16,11 +16,14 @@ en:
         role: Role
         roles:
           admin: Administrator
+          author: Author
+          contributor: Contributor
           program_manager: Program manager
           server_admin: Server admin
           teacher: Teacher
           visitor: Visitor
           website_manager: Website manager
+        websites_to_manage: Websites managed
     errors:
       models:
         user:
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index cfb4f9c6e825a5e1a49203a8d982ad65e2e16602..2963ff7588bcd53d3ef6666f8267e72bb08564de 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -16,11 +16,14 @@ fr:
         role: Rôle
         roles:
           admin: Administrateur
+          author: Auteur
+          contributor: Contributeur
           program_manager: Responsable de formation
           server_admin: Administrateur du serveur
           teacher: Enseignant·e
           visitor: Visiteur
           website_manager: Responsable de site web
+        websites_to_manage: Sites gérés
     errors:
       models:
         user:
diff --git a/config/locales/university/en.yml b/config/locales/university/en.yml
index 7762500fbf91d803ec8cc59e4c2d96097239854c..dcd43222b31c92c6049f9ab1ea77c12c41bc0948 100644
--- a/config/locales/university/en.yml
+++ b/config/locales/university/en.yml
@@ -18,6 +18,9 @@ en:
         zipcode: Zipcode
       university/person:
         abilities: Abilities
+        administration: Administrative staff
+        alumnus: Alumnus
+        author: Author
         biography: Biography
         communication_website_posts: Posts
         description: Meta Description
@@ -37,8 +40,11 @@ en:
         phone: Phone
         picture: Profile picture
         research_journal_articles: Articles
+        researcher: Researcher
+        roles: Roles
         slug: Slug
         socials: Socials
+        teacher: Teacher
         tenure: Has tenure?
         twitter: Twitter username
         url: Website
@@ -82,8 +88,8 @@ en:
         one: Alumnus
         other: Alumni
       university/organization:
-        one: Third party
-        other: Third parties
+        one: Organization
+        other: Organizations
       university/organization/import:
         one: Import
         other: Imports
diff --git a/config/locales/university/fr.yml b/config/locales/university/fr.yml
index 26e9ee1d95282668b412c266859bba48e2c1eac9..a401225ae0a8c5cf267e499a01385dd8b14804fe 100644
--- a/config/locales/university/fr.yml
+++ b/config/locales/university/fr.yml
@@ -18,6 +18,9 @@ fr:
         zipcode: Code postal
       university/person:
         abilities: Responsabilités
+        administration: Personnel administratif
+        alumnus: Alumnus
+        author: Auteur·rice
         biography: Biographie
         communication_website_posts: Actualités
         description: Meta Description
@@ -31,19 +34,17 @@ fr:
         is_author: Auteur·rice
         is_researcher: Chercheur·se
         is_teacher: Enseignant·e
-        administration: Personnel administratif
-        alumnus: Alumnus
-        author: Auteur·rice
-        researcher: Chercheur·se
-        teacher: Enseignant·e
         last_name: Nom de famille
         linkedin: LinkedIn (URL)
         name: Nom
         phone: Téléphone
         picture: Photo de profil
         research_journal_articles: Articles
+        researcher: Chercheur·se
+        roles: Rôles
         slug: Slug
         socials: Réseaux sociaux
+        teacher: Enseignant·e
         tenure: Titulaire ?
         twitter: Twitter (nom d'utilisateur)
         url: Site web
@@ -87,8 +88,8 @@ fr:
         one: Alumnus
         other: Alumni
       university/organization:
-        one: Tierce partie
-        other: Tierces parties
+        one: Organisation
+        other: Organisations
       university/organization/import:
         one: Import
         other: Imports
diff --git a/config/routes/admin/communication.rb b/config/routes/admin/communication.rb
index 4ebcf1ce0531974450775c95d87cadc58b771641..8e3cfeb11a6f02f1bd38a25c432cc4585f7f19e5 100644
--- a/config/routes/admin/communication.rb
+++ b/config/routes/admin/communication.rb
@@ -25,7 +25,9 @@ namespace :communication do
       end
     end
     resources :authors, controller: 'website/authors', only: [:index, :show]
-    resources :posts, controller: 'website/posts'
+    resources :posts, controller: 'website/posts' do
+      post :publish, on: :collection
+    end
     resources :curations,
               path: 'posts/curations',
               as: :post_curations,
@@ -44,7 +46,7 @@ namespace :communication do
     end
     get   'structure'     => 'website/structure#edit'
     patch 'structure'     => 'website/structure#update'
-    
+
   end
   resources :blocks, controller: 'blocks', except: :index do
     collection do
diff --git a/db/migrate/20220425150705_add_is_author_to_university_people.rb b/db/migrate/20220425150705_add_is_author_to_university_people.rb
new file mode 100644
index 0000000000000000000000000000000000000000..287c2cb05772e3445ca8518061f49dc0e35df04b
--- /dev/null
+++ b/db/migrate/20220425150705_add_is_author_to_university_people.rb
@@ -0,0 +1,5 @@
+class AddIsAuthorToUniversityPeople < ActiveRecord::Migration[6.1]
+  def change
+    add_column :university_people, :is_author, :boolean
+  end
+end
diff --git a/db/migrate/20220425152944_add_name_to_university_persons.rb b/db/migrate/20220425152944_add_name_to_university_persons.rb
new file mode 100644
index 0000000000000000000000000000000000000000..dbe49902d68fc647fa56e3e8243f834f6b5e7917
--- /dev/null
+++ b/db/migrate/20220425152944_add_name_to_university_persons.rb
@@ -0,0 +1,5 @@
+class AddNameToUniversityPersons < ActiveRecord::Migration[6.1]
+  def change
+    add_column :university_people, :name, :string
+  end
+end
diff --git a/db/migrate/20220427072259_create_join_table_university_people_education_academic_year.rb b/db/migrate/20220427072259_create_join_table_university_people_education_academic_year.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9e28fe6f4ad5b2a17ce45db7427dec8f3e7925d2
--- /dev/null
+++ b/db/migrate/20220427072259_create_join_table_university_people_education_academic_year.rb
@@ -0,0 +1,8 @@
+class CreateJoinTableUniversityPeopleEducationAcademicYear < ActiveRecord::Migration[6.1]
+  def change
+    create_join_table :university_people, :education_academic_years, column_options: {type: :uuid} do |t|
+      t.index [:"university_person_id", :"education_academic_year_id"], name: 'index_person_academic_year'
+      t.index [:"education_academic_year_id", :"university_person_id"], name: 'index_academic_year_person'
+    end
+  end
+end
diff --git a/db/migrate/20220427094234_create_join_table_university_people_education_program.rb b/db/migrate/20220427094234_create_join_table_university_people_education_program.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b5452450c595010665c4398fbf656ceed72456bb
--- /dev/null
+++ b/db/migrate/20220427094234_create_join_table_university_people_education_program.rb
@@ -0,0 +1,8 @@
+class CreateJoinTableUniversityPeopleEducationProgram < ActiveRecord::Migration[6.1]
+  def change
+    create_join_table :university_people, :education_programs, column_options: {type: :uuid} do |t|
+      t.index [:"university_person_id", :"education_program_id"], name: 'index_person_program'
+      t.index [:"education_program_id", :"university_person_id"], name: 'index_program_person'
+    end
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 0eae79db7e8a734c78fe9f1aa7e96c7a1b411065..1b22f72bbaac4ee2327c940af97a497eb44db2cf 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_04_21_093107) do
+ActiveRecord::Schema.define(version: 2022_04_27_094234) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "pgcrypto"
@@ -407,6 +407,13 @@ ActiveRecord::Schema.define(version: 2022_04_21_093107) do
     t.index ["university_id"], name: "index_education_academic_years_on_university_id"
   end
 
+  create_table "education_academic_years_university_people", id: false, force: :cascade do |t|
+    t.uuid "university_person_id", null: false
+    t.uuid "education_academic_year_id", null: false
+    t.index ["education_academic_year_id", "university_person_id"], name: "index_academic_year_person"
+    t.index ["university_person_id", "education_academic_year_id"], name: "index_person_academic_year"
+  end
+
   create_table "education_cohorts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "program_id", null: false
@@ -467,6 +474,13 @@ ActiveRecord::Schema.define(version: 2022_04_21_093107) do
     t.index ["education_school_id", "education_program_id"], name: "school_program"
   end
 
+  create_table "education_programs_university_people", id: false, force: :cascade do |t|
+    t.uuid "university_person_id", null: false
+    t.uuid "education_program_id", null: false
+    t.index ["education_program_id", "university_person_id"], name: "index_program_person"
+    t.index ["university_person_id", "education_program_id"], name: "index_person_program"
+  end
+
   create_table "education_programs_users", id: false, force: :cascade do |t|
     t.uuid "education_program_id", null: false
     t.uuid "user_id", null: false
@@ -688,6 +702,8 @@ ActiveRecord::Schema.define(version: 2022_04_21_093107) do
     t.string "linkedin"
     t.boolean "is_alumnus", default: false
     t.text "description_short"
+    t.string "name"
+    t.boolean "is_author"
     t.index ["university_id"], name: "index_university_people_on_university_id"
     t.index ["user_id"], name: "index_university_people_on_user_id"
   end
diff --git a/lib/tasks/app.rake b/lib/tasks/app.rake
index 616dfe0f4cbf34b41bc0261d9369ef8b0d4bf344..c83caba11e229f3bca805868771096e0cbde6865 100644
--- a/lib/tasks/app.rake
+++ b/lib/tasks/app.rake
@@ -8,17 +8,9 @@ namespace :app do
 
   desc 'Fix things'
   task fix: :environment do
-    Communication::Block.where(template: 'partners').find_each do |partner|
-      next if partner.data.nil?
-      data = partner.data
-      next unless data.has_key? 'elements'
-      elements = data['elements']
-      next if elements.none?
-      first = elements.first
-      next unless first.has_key? 'partners'
-      partner.title = first['title']
-      partner.data['elements'] = first['partners']
-      partner.save
+    University::Person.find_each do |person|
+      person.is_author = person.communication_website_posts.any?
+      person.save
     end
   end