diff --git a/app/controllers/admin/education/program/roles_controller.rb b/app/controllers/admin/education/program/roles_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b3488e4ad86c359ce2404b33cffcc1b10c3a5450
--- /dev/null
+++ b/app/controllers/admin/education/program/roles_controller.rb
@@ -0,0 +1,54 @@
+class Admin::Education::Program::RolesController < Admin::Education::Program::ApplicationController
+  load_and_authorize_resource class: Education::Program::Role, through: :program
+
+  def show
+    breadcrumb
+  end
+
+  def new
+    breadcrumb
+  end
+
+  def edit
+    breadcrumb
+    add_breadcrumb t('edit')
+  end
+
+  def create
+    if @role.save
+      redirect_to admin_education_program_role_path(@role), notice: t('admin.successfully_created_html', model: @role.to_s)
+    else
+      breadcrumb
+      render :new, status: :unprocessable_entity
+    end
+  end
+
+  def update
+    if @role.update(role_params)
+      redirect_to admin_education_program_role_path(@role), notice: t('admin.successfully_updated_html', model: @role.to_s)
+    else
+      breadcrumb
+      render :edit, status: :unprocessable_entity
+      add_breadcrumb t('edit')
+    end
+  end
+
+  def destroy
+    @role.destroy
+    redirect_to admin_education_program_path(@program), notice: t('admin.successfully_destroyed_html', model: @role.to_s)
+  end
+
+  protected
+
+  def breadcrumb
+    super
+    add_breadcrumb Education::Program::Role.model_name.human(count: 2)
+    breadcrumb_for @role
+  end
+
+  def role_params
+    params.require(:education_program_role)
+          .permit(:title)
+          .merge(program_id: @program.id, university_id: current_university.id)
+  end
+end
diff --git a/app/models/concerns/with_media.rb b/app/models/concerns/with_media.rb
index 0387cea3f40962723fd814f21045392b71ffe565..816bb071cff07fcf32321309755f509a7c840602 100644
--- a/app/models/concerns/with_media.rb
+++ b/app/models/concerns/with_media.rb
@@ -26,7 +26,8 @@ module WithMedia
 
   def rich_text_blob_ids
     rich_text_reflection_names.map { |rich_text_reflection_name|
-      public_send(rich_text_reflection_name).embeds.blobs.pluck(:id)
+      rich_text = public_send(rich_text_reflection_name)
+      rich_text.present? ? rich_text.embeds.blobs.pluck(:id) : []
     }.flatten
   end
 
diff --git a/app/models/education/program/role.rb b/app/models/education/program/role.rb
index 2bb9dd6c76c02b20e6f2efcb39c8158d68e926a7..5a0365f34fd024730760fe4298958fd0c9d4c693 100644
--- a/app/models/education/program/role.rb
+++ b/app/models/education/program/role.rb
@@ -26,6 +26,10 @@ class Education::Program::Role < ApplicationRecord
   belongs_to :university
   belongs_to :program, class_name: 'Education::Program'
 
+  def to_s
+    "#{title}"
+  end
+
   protected
 
   def last_ordered_element
diff --git a/app/models/education/school.rb b/app/models/education/school.rb
index 9dfad42681b112d3ff8bc87a43fd4aed38e9aac6..c6be336047fb8fe10188c9cb9dfbede775789d10 100644
--- a/app/models/education/school.rb
+++ b/app/models/education/school.rb
@@ -27,7 +27,7 @@ class Education::School < ApplicationRecord
   include WithGit
 
   belongs_to :university
-  has_many :websites, class_name: 'Communication::Website', as: :about
+  has_many :websites, class_name: 'Communication::Website', as: :about, dependent: :nullify
   has_and_belongs_to_many :programs,
                           class_name: 'Education::Program',
                           join_table: 'education_programs_schools',
diff --git a/app/models/research/journal.rb b/app/models/research/journal.rb
index dc74e3252302262082c9aee0423720727f9e34ad..3b815b5ade15b803e6af74fa335c866c36458f33 100644
--- a/app/models/research/journal.rb
+++ b/app/models/research/journal.rb
@@ -24,7 +24,7 @@ class Research::Journal < ApplicationRecord
   include WithGit
 
   belongs_to :university
