create send_scores_in_emails course override

Adds the `send_scores_in_emails` option as an override to the course
level notification preferences.

fixes VICE-635
flag=notification_update_account_ui

/ ---- ---- \
| Test Plan |
\ ---- ---- /

- Create a course and an assignment
- Add a user to the course
- Navigate to the global notification preferences page and toggle the
  `send_scores_in_emails` option under the grading category to off
- Navigate to the course notification preferences page
- Toggle the `send_scores_in_emails` option to on
- Complete the assignment and then as the teacher grade the assignment
- Navigate to /users/<user_id>/messages
- Note the assignment graded message contains your score

- Do the previous steps but enable the toggle on for your global
  notification preferences and disable it for your course
- Note that the message now does not contain any grade information

- You can also create a new course and not specifically set the setting
  on the course notification preferences
- Doing the above steps should then just use the global notification
  preferences setting instead

Change-Id: Ie1a9ef98ce274718197e7103318e7b971c0ffef5
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/243138
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Rob Orton <rob@instructure.com>
QA-Review: Rob Orton <rob@instructure.com>
Product-Review: Rob Orton <rob@instructure.com>
This commit is contained in:
Matthew Lemon 2020-07-21 15:47:25 -06:00
parent 7daa973e04
commit 3fecd58842
18 changed files with 113 additions and 39 deletions

View File

@ -90,8 +90,12 @@ class Mutations::UpdateNotificationPreferences < Mutations::BaseMutation
end
if !input[:send_scores_in_emails].nil? && context.root_account.present? && context.root_account.settings[:allow_sending_scores_in_emails] != false
current_user.preferences[:send_scores_in_emails] = input[:send_scores_in_emails]
current_user.save!
if context.is_a?(Course)
current_user.set_preference(:send_scores_in_emails_override, "course_" + context.global_id.to_s, input[:send_scores_in_emails])
else
current_user.preferences[:send_scores_in_emails] = input[:send_scores_in_emails]
current_user.save!
end
end
# Because we validate the arguments for updating notification policies above we only need to

View File

@ -30,9 +30,16 @@ module Types
field :send_scores_in_emails, Boolean, null: true do
argument :user_id, ID, required: true, prepare: GraphQLHelpers.relay_or_legacy_id_prepare_func('User')
argument :course_id, ID, required: false, prepare: GraphQLHelpers.relay_or_legacy_id_prepare_func('Course')
end
def send_scores_in_emails(user_id:)
def send_scores_in_emails(user_id:, course_id: nil)
user = User.find(user_id)
if course_id
course = Course.find(course_id)
return user.send_scores_in_emails?(course)
end
# send_scores_in_emails can be nil and we want the default to be false if unset
user.preferences[:send_scores_in_emails] == true
end

View File

