# frozen_string_literal: true module Decidim module Admin module ContentBlocks # A command that reorders a collection of content blocks # the ones that might be missing. class ReorderContentBlocks < Decidim::Command # Public: Initializes the command. # # organization - the Organization where the content blocks reside # scope - the scope applied to the content blocks # order - an Array holding the order of IDs of published content blocks. # scoped_resource_id - (optional) The id of the resource the content blocks belongs to. def initialize(organization, scope, order, scoped_resource_id = nil) @organization = organization @scope = scope @order = order @scoped_resource_id = scoped_resource_id end # Executes the command. Broadcasts these events: # # - :ok when everything is valid. # - :invalid if the data was not valid and we could not proceed. # # Returns nothing. def call return broadcast(:invalid) if order.blank? reorder_steps broadcast(:ok) end private attr_reader :organization, :scope, :scoped_resource_id def reorder_steps transaction do reset_weights collection.reload set_new_weights unpublish_removed_content_blocks publish_appearing_content_blocks end end def reset_weights # rubocop:disable Rails/SkipsModelValidations collection.where.not(weight: nil).update_all(weight: nil) # rubocop:enable Rails/SkipsModelValidations end def set_new_weights data = order.each_with_index.inject({}) do |hash, (id, index)| hash.update(id => index + 1) end data.each do |id, weight| content_block = collection.find_by(id:) content_block.update!(weight:) if content_block.present? end end # rubocop:disable Rails/SkipsModelValidations def unpublish_removed_content_blocks collection.where(weight: nil).update_all(published_at: nil) end def publish_appearing_content_blocks collection.where(published_at: nil).where.not(weight: nil).update_all(published_at: Time.current) end # rubocop:enable Rails/SkipsModelValidations def order return nil unless @order.is_a?(Array) && @order.present? @order end def collection @collection ||= Decidim::ContentBlock.for_scope(scope, organization:).where(scoped_resource_id:) end end end end end