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