mumuki-domain 9.11.0 → 9.14.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 641d60f1b417f2fa2a9d05fbdd0357b3415a42a222b8a38ab989c63baf5b064b
4
- data.tar.gz: db353f1f369e0ae15f0349c2448d476e302921ef36479236408d74bada703f29
3
+ metadata.gz: 91e234afff822bf8ff746f174c941942d6a5d90d2b285e1613f4a739f13e7df6
4
+ data.tar.gz: d14adec7ec240db3d6aeff97d2ea7c05cf6dd72134a0118a5a2141c8d97e8215
5
5
  SHA512:
6
- metadata.gz: b8d1d6c40137d84dd7bf3d4c12c406ce88914efb9cce0a231a4ec1758e9b30ac61c881d940c0890a58dfaec3f69eefad432c67de3c88bef11950be1953f71ded
7
- data.tar.gz: 6e784be9dc46359358a831aae422ebadca1840cc9516d664aaa2d16b3100350dd8887a6dba2b1fa064a697b7a2443848cba238f5692caec72113c45a01ffd035
6
+ metadata.gz: 916afc62518c9ad05a8bd182f56f13b926584652bdc45ee11d267114cf1207b35bd267cccab98d3dffe0df8da200f44937732d8173677869794fb0ea3fa7aaf4
7
+ data.tar.gz: a0c2e513345ace615a0532c0f9443cc236f33ec1a17aa5177212162a1b1387353d5167f48641ecd2051e5e1f71b651bd454125976615882127a2ba6dd876d4bf
@@ -1,6 +1,8 @@
1
1
  class ApplicationRecord < ActiveRecord::Base
2
2
  self.abstract_class = true
3
3
 
4
+ include WithPgLock
5
+
4
6
  delegate :whitelist_attributes, to: :class
5
7
 
6
8
  def self.teaser_on(*args)
@@ -123,7 +123,7 @@ class Assignment < Progress
123
123
  end
124
124
 
125
125
  def content=(content)
126
- if exercise.solvable?
126
+ if content.present? && exercise.solvable?
127
127
  self.solution = exercise.single_choice? ? exercise.choice_index_for(content) : content
128
128
  end
129
129
  end
@@ -0,0 +1,22 @@
1
+ class Organization::Status::Base
2
+
3
+ attr_reader :organization
4
+
5
+ implements :teacher_access_mode, :student_access_mode, :ex_student_access_mode, :outsider_access_mode, :validate!
6
+
7
+ def initialize(organization)
8
+ @organization = organization
9
+ end
10
+
11
+ def access_mode(user)
12
+ if user&.teacher_of? organization
13
+ teacher_access_mode(user)
14
+ elsif user&.student_of? organization
15
+ student_access_mode(user)
16
+ elsif user&.ex_student_of? organization
17
+ ex_student_access_mode(user)
18
+ else
19
+ outsider_access_mode(user)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ class Organization::Status::Disabled < Organization::Status::Base
2
+
3
+ def teacher_access_mode(user)
4
+ OrganizationAccessMode::Full.new user, organization
5
+ end
6
+
7
+ def student_access_mode(user)
8
+ OrganizationAccessMode::ReadOnly.new user, organization, :faqs, :profile, :exercises, :discussions
9
+ end
10
+
11
+ def ex_student_access_mode(user)
12
+ OrganizationAccessMode::ReadOnly.new user, organization, :faqs, :profile
13
+ end
14
+
15
+ def outsider_access_mode(user)
16
+ if organization.public?
17
+ OrganizationAccessMode::Gone.new user, organization
18
+ else
19
+ OrganizationAccessMode::Forbidden.new user, organization
20
+ end
21
+ end
22
+
23
+ def validate!(user = nil)
24
+ raise Mumuki::Domain::DisabledOrganizationError unless user
25
+ end
26
+
27
+ end
28
+
@@ -0,0 +1,26 @@
1
+ class Organization::Status::Enabled < Organization::Status::Base
2
+
3
+ def teacher_access_mode(user)
4
+ OrganizationAccessMode::Full.new user, organization
5
+ end
6
+
7
+ def student_access_mode(user)
8
+ OrganizationAccessMode::Full.new user, organization
9
+ end
10
+
11
+ def ex_student_access_mode(user)
12
+ OrganizationAccessMode::ReadOnly.new user, organization, :faqs, :profile, :discussions, exercises: :submitted
13
+ end
14
+
15
+ def outsider_access_mode(user)
16
+ if organization.public?
17
+ OrganizationAccessMode::Full.new user, organization
18
+ else
19
+ OrganizationAccessMode::Forbidden.new user, organization
20
+ end
21
+ end
22
+
23
+ def validate!(_user = nil)
24
+ end
25
+
26
+ end
@@ -0,0 +1,27 @@
1
+ class Organization::Status::InPreparation < Organization::Status::Base
2
+
3
+ def teacher_access_mode(user)
4
+ OrganizationAccessMode::Full.new user, organization
5
+ end
6
+
7
+ def student_access_mode(user)
8
+ OrganizationAccessMode::ComingSoon.new user, organization
9
+ end
10
+
11
+ def ex_student_access_mode(user)
12
+ OrganizationAccessMode::Forbidden.new user, organization
13
+ end
14
+
15
+ def outsider_access_mode(user)
16
+ if organization.public?
17
+ OrganizationAccessMode::ComingSoon.new user, organization
18
+ else
19
+ OrganizationAccessMode::Forbidden.new user, organization
20
+ end
21
+ end
22
+
23
+ def validate!(user = nil)
24
+ raise Mumuki::Domain::UnpreparedOrganizationError unless user
25
+ end
26
+
27
+ end
@@ -30,4 +30,10 @@ module WithAssignments
30
30
  def assignment_for(user, organization=Organization.current)
