diff --git a/Gemfile b/Gemfile
index 53b050864a6166ed6de4c08b45531420b8359996..de8267d4abdbfd4e7e0be905ace34010560f5b8f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -22,6 +22,7 @@ gem "country_select"
 gem "csl-styles", "~> 2.0"
 gem "curation"#, path: "../../arnaudlevy/curation"
 gem "delayed_job_active_record"
+gem "delayed_job_prevent_duplicate"#, path: "../delayed_job_prevent_duplicate"
 gem "delayed_job_web"
 gem "devise"
 gem "devise-i18n"
diff --git a/Gemfile.lock b/Gemfile.lock
index ec027c15b0cd75e4471a32408df703c6440ba8ad..811b94e607e94e976fb46b49e72c27aab6e8d593 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -100,10 +100,10 @@ GEM
       activerecord (>= 3.2, < 8.0)
       rake (>= 10.4, < 14.0)
     ast (2.4.2)
-    autoprefixer-rails (10.4.13.0)
+    autoprefixer-rails (10.4.15.0)
       execjs (~> 2)
     aws-eventstream (1.2.0)
-    aws-partitions (1.809.0)
+    aws-partitions (1.819.0)
     aws-sdk-core (3.181.0)
       aws-eventstream (~> 1, >= 1.0.2)
       aws-partitions (~> 1, >= 1.651.0)
@@ -112,7 +112,7 @@ GEM
     aws-sdk-kms (1.71.0)
       aws-sdk-core (~> 3, >= 3.177.0)
       aws-sigv4 (~> 1.1)
-    aws-sdk-s3 (1.133.0)
+    aws-sdk-s3 (1.134.0)
       aws-sdk-core (~> 3, >= 3.181.0)
       aws-sdk-kms (~> 1)
       aws-sigv4 (~> 1.6)
@@ -172,9 +172,9 @@ GEM
       rexml
     csl-styles (2.0.1)
       csl (~> 2.0)
-    curation (1.10)
+    curation (1.11)
       htmlentities
-      metainspector (~> 5.12)
+      metainspector
       nokogiri
     date (3.3.3)
     delayed_job (4.1.11)
@@ -182,6 +182,8 @@ GEM
     delayed_job_active_record (4.1.7)
       activerecord (>= 3.0, < 8.0)
       delayed_job (>= 3.0, < 5)
+    delayed_job_prevent_duplicate (0.1.1)
+      delayed_job (>= 3.0, < 5)
     delayed_job_web (1.4.4)
       activerecord (> 3.0.0)
       delayed_job (> 2.0.3)
@@ -253,8 +255,8 @@ GEM
     gitlab (4.19.0)
       httparty (~> 0.20)
       terminal-table (>= 1.5.1)
-    globalid (1.1.0)
-      activesupport (>= 5.0)
+    globalid (1.2.1)
+      activesupport (>= 6.1)
     hal_openscience (0.1.0)
       json
       net-http
@@ -335,7 +337,7 @@ GEM
     method_source (1.0.0)
     mini_magick (4.12.0)
     mini_mime (1.1.5)
-    minitest (5.19.0)
+    minitest (5.20.0)
     msgpack (1.7.2)
     multi_xml (0.6.0)
     multipart-post (2.3.0)
@@ -368,7 +370,7 @@ GEM
       rack (>= 1.2, < 4)
       snaky_hash (~> 2.0)
       version_gem (~> 1.1)
-    octokit (7.0.0)
+    octokit (7.1.0)
       faraday (>= 1, < 3)
       sawyer (~> 0.9)
     omniauth (2.1.1)
@@ -391,7 +393,7 @@ GEM
       racc
     pexels (0.5.0)
       requests (~> 1.0.2)
-    pg (1.5.3)
+    pg (1.5.4)
     popper_js (2.11.8)
     public_suffix (5.0.3)
     puma (6.3.1)
@@ -452,7 +454,7 @@ GEM
     roo (2.10.0)
       nokogiri (~> 1)
       rubyzip (>= 1.3.0, < 3.0.0)
-    rotp (6.2.2)
+    rotp (6.3.0)
     ruby-saml (1.15.0)
       nokogiri (>= 1.13.10)
       rexml
@@ -511,7 +513,7 @@ GEM
       hashie
       version_gem (~> 1.1, >= 1.1.1)
     spring (4.1.1)
-    sprockets (4.2.0)
+    sprockets (4.2.1)
       concurrent-ruby (~> 1.0)
       rack (>= 2.2.4, < 4)
     sprockets-rails (3.4.2)
@@ -548,7 +550,7 @@ GEM
     version_gem (1.1.3)
     warden (1.2.9)
       rack (>= 2.0.9)
-    web-console (4.2.0)
+    web-console (4.2.1)
       actionview (>= 6.0.0)
       activemodel (>= 6.0.0)
       bindex (>= 0.4.0)
@@ -557,7 +559,7 @@ GEM
       nokogiri (~> 1.6)
       rubyzip (>= 1.3.0)
       selenium-webdriver (~> 4.0, < 4.11)
-    webmock (3.18.1)
+    webmock (3.19.1)
       addressable (>= 2.8.0)
       crack (>= 0.3.2)
       hashdiff (>= 0.4.0, < 2.0.0)
@@ -599,6 +601,7 @@ DEPENDENCIES
   csl-styles (~> 2.0)
   curation
   delayed_job_active_record
+  delayed_job_prevent_duplicate
   delayed_job_web
   devise
   devise-i18n
diff --git a/app/controllers/admin/communication/websites/agenda/application_controller.rb b/app/controllers/admin/communication/websites/agenda/application_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4f8e4dafd9219155a2ce8161a6c1fb3299972590
--- /dev/null
+++ b/app/controllers/admin/communication/websites/agenda/application_controller.rb
@@ -0,0 +1,10 @@
+class Admin::Communication::Websites::Agenda::ApplicationController < Admin::Communication::Websites::ApplicationController
+
+  protected
+
+  def breadcrumb
+    super
+    add_breadcrumb Communication::Website::Agenda.model_name.human(count: 2)
+  end
+
+end
diff --git a/app/controllers/admin/communication/websites/agenda/events_controller.rb b/app/controllers/admin/communication/websites/agenda/events_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a78ff1a9104db908706f2a990e533add4627c9d0
--- /dev/null
+++ b/app/controllers/admin/communication/websites/agenda/events_controller.rb
@@ -0,0 +1,90 @@
+class Admin::Communication::Websites::Agenda::EventsController < Admin::Communication::Websites::Agenda::ApplicationController
+  load_and_authorize_resource class: Communication::Website::Agenda::Event, 
+                              through: :website
+
+  def index
+    @events = apply_scopes(@events).for_language(current_website_language).ordered.page params[:page]
+    breadcrumb
+  end
+
+  def publish
+    @event.published = true
+    @event.save_and_sync
+    redirect_back fallback_location: admin_communication_website_agenda_event_path(@event),
+                  notice: t('admin.communication.website.publish.notice')
+  end
+
+  def show
+    breadcrumb
+  end
+
+  def static
+    @about = @event
+    render layout: false
+  end
+
+  def new
+    breadcrumb
+  end
+
+  def edit
+    breadcrumb
+    add_breadcrumb t('edit')
+  end
+
+  def create
+    @event.website = @website
+    @event.add_photo_import params[:photo_import]
+    if @event.save_and_sync
+      redirect_to admin_communication_website_agenda_event_path(@event), 
+                  notice: t('admin.successfully_created_html', model: @event.to_s)
+    else
+      breadcrumb
+      render :new, status: :unprocessable_entity
+    end
+  end
+
+  def update
+    @event.add_photo_import params[:photo_import]
+    if @event.update_and_sync(event_params)
+      redirect_to admin_communication_website_agenda_event_path(@event), 
+                  notice: t('admin.successfully_updated_html', model: @event.to_s)
+    else
+      breadcrumb
+      add_breadcrumb t('edit')
+      render :edit, status: :unprocessable_entity
+    end
+  end
+
+  def duplicate
+    redirect_to [:admin, @event.duplicate],
+                notice: t('admin.successfully_duplicated_html', model: @event.to_s)
+  end
+
+  def destroy
+    @event.destroy
+    redirect_to admin_communication_website_agenda_events_url, 
+                notice: t('admin.successfully_destroyed_html', model: @event.to_s)
+  end
+  protected
+
+  def breadcrumb
+    super
+    add_breadcrumb  Communication::Website::Agenda::Event.model_name.human(count: 2),
+                    admin_communication_website_agenda_events_path
+    breadcrumb_for @event
+  end
+
+  def event_params
+    params.require(:communication_website_agenda_event)
+    .permit(
+      :title, :meta_description, :summary, :published, :slug,
+      :featured_image, :featured_image_delete, :featured_image_infos, :featured_image_alt, :featured_image_credit,
+      :from_day, :from_hour, :to_day, :to_hour
+    )
+    .merge(
+      university_id: current_university.id,
+      language_id: current_website_language.id
+    )
+  end
+end
\ No newline at end of file
diff --git a/app/controllers/admin/communication/websites/pages_controller.rb b/app/controllers/admin/communication/websites/pages_controller.rb
index 68efb95662ed6377febe4e5457ac10ef9d59fb9e..ec188547031ffc785b57ac48e146945991b8e978 100644
--- a/app/controllers/admin/communication/websites/pages_controller.rb
+++ b/app/controllers/admin/communication/websites/pages_controller.rb
@@ -47,6 +47,13 @@ class Admin::Communication::Websites::PagesController < Admin::Communication::We
     add_breadcrumb(@page, admin_communication_website_page_path(@page))
   end
 
+  def publish
+    @page.published = true
+    @page.save_and_sync
+    redirect_back fallback_location: admin_communication_website_page_path(@page),
+                  notice: t('admin.communication.website.publish.notice')
+  end
+
   def static
     @about = @page
     render layout: false
diff --git a/app/controllers/admin/communication/websites/posts_controller.rb b/app/controllers/admin/communication/websites/posts_controller.rb
index 150442c0a3270e6c33408c104d8029ff9280c72c..e16f48cec4ea647086c794a928ac865985138ce8 100644
--- a/app/controllers/admin/communication/websites/posts_controller.rb
+++ b/app/controllers/admin/communication/websites/posts_controller.rb
@@ -23,7 +23,7 @@ class Admin::Communication::Websites::PostsController < Admin::Communication::We
     breadcrumb
   end
 
-  def publish
+  def publish_batch
     ids = params[:ids] || []
     target_posts = @website.posts.where(id: ids)
     is_published = params[:published] == "true"
@@ -35,6 +35,13 @@ class Admin::Communication::Websites::PostsController < Admin::Communication::We
                   notice: t('communication.website.posts.successful_batch_update')
   end
 
+  def publish
+    @post.published = true
+    @post.save_and_sync
+    redirect_back fallback_location: admin_communication_website_post_path(@post),
+                  notice: t('admin.communication.website.publish.notice')
+  end
+
   def show
     @preview = true
     breadcrumb
diff --git a/app/controllers/admin/communication/websites_controller.rb b/app/controllers/admin/communication/websites_controller.rb
index fa3362e8f5b0e5aa775ce04fe5c1d52b776be0a5..9f311420edce99712300778865889c6f0d7886b4 100644
--- a/app/controllers/admin/communication/websites_controller.rb
+++ b/app/controllers/admin/communication/websites_controller.rb
@@ -38,6 +38,8 @@ class Admin::Communication::WebsitesController < Admin::Communication::Websites:
     @pages = @all_pages.recent
     @all_posts = @website.posts.accessible_by(current_ability).for_language(current_website_language)
     @posts = @all_posts.recent
+    @all_events = @website.events.accessible_by(current_ability).for_language(current_website_language)
+    @events = @all_events.recent
     breadcrumb
   end
 
@@ -86,6 +88,7 @@ class Admin::Communication::WebsitesController < Admin::Communication::Websites:
     attribute_names = [
       :name, :url, :repository, :about_type, :about_id, :in_production,
       :git_provider, :git_endpoint, :git_branch, :plausible_url, 
+      :feature_posts, :feature_agenda,
       :deployment_status_badge, :autoupdate_theme, language_ids: []
     ]
     attribute_names << :access_token unless params[:communication_website][:access_token].blank?
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 23d8b4bec9ac7c1e78b14394bf7747d11b99db30..6b668bc7371d0108d2459933469068b08fde5f06 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -13,6 +13,10 @@ class ApplicationController < ActionController::Base
 
   private
 
+  def current_ability
+    @current_ability ||= Ability.for(current_user)
+  end
+
   def ensure_university
     render_forbidden unless current_university
   end
diff --git a/app/helpers/admin/application_helper.rb b/app/helpers/admin/application_helper.rb
index 07e0aad316697e45d572dc8f3f26ac53c1595160..d914917ac4be6f5266fee49493b4a3967862f73f 100644
--- a/app/helpers/admin/application_helper.rb
+++ b/app/helpers/admin/application_helper.rb
@@ -55,6 +55,14 @@ module Admin::ApplicationHelper
                   aria-controls=\"preview\">#{ t 'preview.button'}</button>"
   end
 
+  def publish_link(object)
+    return if object.published
+    link_to t('admin.communication.website.publish.button'),
+            [:publish, :admin, object],
+            method: :post,
+            class: button_classes
+  end
+
   def static_link(path)
     return unless current_user.server_admin?
     raw "<a href=\"#{path}\" class=\"btn btn-light btn-xs\">#{t 'static' }</a>"
diff --git a/app/models/ability.rb b/app/models/ability.rb
index d7bd0f6e23e1ea24f5ab628ce8914d64f4752dcd..e98457ce2a9838b4c08ca234d3e33e732da40dbd 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -3,173 +3,17 @@
 class Ability
   include CanCan::Ability
 
+  def self.for(user)
+    "Ability::#{user.role.classify}".constantize.new user
+  end
+
   def initialize(user)
     @user = user ||= User.new # guest user (not logged in)
-    send @user.role.to_sym
   end
 
   protected
 
