diff --git a/app/controllers/api/lessons_controller.rb b/app/controllers/api/lessons_controller.rb index 305b2a632..b167e3d1b 100644 --- a/app/controllers/api/lessons_controller.rb +++ b/app/controllers/api/lessons_controller.rb @@ -4,7 +4,7 @@ module Api class LessonsController < ApiController before_action :authorize_user, except: %i[index show] before_action :verify_school_class_belongs_to_school, only: :create - load_and_authorize_resource :lesson + load_and_authorize_resource :lesson, except: [:create_lesson_with_remix] def index archive_scope = params[:include_archived] == 'true' ? Lesson : Lesson.unarchived @@ -41,6 +41,21 @@ def create_copy end end + def create_lesson_with_remix + remix_origin = request.origin || request.referer + + puts("lesson_params: #{lesson_params}") + + result = Lesson::CreateRemix.call(lesson_params: lesson_params, remix_origin:) + + if result.success? + @lesson_with_user = result[:lesson].with_user + render :show, formats: [:json], status: :created + else + render json: { error: result[:error] }, status: :unprocessable_entity + end + end + def update # TODO: Consider removing user_id from the lesson_params for update so users can update other users' lessons without changing ownership # OR consider dropping user_id on lessons and using teacher id/ids on the class instead @@ -75,9 +90,16 @@ def verify_school_class_belongs_to_school end def lesson_params + puts("base_params: #{base_params}") base_params.merge(user_id: current_user.id) end + def remix_lesson_params + lesson_params.merge(params.fetch(:lesson, {}).permit( + { project_attributes: [:identifier] } + )) + end + def base_params params.fetch(:lesson, {}).permit( :school_id, @@ -86,6 +108,7 @@ def base_params :description, :visibility, :due_date, + :project_identifier, { project_attributes: [ :name, diff --git a/app/models/ability.rb b/app/models/ability.rb index bca1f2dfa..a5a030b57 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -67,6 +67,7 @@ def define_school_owner_abilities(school:) can(%i[read create create_batch update destroy], :school_student) can(%i[create create_copy], Lesson, school_id: school.id) can(%i[read update destroy], Lesson, school_id: school.id, visibility: %w[teachers students public]) + can(%i[remix], Lesson) end def define_school_teacher_abilities(user:, school:) @@ -81,13 +82,14 @@ def define_school_teacher_abilities(user:, school:) can(%i[create update destroy], Lesson) do |lesson| school_teacher_can_manage_lesson?(user:, school:, lesson:) end + can(%i[remix], Lesson) can(%i[read create_copy], Lesson, school_id: school.id, visibility: %w[teachers students]) can(%i[create], Project) do |project| school_teacher_can_manage_project?(user:, school:, project:) end can(%i[read update show_context], Project, school_id: school.id, lesson: { visibility: %w[teachers students] }) can(%i[read], Project, - remixed_from_id: Project.where(school_id: school.id, remixed_from_id: nil, lesson_id: Lesson.where(school_class_id: ClassTeacher.where(teacher_id: user.id).select(:school_class_id))).pluck(:id)) + remixed_from_id: Project.where(school_id: school.id, lesson_id: Lesson.where(school_class_id: ClassTeacher.where(teacher_id: user.id).select(:school_class_id))).pluck(:id)) end def define_school_student_abilities(user:, school:) diff --git a/app/views/api/lessons/index.json.jbuilder b/app/views/api/lessons/index.json.jbuilder index 6429df43c..2f0fb6b43 100644 --- a/app/views/api/lessons/index.json.jbuilder +++ b/app/views/api/lessons/index.json.jbuilder @@ -23,7 +23,7 @@ json.array!(@lessons_with_users) do |lesson, user| :identifier, :project_type ) - json.project.finished(lesson.project.finished) if lesson.project.remixed_from_id.present? + # json.project.finished(lesson.project.finished) if lesson.project.remixed_from_id.present? end json.user_name(user&.name) diff --git a/config/routes.rb b/config/routes.rb index f48edf7f1..7dad0c4aa 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -61,6 +61,7 @@ resources :lessons, only: %i[index create show update destroy] do post :copy, on: :member, to: 'lessons#create_copy' + post :remix, on: :collection, to: 'lessons#create_lesson_with_remix' end resources :teacher_invitations, param: :token, only: :show do diff --git a/lib/concepts/lesson/operations/create_remix.rb b/lib/concepts/lesson/operations/create_remix.rb new file mode 100644 index 000000000..54f305260 --- /dev/null +++ b/lib/concepts/lesson/operations/create_remix.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +class Lesson + class CreateRemix + class << self + def call(lesson_params:, remix_origin:) + ActiveRecord::Base.transaction do + response = OperationResponse.new + response[:lesson] = build_remix(lesson_params, remix_origin) + response[:lesson].save! + response + rescue StandardError => e + Sentry.capture_exception(e) + pp(e) + errors = response[:lesson].errors.full_messages.join(',') + response[:error] = "Error creating remix of lesson: #{errors}" + response + end + end + + private + + def build_remix(lesson_params, remix_origin) + original_project = Project.find_by(identifier: lesson_params[:project_identifier]) + lesson_copy = Lesson.new(name: original_project.name) + filtered_params = lesson_params.except(:project_identifier) + lesson_copy.assign_attributes(filtered_params) + lesson_copy.project = build_project_remix(original_project, lesson_params, remix_origin) + + lesson_copy + end + + def build_project_remix(original_project, lesson_params, remix_origin) + response = Project::CreateRemix.call( + params: {school_id: lesson_params[:school_id]}, + user_id: lesson_params[:user_id], + original_project: original_project, + remix_origin: remix_origin + ) + response[:project] + end + end + end +end diff --git a/lib/concepts/project/operations/create_remix.rb b/lib/concepts/project/operations/create_remix.rb index ef42defc3..6df631ec5 100644 --- a/lib/concepts/project/operations/create_remix.rb +++ b/lib/concepts/project/operations/create_remix.rb @@ -18,7 +18,7 @@ def call(params:, user_id:, original_project:, remix_origin:) private def validate_params(response, params, user_id, original_project, remix_origin) - valid = params[:identifier].present? && user_id.present? && original_project.present? && remix_origin.present? + valid = (params[:identifier].present? || original_project.identifier.present?) && user_id.present? && original_project.present? && remix_origin.present? response[:error] = I18n.t('errors.project.remixing.invalid_params') unless valid end @@ -30,7 +30,7 @@ def remix_project(response, params, user_id, original_project, remix_origin) def create_remix(original_project, params, user_id, remix_origin) remix = format_project(original_project, params, user_id, remix_origin) - + puts("remix: #{remix}") original_project.images.each do |image| remix.images.attach(image.blob) end @@ -43,9 +43,10 @@ def create_remix(original_project, params, user_id, remix_origin) remix.audio.attach(audio_file.blob) end - params[:components].each do |x| + (params[:components] || original_project.components).each do |x| remix.components.build(x.slice(:name, :extension, :content)) end + pp(remix) remix end @@ -54,11 +55,12 @@ def format_project(original_project, params, user_id, remix_origin) original_project.dup.tap do |proj| proj.identifier = PhraseIdentifier.generate proj.locale = nil - proj.name = params[:name] + proj.name = params[:name] || original_project.name proj.user_id = user_id proj.remixed_from_id = original_project.id proj.remix_origin = remix_origin proj.lesson_id = nil # Only the original can have a lesson id + proj.school_id = params[:school_id] || original_project.school_id end end end