@ -43,7 +43,7 @@ export const UPDATE_COURSE_NOTIFICATION_PREFERENCES = gql`
_id
notificationPreferencesEnabled(contextType: Course, courseId: $courseId)
notificationPreferences {
sendScoresInEmails(userId: $userId)
sendScoresInEmails(userId: $userId, courseId: $courseId)
channels(channelId: $channelId) {
_id
path

View File

@ -24,7 +24,7 @@ export const COURSE_NOTIFICATIONS_QUERY = gql`
_id
notificationPreferencesEnabled(contextType: Course, courseId: $courseId)
notificationPreferences {
sendScoresInEmails(userId: $userId)
sendScoresInEmails(userId: $userId, courseId: $courseId)
channels {
_id
path

View File

@ -15,7 +15,7 @@
<% end %>
<%= t :regraded_date, "re-graded: %{date}", :date => (datetime_string(force_zone(asset.graded_at)) rescue t(:no_date_set, "No Date Set")) %>
<% if user.try(:send_scores_in_emails?, asset.assignment.context.root_account) %>
<% if user.try(:send_scores_in_emails?, asset.assignment.context) %>
<% if asset.excused? %>
<%= t :excused, "This assignment has been excused." %>
<% elsif asset.score %>

View File

@ -20,7 +20,7 @@
<p><%= t :regraded_date, "re-graded: %{date}", :date => (datetime_string(force_zone(asset.graded_at)) rescue t(:no_date_set, "No Date Set")) %></p>
<% if user.try(:send_scores_in_emails?, asset.assignment.context.root_account) %>
<% if user.try(:send_scores_in_emails?, asset.assignment.context) %>
<% if asset.excused? %>
<p><%= t :excused, "This assignment has been excused." %></p>
<% elsif asset.score %>

View File

@ -5,7 +5,7 @@
<% end %>
<%= t :regraded_date, "re-graded: %{date}", :date => (datetime_string(force_zone(asset.graded_at)) rescue t(:no_date_set, "No Date Set")) %>
<% if asset.score && user.try(:send_scores_in_emails?, asset.assignment.context.root_account) %>
<% if asset.score && user.try(:send_scores_in_emails?, asset.assignment.context) %>
<%= t :score, "score: %{score} out of %{total}", :score => asset.score, :total => (asset.assignment.points_possible || t(:not_applicable, "N/A")) %>
<% end %>
<%= t(:score_pending, "score pending review by the teacher") if asset.pending_review? %>

View File

@ -10,7 +10,7 @@
<%= t "For %{name}", name: asset.user.name %>
<% end %>
<%= t :regraded_date, "re-graded: %{date}", :date => (datetime_string(force_zone(asset.graded_at)) rescue t(:no_date_set, "No Date Set")) %>
<% if asset.score && user.try(:send_scores_in_emails?, asset.assignment.context.root_account) %>
<% if asset.score && user.try(:send_scores_in_emails?, asset.assignment.context) %>
<%= t :score, "score: %{score} out of %{total}", :score => asset.score, :total => (asset.assignment.points_possible || t(:not_applicable, "N/A")) %>
<% end %>
<%= t(:score_pending, "score pending review by the teacher") if asset.pending_review? %>

View File

@ -15,7 +15,7 @@
<% end %>
<%= t :graded_date, "graded: %{date}", :date => (datetime_string(force_zone(asset.graded_at)) rescue t(:no_date_set, "No Date Set")) %>
<% if user.try(:send_scores_in_emails?, asset.assignment.context.root_account) %>
<% if user.try(:send_scores_in_emails?, asset.assignment.context) %>
<% if asset.excused? %>
<%= t :excused, "This assignment has been excused." %>
<% elsif asset.score %>

View File

@ -20,7 +20,7 @@
<p><%= t :graded_date, "graded: %{date}", :date => (datetime_string(force_zone(asset.graded_at)) rescue t(:no_date_set, "No Date Set")) %></p>
<% if user.try(:send_scores_in_emails?, asset.assignment.context.root_account) %>
<% if user.try(:send_scores_in_emails?, asset.assignment.context) %>
<% if asset.excused? %>
<p><%= t :excused, "This assignment has been excused." %>
<% elsif asset.score %>

View File

@ -3,7 +3,7 @@
<% else %>
<%= t :sms_body, "%{assignment} has been graded.", :assignment => asset.assignment.title, :context => asset.assignment.context.name %>
<% end %>
<% if asset.score && user.try(:send_scores_in_emails?, asset.assignment.context.root_account) %>
<% if asset.score && user.try(:send_scores_in_emails?, asset.assignment.context) %>
<%= t :score, "score: %{score} out of %{total}", :score => asset.score, :total => (asset.assignment.points_possible || t(:not_applicable, "N/A")) %>
<% end %>

View File

@ -10,7 +10,7 @@
<%= t "For %{name}", name: asset.user.name %>
<% end %>
<%= t :graded_date, "graded: %{date}", :date => (datetime_string(force_zone(asset.graded_at)) rescue t(:no_date_set, "No Date Set")) %>
<% if asset.score && user.try(:send_scores_in_emails?, asset.assignment.context.root_account) %>
<% if asset.score && user.try(:send_scores_in_emails?, asset.assignment.context) %>
<%= t :score, "score: %{score} out of %{total}", :score => asset.score, :total => (asset.assignment.points_possible || t(:not_applicable, "N/A")) %>
<% end %>
<%= t(:score_pending_review, "score pending review by the teacher") if asset.pending_review? %>

View File

@ -9,6 +9,6 @@
<%= datetime_string(force_zone(asset.graded_at)) rescue t(:no_date_set, "No Date Set") %>
<% if asset.score && user.try(:send_scores_in_emails?, asset.assignment.context.root_account) %>
<% if asset.score && user.try(:send_scores_in_emails?, asset.assignment.context) %>
<%= t :score, "score: %{score} out of %{total}", :score => asset.score, :total => (asset.assignment.points_possible || t(:not_applicable, "N/A")) %>
<% end %><%= t(:score_pending_review, "(pending review)") if asset.pending_review? %>

View File

@ -1,7 +1,7 @@
<%= t :body_sms, "Your grade for %{title}, %{context} just changed.", :title => asset.assignment.title, :context => asset.assignment.context.name %>
<%= t :regraded_date, "re-graded: %{date}", :date => (datetime_string(force_zone(asset.graded_at)) rescue t(:no_date_set, "No Date Set")) %>
<% if asset.score && user.try(:send_scores_in_emails?, asset.assignment.context.root_account) %>
<% if asset.score && user.try(:send_scores_in_emails?, asset.assignment.context) %>
<%= t :score, "score: %{score} out of %{total}", :score => asset.score, :total => (asset.assignment.points_possible || t(:not_applicable, "N/A")) %>
<% end %>
<%= t(:score_pending, "score pending review by the teacher") if asset.pending_review? %>

View File

@ -1584,8 +1584,12 @@ class User < ActiveRecord::Base
end
end
def send_scores_in_emails?(root_account)
preferences[:send_scores_in_emails] == true && root_account.settings[:allow_sending_scores_in_emails] != false
def send_scores_in_emails?(course)
root_account = course.root_account
return false if root_account.settings[:allow_sending_scores_in_emails] == false
pref = get_preference(:send_scores_in_emails_override, "course_" + course.global_id.to_s)
pref = preferences[:send_scores_in_emails] if pref.nil?
!!pref
end
def send_observed_names_in_notifications?

View File

@ -46,6 +46,7 @@ class UserPreferenceValue < ActiveRecord::Base
add_user_preference :gradebook_settings, use_sub_keys: true
add_user_preference :new_user_tutorial_statuses
add_user_preference :selected_calendar_contexts
add_user_preference :send_scores_in_emails_override, use_sub_keys: true
def self.settings
@preference_settings ||= {}

View File

@ -83,7 +83,7 @@ RSpec.describe Mutations::UpdateNotificationPreferences do
#{"accountId: #{account_id}" if account_id}
)" if context_type && (course_id || account_id)}
notificationPreferences {
#{"sendScoresInEmails(userId: #{user_id})" if user_id}
#{"sendScoresInEmails(userId: #{user_id}, courseId: #{course_id})" if user_id}
channels {
#{"notificationPolicyOverrides(
contextType: #{context_type},
@ -115,28 +115,54 @@ RSpec.describe Mutations::UpdateNotificationPreferences do
result.to_h.with_indifferent_access
end
it 'sets the send_scores_in_emails setting' do
result = run_mutation(
user_id: @teacher.id,
account_id: @account.id,
context_type: 'Account',
send_scores_in_emails: true
)
expect(result.dig(:data, :updateNotificationPreferences, :errors)).to be nil
expect(result.dig(
:data, :updateNotificationPreferences, :user, :notificationPreferences, :sendScoresInEmails
)).to be true
context 'send scores in emails' do
it 'sets the global setting' do
result = run_mutation(
user_id: @teacher.id,
account_id: @account.id,
context_type: 'Account',
send_scores_in_emails: true
)
expect(result.dig(:data, :updateNotificationPreferences, :errors)).to be nil
expect(result.dig(
:data, :updateNotificationPreferences, :user, :notificationPreferences, :sendScoresInEmails
)).to be true
result = run_mutation(
user_id: @teacher.id,
account_id: @account.id,
context_type: 'Account',
send_scores_in_emails: false
)
expect(result.dig(:data, :updateNotificationPreferences, :errors)).to be nil
expect(result.dig(
:data, :updateNotificationPreferences, :user, :notificationPreferences, :sendScoresInEmails
)).to be false
result = run_mutation(
user_id: @teacher.id,
account_id: @account.id,
context_type: 'Account',
send_scores_in_emails: false
)
expect(result.dig(:data, :updateNotificationPreferences, :errors)).to be nil
expect(result.dig(
:data, :updateNotificationPreferences, :user, :notificationPreferences, :sendScoresInEmails
)).to be false
end
it 'sets the course override setting' do
result = run_mutation(
user_id: @teacher.id,
course_id: @course.id,
context_type: 'Course',
send_scores_in_emails: true
)
expect(result.dig(:data, :updateNotificationPreferences, :errors)).to be nil
expect(result.dig(
:data, :updateNotificationPreferences, :user, :notificationPreferences, :sendScoresInEmails
)).to be true
result = run_mutation(
user_id: @teacher.id,
course_id: @course.id,
context_type: 'Course',
send_scores_in_emails: false
)
expect(result.dig(:data, :updateNotificationPreferences, :errors)).to be nil
expect(result.dig(
:data, :updateNotificationPreferences, :user, :notificationPreferences, :sendScoresInEmails
)).to be false
end
end
context 'course' do

View File

@ -2381,6 +2381,38 @@ describe User do
end
end
describe "send_scores_in_emails" do
before :once do
course_with_student(:active_all => true)
end
it "returns false if the root account setting is disabled" do
root_account = @course.root_account
root_account.settings[:allow_sending_scores_in_emails] = false
root_account.save!
expect(@student.send_scores_in_emails?(@course)).to be false
end
it "uses the user preference setting if no course overrides are available" do
@student.preferences[:send_scores_in_emails] = true
expect(@student.send_scores_in_emails?(@course)).to be true
@student.preferences[:send_scores_in_emails] = false
expect(@student.send_scores_in_emails?(@course)).to be false
end
it "uses course overrides if available" do
@student.preferences[:send_scores_in_emails] = false
@student.set_preference(:send_scores_in_emails_override, "course_" + @course.global_id.to_s, true)
expect(@student.send_scores_in_emails?(@course)).to be true
@student.preferences[:send_scores_in_emails] = true
@student.set_preference(:send_scores_in_emails_override, "course_" + @course.global_id.to_s, false)
expect(@student.send_scores_in_emails?(@course)).to be false
end
end
describe "preferred_gradebook_version" do
subject { user.preferred_gradebook_version }