-  def visitor
-  end
-
-  def contributor
-    author
-    cannot :publish, Communication::Website::Post
-  end
-  
-  def author
-    managed_websites_ids = @user.websites_to_manage.pluck(:communication_website_id)
-    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'Communication::Website::Post', about_id: Communication::Website::Post.where(university_id: @user.university_id, author_id: @user.person&.id).pluck(:id)
-    can :create, Communication::Block
-    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'Communication::Website::Post', about_id: Communication::Website::Post.where(university_id: @user.university_id, author_id: @user.person&.id).pluck(:id)
-    can :create, Communication::Block::Heading
-    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, Communication::Block, university_id: @user.university_id, about_type: 'Education::Program', about_id: Education::Program.where(university_id: @user.university_id).pluck(:id)
-    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'University::Person', about_id: University::Person.where(university_id: @user.university_id, user_id: @user.id).pluck(:id)
-    can :create, Communication::Block
-    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'Education::Program', about_id: Education::Program.where(university_id: @user.university_id).pluck(:id)
-    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'University::Person', about_id: University::Person.where(university_id: @user.university_id, user_id: @user.id).pluck(:id)
-    can :create, Communication::Block::Heading
-    can [:read, :children], Education::Program, university_id: @user.university_id
-    can :manage, University::Person, user_id: @user.id
-    cannot :create, University::Person
-    can :manage, University::Person::Involvement, person_id: @user.person&.id
-    can :read, University::Person::Involvement, university_id: @user.university_id
-    can :read, University::Role, university_id: @user.university_id
-  end
-  
-  def program_manager
-    managed_programs_ids = @user.programs_to_manage.pluck(:education_program_id)
-    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'Communication::Website::Post', about_id: Communication::Website::Post.where(university_id: @user.university_id).pluck(:id)
-    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'University::Person', about_id: University::Person.where(university_id: @user.university_id).pluck(:id)
-    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'Education::Program', about_id: managed_programs_ids
-    can :create, Communication::Block
-    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'Communication::Website::Post', about_id: Communication::Website::Post.where(university_id: @user.university_id).pluck(:id)
-    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'Education::Program', about_id: managed_programs_ids
-    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'University::Person', about_id: University::Person.where(university_id: @user.university_id).pluck(:id)
-    can :create, Communication::Block::Heading
-    can :read, Communication::Website, university_id: @user.university_id
-    can :manage, Communication::Website::Post, university_id: @user.university_id
-    can :manage, Education::Program, id: managed_programs_ids
-    can [:read, :children], Education::Program, university_id: @user.university_id
-    cannot :create, Education::Program
-    can :manage, University::Person, university_id: @user.university_id
-    can :manage, University::Person::Involvement, target_type: "Education::Program", target_id: managed_programs_ids
-    can :manage, University::Role, target_type: "Education::Program", target_id: managed_programs_ids
-  end
-
-  def website_manager
-    managed_websites_ids = @user.websites_to_manage.pluck(:communication_website_id)
-    managed_pages_ids = Communication::Website::Page.where(communication_website_id: managed_websites_ids).pluck(:id)
-    managed_posts_ids = Communication::Website::Post.where(communication_website_id: managed_websites_ids).pluck(:id)
-    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'Communication::Website::Page', about_id: managed_pages_ids
-    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'Communication::Website::Post', about_id: managed_posts_ids
-    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'University::Organization', about_id: University::Organization.where(university_id: @user.university_id).pluck(:id)
-    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'University::Person', about_id: University::Person.where(university_id: @user.university_id).pluck(:id)
-    can :create, Communication::Block
-    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'Communication::Website::Page', about_id: managed_pages_ids
-    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'Communication::Website::Post', about_id: managed_posts_ids
-    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'University::Organization', about_id: University::Organization.where(university_id: @user.university_id).pluck(:id)
-    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'University::Person', about_id: University::Person.where(university_id: @user.university_id).pluck(:id)
-    can :create, Communication::Block::Heading
-    can [:read, :analytics], Communication::Website, university_id: @user.university_id, id: managed_websites_ids
-    can :manage, Communication::Website::Category, university_id: @user.university_id, communication_website_id: managed_websites_ids
-    can [:read, :update, :reorder], Communication::Website::Menu, university_id: @user.university_id, communication_website_id: managed_websites_ids
-    can :manage, Communication::Website::Menu::Item, university_id: @user.university_id, website_id: managed_websites_ids
-    can :create, Communication::Website::Menu::Item, university_id: @user.university_id
-    can :manage, Communication::Website::Page, university_id: @user.university_id, communication_website_id: managed_websites_ids
-    can :manage, Communication::Website::Post, university_id: @user.university_id, communication_website_id: managed_websites_ids
-    can :manage, University::Organization, university_id: @user.university_id
-    can :manage, University::Person, university_id: @user.university_id
-    can :manage, University::Person::Category, university_id: @user.university_id
-    can :manage, University::Person::Experience, university_id: @user.university_id
-    can :manage, University::Person::Involvement, university_id: @user.university_id
-  end
-
-  def admin
-    admin_university
-    admin_education
-    admin_research
-    admin_communication
-    admin_communication_extranet
-    admin_administration
-    can :manage, Import, university_id: @user.university_id
-  end
-
-  def admin_university
-    can :manage, University::Organization, university_id: @user.university_id
-    can :manage, University::Organization::Category, university_id: @user.university_id
-    can :manage, University::Person, university_id: @user.university_id
-    can :manage, University::Person::Category, university_id: @user.university_id
-    can :manage, University::Person::Experience, university_id: @user.university_id
-    can :manage, University::Person::Involvement, university_id: @user.university_id
-    can :manage, University::Role, university_id: @user.university_id
-    can :read, User, university_id: @user.university_id
-    can :manage, User, university_id: @user.university_id, role: @user.managed_roles
-  end
-
-  def admin_education
-    can :manage, Education::AcademicYear, university_id: @user.university_id
-    can :manage, Education::Cohort, university_id: @user.university_id
-    can :manage, Education::Diploma, university_id: @user.university_id
-    can :manage, Education::Program, university_id: @user.university_id
-    can :manage, Education::School, university_id: @user.university_id
-    can :manage, :all_programs # needed to prevent program_manager to access specific global screens
-  end
-
-  def admin_research
-    can :manage, Research::Hal::Author
-    can :manage, Research::Hal::Publication
-    can :manage, Research::Journal, university_id: @user.university_id
-    can :manage, Research::Journal::Paper, university_id: @user.university_id
-    can :manage, Research::Journal::Paper::Kind, university_id: @user.university_id
-    can :manage, Research::Journal::Volume, university_id: @user.university_id
-    can :manage, Research::Laboratory, university_id: @user.university_id
-    can :manage, Research::Laboratory::Axis, university_id: @user.university_id
-    can :manage, Research::Thesis, university_id: @user.university_id
-  end
-
-  def admin_communication
-    can :manage, Communication::Block, university_id: @user.university_id
-    can :create, Communication::Block
-    can :manage, Communication::Block::Heading, university_id: @user.university_id
-    can :create, Communication::Block::Heading
-    can :manage, Communication::Website, university_id: @user.university_id
-    # Est-ce bien raisonnable de laisser supprimer un site ?
-    # Le risque de faussse manip est grand.
-    cannot :destroy, Communication::Website, university_id: @user.university_id
-    can :manage, Communication::Website::Category, university_id: @user.university_id
-    can :manage, Communication::Website::Imported::Website, university_id: @user.university_id
-    can :manage, Communication::Website::Imported::Page, university_id: @user.university_id
-    can :manage, Communication::Website::Imported::Post, university_id: @user.university_id
-    can :manage, Communication::Website::Menu, university_id: @user.university_id
-    can :manage, Communication::Website::Menu::Item, university_id: @user.university_id
-    can :manage, Communication::Website::Page, university_id: @user.university_id
-    can :manage, Communication::Website::Post, university_id: @user.university_id
-  end
-  
-  def admin_communication_extranet
-    can [:read, :update], Communication::Extranet, university_id: @user.university_id
-    can :manage, Communication::Extranet::Connection, university_id: @user.university_id
-    can :manage, Communication::Extranet::Document, university_id: @user.university_id
-    can :manage, Communication::Extranet::Document::Category, university_id: @user.university_id
-    can :manage, Communication::Extranet::Document::Kind, university_id: @user.university_id
-    can :manage, Communication::Extranet::Post, university_id: @user.university_id
-    can :manage, Communication::Extranet::Post::Category, university_id: @user.university_id
-  end
-
-  def admin_administration
-    can :read, Administration::Qualiopi
-    can :read, Administration::Qualiopi::Criterion
-    can :read, Administration::Qualiopi::Indicator
-  end
-
-  def server_admin
-    can :manage, :all
+  def managed_websites_ids
+    @managed_websites_ids ||= @user.websites_to_manage.pluck(:communication_website_id)
   end
 end
diff --git a/app/models/ability/admin.rb b/app/models/ability/admin.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f6692c611cb18f7a4c4723f9316bc7bd865e1afa
--- /dev/null
+++ b/app/models/ability/admin.rb
@@ -0,0 +1,84 @@
+class Ability::Admin < Ability
+
+  def initialize(user)
+    super
+    admin_university
+    admin_education
+    admin_research
+    admin_communication
+    admin_communication_extranet
+    admin_administration
+    can :manage, Import, university_id: @user.university_id
+  end
+
+  protected
+
+  def admin_university
+    can :manage, University::Organization, university_id: @user.university_id
+    can :manage, University::Organization::Category, university_id: @user.university_id
+    can :manage, University::Person, university_id: @user.university_id
+    can :manage, University::Person::Category, university_id: @user.university_id
+    can :manage, University::Person::Experience, university_id: @user.university_id
+    can :manage, University::Person::Involvement, university_id: @user.university_id
+    can :manage, University::Role, university_id: @user.university_id
+    can :read, User, university_id: @user.university_id
+    can :manage, User, university_id: @user.university_id, role: @user.managed_roles
+  end
+
+  def admin_education
+    can :manage, Education::AcademicYear, university_id: @user.university_id
+    can :manage, Education::Cohort, university_id: @user.university_id
+    can :manage, Education::Diploma, university_id: @user.university_id
+    can :manage, Education::Program, university_id: @user.university_id
+    can :manage, Education::School, university_id: @user.university_id
+    can :manage, :all_programs # needed to prevent program_manager to access specific global screens
+  end
+
+  def admin_research
+    can :manage, Research::Hal::Author
+    can :manage, Research::Hal::Publication
+    can :manage, Research::Journal, university_id: @user.university_id
+    can :manage, Research::Journal::Paper, university_id: @user.university_id
+    can :manage, Research::Journal::Paper::Kind, university_id: @user.university_id
+    can :manage, Research::Journal::Volume, university_id: @user.university_id
+    can :manage, Research::Laboratory, university_id: @user.university_id
+    can :manage, Research::Laboratory::Axis, university_id: @user.university_id
+    can :manage, Research::Thesis, university_id: @user.university_id
+  end
+
+  def admin_communication
+    can :manage, Communication::Block, university_id: @user.university_id
+    can :create, Communication::Block
+    can :manage, Communication::Block::Heading, university_id: @user.university_id
+    can :create, Communication::Block::Heading
+    can :manage, Communication::Website, university_id: @user.university_id
+    # Est-ce bien raisonnable de laisser supprimer un site ?
+    # Le risque de faussse manip est grand.
+    cannot :destroy, Communication::Website, university_id: @user.university_id
+    can :manage, Communication::Website::Category, university_id: @user.university_id
+    can :manage, Communication::Website::Imported::Website, university_id: @user.university_id
+    can :manage, Communication::Website::Imported::Page, university_id: @user.university_id
+    can :manage, Communication::Website::Imported::Post, university_id: @user.university_id
+    can :manage, Communication::Website::Menu, university_id: @user.university_id
+    can :manage, Communication::Website::Menu::Item, university_id: @user.university_id
+    can :manage, Communication::Website::Page, university_id: @user.university_id
+    can :manage, Communication::Website::Post, university_id: @user.university_id
+    can :manage, Communication::Website::Agenda::Event, university_id: @user.university_id
+  end
+  
+  def admin_communication_extranet
+    can [:read, :update], Communication::Extranet, university_id: @user.university_id
+    can :manage, Communication::Extranet::Connection, university_id: @user.university_id
+    can :manage, Communication::Extranet::Document, university_id: @user.university_id
+    can :manage, Communication::Extranet::Document::Category, university_id: @user.university_id
+    can :manage, Communication::Extranet::Document::Kind, university_id: @user.university_id
+    can :manage, Communication::Extranet::Post, university_id: @user.university_id
+    can :manage, Communication::Extranet::Post::Category, university_id: @user.university_id
+  end
+
+  def admin_administration
+    can :read, Administration::Qualiopi
+    can :read, Administration::Qualiopi::Criterion
+    can :read, Administration::Qualiopi::Indicator
+  end
+end
\ No newline at end of file
diff --git a/app/models/ability/contributor.rb b/app/models/ability/contributor.rb
new file mode 100644
index 0000000000000000000000000000000000000000..26b690dc162858d7e61129361ee23954411cd126
--- /dev/null
+++ b/app/models/ability/contributor.rb
@@ -0,0 +1,15 @@
+class Ability::Contributor < Ability
+
+  def initialize(user)
+    super
+    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'Communication::Website::Post', about_id: Communication::Website::Post.where(university_id: @user.university_id, author_id: @user.person&.id).pluck(:id)
+    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'Communication::Website::Agenda::Event', about_id: Communication::Website::Agenda::Event.where(university_id: @user.university_id, author_id: @user.person&.id).pluck(:id)
+    can :create, Communication::Block
+    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'Communication::Website::Post', about_id: Communication::Website::Post.where(university_id: @user.university_id, author_id: @user.person&.id).pluck(:id)
+    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'Communication::Website::Agenda::Event', about_id: Communication::Website::Agenda::Event.where(university_id: @user.university_id, author_id: @user.person&.id).pluck(:id)
+    can :create, Communication::Block::Heading
+    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
+end
\ No newline at end of file
diff --git a/app/models/ability/program_manager.rb b/app/models/ability/program_manager.rb
new file mode 100644
index 0000000000000000000000000000000000000000..54ffe891f0b5d592f4f264b15edd9218aec96385
--- /dev/null
+++ b/app/models/ability/program_manager.rb
@@ -0,0 +1,31 @@
+class Ability::ProgramManager < Ability
+
+  def initialize(user)
+    super
+    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'Communication::Website::Post', about_id: Communication::Website::Post.where(university_id: @user.university_id).pluck(:id)
+    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'Communication::Website::Agenda::Event', about_id: Communication::Website::Agenda::Event.where(university_id: @user.university_id).pluck(:id)
+    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'University::Person', about_id: University::Person.where(university_id: @user.university_id).pluck(:id)
+    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'Education::Program', about_id: managed_programs_ids
+    can :create, Communication::Block
+    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'Communication::Website::Post', about_id: Communication::Website::Post.where(university_id: @user.university_id).pluck(:id)
+    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'Communication::Website::Agenda::Event', about_id: Communication::Website::Agenda::Event.where(university_id: @user.university_id).pluck(:id)
+    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'Education::Program', about_id: managed_programs_ids
+    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'University::Person', about_id: University::Person.where(university_id: @user.university_id).pluck(:id)
+    can :create, Communication::Block::Heading
+    can :read, Communication::Website, university_id: @user.university_id
+    can :manage, Communication::Website::Post, university_id: @user.university_id
+    can :manage, Communication::Website::Agenda::Event, university_id: @user.university_id
+    can :manage, Education::Program, id: managed_programs_ids
+    can [:read, :children], Education::Program, university_id: @user.university_id
+    cannot :create, Education::Program
+    can :manage, University::Person, university_id: @user.university_id
+    can :manage, University::Person::Involvement, target_type: "Education::Program", target_id: managed_programs_ids
+    can :manage, University::Role, target_type: "Education::Program", target_id: managed_programs_ids
+  end
+
+  protected
+
+  def managed_programs_ids
+    @managed_programs_ids ||= @user.programs_to_manage.pluck(:education_program_id)
+  end
+end
\ No newline at end of file
diff --git a/app/models/ability/server_admin.rb b/app/models/ability/server_admin.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ba1e7f835a3991fc3ea43f9720929e00c2704d47
--- /dev/null
+++ b/app/models/ability/server_admin.rb
@@ -0,0 +1,7 @@
+class Ability::ServerAdmin < Ability
+
+  def initialize(user)
+    super
+    can :manage, :all
+  end
+end
\ No newline at end of file
diff --git a/app/models/ability/teacher.rb b/app/models/ability/teacher.rb
new file mode 100644
index 0000000000000000000000000000000000000000..db0816822859e031fc4f8550ddea8381ed4e92f1
--- /dev/null
+++ b/app/models/ability/teacher.rb
@@ -0,0 +1,18 @@
+class Ability::Teacher < Ability
+
+  def initialize(user)
+    super
+    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'Education::Program', about_id: Education::Program.where(university_id: @user.university_id).pluck(:id)
+    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'University::Person', about_id: University::Person.where(university_id: @user.university_id, user_id: @user.id).pluck(:id)
+    can :create, Communication::Block
+    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'Education::Program', about_id: Education::Program.where(university_id: @user.university_id).pluck(:id)
+    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'University::Person', about_id: University::Person.where(university_id: @user.university_id, user_id: @user.id).pluck(:id)
+    can :create, Communication::Block::Heading
+    can [:read, :children], Education::Program, university_id: @user.university_id
+    can :manage, University::Person, user_id: @user.id
+    cannot :create, University::Person
+    can :manage, University::Person::Involvement, person_id: @user.person&.id
+    can :read, University::Person::Involvement, university_id: @user.university_id
+    can :read, University::Role, university_id: @user.university_id
+  end
+end
\ No newline at end of file
diff --git a/app/models/ability/visitor.rb b/app/models/ability/visitor.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0784040545b0ce67dd09f96d0d75f09488f8f5d7
--- /dev/null
+++ b/app/models/ability/visitor.rb
@@ -0,0 +1,2 @@
+class Ability::Visitor < Ability
+end
\ No newline at end of file
diff --git a/app/models/ability/website_manager.rb b/app/models/ability/website_manager.rb
new file mode 100644
index 0000000000000000000000000000000000000000..593b9fdca0988c9389b947d3d406da6c18a3119c
--- /dev/null
+++ b/app/models/ability/website_manager.rb
@@ -0,0 +1,45 @@
+class Ability::WebsiteManager < Ability
+
+  def initialize(user)
+    super
+    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'Communication::Website::Page', about_id: managed_pages_ids
+    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'Communication::Website::Post', about_id: managed_posts_ids
+    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'Communication::Website::Agenda::Event', about_id: managed_events_ids
+    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'University::Organization', about_id: University::Organization.where(university_id: @user.university_id).pluck(:id)
+    can :manage, Communication::Block, university_id: @user.university_id, about_type: 'University::Person', about_id: University::Person.where(university_id: @user.university_id).pluck(:id)
+    can :create, Communication::Block
+    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'Communication::Website::Page', about_id: managed_pages_ids
+    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'Communication::Website::Post', about_id: managed_posts_ids
+    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'Communication::Website::Agenda::Event', about_id: managed_events_ids
+    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'University::Organization', about_id: University::Organization.where(university_id: @user.university_id).pluck(:id)
+    can :manage, Communication::Block::Heading, university_id: @user.university_id, about_type: 'University::Person', about_id: University::Person.where(university_id: @user.university_id).pluck(:id)
+    can :create, Communication::Block::Heading
+    can [:read, :analytics], Communication::Website, university_id: @user.university_id, id: managed_websites_ids
+    can :manage, Communication::Website::Category, university_id: @user.university_id, communication_website_id: managed_websites_ids
+    can [:read, :update, :reorder], Communication::Website::Menu, university_id: @user.university_id, communication_website_id: managed_websites_ids
+    can :manage, Communication::Website::Menu::Item, university_id: @user.university_id, website_id: managed_websites_ids
+    can :create, Communication::Website::Menu::Item, university_id: @user.university_id
+    can :manage, Communication::Website::Page, university_id: @user.university_id, communication_website_id: managed_websites_ids
+    can :manage, Communication::Website::Post, university_id: @user.university_id, communication_website_id: managed_websites_ids
+    can :manage, University::Organization, university_id: @user.university_id
+    can :manage, University::Person, university_id: @user.university_id
+    can :manage, University::Person::Category, university_id: @user.university_id
+    can :manage, University::Person::Experience, university_id: @user.university_id
+    can :manage, University::Person::Involvement, university_id: @user.university_id
+  end
+
+  protected
+
+  def managed_pages_ids
+    @managed_pages_ids ||= Communication::Website::Page.where(communication_website_id: managed_websites_ids).pluck(:id)
+  end
+
+  def managed_posts_ids
+    @managed_posts_ids ||= Communication::Website::Post.where(communication_website_id: managed_websites_ids).pluck(:id)
+  end
+
+  def managed_events_ids
+    @managed_events_ids ||= Communication::Website::Agenda::Event.where(communication_website_id: managed_websites_ids).pluck(:id)
+  end
+
+end
\ No newline at end of file
diff --git a/app/models/communication/block.rb b/app/models/communication/block.rb
index 293d4d1388b3622b6fdf15a715aaf845a36fcf8a..bc8c2442744c3d38dd7c8141d1b264a6850eda7f 100644
--- a/app/models/communication/block.rb
+++ b/app/models/communication/block.rb
@@ -146,6 +146,10 @@ class Communication::Block < ApplicationRecord
     translation.save
   end
 