-  has_many :websites, class_name: 'Communication::Website', as: :about
+  has_many :websites, class_name: 'Communication::Website', as: :about, dependent: :nullify
   has_many :volumes, foreign_key: :research_journal_id, dependent: :destroy
   has_many :articles, foreign_key: :research_journal_id, dependent: :destroy
   has_many :researchers, through: :articles
diff --git a/app/views/admin/education/program/roles/_form.html.erb b/app/views/admin/education/program/roles/_form.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..26ccb783e9660e9a9c21fdeab7c8088934d0a0c9
--- /dev/null
+++ b/app/views/admin/education/program/roles/_form.html.erb
@@ -0,0 +1,17 @@
+<%= simple_form_for [:admin, role] do |f| %>
+  <div class="row">
+    <div class="col-md-8">
+      <div class="card flex-fill w-100">
+        <div class="card-header">
+          <h5 class="card-title mb-0"><%= t('admin.infos') %></h5>
+        </div>
+        <div class="card-body">
+          <%= f.input :title %>
+        </div>
+      </div>
+    </div>
+  </div>
+  <% content_for :action_bar_right do %>
+    <%= submit f %>
+  <% end %>
+<% end %>
diff --git a/app/views/admin/education/program/roles/_list.html.erb b/app/views/admin/education/program/roles/_list.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..b3a3b5fee618931e112cf5e7958f796ff8e657cb
--- /dev/null
+++ b/app/views/admin/education/program/roles/_list.html.erb
@@ -0,0 +1,31 @@
+<table class="table">
+  <thead>
+    <tr>
+      <th><%= Education::Program::Role.model_name.human %></th>
+      <th></th>
+    </tr>
+  </thead>
+  <tbody>
+    <% roles.each do |role| %>
+      <tr>
+        <td>
+          <%= link_to_if  can?(:read, role),
+                          role,
+                          admin_education_program_role_path(role, { program_id: @program.id }) %>
+        </td>
+        <td class="text-end pe-0">
+          <div class="btn-group" role="group">
+            <%= link_to t('edit'),
+                        edit_admin_education_program_role_path(role, { program_id: @program.id }),
+                        class: button_classes %>
+            <%= link_to t('delete'),
+                        admin_education_program_role_path(role, { program_id: @program.id }),
+                        method: :delete,
+                        data: { confirm: t('please_confirm') },
+                        class: button_classes_danger %>
+          </div>
+        </td>
+      </tr>
+    <% end %>
+  </tbody>
+</table>
diff --git a/app/views/admin/education/program/roles/edit.html.erb b/app/views/admin/education/program/roles/edit.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..ff1ec73bd6f1465a96497606080d481b6fd6ca51
--- /dev/null
+++ b/app/views/admin/education/program/roles/edit.html.erb
@@ -0,0 +1,3 @@
+<% content_for :title, @role %>
+
+<%= render 'form', role: @role %>
diff --git a/app/views/admin/education/program/roles/new.html.erb b/app/views/admin/education/program/roles/new.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..1bad6fa6b7171dc693ea7eeb1652b264eca17d07
--- /dev/null
+++ b/app/views/admin/education/program/roles/new.html.erb
@@ -0,0 +1,3 @@
+<% content_for :title, Education::Program::Role.model_name.human %>
+
+<%= render 'form', role: @role %>
diff --git a/app/views/admin/education/program/roles/show.html.erb b/app/views/admin/education/program/roles/show.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..e6d4117f32fda150675fcedacdeab4a3c54ae5d0
--- /dev/null
+++ b/app/views/admin/education/program/roles/show.html.erb
@@ -0,0 +1,19 @@
+<% content_for :title, @role %>
+
+<div class="row">
+  <div class="col-md-4">
+    <div class="card flex-fill w-100">
+      <div class="card-header">
+        <h2 class="card-title mb-0 h5"><%= t('admin.infos') %></h2>
+      </div>
+      <div class="card-body">
+        <h3 class="h5"><%= Education::Program::Role.human_attribute_name('title') %></h3>
+        <%= @role.title %>
+      </div>
+    </div>
+  </div>
+</div>
+
+<% content_for :action_bar_right do %>
+  <%= edit_link @role %>
+<% end %>
diff --git a/app/views/admin/education/program/teachers/_list.html.erb b/app/views/admin/education/program/teachers/_list.html.erb
new file mode 100644
index 0000000000000000000000000000000000000000..d609934057f34ebea423ae7e43a87501700a9d58
--- /dev/null
+++ b/app/views/admin/education/program/teachers/_list.html.erb
@@ -0,0 +1,35 @@
+<% if teachers.any? %>
+  <table class="table">
+    <thead>
+      <tr>
+        <th><%= Education::Program::Teacher.model_name.human %></th>
+        <th><%= Education::Program::Teacher.human_attribute_name('description') %></th>
+        <th></th>
+      </tr>
+    </thead>
+    <tbody>
+      <% teachers.each do |teacher| %>
+        <tr>
+          <td>
+            <%= link_to_if  can?(:read, teacher.person),
+                            teacher.person,
+                            admin_education_teacher_path(teacher.person) %>
+          </td>
+          <td><%= teacher.description %></td>
+          <td class="text-end pe-0">
+            <div class="btn-group" role="group">
+              <%= link_to t('edit'),
+                          edit_admin_education_program_teacher_path(teacher, { program_id: @program.id }),
+                          class: button_classes %>
+              <%= link_to t('delete'),
+                          admin_education_program_teacher_path(teacher, { program_id: @program.id }),
+                          method: :delete,
+                          data: { confirm: t('please_confirm') },
+                          class: button_classes_danger %>
+            </div>
+          </td>
+        </tr>
+      <% end %>
+    </tbody>
+  </table>
+<% end %>
diff --git a/app/views/admin/education/programs/show.html.erb b/app/views/admin/education/programs/show.html.erb
index e6c1563c711d8ac056d5ad655e10635548221c6f..1a929183479aa4c93b6ef942f94c3a72e919b497 100644
--- a/app/views/admin/education/programs/show.html.erb
+++ b/app/views/admin/education/programs/show.html.erb
@@ -102,48 +102,25 @@
           <%= @program.public_send prop %>
           <% i += 1 %>
         <% end %>
