diff --git a/Gemfile b/Gemfile index f62c636d05..9625f1481c 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,7 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } ruby "3.4.1" gem "activestorage-scaleway-service"#, path: "../activestorage-scaleway-service" +gem "active_hashcash", "~> 0.3.2" gem "active_storage_validations", "~> 1" gem "add_to_calendar" gem "aws-sdk-s3" @@ -39,6 +40,7 @@ gem "i18n_data", "~> 0" gem "i18n_date_range" # gem "i18n_date_range", path: "../../noesya/i18n_date_range" gem "image_processing" +gem "invisible_captcha", "~> 2.3" gem "jbuilder" gem "jquery-rails" gem "jquery-ui-rails", git: "https://github.com/jquery-ui-rails/jquery-ui-rails.git", tag: "v7.0.0" diff --git a/Gemfile.lock b/Gemfile.lock index bac045381f..2a1cf81bce 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -72,6 +72,8 @@ GEM erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) + active_hashcash (0.3.2) + rails (>= 5.2.0) active_storage_validations (1.4.0) activejob (>= 6.1.4) activemodel (>= 6.1.4) @@ -331,6 +333,8 @@ GEM image_processing (1.13.0) mini_magick (>= 4.9.5, < 5) ruby-vips (>= 2.0.17, < 3) + invisible_captcha (2.3.0) + rails (>= 5.2) io-console (0.8.0) irb (1.15.1) pp (>= 0.6.0) @@ -720,6 +724,7 @@ PLATFORMS x86_64-linux DEPENDENCIES + active_hashcash (~> 0.3.2) active_storage_validations (~> 1) activestorage-scaleway-service add_to_calendar @@ -755,6 +760,7 @@ DEPENDENCIES i18n_data (~> 0) i18n_date_range image_processing + invisible_captcha (~> 2.3) jbuilder jquery-rails jquery-ui-rails! diff --git a/app/assets/javascripts/admin/commons/search.js b/app/assets/javascripts/admin/commons/search.js index 8ae0bf8d15..6d9a1dff5a 100644 --- a/app/assets/javascripts/admin/commons/search.js +++ b/app/assets/javascripts/admin/commons/search.js @@ -6,6 +6,9 @@ window.osuny.search = { this.modal = document.getElementById('searchModal'); this.field = document.getElementById('searchField'); this.results = document.getElementById('searchResults'); + if (this.modal === null) { + return; + } this.modal.addEventListener('shown.bs.modal', this.open.bind(this)); this.field.addEventListener('input', this.update.bind(this)); this.request = new XMLHttpRequest(); diff --git a/app/assets/javascripts/devise.js b/app/assets/javascripts/devise.js index 4cd55b2404..de83fc6e86 100644 --- a/app/assets/javascripts/devise.js +++ b/app/assets/javascripts/devise.js @@ -8,6 +8,7 @@ //= require simple_form_bs5_file_input //= require cropperjs/dist/cropper //= require jquery-cropper/dist/jquery-cropper +//= require hashcash //= require_self window.osuny = {}; diff --git a/app/assets/javascripts/extranet.js b/app/assets/javascripts/extranet.js index 9476458dbe..805f7300c7 100644 --- a/app/assets/javascripts/extranet.js +++ b/app/assets/javascripts/extranet.js @@ -11,6 +11,7 @@ //= require simple_form_bs5_file_input //= require summernote/summernote-bs5 //= require autocomplete-rails +//= require hashcash //= require_tree ./application/plugins //= require_tree ./extranet //= require osuny-hugo-theme/script diff --git a/app/controllers/admin/communication/websites/posts_controller.rb b/app/controllers/admin/communication/websites/posts_controller.rb index dc810268b8..5c480a006b 100644 --- a/app/controllers/admin/communication/websites/posts_controller.rb +++ b/app/controllers/admin/communication/websites/posts_controller.rb @@ -20,7 +20,7 @@ def publish_batch target_posts.each do |post| l10n = post.localization_for(current_language) next unless l10n.present? - l10n.publish! + is_published ? l10n.publish! : l10n.unpublish! post.save_and_sync end redirect_back fallback_location: admin_communication_website_posts_path, diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb index b23c6d8fa3..249a381614 100644 --- a/app/controllers/users/registrations_controller.rb +++ b/app/controllers/users/registrations_controller.rb @@ -1,7 +1,11 @@ class Users::RegistrationsController < Devise::RegistrationsController + include ActiveHashcash include Users::AddContextToRequestParams include Users::LayoutChoice + invisible_captcha only: [:create], honeypot: :osuny_verification + + before_action :check_hashcash, only: :create before_action :configure_sign_up_params, only: :create before_action :configure_account_update_params, only: :update before_action :confirm_two_factor_authenticated, except: [:new, :create, :cancel] diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index 166de839e8..2b1b6e3952 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -1,7 +1,12 @@ class Users::SessionsController < Devise::SessionsController + include ActiveHashcash include Users::AddContextToRequestParams include Users::LayoutChoice + invisible_captcha only: [:create], honeypot: :osuny_verification + + before_action :check_hashcash, only: :create + # DELETE /resource/sign_out def destroy current_user.invalidate_all_sessions! diff --git a/app/models/ability/program_manager.rb b/app/models/ability/program_manager.rb index 6f6d297e2e..e8667ce60f 100644 --- a/app/models/ability/program_manager.rb +++ b/app/models/ability/program_manager.rb @@ -65,7 +65,7 @@ def managed_programs_ids end def managed_program_localization_ids - @managed_program_localization_ids ||= Education::Program::Localization.where(about_id: managed_program_ids).pluck(:id) + @managed_program_localization_ids ||= Education::Program::Localization.where(about_id: managed_programs_ids).pluck(:id) end def managed_program_category_ids diff --git a/app/models/communication/block/template/program.rb b/app/models/communication/block/template/program.rb index 31c6c72bdd..314b1cb157 100644 --- a/app/models/communication/block/template/program.rb +++ b/app/models/communication/block/template/program.rb @@ -16,7 +16,7 @@ def selected_programs program = element.program next if program.nil? l10n = program.localization_for(about.language) - next if l10n.draft? + next if l10n.nil? || l10n.draft? element.program }.compact end diff --git a/app/models/communication/extranet/post/category/localization.rb b/app/models/communication/extranet/post/category/localization.rb index 287344bf34..2735de9533 100644 --- a/app/models/communication/extranet/post/category/localization.rb +++ b/app/models/communication/extranet/post/category/localization.rb @@ -47,7 +47,11 @@ def to_s def slug_unavailable?(slug) self.class.unscoped - .where(extranet_id: self.extranet_id, slug: slug) + .where( + extranet_id: self.extranet_id, + language_id: self.language_id, + slug: slug + ) .where.not(id: self.id) .exists? end diff --git a/app/models/communication/extranet/post/localization.rb b/app/models/communication/extranet/post/localization.rb index 9fe1cb879e..e0d18d529f 100644 --- a/app/models/communication/extranet/post/localization.rb +++ b/app/models/communication/extranet/post/localization.rb @@ -44,9 +44,9 @@ class Communication::Extranet::Post::Localization < ApplicationRecord include WithUniversity belongs_to :extranet, class_name: 'Communication::Extranet' - + has_summernote :summary - + validates :title, presence: true before_validation :set_extranet_id, on: :create @@ -62,7 +62,11 @@ def check_accessibility def slug_unavailable?(slug) self.class.unscoped - .where(extranet_id: self.extranet_id, slug: slug) + .where( + extranet_id: self.extranet_id, + language_id: self.language_id, + slug: slug + ) .where.not(id: self.id) .exists? end diff --git a/app/models/communication/website/agenda/event/localization.rb b/app/models/communication/website/agenda/event/localization.rb index 81b4e7a91c..f05740cd77 100644 --- a/app/models/communication/website/agenda/event/localization.rb +++ b/app/models/communication/website/agenda/event/localization.rb @@ -11,6 +11,7 @@ # header_cta_url :string # meta_description :string # migration_identifier :string +# notes :text # published :boolean default(FALSE) # published_at :datetime # slug :string diff --git a/app/models/communication/website/page.rb b/app/models/communication/website/page.rb index 449e31205a..5778cbeb80 100644 --- a/app/models/communication/website/page.rb +++ b/app/models/communication/website/page.rb @@ -4,7 +4,6 @@ # # id :uuid not null, primary key # bodyclass :string -# design_options :jsonb # full_width :boolean default(FALSE) # migration_identifier :string # position :integer default(0), not null diff --git a/app/models/concerns/as_localized_tree.rb b/app/models/concerns/as_localized_tree.rb index 5ad31fe2c7..2032cb45e4 100644 --- a/app/models/concerns/as_localized_tree.rb +++ b/app/models/concerns/as_localized_tree.rb @@ -6,7 +6,9 @@ module AsLocalizedTree joins(:about).where(about_table_name => { parent_id: nil }) } scope :ordered, -> (language = nil) { - joins(:about).order("#{about_table_name}.position") + # Use the ordered scope from the about class + joins(:about) + .merge(about_class.ordered(language)) } end diff --git a/app/models/concerns/duplicable.rb b/app/models/concerns/duplicable.rb index eb55669af5..7f6648421d 100644 --- a/app/models/concerns/duplicable.rb +++ b/app/models/concerns/duplicable.rb @@ -27,6 +27,8 @@ def duplicate_localizations_for(instance) instance_l10n.about = instance # note: fragile. It only works because every duplicate objects currently has a "title" property. instance_l10n.title = I18n.t('copy_of', title: l10n.title) + instance_l10n.published = false if instance_l10n.respond_to?(:published) + instance_l10n.published_at = nil if instance_l10n.respond_to?(:published_at) instance_l10n.save duplicate_featured_image(l10n, instance_l10n) duplicate_blocks(l10n, instance_l10n) diff --git a/app/models/concerns/with_publication.rb b/app/models/concerns/with_publication.rb index 95e5d92e24..e9435e9f0b 100644 --- a/app/models/concerns/with_publication.rb +++ b/app/models/concerns/with_publication.rb @@ -33,6 +33,12 @@ def publish! save end + def unpublish! + self.published = false + self.published_at = nil + save + end + def draft? !published end diff --git a/app/models/education/program.rb b/app/models/education/program.rb index 11d9af5e13..32f5b7aa24 100644 --- a/app/models/education/program.rb +++ b/app/models/education/program.rb @@ -31,7 +31,7 @@ class Education::Program < ApplicationRecord include Filterable include Categorizable # Must be loaded after Filterable to be filtered by categories include Localizable - include LocalizableOrderBySlugScope + include LocalizableOrderByNameScope include Sanitizable include Searchable include WebsitesLinkable diff --git a/app/models/education/program/localization.rb b/app/models/education/program/localization.rb index ed1a803203..823ae6711e 100644 --- a/app/models/education/program/localization.rb +++ b/app/models/education/program/localization.rb @@ -53,7 +53,7 @@ # class Education::Program::Localization < ApplicationRecord include AsLocalization - include AsLocalizedTree + include AsLocalizedTree # ordered scope is overridden below include Contentful include Initials include Pathable @@ -92,6 +92,8 @@ class Education::Program::Localization < ApplicationRecord validates :downloadable_summary, size: { less_than: 50.megabytes } validates :logo, size: { less_than: 5.megabytes } + scope :ordered, -> (language = nil) { order(:slug) } + def git_path(website) return unless published? && for_website?(website) clean_path = Static.clean_path "#{git_path_content_prefix(website)}programs/#{path}/" diff --git a/app/models/education/program/with_websites_categories.rb b/app/models/education/program/with_websites_categories.rb index f47a1dbbfc..41a189ff3b 100644 --- a/app/models/education/program/with_websites_categories.rb +++ b/app/models/education/program/with_websites_categories.rb @@ -12,6 +12,10 @@ module Education::Program::WithWebsitesCategories class_name: 'Communication::Website::Portfolio::Category', dependent: :destroy + has_many :website_page_categories, + class_name: 'Communication::Website::Page::Category', + dependent: :destroy + has_many :website_post_categories, class_name: 'Communication::Website::Post::Category', dependent: :destroy @@ -26,6 +30,7 @@ def website_category_l10ns_for(website, language) categories[:events] = website_agenda_categories.find_by(communication_website_id: website.id)&.localization_for(language) categories[:posts] = website_post_categories.find_by(communication_website_id: website.id)&.localization_for(language) categories[:projects] = website_portfolio_categories.find_by(communication_website_id: website.id)&.localization_for(language) + categories[:pages] = website_page_categories.find_by(communication_website_id: website.id)&.localization_for(language) categories.compact end end diff --git a/app/views/admin/application/categories/widget/_static.html.erb b/app/views/admin/application/categories/widget/_static.html.erb index da6b50e5cf..07ac09cef8 100644 --- a/app/views/admin/application/categories/widget/_static.html.erb +++ b/app/views/admin/application/categories/widget/_static.html.erb @@ -29,6 +29,7 @@ taxonomies: if free_categories_content.present? %> - name: "<%= t('category.title') %>" slug: "" + path: "" categories: <%= sanitize free_categories_content %> <% end %> @@ -47,6 +48,7 @@ taxonomies: if taxonomy_content.present? %> - name: "<%= prepare_text_for_static taxonomy_l10n.to_s %>" slug: "<%= hugo.slug %>" + path: "<%= hugo.path %>" categories: <%= sanitize taxonomy_content %> <% diff --git a/app/views/admin/communication/blocks/edit.html.erb b/app/views/admin/communication/blocks/edit.html.erb index eb0a39bdd0..2dc6ba4d83 100644 --- a/app/views/admin/communication/blocks/edit.html.erb +++ b/app/views/admin/communication/blocks/edit.html.erb @@ -11,7 +11,7 @@
#<%= slug %><% - if about.is_direct_object? + if about.try(:is_direct_object?) website = about.website hugo = about_l10n.hugo(website) %> @@ -266,9 +266,9 @@ window.addEventListener('load', function(){ setTimeout(function() { app.mount('#app'); - + $("#block-form").on("ajax:success", function(e) { - let blockIdentifier = "<%= @block.id %>", + let blockIdentifier = "<%= @block.id %>", blockPath = "<%= admin_communication_block_path(@block) %>"; parent.window.osuny.contentEditor.offcanvas.onBlockSave(blockIdentifier, blockPath); }); diff --git a/app/views/admin/communication/medias/contexts/_context.html.erb b/app/views/admin/communication/medias/contexts/_context.html.erb index 41560c439e..d72df9b237 100644 --- a/app/views/admin/communication/medias/contexts/_context.html.erb +++ b/app/views/admin/communication/medias/contexts/_context.html.erb @@ -9,6 +9,7 @@ partial = "admin/communication/medias/contexts/#{key}" <% rescue %> <% object = context.about +next if object.nil? # If it's a localization, get real object if object.respond_to?(:about) object = object.about diff --git a/app/views/admin/communication/websites/pages/static/_section_taxonomies.html.erb b/app/views/admin/communication/websites/pages/static/_section_taxonomies.html.erb index c5c11416e0..b48dbc0c68 100644 --- a/app/views/admin/communication/websites/pages/static/_section_taxonomies.html.erb +++ b/app/views/admin/communication/websites/pages/static/_section_taxonomies.html.erb @@ -15,6 +15,7 @@ section_taxonomies: <% if free_categories_content.present? %> - name: "<%= t('category.title') %>" slug: "" + path: "" categories: <%= sanitize free_categories_content %> <% end %> @@ -32,6 +33,7 @@ section_taxonomies: if taxonomy_content.present? %> - name: "<%= prepare_text_for_static taxonomy_l10n.to_s %>" slug: "<%= hugo.slug %>" + path: "<%= hugo.path %>" categories: <%= sanitize taxonomy_content %><% end diff --git a/app/views/admin/search/index.html.erb b/app/views/admin/search/index.html.erb index 42df22287b..5a1d426412 100644 --- a/app/views/admin/search/index.html.erb +++ b/app/views/admin/search/index.html.erb @@ -5,8 +5,7 @@ <% @results.each do |result| object = result.about_object - next unless can?(:read, object) - l10n = result.about_localization + next unless object.present? && can?(:read, object) # communication_website_post path_class = object.class.polymorphic_name.parameterize.underscore # admin_communication_website_post_path diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb index a5d860a8f8..9f27868cdd 100644 --- a/app/views/devise/registrations/new.html.erb +++ b/app/views/devise/registrations/new.html.erb @@ -8,6 +8,8 @@