+  def full_text
+    template.full_text
+  end
+
   def to_s
     title.blank?  ? "#{Communication::Block.model_name.human} #{position}"
                   : "#{title}"
diff --git a/app/models/communication/block/component/base.rb b/app/models/communication/block/component/base.rb
index d6e26f46371c2a50736af186c3c3379256d12bf0..cf987455a0baefd501d0841f0e56950f353e6d4d 100644
--- a/app/models/communication/block/component/base.rb
+++ b/app/models/communication/block/component/base.rb
@@ -34,6 +34,10 @@ class Communication::Block::Component::Base
     # By default, does nothing. Specific cases are handled in their own definitions. (example: post)
   end
 
+  def full_text
+    ''
+  end
+
   def to_s
     self.class.to_s.demodulize
   end
diff --git a/app/models/communication/block/component/rich_text.rb b/app/models/communication/block/component/rich_text.rb
index 428b4bff02e81b87946183a2c1ef97f4c25add04..1f2e7f1afe5c2b4f91894b9d0dd1b0a802ecbaed 100644
--- a/app/models/communication/block/component/rich_text.rb
+++ b/app/models/communication/block/component/rich_text.rb
@@ -6,4 +6,8 @@ class Communication::Block::Component::RichText < Communication::Block::Componen
     @data = value
   end
 
+  def full_text
+    ActionController::Base.helpers.strip_tags data
+  end
+
 end
diff --git a/app/models/communication/block/component/string.rb b/app/models/communication/block/component/string.rb
index 2d4302f3a600742d583a8c77b18016c25680b929..6509381f276ccb452e6e56d5e03a845886911ab8 100644
--- a/app/models/communication/block/component/string.rb
+++ b/app/models/communication/block/component/string.rb
@@ -4,4 +4,8 @@ class Communication::Block::Component::String < Communication::Block::Component:
     @data = Osuny::Sanitizer.sanitize value, 'string'
   end
 
+  def full_text
+    data
+  end
+
 end
diff --git a/app/models/communication/block/component/text.rb b/app/models/communication/block/component/text.rb
index a5fdf576725700081e139019a11d568226790147..66db4f641734a098b436b30206d305c58e94d5a1 100644
--- a/app/models/communication/block/component/text.rb
+++ b/app/models/communication/block/component/text.rb
@@ -4,4 +4,8 @@ class Communication::Block::Component::Text < Communication::Block::Component::B
     @data = Osuny::Sanitizer.sanitize value, 'string'
   end
 
+  def full_text
+    data
+  end
+
 end
diff --git a/app/models/communication/block/heading.rb b/app/models/communication/block/heading.rb
index cbf44bb4f99c80d350c1f8b8ca74bbb853a9559e..770c78932f39ae8b87e8f21b892cb4ebaf59e542 100644
--- a/app/models/communication/block/heading.rb
+++ b/app/models/communication/block/heading.rb
@@ -75,6 +75,10 @@ class Communication::Block::Heading < ApplicationRecord
     end
   end
 
+  def full_text
+    to_s
+  end
+
   def to_s
     "#{title}"
   end
diff --git a/app/models/communication/block/template/base.rb b/app/models/communication/block/template/base.rb
index cd828e2b1e6a246f2816f3819a6a8cc188e304d6..b241d11b2582396184500e9e6d555370d5577f0d 100644
--- a/app/models/communication/block/template/base.rb
+++ b/app/models/communication/block/template/base.rb
@@ -126,6 +126,21 @@ class Communication::Block::Template::Base
     hash
   end
 
+  def full_text
+    unless @full_text
+      @full_text = ''
+      components.each do |component|
+        @full_text += "#{component.full_text}\r"
+      end
+      if has_element_class?
+        elements.each do |element|
+          @full_text += "#{element.full_text}\r"
+        end
+      end
+    end
+    @full_text
+  end
+
   def to_s
     self.class.to_s.demodulize
   end
diff --git a/app/models/communication/extranet.rb b/app/models/communication/extranet.rb
index 0dc31636314ab864924471003317609996665e7c..1a4615b3d6c6865f180dd318b43f9c1268235907 100644
--- a/app/models/communication/extranet.rb
+++ b/app/models/communication/extranet.rb
@@ -2,34 +2,35 @@
 #
 # Table name: communication_extranets
 #
-#  id                         :uuid             not null, primary key
-#  about_type                 :string           indexed => [about_id]
-#  color                      :string
-#  cookies_policy             :text
-#  css                        :text
-#  feature_alumni             :boolean          default(FALSE)
-#  feature_contacts           :boolean          default(FALSE)
-#  feature_jobs               :boolean          default(FALSE)
-#  feature_library            :boolean          default(FALSE)
-#  feature_posts              :boolean          default(FALSE)
-#  has_sso                    :boolean          default(FALSE)
-#  home_sentence              :text
-#  host                       :string
-#  name                       :string
-#  privacy_policy             :text
-#  registration_contact       :string
-#  sass                       :text
-#  sso_button_label           :string
-#  sso_cert                   :text
-#  sso_mapping                :jsonb
-#  sso_name_identifier_format :string
-#  sso_provider               :integer          default("saml")
-#  sso_target_url             :string
-#  terms                      :text
-#  created_at                 :datetime         not null
-#  updated_at                 :datetime         not null
-#  about_id                   :uuid             indexed => [about_type]
-#  university_id              :uuid             not null, indexed
+#  id                             :uuid             not null, primary key
+#  about_type                     :string           indexed => [about_id]
+#  allow_experiences_modification :boolean          default(TRUE)
+#  color                          :string
+#  cookies_policy                 :text
+#  css                            :text
+#  feature_alumni                 :boolean          default(FALSE)
+#  feature_contacts               :boolean          default(FALSE)
+#  feature_jobs                   :boolean          default(FALSE)
+#  feature_library                :boolean          default(FALSE)
+#  feature_posts                  :boolean          default(FALSE)
+#  has_sso                        :boolean          default(FALSE)
+#  home_sentence                  :text
+#  host                           :string
+#  name                           :string
+#  privacy_policy                 :text
+#  registration_contact           :string
+#  sass                           :text
+#  sso_button_label               :string
+#  sso_cert                       :text
+#  sso_mapping                    :jsonb
+#  sso_name_identifier_format     :string
+#  sso_provider                   :integer          default("saml")
+#  sso_target_url                 :string
+#  terms                          :text
+#  created_at                     :datetime         not null
+#  updated_at                     :datetime         not null
+#  about_id                       :uuid             indexed => [about_type]
+#  university_id                  :uuid             not null, indexed
 #
 # Indexes
 #
diff --git a/app/models/communication/website.rb b/app/models/communication/website.rb
index 1ae31f1e242e3566f10171d1a90d3eb87ed8a4e6..39f1f18be53a58493ec7bb80ef16c64c01ba3ce3 100644
--- a/app/models/communication/website.rb
+++ b/app/models/communication/website.rb
@@ -7,6 +7,8 @@
 #  access_token            :string
 #  autoupdate_theme        :boolean          default(TRUE)
 #  deployment_status_badge :text
+#  feature_agenda          :boolean          default(FALSE)
+#  feature_posts           :boolean          default(TRUE)
 #  git_branch              :string
 #  git_endpoint            :string
 #  git_provider            :integer          default("github")
@@ -90,6 +92,7 @@ class Communication::Website < ApplicationRecord
     configs +
     pages.where(language_id: language_ids) +
     posts.where(language_id: language_ids) +
+    events.where(language_id: language_ids) +
     categories.where(language_id: language_ids) +
     menus.where(language_id: language_ids) +
     [about]