+
         <h3 class="h5 <%= 'mt-4' if i > 0 %>"><%= Education::Program.human_attribute_name('teachers') %></h3>
         <p><%= link_to t('create'), new_admin_education_program_teacher_path(program_id: @program.id), class: 'btn btn-primary' %></p>
-        <% if @program.teachers.any? %>
-          <table class="table">
-            <thead>
-              <tr>
-                <th><%= Education::Program::Teacher.model_name.human %></th>
-                <th><%= Education::Program::Teacher.human_attribute_name('description') %></th>
-                <th></th>
-              </tr>
-            </thead>
-            <tbody>
-              <% @program.teachers.includes(:person).ordered.each do |teacher| %>
-                <tr>
-                  <td>
-                    <%= link_to_if  can?(:read, teacher.person),
-                                    teacher.person,
-                                    admin_education_teacher_path(teacher.person) %>
-                  </td>
-                  <td><%= teacher.description %></td>
-                  <td class="text-end pe-0">
-                    <div class="btn-group" role="group">
-                      <%= link_to t('edit'),
-                                  edit_admin_education_program_teacher_path(teacher, { program_id: @program.id }),
-                                  class: button_classes %>
-                      <%= link_to t('delete'),
-                                  admin_education_program_teacher_path(teacher, { program_id: @program.id }),
-                                  method: :delete,
-                                  data: { confirm: t('please_confirm') },
-                                  class: button_classes_danger %>
-                    </div>
-                  </td>
-                </tr>
-              <% end %>
-            </tbody>
-          </table>
-        <% end %>
+        <%= render 'admin/education/program/teachers/list', teachers: @program.teachers.includes(:person).ordered %>
       </div>
     </div>
   </div>
 </div>
 
