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