diff --git a/app/models/communication/website/agenda.rb b/app/models/communication/website/agenda.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a432277cf265540243e09b1f70f416fcc6b8221f
--- /dev/null
+++ b/app/models/communication/website/agenda.rb
@@ -0,0 +1,8 @@
+module Communication::Website::Agenda
+  extend ActiveModel::Naming
+  extend ActiveModel::Translation
+
+  def self.table_name_prefix
+    "communication_website_agenda_"
+  end
+end
diff --git a/app/models/communication/website/agenda/event.rb b/app/models/communication/website/agenda/event.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2688faaf78ebfcfd45532bf14be2bfae4e1e8a1c
--- /dev/null
+++ b/app/models/communication/website/agenda/event.rb
@@ -0,0 +1,77 @@
+# == Schema Information
+#
+# Table name: communication_website_agenda_events
+#
+#  id                       :uuid             not null, primary key
+#  featured_image_alt       :text
+#  featured_image_credit    :text
+#  from_day                 :date
+#  from_hour                :time
+#  meta_description         :text
+#  published                :boolean          default(FALSE)
+#  slug                     :text
+#  summary                  :text
+#  title                    :string
+#  to_day                   :date
+#  to_hour                  :time
+#  created_at               :datetime         not null
+#  updated_at               :datetime         not null
+#  communication_website_id :uuid             not null, indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
+#  parent_id                :uuid             indexed
+#  university_id            :uuid             not null, indexed
+#
+# Indexes
+#
+#  index_agenda_events_on_communication_website_id             (communication_website_id)
+#  index_communication_website_agenda_events_on_language_id    (language_id)
+#  index_communication_website_agenda_events_on_original_id    (original_id)
+#  index_communication_website_agenda_events_on_parent_id      (parent_id)
+#  index_communication_website_agenda_events_on_university_id  (university_id)
+#
+# Foreign Keys
+#
+#  fk_rails_00ca585c35  (university_id => universities.id)
+#  fk_rails_5fa53206f2  (communication_website_id => communication_websites.id)
+#  fk_rails_67834f0062  (language_id => languages.id)
+#  fk_rails_917095d5ca  (parent_id => communication_website_agenda_events.id)
+#  fk_rails_fc3fea77c2  (original_id => communication_website_agenda_events.id)
+#
+class Communication::Website::Agenda::Event < ApplicationRecord
+  include AsDirectObject
+  include Sanitizable
+  include WithAccessibility
+  include WithBlobs
+  include WithBlocks
+  include WithDuplication
+  include WithFeaturedImage
+  include WithMenuItemTarget
+  include WithPermalink
+  include WithSlug
+  include WithTranslations
+  include WithTree
+  include WithUniversity
+
+  belongs_to  :parent,
+              class_name: 'Communication::Website::Agenda::Event',
+              optional: true
+
+  scope :ordered, -> { order(from_day: :desc, from_hour: :desc) }
+  scope :recent, -> { ordered.limit(5) }
+
+  validates_presence_of :from_day
+
+  def git_path(website)
+    return unless website.id == communication_website_id && published
+    "#{git_path_content_prefix(website)}events/#{from_day.year}/#{from_day.strftime "%Y-%m-%d"}-#{slug}.html"
+  end
+
+  def template_static
+    "admin/communication/websites/agenda/events/static"
+  end
+
+  def to_s
+    "#{title}"
+  end
+end
diff --git a/app/models/communication/website/configs/base.rb b/app/models/communication/website/configs/base.rb
index e653b3ff703f5029f06fa06632ef8db842f5797e..bdaf514d64d6b238b81ee1fdb5051c85c8a7cbe5 100644
--- a/app/models/communication/website/configs/base.rb
+++ b/app/models/communication/website/configs/base.rb
@@ -7,6 +7,8 @@
 #  access_token            :string
 #  autoupdate_theme        :boolean          default(TRUE)
 #  deployment_status_badge :text
+#  feature_agenda          :boolean          default(FALSE)
+#  feature_posts           :boolean          default(TRUE)
 #  git_branch              :string
 #  git_endpoint            :string
 #  git_provider            :integer          default("github")
diff --git a/app/models/communication/website/configs/default_languages.rb b/app/models/communication/website/configs/default_languages.rb
index ee08fe55fb5c1a4dc86a03f33a00e2f4685926d1..2ba91739b05cdeaf4a968c653a7fb33cefa15df3 100644
--- a/app/models/communication/website/configs/default_languages.rb
+++ b/app/models/communication/website/configs/default_languages.rb
@@ -7,6 +7,8 @@
 #  access_token            :string
 #  autoupdate_theme        :boolean          default(TRUE)
 #  deployment_status_badge :text
+#  feature_agenda          :boolean          default(FALSE)
+#  feature_posts           :boolean          default(TRUE)
 #  git_branch              :string
 #  git_endpoint            :string
 #  git_provider            :integer          default("github")
diff --git a/app/models/communication/website/configs/default_permalinks.rb b/app/models/communication/website/configs/default_permalinks.rb
index eaaaafc83e24d692acebbd79ccf2a32de22e6d13..1ec87b7aa512caabdcfcd9607006fff6ccc86d45 100644
--- a/app/models/communication/website/configs/default_permalinks.rb
+++ b/app/models/communication/website/configs/default_permalinks.rb
@@ -7,6 +7,8 @@
 #  access_token            :string
 #  autoupdate_theme        :boolean          default(TRUE)
 #  deployment_status_badge :text
+#  feature_agenda          :boolean          default(FALSE)
+#  feature_posts           :boolean          default(TRUE)
 #  git_branch              :string
 #  git_endpoint            :string
 #  git_provider            :integer          default("github")
diff --git a/app/models/communication/website/configs/development_config.rb b/app/models/communication/website/configs/development_config.rb
index 0bfeb9540e76bb5e5eca67bf0dea65881d701e1c..2dc3d1862247d8cccd6b0b9765727a610931c945 100644
--- a/app/models/communication/website/configs/development_config.rb
+++ b/app/models/communication/website/configs/development_config.rb
@@ -7,6 +7,8 @@
 #  access_token            :string
 #  autoupdate_theme        :boolean          default(TRUE)
 #  deployment_status_badge :text
+#  feature_agenda          :boolean          default(FALSE)
+#  feature_posts           :boolean          default(TRUE)
 #  git_branch              :string
 #  git_endpoint            :string
 #  git_provider            :integer          default("github")
diff --git a/app/models/communication/website/configs/production_config.rb b/app/models/communication/website/configs/production_config.rb
index 4e69ac120788f5a1b64428fe89cb76354401b878..0011aba777778f2c306a7e97345caad0ce47e18d 100644
--- a/app/models/communication/website/configs/production_config.rb
+++ b/app/models/communication/website/configs/production_config.rb
@@ -7,6 +7,8 @@
 #  access_token            :string
 #  autoupdate_theme        :boolean          default(TRUE)
 #  deployment_status_badge :text
+#  feature_agenda          :boolean          default(FALSE)
+#  feature_posts           :boolean          default(TRUE)
 #  git_branch              :string
 #  git_endpoint            :string
 #  git_provider            :integer          default("github")
diff --git a/app/models/communication/website/page/communication_agenda.rb b/app/models/communication/website/page/communication_agenda.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d30481e867167faae0dde427640bf24ab53d226c
--- /dev/null
+++ b/app/models/communication/website/page/communication_agenda.rb
@@ -0,0 +1,70 @@
+# == Schema Information
+#
+# Table name: communication_website_pages
+#
+#  id                       :uuid             not null, primary key
+#  bodyclass                :string
+#  breadcrumb_title         :string
+#  featured_image_alt       :string
+#  featured_image_credit    :text
+#  full_width               :boolean          default(FALSE)
+#  header_text              :text
+#  kind                     :integer
+#  meta_description         :text
+#  position                 :integer          default(0), not null
+#  published                :boolean          default(FALSE)
+#  slug                     :string
+#  summary                  :text
+#  text                     :text
+#  title                    :string
+#  type                     :string
+#  created_at               :datetime         not null
+#  updated_at               :datetime         not null
+#  communication_website_id :uuid             not null, indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
+#  parent_id                :uuid             indexed
+#  university_id            :uuid             not null, indexed
+#
+# Indexes
+#
+#  index_communication_website_pages_on_communication_website_id  (communication_website_id)
+#  index_communication_website_pages_on_language_id               (language_id)
+#  index_communication_website_pages_on_original_id               (original_id)
+#  index_communication_website_pages_on_parent_id                 (parent_id)
+#  index_communication_website_pages_on_university_id             (university_id)
+#
+# Foreign Keys
+#
+#  fk_rails_1a42003f06  (parent_id => communication_website_pages.id)
+#  fk_rails_280107c62b  (communication_website_id => communication_websites.id)
+#  fk_rails_304f57360f  (original_id => communication_website_pages.id)
+#  fk_rails_d208d15a73  (university_id => universities.id)
+#
+class Communication::Website::Page::CommunicationAgenda < Communication::Website::Page
+
+  def editable_width?
+    false
+  end
+
+  def full_width_by_default?
+    true
+  end
+
+  def is_necessary_for_website?
+    website.feature_agenda
+  end
+
+  def dependencies
+    super +
+    [website.config_default_languages] +
+    website.events
+  end
+
+  protected
+
+  def current_git_path
+    @current_git_path ||= "#{git_path_prefix}events/_index.html"
+  end
+
+end
diff --git a/app/models/communication/website/page/with_type.rb b/app/models/communication/website/page/with_type.rb
index d264af369064759f049f3e1015e4e7562f564416..0257d94c95643e8fecefeea0ed07a0a130072d63 100644
--- a/app/models/communication/website/page/with_type.rb
+++ b/app/models/communication/website/page/with_type.rb
@@ -9,6 +9,7 @@ module Communication::Website::Page::WithType
       Communication::Website::Page::Home,
       # Global objects
       Communication::Website::Page::CommunicationPost,
+      Communication::Website::Page::CommunicationAgenda,
       Communication::Website::Page::Person,
       Communication::Website::Page::Organization,
       # Education
diff --git a/app/models/communication/website/permalink.rb b/app/models/communication/website/permalink.rb
index 90d2edaa2f6adab3f7e7b4535ab31ecbbdf507b0..f0ad95d3bb04b34534913a03848e509c009197fa 100644
--- a/app/models/communication/website/permalink.rb
+++ b/app/models/communication/website/permalink.rb
@@ -28,6 +28,7 @@ class Communication::Website::Permalink < ApplicationRecord
     "Communication::Website::Category" => Communication::Website::Permalink::Category,
     "Communication::Website::Page" => Communication::Website::Permalink::Page,
     "Communication::Website::Post" => Communication::Website::Permalink::Post,
+    "Communication::Website::Agenda::Event" => Communication::Website::Permalink::Agenda::Event,
     "Education::Diploma" => Communication::Website::Permalink::Diploma,
     "Education::Program" => Communication::Website::Permalink::Program,
     "Research::Journal::Paper" => Communication::Website::Permalink::Paper,
diff --git a/app/models/communication/website/permalink/agenda/event.rb b/app/models/communication/website/permalink/agenda/event.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bff708063f82e1d2c6a6a91eaefe18b50d88862e
--- /dev/null
+++ b/app/models/communication/website/permalink/agenda/event.rb
@@ -0,0 +1,55 @@
+# == Schema Information
+#
+# Table name: communication_website_permalinks
+#
+#  id            :uuid             not null, primary key
+#  about_type    :string           not null, indexed => [about_id]
+#  is_current    :boolean          default(TRUE)
+#  path          :string
+#  created_at    :datetime         not null
+#  updated_at    :datetime         not null
+#  about_id      :uuid             not null, indexed => [about_type]
+#  university_id :uuid             not null, indexed
+#  website_id    :uuid             not null, indexed
+#
+# Indexes
+#
+#  index_communication_website_permalinks_on_about          (about_type,about_id)
+#  index_communication_website_permalinks_on_university_id  (university_id)
+#  index_communication_website_permalinks_on_website_id     (website_id)
+#
+# Foreign Keys
+#
+#  fk_rails_e9646cce64  (university_id => universities.id)
+#  fk_rails_f389ba7d45  (website_id => communication_websites.id)
+#
+class Communication::Website::Permalink::Agenda::Event < Communication::Website::Permalink
+  def self.required_in_config?(website)
+    website.feature_agenda
+  end
+
+  def self.static_config_key
+    :events
+  end
+
+  # /agenda/2022-10-21-un-article/
+  def self.pattern_in_website(website, language)
+    "/#{website.special_page(Communication::Website::Page::CommunicationAgenda, language: language).slug_with_ancestors}/:year-:month-:day-:slug/"
+  end
+
+  protected
+
+  def published?
+    website.id == about.communication_website_id && about.published
+  end
+
+  def substitutions
+    {
+      year: about.from_day.strftime("%Y"),
+      month: about.from_day.strftime("%m"),
+      day: about.from_day.strftime("%d"),
+      slug: about.slug
+    }
+  end
+
+end
diff --git a/app/models/communication/website/with_associated_objects.rb b/app/models/communication/website/with_associated_objects.rb
index 1e293ed3f3f9c8297d705019ab98a080a7e7d735..413ef344fd2338864e674b1522c52df39883bb07 100644
--- a/app/models/communication/website/with_associated_objects.rb
+++ b/app/models/communication/website/with_associated_objects.rb
@@ -26,7 +26,11 @@ module Communication::Website::WithAssociatedObjects
                 class_name: "Communication::Block",
                 foreign_key: :communication_website_id
     alias       :blocks :communication_blocks
-
+    
+    has_many    :agenda_events,
+                class_name: "Communication::Website::Agenda::Event",
+                foreign_key: :communication_website_id
+    alias       :events :agenda_events
   end
 
   def blocks_from_education
diff --git a/app/models/concerns/with_blocks.rb b/app/models/concerns/with_blocks.rb
index 13778ae89d02bd85ed6ac86e03bb6fd9db45457c..4788d0812029145a4cd3fa83005c32a184dbfc39 100644
--- a/app/models/concerns/with_blocks.rb
+++ b/app/models/concerns/with_blocks.rb
@@ -14,11 +14,16 @@ module WithBlocks
       end
       headings.ordered.each do |heading|
         @contents << heading
+        @contents.concat heading.blocks
       end
     end
     @contents
   end
 
+  def contents_full_text
+    @contents_full_text ||= contents.collect(&:full_text).join("\r")
+  end
+
   # Basic rule is: TOC if 2 titles or more
   def show_toc?
     headings.many?
diff --git a/app/services/icon.rb b/app/services/icon.rb
index c24cf201851f3381d40ff46f753792d2d0363464..456d263355591173b21bb43ee4315be6acaa60e2 100644
--- a/app/services/icon.rb
+++ b/app/services/icon.rb
@@ -9,6 +9,7 @@ class Icon
   COMMUNICATION_WEBSITE_PAGE = 'fas fa-file'
   COMMUNICATION_WEBSITE_PAGES = 'fas fa-sitemap'
   COMMUNICATION_WEBSITE_MENUS = 'fas fa-bars'
+  COMMUNICATION_WEBSITE_AGENDA = 'fas fa-calendar'
   COMMUNICATION_WEBSITE_ANALYTICS = 'fas fa-chart-pie'
   COMMUNICATION_WEBSITE_PREVIEW_MOBILE = 'fas fa-mobile-alt'
   COMMUNICATION_WEBSITE_PREVIEW_TABLET = 'fas fa-tablet-alt'
diff --git a/app/views/admin/communication/blocks/content/_static.html.erb b/app/views/admin/communication/blocks/content/_static.html.erb
index c6c34bcdedb28349592e0cf4d8b23e2ba99dc44e..8a05c6e6b4aa0e64472f2cd033632622f30d8b2e 100644
--- a/app/views/admin/communication/blocks/content/_static.html.erb
+++ b/app/views/admin/communication/blocks/content/_static.html.erb
@@ -1,6 +1,11 @@
 <%
 @university = about.university
+I18n.locale = about.language.iso_code if about.respond_to?(:language)
 %>