+<div class="card flex-fill w-100">
+  <div class="card-header">
+    <h5 class="card-title mb-0"><%= Education::Program.human_attribute_name('roles') %></h5>
+  </div>
+  <div class="card-body">
+    <p><%= link_to t('create'), new_admin_education_program_role_path(program_id: @program.id), class: 'btn btn-primary' %></p>
+    <%= render 'admin/education/program/roles/list', roles: @program.roles.ordered %>
+  </div>
+</div>
+
 <% if @program.children.any? %>
   <div class="card flex-fill w-100">
     <div class="card-header">
diff --git a/app/views/admin/education/programs/static.html.erb b/app/views/admin/education/programs/static.html.erb
index 5907c32ca463ba195dd1ef441e029ef4de850395..6bdeade466a4159b5b589a5290d10c96cde255ee 100644
--- a/app/views/admin/education/programs/static.html.erb
+++ b/app/views/admin/education/programs/static.html.erb
@@ -45,6 +45,6 @@ ects: <%= @program.ects %>
   :content
 ].each do |property| %>
 <%= property.to_s %>: >
-  <%= prepare_for_github @program.send "best_#{property}", @program.university %>
+  <%= prepare_for_github @program.send("best_#{property}"), @program.university %>
 <% end %>
 ---
diff --git a/config/locales/education/en.yml b/config/locales/education/en.yml
index fd30ffcd168332785b304ac0bb935ecc5f2c2fc2..a5e6d3c66d384495ce2d0d80a1bd3e996a457c0a 100644
--- a/config/locales/education/en.yml
+++ b/config/locales/education/en.yml
@@ -7,6 +7,9 @@ en:
       education/program:
         one: Program
         other: Programs
+      education/program/role:
+        one: Role
+        other: Roles
       education/program/teacher:
         one: Teacher
         other: Teachers
@@ -35,9 +38,13 @@ en:
         pricing: Tarifs
         published: Published?
         registration: Modalités et délais d’accès
+        roles: Roles
         schools: Schools with this formation
         teachers: Teachers
         team: Team
+      education/program/role:
+        people: People
+        title: Title
       education/program/teacher:
         description: Description
         person: Person
diff --git a/config/locales/education/fr.yml b/config/locales/education/fr.yml
index 7efba94a1882acb64df05104f79392bbfec10302..c087f4a27aea731ad9d5d2bc7fc87a8d1bd20b7b 100644
--- a/config/locales/education/fr.yml
+++ b/config/locales/education/fr.yml
@@ -7,9 +7,12 @@ fr:
       education/program:
         one: Formation
         other: Formations
+      education/program/role:
+        one: Rôle
+        other: Rôles
       education/program/teacher:
-        one: Enseignant
-        other: Enseignants
+        one: Enseignant·e
+        other: Enseignants·es
       education/school:
         one: École
         other: Écoles
@@ -35,11 +38,15 @@ fr:
         pricing: Tarifs
         published: Publiée ?
         registration: Modalités et délais d’accès
+        roles: Rôles
         schools: Écoles proposant cette formation
         teachers: Enseignants·es
         team: Équipe
         content: Contenus de la formation
         results: Indicateurs de résultats
+      education/program/role:
+        people: Personnes
+        title: Titre
       education/program/teacher:
         description: Description
         person: Personne
@@ -59,6 +66,9 @@ fr:
       educational_informations: Informations pédagogiques
       main_informations: Informations essentielles
       useful_informations: Informations pratiques
+    roles:
+      one: Rôle
+      other: Rôles
     teachers:
       one: Enseignant·e
       other: Enseignants·es
diff --git a/config/routes/admin/education.rb b/config/routes/admin/education.rb
index 115919b3d952703d5df46725d560133c7cb0526d..75ca3697efed49b6227f94127c53190c52936003 100644
--- a/config/routes/admin/education.rb
+++ b/config/routes/admin/education.rb
@@ -2,6 +2,7 @@ namespace :education do
   resources :teachers, only: [:index, :show]
   resources :schools
   resources :programs do
+    resources :roles, controller: 'program/roles', except: :index
     resources :teachers, controller: 'program/teachers', except: [:index, :show]
     collection do
       post :reorder