-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathproject.rb
94 lines (72 loc) · 3.34 KB
/
project.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# frozen_string_literal: true
class Project < ApplicationRecord
attr_accessor :current_user
belongs_to :school, optional: true
belongs_to :lesson, optional: true
belongs_to :parent, optional: true, class_name: :Project, foreign_key: :remixed_from_id, inverse_of: :remixes
has_many :remixes, dependent: :nullify, class_name: :Project, foreign_key: :remixed_from_id, inverse_of: :parent
has_many :components, -> { order(default: :desc, name: :asc) }, dependent: :destroy, inverse_of: :project
has_many :project_errors, dependent: :nullify
has_many_attached :images
accepts_nested_attributes_for :components
before_validation :check_unique_not_null, on: :create
validates :identifier, presence: true, uniqueness: { scope: :locale }
validate :identifier_cannot_be_taken_by_another_user
validates :locale, presence: true, unless: :user_id
validate :user_has_a_role_within_the_school
validate :user_is_a_member_or_the_owner_of_the_lesson_or_school
scope :internal_projects, -> { where(user_id: nil) }
has_paper_trail(
if: ->(p) { p&.school_id },
meta: {
meta_remixed_from_id: ->(p) { p&.remixed_from_id },
meta_school_id: ->(p) { p&.school_id }
}
)
def self.users(current_user)
school = School.find_by(id: pluck(:school_id))
SchoolStudent::List.call(school:, token: current_user.token, student_ids: pluck(:user_id).uniq)[:school_students] || []
end
def self.with_users(current_user)
by_id = users(current_user).index_by(&:id)
all.map { |instance| [instance, by_id[instance.user_id]] }
end
def with_user(current_user)
school = School.find_by(id: school_id)
students = SchoolStudent::List.call(school:, token: current_user.token,
student_ids: [user_id])[:school_students] || []
[self, students.first]
end
# Work around a CanCanCan issue with accepts_nested_attributes_for.
# https://github.com/CanCanCommunity/cancancan/issues/774
def components=(array)
super(array.map { |o| o.is_a?(Hash) ? Component.new(o) : o })
end
def last_edited_at
# datetime that the project or one of its components was last updated
[updated_at, components.maximum(:updated_at)].compact.max
end
private
def check_unique_not_null
self.identifier ||= PhraseIdentifier.generate
end
def identifier_cannot_be_taken_by_another_user
return if Project.where(identifier: self.identifier).where.not(user_id:).empty?
errors.add(:identifier, "can't be taken by another user")
end
def user_has_a_role_within_the_school
return unless user_id_changed? && errors.blank? && school
user = User.new(id: user_id)
return if user.school_roles(school).any?
msg = "'#{user_id}' does not have any roles for for organisation '#{school_id}'"
errors.add(:user, msg)
end
def user_is_a_member_or_the_owner_of_the_lesson_or_school
return if !lesson || user_id == lesson.user_id || lesson.school_class&.members&.exists?(student_id: user_id)
# either we explicitly check the user is an owner of the school
return if current_user&.school_owner?(lesson.school)
# or we bypass if the lesson is associated with a school but not a school class
return if lesson.school && !lesson.school_class && !lesson.school_class&.members&.any?
errors.add(:user, "'#{user_id}' is not the owner or a member of the lesson '#{lesson_id}'")
end
end