+contents_reading_time:
+  seconds: <%= about.contents_full_text.reading_time %>
+  text: >-
+    <%= distance_of_time_in_words(0, about.contents_full_text.reading_time) %>
 contents:
 <% about.blocks.without_heading.published.ordered.each do |block| %>
 <%= render 'admin/communication/blocks/static', block: block %>
diff --git a/app/views/admin/communication/blocks/content/_static.json.jbuilder b/app/views/admin/communication/blocks/content/_static.json.jbuilder
index 33c49fb9a21cb801cab0169a81a56dfccf411939..a965c87f5f3d6b28a062efe9eba13e050116d4a1 100644
--- a/app/views/admin/communication/blocks/content/_static.json.jbuilder
+++ b/app/views/admin/communication/blocks/content/_static.json.jbuilder
@@ -2,5 +2,6 @@ json.contents about.contents do |block_or_heading|
   if block_or_heading.is_a? Communication::Block
     json.partial! 'admin/communication/blocks/static', block: block_or_heading
   else
+    # TODO
   end
 end
\ No newline at end of file
diff --git a/app/views/admin/communication/websites/_form.html.erb b/app/views/admin/communication/websites/_form.html.erb
index b7f8864b504e8cc91faecbcbed87060e4ca2a2b2..1c20c4e24cfd29d839e194fa3e4d20d7b41c1cac 100644
--- a/app/views/admin/communication/websites/_form.html.erb
+++ b/app/views/admin/communication/websites/_form.html.erb
@@ -7,10 +7,16 @@
       <%= osuny_panel t('metadata') do %>
         <%= f.input :name %>
         <%= f.input :url %>
+        <%= f.input :in_production %>
         <%= render 'admin/communication/abouts', f: f, i18n_key: 'activerecord.attributes.communication/website.about_' %>
+      <% end %>
+      <%= osuny_panel Communication::Website.human_attribute_name('features') do %>
+        <%= f.input :feature_posts %>
+        <%= f.input :feature_agenda %>
+      <% end %>
+      <%= osuny_panel Language.model_name.human(count: 2) do %>
         <%= f.association :languages, as: :check_boxes, required: true, wrapper_html: { class: "js-languages" }, label_method: lambda { |l| language_name(l.iso_code) } %>
         <%= f.association :default_language, include_blank: t('simple_form.include_blanks.defaults.language'), input_html: (@website.persisted? ? { disabled: true } : { class: "js-default-language" }) %>
-        <%= f.input :in_production %>
       <% end %>
     </div>
     <div class="col-xl-8">
diff --git a/app/views/admin/communication/websites/_sidebar.html.erb b/app/views/admin/communication/websites/_sidebar.html.erb
index a9128724f477312efb9f3446668bb841ea6e2e0a..548a4029517cb1018dea115f757211969a73e9e4 100644
--- a/app/views/admin/communication/websites/_sidebar.html.erb
+++ b/app/views/admin/communication/websites/_sidebar.html.erb
@@ -2,32 +2,41 @@
   <div class="col-lg-3">
     <ul class="list-unstyled" role="tablist">
       <%