31
31
  find_assignment_for(user, organization) || user.build_assignment(self, organization)
32
32
  end
33
+
34
+ def has_progress_for?(user, organization)
35
+ user.present? && find_assignment_for(user, organization).present?
36
+ end
37
+
38
+
33
39
  end
@@ -7,7 +7,7 @@ module WithNotifications
7
7
 
8
8
  def unread_notifications
9
9
  # TODO: message and discussion should trigger a notification instead of being one
10
- all = notifications.where(read: false) + unread_messages + unread_discussions
10
+ all = notifications_in_organization.where(read: false) + unread_messages + unread_discussions
11
11
  all.sort_by(&:created_at).reverse
12
12
  end
13
13
 
@@ -0,0 +1,32 @@
1
+ module WithOrganizationStatus
2
+
3
+ def status
4
+ @status ||= _status
5
+ end
6
+
7
+ def access_mode(user)
8
+ status.access_mode(user)
9
+ end
10
+
11
+ def validate_active!
12
+ status.validate!
13
+ end
14
+
15
+ def validate_active_for!(user)
16
+ status.validate!(user)
17
+ access_mode(user).validate_active!
18
+ end
19
+
20
+ private
21
+
22
+ def _status
23
+ if disabled?
24
+ Organization::Status::Disabled.new self
25
+ elsif in_preparation?
26
+ Organization::Status::InPreparation.new self
27
+ else
28
+ Organization::Status::Enabled.new self
29
+ end
30
+ end
31
+
32
+ end
@@ -7,6 +7,10 @@ module WithProgress
7
7
  progress_for(user, organization).completion_percentage
8
8
  end
9
9
 
10
+ def has_progress_for?(user, organization)
11
+ progress_for(user, organization).persisted?
12
+ end
13
+
10
14
  def dirty_progresses!
11
15
  Indicator.dirty_by_content_change! self
12
16
  end
@@ -1,6 +1,5 @@
1
1
  module WithReminders
2
2
  extend ActiveSupport::Concern
3
- include WithPgLock
4
3
 
5
4
  def build_reminder
6
5
  mailer = UserMailer.new
@@ -10,7 +9,7 @@ module WithReminders
10
9
  end
11
10
 
12
11
  def remind!
13
- build_reminder.deliver_now
12
+ build_reminder.post!
14
13
  update! last_reminded_date: Time.current
15
14
  end
16
15
 
@@ -1,5 +1,4 @@
1
1
  class ExamRegistration < ApplicationRecord
2
- include WithPgLock
3
2
  include WithTimedEnablement
