diff --git a/app/assets/javascripts/admin/notyf.js b/app/assets/javascripts/admin/notyf.js index e5fb41244591bc9c3d6d8a2a0335c75adabce716..68499577b5c1695a8ec1a79dc9659ebb2c3f0909 100644 --- a/app/assets/javascripts/admin/notyf.js +++ b/app/assets/javascripts/admin/notyf.js @@ -7,8 +7,8 @@ if (notyfAlerts.length > 0) { notyf.open({ type: 'error', position: { - x: 'right', - y: 'top' + x: 'center', + y: 'bottom' }, message: notyfAlerts[0].innerText, duration: 9000, diff --git a/app/assets/javascripts/admin/sortable.js b/app/assets/javascripts/admin/sortable.js new file mode 100644 index 0000000000000000000000000000000000000000..e34e6fbfd08c626604e59e291d180ac10a2ed92b --- /dev/null +++ b/app/assets/javascripts/admin/sortable.js @@ -0,0 +1,26 @@ +/*global $, Sortable */ +$(function () { + 'use strict'; + // Re-order elements of a table. Needs a "table-sortable" class on the table, a "data-reorder-url" param on the tbody and a "data-id" param on each tr + var nestedSortables = [].slice.call(document.querySelectorAll('.table-sortable tbody')), + i; + for (i = 0; i < nestedSortables.length; i += 1) { + new Sortable(nestedSortables[i], { + handle: '.handle', + group: 'nested', + animation: 150, + fallbackOnBody: true, + swapThreshold: 0.65, + onEnd: function (evt) { + var to = evt.to, + ids = [], + url = $(to).attr('data-reorder-url'); + // get list of ids + $('> tr', to).each(function () { + ids.push($(this).attr('data-id')); + }); + $.post(url, { ids: ids }); + } + }); + } +}); diff --git a/app/controllers/admin/communication/website/categories_controller.rb b/app/controllers/admin/communication/website/categories_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..e4ed189322b48bf825a59f7a31cccf10fc940eed --- /dev/null +++ b/app/controllers/admin/communication/website/categories_controller.rb @@ -0,0 +1,64 @@ +class Admin::Communication::Website::CategoriesController < Admin::Communication::Website::ApplicationController + load_and_authorize_resource class: Communication::Website::Category + + include Admin::Reorderable + + def index + @categories = @website.categories.ordered + breadcrumb + end + + def show + breadcrumb + end + + def new + @category.website = @website + breadcrumb + end + + def edit + breadcrumb + add_breadcrumb t('edit') + end + + def create + @category.university = current_university + @category.website = @website + if @category.save + redirect_to admin_communication_website_category_path(@category), notice: t('admin.successfully_created_html', model: @category.to_s) + else + breadcrumb + render :new, status: :unprocessable_entity + end + end + + def update + if @category.update(category_params) + redirect_to admin_communication_website_category_path(@category), notice: t('admin.successfully_updated_html', model: @category.to_s) + else + breadcrumb + add_breadcrumb t('edit') + render :edit, status: :unprocessable_entity + end + end + + def destroy + @category.destroy + redirect_to admin_communication_website_categories_url, notice: t('admin.successfully_destroyed_html', model: @category.to_s) + end + + protected + + def breadcrumb + super + add_breadcrumb Communication::Website::Category.model_name.human(count: 2), + admin_communication_website_categories_path + breadcrumb_for @category + end + + def category_params + params.require(:communication_website_category) + .permit(:university_id, :website_id, :name, :description) + end +end diff --git a/app/controllers/admin/communication/website/posts_controller.rb b/app/controllers/admin/communication/website/posts_controller.rb index 14abbb9696e8ea2d92e8ecf91a8052e3b9bfed28..1358b60c5cb0026e5fd56c979467d73cabf80fb4 100644 --- a/app/controllers/admin/communication/website/posts_controller.rb +++ b/app/controllers/admin/communication/website/posts_controller.rb @@ -57,6 +57,6 @@ class Admin::Communication::Website::PostsController < Admin::Communication::Web def post_params params.require(:communication_website_post) - .permit(:university_id, :website_id, :title, :description, :text, :published, :published_at, :featured_image, :featured_image_delete, :featured_image_infos, :slug) + .permit(:university_id, :website_id, :title, :description, :text, :published, :published_at, :featured_image, :featured_image_delete, :featured_image_infos, :slug, category_ids: []) end end diff --git a/app/controllers/concerns/admin/reorderable.rb b/app/controllers/concerns/admin/reorderable.rb new file mode 100644 index 0000000000000000000000000000000000000000..2ce07bf582985548f7f69d1f07f0363034e669e6 --- /dev/null +++ b/app/controllers/concerns/admin/reorderable.rb @@ -0,0 +1,18 @@ +module Admin::Reorderable + extend ActiveSupport::Concern + + included do + def reorder + ids = params[:ids] + ids.each.with_index do |id, index| + object = model.find_by(id: id) + object.update_column(:position, index + 1) unless object.nil? + end + end + + def model + self.class.to_s.remove('Admin::').remove('Controller').singularize.safe_constantize + end + end + +end diff --git a/app/models/communication/website.rb b/app/models/communication/website.rb index c41cac826474e3dfc420fe3c9bb66f92960ab53a..eac3c8285a7b9cbfe9873ea92db139369f64e8ab 100644 --- a/app/models/communication/website.rb +++ b/app/models/communication/website.rb @@ -27,6 +27,7 @@ class Communication::Website < ApplicationRecord belongs_to :about, polymorphic: true, optional: true has_many :pages, foreign_key: :communication_website_id has_many :posts, foreign_key: :communication_website_id + has_many :categories, class_name: 'Communication::Website::Category', foreign_key: :communication_website_id has_one :imported_website, class_name: 'Communication::Website::Imported::Website', dependent: :destroy diff --git a/app/models/communication/website/category.rb b/app/models/communication/website/category.rb new file mode 100644 index 0000000000000000000000000000000000000000..8f8fec3a190be72815628ff8da92fa582ab7be89 --- /dev/null +++ b/app/models/communication/website/category.rb @@ -0,0 +1,58 @@ +# == Schema Information +# +# Table name: communication_website_categories +# +# id :uuid not null, primary key +# description :text +# name :string +# position :integer +# created_at :datetime not null +# updated_at :datetime not null +# communication_website_id :uuid not null +# university_id :uuid not null +# +# Indexes +# +# idx_communication_website_post_cats_on_communication_website_id (communication_website_id) +# index_communication_website_categories_on_university_id (university_id) +# +# Foreign Keys +# +# fk_rails_... (communication_website_id => communication_websites.id) +# fk_rails_... (university_id => universities.id) +# +class Communication::Website::Category < ApplicationRecord + + belongs_to :university + belongs_to :website, + foreign_key: :communication_website_id + has_and_belongs_to_many :posts, + class_name: 'Communication::Website::Post', + join_table: 'communication_website_categories_posts', + foreign_key: 'communication_website_category_id', + association_foreign_key: 'communication_website_post_id' + + validates :name, presence: true + + scope :ordered, -> { order(:position) } + + before_create :set_position + + + def to_s + "#{name}" + end + + protected + + def set_position + last_element = website.categories.ordered.last + + unless last_element.nil? + self.position = last_element.position + 1 + else + self.position = 1 + end + end + +end diff --git a/app/models/communication/website/post.rb b/app/models/communication/website/post.rb index dd723da99ed0df46e3529fe9a0dd6deb5c907665..42fd81f8ca215fd88dbca5788c68ea89b29f1569 100644 --- a/app/models/communication/website/post.rb +++ b/app/models/communication/website/post.rb @@ -36,6 +36,11 @@ class Communication::Website::Post < ApplicationRecord belongs_to :university belongs_to :website, foreign_key: :communication_website_id + has_and_belongs_to_many :categories, + class_name: 'Communication::Website::Category', + join_table: 'communication_website_categories_posts', + foreign_key: 'communication_website_post_id', + association_foreign_key: 'communication_website_category_id' scope :ordered, -> { order(published_at: :desc, created_at: :desc) } scope :recent, -> { order(published_at: :desc).limit(5) } diff --git a/app/views/admin/communication/website/categories/_form.html.erb b/app/views/admin/communication/website/categories/_form.html.erb new file mode 100644 index 0000000000000000000000000000000000000000..1d988e3fa744856c5353c2aef0df3e1a7ce668c2 --- /dev/null +++ b/app/views/admin/communication/website/categories/_form.html.erb @@ -0,0 +1,18 @@ +<%= simple_form_for [:admin, category] do |f| %> + <div class="row"> + <div class="col-md-8"> + <div class="card flex-fill w-100"> + <div class="card-header"> + <h5 class="card-title mb-0"><%= t('communication.website.content') %></h5> + </div> + <div class="card-body"> + <%= f.input :name %> + <%= f.input :description %> + </div> + </div> + </div> + </div> + <% content_for :action_bar_right do %> + <%= submit f %> + <% end %> +<% end %> diff --git a/app/views/admin/communication/website/categories/_list.html.erb b/app/views/admin/communication/website/categories/_list.html.erb new file mode 100644 index 0000000000000000000000000000000000000000..dad41187f932bc67d1d0a660a9a80ac7a5156b2d --- /dev/null +++ b/app/views/admin/communication/website/categories/_list.html.erb @@ -0,0 +1,27 @@ +<table class="<%= table_classes %> table-sortable"> + <thead> + <tr> + <% if can? :reorder, Communication::Website::Category %> + <th width="20"> </th> + <% end %> + <th><%= Communication::Website::Category.human_attribute_name('title') %></th> + <th></th> + </tr> + </thead> + <tbody data-reorder-url="<%= reorder_admin_communication_website_categories_path(@website) %>"> + <% categories.each do |category| %> + <tr data-id="<%= category.id %>"> + <% if can? :reorder, Communication::Website::Category %> + <td><i class="fa fa-bars handle"></i></td> + <% end %> + <td><%= link_to category, admin_communication_website_category_path(website_id: category.website.id, id: category.id) %></td> + <td class="text-end"> + <div class="btn-group" role="group"> + <%= edit_link category %> + <%= destroy_link category %> + </div> + </td> + </tr> + <% end %> + </tbody> +</table> diff --git a/app/views/admin/communication/website/categories/edit.html.erb b/app/views/admin/communication/website/categories/edit.html.erb new file mode 100644 index 0000000000000000000000000000000000000000..123bc888b09752ba30b4db4eba62ec37bcbd07d2 --- /dev/null +++ b/app/views/admin/communication/website/categories/edit.html.erb @@ -0,0 +1,3 @@ +<% content_for :title, @category %> + +<%= render 'form', category: @category %> diff --git a/app/views/admin/communication/website/categories/index.html.erb b/app/views/admin/communication/website/categories/index.html.erb new file mode 100644 index 0000000000000000000000000000000000000000..bb47a5f4750937fd78a5d7a2c2141a87ae266782 --- /dev/null +++ b/app/views/admin/communication/website/categories/index.html.erb @@ -0,0 +1,7 @@ +<% content_for :title, "#{Communication::Website::Category.model_name.human(count: 2)} (#{@categories.count})" %> + +<%= render 'list', categories: @categories %> + +<% content_for :action_bar_right do %> + <%= create_link Communication::Website::Category %> +<% end %> diff --git a/app/views/admin/communication/website/categories/new.html.erb b/app/views/admin/communication/website/categories/new.html.erb new file mode 100644 index 0000000000000000000000000000000000000000..f92532bde4aa34d077cb23a65cfdc18643b689a7 --- /dev/null +++ b/app/views/admin/communication/website/categories/new.html.erb @@ -0,0 +1,3 @@ +<% content_for :title, Communication::Website::Category.model_name.human %> + +<%= render 'form', category: @category %> diff --git a/app/views/admin/communication/website/categories/show.html.erb b/app/views/admin/communication/website/categories/show.html.erb new file mode 100644 index 0000000000000000000000000000000000000000..96aa0af3fe3d008b278a5d1891db34d087e95f12 --- /dev/null +++ b/app/views/admin/communication/website/categories/show.html.erb @@ -0,0 +1,21 @@ +<% content_for :title, @category %> + +<div class="row"> + <div class="col-md-8"> + <div class="card flex-fill w-100"> + <div class="card-header"> + <h5 class="card-title mb-0"><%= t('communication.website.content') %></h5> + </div> + <div class="card-body"> + <p> + <strong><%= Communication::Website::Category.human_attribute_name('description') %></strong> + </p> + <%= sanitize @category.description %> + </div> + </div> + </div> +</div> + +<% content_for :action_bar_right do %> + <%= edit_link @category %> +<% end %> diff --git a/app/views/admin/communication/website/posts/_form.html.erb b/app/views/admin/communication/website/posts/_form.html.erb index 004e6c9ceb09c9518df16b31dae302ffb1955ada..97b5236b11575dfb82166af6eea7082ca9ee756d 100644 --- a/app/views/admin/communication/website/posts/_form.html.erb +++ b/app/views/admin/communication/website/posts/_form.html.erb @@ -21,6 +21,7 @@ <%= f.input :slug, as: :string %> <%= f.input :published %> <%= f.input :published_at, html5: true %> + <%= f.association :categories, as: :check_boxes if @website.categories.any? %> </div> </div> <div class="card flex-fill w-100"> diff --git a/app/views/admin/communication/website/posts/show.html.erb b/app/views/admin/communication/website/posts/show.html.erb index be2b0b9a42fbb730d07e332ff7a4492763b10465..2a95763dd05bd20a4a513c76a79e0298a8f1e4c6 100644 --- a/app/views/admin/communication/website/posts/show.html.erb +++ b/app/views/admin/communication/website/posts/show.html.erb @@ -38,6 +38,18 @@ <% end %> </td> </tr> + <% if @post.categories.any? %> + <tr> + <td><%= Communication::Website::Post.human_attribute_name('categories') %></td> + <td> + <ul class="list-unstyled mb-0"> + <% @post.categories.each do |category| %> + <li><%= link_to_if can?(:read, category), category, [:admin, category] %></li> + <% end %> + </ul> + </td> + </tr> + <% end %> </tbody> </table> </div> diff --git a/app/views/admin/communication/websites/show.html.erb b/app/views/admin/communication/websites/show.html.erb index 074e302de7c4299b43a6884f8130734542370f8c..8f372b3bcb2e39958ecfddf1000bea39a8ed3ee8 100644 --- a/app/views/admin/communication/websites/show.html.erb +++ b/app/views/admin/communication/websites/show.html.erb @@ -30,6 +30,7 @@ </div> <%= render 'admin/communication/website/posts/list', posts: @website.posts.recent %> </div> + <div class="card mt-5"> <div class="card-header"> <div class="float-end"> @@ -50,6 +51,21 @@ <%= render 'admin/communication/website/pages/list', pages: @website.pages.recent %> </div> +<div class="card mt-5"> + <div class="card-header"> + <div class="float-end"> + <%= link_to t('create'), + new_admin_communication_website_category_path(website_id: @website), + class: button_classes %> + </div> + <h2 class="card-title"> + <%= Communication::Website::Category.model_name.human(count: 2) %> + <%= "(#{@website.categories.count})" %> + </h2> + </div> + <%= render 'admin/communication/website/categories/list', categories: @website.categories.ordered %> +</div> + <% content_for :action_bar_right do %> <% if @website.imported? %> diff --git a/config/locales/communication/en.yml b/config/locales/communication/en.yml index b04f9923b29609e13850955103ba4d13df13e90c..52cb38cbc2ef08eed38723a066b8b39b4281a61d 100644 --- a/config/locales/communication/en.yml +++ b/config/locales/communication/en.yml @@ -25,6 +25,13 @@ en: communication/website: one: Website other: Websites + communication/website/category: + one: Category + other: Categories + all: All categories + communication/website/imported/website: + one: Imported website + other: Imported websites communication/website/page: one: Page other: Pages @@ -33,17 +40,18 @@ en: one: Post other: Posts all: All posts - communication/website/imported/website: - one: Imported website - other: Imported websites attributes: communication/website: + categories: Categories name: Name domain: Domain about_type: About about_: Independent website (no specific subject) about_Research::Journal: Journal website about_Education::School: School website + communication/website/category: + description: Description + title: Title communication/website/imported/medium: filename: Filename communication/website/page: diff --git a/config/locales/communication/fr.yml b/config/locales/communication/fr.yml index cfd9c6d99e1541e7e60c3e08a574af44cedd0534..ae1d3c0a4f86a07cd299721e0e2c94c358e094c4 100644 --- a/config/locales/communication/fr.yml +++ b/config/locales/communication/fr.yml @@ -25,6 +25,13 @@ fr: communication/website: one: Site Web other: Sites Web + communication/website/category: + one: Catégorie + other: Catégories + all: Toutes les catégories + communication/website/imported/website: + one: Site importé + other: Sites importés communication/website/page: one: Page other: Pages @@ -33,9 +40,6 @@ fr: one: Actualité other: Actualités all: Toutes les actualités - communication/website/imported/website: - one: Site importé - other: Sites importés attributes: communication/website: name: Nom @@ -44,6 +48,9 @@ fr: about_: Site indépendant (aucun sujet) about_Research::Journal: Site de revue scientifique about_Education::School: Site d'école + communication/website/category: + description: Description + title: Titre communication/website/imported/medium: filename: Nom du fichier communication/website/page: @@ -55,6 +62,7 @@ fr: title: Titre website: Site Web communication/website/post: + categories: Categories description: Description (SEO) featured_image: Image à la une published: Publié ? diff --git a/config/routes/admin/communication.rb b/config/routes/admin/communication.rb index 98e4d278f3283e539df38fee03866e27ad678d29..ece4457558c25348270668532e66de271dab4e7d 100644 --- a/config/routes/admin/communication.rb +++ b/config/routes/admin/communication.rb @@ -12,6 +12,11 @@ namespace :communication do get :children end end + resources :categories, controller: 'website/categories' do + collection do + post :reorder + end + end resources :posts, controller: 'website/posts' end end diff --git a/db/migrate/20211026124139_create_communication_website_categories.rb b/db/migrate/20211026124139_create_communication_website_categories.rb new file mode 100644 index 0000000000000000000000000000000000000000..febee26b527912f20c22fb12a9e0eed423c1b343 --- /dev/null +++ b/db/migrate/20211026124139_create_communication_website_categories.rb @@ -0,0 +1,16 @@ +class CreateCommunicationWebsiteCategories < ActiveRecord::Migration[6.1] + def change + create_table :communication_website_categories, id: :uuid do |t| + t.references :university, null: false, foreign_key: true, type: :uuid + t.references :communication_website, + null: false, + foreign_key: { to_table: :communication_websites }, + type: :uuid, + index: { name: 'idx_communication_website_post_cats_on_communication_website_id' } + t.string :name + t.text :description + t.integer :position + t.timestamps + end + end +end diff --git a/db/migrate/20211026142142_create_join_table_website_posts_categories.rb b/db/migrate/20211026142142_create_join_table_website_posts_categories.rb new file mode 100644 index 0000000000000000000000000000000000000000..82d4186620080ad767fd8136dab13396d6b79498 --- /dev/null +++ b/db/migrate/20211026142142_create_join_table_website_posts_categories.rb @@ -0,0 +1,8 @@ +class CreateJoinTableWebsitePostsCategories < ActiveRecord::Migration[6.1] + def change + create_join_table :communication_website_posts, :communication_website_categories, column_options: {type: :uuid} do |t| + t.index [:communication_website_post_id, :communication_website_category_id], name: 'post_category' + t.index [:communication_website_category_id, :communication_website_post_id], name: 'category_post' + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 303bcfee2efd01c90219c4c8975c3c60550b4eb9..3b75d9b9f394c2c1f2e1f9c1611697d822dbdb7b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2021_10_26_094556) do +ActiveRecord::Schema.define(version: 2021_10_26_142142) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" @@ -78,6 +78,25 @@ ActiveRecord::Schema.define(version: 2021_10_26_094556) do t.index ["criterion_id"], name: "index_administration_qualiopi_indicators_on_criterion_id" end + create_table "communication_website_categories", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + t.uuid "university_id", null: false + t.uuid "communication_website_id", null: false + t.string "name" + t.text "description" + t.integer "position" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["communication_website_id"], name: "idx_communication_website_post_cats_on_communication_website_id" + t.index ["university_id"], name: "index_communication_website_categories_on_university_id" + end + + create_table "communication_website_categories_posts", id: false, force: :cascade do |t| + t.uuid "communication_website_post_id", null: false + t.uuid "communication_website_category_id", null: false + t.index ["communication_website_category_id", "communication_website_post_id"], name: "category_post" + t.index ["communication_website_post_id", "communication_website_category_id"], name: "post_category" + end + create_table "communication_website_imported_media", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| t.string "identifier" t.jsonb "data" @@ -386,6 +405,8 @@ ActiveRecord::Schema.define(version: 2021_10_26_094556) do add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" add_foreign_key "administration_qualiopi_indicators", "administration_qualiopi_criterions", column: "criterion_id" + add_foreign_key "communication_website_categories", "communication_websites" + add_foreign_key "communication_website_categories", "universities" add_foreign_key "communication_website_imported_media", "communication_website_imported_websites", column: "website_id" add_foreign_key "communication_website_imported_media", "universities" add_foreign_key "communication_website_imported_pages", "communication_website_imported_media", column: "featured_medium_id"