-      navigation = [
-        {
-          title: Communication::Website.model_name.human,
-          path: admin_communication_website_path(id: @website, website_id: nil),
-          icon: Icon::COMMUNICATION_WEBSITE_HOME,
-          ability: can?(:read, @website)
-        },
-        {
-          title: Communication::Website::Post.model_name.human(count: 2),
-          path: admin_communication_website_posts_path(website_id: @website),
-          icon: Icon::COMMUNICATION_WEBSITE_POST,
-          ability: can?(:read, Communication::Website::Post)
-        },
-        {
-          title: t('admin.communication.website.pages.structure'),
-          path: admin_communication_website_pages_path(website_id: @website),
-          icon: Icon::COMMUNICATION_WEBSITE_PAGES,
-          ability: can?(:read, Communication::Website::Page)
-        },
-        {
-          title: Communication::Website::Menu.model_name.human(count: 2),
-          path: admin_communication_website_menus_path(website_id: @website),
-          icon: Icon::COMMUNICATION_WEBSITE_MENUS,
-          ability: can?(:read, Communication::Website::Category)
-        }
-      ]
+      navigation = []
+      navigation << {
+        title: Communication::Website.model_name.human,
+        path: admin_communication_website_path(id: @website, website_id: nil),
+        icon: Icon::COMMUNICATION_WEBSITE_HOME,
+        ability: can?(:read, @website)
+      }
+      
+      navigation << {
+        title: Communication::Website::Post.model_name.human(count: 2),
+        path: admin_communication_website_posts_path(website_id: @website),
+        icon: Icon::COMMUNICATION_WEBSITE_POST,
+        ability: can?(:read, Communication::Website::Post)
+      } if @website.feature_posts
+
+      navigation << {
+        title: Communication::Website::Agenda.model_name.human(count: 2),
+        path: admin_communication_website_agenda_events_path(website_id: @website),
+        icon: Icon::COMMUNICATION_WEBSITE_AGENDA,
+        ability: can?(:read, Communication::Website::Agenda::Event)
+      } if @website.feature_agenda
+
+      navigation << {
+        title: t('admin.communication.website.pages.structure'),
+        path: admin_communication_website_pages_path(website_id: @website),
+        icon: Icon::COMMUNICATION_WEBSITE_PAGES,
+        ability: can?(:read, Communication::Website::Page)
+      }
+
+      navigation << {
+        title: Communication::Website::Menu.model_name.human(count: 2),
+        path: admin_communication_website_menus_path(website_id: @website),
+        icon: Icon::COMMUNICATION_WEBSITE_MENUS,
+        ability: can?(:read, Communication::Website::Category)
+      }
 
       navigation << {
         title: t('communication.website.analytics'),
diff --git a/app/views/admin/communication/websites/agenda/events/_dates.html.erb b/app/views/admin/communication/websites/agenda/events/_dates.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..870e21eb40e032eafeab9b7a85979a0a9a48814c
--- /dev/null
+++ b/app/views/admin/communication/websites/agenda/events/_dates.html.erb
@@ -0,0 +1,9 @@
+<i class="fas fa-play-circle"></i>
+<%= l(event.from_day) %>
+<%= l(event.from_hour, format: :time_only) if event.from_hour %>
+<% if event.to_day %>
+  <br>
+  <i class="fas fa-stop-circle"></i>
+  <%= l(event.to_day) %>
+  <%= l(event.to_hour, format: :time_only) if event.to_hour %>
+<% end %>
\ No newline at end of file
diff --git a/app/views/admin/communication/websites/agenda/events/_form.html.erb b/app/views/admin/communication/websites/agenda/events/_form.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..576ba9c86683bd85324da3d3050d4f6146daa576
--- /dev/null
+++ b/app/views/admin/communication/websites/agenda/events/_form.html.erb
@@ -0,0 +1,47 @@
+<%= simple_form_for [:admin, event] do |f| %>
+  <%= f.error_notification %>
+  <%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>
+
+  <div class="row">
+    <div class="col-md-8">
+      <%= osuny_panel t('content') do %>
+        <%= f.input :title %>
+        <%= render 'admin/application/summary/form', f: f, about: event %>
+      <% end %>
+      <%= osuny_panel Communication::Website::Agenda::Event.human_attribute_name('dates') do %>
+      <div class="row pure__row--small">
+        <div class="col-md-6">
+          <%= f.input :from_day, html5: true %>
+        </div>
+        <div class="col-md-6">
+          <%= f.input :from_hour, html5: true %>
+        </div>
+      </div>
+      <div class="row pure__row--small">
+        <div class="col-md-6">
+          <%= f.input :to_day, html5: true %>
+        </div>
+        <div class="col-md-6">
+          <%= f.input :to_hour, html5: true %>
+        </div>
+      </div>
+      <% end %>
+      <%= render 'admin/application/meta_description/form', f: f, about: event %>
+    </div>
+    <div class="col-md-4">
+      <%= osuny_panel t('metadata') do %>
+        <%= f.input :published if can? :publish, event %>
+        <%= f.input :slug,
+                    as: :string,
+                    input_html: event.persisted? ? {} : {
+                      class: 'js-slug-input',
+                      data: { source: '#communication_website_agenda_event_title' }
+                    } %>
+      <% end %>
+      <%= render 'admin/application/featured_image/edit', about: event, f: f %>
+    </div>
+  </div>
+  <% content_for :action_bar_right do %>
+    <%= submit f %>
+  <% end %>
+<% end %>
diff --git a/app/views/admin/communication/websites/agenda/events/_list.html.erb b/app/views/admin/communication/websites/agenda/events/_list.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..3198eeb2cb4b9489565aae69d1cbf968defa39b8
--- /dev/null
+++ b/app/views/admin/communication/websites/agenda/events/_list.html.erb
@@ -0,0 +1,29 @@
+<% if events.none? %>
+  <p><%= t('admin.communication.website.agenda.events.none') %></p>
+<% else %>
+  <div class="table-responsive">
+    <table class="<%= table_classes %>">
+      <thead>
+        <tr>
+          <th class="ps-0" style="min-width: 250px"><%= Communication::Website::Agenda::Event.human_attribute_name('title') %></th>
+          <th><%= Communication::Website::Agenda::Event.human_attribute_name('dates') %></th>
+          <th><%= Communication::Website::Agenda::Event.human_attribute_name('featured_image') %></th>
+        </tr>
+      </thead>
+      <tbody>
+        <% events.each do |event| %>
+          <tr class="<% 'draft' unless event.published? %>">
+            <td class="ps-0">
+              <%= link_to event,
+                          admin_communication_website_agenda_event_path(website_id: event.website.id, id: event.id),
+                          class: "#{'draft' unless event.published?}" %>
+            </td>
+            <td class="small"><%= render 'admin/communication/websites/agenda/events/dates', event: event %></td>
+            <td><%= image_tag event.featured_image.representation(resize: '200x'),
+                              width: 100 if event.featured_image.attached? && event.featured_image.representable? %></td>
+          </tr>
+        <% end %>
+      </tbody>
+    </table>
+  </div>
+<% end %>
\ No newline at end of file
diff --git a/app/views/admin/communication/websites/agenda/events/edit.html.erb b/app/views/admin/communication/websites/agenda/events/edit.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..5f1d676443a2384e4ecc22873b77ffce2ab3234f
--- /dev/null
+++ b/app/views/admin/communication/websites/agenda/events/edit.html.erb
@@ -0,0 +1,5 @@
+<% content_for :title, @event %>
+
+<%= render 'admin/communication/websites/sidebar' do %>
+  <%= render 'form', event: @event %>
+<% end %>
diff --git a/app/views/admin/communication/websites/agenda/events/index.html.erb b/app/views/admin/communication/websites/agenda/events/index.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..92fd9962628ccbe23741f26bf10e925457d52372
--- /dev/null
+++ b/app/views/admin/communication/websites/agenda/events/index.html.erb
@@ -0,0 +1,18 @@
+<% content_for :title, Communication::Website::Agenda::Event.model_name.human(count: 2) %>
+
+
+<% content_for :title_right do %>
+  <%= @events.total_count %>
+  <%= Communication::Website::Agenda::Event.model_name.human(count: @events.total_count).downcase %>
+<% end %>
+
+<%= render 'admin/communication/websites/sidebar' do %>
+  <% 
+  title = Communication::Website::Agenda::Event.model_name.human(count: 2)
+  action = create_link Communication::Website::Agenda::Event 
+  %>
+  <%= osuny_panel title, action: action do %>
+    <%= render 'admin/communication/websites/agenda/events/list', events: @events %>
+    <%= paginate @events, theme: 'bootstrap-5' %>
+  <% end %>
+<% end %>
diff --git a/app/views/admin/communication/websites/agenda/events/new.html.erb b/app/views/admin/communication/websites/agenda/events/new.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..28707ba18ba62d00f813a3c8e15bbbe2e19bfde6
--- /dev/null
+++ b/app/views/admin/communication/websites/agenda/events/new.html.erb
@@ -0,0 +1,5 @@
+<% content_for :title, Communication::Website::Agenda::Event.model_name.human %>
+
+<%= render 'admin/communication/websites/sidebar' do %>
+  <%= render 'form', event: @event %>
+<% end %>
diff --git a/app/views/admin/communication/websites/agenda/events/show.html.erb b/app/views/admin/communication/websites/agenda/events/show.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..3bd2fded9ad281eaf2eb25d37abff5c6a187a704
--- /dev/null
+++ b/app/views/admin/communication/websites/agenda/events/show.html.erb
@@ -0,0 +1,44 @@
+<% content_for :title, @event %>
+
+<%= render 'admin/communication/websites/sidebar' do %>
+  <div class="row">
+    <div class="col-lg-7">
+      <%= osuny_panel Communication::Website::Agenda::Event.human_attribute_name(:title), small: true do %>
+        <p class="lead"><%= @event.title %></p>
+        <p><%= render 'admin/communication/websites/agenda/events/dates', event: @event %></p>
+      <% end %>
+    </div>
+    <div class="offset-lg-1 col-lg-4">
+      <%= render 'admin/application/featured_image/show', about: @event, small: true %>
+    </div>
+  </div>
+  <hr class="mb-5">
+  <%= render 'admin/application/a11y/widget', about: @event, horizontal: true %>
+  <hr class="mb-5">
+  <div class="row">
+    <div class="col-lg-4">
+      <div class="mb-4">
+        <%= osuny_label t('metadata') %><br>
+        <% # TODO i18n après Violenn %>
+        <%= @event.published ? 'Publié' : 'Brouillon' %>
+        <%= render 'admin/application/i18n/inline', about: @event %>
+      </div>
+    </div>
+    <div class="col-lg-8">
+      <%= render 'admin/application/summary/show', about: @event, small: true %>
+      <%= render 'admin/application/meta_description/show', about: @event %>
+    </div>
+  </div>
+  <%= render 'admin/communication/blocks/content/editor', about: @event %>
+<% end %>
+
+<% content_for :action_bar_left do %>
+  <%= destroy_link @event %>
+  <%= duplicate_link @event %>
+  <%= static_link static_admin_communication_website_agenda_event_path(@event) %>
+<% end %>
+
+<% content_for :action_bar_right do %>
+  <%= edit_link @event %>
+  <%= publish_link @event %>
+<% end %>
diff --git a/app/views/admin/communication/websites/agenda/events/static.html.erb b/app/views/admin/communication/websites/agenda/events/static.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..40753a91c737a76d2726690e5cfeb3d2de170909
--- /dev/null
+++ b/app/views/admin/communication/websites/agenda/events/static.html.erb
@@ -0,0 +1,21 @@
+---
+title: "<%= @about.title %>"
+dates:
+  from:
+    day: <%= @about.from_day %>
+<% if @about.from_hour %>
+    hour: <%= @about.from_hour.strftime "%H:%M" %>
+<% end %>
+  to:
+    day: <%= @about.to_day %>
+<% if @about.to_hour %>
+    hour: <%= @about.to_hour.strftime "%H:%M" %>
+<% end %>
+<%= render 'admin/application/static/permalink' %>
+<%= render 'admin/application/static/design', full_width: false, toc_offcanvas: false %>
+<%= render 'admin/application/i18n/static' %>
+<%= render 'admin/application/featured_image/static' %>
+<%= render 'admin/application/meta_description/static' %>
+<%= render 'admin/application/summary/static' %>
+<%= render 'admin/communication/blocks/content/static', about: @about %>
+---
diff --git a/app/views/admin/communication/websites/pages/_treebranch.html.erb b/app/views/admin/communication/websites/pages/_treebranch.html.erb
index aa1c88af2ddf460165cd5fb2b87a07317844abc6..3aac16cddb3a5dd8fb537830fb7bd021440c2d90 100644
--- a/app/views/admin/communication/websites/pages/_treebranch.html.erb
+++ b/app/views/admin/communication/websites/pages/_treebranch.html.erb
@@ -5,7 +5,9 @@
                   class: 'js-treeview-openzone d-inline-block p-2 ps-0', style: 'width: 22px', remote: true do %>
         <%= render 'admin/application/tree/folder' %>
       <% end %>
-      <span class="leaf-title"><%= page %></span>
+      <span class="leaf-title">
+        <%= link_to page, admin_communication_website_page_path(page) %>
+      </span>
       <%= render 'admin/application/tree/sort' unless page.is_home? %>
       <%= link_to children_admin_communication_website_page_path(website_id: page.website.id, id: page.id),
                   class: 'js-treeview-openzone small ps-2', remote: true do %>
@@ -14,12 +16,9 @@
       <% end %>
       <div class="ms-auto align-items-center" role="group">
         <% if page.is_special_page? %>
-          <span class="me-3 show-on-hover"><%= t("communication.website.pages.defaults.#{page.type_key}.title") %></span>
+          <span class="show-on-hover"><%= t("communication.website.pages.defaults.#{page.type_key}.title") %></span>
         <% end %>
       </div>
-      <div class="btn-group">
-        <%= link_to t('admin.communication.website.pages.show'), admin_communication_website_page_path(page), class: 'action' %>
-      </div>
     </div>
     <ul class="list-unstyled treeview__children js-treeview-children <%= 'js-treeview-sortable-container' if can?(:reorder, page) %> ms-4" data-id="<%= page.id %>">
       <li class="treeview__empty">
diff --git a/app/views/admin/communication/websites/pages/show.html.erb b/app/views/admin/communication/websites/pages/show.html.erb
index e476827977f011c177074ba54edcc284755f728a..9c7b4121a4fffa19c521bf677959b6034665df10 100644
--- a/app/views/admin/communication/websites/pages/show.html.erb
+++ b/app/views/admin/communication/websites/pages/show.html.erb
@@ -41,5 +41,10 @@
               target: :_blank,
               class: 'btn btn-light btn-xs' if @page.published %>
   <%= preview_link %>
-  <%= link_to t('edit'), edit_admin_communication_website_page_path(@page), class: button_classes %>
+  <%= link_to t('edit'), 
+              edit_admin_communication_website_page_path(@page), 
+              class: button_classes 
+              # This is not edit_link @page because of the polymorphism of the special pages, which would create a wrong path 
+              %>
+  <%= publish_link @page %>
 <% end %>
diff --git a/app/views/admin/communication/websites/pages/tree.html.erb b/app/views/admin/communication/websites/pages/tree.html.erb
index f0561991e6cad894d89df80ffbaf40bc07e98acd..29dbbb16ff7b1e7b22cefd49880b5a399bbab108 100644
--- a/app/views/admin/communication/websites/pages/tree.html.erb
+++ b/app/views/admin/communication/websites/pages/tree.html.erb
@@ -30,13 +30,10 @@
               <i class="close_btn--with_children <%= Icon::FOLDER_OPENED_FULL %>"></i>
             </span>
           </div>
-          <%= @homepage %>
+          <%= link_to @homepage, admin_communication_website_page_path(@homepage) %>
           <div class="ms-auto align-items-center" role="group">
             <span class="me-3 show-on-hover"><%= t("communication.website.pages.defaults.home.title") %></span>
           </div>
-          <div class="btn-group">
-            <%= link_to t('admin.communication.website.pages.show'), admin_communication_website_page_path(@homepage), class: 'action' %>
-          </div>
         </div>
         <ul class="list-unstyled ms-4 treeview__children js-treeview <%= 'treeview--sortable js-treeview-sortable js-treeview-sortable-container' if can?(:reorder, @homepage) %>"
             data-id="<%= @homepage.id %>"
diff --git a/app/views/admin/communication/websites/posts/_list.html.erb b/app/views/admin/communication/websites/posts/_list.html.erb
index 46665f5ba46dd6381957b3e6cb05bd4a3fdcd89b..9ca463830182bb7c3430b9fc0e17ee06fd1c5978 100644
--- a/app/views/admin/communication/websites/posts/_list.html.erb
+++ b/app/views/admin/communication/websites/posts/_list.html.erb
@@ -19,8 +19,8 @@ selectable ||= false
               <%= check_box_tag nil, nil, false, data: { batch_selectable_role: "select-all" } if selectable %>
             </th>
           <% end %>
-          <th class="ps-0" width="60%"><%= Communication::Website::Post.human_attribute_name('title') %></th>
-          <th class="ps-3"><%= Communication::Website::Post.human_attribute_name('meta') %></th>
+          <th class="ps-0" style="min-width: 250px"><%= Communication::Website::Post.human_attribute_name('title') %></th>
+          <th class="ps-3" style="min-width: 200px"><%= Communication::Website::Post.human_attribute_name('meta') %></th>
           <th><%= Communication::Website::Post.human_attribute_name('featured_image') %></th>
         </tr>
       </thead>
diff --git a/app/views/admin/communication/websites/posts/index.html.erb b/app/views/admin/communication/websites/posts/index.html.erb
index c6c64458d91a39a72a97e5b8d1ea798d241b7ca8..1c30eb953a2d71951d03782dde79bbe61786ce09 100644
--- a/app/views/admin/communication/websites/posts/index.html.erb
+++ b/app/views/admin/communication/websites/posts/index.html.erb
@@ -14,7 +14,7 @@
   %>
   <%= osuny_panel Communication::Website::Post.model_name.human(count: 2), action: action do %>
     <div data-batch-selectable class="mb-5">
-      <%= form_tag publish_admin_communication_website_posts_path do %>
+      <%= form_tag publish_batch_admin_communication_website_posts_path do %>
         <%= render 'filters', current_path: admin_communication_website_posts_path, filters: @filters %>
         <%= render 'admin/communication/websites/posts/list', posts: @posts, selectable: true %>
         <%= paginate @posts, theme: 'bootstrap-5' %>
diff --git a/app/views/admin/communication/websites/posts/show.html.erb b/app/views/admin/communication/websites/posts/show.html.erb
index 9ba6dc7ade7e5c248a1d285838bc3ed6dbc4dbbe..ce732111f40f4ff2a2d0f2e684ae00c4ba504f72 100644
--- a/app/views/admin/communication/websites/posts/show.html.erb
+++ b/app/views/admin/communication/websites/posts/show.html.erb
@@ -39,4 +39,5 @@
               class: 'btn btn-light btn-xs' if @post.url %>
   <%= preview_link %>
   <%= edit_link @post %>
+  <%= publish_link @post %>
 <% end %>
diff --git a/app/views/admin/communication/websites/show.html.erb b/app/views/admin/communication/websites/show.html.erb
index 18bb57db469db1c10bf88f9111582942818e6c27..a6687f6833d72ce77134a9c54e65f5fb3bc4760c 100644
--- a/app/views/admin/communication/websites/show.html.erb
+++ b/app/views/admin/communication/websites/show.html.erb
@@ -14,8 +14,9 @@
 <% end %>
 
 <%= render 'admin/communication/websites/sidebar' do %>
-  <%= render 'admin/communication/websites/show/posts' if can? :read, Communication::Website::Post %>
-  <%= render 'admin/communication/websites/show/pages' if can? :read, Communication::Website::Page %>
+  <%= render 'admin/communication/websites/show/posts' if @website.feature_posts && can?(:read, Communication::Website::Post) %>
+  <%= render 'admin/communication/websites/show/events' if @website.feature_agenda && can?(:read, Communication::Website::Agenda::Event) %>
+  <%= render 'admin/communication/websites/show/pages' if can?(:read, Communication::Website::Page) %>
   <%= image_tag @website.deployment_status_badge, alt: '' if @website.deployment_status_badge.present? %>
 <% end %>
 
diff --git a/app/views/admin/communication/websites/show/_events.html.erb b/app/views/admin/communication/websites/show/_events.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..371000ee46db8048c2299fd7c38411d1734ce677
--- /dev/null
+++ b/app/views/admin/communication/websites/show/_events.html.erb
@@ -0,0 +1,17 @@
+<%
+action = ''
+action += link_to t('create'),
+                  new_admin_communication_website_agenda_event_path(website_id: @website),
+                  class: button_classes if can?(:create, Communication::Website::Agenda::Event)
+subtitle = ''
+if @all_events.any? 
+  subtitle = link_to  t('communication.website.see_all', number: @all_events.size), 
+                      admin_communication_website_agenda_events_path(website_id: @website)
+end
+%>
+<%= osuny_panel t('communication.website.last_events'),
+                subtitle: subtitle,
+                action: action do %>
+  <%= render 'admin/communication/websites/agenda/events/list',
+              events: @events %>
+<% end %>
diff --git a/app/views/admin/layouts/themes/pure/_panel.html.erb b/app/views/admin/layouts/themes/pure/_panel.html.erb
index 84c6dc65f86f5893e5bcf7cd5527331ee20c2e49..46f1b88349807e8770184d9d584262d28c010dc3 100644
--- a/app/views/admin/layouts/themes/pure/_panel.html.erb
+++ b/app/views/admin/layouts/themes/pure/_panel.html.erb
@@ -1,15 +1,15 @@
 <section class="pure__section flex-fill position-relative <%= classes %>">
   <%= image_tag image, class: 'img-fluid mb-3', loading: :lazy if image %>
-  <div class="d-flex">
+  <div class="d-lg-flex me-4 <%= 'mb-3' if small %>">
     <% if title %>
       <% if small %>
         <%= osuny_label title %>
       <% else %>
-        <h2 class="mb-3"><%= title %></h2>
+        <h2 class="me-3"><%= title %></h2>
       <% end %>
     <% end %>
     <% if action %>
-      <div class="ms-4"><%= raw action %></div>
+      <div class="mb-2"><%= raw action %></div>
     <% end %>
   </div>
   <% if subtitle %>
diff --git a/config/initializers/string.rb b/config/initializers/string.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bfa4450fc4bf1a126479c2e837c37a75aeba1287
--- /dev/null
+++ b/config/initializers/string.rb
@@ -0,0 +1,15 @@
+# encoding: utf-8
+String.class_eval do
+
+  # Time in seconds
+  # 
+  def reading_time
+    # https://www.sciencedirect.com/science/article/abs/pii/S0749596X19300786
+    words_per_minute = 238
+    (60.0 * words / words_per_minute).round
+  end
+
+  def words
+    self.scan(/(\w|-)+/).size
+  end
+end
\ No newline at end of file
diff --git a/config/locales/communication/en.yml b/config/locales/communication/en.yml
index 7695c76d012982b79ba1aeb485f4a8f9f0261f60..f67035ebb8399780bebf6e5376bbe5a4e1340257 100644
--- a/config/locales/communication/en.yml
+++ b/config/locales/communication/en.yml
@@ -31,6 +31,9 @@ en:
       communication/website:
         one: Website
         other: Websites
+      communication/website/agenda/event:
+        one: Event
+        other: Events
       communication/website/category:
         one: Category
         other: Categories
@@ -135,6 +138,9 @@ en:
         created_at: Creation
         default_language: Default language
         deployment_status_badge: Deployment status badge
+        features: Features
+        feature_posts: Posts
+        feature_agenda: Agenda
         git_branch: Git branch
         git_endpoint: Git endpoint
         git_provider: Git provider
@@ -144,6 +150,14 @@ en:
         plausible_url: Plausible dashboard URL
         repository: Repository
         url: URL
+      communication/website/agenda/event:
+        dates: Dates
+        featured_image: Featured image
+        from_day: From day
+        from_hour: From hour
+        title: Title
+        to_day: To day
+        to_hour: To hour
       communication/website/category:
         children: Children categories
         featured_image: Featured image
@@ -708,6 +722,9 @@ en:
             tab: Organize structure
             description: In “Organize structure” mode, you do not see the blocks, only the titles. When you move a title, all the blocks follow. This is the ideal mode for organising long documents.
       website:
+        agenda:
+          events:
+            none: No event yet
         pages:
           as_list: See as a list
           as_tree: Organize structure
@@ -718,6 +735,9 @@ en:
           published_status: Publication status
         post:
           pinned_status: Pinned status
+        publish: 
+          button: Publish
+          notice: Publication in progress, it should take a few minutes to be online.
   communication:
     authors:
       one: Author
@@ -760,6 +780,7 @@ en:
         refresh: Refresh import
         show: Show import
         pending: Import in progress
+      last_events: Last events
       last_pages: Last pages
       last_posts: Last posts
       menus:
@@ -782,6 +803,9 @@ en:
           communication_post:
             slug: posts
             title: Posts
+          communication_agenda:
+            slug: agenda
+            title: Agenda
           education_diploma:
             slug: diplomas
             title: Diplomas
diff --git a/config/locales/communication/fr.yml b/config/locales/communication/fr.yml
index aa22bd93608d2eda2009034bcb58de65aec084cc..903ab9577a6098a241ec2e7cb9de1fbe44fd2d1e 100644
--- a/config/locales/communication/fr.yml
+++ b/config/locales/communication/fr.yml
@@ -31,6 +31,9 @@ fr:
       communication/website:
         one: Site Web
         other: Sites Web
+      communication/website/agenda/event:
+        one: Événement
+        other: Événements
       communication/website/category:
         one: Catégorie
         other: Catégories
@@ -135,6 +138,9 @@ fr:
         created_at: Création
         default_language: Langue par défaut
         deployment_status_badge: Badge de statut du déploiement
+        features: Fonctionnalités
+        feature_posts: Actualités
+        feature_agenda: Agenda
         git_branch: Branche
         git_endpoint: Point d'accès Git
         git_provider: Fournisseur Git
@@ -144,6 +150,14 @@ fr:
         plausible_url: Tableau de bord Plausible
         repository: Référentiel
         url: URL
+      communication/website/agenda/event:
+        dates: Dates
+        featured_image: Image à la une
+        from_day: Jour de début
+        from_hour: Heure de début
+        title: Titre
+        to_day: Jour de fin
+        to_hour: Heure de fin
       communication/website/category:
         children: Catégories enfants
         featured_image: Image à la une
@@ -705,6 +719,9 @@ fr:
             tab: Organiser le plan
             description: Dans le mode “Organiser le plan”, vous ne voyez pas les blocs mais seulement les titres. Lorsque vous déplacez un titre, tous les blocs suivent. C'est le mode idéal pour ranger les documents longs.
       website:
+        agenda:
+          events:
+            none: Pas encore d'événements
         pages:
           as_list: Voir en liste
           as_tree: Organiser l'arborescence
@@ -715,6 +732,9 @@ fr:
           published_status: État de publication
         post:
           pinned_status: Mise en avant
+        publish: 
+          button: Publier
+          notice: Publication en cours, cela devrait prendre quelques minutes pour arriver en ligne.
   communication:
     authors:
       one: Auteur·rice
@@ -757,6 +777,7 @@ fr:
         refresh: Relancer l'import
         show: Voir l'import
         pending: Import en cours
+      last_events: Derniers événements
       last_pages: Dernières pages
       last_posts: Dernières actualités
       menus:
@@ -779,6 +800,9 @@ fr:
           communication_post:
             slug: actualites
             title: Actualités
+          communication_agenda:
+            slug: agenda
+            title: Agenda
           education_diploma:
             slug: diplomes
             title: "Diplômes"
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 2d621b80980ace10fe8a61d26a8eb2b7350d1dc1..71db7a90ce4ef0d7eaf049a1366df95f35737386 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -353,6 +353,7 @@ en:
       date_only: "%m/%d/%Y"
       date_with_explicit_month: "%B %d, %Y"
       date_with_hour: "%B %d, %Y %H:%M"
+      time_only: "%H:%M"
   today: Today
   true: Yes
   user:
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 7a7578d75e435b9a1e9a4e6fb33b412efb12887c..0f854167f66d5e1f29c889ce7d0161047387ad43 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -353,6 +353,7 @@ fr:
       date_only: "%d/%m/%Y"
       date_with_explicit_month: "%d %B %Y"
       date_with_hour: "%d %B %Y %H:%M"
+      time_only: "%H:%M"
   today: Aujourd'hui
   true: Oui
   user:
diff --git a/config/routes/admin/communication.rb b/config/routes/admin/communication.rb
index ce05293a096bf83634e0a433db3583aeeae6a4a3..63b172ca1f1d6673e2918c2e706c8eaa56c37497 100644
--- a/config/routes/admin/communication.rb
+++ b/config/routes/admin/communication.rb
@@ -24,8 +24,9 @@ namespace :communication do
         get :children
         get :static
         get :preview
-        get "translate" => "websites/pages#translate", as: :translate
+        get 'translate' => "websites/pages#translate", as: :translate
         post :duplicate
+        post :publish
         post :connect
         post :disconnect
         post 'generate-from-template' => 'websites/pages#generate_from_template', as: :generate
@@ -44,12 +45,22 @@ namespace :communication do
     resources :posts, controller: 'websites/posts', path: '/:lang/posts' do
       collection do
         resources :curations, as: :post_curations, controller: 'websites/posts/curations', only: [:new, :create]
-        post :publish
+        post :publish_batch
       end
       member do
         get :static
         get :preview
         post :duplicate
+        post :publish
+      end
+    end
+    namespace :agenda do
+      resources :events, controller: '/admin/communication/websites/agenda/events', path: '/:lang/events' do
+        member do
+          get :static
+          post :duplicate
+          post :publish
+        end
       end
     end
     resources :menus, controller: 'websites/menus', path: '/:lang/menus' do
diff --git a/cron.json b/cron.json
index 5003dcc2da2e14129d8b41d0bcde800bdfe4c23d..b4df0f56cab3ce7f32d203e567b592b28677cce2 100644
--- a/cron.json
+++ b/cron.json
@@ -1,10 +1,12 @@
 {
   "jobs": [
     {
-      "command": "0 1 * * * rails auto:update_publications_from_hal"
+      "command": "0 1 * * * rails auto:update_publications_from_hal",
+      "size": "L"
     },
     {
-      "command": "0 3 * * * rails auto:clean_and_rebuild_websites"
+      "command": "0 3 * * * rails auto:clean_and_rebuild_websites",
+      "size": "XL"
     }
   ]
 }
\ No newline at end of file
diff --git a/db/migrate/20230905111330_create_communication_website_agenda_events.rb b/db/migrate/20230905111330_create_communication_website_agenda_events.rb
new file mode 100644
index 0000000000000000000000000000000000000000..be10bfea3bf53908db4e2dd15b0e9c26398b9832
--- /dev/null
+++ b/db/migrate/20230905111330_create_communication_website_agenda_events.rb
@@ -0,0 +1,23 @@
+class CreateCommunicationWebsiteAgendaEvents < ActiveRecord::Migration[7.0]
+  def change
+    create_table :communication_website_agenda_events, id: :uuid do |t|
+      t.string :title
+      t.text :summary
+      t.references :university, null: false, foreign_key: true, type: :uuid
+      t.references :communication_website, null: false, foreign_key: true, type: :uuid, index: { name: 'index_agenda_events_on_communication_website_id' }
+      t.references :language, null: false, foreign_key: true, type: :uuid
+      t.references :original, null: false, foreign_key: {to_table: :communication_website_agenda_events}, type: :uuid
+      t.boolean :published, default: false
+      t.date :from_day
+      t.time :from_hour
+      t.date :to_day
+      t.time :to_hour
+      t.text :featured_image_alt
+      t.text :featured_image_credit
+      t.text :meta_description
+      t.references :parent, null: false, foreign_key: {to_table: :communication_website_agenda_events}, type: :uuid
+
+      t.timestamps
+    end
+  end
+end
diff --git a/db/migrate/20230905112141_add_features_to_communication_websites.rb b/db/migrate/20230905112141_add_features_to_communication_websites.rb
new file mode 100644
index 0000000000000000000000000000000000000000..05c602c2379a2b984fd94ffc736ad9bc9bfca405
--- /dev/null
+++ b/db/migrate/20230905112141_add_features_to_communication_websites.rb
@@ -0,0 +1,6 @@
+class AddFeaturesToCommunicationWebsites < ActiveRecord::Migration[7.0]
+  def change
+    add_column :communication_websites, :feature_posts, :boolean, default: true
+    add_column :communication_websites, :feature_agenda, :boolean, default: false
+  end
+end
diff --git a/db/migrate/20230905124209_authorize_null_original_for_communication_website_agenda_events.rb b/db/migrate/20230905124209_authorize_null_original_for_communication_website_agenda_events.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5bf25aa9ab318581853c790634c06a93bab1d8a3
--- /dev/null
+++ b/db/migrate/20230905124209_authorize_null_original_for_communication_website_agenda_events.rb
@@ -0,0 +1,6 @@
+class AuthorizeNullOriginalForCommunicationWebsiteAgendaEvents < ActiveRecord::Migration[7.0]
+  def change
+    change_column_null :communication_website_agenda_events, :original_id, true
+    change_column_null :communication_website_agenda_events, :parent_id, true
+  end
+end
diff --git a/db/migrate/20230905141601_add_slug_to_communication_websites_agenda_events.rb b/db/migrate/20230905141601_add_slug_to_communication_websites_agenda_events.rb
new file mode 100644
index 0000000000000000000000000000000000000000..25182888e3a7a1eb63217e5f08c3b4082a1cf28f
--- /dev/null
+++ b/db/migrate/20230905141601_add_slug_to_communication_websites_agenda_events.rb
@@ -0,0 +1,5 @@
+class AddSlugToCommunicationWebsitesAgendaEvents < ActiveRecord::Migration[7.0]
+  def change
+    add_column :communication_website_agenda_events, :slug, :text
+  end
+end
diff --git a/db/migrate/20230907152751_add_signature_to_delayed_job.rb b/db/migrate/20230907152751_add_signature_to_delayed_job.rb
new file mode 100644
index 0000000000000000000000000000000000000000..84df911780393324943d8204e38c04e0f3b945e7
--- /dev/null
+++ b/db/migrate/20230907152751_add_signature_to_delayed_job.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+class AddSignatureToDelayedJob < ActiveRecord::Migration[7.0]
+  def change
+    add_column :delayed_jobs, :signature, :string
+    add_column :delayed_jobs, :args, :text
+  end
+end
\ No newline at end of file
diff --git a/db/schema.rb b/db/schema.rb
index 6d4fbf26c7507e557e4fc44932c892c6316abd67..fc7099dc703e6f60746ec55e45e848458e1b1594 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,13 +10,13 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
+ActiveRecord::Schema[7.0].define(version: 2023_09_07_152751) do
   # These are extensions that must be enabled in order to support this database
   enable_extension "pgcrypto"
   enable_extension "plpgsql"
   enable_extension "unaccent"
 
-  create_table "action_text_rich_texts", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "action_text_rich_texts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.string "name", null: false
     t.text "body"
     t.string "record_type", null: false
@@ -26,7 +26,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["record_type", "record_id", "name"], name: "index_action_text_rich_texts_uniqueness", unique: true
   end
 
-  create_table "active_storage_attachments", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "active_storage_attachments", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.string "name", null: false
     t.string "record_type", null: false
     t.uuid "record_id", null: false
@@ -36,7 +36,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
   end
 
-  create_table "active_storage_blobs", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "active_storage_blobs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.string "key", null: false
     t.string "filename", null: false
     t.string "content_type"
@@ -50,13 +50,13 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_id"], name: "index_active_storage_blobs_on_university_id"
   end
 
-  create_table "active_storage_variant_records", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "active_storage_variant_records", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "blob_id", null: false
     t.string "variation_digest", null: false
     t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
   end
 
-  create_table "administration_qualiopi_criterions", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "administration_qualiopi_criterions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.integer "number"
     t.text "name"
     t.text "description"
@@ -64,7 +64,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.datetime "updated_at", null: false
   end
 
-  create_table "administration_qualiopi_indicators", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "administration_qualiopi_indicators", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "criterion_id", null: false
     t.integer "number"
     t.text "name"
@@ -94,7 +94,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_id"], name: "index_communication_block_headings_on_university_id"
   end
 
-  create_table "communication_blocks", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_blocks", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.string "about_type"
     t.uuid "about_id"
@@ -105,8 +105,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.datetime "updated_at", null: false
     t.string "title"
     t.boolean "published", default: true
-    t.uuid "heading_id"
     t.uuid "communication_website_id"
+    t.uuid "heading_id"
     t.index ["about_type", "about_id"], name: "index_communication_website_blocks_on_about"
     t.index ["communication_website_id"], name: "index_communication_blocks_on_communication_website_id"
     t.index ["heading_id"], name: "index_communication_blocks_on_heading_id"
@@ -195,7 +195,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_id"], name: "index_communication_extranet_posts_on_university_id"
   end
 
-  create_table "communication_extranets", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_extranets", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.string "name"
     t.uuid "university_id", null: false
     t.string "host"
@@ -223,11 +223,38 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.text "home_sentence"
     t.text "sass"
     t.text "css"
+    t.boolean "allow_experiences_modification", default: true
     t.index ["about_type", "about_id"], name: "index_communication_extranets_on_about"
     t.index ["university_id"], name: "index_communication_extranets_on_university_id"
   end
 
-  create_table "communication_website_categories", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_agenda_events", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+    t.string "title"
+    t.text "summary"
+    t.uuid "university_id", null: false
+    t.uuid "communication_website_id", null: false
+    t.uuid "language_id", null: false
+    t.uuid "original_id"
+    t.boolean "published", default: false
+    t.date "from_day"
+    t.time "from_hour"
+    t.date "to_day"
+    t.time "to_hour"
+    t.text "featured_image_alt"
+    t.text "featured_image_credit"
+    t.text "meta_description"
+    t.uuid "parent_id"
+    t.datetime "created_at", null: false
+    t.datetime "updated_at", null: false
+    t.text "slug"
+    t.index ["communication_website_id"], name: "index_agenda_events_on_communication_website_id"
+    t.index ["language_id"], name: "index_communication_website_agenda_events_on_language_id"
+    t.index ["original_id"], name: "index_communication_website_agenda_events_on_original_id"
+    t.index ["parent_id"], name: "index_communication_website_agenda_events_on_parent_id"
+    t.index ["university_id"], name: "index_communication_website_agenda_events_on_university_id"
+  end
+
+  create_table "communication_website_categories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "communication_website_id", null: false
     t.string "name"
@@ -275,7 +302,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["website_id"], name: "index_communication_website_connections_on_website_id"
   end
 
-  create_table "communication_website_git_files", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_git_files", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.string "previous_path"
     t.string "about_type", null: false
     t.uuid "about_id", null: false
@@ -287,7 +314,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["website_id"], name: "index_communication_website_git_files_on_website_id"
   end
 
-  create_table "communication_website_imported_authors", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_imported_authors", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "website_id", null: false
     t.uuid "author_id"
@@ -303,7 +330,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["website_id"], name: "idx_communication_website_imported_auth_on_website"
   end
 
-  create_table "communication_website_imported_categories", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_imported_categories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "website_id", null: false
     t.uuid "category_id"
@@ -321,7 +348,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["website_id"], name: "idx_communication_website_imported_cat_on_website"
   end
 
-  create_table "communication_website_imported_media", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_imported_media", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.string "identifier"
     t.jsonb "data"
     t.text "file_url"
@@ -336,7 +363,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["website_id"], name: "index_communication_website_imported_media_on_website_id"
   end
 
-  create_table "communication_website_imported_pages", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_imported_pages", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "website_id", null: false
     t.uuid "page_id"
@@ -360,7 +387,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["website_id"], name: "index_communication_website_imported_pages_on_website_id"
   end
 
-  create_table "communication_website_imported_posts", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_imported_posts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "website_id", null: false
     t.uuid "post_id"
@@ -385,7 +412,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["website_id"], name: "index_communication_website_imported_posts_on_website_id"
   end
 
-  create_table "communication_website_imported_websites", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_imported_websites", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "website_id", null: false
     t.integer "status", default: 0
@@ -395,7 +422,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["website_id"], name: "index_communication_website_imported_websites_on_website_id"
   end
 
-  create_table "communication_website_menu_items", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_menu_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "website_id", null: false
     t.uuid "menu_id", null: false
@@ -415,7 +442,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["website_id"], name: "index_communication_website_menu_items_on_website_id"
   end
 
-  create_table "communication_website_menus", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_menus", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "communication_website_id", null: false
     t.string "title"
@@ -431,7 +458,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_id"], name: "index_communication_website_menus_on_university_id"
   end
 
-  create_table "communication_website_pages", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_pages", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "communication_website_id", null: false
     t.string "title"
@@ -445,10 +472,10 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.boolean "published", default: false
     t.string "featured_image_alt"
     t.text "text"
-    t.text "summary"
     t.string "breadcrumb_title"
     t.text "header_text"
     t.integer "kind"
+    t.text "summary"
     t.string "bodyclass"
     t.uuid "language_id", null: false
     t.text "featured_image_credit"
@@ -462,7 +489,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_id"], name: "index_communication_website_pages_on_university_id"
   end
 
-  create_table "communication_website_permalinks", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_permalinks", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "website_id", null: false
     t.string "about_type", null: false
@@ -476,7 +503,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["website_id"], name: "index_communication_website_permalinks_on_website_id"
   end
 
-  create_table "communication_website_posts", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_website_posts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "communication_website_id", null: false
     t.string "title"
@@ -501,7 +528,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_id"], name: "index_communication_website_posts_on_university_id"
   end
 
-  create_table "communication_websites", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "communication_websites", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.string "name"
     t.string "url"
@@ -522,6 +549,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.string "theme_version", default: "NA"
     t.text "deployment_status_badge"
     t.boolean "autoupdate_theme", default: true
+    t.boolean "feature_posts", default: true
+    t.boolean "feature_agenda", default: false
     t.index ["about_type", "about_id"], name: "index_communication_websites_on_about"
     t.index ["default_language_id"], name: "index_communication_websites_on_default_language_id"
     t.index ["university_id"], name: "index_communication_websites_on_university_id"
@@ -552,10 +581,12 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.string "queue"
     t.datetime "created_at"
     t.datetime "updated_at"
+    t.string "signature"
+    t.text "args"
     t.index ["priority", "run_at"], name: "delayed_jobs_priority"
   end
 
-  create_table "education_academic_years", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "education_academic_years", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.integer "year"
     t.datetime "created_at", null: false
@@ -570,7 +601,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_person_id", "education_academic_year_id"], name: "index_person_academic_year"
   end
 
-  create_table "education_cohorts", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  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
     t.uuid "academic_year_id", null: false
@@ -591,7 +622,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_person_id", "education_cohort_id"], name: "index_person_cohort"
   end
 
-  create_table "education_diplomas", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "education_diplomas", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.string "name"
     t.string "short_name"
     t.integer "level", default: 0
@@ -605,7 +636,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_id"], name: "index_education_diplomas_on_university_id"
   end
 
-  create_table "education_programs", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "education_programs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.string "name"
     t.integer "capacity"
@@ -665,7 +696,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["education_program_id", "user_id"], name: "index_education_programs_users_on_program_id_and_user_id"
   end
 
-  create_table "education_schools", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "education_schools", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.string "name"
     t.string "address"
@@ -696,7 +727,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_id"], name: "index_emergency_messages_on_university_id", where: "(university_id IS NOT NULL)"
   end
 
-  create_table "imports", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "imports", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.integer "number_of_lines"
     t.jsonb "processing_errors"
     t.integer "kind"
@@ -709,7 +740,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["user_id"], name: "index_imports_on_user_id"
   end
 
-  create_table "languages", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "languages", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.string "name"
     t.string "iso_code"
     t.datetime "created_at", null: false
@@ -783,7 +814,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_id"], name: "index_research_journal_paper_kinds_on_university_id"
   end
 
-  create_table "research_journal_papers", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "research_journal_papers", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.string "title"
     t.datetime "published_at", precision: nil
     t.uuid "university_id", null: false
@@ -820,7 +851,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["researcher_id"], name: "index_research_journal_papers_researchers_on_researcher_id"
   end
 
-  create_table "research_journal_volumes", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "research_journal_volumes", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "research_journal_id", null: false
     t.string "title"
@@ -840,7 +871,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_id"], name: "index_research_journal_volumes_on_university_id"
   end
 
-  create_table "research_journals", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "research_journals", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.string "title"
     t.text "meta_description"
@@ -851,7 +882,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_id"], name: "index_research_journals_on_university_id"
   end
 
-  create_table "research_laboratories", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "research_laboratories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.string "name"
     t.string "address"
@@ -870,7 +901,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_person_id", "research_laboratory_id"], name: "person_laboratory"
   end
 
-  create_table "research_laboratory_axes", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "research_laboratory_axes", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "research_laboratory_id", null: false
     t.string "name"
@@ -884,7 +915,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_id"], name: "index_research_laboratory_axes_on_university_id"
   end
 
-  create_table "research_theses", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "research_theses", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "research_laboratory_id", null: false
     t.uuid "author_id", null: false
@@ -902,7 +933,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_id"], name: "index_research_theses_on_university_id"
   end
 
-  create_table "universities", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "universities", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.string "name"
     t.string "identifier"
     t.string "address"
@@ -938,7 +969,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_id"], name: "index_university_organization_categories_on_university_id"
   end
 
-  create_table "university_organizations", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "university_organizations", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.string "name"
     t.string "long_name"
@@ -980,7 +1011,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["organization_id"], name: "index_university_organizations_categories_on_organization_id"
   end
 
-  create_table "university_people", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "university_people", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "user_id"
     t.string "last_name"
@@ -1036,7 +1067,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_id"], name: "index_university_person_categories_on_university_id"
   end
 
-  create_table "university_person_experiences", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "university_person_experiences", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "person_id", null: false
     t.uuid "organization_id", null: false
@@ -1050,7 +1081,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_id"], name: "index_university_person_experiences_on_university_id"
   end
 
-  create_table "university_person_involvements", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "university_person_involvements", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.uuid "person_id", null: false
     t.integer "kind"
@@ -1065,7 +1096,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["university_id"], name: "index_university_person_involvements_on_university_id"
   end
 
-  create_table "university_roles", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "university_roles", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.string "target_type"
     t.uuid "target_id"
@@ -1087,7 +1118,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
     t.index ["user_id"], name: "index_user_favorites_on_user_id"
   end
 
-  create_table "users", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
+  create_table "users", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
     t.uuid "university_id", null: false
     t.string "first_name"
     t.string "last_name"
@@ -1158,6 +1189,11 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_29_141647) do
   add_foreign_key "communication_extranet_posts", "universities"
   add_foreign_key "communication_extranet_posts", "university_people", column: "author_id"
   add_foreign_key "communication_extranets", "universities"