4
3
  include TerminalNavigation
5
4
 
@@ -77,6 +76,14 @@ class ExamRegistration < ApplicationRecord
77
76
  authorization_criterion.meets_criterion?(user, organization)
78
77
  end
79
78
 
79
+ def multiple_options?
80
+ exams.count > 1
81
+ end
82
+
83
+ def ended?
84
+ end_time.past?
85
+ end
86
+
80
87
  private
81
88
 
82
89
  def notify_registree!(registree)
@@ -6,6 +6,7 @@ class Organization < ApplicationRecord
6
6
  include Mumukit::Login::OrganizationHelpers
7
7
 
8
8
  include WithTargetAudience
9
+ include WithOrganizationStatus
9
10
 
10
11
  serialize :profile, Mumuki::Domain::Organization::Profile
11
12
  serialize :settings, Mumuki::Domain::Organization::Settings
@@ -0,0 +1,53 @@
1
+ class OrganizationAccessMode::Base
2
+ attr_reader :user, :organization
3
+
4
+ def initialize(user, organization)
5
+ @user = user
6
+ @organization = organization
7
+ end
8
+
9
+ def validate_active!
10
+ end
11
+
12
+ def faqs_here?
13
+ organization.faqs.present?
14
+ end
15
+
16
+ def submit_solutions_here?
17
+ false
18
+ end
19
+
20
+ def resolve_discussions_here?
21
+ false
22
+ end
23
+
24
+ def discuss_here?
25
+ organization.forum_enabled? && user.discusser_of?(organization) &&
26
+ user.trusted_as_discusser_in?(organization) && !user.banned_from_forum?
27
+ end
28
+
29
+ def show_discussion_element?
30
+ false
31
+ end
32
+
33
+ def show_content_element?
34
+ false
35
+ end
36
+
37
+ def restore_indicators?(_content)
38
+ false
39
+ end
40
+
41
+ def read_only?
42
+ false
43
+ end
44
+
45
+ def validate_discuss_here!(_discussion)
46
+ raise Mumuki::Domain::ForbiddenError
47
+ end
48
+
49
+ def validate_content_here!(content)
50
+ raise Mumuki::Domain::ForbiddenError unless show_content?(content)
51
+ end
52
+ end
53
+
@@ -0,0 +1,5 @@
1
+ class OrganizationAccessMode::ComingSoon < OrganizationAccessMode::Forbidden
2
+ def validate_active!
3
+ raise Mumuki::Domain::UnpreparedOrganizationError
4
+ end
5
+ end
@@ -0,0 +1,22 @@
1
+ class OrganizationAccessMode::Forbidden < OrganizationAccessMode::Base
2
+ def validate_active!
3
+ raise Mumuki::Domain::ForbiddenError if organization.private? && user.present?
4
+ end
5
+
6
+ def faqs_here?
7
+ false
8
+ end
9
+
10
+ def profile_here?
11
+ false
12
+ end
13
+
14
+ def discuss_here?
15
+ false
16
+ end
17
+
18
+ def show_content?(_content)
19
+ false
20
+ end
21
+
22
+ end
@@ -0,0 +1,28 @@
1
+ class OrganizationAccessMode::Full < OrganizationAccessMode::Base
2
+ def profile_here?
3
+ true
4
+ end
5
+
6
+ def submit_solutions_here?
7
+ true
8
+ end
9
+
10
+ def show_discussion_element?
11
+ true
12
+ end
13
+
14
+ def resolve_discussions_here?
15
+ discuss_here?
16
+ end
17
+
18
+ def validate_discuss_here!(_discussion)
19
+ end
20
+
21
+ def show_content?(_content)
22
+ true
23
+ end
24
+
25
+ def show_content_element?
26
+ true
27
+ end
28
+ end
@@ -0,0 +1,5 @@
1
+ class OrganizationAccessMode::Gone < OrganizationAccessMode::Forbidden
2
+ def validate_active!
3
+ raise Mumuki::Domain::DisabledOrganizationError
4
+ end
5
+ end
@@ -0,0 +1,41 @@
1
+ class OrganizationAccessMode::ReadOnly < OrganizationAccessMode::Base
2
+ def initialize(user, organization, *global_scopes, **specific_scopes)
3
+ super user, organization
4
+ @scopes = global_scopes.map { |scope| [scope, :all] }.to_h.merge specific_scopes
5
+ end
6
+
7
+ def faqs_here?
8
+ has_scope(:faqs) && super
9
+ end
10
+
11
+ def profile_here?
12
+ has_scope(:profile)
13
+ end
14
+
15
+ def discuss_here?
16
+ has_scope(:discussions) && super
17
+ end
18
+
19
+ def validate_discuss_here!(discussion)
20
+ super(discussion) unless discussion&.initiator == user
21
+ end
22
+
23
+ def show_content?(content)
24
+ has_scope(:exercises) ||
25
+ (has_scope(:exercises, :submitted) && content.has_progress_for?(user, organization))
26
+ end
27
+
28
+ def restore_indicators?(book)
29
+ !book.has_progress_for?(user, organization) && user.has_assignments_in_organization?(organization)
30
+ end
31
+
32
+ def read_only?
33
+ true
34
+ end
35
+
36
+ private
37
+
38
+ def has_scope(key, value = :all)
39
+ @scopes[key] == value
40
+ end
41
+ end
@@ -0,0 +1,2 @@
1
+ module OrganizationAccessMode
2
+ end
data/app/models/user.rb CHANGED
@@ -54,6 +54,8 @@ class User < ApplicationRecord
54
54
  resource_fields :uid, :social_id, :email, :permissions, :verified_first_name, :verified_last_name, *profile_fields
