1165 lines
51 KiB
Ruby
1165 lines
51 KiB
Ruby
#
|
|
# Copyright (C) 2012 Instructure, Inc.
|
|
#
|
|
# This file is part of Canvas.
|
|
#
|
|
# Canvas is free software: you can redistribute it and/or modify it under
|
|
# the terms of the GNU Affero General Public License as published by the Free
|
|
# Software Foundation, version 3 of the License.
|
|
#
|
|
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License along
|
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
require 'set'
|
|
|
|
# @API Courses
|
|
#
|
|
# API for accessing course information.
|
|
class CoursesController < ApplicationController
|
|
include SearchHelper
|
|
|
|
before_filter :require_user, :only => [:index]
|
|
before_filter :require_context, :only => [:roster, :locks, :switch_role, :create_file]
|
|
|
|
include Api::V1::Course
|
|
|
|
# @API List your courses
|
|
# Returns the list of active courses for the current user.
|
|
#
|
|
# @argument enrollment_type [optional, "teacher"|"student"|"ta"|"observer"|"designer"]
|
|
# When set, only return courses where the user is enrolled as this type. For
|
|
# example, set to "teacher" to return only courses where the user is
|
|
# enrolled as a Teacher.
|
|
#
|
|
# @argument include[] ["needs_grading_count"] Optional information to include with each Course.
|
|
# When needs_grading_count is given, and the current user has grading
|
|
# rights, the total number of submissions needing grading for all
|
|
# assignments is returned.
|
|
#
|
|
# @argument include[] ["syllabus_body"] Optional information to include with each Course.
|
|
# When syllabus_body is given the user-generated html for the course
|
|
# syllabus is returned.
|
|
#
|
|
# @argument include[] ["total_scores"] Optional information to include with each Course.
|
|
# When total_scores is given, any enrollments with type 'student' will also
|
|
# include the fields 'calculated_current_score', 'calculated_final_score',
|
|
# and 'calculated_final_grade'. calculated_current_score is the student's
|
|
# score in the course, ignoring ungraded assignments. calculated_final_score
|
|
# is the student's score in the course including ungraded assignments with
|
|
# a score of 0. calculated_final_grade is the letter grade equivalent of
|
|
# calculated_final_score (if available). This argument is ignored if the
|
|
# course is configured to hide final grades.
|
|
#
|
|
# @response_field id The unique identifier for the course.
|
|
# @response_field name The name of the course.
|
|
# @response_field course_code The course code.
|
|
# @response_field enrollments A list of enrollments linking the current user
|
|
# to the course.
|
|
# @response_field sis_course_id The SIS id of the course, if defined.
|
|
#
|
|
# @response_field needs_grading_count Number of submissions needing grading
|
|
# for all the course assignments. Only returned if
|
|
# include[]=needs_grading_count
|
|
#
|
|
# @example_response
|
|
# [ { 'id': 1, 'name': 'first course', 'course_code': 'first', 'enrollments': [{'type': 'student', 'computed_current_score': 84.8, 'computed_final_score': 62.9, 'computed_final_grade': 'D-'}], 'calendar': { 'ics': '..url..' } },
|
|
# { 'id': 2, 'name': 'second course', 'course_code': 'second', 'enrollments': [{'type': 'teacher'}], 'calendar': { 'ics': '..url..' } } ]
|
|
def index
|
|
respond_to do |format|
|
|
format.html {
|
|
@current_enrollments = @current_user.cached_current_enrollments(:include_enrollment_uuid => session[:enrollment_uuid]).sort_by{|e| [e.active? ? 1 : 0, e.long_name] }
|
|
@past_enrollments = @current_user.enrollments.ended.scoped(:conditions=>"enrollments.workflow_state NOT IN ('invited', 'deleted')")
|
|
@past_enrollments.concat(@current_enrollments.select { |e| e.state_based_on_date == :completed })
|
|
@current_enrollments.reject! { |e| [:inactive, :completed].include?(e.state_based_on_date) }
|
|
}
|
|
format.json {
|
|
enrollments = @current_user.cached_current_enrollments
|
|
if params[:enrollment_type]
|
|
e_type = "#{params[:enrollment_type].capitalize}Enrollment"
|
|
enrollments = enrollments.reject { |e| e.class.name != e_type }
|
|
end
|
|
|
|
includes = Set.new(Array(params[:include]))
|
|
|
|
hash = []
|
|
enrollments.group_by(&:course_id).each do |course_id, course_enrollments|
|
|
course = course_enrollments.first.course
|
|
hash << course_json(course, @current_user, session, includes, course_enrollments)
|
|
end
|
|
render :json => hash.to_json
|
|
}
|
|
end
|
|
end
|
|
|
|
# @API Create a new course
|
|
# Create a new course
|
|
#
|
|
# @argument account_id [Integer] The unique ID of the account to create to course under.
|
|
# @argument course[name] [String] [optional] The name of the course. If omitted, the course will be named "Unnamed Course."
|
|
# @argument course[course_code] [String] [optional] The course code for the course.
|
|
# @argument course[start_at] [Datetime] [optional] Course start date in ISO8601 format, e.g. 2011-01-01T01:00Z
|
|
# @argument course[end_at] [Datetime] [optional] Course end date in ISO8601 format. e.g. 2011-01-01T01:00Z
|
|
# @argument course[license] [String] [optional] The name of the licensing. Should be one of the following abbreviations (a descriptive name is included in parenthesis for reference): 'private' (Private Copyrighted); 'cc_by_nc_nd' (CC Attribution Non-Commercial No Derivatives); 'cc_by_nc_sa' (CC Attribution Non-Commercial Share Alike); 'cc_by_nc' (CC Attribution Non-Commercial); 'cc_by_nd' (CC Attribution No Derivatives); 'cc_by_sa' (CC Attribution Share Alike); 'cc_by' (CC Attribution); 'public_domain' (Public Domain).
|
|
# @argument course[is_public] [Boolean] [optional] Set to true if course if public.
|
|
# @argument course[public_description] [String] [optional] A publicly visible description of the course.
|
|
# @argument course[allow_student_wiki_edits] [Boolean] [optional] If true, students will be able to modify the course wiki.
|
|
# @argument course[allow_student_assignment_edits] [optional] Set to true if students should be allowed to make modifications to assignments.
|
|
# @argument course[allow_wiki_comments] [Boolean] [optional] If true, course members will be able to comment on wiki pages.
|
|
# @argument course[allow_student_forum_attachments] [Boolean] [optional] If true, students can attach files to forum posts.
|
|
# @argument course[open_enrollment] [Boolean] [optional] Set to true if the course is open enrollment.
|
|
# @argument course[self_enrollment] [Boolean] [optional] Set to true if the course is self enrollment.
|
|
# @argument course[restrict_enrollments_to_course_dates] [Boolean] [optional] Set to true to restrict user enrollments to the start and end dates of the course.
|
|
# @argument course[enroll_me] [Boolean] [optional] Set to true to enroll the current user as the teacher.
|
|
# @argument course[sis_course_id] [String] [optional] The unique SIS identifier.
|
|
# @argument offer [Boolean] [optional] If this option is set to true, the course will be available to students immediately.
|
|
#
|
|
def create
|
|
@account = Account.find(params[:account_id])
|
|
if authorized_action(@account, @current_user, :manage_courses)
|
|
if (sub_account_id = params[:course].delete(:account_id)) && sub_account_id.to_i != @account.id
|
|
@sub_account = @account.find_child(sub_account_id) || raise(ActiveRecord::RecordNotFound)
|
|
end
|
|
|
|
if enrollment_term_id = params[:course].delete(:enrollment_term_id)
|
|
params[:course][:enrollment_term] = @account.root_account.enrollment_terms.find(enrollment_term_id)
|
|
end
|
|
|
|
sis_course_id = params[:course].delete(:sis_course_id)
|
|
|
|
# accept end_at as an alias for conclude_at. continue to accept
|
|
# conclude_at for legacy support, and return conclude_at only if
|
|
# the user uses that name.
|
|
course_end = if params[:course][:end_at].present?
|
|
params[:course][:conclude_at] = params[:course].delete(:end_at)
|
|
:end_at
|
|
else
|
|
:conclude_at
|
|
end
|
|
|
|
@course = (@sub_account || @account).courses.build(params[:course])
|
|
@course.sis_source_id = sis_course_id if api_request? && @account.grants_right?(@current_user, :manage_sis)
|
|
respond_to do |format|
|
|
if @course.save
|
|
@course.enroll_user(@current_user, 'TeacherEnrollment', :enrollment_state => 'active') if params[:enroll_me].to_s == 'true'
|
|
# offer updates the workflow state, saving the record without doing validation callbacks
|
|
@course.offer if api_request? and params[:offer].present?
|
|
format.html
|
|
format.json { render :json => course_json(
|
|
@course,
|
|
@current_user,
|
|
session,
|
|
[:start_at, course_end, :license, :publish_grades_immediately,
|
|
:is_public, :allow_student_assignment_edits, :allow_wiki_comments,
|
|
:allow_student_forum_attachments, :open_enrollment, :self_enrollment,
|
|
:root_account_id, :account_id, :public_description,
|
|
:restrict_enrollments_to_course_dates, :workflow_state], nil)
|
|
}
|
|
else
|
|
flash[:error] = t('errors.create_failed', "Course creation failed")
|
|
format.html { redirect_to :root_url }
|
|
format.json { render :json => @course.errors.to_json, :status => :bad_request }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
# @API Upload a file
|
|
#
|
|
# Upload a file to the course.
|
|
#
|
|
# This API endpoint is the first step in uploading a file to a course.
|
|
# See the {file:file_uploads.html File Upload Documentation} for details on
|
|
# the file upload workflow.
|
|
#
|
|
# Only those with the "Manage Files" permission on a course can upload files
|
|
# to the course. By default, this is Teachers, TAs and Designers.
|
|
def create_file
|
|
@attachment = Attachment.new(:context => @context)
|
|
if authorized_action(@attachment, @current_user, :create)
|
|
api_attachment_preflight(@context, request, :check_quota => true)
|
|
end
|
|
end
|
|
|
|
def backup
|
|
get_context
|
|
if authorized_action(@context, @current_user, :update)
|
|
backup_json = @context.backup_to_json
|
|
send_file_headers!( :length=>backup_json.length, :filename=>"#{@context.name.underscore.gsub(/\s/, "_")}_#{Time.zone.today.to_s}_backup.instructure", :disposition => 'attachment', :type => 'application/instructure')
|
|
render :text => proc {|response, output|
|
|
output.write backup_json
|
|
}
|
|
end
|
|
end
|
|
|
|
def restore
|
|
get_context
|
|
if authorized_action(@context, @current_user, :update)
|
|
respond_to do |format|
|
|
if params[:restore]
|
|
@context.restore_from_json_backup(params[:restore])
|
|
flash[:notice] = t('notices.backup_restored', "Backup Successfully Restored!")
|
|
format.html { redirect_to named_context_url(@context, :context_url) }
|
|
else
|
|
format.html
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def unconclude
|
|
get_context
|
|
if authorized_action(@context, @current_user, :change_course_state)
|
|
@context.unconclude
|
|
flash[:notice] = t('notices.unconcluded', "Course un-concluded")
|
|
redirect_to(named_context_url(@context, :context_url))
|
|
end
|
|
end
|
|
|
|
include Api::V1::User
|
|
|
|
# @API List students
|
|
#
|
|
# Returns the list of students enrolled in this course.
|
|
#
|
|
# @response_field id The unique identifier for the student.
|
|
# @response_field name The full student name.
|
|
# @response_field sis_user_id The SIS id for the user's primary pseudonym.
|
|
#
|
|
# @example_response
|
|
# [ { 'id': 1, 'name': 'first student', 'sis_user_id': null, 'sis_login_id': null },
|
|
# { 'id': 2, 'name': 'second student', 'sis_user_id': 'from-sis', 'sis_login_id': 'login-from-sis' } ]
|
|
def students
|
|
# DEPRECATED. #users should replace this in a new version of the API.
|
|
get_context
|
|
if authorized_action(@context, @current_user, :read_roster)
|
|
proxy = @context.students.order_by_sortable_name
|
|
if user_json_is_admin?
|
|
proxy = proxy.scoped(:include => :pseudonyms)
|
|
end
|
|
render :json => proxy.map { |u| user_json(u, @current_user, session) }
|
|
end
|
|
end
|
|
|
|
# @API List users
|
|
# Returns the list of users in this course. And optionally the user's enrollments
|
|
# in the course.
|
|
#
|
|
# @argument enrollment_type [optional, "teacher"|"student"|"ta"|"observer"|"designer"]
|
|
# When set, only return users where the user is enrolled as this type.
|
|
#
|
|
# @argument include[] ["email"] Optional user email.
|
|
# @argument include[] ["enrollments"] Optionally include with each Course the
|
|
# user's current and invited enrollments.
|
|
# @argument include[] ["locked"] Optionally include whether an enrollment is locked.
|
|
# @argument include[] ["avatar_url"] Optionally include avatar_url.
|
|
#
|
|
# @response_field id The unique identifier for the user.
|
|
# @response_field name The full user name.
|
|
# @response_field sortable_name The sortable user name.
|
|
# @response_field short_name The short user name.
|
|
# @response_field sis_user_id The SIS id for the user's primary pseudonym.
|
|
#
|
|
# @response_field enrollments The user's enrollments in this course.
|
|
# See the API documentation for enrollment data returned; however, the user data is not included.
|
|
#
|
|
# @example_response
|
|
# [ { 'id': 1, 'name': 'first user', 'sis_user_id': null, 'sis_login_id': null,
|
|
# 'enrollments': [ ... ] },
|
|
# { 'id': 2, 'name': 'second user', 'sis_user_id': 'from-sis', 'sis_login_id': 'login-from-sis',
|
|
# 'enrollments': [ ... ] }]
|
|
def users
|
|
get_context
|
|
if authorized_action(@context, @current_user, :read_roster)
|
|
enrollment_type = "#{params[:enrollment_type].capitalize}Enrollment" if params[:enrollment_type]
|
|
users = @context.users_visible_to(@current_user)
|
|
users = users.scoped(:order => "users.sortable_name")
|
|
users = users.scoped(:conditions => ["enrollments.type = ? ", enrollment_type]) if enrollment_type
|
|
if user_json_is_admin?
|
|
users = users.scoped(:include => {:pseudonym => :communication_channel})
|
|
end
|
|
users = Api.paginate(users, self, api_v1_course_users_path)
|
|
includes = Array(params[:include])
|
|
if includes.include?('enrollments')
|
|
User.send(:preload_associations, users, :not_ended_enrollments,
|
|
:conditions => ['enrollments.course_id = ?', @context.id])
|
|
end
|
|
render :json => users.map { |u|
|
|
enrollments = u.not_ended_enrollments if includes.include?('enrollments')
|
|
user_json(u, @current_user, session, includes, @context, enrollments)
|
|
}
|
|
end
|
|
end
|
|
|
|
# @API
|
|
# Return information on a single user.
|
|
#
|
|
# Accepts the same include[] parameters as the :users: action, and returns a
|
|
# single user with the same fields as that action.
|
|
def user
|
|
get_context
|
|
if authorized_action(@context, @current_user, :read_roster)
|
|
users = @context.users_visible_to(@current_user)
|
|
users = users.scoped(:conditions => ['users.id = ?', params[:id]])
|
|
if user_json_is_admin?
|
|
users = users.scoped(:include => {:pseudonym => :communication_channel})
|
|
end
|
|
includes = Array(params[:include])
|
|
if includes.include?('enrollments')
|
|
User.send(:preload_associations, users, :not_ended_enrollments,
|
|
:conditions => ['enrollments.course_id = ?', @context.id])
|
|
end
|
|
user = users.first
|
|
enrollments = user.not_ended_enrollments if includes.include?('enrollments')
|
|
render :json => user_json(user, @current_user, session, includes, @context, enrollments)
|
|
end
|
|
end
|
|
|
|
include Api::V1::StreamItem
|
|
# @API Course activity stream
|
|
# Returns the current user's course-specific activity stream, paginated.
|
|
#
|
|
# For full documentation, see the API documentation for the user activity
|
|
# stream, in the user api.
|
|
def activity_stream
|
|
get_context
|
|
if authorized_action(@context, @current_user, :read)
|
|
api_render_stream_for_contexts([@context], :api_v1_course_activity_stream_url)
|
|
end
|
|
end
|
|
|
|
include Api::V1::TodoItem
|
|
# @API Course TODO items
|
|
# Returns the current user's course-specific todo items.
|
|
#
|
|
# For full documentation, see the API documentation for the user todo items, in the user api.
|
|
def todo_items
|
|
get_context
|
|
if authorized_action(@context, @current_user, :read)
|
|
grading = @current_user.assignments_needing_grading(:contexts => [@context]).map { |a| todo_item_json(a, @current_user, session, 'grading') }
|
|
submitting = @current_user.assignments_needing_submitting(:contexts => [@context]).map { |a| todo_item_json(a, @current_user, session, 'submitting') }
|
|
render :json => (grading + submitting)
|
|
end
|
|
end
|
|
|
|
# @API Conclude a course
|
|
# Delete or conclude an existing course
|
|
#
|
|
# @argument event [String] ["delete"|"conclude"] The action to take on the course. available options are 'delete' and 'conclude.'
|
|
def destroy
|
|
@context = api_request? ? api_find(Course, params[:id]) : Course.find(params[:id])
|
|
if api_request? && !['delete', 'conclude'].include?(params[:event])
|
|
return render(:json => { :message => 'Only "delete" and "conclude" events are allowed.' }.to_json, :status => :bad_request)
|
|
end
|
|
if params[:event] != 'conclude' && (@context.created? || @context.claimed? || params[:event] == 'delete')
|
|
return unless authorized_action(@context, @current_user, permission_for_event(params[:event]))
|
|
@context.workflow_state = 'deleted'
|
|
@context.sis_source_id = nil
|
|
@context.save
|
|
flash[:notice] = t('notices.deleted', "Course successfully deleted")
|
|
else
|
|
return unless authorized_action(@context, @current_user, permission_for_event(params[:event]))
|
|
@context.complete
|
|
flash[:notice] = t('notices.concluded', "Course successfully concluded")
|
|
end
|
|
@current_user.touch
|
|
respond_to do |format|
|
|
format.html { redirect_to dashboard_url }
|
|
format.json {
|
|
render :json => { params[:event] => true }.to_json
|
|
}
|
|
end
|
|
end
|
|
|
|
def statistics
|
|
get_context
|
|
if authorized_action(@context, @current_user, :read_reports)
|
|
@student_ids = @context.students.map &:id
|
|
@range_start = Date.parse("Jan 1 2000")
|
|
@range_end = Date.tomorrow
|
|
|
|
query = "SELECT COUNT(id), SUM(size) FROM attachments WHERE context_id=%s AND context_type='Course' AND root_attachment_id IS NULL AND file_state != 'deleted'"
|
|
row = Attachment.connection.select_rows(query % [@context.id]).first
|
|
@file_count, @files_size = [row[0].to_i, row[1].to_i]
|
|
query = "SELECT COUNT(id), SUM(max_size) FROM media_objects WHERE context_id=%s AND context_type='Course' AND attachment_id IS NULL AND workflow_state != 'deleted'"
|
|
row = MediaObject.connection.select_rows(query % [@context.id]).first
|
|
@media_file_count, @media_files_size = [row[0].to_i, row[1].to_i]
|
|
|
|
if params[:range] && params[:date]
|
|
date = Date.parse(params[:date]) rescue nil
|
|
date ||= Time.zone.today
|
|
if params[:range] == 'week'
|
|
@view_week = (date - 1) - (date - 1).wday + 1
|
|
@range_start = @view_week
|
|
@range_end = @view_week + 6
|
|
@old_range_start = @view_week - 7.days
|
|
elsif params[:range] == 'month'
|
|
@view_month = Date.new(date.year, date.month, d=1) #view.created_at.strftime("%m:%Y")
|
|
@range_start = @view_month
|
|
@range_end = (@view_month >> 1) - 1
|
|
@old_range_start = @view_month << 1
|
|
end
|
|
end
|
|
|
|
@recently_logged_students = @context.students.recently_logged_in
|
|
respond_to do |format|
|
|
format.html
|
|
format.json{ render :json => @categories.to_json }
|
|
end
|
|
end
|
|
end
|
|
|
|
def settings
|
|
get_context
|
|
if authorized_action(@context, @current_user, :read_as_admin)
|
|
load_all_contexts
|
|
users_scope = @context.users_visible_to(@current_user)
|
|
enrollment_counts = users_scope.count(:distinct => true, :group => 'enrollments.type', :select => 'users.id')
|
|
@user_counts = {
|
|
:student => enrollment_counts['StudentEnrollment'] || 0,
|
|
:teacher => enrollment_counts['TeacherEnrollment'] || 0,
|
|
:ta => enrollment_counts['TaEnrollment'] || 0,
|
|
:observer => enrollment_counts['ObserverEnrollment'] || 0,
|
|
:designer => enrollment_counts['DesignerEnrollment'] || 0,
|
|
:invited => users_scope.count(:distinct => true, :select => 'users.id', :conditions => ["enrollments.workflow_state = 'invited'"])
|
|
}
|
|
js_env(:COURSE_ID => @context.id,
|
|
:USER_COUNTS => @user_counts,
|
|
:USERS_URL => "/api/v1/courses/#{ @context.id }/users",
|
|
:COURSE_ROOT_URL => "/courses/#{ @context.id }",
|
|
:SEARCH_URL => search_recipients_url,
|
|
:CONTEXTS => @contexts,
|
|
:USER_PARAMS => {:include => ['email', 'enrollments', 'locked']},
|
|
:PERMISSIONS => {
|
|
:manage_students => (@context.grants_right?(@current_user, session, :manage_students) ||
|
|
@context.grants_right?(@current_user, session, :manage_admin_users)),
|
|
:manage_account_settings => @context.account.grants_right?(@current_user, session, :manage_account_settings),
|
|
})
|
|
|
|
@alerts = @context.alerts
|
|
@role_types = []
|
|
add_crumb(t('#crumbs.settings', "Settings"), named_context_url(@context, :context_details_url))
|
|
end
|
|
end
|
|
|
|
def update_nav
|
|
get_context
|
|
if authorized_action(@context, @current_user, :update)
|
|
@context.tab_configuration = JSON.parse(params[:tabs_json])
|
|
@context.save
|
|
respond_to do |format|
|
|
format.html { redirect_to named_context_url(@context, :context_details_url) }
|
|
format.json { render :json => {:update_nav => true}.to_json }
|
|
end
|
|
end
|
|
end
|
|
|
|
def roster
|
|
if authorized_action(@context, @current_user, :read_roster)
|
|
log_asset_access("roster:#{@context.asset_string}", "roster", "other")
|
|
@students = @context.participating_students.order_by_sortable_name
|
|
@teachers = @context.instructors.order_by_sortable_name
|
|
@groups = @context.groups.active
|
|
end
|
|
end
|
|
|
|
def re_send_invitations
|
|
get_context
|
|
if authorized_action(@context, @current_user, [:manage_students, :manage_admin_users])
|
|
@context.detailed_enrollments.each do |e|
|
|
e.re_send_confirmation! if e.invited?
|
|
end
|
|
respond_to do |format|
|
|
format.html { redirect_to course_settings_url }
|
|
format.json { render :json => {:re_sent => true}.to_json }
|
|
end
|
|
end
|
|
end
|
|
|
|
def enrollment_invitation
|
|
get_context
|
|
return if check_enrollment
|
|
if !@pending_enrollment
|
|
redirect_to course_url(@context.id)
|
|
return true
|
|
end
|
|
if params[:reject]
|
|
if @pending_enrollment.invited?
|
|
@pending_enrollment.reject!
|
|
flash[:notice] = t('notices.invitation_cancelled', "Invitation canceled.")
|
|
end
|
|
session.delete(:enrollment_uuid)
|
|
if @current_user
|
|
redirect_to dashboard_url
|
|
else
|
|
redirect_to root_url
|
|
end
|
|
elsif params[:accept]
|
|
if @current_user && @pending_enrollment.user == @current_user
|
|
if @pending_enrollment.invited?
|
|
@pending_enrollment.accept!
|
|
flash[:notice] = t('notices.invitation_accepted', "Invitation accepted! Welcome to %{course}!", :course => @context.name)
|
|
end
|
|
session[:accepted_enrollment_uuid] = @pending_enrollment.uuid
|
|
if params[:action] != 'show'
|
|
redirect_to course_url(@context.id)
|
|
else
|
|
@context_enrollment = @pending_enrollment
|
|
@pending_enrollment = nil
|
|
return false
|
|
end
|
|
elsif !@current_user && @pending_enrollment.user.registered? || !@pending_enrollment.user.email_channel
|
|
session[:return_to] = course_url(@context.id)
|
|
flash[:notice] = t('notices.login_to_accept', "You'll need to log in before you can accept the enrollment.")
|
|
return redirect_to login_url(:re_login => 1) if @current_user
|
|
redirect_to login_url
|
|
else
|
|
# defer to CommunicationChannelsController#confirm for the logic of merging users
|
|
redirect_to registration_confirmation_path(@pending_enrollment.user.email_channel.confirmation_code, :enrollment => @pending_enrollment.uuid)
|
|
end
|
|
else
|
|
redirect_to course_url(@context.id)
|
|
end
|
|
true
|
|
end
|
|
|
|
def claim_course
|
|
if params[:verification] == @context.uuid
|
|
session[:claim_course_uuid] = @context.uuid
|
|
# session[:course_uuid] = @context.uuid
|
|
end
|
|
if session[:claim_course_uuid] == @context.uuid && @current_user && @context.state == :created
|
|
claim_session_course(@context, @current_user)
|
|
end
|
|
end
|
|
protected :claim_course
|
|
|
|
def check_enrollment
|
|
return false if @pending_enrollment
|
|
|
|
# Use the enrollment we already fetched, if possible
|
|
enrollment = @context_enrollment if @context_enrollment && @context_enrollment.pending? && (@context_enrollment.uuid == params[:invitation] || params[:invitation].blank?)
|
|
# Overwrite with the session enrollment, if one exists, and it's different than the current user's
|
|
enrollment = @context.enrollments.find_by_uuid_and_workflow_state(session[:enrollment_uuid], "invited") if session[:enrollment_uuid] && enrollment.try(:uuid) != session[:enrollment_uuid] && params[:invitation].blank? && session[:enrollment_uuid_course_id] == @context.id
|
|
# Look for enrollments to matching temporary users
|
|
enrollment ||= @current_user.temporary_invitations.find { |i| i.course_id == @context.id } if @current_user
|
|
# Look up the explicitly provided invitation
|
|
enrollment ||= @context.enrollments.find(:first, :conditions => ["uuid=? AND workflow_state IN ('invited', 'rejected')", params[:invitation]]) unless params[:invitation].blank?
|
|
if enrollment && enrollment.state_based_on_date == :inactive
|
|
flash[:notice] = t('notices.enrollment_not_active', "Your membership in the course, %{course}, is not yet activated", :course => @context.name)
|
|
redirect_to dashboard_url
|
|
return true
|
|
end
|
|
if enrollment
|
|
if enrollment.rejected?
|
|
enrollment.workflow_state = 'invited'
|
|
enrollment.save_without_broadcasting
|
|
end
|
|
if enrollment.self_enrolled?
|
|
redirect_to registration_confirmation_path(enrollment.user.email_channel.confirmation_code, :enrollment => enrollment.uuid)
|
|
return true
|
|
end
|
|
e = enrollment
|
|
session[:enrollment_uuid] = e.uuid
|
|
session[:session_affects_permissions] = true
|
|
session[:enrollment_as_student] = true if e.student?
|
|
session[:enrollment_uuid_course_id] = e.course_id
|
|
@pending_enrollment = enrollment
|
|
if @context.root_account.allow_invitation_previews?
|
|
flash[:notice] = t('notices.preview_course', "You've been invited to join this course. You can look around, but you'll need to accept the enrollment invitation before you can participate.")
|
|
elsif params[:action] != "enrollment_invitation"
|
|
# directly call the next action; it's just going to redirect anyway, so no need to have
|
|
# an additional redirect to get to it
|
|
params[:accept] = 1
|
|
return enrollment_invitation
|
|
end
|
|
end
|
|
if session[:accepted_enrollment_uuid] && (e = @context.enrollments.find_by_uuid(session[:accepted_enrollment_uuid]))
|
|
if e.invited?
|
|
e.accept!
|
|
flash[:notice] = t('notices.invitation_accepted', "Invitation accepted! Welcome to %{course}!", :course => @context.name)
|
|
end
|
|
session.delete(:accepted_enrollment_uuid)
|
|
session.delete(:enrollment_uuid_course_id)
|
|
session.delete(:enrollment_uuid) if session[:enrollment_uuid] == session[:accepted_enrollment_uuid]
|
|
end
|
|
false
|
|
end
|
|
protected :check_enrollment
|
|
|
|
def locks
|
|
if authorized_action(@context, @current_user, :read)
|
|
assets = params[:assets].split(",")
|
|
types = {}
|
|
assets.each do |asset|
|
|
split = asset.split("_")
|
|
id = split.pop
|
|
(types[split.join("_")] ||= []) << id
|
|
end
|
|
locks_hash = Rails.cache.fetch(['locked_for_results', @current_user, Digest::MD5.hexdigest(params[:assets])].cache_key) do
|
|
locks = {}
|
|
types.each do |type, ids|
|
|
if type == 'assignment'
|
|
@context.assignments.active.find_all_by_id(ids).compact.each do |assignment|
|
|
locks[assignment.asset_string] = assignment.locked_for?(@current_user)
|
|
end
|
|
elsif type == 'quiz'
|
|
@context.quizzes.active.include_assignment.find_all_by_id(ids).compact.each do |quiz|
|
|
locks[quiz.asset_string] = quiz.locked_for?(@current_user)
|
|
end
|
|
elsif type == 'discussion_topic'
|
|
@context.discussion_topics.active.find_all_by_id(ids).compact.each do |topic|
|
|
locks[topic.asset_string] = topic.locked_for?(@current_user)
|
|
end
|
|
end
|
|
end
|
|
locks
|
|
end
|
|
render :json => locks_hash.to_json
|
|
end
|
|
end
|
|
|
|
def self_unenrollment
|
|
get_context
|
|
unless @context_enrollment && params[:self_unenrollment] && params[:self_unenrollment] == @context_enrollment.uuid && @context_enrollment.self_enrolled?
|
|
redirect_to course_url(@context)
|
|
return
|
|
end
|
|
@context_enrollment.complete
|
|
redirect_to course_url(@context)
|
|
end
|
|
|
|
def self_enrollment
|
|
get_context
|
|
unless @context.self_enrollment && params[:self_enrollment] && @context.self_enrollment_codes.include?(params[:self_enrollment])
|
|
return redirect_to course_url(@context)
|
|
end
|
|
unless @current_user || @context.root_account.open_registration?
|
|
store_location
|
|
flash[:notice] = t('notices.login_required', "Please log in to join this course.")
|
|
return redirect_to login_url
|
|
end
|
|
if @current_user
|
|
@enrollment = @context.self_enroll_student(@current_user)
|
|
flash[:notice] = t('notices.enrolled', "You are now enrolled in this course.")
|
|
return redirect_to course_url(@context)
|
|
end
|
|
if params[:email]
|
|
begin
|
|
address = TMail::Address::parse(params[:email])
|
|
rescue
|
|
flash[:error] = t('errors.invalid_email', "Invalid e-mail address, please try again.")
|
|
render :action => 'open_enrollment'
|
|
return
|
|
end
|
|
user = User.new(:name => address.name || address.address)
|
|
user.communication_channels.build(:path => address.address)
|
|
user.workflow_state = 'creation_pending'
|
|
user.save!
|
|
@enrollment = @context.enroll_student(user)
|
|
@enrollment.update_attribute(:self_enrolled, true)
|
|
return render :action => 'open_enrollment_confirmed'
|
|
end
|
|
render :action => 'open_enrollment'
|
|
end
|
|
|
|
def check_pending_teacher
|
|
store_location if @context.created?
|
|
if session[:saved_course_uuid] == @context.uuid
|
|
@context_just_saved = true
|
|
session.delete(:saved_course_uuid)
|
|
end
|
|
return unless session[:claimed_course_uuids] && session[:claimed_enrollment_uuids]
|
|
if session[:claimed_course_uuids].include?(@context.uuid)
|
|
session[:claimed_enrollment_uuids].each do |uuid|
|
|
e = @context.enrollments.find_by_uuid(uuid)
|
|
@pending_teacher = e.user if e
|
|
end
|
|
end
|
|
end
|
|
protected :check_pending_teacher
|
|
|
|
def check_unknown_user
|
|
@public_view = true unless @current_user && @context.grants_right?(@current_user, session, :read_roster)
|
|
end
|
|
protected :check_unknown_user
|
|
|
|
def check_for_xlist
|
|
return false unless @current_user.present? && @context_enrollment.blank?
|
|
xlist_enrollment = @current_user.enrollments.scoped({
|
|
:joins => :course_section,
|
|
:conditions => { :course_sections => { :nonxlist_course_id => @context.id } },
|
|
}).first
|
|
if xlist_enrollment.present?
|
|
redirect_params = {}
|
|
redirect_params[:invitation] = params[:invitation] if params[:invitation].present?
|
|
redirect_to course_path(xlist_enrollment.course_id, redirect_params)
|
|
return true
|
|
end
|
|
false
|
|
end
|
|
protected :check_for_xlist
|
|
|
|
# @API Get a single course
|
|
# Return information on a single course.
|
|
#
|
|
# Accepts the same include[] parameters as the list action, and returns a
|
|
# single course with the same fields as that action.
|
|
def show
|
|
if api_request?
|
|
@context = api_find(Course, params[:id])
|
|
if authorized_action(@context, @current_user, :read)
|
|
enrollments = @context.current_enrollments.all(:conditions => { :user_id => @current_user.id })
|
|
includes = Set.new(Array(params[:include]))
|
|
render :json => course_json(@context, @current_user, session, includes, enrollments)
|
|
end
|
|
return
|
|
end
|
|
|
|
@context = Course.find(params[:id])
|
|
if request.xhr?
|
|
if authorized_action(@context, @current_user, [:read, :read_as_admin])
|
|
if is_authorized_action?(@context, @current_user, [:manage_students, :manage_admin_users])
|
|
render :json => @context.to_json(:include => {:current_enrollments => {:methods => :email}})
|
|
else
|
|
render :json => @context.to_json
|
|
end
|
|
end
|
|
return
|
|
end
|
|
|
|
@context_enrollment = @context.enrollments.find_by_user_id(@current_user.id) if @context && @current_user
|
|
return if check_for_xlist
|
|
@unauthorized_message = t('unauthorized.invalid_link', "The enrollment link you used appears to no longer be valid. Please contact the course instructor and make sure you're still correctly enrolled.") if params[:invitation]
|
|
claim_course if session[:claim_course_uuid] || params[:verification]
|
|
@context.claim if @context.created?
|
|
return if check_enrollment
|
|
check_pending_teacher
|
|
check_unknown_user
|
|
@user_groups = @current_user.group_memberships_for(@context) if @current_user
|
|
|
|
if !@context.grants_right?(@current_user, session, :read) && @context.grants_right?(@current_user, session, :read_as_admin)
|
|
return redirect_to course_settings_path(@context.id)
|
|
end
|
|
|
|
@context_enrollment ||= @pending_enrollment
|
|
if is_authorized_action?(@context, @current_user, :read)
|
|
if @current_user && @context.grants_right?(@current_user, session, :manage_grades)
|
|
@assignments_needing_publishing = @context.assignments.active.need_publishing || []
|
|
end
|
|
|
|
add_crumb(@context.short_name, url_for(@context), :id => "crumb_#{@context.asset_string}")
|
|
|
|
@course_home_view = (params[:view] == "feed" && 'feed') || @context.default_view || 'feed'
|
|
|
|
@contexts = [@context]
|
|
case @course_home_view
|
|
when "wiki"
|
|
@wiki = @context.wiki
|
|
@page = @wiki.wiki_page
|
|
when 'assignments'
|
|
add_crumb(t('#crumbs.assignments', "Assignments"))
|
|
get_sorted_assignments
|
|
when 'modules'
|
|
add_crumb(t('#crumbs.modules', "Modules"))
|
|
@modules = @context.context_modules.active
|
|
@collapsed_modules = ContextModuleProgression.for_user(@current_user).for_modules(@modules).scoped(:select => 'context_module_id, collapsed').select{|p| p.collapsed? }.map(&:context_module_id)
|
|
when 'syllabus'
|
|
add_crumb(t('#crumbs.syllabus', "Syllabus"))
|
|
@groups = @context.assignment_groups.active.find(:all, :order => 'position, name')
|
|
@events = @context.calendar_events.active.to_a
|
|
@events.concat @context.assignments.active.to_a
|
|
@undated_events = @events.select {|e| e.start_at == nil}
|
|
@dates = (@events.select {|e| e.start_at != nil}).map {|e| e.start_at.to_date}.uniq.sort.sort
|
|
else
|
|
@active_tab = "home"
|
|
if @context.grants_right?(@current_user, session, :manage_groups)
|
|
@contexts += @context.groups
|
|
else
|
|
@contexts += @user_groups if @user_groups
|
|
end
|
|
@current_conferences = @context.web_conferences.select{|c| c.active? && c.users.include?(@current_user) }
|
|
end
|
|
|
|
if @current_user and (@show_recent_feedback = @context.user_is_student?(@current_user))
|
|
@recent_feedback = (@current_user && @current_user.recent_feedback(:contexts => @contexts)) || []
|
|
end
|
|
else
|
|
# clear notices that would have been displayed as a result of processing
|
|
# an enrollment invitation, since we're giving an error
|
|
flash[:notice] = nil
|
|
render_unauthorized_action(@context)
|
|
end
|
|
end
|
|
|
|
def switch_role
|
|
@enrollments = @context.enrollments.scoped({:conditions => ['workflow_state = ?', 'active']}).for_user(@current_user)
|
|
@enrollment = @enrollments.sort_by{|e| [e.state_sortable, e.rank_sortable] }.first
|
|
if params[:role] == 'revert'
|
|
session.delete("role_course_#{@context.id}")
|
|
flash[:notice] = t('notices.role_restored', "Your default role and permissions have been restored")
|
|
elsif (@enrollment && @enrollment.can_switch_to?(params[:role])) || @context.grants_right?(@current_user, session, :manage_admin_users)
|
|
@temp_enrollment = Enrollment.typed_enrollment(params[:role]).new rescue nil
|
|
if @temp_enrollment
|
|
session["role_course_#{@context.id}"] = params[:role]
|
|
session[:session_affects_permissions] = true
|
|
flash[:notice] = t('notices.role_switched', "You have switched roles for this course. You will now see the course in this new role: %{enrollment_type}", :enrollment_type => @temp_enrollment.readable_type)
|
|
else
|
|
flash[:error] = t('errors.invalid_role', "Invalid role type")
|
|
end
|
|
else
|
|
flash[:error] = t('errors.unauthorized.switch_roles', "You do not have permission to switch roles")
|
|
end
|
|
redirect_to course_url(@context)
|
|
end
|
|
|
|
def confirm_action
|
|
get_context
|
|
params[:event] ||= (@context.claimed? || @context.created? || @context.completed?) ? 'delete' : 'conclude'
|
|
return unless authorized_action(@context, @current_user, permission_for_event(params[:event]))
|
|
end
|
|
|
|
def conclude_user
|
|
get_context
|
|
@enrollment = @context.enrollments.find(params[:id])
|
|
if @enrollment.can_be_concluded_by(@current_user, @context, session)
|
|
respond_to do |format|
|
|
if @enrollment.conclude
|
|
format.json { render :json => @enrollment.to_json }
|
|
else
|
|
format.json { render :json => @enrollment.to_json, :status => :bad_request }
|
|
end
|
|
end
|
|
else
|
|
authorized_action(@context, @current_user, :permission_fail)
|
|
end
|
|
end
|
|
|
|
def unconclude_user
|
|
get_context
|
|
@enrollment = @context.enrollments.find(params[:id])
|
|
can_remove = @enrollment.is_a?(StudentEnrollment) && @context.grants_right?(@current_user, session, :manage_students)
|
|
can_remove ||= @context.grants_right?(@current_user, session, :manage_admin_users)
|
|
if can_remove
|
|
respond_to do |format|
|
|
@enrollment.workflow_state = 'active'
|
|
if @enrollment.save
|
|
format.json { render :json => @enrollment.to_json }
|
|
else
|
|
format.json { render :json => @enrollment.to_json, :status => :bad_request }
|
|
end
|
|
end
|
|
else
|
|
authorized_action(@context, @current_user, :permission_fail)
|
|
end
|
|
end
|
|
|
|
def limit_user
|
|
get_context
|
|
@user = @context.users.find(params[:id])
|
|
if authorized_action(@context, @current_user, :manage_admin_users)
|
|
if params[:limit] == "1"
|
|
Enrollment.limit_privileges_to_course_section!(@context, @user, true)
|
|
render :json => {:limited => true}.to_json
|
|
else
|
|
Enrollment.limit_privileges_to_course_section!(@context, @user, false)
|
|
render :json => {:limited => false}.to_json
|
|
end
|
|
else
|
|
authorized_action(@context, @current_user, :permission_fail)
|
|
end
|
|
end
|
|
|
|
def unenroll_user
|
|
get_context
|
|
@enrollment = @context.enrollments.find(params[:id])
|
|
if @enrollment.can_be_deleted_by(@current_user, @context, session)
|
|
respond_to do |format|
|
|
if (!@enrollment.defined_by_sis? || @context.grants_right?(@current_user, session, :manage_account_settings)) && @enrollment.destroy
|
|
format.json { render :json => @enrollment.to_json }
|
|
else
|
|
format.json { render :json => @enrollment.to_json, :status => :bad_request }
|
|
end
|
|
end
|
|
else
|
|
authorized_action(@context, @current_user, :permission_fail)
|
|
end
|
|
end
|
|
|
|
def enroll_users
|
|
get_context
|
|
params[:enrollment_type] ||= 'StudentEnrollment'
|
|
params[:course_section_id] ||= @context.default_section.id
|
|
if @current_user && @current_user.can_create_enrollment_for?(@context, session, params[:enrollment_type])
|
|
params[:user_list] ||= ""
|
|
|
|
respond_to do |format|
|
|
if (@enrollments = EnrollmentsFromUserList.process(UserList.new(params[:user_list], @context.root_account, @context.user_list_search_mode_for(@current_user)), @context, :course_section_id => params[:course_section_id], :enrollment_type => params[:enrollment_type], :limit_privileges_to_course_section => params[:limit_privileges_to_course_section] == '1'))
|
|
format.json do
|
|
Enrollment.send(:preload_associations, @enrollments, [:course_section, {:user => [:communication_channel, :pseudonym]}])
|
|
json = @enrollments.map { |e|
|
|
{ 'enrollment' =>
|
|
{ 'associated_user_id' => e.associated_user_id,
|
|
'communication_channel_id' => e.user.communication_channel.try(:id),
|
|
'email' => e.email,
|
|
'id' => e.id,
|
|
'name' => (e.user.last_name_first || e.user.name),
|
|
'pseudonym_id' => e.user.pseudonym.try(:id),
|
|
'section' => e.course_section.display_name,
|
|
'short_name' => e.user.short_name,
|
|
'type' => e.type,
|
|
'user_id' => e.user_id,
|
|
'workflow_state' => e.workflow_state
|
|
}
|
|
}
|
|
}
|
|
render :json => json
|
|
end
|
|
else
|
|
format.json { render :json => "", :status => :bad_request }
|
|
end
|
|
end
|
|
else
|
|
authorized_action(@context, @current_user, :permission_fail)
|
|
end
|
|
end
|
|
|
|
def link_enrollment
|
|
get_context
|
|
if authorized_action(@context, @current_user, :manage_admin_users)
|
|
enrollment = @context.observer_enrollments.find(params[:enrollment_id])
|
|
student = nil
|
|
student = @context.students.find(params[:student_id]) if params[:student_id] != 'none'
|
|
enrollment.update_attribute(:associated_user_id, student && student.id)
|
|
render :json => enrollment.to_json(:methods => :associated_user_name)
|
|
end
|
|
end
|
|
|
|
def move_enrollment
|
|
get_context
|
|
@enrollment = @context.enrollments.find(params[:id])
|
|
can_move = [StudentEnrollment, ObserverEnrollment].include?(@enrollment.class) && @context.grants_right?(@current_user, session, :manage_students)
|
|
can_move ||= @context.grants_right?(@current_user, session, :manage_admin_users)
|
|
can_move &&= @context.grants_right?(@current_user, session, :manage_account_settings) if @enrollment.defined_by_sis?
|
|
if can_move
|
|
respond_to do |format|
|
|
# ensure user_id,section_id,type,associated_user_id is unique (this
|
|
# will become a DB constraint eventually)
|
|
@possible_dup = @context.enrollments.find(:first, :conditions =>
|
|
["id <> ? AND user_id = ? AND course_section_id = ? AND type = ? AND (associated_user_id IS NULL OR associated_user_id = ?)",
|
|
@enrollment.id, @enrollment.user_id, params[:course_section_id], @enrollment.type, @enrollment.associated_user_id])
|
|
if @possible_dup.present?
|
|
format.json { render :json => @enrollment.to_json, :status => :forbidden }
|
|
else
|
|
@enrollment.course_section = @context.course_sections.find(params[:course_section_id])
|
|
@enrollment.save!
|
|
|
|
format.json { render :json => @enrollment.to_json }
|
|
end
|
|
end
|
|
else
|
|
authorized_action(@context, @current_user, :permission_fail)
|
|
end
|
|
end
|
|
|
|
def copy
|
|
get_context
|
|
authorized_action(@context, @current_user, :read) &&
|
|
authorized_action(@context, @current_user, :read_as_admin) &&
|
|
authorized_action(@domain_root_account.manually_created_courses_account, @current_user, [:create_courses, :manage_courses])
|
|
end
|
|
|
|
def copy_course
|
|
get_context
|
|
if authorized_action(@context, @current_user, :read) &&
|
|
authorized_action(@context, @current_user, :read_as_admin)
|
|
args = params[:course].slice(:name, :start_at, :conclude_at, :course_code)
|
|
account = @context.account
|
|
if params[:course][:account_id]
|
|
account = Account.find(params[:course][:account_id])
|
|
end
|
|
account = nil unless account.grants_rights?(@current_user, session, :create_courses, :manage_courses).values.any?
|
|
account ||= @domain_root_account.manually_created_courses_account
|
|
return unless authorized_action(account, @current_user, [:create_courses, :manage_courses])
|
|
if account.grants_rights?(@current_user, session, :manage_courses)
|
|
root_account = account.root_account
|
|
args[:enrollment_term] = if params[:course][:enrollment_term_id].present?
|
|
root_account.enrollment_terms.find_by_id(params[:course][:enrollment_term_id])
|
|
end
|
|
end
|
|
args[:enrollment_term] ||= @context.enrollment_term
|
|
args[:abstract_course] = @context.abstract_course
|
|
args[:account] = account
|
|
@course = @context.account.courses.new
|
|
@course.attributes = args
|
|
@course.workflow_state = 'claimed'
|
|
@course.save
|
|
@course.enroll_user(@current_user, 'TeacherEnrollment', :enrollment_state => 'active')
|
|
redirect_to course_import_choose_content_url(@course, 'source_course' => @context.id)
|
|
end
|
|
end
|
|
|
|
def update
|
|
@course = Course.find(params[:id])
|
|
if authorized_action(@course, @current_user, :update)
|
|
root_account_id = params[:course].delete :root_account_id
|
|
if root_account_id && Account.site_admin.grants_right?(@current_user, session, :manage_courses)
|
|
@course.root_account = Account.root_accounts.find(root_account_id)
|
|
end
|
|
standard_id = params[:course].delete :grading_standard_id
|
|
if standard_id && @course.grants_right?(@current_user, session, :manage_grades)
|
|
@course.grading_standard = GradingStandard.standards_for(@course).detect{|s| s.id == standard_id.to_i }
|
|
end
|
|
if @course.root_account.grants_right?(@current_user, session, :manage_courses)
|
|
if params[:course][:account_id]
|
|
account = Account.find(params[:course].delete(:account_id))
|
|
@course.account = account if account != @course.account && account.grants_right?(@current_user, session, :manage)
|
|
end
|
|
if params[:course][:enrollment_term_id]
|
|
enrollment_term = @course.root_account.enrollment_terms.active.find(params[:course].delete(:enrollment_term_id))
|
|
@course.enrollment_term = enrollment_term if enrollment_term != @course.enrollment_term
|
|
end
|
|
else
|
|
params[:course].delete :account_id
|
|
params[:course].delete :enrollment_term_id
|
|
end
|
|
if !@course.account.grants_right?(@current_user, session, :manage_courses)
|
|
params[:course].delete :storage_quota
|
|
params[:course].delete :storage_quota_mb
|
|
if @course.root_account.settings[:prevent_course_renaming_by_teachers]
|
|
params[:course].delete :name
|
|
params[:course].delete :course_code
|
|
end
|
|
end
|
|
if sis_id = params[:course].delete(:sis_source_id)
|
|
if sis_id != @course.sis_source_id && @course.root_account.grants_right?(@current_user, session, :manage_sis)
|
|
if sis_id == ''
|
|
@course.sis_source_id = nil
|
|
else
|
|
@course.sis_source_id = sis_id
|
|
end
|
|
end
|
|
end
|
|
@course.process_event(params[:course].delete(:event)) if params[:course][:event] && @course.grants_right?(@current_user, session, :change_course_state)
|
|
respond_to do |format|
|
|
@default_wiki_editing_roles_was = @course.default_wiki_editing_roles
|
|
if @course.update_attributes(params[:course])
|
|
@current_user.touch
|
|
if params[:update_default_pages]
|
|
@course.wiki.update_default_wiki_page_roles(@course.default_wiki_editing_roles, @default_wiki_editing_roles_was)
|
|
end
|
|
flash[:notice] = t('notices.updated', 'Course was successfully updated.')
|
|
format.html { redirect_to((!params[:continue_to] || params[:continue_to].empty?) ? course_url(@course) : params[:continue_to]) }
|
|
format.json { render :json => @course.to_json(:methods => [:readable_license, :quota, :account_name, :term_name, :grading_standard_title, :storage_quota_mb]), :status => :ok }
|
|
else
|
|
format.html { render :action => "edit" }
|
|
format.json { render :json => @course.errors.to_json, :status => :bad_request }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def public_feed
|
|
return unless get_feed_context(:only => [:course])
|
|
feed = Atom::Feed.new do |f|
|
|
f.title = t('titles.rss_feed', "%{course} Feed", :course => @context.name)
|
|
f.links << Atom::Link.new(:href => course_url(@context), :rel => 'self')
|
|
f.updated = Time.now
|
|
f.id = course_url(@context)
|
|
end
|
|
@entries = []
|
|
@entries.concat @context.assignments.active
|
|
@entries.concat @context.calendar_events.active
|
|
@entries.concat @context.discussion_topics.active.reject{|a| a.locked_for?(@current_user, :check_policies => true) }
|
|
@entries.concat WikiNamespace.default_for_context(@context).wiki.wiki_pages.select{|p| !p.new_record?}
|
|
@entries = @entries.sort_by{|e| e.updated_at}
|
|
@entries.each do |entry|
|
|
feed.entries << entry.to_atom(:context => @context)
|
|
end
|
|
respond_to do |format|
|
|
format.atom { render :text => feed.to_xml }
|
|
end
|
|
end
|
|
|
|
def publish_to_sis
|
|
sis_publish_status(true)
|
|
end
|
|
|
|
def sis_publish_status(publish_grades=false)
|
|
get_context
|
|
return unless authorized_action(@context, @current_user, :manage_grades)
|
|
@context.publish_final_grades(@current_user) if publish_grades
|
|
|
|
processed_grade_publishing_statuses = {}
|
|
grade_publishing_statuses, overall_status = @context.grade_publishing_statuses
|
|
grade_publishing_statuses.each do |message, enrollments|
|
|
processed_grade_publishing_statuses[message] = enrollments.map do |enrollment|
|
|
{ :id => enrollment.user.id,
|
|
:name => enrollment.user.name,
|
|
:sortable_name => enrollment.user.sortable_name,
|
|
:url => course_user_url(@context, enrollment.user) }
|
|
end
|
|
end
|
|
|
|
render :json => { :sis_publish_overall_status => overall_status,
|
|
:sis_publish_statuses => processed_grade_publishing_statuses }
|
|
end
|
|
|
|
def reset_content
|
|
get_context
|
|
return unless authorized_action(@context, @current_user, :reset_content)
|
|
@new_course = @context.reset_content
|
|
redirect_to course_settings_path(@new_course.id)
|
|
end
|
|
|
|
def student_view
|
|
get_context
|
|
if authorized_action(@context, @current_user, :use_student_view)
|
|
enter_student_view
|
|
end
|
|
end
|
|
|
|
def leave_student_view
|
|
session.delete(:become_user_id)
|
|
return_url = session[:masquerade_return_to]
|
|
session.delete(:masquerade_return_to)
|
|
return return_to(return_url, request.referer || dashboard_url)
|
|
end
|
|
|
|
def reset_test_student
|
|
get_context
|
|
if @current_user.fake_student? && authorized_action(@context, @real_current_user, :use_student_view)
|
|
# destroy the exising student
|
|
@fake_student = @context.student_view_student
|
|
@fake_student.destroy
|
|
flash[:notice] = t('notices.reset_test_student', "The test student has been reset successfully.")
|
|
enter_student_view
|
|
end
|
|
end
|
|
|
|
def enter_student_view
|
|
@fake_student = @context.student_view_student
|
|
session[:become_user_id] = @fake_student.id
|
|
return_url = course_path(@context)
|
|
session.delete(:masquerade_return_to)
|
|
return return_to(return_url, request.referer || dashboard_url)
|
|
end
|
|
protected :enter_student_view
|
|
|
|
def permission_for_event(event)
|
|
case event
|
|
when 'conclude'
|
|
:change_course_state
|
|
when 'delete'
|
|
:delete
|
|
else
|
|
:nothing
|
|
end
|
|
end
|
|
end
|