+  add_foreign_key "communication_website_agenda_events", "communication_website_agenda_events", column: "original_id"
+  add_foreign_key "communication_website_agenda_events", "communication_website_agenda_events", column: "parent_id"
+  add_foreign_key "communication_website_agenda_events", "communication_websites"
+  add_foreign_key "communication_website_agenda_events", "languages"
+  add_foreign_key "communication_website_agenda_events", "universities"
   add_foreign_key "communication_website_categories", "communication_website_categories", column: "original_id"
   add_foreign_key "communication_website_categories", "communication_website_categories", column: "parent_id"
   add_foreign_key "communication_website_categories", "communication_websites"
diff --git a/test/fixtures/communication/extranets.yml b/test/fixtures/communication/extranets.yml
index c4a72564511a4905256b44eff1b90d9ad95d76db..5b00f559b78ac19b9a3871e2736923e92e325c4b 100644
--- a/test/fixtures/communication/extranets.yml
+++ b/test/fixtures/communication/extranets.yml
@@ -2,34 +2,35 @@
 #
 # Table name: communication_extranets
 #
-#  id                         :uuid             not null, primary key
-#  about_type                 :string           indexed => [about_id]
-#  color                      :string
-#  cookies_policy             :text
-#  css                        :text
-#  feature_alumni             :boolean          default(FALSE)
-#  feature_contacts           :boolean          default(FALSE)
-#  feature_jobs               :boolean          default(FALSE)
-#  feature_library            :boolean          default(FALSE)
-#  feature_posts              :boolean          default(FALSE)
-#  has_sso                    :boolean          default(FALSE)
-#  home_sentence              :text
-#  host                       :string
-#  name                       :string
-#  privacy_policy             :text
-#  registration_contact       :string
-#  sass                       :text
-#  sso_button_label           :string
-#  sso_cert                   :text
-#  sso_mapping                :jsonb
-#  sso_name_identifier_format :string
-#  sso_provider               :integer          default("saml")
-#  sso_target_url             :string
-#  terms                      :text
-#  created_at                 :datetime         not null
-#  updated_at                 :datetime         not null
-#  about_id                   :uuid             indexed => [about_type]
-#  university_id              :uuid             not null, indexed
+#  id                             :uuid             not null, primary key
+#  about_type                     :string           indexed => [about_id]
+#  allow_experiences_modification :boolean          default(TRUE)
+#  color                          :string
+#  cookies_policy                 :text
+#  css                            :text
+#  feature_alumni                 :boolean          default(FALSE)
+#  feature_contacts               :boolean          default(FALSE)
+#  feature_jobs                   :boolean          default(FALSE)
+#  feature_library                :boolean          default(FALSE)
+#  feature_posts                  :boolean          default(FALSE)
+#  has_sso                        :boolean          default(FALSE)
+#  home_sentence                  :text
+#  host                           :string
+#  name                           :string
+#  privacy_policy                 :text
+#  registration_contact           :string
+#  sass                           :text
+#  sso_button_label               :string
+#  sso_cert                       :text
+#  sso_mapping                    :jsonb
+#  sso_name_identifier_format     :string
+#  sso_provider                   :integer          default("saml")
+#  sso_target_url                 :string
+#  terms                          :text
+#  created_at                     :datetime         not null
+#  updated_at                     :datetime         not null
+#  about_id                       :uuid             indexed => [about_type]
+#  university_id                  :uuid             not null, indexed
 #
 # Indexes
 #