55
55
  with_temporary_token :delete_account_token
56
56
 
57
+ organic_on :notifications, :assignments
58
+
57
59
  def last_lesson
58
60
  last_guide.try(:lesson)
59
61
  end
@@ -105,6 +107,16 @@ class User < ApplicationRecord
105
107
  end
106
108
  end
107
109
 
110
+ def restore_organization_progress!(organization)
111
+ assignments_in_organization(organization).each do |assignment|
112
+ assignment.tap(&:parent).save!
113
+ end
114
+ end
115
+
116
+ def has_assignments_in_organization?(organization)
117
+ assignments_in_organization(organization).exists?
118
+ end
119
+
108
120
  def accept_invitation!(invitation)
109
121
  make_student_of! invitation.course_slug
110
122
  end
@@ -195,7 +207,7 @@ class User < ApplicationRecord
195
207
  # This is true only when this organization has the forum enabled and the user
196
208
  # has the discusser pseudo-permission and the discusser is trusted
197
209
  def can_discuss_in?(organization)
198
- organization.forum_enabled? && discusser_of?(organization) && trusted_as_discusser_in?(organization) && !banned_from_forum?
210
+ organization.access_mode(self).discuss_here?
199
211
  end
200
212
 
201
213
  def trusted_as_discusser_in?(organization)
@@ -277,6 +289,10 @@ class User < ApplicationRecord
277
289
  }
278
290
  end
279
291
 
292
+ def solved_any_exercises?(organization = Organization.current)
293
+ Assignment.exists?(organization: organization, submitter: self)
294
+ end
295
+
280
296
  def save_and_notify!
281
297
  save!
282
298
  notify_permissions_changed!
@@ -300,7 +316,7 @@ class User < ApplicationRecord
300
316
  def certificate_in(certificate_program, certificate_h)
301
317
  return if certificated_in?(certificate_program)
302
318
  certificate = certificates.create certificate_h.merge(certificate_program: certificate_program)
303
- UserMailer.certificate(certificate).deliver_later
319
+ UserMailer.certificate(certificate).post!
304
320
  end
305
321
 
306
322
  def clear_progress_for!(organization)
@@ -316,7 +332,7 @@ class User < ApplicationRecord
316
332
  end
317
333
 
318
334
  def notify_via_email!(notification)
319
- UserMailer.notification(notification).deliver_later unless ignores_notification? notification
335
+ UserMailer.notification(notification).post! unless ignores_notification? notification
320
336
  end
321
337
 
322
338
  def ignores_notification?(notification)
@@ -327,7 +343,7 @@ class User < ApplicationRecord
327
343
 
328
344
  def welcome_to_new_organizations!
