diff --git a/app/controllers/admin/communication/files/application_controller.rb b/app/controllers/admin/communication/files/application_controller.rb
new file mode 100644
index 0000000000..0d1fa013fe
--- /dev/null
+++ b/app/controllers/admin/communication/files/application_controller.rb
@@ -0,0 +1,10 @@
+class Admin::Communication::Files::ApplicationController < Admin::Communication::ApplicationController
+
+ protected
+
+ def breadcrumb
+ super
+ add_breadcrumb t('communication.description.parts.file.title'), admin_communication_files_path
+ breadcrumb_for @file
+ end
+end
diff --git a/app/controllers/admin/communication/files/categories_controller.rb b/app/controllers/admin/communication/files/categories_controller.rb
new file mode 100644
index 0000000000..450c727138
--- /dev/null
+++ b/app/controllers/admin/communication/files/categories_controller.rb
@@ -0,0 +1,84 @@
+class Admin::Communication::Files::CategoriesController < Admin::Communication::Files::ApplicationController
+ load_and_authorize_resource class: Communication::File::Category,
+ through: :current_university,
+ through_association: :communication_file_categories
+
+ include Admin::ActAsCategories
+ include Admin::Localizable
+
+ def index
+ @root_categories = categories.root.ordered(current_language)
+ @categories_class = categories_class
+ @feature_nav = 'navigation/admin/communication/files'
+ breadcrumb
+ end
+
+ def show
+ @files = @category.files
+ .ordered(current_language)
+ .page(params[:page])
+ breadcrumb
+ end
+
+ def new
+ @categories = categories
+ breadcrumb
+ end
+
+ def edit
+ @categories = categories
+ breadcrumb
+ add_breadcrumb t('edit')
+ end
+
+ def create
+ if @category.save
+ redirect_to admin_communication_file_category_path(@category),
+ notice: t('admin.successfully_created_html', model: @category.to_s_in(current_language))
+ else
+ @categories = categories
+ breadcrumb
+ render :new, status: :unprocessable_content
+ end
+ end
+
+ def update
+ if @category.update(category_params)
+ redirect_to admin_communication_file_category_path(@category),
+ notice: t('admin.successfully_updated_html', model: @category.to_s_in(current_language))
+ else
+ load_invalid_localization
+ @categories = categories
+ breadcrumb
+ add_breadcrumb t('edit')
+ render :edit, status: :unprocessable_content
+ end
+ end
+
+ def destroy
+ @category.destroy
+ redirect_to admin_communication_file_categories_path,
+ notice: t('admin.successfully_destroyed_html', model: @category.to_s_in(current_language))
+ end
+
+ protected
+
+ def categories_class
+ Communication::File::Category
+ end
+
+ def categories
+ current_university.file_categories.ordered(current_language)
+ end
+
+ def breadcrumb
+ super
+ add_breadcrumb Communication::File::Category.model_name.human(count: 2),
+ admin_communication_file_categories_path
+ breadcrumb_for @category
+ end
+
+ def category_params
+ permitted_params_for(:communication_file_category)
+ end
+end
diff --git a/app/controllers/admin/communication/files_controller.rb b/app/controllers/admin/communication/files_controller.rb
new file mode 100644
index 0000000000..37555ae3a4
--- /dev/null
+++ b/app/controllers/admin/communication/files_controller.rb
@@ -0,0 +1,85 @@
+class Admin::Communication::FilesController < Admin::Communication::Files::ApplicationController
+ load_and_authorize_resource class: Communication::File,
+ through: :current_university
+
+ include Admin::Localizable
+
+ def index
+ @files = @files.filter_by(params[:filters], current_language)
+ .ordered(current_language)
+ .page(params[:page])
+ @categories = categories.root
+ breadcrumb
+ @feature_nav = 'navigation/admin/communication/files'
+ end
+
+ def show
+ breadcrumb
+ end
+
+ def new
+ @categories = categories
+ breadcrumb
+ end
+
+ def pick
+ picker = Osuny::File::Picker.new
+ picker.university = current_university
+ picker.language = current_language
+ picker.params = params.to_unsafe_hash
+ render json: picker.to_json
+ end
+
+ def edit
+ @categories = categories
+ breadcrumb
+ add_breadcrumb t('admin.subnav.settings')
+ end
+
+ def create
+ if @file.save
+ redirect_to [:admin, @file], notice: t('admin.successfully_created_html', model: @file.to_s_in(current_language))
+ else
+ load_invalid_localization
+ @categories = categories
+ breadcrumb
+ render :new, status: :unprocessable_content
+ end
+ end
+
+ def update
+ if @file.update(file_params)
+ redirect_to [:admin, @file], notice: t('admin.successfully_updated_html', model: @file.to_s_in(current_language))
+ else
+ load_invalid_localization
+ @categories = categories
+ breadcrumb
+ add_breadcrumb t('edit')
+ render :edit, status: :unprocessable_content
+ end
+ end
+
+ def destroy
+ @file.destroy
+ redirect_to admin_communication_files_url, notice: t('admin.successfully_destroyed_html', model: @file.to_s_in(current_language))
+ end
+
+ protected
+
+ def file_params
+ params.require(:communication_file)
+ .permit(
+ category_ids: [],
+ localizations_attributes: [
+ :id, :name, :alt, :credit, :internal_description,
+ :original_uploaded_file, :language_id
+ ]
+ )
+ .merge(university_id: current_university.id)
+ end
+
+ def categories
+ current_university.communication_file_categories.ordered
+ end
+
+end
\ No newline at end of file
diff --git a/app/models/communication.rb b/app/models/communication.rb
index c378966887..fc181369df 100644
--- a/app/models/communication.rb
+++ b/app/models/communication.rb
@@ -12,6 +12,7 @@ def self.parts
[Communication::Website, :admin_communication_websites_path],
[Communication::Extranet, :admin_communication_extranets_path],
[Communication::Media, :admin_communication_medias_path],
+ [Communication::File, :admin_communication_files_path],
]
end
end
diff --git a/app/models/communication/block.rb b/app/models/communication/block.rb
index ba2f240345..40ee9d1433 100644
--- a/app/models/communication/block.rb
+++ b/app/models/communication/block.rb
@@ -9,6 +9,7 @@
# html_class :string
# metadata :jsonb
# migration_identifier :string
+# native :boolean default(FALSE)
# position :integer not null
# published :boolean default(TRUE)
# template_kind :integer default(NULL), not null, indexed => [university_id]
diff --git a/app/models/communication/file.rb b/app/models/communication/file.rb
new file mode 100644
index 0000000000..673c347c9e
--- /dev/null
+++ b/app/models/communication/file.rb
@@ -0,0 +1,40 @@
+# == Schema Information
+#
+# Table name: communication_files
+#
+# id :uuid not null, primary key
+# original_byte_size :bigint
+# original_checksum :string
+# original_content_type :string
+# original_filename :string
+# created_at :datetime not null
+# updated_at :datetime not null
+# original_blob_id :uuid not null, indexed
+# university_id :uuid not null, indexed
+#
+# Indexes
+#
+# index_communication_files_on_original_blob_id (original_blob_id)
+# index_communication_files_on_university_id (university_id)
+#
+# Foreign Keys
+#
+# fk_rails_28f27bc358 (university_id => universities.id)
+#
+class Communication::File < ApplicationRecord
+ include Filterable
+ include Categorizable # Must be loaded after Filterable to be filtered by categories
+ include Localizable
+ include LocalizableOrderByNameScope
+ include WithOpenApi
+ include WithUniversity
+
+ scope :for_search_term, -> (term, language = nil) {
+ joins(:localizations)
+ .where(communication_file_localizations: { language_id: language.id })
+ .where("
+ unaccent(communication_file_localizations.name) ILIKE unaccent(:term) OR
+ unaccent(communication_file_localizations.internal_description) ILIKE unaccent(:term)
+ ", term: "%#{sanitize_sql_like(term)}%")
+ }
+end
diff --git a/app/models/communication/file/category.rb b/app/models/communication/file/category.rb
new file mode 100644
index 0000000000..c47178e4fb
--- /dev/null
+++ b/app/models/communication/file/category.rb
@@ -0,0 +1,32 @@
+# == Schema Information
+#
+# Table name: communication_file_categories
+#
+# id :uuid not null, primary key
+# bodyclass :string
+# is_taxonomy :boolean default(FALSE)
+# position :integer default(0)
+# position_in_tree :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+# parent_id :uuid indexed
+# university_id :uuid not null, indexed
+#
+# Indexes
+#
+# index_communication_file_categories_on_parent_id (parent_id)
+# index_communication_file_categories_on_university_id (university_id)
+#
+# Foreign Keys
+#
+# fk_rails_00fa9503c3 (university_id => universities.id)
+# fk_rails_061315a91c (parent_id => communication_file_categories.id)
+#
+class Communication::File::Category < ApplicationRecord
+ include AsCategory
+ include Localizable
+ include WithUniversity
+
+ has_and_belongs_to_many :files
+ alias :category_objects :files
+end
diff --git a/app/models/communication/file/category/localization.rb b/app/models/communication/file/category/localization.rb
new file mode 100644
index 0000000000..3e89075565
--- /dev/null
+++ b/app/models/communication/file/category/localization.rb
@@ -0,0 +1,33 @@
+# == Schema Information
+#
+# Table name: communication_file_category_localizations
+#
+# id :uuid not null, primary key
+# featured_image_alt :text
+# featured_image_credit :text
+# meta_description :text
+# name :string
+# slug :string
+# summary :text
+# created_at :datetime not null
+# updated_at :datetime not null
+# about_id :uuid uniquely indexed => [language_id], indexed
+# language_id :uuid uniquely indexed => [about_id], indexed
+# university_id :uuid indexed
+#
+# Indexes
+#
+# idx_on_about_id_language_id_f3d63b2051 (about_id,language_id) UNIQUE
+# idx_on_university_id_7bebff08b4 (university_id)
+# index_communication_file_category_localizations_on_about_id (about_id)
+# index_communication_file_category_localizations_on_language_id (language_id)
+#
+# Foreign Keys
+#
+# fk_rails_085826a452 (university_id => universities.id)
+# fk_rails_cbf5631003 (language_id => languages.id)
+# fk_rails_dbc01ccb51 (about_id => communication_file_categories.id)
+#
+class Communication::File::Category::Localization < ApplicationRecord
+ include AsCategoryLocalization
+end
diff --git a/app/models/communication/file/localization.rb b/app/models/communication/file/localization.rb
new file mode 100644
index 0000000000..4815d75e1a
--- /dev/null
+++ b/app/models/communication/file/localization.rb
@@ -0,0 +1,107 @@
+# == Schema Information
+#
+# Table name: communication_file_localizations
+#
+# id :uuid not null, primary key
+# internal_description :text
+# name :string
+# original_byte_size :bigint
+# original_checksum :string
+# original_content_type :string
+# original_filename :string
+# slug :string
+# created_at :datetime not null
+# updated_at :datetime not null
+# about_id :uuid not null, indexed
+# language_id :uuid not null, indexed
+# original_blob_id :uuid not null, indexed
+# university_id :uuid not null, indexed
+#
+# Indexes
+#
+# index_communication_file_localizations_on_about_id (about_id)
+# index_communication_file_localizations_on_language_id (language_id)
+# index_communication_file_localizations_on_original_blob_id (original_blob_id)
+# index_communication_file_localizations_on_university_id (university_id)
+#
+# Foreign Keys
+#
+# fk_rails_2caf77cf04 (original_blob_id => active_storage_blobs.id)
+# fk_rails_38de4b5d8a (language_id => languages.id)
+# fk_rails_6f750651f5
+# fk_rails_fcfa27eb47 (university_id => universities.id)
+#
+class Communication::File::Localization < ApplicationRecord
+ include AsLocalization
+ include Initials
+ include WithOpenApi
+ include WithUniversity
+
+ belongs_to :original_blob,
+ class_name: 'ActiveStorage::Blob'
+
+ attr_accessor :original_uploaded_file
+
+ before_validation :create_original_blob_from_upload, on: :create, if: :original_uploaded_file
+
+ validates :original_uploaded_file, presence: true, on: :create, unless: :original_blob
+
+ def to_s
+ "#{name}"
+ end
+
+ protected
+
+ def create_original_blob_from_upload
+ return if wrong_uploaded_file? || file_size_too_big?
+ original_uploaded_file_io = original_uploaded_file.open
+ blob = build_blob_from_upload(original_uploaded_file_io)
+ return if file_exists_for_blob_checksum?(blob)
+ # Blob is not a duplicate, we can save it and upload the file
+ blob.save!
+ # https://apidock.com/rails/v7.0.0/ActiveStorage/Blob/upload_without_unfurling
+ blob.upload_without_unfurling(original_uploaded_file_io)
+ blob.update_column :university_id, university_id
+ self.original_blob = blob
+ end
+
+ def wrong_uploaded_file?
+ if !original_uploaded_file.is_a?(ActionDispatch::Http::UploadedFile)
+ errors.add :original_uploaded_file, :no_file
+ true
+ else
+ false
+ end
+ end
+
+ def file_size_too_big?
+ if original_uploaded_file.size > Rails.application.config.default_image_max_size
+ errors.add :original_uploaded_file, :too_big
+ true
+ else
+ false
+ end
+ end
+
+ def file_exists_for_blob_checksum?(blob)
+ if university.communication_file_localizations.where(original_checksum: blob.checksum).any?
+ errors.add :original_uploaded_file, :already_imported
+ true
+ else
+ false
+ end
+ end
+
+ def build_blob_from_upload(io)
+ # We don't use create_and_upload! method as persisting the blob and uploading the file might be useless.
+ # Instead, we use the build_and_unfurl method of the Blob class:
+ # - Build will initialize a new Blob object
+ # - Unfurl will calculate the checksum, the content type and the byte size
+ # https://apidock.com/rails/v7.0.0/ActiveStorage/Blob/build_after_unfurling/class
+ ActiveStorage::Blob.build_after_unfurling(
+ io: io,
+ filename: original_uploaded_file.original_filename,
+ content_type: original_uploaded_file.content_type
+ )
+ end
+end
diff --git a/app/models/communication/file/localization/with_open_api.rb b/app/models/communication/file/localization/with_open_api.rb
new file mode 100644
index 0000000000..b540e5de2a
--- /dev/null
+++ b/app/models/communication/file/localization/with_open_api.rb
@@ -0,0 +1,32 @@
+module Communication::File::Localization::WithOpenApi
+ extend ActiveSupport::Concern
+
+ included do
+ OPENAPI_SCHEMA = {
+ type: :object,
+ title: "Communication::File::Localization",
+ properties: {
+ id: { type: :string, format: :uuid, nullable: true },
+ name: { type: :string, nullable: true },
+ slug: { type: :string, nullable: true },
+ internal_description: { type: :string, nullable: true },
+ original_byte_size: { type: :string, nullable: true },
+ original_checksum: { type: :string, nullable: true },
+ original_content_type: { type: :string, nullable: true },
+ original_filename: { type: :string, nullable: true },
+ original_blob: {
+ type: :object,
+ properties: {
+ id: { type: :string, format: :uuid, nullable: true },
+ signed_id: { type: :string, nullable: true },
+ filename: { type: :string, nullable: true },
+ content_type: { type: :string, nullable: true }
+ },
+ nullable: true
+ },
+ created_at: { type: :string, format: "date-time" },
+ updated_at: { type: :string, format: "date-time" }
+ }
+ }
+ end
+end
\ No newline at end of file
diff --git a/app/models/communication/file/with_open_api.rb b/app/models/communication/file/with_open_api.rb
new file mode 100644
index 0000000000..bfeed76906
--- /dev/null
+++ b/app/models/communication/file/with_open_api.rb
@@ -0,0 +1,22 @@
+module Communication::File::WithOpenApi
+ extend ActiveSupport::Concern
+
+ included do
+ OPENAPI_SCHEMA = {
+ type: :object,
+ title: "Communication::File",
+ properties: {
+ id: { type: :string, format: :uuid, nullable: true },
+ localizations: {
+ type: :object,
+ description: "Localizations of the file. The key is the language's ISO 639-1 code.",
+ additionalProperties: {
+ "$ref": "#/components/schemas/communication_media_localization"
+ }
+ },
+ created_at: { type: :string, format: "date-time" },
+ updated_at: { type: :string, format: "date-time" }
+ }
+ }
+ end
+end
\ No newline at end of file
diff --git a/app/models/university/organization.rb b/app/models/university/organization.rb
index 7a9a68d5ba..9d6f74235c 100644
--- a/app/models/university/organization.rb
+++ b/app/models/university/organization.rb
@@ -9,6 +9,9 @@
# country :string
# deleted_at :datetime
# email :string
+# is_laboratory :boolean default(FALSE)
+# is_location :boolean default(FALSE)
+# is_school :boolean default(FALSE)
# kind :integer default("company")
# latitude :float
# longitude :float
diff --git a/app/models/university/with_communication.rb b/app/models/university/with_communication.rb
index b5ef20b3d0..0e5f6c7a00 100644
--- a/app/models/university/with_communication.rb
+++ b/app/models/university/with_communication.rb
@@ -12,6 +12,21 @@ module University::WithCommunication
dependent: :destroy
alias_method :extranets, :communication_extranets
+ has_many :communication_files,
+ class_name: 'Communication::File',
+ dependent: :destroy
+ alias_method :files, :communication_files
+
+ has_many :communication_file_localizations,
+ class_name: 'Communication::File::Localization',
+ dependent: :destroy
+ alias_method :file_localizations, :communication_file_localizations
+
+ has_many :communication_file_categories,
+ class_name: 'Communication::File::Category',
+ dependent: :destroy
+ alias_method :file_categories, :communication_file_categories
+
has_many :communication_medias,
class_name: 'Communication::Media',
dependent: :destroy
diff --git a/app/views/admin/application/categories/_form.html.erb b/app/views/admin/application/categories/_form.html.erb
index 5e2a83b658..81348949b0 100644
--- a/app/views/admin/application/categories/_form.html.erb
+++ b/app/views/admin/application/categories/_form.html.erb
@@ -3,7 +3,7 @@
<%= osuny_panel t('content') do %>
<%= lf.input :name,
label: t('category.name'),
- input_html: { data: { translatable: true } } %>
+ input_html: { data: { translatable: true }, id: "category_name" } %>
<%= lf.input :subtitle,
label: t('category.subtitle'),
input_html: { data: { translatable: true } } if l10n.respond_to?(:subtitle) %>
diff --git a/app/views/admin/communication/files/_file.html.erb b/app/views/admin/communication/files/_file.html.erb
new file mode 100644
index 0000000000..ae307917bd
--- /dev/null
+++ b/app/views/admin/communication/files/_file.html.erb
@@ -0,0 +1,19 @@
+<%
+l10n = file.best_localization_for(current_language)
+initials = l10n.initials
+image = l10n.original_blob
+%>
+
+
+
+ <%= initials %>
+
+
+
+ <%= osuny_link_localized file,
+ admin_communication_file_path(file),
+ classes: 'stretched-link' %>
+
+
diff --git a/app/views/admin/communication/files/_filters.html.erb b/app/views/admin/communication/files/_filters.html.erb
new file mode 100644
index 0000000000..7703b84512
--- /dev/null
+++ b/app/views/admin/communication/files/_filters.html.erb
@@ -0,0 +1,21 @@
+<%= simple_form_for :filters, url: current_path, method: :get do |f| %>
+ <%= filters_panel current_path: current_path, active_filters_count: active_filters_count do |form| %>
+
+ <%= render_filter f,
+ :string,
+ :for_search_term,
+ label: t('search')
+ %>
+
+ <% if current_university.communication_file_categories.any? %>
+ <%= render_filter f,
+ :select,
+ :for_category,
+ label: t('filters.attributes.category'),
+ collection: osuny_collection_tree(current_university.communication_file_categories.root, localized: true),
+ multiple: true
+ %>
+ <% end %>
+
+ <% end %>
+<% end %>
\ No newline at end of file
diff --git a/app/views/admin/communication/files/_form.html.erb b/app/views/admin/communication/files/_form.html.erb
new file mode 100644
index 0000000000..1f428db9a9
--- /dev/null
+++ b/app/views/admin/communication/files/_form.html.erb
@@ -0,0 +1,26 @@
+<%= simple_form_for [:admin, file] do |f| %>
+ <%= f.simple_fields_for :localizations, l10n do |lf| %>
+ <%= f.error_notification %>
+ <%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>
+ <%= lf.hidden_field :language_id, value: current_language.id %>
+
+
+
+ <% if l10n.new_record? %>
+ <%= lf.input :original_uploaded_file,
+ as: :file %>
+ <% end %>
+
+
+ <%= lf.input :name %>
+ <%= render 'admin/application/categories/widget/form', f: f, categories: @categories %>
+ <%= lf.input :internal_description, hint: t('simple_form.hints.communication_file_localization.internal_description') %>
+
+
+ <% content_for :action_bar_right do %>
+ <%= cancel [:admin, file] %>
+ <%= render 'admin/application/l10n/libre_translate_button', l10n: l10n %>
+ <%= submit f %>
+ <% end %>
+ <% end %>
+<% end %>
diff --git a/app/views/admin/communication/files/_list.html.erb b/app/views/admin/communication/files/_list.html.erb
new file mode 100644
index 0000000000..0b786128f9
--- /dev/null
+++ b/app/views/admin/communication/files/_list.html.erb
@@ -0,0 +1,7 @@
+
+ <% files.each do |file| %>
+
+ <%= render 'admin/communication/files/file', file: file %>
+
+ <% end %>
+
\ No newline at end of file
diff --git a/app/views/admin/communication/files/_picker.html.erb b/app/views/admin/communication/files/_picker.html.erb
new file mode 100644
index 0000000000..1b91b07ab2
--- /dev/null
+++ b/app/views/admin/communication/files/_picker.html.erb
@@ -0,0 +1,27 @@
+<%
+picker = Osuny::File::Picker.new(about: about)
+lang = about&.language&.iso_code if about.respond_to? :language
+summernote_lang = current_interface_language.summernote_locale.presence() || 'en-US'
+cloud = {
+ unsplash: {
+ logo: asset_path('communication/photo_imports/unsplash.svg'),
+ endpoint: admin_communication_unsplash_path(website_id: nil, extranet_id: nil, journal_id: nil, format: :json),
+ },
+ pexels: {
+ logo: asset_path('communication/photo_imports/pexels.png'),
+ endpoint: admin_communication_pexels_path(website_id: nil, extranet_id: nil, journal_id: nil, format: :json),
+ },
+}
+%>
+"
+ data-file-endpoint="<%= admin_communication_files_url(website_id: nil, extranet_id: nil, journal_id: nil, format: :json) %>"
+ >
+
diff --git a/app/views/admin/communication/files/categories/_form.html.erb b/app/views/admin/communication/files/categories/_form.html.erb
new file mode 100644
index 0000000000..1b3dff53d2
--- /dev/null
+++ b/app/views/admin/communication/files/categories/_form.html.erb
@@ -0,0 +1,14 @@
+<%= simple_form_for [:admin, category] do |f| %>
+ <%= f.simple_fields_for :localizations, l10n do |lf| %>
+ <%= f.error_notification %>
+ <%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>
+ <%= lf.hidden_field :language_id, value: current_language.id %>
+ <%= render 'admin/application/categories/form',
+ f: f, lf: lf, category: category, l10n: l10n,
+ collection: @categories.root %>
+ <% content_for :action_bar_right do %>
+ <%= cancel [:admin, category] %>
+ <%= submit f %>
+ <% end %>
+ <% end %>
+<% end %>
\ No newline at end of file
diff --git a/app/views/admin/communication/files/categories/edit.html.erb b/app/views/admin/communication/files/categories/edit.html.erb
new file mode 100644
index 0000000000..a80651ce13
--- /dev/null
+++ b/app/views/admin/communication/files/categories/edit.html.erb
@@ -0,0 +1,3 @@
+<% content_for :title, @l10n %>
+
+<%= render 'form', category: @category, l10n: @l10n %>
diff --git a/app/views/admin/communication/files/categories/index.html.erb b/app/views/admin/communication/files/categories/index.html.erb
new file mode 100644
index 0000000000..6d0b345949
--- /dev/null
+++ b/app/views/admin/communication/files/categories/index.html.erb
@@ -0,0 +1,9 @@
+<% content_for :title, Communication::File::Category.model_name.human(count: 2) %>
+
+<% content_for :title_right do %>
+ <%= create_link Communication::File::Category %>
+<% end %>
+
+<%= render 'admin/application/categories/list',
+ root_categories: @root_categories,
+ categories_class: @categories_class %>
diff --git a/app/views/admin/communication/files/categories/new.html.erb b/app/views/admin/communication/files/categories/new.html.erb
new file mode 100644
index 0000000000..ee2d59daff
--- /dev/null
+++ b/app/views/admin/communication/files/categories/new.html.erb
@@ -0,0 +1,3 @@
+<% content_for :title, Communication::File::Category.model_name.human %>
+
+<%= render 'form', category: @category, l10n: @l10n %>
diff --git a/app/views/admin/communication/files/categories/show.html.erb b/app/views/admin/communication/files/categories/show.html.erb
new file mode 100644
index 0000000000..0d0a8988cd
--- /dev/null
+++ b/app/views/admin/communication/files/categories/show.html.erb
@@ -0,0 +1,21 @@
+<% content_for :title, @l10n %>
+
+<% if @category.editable? %>
+ <% content_for :title_right do %>
+ <%= button_advanced do %>
+ <%= destroy_link @category %>
+ <% end %>
+ <%= edit_link @category %>
+ <% end %>
+<% end %>
+
+<%= render 'admin/application/categories/show', category: @category, l10n: @l10n %>
+
+<% if @files.total_count > 0 %>
+
+ <%= @files.total_count %>
+ <%= Communication::File.model_name.human(count: @files.total_count).downcase %>
+
+ <%= render 'admin/communication/files/list', files: @files %>
+ <%= paginate @files %>
+<% end %>
diff --git a/app/views/admin/communication/files/edit.html.erb b/app/views/admin/communication/files/edit.html.erb
new file mode 100644
index 0000000000..be6a777f48
--- /dev/null
+++ b/app/views/admin/communication/files/edit.html.erb
@@ -0,0 +1,2 @@
+<% content_for :title, @l10n %>
+<%= render 'form', file: @file, l10n: @l10n %>
diff --git a/app/views/admin/communication/files/index.html.erb b/app/views/admin/communication/files/index.html.erb
new file mode 100644
index 0000000000..62cb4c8d4c
--- /dev/null
+++ b/app/views/admin/communication/files/index.html.erb
@@ -0,0 +1,16 @@
+<% content_for :title, t('communication.description.parts.file.title') %>
+
+<% content_for :title_right do %>
+ <%= create_link Communication::File %>
+<% end %>
+
+
+
+ <%= @files.total_count %>
+ <%= Communication::File.model_name.human(count: @files.total_count).downcase %>
+
+ <%= render 'filters', current_path: admin_communication_files_path %>
+
+
+<%= render 'admin/communication/files/list', files: @files %>
+<%= paginate @files %>
diff --git a/app/views/admin/communication/files/index.json.jbuilder b/app/views/admin/communication/files/index.json.jbuilder
new file mode 100644
index 0000000000..c796745862
--- /dev/null
+++ b/app/views/admin/communication/files/index.json.jbuilder
@@ -0,0 +1,20 @@
+json.total @files.total_count
+json.total_pages @files.total_pages
+json.collections @collections do |collection|
+ json.id collection.id
+ json.name collection.to_s_in(current_language)
+end
+
+json.partial! 'admin/application/categories/list', categories: @categories
+
+json.results @files do |file|
+ next unless file.original_blob.variable?
+ thumb_path = ENV["KEYCDN_HOST"].present? ? file.keycdn_thumb_url
+ : url_for(file.original_blob.variant(resize_to_fit: [600, nil]))
+ l10n = file.best_localization_for(current_language)
+ json.id file.id
+ json.name l10n.name
+ json.credit l10n.credit.to_s
+ json.alt l10n.alt.to_s
+ json.thumb thumb_path
+end
diff --git a/app/views/admin/communication/files/new.html.erb b/app/views/admin/communication/files/new.html.erb
new file mode 100644
index 0000000000..12a9663b4a
--- /dev/null
+++ b/app/views/admin/communication/files/new.html.erb
@@ -0,0 +1,2 @@
+<% content_for :title, Communication::File.model_name.human %>
+<%= render 'form', file: @file, l10n: @l10n %>
diff --git a/app/views/admin/communication/files/show.html.erb b/app/views/admin/communication/files/show.html.erb
new file mode 100644
index 0000000000..3a51ad349c
--- /dev/null
+++ b/app/views/admin/communication/files/show.html.erb
@@ -0,0 +1,35 @@
+<% content_for :title, @l10n %>
+
+<% content_for :title_right do %>
+ <%= button_advanced do %>
+ <%# FIXME = destroy_link @file if @file.contexts.none? %>
+ <% end %>
+ <%= edit_link @file %>
+<% end %>
+
+
+
+ <%= link_to t('download'),
+ download_medium_path(signed_id: @l10n.original_blob.signed_id),
+ class: button_classes %>
+
+
+
+
+ <%= osuny_label Communication::File::Localization.human_attribute_name(:original_filename) %>
+
<%= @l10n.original_filename %>
+
+
+ <%= osuny_label Communication::File::Localization.human_attribute_name(:original_content_type) %>
+
<%= @l10n.original_content_type %>
+
+
+ <%= osuny_label Communication::File::Localization.human_attribute_name(:original_byte_size) %>
+
<%= number_to_human_size @l10n.original_byte_size %>
+
+
+
+ <%= render 'admin/application/categories/widget/show', about: @file %>
+ <%#= render 'admin/communication/files/contexts/list', file: @file %>
+
+
diff --git a/config/locales/communication/en.yml b/config/locales/communication/en.yml
index 968089ec34..e2ae34b187 100644
--- a/config/locales/communication/en.yml
+++ b/config/locales/communication/en.yml
@@ -86,6 +86,13 @@ en:
published_at: Publication date
slug: Slug
title: Title
+ communication/file/localization:
+ internal_description: Internal description
+ name: Name
+ original_byte_size: Size
+ original_content_type: Content type
+ original_filename: Original filename
+ original_uploaded_file: Original file
communication/media:
collection: Collection
height: Height
@@ -334,6 +341,11 @@ en:
attributes:
published:
cannot_unpublished_default: cannot be unpublished (default language)
+ communication/file/localization:
+ attributes:
+ original_uploaded_file:
+ already_imported: already imported in file library
+ too_big: too heavy!
communication/media:
attributes:
original_uploaded_file:
@@ -388,6 +400,15 @@ en:
communication/extranet/post/category:
one: Category
other: Categories
+ communication/file:
+ one: File
+ other: Files
+ communication/file/category:
+ one: Category
+ other: Categories
+ communication/file/context:
+ one: Usage context
+ other: Usage contexts
communication/media:
one: Media
other: Medias
@@ -689,10 +710,14 @@ en:
description: Secure exchange areas dedicated to authenticated users
description_non_university: Secure exchange areas dedicated to authenticated users
title: Extranets
+ file:
+ description: Library of all downloadable files used in this osuny instance
+ description_non_university: Library of all downloadable files used in this osuny instance
+ title: Files
media:
description: Library of all the photos used in this osuny instance
description_non_university: Library of all the photos used in this osuny instance
- title: Media library
+ title: Photos
website:
description: Public spaces for the dissemination of information, ecologically sober and accessible to people with disabilities
description_non_university: Public spaces for the dissemination of information, ecologically sober and accessible to people with disabilities
@@ -884,6 +909,8 @@ en:
favicon: '.png file only'
host: Sans le protocole
sso_button_label: "Default: Sign in via SSO"
+ communication_file_localization:
+ internal_description: Complementary complémentaire of the file. You can write this text to help the users to find a specific file, or learn how it should be used (rights, dates, contexts...).
communication_media_localization:
internal_description: Complementary description of the image for the media library users. You can write this text to help the media library users to find an image, have complementary informations about it or have special instructions regarding the use of the image.
communication_website:
diff --git a/config/locales/communication/fr.yml b/config/locales/communication/fr.yml
index c78c0eae21..8cf34bc136 100644
--- a/config/locales/communication/fr.yml
+++ b/config/locales/communication/fr.yml
@@ -86,6 +86,13 @@ fr:
published_at: Date de publication
slug: Identifiant
title: Titre
+ communication/file/localization:
+ internal_description: Description interne
+ name: Nom
+ original_byte_size: Poids
+ original_content_type: Type de contenu
+ original_filename: Nom du fichier original
+ original_uploaded_file: Fichier original
communication/media:
collection: Collection
height: Hauteur
@@ -334,6 +341,11 @@ fr:
attributes:
published:
cannot_unpublished_default: ne peut pas être dépubliée (langue par défaut)
+ communication/file/localization:
+ attributes:
+ original_uploaded_file:
+ already_imported: déjà importé dans la bibliothèque de fichiers
+ too_big: trop lourd !
communication/media:
attributes:
original_uploaded_file:
@@ -388,6 +400,15 @@ fr:
communication/extranet/post/category:
one: Catégorie
other: Catégories
+ communication/file:
+ one: Fichier
+ other: Fichiers
+ communication/file/category:
+ one: Catégorie
+ other: Catégories
+ communication/file/context:
+ one: Contexte d'utilisation
+ other: Contextes d'utilisation
communication/media:
one: Média
other: Médias
@@ -689,10 +710,14 @@ fr:
description: Espaces d'échanges sécurisés dédiés aux utilisateur·rices authentifié·es
description_non_university: Espaces d'échanges sécurisés dédiés aux utilisateur·rices authentifié·es
title: Extranets
+ file:
+ description: Bibliothèque de fichiers regroupant l'intégralité des fichiers téléchargeables
+ description_non_university: Bibliothèque de fichiers regroupant l'intégralité des fichiers téléchargeables
+ title: Fichiers
media:
- description: Bibliothèque de médias regroupant l'intégralité des fichiers utilisés dans le système
- description_non_university: Bibliothèque de médias regroupant l'intégralité des fichiers utilisés dans le système
- title: Médiathèque
+ description: Bibliothèque de photos regroupant l'intégralité des images utilisés dans le système
+ description_non_university: Bibliothèque de photos regroupant l'intégralité des images utilisés dans le système
+ title: Photos
website:
description: Espaces publics de diffusion d'information, sobre sur le plan écologique et accessible aux personnes en situation de handicap
description_non_university: Espaces publics de diffusion d'information, sobre sur le plan écologique et accessible aux personnes en situation de handicap
@@ -884,6 +909,8 @@ fr:
favicon: 'Fichier .png uniquement'
host: Sans le protocole
sso_button_label: "Par défaut : Se connecter en SSO"
+ communication_file_localization:
+ internal_description: Description complémentaire du fichier. Vous pouvez donner des informations complémentaires sur le fichier, utiles pour la recherche et indiquer des instructions spécifiques d'usage.
communication_media_localization:
internal_description: Description complémentaire de l'image. Vous pouvez donner des informations complémentaires sur l'image, utiles pour la recherche et indiquer des instructions spécifiques d'usage pour les utilisateur·rices de la médiathèque.
communication_website:
diff --git a/config/locales/vue/en.yml b/config/locales/vue/en.yml
index e530258ee8..3bc281ec90 100644
--- a/config/locales/vue/en.yml
+++ b/config/locales/vue/en.yml
@@ -58,7 +58,7 @@ en:
placeholder: Type your search here
previous: Previous images
search: Search
- title: Media library
+ title: Photo library
remove: Remove image
title: Image
timeSlots:
diff --git a/config/locales/vue/fr.yml b/config/locales/vue/fr.yml
index 951c61cc8c..d9cc333a83 100644
--- a/config/locales/vue/fr.yml
+++ b/config/locales/vue/fr.yml
@@ -51,14 +51,14 @@ fr:
text: "L'image envoyée est beaucoup trop lourde, elle pèse {size} Mo ! Sur osuny, nous traitons automatiquement les images de moins de {max} Mo. Au-delà de ce poids, il est nécessaire de réduire l'image avant de l'envoyer, par exemple en utilisant un outil comme iLoveIMG."
title: Image trop lourde
medias:
- button: Choisir dans la médiathèque
+ button: Choisir dans la photothèque
collections: Collections
next: Images suivantes
nothing: Aucun résultat pour cette recherche
placeholder: Tapez votre recherche
previous: Images précédentes
search: Chercher
- title: Médiathèque
+ title: Photothèque
remove: Supprimer l'image
title: Image
timeSlots:
diff --git a/config/navigation/admin/communication/files_navigation.rb b/config/navigation/admin/communication/files_navigation.rb
new file mode 100644
index 0000000000..3c99e5a548
--- /dev/null
+++ b/config/navigation/admin/communication/files_navigation.rb
@@ -0,0 +1,17 @@
+SimpleNavigation::Configuration.run do |navigation|
+ navigation.renderer = ::SimpleNavigation::Renderer::Osuny::FeatureNav
+ navigation.auto_highlight = true
+ navigation.highlight_on_subpath = true
+
+ navigation.items do |primary|
+ primary.item :feature_nav_files,
+ t('communication.description.parts.file.title'),
+ admin_communication_files_path,
+ highlights_on: lambda {
+ controller_name == "files" && action_name == "index"
+ }
+ primary.item :feature_nav_categories,
+ Communication::File::Category.model_name.human(count: 2),
+ admin_communication_file_categories_path if can?(:read, Communication::File::Category)
+ end
+end
diff --git a/config/navigation/admin/communication_navigation.rb b/config/navigation/admin/communication_navigation.rb
index 39722d28a5..667b9b1a35 100644
--- a/config/navigation/admin/communication_navigation.rb
+++ b/config/navigation/admin/communication_navigation.rb
@@ -23,5 +23,8 @@
primary.item :subnav_medias,
t('communication.description.parts.media.title'),
admin_communication_medias_path
+ primary.item :subnav_file,
+ t('communication.description.parts.file.title'),
+ admin_communication_files_path
end
end
diff --git a/config/routes/admin/communication.rb b/config/routes/admin/communication.rb
index acefb09c82..efe10893b7 100644
--- a/config/routes/admin/communication.rb
+++ b/config/routes/admin/communication.rb
@@ -284,5 +284,18 @@
resources :collections, controller: '/admin/communication/medias/collections', as: 'media_collections'
end
end
+ resources :files do
+ collection do
+ post 'pick' => 'files#pick', as: :pick
+ resources :categories, controller: '/admin/communication/files/categories', as: 'file_categories' do
+ collection do
+ post :reorder
+ end
+ member do
+ get :children
+ end
+ end
+ end
+ end
root to: 'dashboard#index'
end
diff --git a/db/migrate/20260626133423_create_communication_files.rb b/db/migrate/20260626133423_create_communication_files.rb
new file mode 100644
index 0000000000..1f00fe216f
--- /dev/null
+++ b/db/migrate/20260626133423_create_communication_files.rb
@@ -0,0 +1,25 @@
+class CreateCommunicationFiles < ActiveRecord::Migration[8.1]
+ def change
+ create_table :communication_files, id: :uuid do |t|
+ t.references :university, null: false, foreign_key: true, type: :uuid
+
+ t.timestamps
+ end
+
+ create_table :communication_file_localizations, id: :uuid do |t|
+ t.string :name
+ t.string :slug
+ t.text :internal_description
+ t.bigint :original_byte_size
+ t.string :original_checksum
+ t.string :original_content_type
+ t.string :original_filename
+ t.references :original_blob, null: false, foreign_key: {to_table: :active_storage_blobs}, type: :uuid
+ t.references :language, null: false, foreign_key: true, type: :uuid
+ t.references :about, null: false, foreign_key: {to_table: :communication_files}, type: :uuid
+ t.references :university, null: false, foreign_key: true, type: :uuid
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20260626145132_create_communication_file_categories.rb b/db/migrate/20260626145132_create_communication_file_categories.rb
new file mode 100644
index 0000000000..c47fe0b22c
--- /dev/null
+++ b/db/migrate/20260626145132_create_communication_file_categories.rb
@@ -0,0 +1,39 @@
+class CreateCommunicationFileCategories < ActiveRecord::Migration[8.1]
+ def change
+ create_table :communication_file_categories, id: :uuid do |t|
+ t.string :bodyclass
+ t.boolean :is_taxonomy, default: false
+ t.integer :position, default: 0
+ t.integer :position_in_tree
+ t.references :university, null: false, foreign_key: true, type: :uuid
+ t.references :parent, foreign_key: {to_table: :communication_file_categories}, type: :uuid
+
+ t.timestamps
+ end
+
+ create_table :communication_file_category_localizations, id: :uuid do |t|
+ t.string :slug
+ t.string :name
+ t.text :featured_image_alt
+ t.text :featured_image_credit
+ t.text :summary
+ t.text :meta_description
+
+ t.references :about, foreign_key: { to_table: :communication_file_categories }, type: :uuid
+ t.references :language, foreign_key: true, type: :uuid
+ t.references :university, foreign_key: true, type: :uuid
+
+ t.timestamps
+
+ t.index [:about_id, :language_id], unique: true
+ end
+
+ create_table :communication_file_categories_files, id: false do |t|
+ t.references :file, null: false, foreign_key: { to_table: :communication_files }, type: :uuid
+ t.references :category, null: false, foreign_key: { to_table: :communication_file_categories }, type: :uuid
+ t.index [:file_id, :category_id], name: 'file_category'
+ end
+
+ end
+
+end
diff --git a/db/schema.rb b/db/schema.rb
index b82b34f883..4e319cda98 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[8.1].define(version: 2026_06_25_100524) do
+ActiveRecord::Schema[8.1].define(version: 2026_06_26_145132) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"
enable_extension "pg_stat_statements"
@@ -18,7 +18,7 @@
enable_extension "pgcrypto"
enable_extension "unaccent"
- create_table "action_text_rich_texts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "action_text_rich_texts", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.text "body"
t.datetime "created_at", null: false
t.string "name", null: false
@@ -45,7 +45,7 @@
t.index ["ip_address", "created_at"], name: "index_active_hashcash_stamps_on_ip_address_and_created_at", where: "(ip_address IS NOT NULL)"
end
- create_table "active_storage_attachments", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "active_storage_attachments", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.uuid "blob_id", null: false
t.datetime "created_at", precision: nil, null: false
t.datetime "deleted_at"
@@ -56,7 +56,7 @@
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: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "active_storage_blobs", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.bigint "byte_size", null: false
t.string "checksum"
t.string "content_type"
@@ -70,7 +70,7 @@
t.index ["university_id"], name: "index_active_storage_blobs_on_university_id"
end
- create_table "active_storage_variant_records", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "active_storage_variant_records", id: :uuid, default: -> { "public.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
@@ -90,7 +90,7 @@
t.index ["university_id"], name: "idx_on_university_id_31eabbc7a7"
end
- create_table "administration_academic_years", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "administration_academic_years", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "deleted_at"
t.uuid "university_id", null: false
@@ -120,7 +120,7 @@
t.index ["university_id"], name: "index_administration_cohort_localizations_on_university_id"
end
- create_table "administration_cohorts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "administration_cohorts", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.uuid "academic_year_id", null: false
t.datetime "created_at", null: false
t.datetime "deleted_at"
@@ -193,7 +193,7 @@
t.index ["education_school_id", "administration_location_id"], name: "index_location_school"
end
- create_table "administration_qualiopi_criterions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "administration_qualiopi_criterions", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.text "description"
t.text "name"
@@ -201,7 +201,7 @@
t.datetime "updated_at", null: false
end
- create_table "administration_qualiopi_indicators", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "administration_qualiopi_indicators", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.uuid "criterion_id", null: false
t.text "glossary"
@@ -215,7 +215,7 @@
t.index ["criterion_id"], name: "index_administration_qualiopi_indicators_on_criterion_id"
end
- create_table "communication_blocks", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "communication_blocks", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.uuid "about_id"
t.string "about_type"
t.uuid "communication_website_id"
@@ -225,6 +225,7 @@
t.string "html_class"
t.jsonb "metadata"
t.string "migration_identifier"
+ t.boolean "native", default: false
t.integer "position", null: false
t.boolean "published", default: true
t.integer "template_kind", default: 0, null: false
@@ -236,7 +237,7 @@
t.index ["university_id", "template_kind"], name: "index_communication_blocks_on_university_id_and_template_kind"
end
- create_table "communication_extranet_connections", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "communication_extranet_connections", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.uuid "about_id"
t.string "about_type"
t.datetime "created_at", null: false
@@ -248,7 +249,7 @@
t.index ["university_id"], name: "index_communication_extranet_connections_on_university_id"
end
- create_table "communication_extranet_document_categories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "communication_extranet_document_categories", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.uuid "extranet_id", null: false
t.uuid "university_id", null: false
@@ -289,7 +290,7 @@
t.index ["university_id"], name: "idx_on_university_id_0dc1259072"
end
- create_table "communication_extranet_document_kinds", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "communication_extranet_document_kinds", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.uuid "extranet_id", null: false
t.uuid "university_id", null: false
@@ -315,7 +316,7 @@
t.index ["university_id"], name: "idx_on_university_id_95419f1df4"
end
- create_table "communication_extranet_documents", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "communication_extranet_documents", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.uuid "category_id"
t.datetime "created_at", null: false
t.uuid "extranet_id", null: false
@@ -351,7 +352,7 @@
t.index ["university_id"], name: "index_communication_extranet_localizations_on_university_id"
end
- create_table "communication_extranet_post_categories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "communication_extranet_post_categories", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.uuid "extranet_id", null: false
t.uuid "university_id", null: false
@@ -398,7 +399,7 @@
t.index ["university_id"], name: "idx_on_university_id_28188e2217"
end
- create_table "communication_extranet_posts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "communication_extranet_posts", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.uuid "author_id"
t.uuid "category_id"
t.datetime "created_at", null: false
@@ -411,7 +412,7 @@
t.index ["university_id"], name: "index_communication_extranet_posts_on_university_id"
end
- create_table "communication_extranets", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "communication_extranets", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.uuid "about_id"
t.string "about_type"
t.string "color"
@@ -439,6 +440,72 @@
t.index ["university_id"], name: "index_communication_extranets_on_university_id"
end
+ create_table "communication_file_categories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ t.string "bodyclass"
+ t.datetime "created_at", null: false
+ t.boolean "is_taxonomy", default: false
+ t.uuid "parent_id"
+ t.integer "position", default: 0
+ t.integer "position_in_tree"
+ t.uuid "university_id", null: false
+ t.datetime "updated_at", null: false
+ t.index ["parent_id"], name: "index_communication_file_categories_on_parent_id"
+ t.index ["university_id"], name: "index_communication_file_categories_on_university_id"
+ end
+
+ create_table "communication_file_categories_files", id: false, force: :cascade do |t|
+ t.uuid "category_id", null: false
+ t.uuid "file_id", null: false
+ t.index ["category_id"], name: "index_communication_file_categories_files_on_category_id"
+ t.index ["file_id", "category_id"], name: "file_category"
+ t.index ["file_id"], name: "index_communication_file_categories_files_on_file_id"
+ end
+
+ create_table "communication_file_category_localizations", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ t.uuid "about_id"
+ t.datetime "created_at", null: false
+ t.text "featured_image_alt"
+ t.text "featured_image_credit"
+ t.uuid "language_id"
+ t.text "meta_description"
+ t.string "name"
+ t.string "slug"
+ t.text "summary"
+ t.uuid "university_id"
+ t.datetime "updated_at", null: false
+ t.index ["about_id", "language_id"], name: "idx_on_about_id_language_id_f3d63b2051", unique: true
+ t.index ["about_id"], name: "index_communication_file_category_localizations_on_about_id"
+ t.index ["language_id"], name: "index_communication_file_category_localizations_on_language_id"
+ t.index ["university_id"], name: "idx_on_university_id_7bebff08b4"
+ end
+
+ create_table "communication_file_localizations", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ t.uuid "about_id", null: false
+ t.datetime "created_at", null: false
+ t.text "internal_description"
+ t.uuid "language_id", null: false
+ t.string "name"
+ t.uuid "original_blob_id", null: false
+ t.bigint "original_byte_size"
+ t.string "original_checksum"
+ t.string "original_content_type"
+ t.string "original_filename"
+ t.string "slug"
+ t.uuid "university_id", null: false
+ t.datetime "updated_at", null: false
+ t.index ["about_id"], name: "index_communication_file_localizations_on_about_id"
+ t.index ["language_id"], name: "index_communication_file_localizations_on_language_id"
+ t.index ["original_blob_id"], name: "index_communication_file_localizations_on_original_blob_id"
+ t.index ["university_id"], name: "index_communication_file_localizations_on_university_id"
+ end
+
+ create_table "communication_files", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ t.datetime "created_at", null: false
+ t.uuid "university_id", null: false
+ t.datetime "updated_at", null: false
+ t.index ["university_id"], name: "index_communication_files_on_university_id"
+ end
+
create_table "communication_media_categories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string "bodyclass"
t.datetime "created_at", null: false
@@ -686,7 +753,7 @@
t.index ["university_id"], name: "idx_on_university_id_bca328e63c"
end
- create_table "communication_website_agenda_events", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "communication_website_agenda_events", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.string "bodyclass"
t.uuid "communication_website_id", null: false
t.datetime "created_at", null: false
@@ -877,7 +944,7 @@
t.index ["university_id"], name: "index_communication_website_alerts_on_university_id"
end
- create_table "communication_website_connections", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "communication_website_connections", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.uuid "direct_source_id"
t.string "direct_source_type"
@@ -933,7 +1000,7 @@
t.index ["university_id"], name: "index_communication_website_git_file_orphans_on_university_id"
end
- create_table "communication_website_git_files", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "communication_website_git_files", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.uuid "about_id"
t.string "about_type"
t.datetime "created_at", null: false
@@ -1079,7 +1146,7 @@
t.index ["university_id"], name: "index_communication_website_localizations_on_university_id"
end
- create_table "communication_website_menu_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "communication_website_menu_items", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.uuid "about_id"
t.string "about_type"
t.datetime "created_at", null: false
@@ -1102,7 +1169,7 @@
t.index ["website_id"], name: "index_communication_website_menu_items_on_website_id"
end
- create_table "communication_website_menus", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "communication_website_menus", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.boolean "automatic", default: true
t.uuid "communication_website_id", null: false
t.datetime "created_at", null: false
@@ -1201,7 +1268,7 @@
t.index ["university_id"], name: "idx_on_university_id_e62b2aba53"
end
- create_table "communication_website_pages", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "communication_website_pages", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.string "bodyclass"
t.uuid "communication_website_id", null: false
t.datetime "created_at", null: false
@@ -1222,7 +1289,7 @@
t.index ["university_id"], name: "index_communication_website_pages_on_university_id"
end
- create_table "communication_website_permalinks", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "communication_website_permalinks", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.uuid "about_id"
t.string "about_type"
t.datetime "created_at", null: false
@@ -1335,7 +1402,7 @@
t.index ["university_id"], name: "idx_on_university_id_ac2f4a0bfc"
end
- create_table "communication_website_post_categories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "communication_website_post_categories", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.string "bodyclass"
t.uuid "communication_website_id", null: false
t.datetime "created_at", null: false
@@ -1423,7 +1490,7 @@
t.index ["unpublication_job_id"], name: "idx_on_unpublication_job_id"
end
- create_table "communication_website_posts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "communication_website_posts", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.string "bodyclass"
t.uuid "communication_website_id", null: false
t.datetime "created_at", null: false
@@ -1458,7 +1525,7 @@
t.index ["communication_website_showcase_tag_id", "communication_website_id"], name: "index_showcase_tag_website"
end
- create_table "communication_websites", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "communication_websites", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.uuid "about_id"
t.string "about_type"
t.string "access_token"
@@ -1549,7 +1616,7 @@
t.index ["university_id"], name: "index_education_diploma_localizations_on_university_id"
end
- create_table "education_diplomas", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "education_diplomas", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.string "certification"
t.datetime "created_at", null: false
t.datetime "deleted_at"
@@ -1648,7 +1715,7 @@
t.index ["university_id"], name: "index_education_program_localizations_on_university_id"
end
- create_table "education_programs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "education_programs", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.boolean "apprenticeship"
t.string "bodyclass"
t.integer "capacity"
@@ -1702,7 +1769,7 @@
t.index ["university_id"], name: "index_education_school_localizations_on_university_id"
end
- create_table "education_schools", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "education_schools", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.string "address"
t.string "city"
t.string "country"
@@ -1717,7 +1784,7 @@
t.index ["university_id"], name: "index_education_schools_on_university_id"
end
- create_table "emergency_messages", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "emergency_messages", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.text "content_en"
t.text "content_fr"
t.datetime "created_at", null: false
@@ -1823,7 +1890,7 @@
t.index ["scheduled_at"], name: "index_good_jobs_on_scheduled_at", where: "(finished_at IS NULL)"
end
- create_table "imports", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "imports", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.integer "kind"
t.uuid "language_id", null: false
@@ -1838,7 +1905,7 @@
t.index ["user_id"], name: "index_imports_on_user_id"
end
- create_table "languages", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "languages", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.string "iso_code"
t.string "name"
@@ -1852,7 +1919,7 @@
t.index ["university_id", "language_id"], name: "index_languages_universities_on_university_id_and_language_id"
end
- create_table "research_hal_authors", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "research_hal_authors", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.string "docid"
t.string "first_name"
@@ -1911,7 +1978,7 @@
t.index ["university_id"], name: "idx_on_university_id_dc9f1267b7"
end
- create_table "research_journal_paper_kinds", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "research_journal_paper_kinds", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "deleted_at"
t.uuid "journal_id", null: false
@@ -1943,7 +2010,7 @@
t.index ["university_id"], name: "index_research_journal_paper_localizations_on_university_id"
end
- create_table "research_journal_papers", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "research_journal_papers", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.date "accepted_at"
t.text "bibliography"
t.datetime "created_at", null: false
@@ -1995,7 +2062,7 @@
t.index ["university_id"], name: "index_research_journal_volume_localizations_on_university_id"
end
- create_table "research_journal_volumes", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "research_journal_volumes", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "deleted_at"
t.integer "number"
@@ -2006,7 +2073,7 @@
t.index ["university_id"], name: "index_research_journal_volumes_on_university_id"
end
- create_table "research_journals", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "research_journals", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "deleted_at"
t.uuid "university_id", null: false
@@ -2014,7 +2081,7 @@
t.index ["university_id"], name: "index_research_journals_on_university_id"
end
- create_table "research_laboratories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "research_laboratories", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.string "address"
t.string "city"
t.string "country"
@@ -2033,7 +2100,7 @@
t.index ["university_person_id", "research_laboratory_id"], name: "laboratory_person"
end
- create_table "research_laboratory_axes", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "research_laboratory_axes", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "deleted_at"
t.integer "position", null: false
@@ -2078,7 +2145,7 @@
t.index ["university_id"], name: "index_research_laboratory_localizations_on_university_id"
end
- create_table "research_publications", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "research_publications", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.text "abstract"
t.text "anr_project_references", default: [], array: true
t.json "authors_citeproc"
@@ -2112,7 +2179,7 @@
t.index ["university_person_id", "research_publication_id"], name: "index_publication_person"
end
- create_table "research_theses", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "research_theses", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.uuid "author_id", null: false
t.boolean "completed", default: false
t.date "completed_at"
@@ -2171,7 +2238,7 @@
t.datetime "updated_at", null: false
end
- create_table "universities", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "universities", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.string "address"
t.boolean "admin_already_auto_promoted", default: false
t.string "city"
@@ -2202,7 +2269,7 @@
t.index ["name"], name: "index_universities_on_name", opclass: :gin_trgm_ops, using: :gin
end
- create_table "university_apps", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "university_apps", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.string "name"
t.string "token"
@@ -2213,7 +2280,7 @@
t.index ["university_id"], name: "index_university_apps_on_university_id"
end
- create_table "university_organization_categories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "university_organization_categories", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.string "bodyclass"
t.datetime "created_at", null: false
t.boolean "is_taxonomy", default: false
@@ -2227,7 +2294,7 @@
t.index ["university_id"], name: "index_university_organization_categories_on_university_id"
end
- create_table "university_organization_categories_organizations", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "university_organization_categories_organizations", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.uuid "category_id", null: false
t.uuid "organization_id", null: false
t.index ["category_id"], name: "idx_on_category_id_7494b991ff"
@@ -2290,7 +2357,7 @@
t.index ["university_id"], name: "index_university_organization_localizations_on_university_id"
end
- create_table "university_organizations", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "university_organizations", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.string "address"
t.string "bodyclass"
t.string "city"
@@ -2299,6 +2366,9 @@
t.uuid "created_by_id"
t.datetime "deleted_at"
t.string "email"
+ t.boolean "is_laboratory", default: false
+ t.boolean "is_location", default: false
+ t.boolean "is_school", default: false
t.integer "kind", default: 10
t.float "latitude"
t.float "longitude"
@@ -2313,7 +2383,7 @@
t.index ["university_id"], name: "index_university_organizations_on_university_id"
end
- create_table "university_people", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "university_people", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.string "address"
t.integer "address_visibility", default: 0
t.date "birthdate"
@@ -2351,14 +2421,14 @@
t.index ["user_id"], name: "index_university_people_on_user_id"
end
- create_table "university_people_person_categories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "university_people_person_categories", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.uuid "category_id", null: false
t.uuid "person_id", null: false
t.index ["category_id"], name: "index_university_people_person_categories_on_category_id"
t.index ["person_id"], name: "index_university_people_person_categories_on_person_id"
end
- create_table "university_person_categories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "university_person_categories", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.string "bodyclass"
t.datetime "created_at", null: false
t.boolean "is_taxonomy", default: false
@@ -2410,7 +2480,7 @@
t.index ["university_id"], name: "idx_on_university_id_1be9c668d5"
end
- create_table "university_person_experiences", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "university_person_experiences", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "deleted_at"
t.integer "from_year"
@@ -2438,7 +2508,7 @@
t.index ["university_id"], name: "idx_on_university_id_0b815cf13a"
end
- create_table "university_person_involvements", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "university_person_involvements", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "deleted_at"
t.integer "kind"
@@ -2497,7 +2567,7 @@
t.index ["university_id"], name: "index_university_role_localizations_on_university_id"
end
- create_table "university_roles", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "university_roles", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "deleted_at"
t.integer "position", null: false
@@ -2509,7 +2579,7 @@
t.index ["university_id"], name: "index_university_roles_on_university_id"
end
- create_table "user_favorites", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "user_favorites", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.uuid "about_id", null: false
t.string "about_type", null: false
t.datetime "created_at", null: false
@@ -2519,7 +2589,21 @@
t.index ["user_id"], name: "index_user_favorites_on_user_id"
end
- create_table "users", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "user_roles", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ t.datetime "created_at", null: false
+ t.integer "role", null: false
+ t.uuid "scope_id"
+ t.string "scope_type"
+ t.uuid "university_id", null: false
+ t.datetime "updated_at", null: false
+ t.uuid "user_id", null: false
+ t.index ["scope_type", "scope_id"], name: "index_user_roles_on_scope"
+ t.index ["university_id"], name: "index_user_roles_on_university_id"
+ t.index ["user_id", "role", "scope_type", "scope_id"], name: "index_user_roles_uniqueness", unique: true
+ t.index ["user_id"], name: "index_user_roles_on_user_id"
+ end
+
+ create_table "users", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.integer "brevo_contact_id"
t.datetime "confirmation_sent_at", precision: nil
t.string "confirmation_token"
@@ -2627,6 +2711,18 @@
add_foreign_key "communication_extranet_posts", "university_people", column: "author_id"
add_foreign_key "communication_extranets", "languages", column: "default_language_id"
add_foreign_key "communication_extranets", "universities"
+ add_foreign_key "communication_file_categories", "communication_file_categories", column: "parent_id"
+ add_foreign_key "communication_file_categories", "universities"
+ add_foreign_key "communication_file_categories_files", "communication_file_categories", column: "category_id"
+ add_foreign_key "communication_file_categories_files", "communication_files", column: "file_id"
+ add_foreign_key "communication_file_category_localizations", "communication_file_categories", column: "about_id"
+ add_foreign_key "communication_file_category_localizations", "languages"
+ add_foreign_key "communication_file_category_localizations", "universities"
+ add_foreign_key "communication_file_localizations", "active_storage_blobs", column: "original_blob_id"
+ add_foreign_key "communication_file_localizations", "communication_files", column: "about_id"
+ add_foreign_key "communication_file_localizations", "languages"
+ add_foreign_key "communication_file_localizations", "universities"
+ add_foreign_key "communication_files", "universities"
add_foreign_key "communication_media_categories", "communication_media_categories", column: "parent_id"
add_foreign_key "communication_media_categories", "universities"
add_foreign_key "communication_media_category_localizations", "communication_media_categories", column: "about_id"
@@ -2902,6 +2998,8 @@
add_foreign_key "university_role_localizations", "university_roles", column: "about_id"
add_foreign_key "university_roles", "universities"
add_foreign_key "user_favorites", "users"
+ add_foreign_key "user_roles", "universities"
+ add_foreign_key "user_roles", "users"
add_foreign_key "users", "languages"
add_foreign_key "users", "universities"
end
diff --git a/spec/models/create_communication_file_spec.rb b/spec/models/create_communication_file_spec.rb
new file mode 100644
index 0000000000..6938f88e42
--- /dev/null
+++ b/spec/models/create_communication_file_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe CreateCommunicationFile, type: :model do
+ pending "add some examples to (or delete) #{__FILE__}"
+end