diff --git a/test/fixtures/communication/website/agenda/events.yml b/test/fixtures/communication/website/agenda/events.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0258a1fb280414504a769ace229dbc0596499e6e
--- /dev/null
+++ b/test/fixtures/communication/website/agenda/events.yml
@@ -0,0 +1,70 @@
+# == Schema Information
+#
+# Table name: communication_website_agenda_events
+#
+#  id                       :uuid             not null, primary key
+#  featured_image_alt       :text
+#  featured_image_credit    :text
+#  from_day                 :date
+#  from_hour                :time
+#  meta_description         :text
+#  published                :boolean          default(FALSE)
+#  slug                     :text
+#  summary                  :text
+#  title                    :string
+#  to_day                   :date
+#  to_hour                  :time
+#  created_at               :datetime         not null
+#  updated_at               :datetime         not null
+#  communication_website_id :uuid             not null, indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
+#  parent_id                :uuid             indexed
+#  university_id            :uuid             not null, indexed
+#
+# Indexes
+#
+#  index_agenda_events_on_communication_website_id             (communication_website_id)
+#  index_communication_website_agenda_events_on_language_id    (language_id)
+#  index_communication_website_agenda_events_on_original_id    (original_id)
+#  index_communication_website_agenda_events_on_parent_id      (parent_id)
+#  index_communication_website_agenda_events_on_university_id  (university_id)
+#
+# Foreign Keys
+#
+#  fk_rails_00ca585c35  (university_id => universities.id)
+#  fk_rails_5fa53206f2  (communication_website_id => communication_websites.id)
+#  fk_rails_67834f0062  (language_id => languages.id)
+#  fk_rails_917095d5ca  (parent_id => communication_website_agenda_events.id)
+#  fk_rails_fc3fea77c2  (original_id => communication_website_agenda_events.id)
+#
+
+one:
+  title: MyString
+  summary: MyText
+  university: default_university
+  website: website_with_github
+  language: fr
+  published: true
+  from_day: 2023-09-05
+  from_hour: 2023-09-05 13:13:30
+  to_day: 2023-09-05
+  to_hour: 2023-09-05 13:13:30
+  featured_image_alt: MyText
+  featured_image_credit: MyText
+  meta_description: MyText
+
+two:
+  title: MyString
+  summary: MyText
+  university: default_university
+  website: website_with_github
+  language: fr
+  published: true
+  from_day: 2023-09-05
+  from_hour: 2023-09-05 13:13:30
+  to_day: 2023-09-05
+  to_hour: 2023-09-05 13:13:30
+  featured_image_alt: MyText
+  featured_image_credit: MyText
+  meta_description: MyText
diff --git a/test/fixtures/communication/websites.yml b/test/fixtures/communication/websites.yml
index 190b521781ce653383118e3a3afd01a499f8576b..ed3627da6b5ddff55ff8fe3c8e9747a001ec9072 100644
--- a/test/fixtures/communication/websites.yml
+++ b/test/fixtures/communication/websites.yml
@@ -7,6 +7,8 @@
 #  access_token            :string
 #  autoupdate_theme        :boolean          default(TRUE)
 #  deployment_status_badge :text
+#  feature_agenda          :boolean          default(FALSE)
+#  feature_posts           :boolean          default(TRUE)
 #  git_branch              :string
 #  git_endpoint            :string
 #  git_provider            :integer          default("github")
diff --git a/test/models/communication/website/agenda/event_test.rb b/test/models/communication/website/agenda/event_test.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f418cf19a0cb9538d08af81d76cf1d48e13449ad
--- /dev/null
+++ b/test/models/communication/website/agenda/event_test.rb
@@ -0,0 +1,47 @@
+# == Schema Information
+#
+# Table name: communication_website_agenda_events
+#
+#  id                       :uuid             not null, primary key
+#  featured_image_alt       :text
+#  featured_image_credit    :text
+#  from_day                 :date
+#  from_hour                :time
+#  meta_description         :text
+#  published                :boolean          default(FALSE)
+#  slug                     :text
+#  summary                  :text
+#  title                    :string
+#  to_day                   :date
+#  to_hour                  :time
+#  created_at               :datetime         not null
+#  updated_at               :datetime         not null
+#  communication_website_id :uuid             not null, indexed
+#  language_id              :uuid             not null, indexed
+#  original_id              :uuid             indexed
+#  parent_id                :uuid             indexed
+#  university_id            :uuid             not null, indexed
+#
+# Indexes
+#
+#  index_agenda_events_on_communication_website_id             (communication_website_id)
+#  index_communication_website_agenda_events_on_language_id    (language_id)
+#  index_communication_website_agenda_events_on_original_id    (original_id)
+#  index_communication_website_agenda_events_on_parent_id      (parent_id)
+#  index_communication_website_agenda_events_on_university_id  (university_id)
+#
+# Foreign Keys
+#
+#  fk_rails_00ca585c35  (university_id => universities.id)
+#  fk_rails_5fa53206f2  (communication_website_id => communication_websites.id)
+#  fk_rails_67834f0062  (language_id => languages.id)
+#  fk_rails_917095d5ca  (parent_id => communication_website_agenda_events.id)
+#  fk_rails_fc3fea77c2  (original_id => communication_website_agenda_events.id)
+#
+require "test_helper"
+
+class Communication::Website::Agenda::EventTest < ActiveSupport::TestCase
+  # test "the truth" do
+  #   assert true
+  # end
+end