329
345
  new_accessible_organizations.each do |organization|
330
- UserMailer.welcome_email(self, organization).deliver_now rescue nil if organization.greet_new_users?
346
+ UserMailer.welcome_email(self, organization).post! rescue nil if organization.greet_new_users?
331
347
  end
332
348
  end
333
349
 
@@ -52,11 +52,6 @@ module Mumuki::Domain::Helpers::Organization
52
52
  Mumukit::Platform.application.organic_domain(name)
53
53
  end
54
54
 
55
- def validate_active!
56
- raise Mumuki::Domain::DisabledOrganizationError if disabled?
57
- raise Mumuki::Domain::UnpreparedOrganizationError if in_preparation?
58
- end
59
-
60
55
  ## API Exposure
61
56
 
62
57
  def to_param
@@ -16,6 +16,22 @@ module Mumuki::Domain
16
16
  false
17
17
  end
18
18
 
19
+ def ex_student_of?(*)
20
+ false
21
+ end
22
+
23
+ def ex_student_here?
24
+ false
25
+ end
26
+
27
+ def student_of?(*)
28
+ false
29
+ end
30
+
31
+ def student_here?
32
+ false
33
+ end
34
+
19
35
  def teacher_here?
20
36
  false
21
37
  end
@@ -28,7 +28,7 @@ class Mumuki::Domain::Organization::Settings < Mumukit::Platform::Model
28
28
  end
29
29
 
30
30
  def forum_discussions_minimal_role
31
- (@forum_discussions_minimal_role || 'student').to_sym
31
+ (@forum_discussions_minimal_role || 'ex_student').to_sym
32
32
  end
33
33
 
34
34
  def disabled_from=(disabled_from)
@@ -1,5 +1,5 @@
1
1
  module Mumuki
2
2
  module Domain
3
- VERSION = '9.11.0'
3
+ VERSION = '9.14.0'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mumuki-domain
3
3
  version: !ruby/object:Gem::Version
4
- version: 9.11.0
4
+ version: 9.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Franco Leonardo Bulgarelli
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-06 00:00:00.000000000 Z
11
+ date: 2021-09-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '7.9'
33
+ version: '7.11'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '7.9'
40
+ version: '7.11'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: mumukit-assistant
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '1.7'
75
+ version: '1.11'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '1.7'
82
+ version: '1.11'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: mumukit-core
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -265,6 +265,10 @@ files:
265
265
  - app/models/concerns/navigation/siblings_navigation.rb
266
266
  - app/models/concerns/navigation/terminal_navigation.rb
267
267
  - app/models/concerns/onomastic.rb
268
+ - app/models/concerns/organization/status/base.rb
269
+ - app/models/concerns/organization/status/disabled.rb
270
+ - app/models/concerns/organization/status/enabled.rb
271
+ - app/models/concerns/organization/status/in_preparation.rb
268
272
  - app/models/concerns/submittable/confirmable.rb
269
273
  - app/models/concerns/submittable/queriable.rb
270
274
  - app/models/concerns/submittable/questionable.rb
@@ -292,6 +296,7 @@ files:
292
296
  - app/models/concerns/with_name.rb
293
297
  - app/models/concerns/with_notifications.rb
294
298
  - app/models/concerns/with_number.rb
299
+ - app/models/concerns/with_organization_status.rb
295
300
  - app/models/concerns/with_pg_lock.rb
296
301
  - app/models/concerns/with_preferences.rb
297
302
  - app/models/concerns/with_profile.rb
@@ -336,6 +341,13 @@ files:
336
341
  - app/models/message.rb
337
342
  - app/models/notification.rb
338
343
  - app/models/organization.rb
344
+ - app/models/organization_access_mode.rb
345
+ - app/models/organization_access_mode/base.rb
346
+ - app/models/organization_access_mode/coming_soon.rb
347
+ - app/models/organization_access_mode/forbidden.rb
348
+ - app/models/organization_access_mode/full.rb
349
+ - app/models/organization_access_mode/gone.rb
350
+ - app/models/organization_access_mode/read_only.rb
339
351
  - app/models/preferences.rb
340
352
  - app/models/progress.rb
341
353
  - app/models/stats.rb