2011-02-01 09:57:29 +08:00
#
2017-04-28 03:37:23 +08:00
# Copyright (C) 2011 - present Instructure, Inc.
2011-02-01 09:57:29 +08:00
#
# 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/>.
#
2012-07-10 23:23:33 +08:00
# @API Sections
#
# API for accessing section information.
#
2014-02-12 07:24:37 +08:00
# @model Section
2012-07-10 23:23:33 +08:00
# {
2014-02-12 07:24:37 +08:00
# "id": "Section",
# "description": "",
# "properties": {
# "id": {
# "description": "The unique identifier for the section.",
# "example": 1,
# "type": "integer"
# },
# "name": {
# "description": "The name of the section.",
# "example": "Section A",
# "type": "string"
# },
# "sis_section_id": {
2014-02-11 08:13:53 +08:00
# "description": "The sis id of the section. This field is only included if the user has permission to view SIS information.",
# "example": "s34643",
2014-02-12 07:24:37 +08:00
# "type": "string"
# },
2014-01-15 14:48:27 +08:00
# "integration_id": {
2014-06-05 02:47:01 +08:00
# "description": "Optional: The integration ID of the section. This field is only included if the user has permission to view SIS information.",
2014-01-15 14:48:27 +08:00
# "example": "3452342345",
# "type": "string"
# },
2014-02-11 08:13:53 +08:00
# "sis_import_id": {
# "description": "The unique identifier for the SIS import if created through SIS. This field is only included if the user has permission to manage SIS information.",
# "example": 47,
# "type": "integer"
# },
2014-02-12 07:24:37 +08:00
# "course_id": {
2014-05-15 23:10:39 +08:00
# "description": "The unique Canvas identifier for the course in which the section belongs",
# "example": 7,
# "type": "integer"
# },
# "sis_course_id": {
2014-06-05 02:47:01 +08:00
# "description": "The unique SIS identifier for the course in which the section belongs. This field is only included if the user has permission to view SIS information.",
2014-02-12 07:24:37 +08:00
# "example": 7,
2014-11-06 04:44:15 +08:00
# "type": "string"
2014-02-12 07:24:37 +08:00
# },
# "start_at": {
# "description": "the start date for the section, if applicable",
# "example": "2012-06-01T00:00:00-06:00",
# "type": "datetime"
# },
# "end_at": {
# "description": "the end date for the section, if applicable",
# "type": "datetime"
# },
# "nonxlist_course_id": {
# "description": "The unique identifier of the original course of a cross-listed section",
# "type": "integer"
2014-12-30 03:44:17 +08:00
# },
# "total_students": {
# "description": "optional: the total number of active and invited students in the section",
# "example": 13,
# "type": "integer"
2014-02-12 07:24:37 +08:00
# }
# }
2012-07-10 23:23:33 +08:00
# }
2014-02-12 07:24:37 +08:00
#
2011-02-01 09:57:29 +08:00
class SectionsController < ApplicationController
2017-02-09 01:08:10 +08:00
before_action :require_context
before_action :require_section , :except = > [ :index , :create ]
2012-07-10 23:23:33 +08:00
include Api :: V1 :: Section
# @API List course sections
# Returns the list of sections for this course.
#
2014-12-17 10:36:27 +08:00
# @argument include[] [String, "students"|"avatar_url"|"enrollments"|"total_students"|"passback_status"]
2013-08-15 00:19:44 +08:00
# - "students": Associations to include with the group. Note: this is only
# available if you have permission to view users or grades in the course
# - "avatar_url": Include the avatar URLs for students returned.
2014-11-06 01:22:32 +08:00
# - "enrollments": If 'students' is also included, return the section
2016-06-24 08:05:47 +08:00
# enrollment for each student
2014-12-30 03:44:17 +08:00
# - "total_students": Returns the total amount of active and invited students
2016-06-24 08:05:47 +08:00
# for the course section
2014-12-17 10:36:27 +08:00
# - "passback_status": Include the grade passback status.
2012-07-10 23:23:33 +08:00
#
# @returns [Section]
def index
2013-06-27 23:50:18 +08:00
if authorized_action ( @context , @current_user , [ :read , :read_roster , :view_all_grades , :manage_grades ] )
2015-01-30 06:51:26 +08:00
if params [ :include ] . present? && ! @context . grants_any_right? ( @current_user , session , :read_roster , :view_all_grades , :manage_grades )
2013-06-27 23:50:18 +08:00
params [ :include ] = nil
end
2012-07-10 23:23:33 +08:00
2013-06-27 23:50:18 +08:00
includes = Array ( params [ :include ] )
2012-07-10 23:23:33 +08:00
2017-02-27 22:42:24 +08:00
sections = @context . active_course_sections . order ( CourseSection . best_unicode_collation_key ( 'name' ) )
2016-06-24 08:05:47 +08:00
2017-02-27 22:42:24 +08:00
unless params [ :all ] . present?
sections = Api . paginate ( sections , self , api_v1_course_sections_url )
end
2016-06-24 08:05:47 +08:00
render :json = > sections_json ( sections , @current_user , session , includes )
2012-07-10 23:23:33 +08:00
end
end
2012-11-10 06:32:42 +08:00
# @API Create course section
# Creates a new section for this course.
#
2013-08-15 00:19:44 +08:00
# @argument course_section[name] [String]
# The name of the section
#
2014-08-15 07:51:24 +08:00
# @argument course_section[sis_section_id] [String]
2013-08-15 00:19:44 +08:00
# The sis ID of the section
#
2014-08-15 07:51:24 +08:00
# @argument course_section[start_at] [DateTime]
2013-08-15 00:19:44 +08:00
# Section start date in ISO8601 format, e.g. 2011-01-01T01:00Z
#
2014-08-15 07:51:24 +08:00
# @argument course_section[end_at] [DateTime]
2013-08-15 00:19:44 +08:00
# Section end date in ISO8601 format. e.g. 2011-01-01T01:00Z
2012-11-10 06:32:42 +08:00
#
2014-12-27 00:15:03 +08:00
# @argument course_section[restrict_enrollments_to_section_dates] [Boolean]
# Set to true to restrict user enrollments to the start and end dates of the section.
#
2015-05-23 03:08:05 +08:00
# @argument enable_sis_reactivation [Boolean]
# When true, will first try to re-activate a deleted section with matching sis_section_id if possible.
#
2012-11-10 06:32:42 +08:00
# @returns Section
2011-02-01 09:57:29 +08:00
def create
2016-01-22 04:19:56 +08:00
if authorized_action ( @context . course_sections . temp_record , @current_user , :create )
2012-11-10 06:32:42 +08:00
sis_section_id = params [ :course_section ] . try ( :delete , :sis_section_id )
2015-05-23 03:08:05 +08:00
can_manage_sis = api_request? && sis_section_id . present? &&
@context . root_account . grants_right? ( @current_user , session , :manage_sis )
if can_manage_sis && value_to_boolean ( params [ :enable_sis_reactivation ] )
@section = @context . course_sections . where ( :sis_source_id = > sis_section_id , :workflow_state = > 'deleted' ) . first
@section . workflow_state = 'active' if @section
end
2017-01-11 05:28:14 +08:00
@section || = @context . course_sections . build ( course_section_params )
2015-05-23 03:08:05 +08:00
@section . sis_source_id = sis_section_id if can_manage_sis
2011-02-01 09:57:29 +08:00
respond_to do | format |
if @section . save
2013-06-28 22:44:31 +08:00
@context . touch
2011-06-22 02:38:28 +08:00
flash [ :notice ] = t ( 'section_created' , " Section successfully created! " )
2011-08-31 04:32:58 +08:00
format . html { redirect_to course_settings_url ( @context ) }
2013-08-23 06:22:14 +08:00
format . json { render :json = > ( api_request? ? section_json ( @section , @current_user , session , [ ] ) : @section ) }
2011-02-01 09:57:29 +08:00
else
2011-06-22 02:38:28 +08:00
flash [ :error ] = t ( 'section_creation_failed' , " Section creation failed " )
2011-08-31 04:32:58 +08:00
format . html { redirect_to course_settings_url ( @context ) }
2013-08-23 06:22:14 +08:00
format . json { render :json = > @section . errors , :status = > :bad_request }
2011-02-01 09:57:29 +08:00
end
end
end
end
2012-11-10 06:32:42 +08:00
def require_section
case @context
when Course
section_id = params [ :section_id ] || params [ :id ]
2014-02-12 07:58:09 +08:00
@section = api_find ( @context . active_course_sections , section_id )
2012-11-10 06:32:42 +08:00
when CourseSection
@section = @context
raise ActiveRecord :: RecordNotFound if @section . deleted? || @section . course . try ( :deleted? )
else
raise ActiveRecord :: RecordNotFound
end
end
2011-04-16 14:22:55 +08:00
def crosslist_check
course_id = params [ :new_course_id ]
# cross-listing should only be allowed within the same root account
2014-08-21 06:35:07 +08:00
@new_course = @section . root_account . all_courses . not_deleted . where ( id : course_id ) . first if course_id =~ Api :: ID_REGEX
@new_course || = @section . root_account . all_courses . not_deleted . where ( sis_source_id : course_id ) . first if course_id . present?
2016-02-26 04:23:40 +08:00
allowed = @new_course && @section . grants_right? ( @current_user , session , :update ) && @new_course . grants_right? ( @current_user , session , :manage )
2011-04-16 14:22:55 +08:00
res = { :allowed = > ! ! allowed }
if allowed
@account = @new_course . account
2013-08-23 06:22:14 +08:00
res [ :section ] = @section . as_json ( include_root : false )
res [ :course ] = @new_course . as_json ( include_root : false )
res [ :account ] = @account . as_json ( include_root : false )
2011-04-16 14:22:55 +08:00
end
2013-08-23 06:22:14 +08:00
render :json = > res
2011-04-16 14:22:55 +08:00
end
2012-11-10 06:32:42 +08:00
# @API Cross-list a Section
# Move the Section to another course. The new course may be in a different account (department),
# but must belong to the same root account (institution).
#
# @returns Section
2011-04-16 14:22:55 +08:00
def crosslist
2014-02-12 07:58:09 +08:00
@new_course = api_find ( @section . root_account . all_courses . not_deleted , params [ :new_course_id ] )
2011-08-16 06:34:57 +08:00
if authorized_action ( @section , @current_user , :update ) && authorized_action ( @new_course , @current_user , :manage )
2011-04-16 14:22:55 +08:00
@section . crosslist_to_course @new_course
respond_to do | format |
2011-06-22 02:38:28 +08:00
flash [ :notice ] = t ( 'section_crosslisted' , " Section successfully cross-listed! " )
2011-04-16 14:22:55 +08:00
format . html { redirect_to named_context_url ( @new_course , :context_section_url , @section . id ) }
2013-08-23 06:22:14 +08:00
format . json { render :json = > ( api_request? ? section_json ( @section , @current_user , session , [ ] ) : @section ) }
2011-04-16 14:22:55 +08:00
end
end
end
2015-12-22 05:03:24 +08:00
2012-11-10 06:32:42 +08:00
# @API De-cross-list a Section
# Undo cross-listing of a Section, returning it to its original course.
#
# @returns Section
2011-04-16 14:22:55 +08:00
def uncrosslist
@new_course = @section . nonxlist_course
2012-11-10 06:32:42 +08:00
return render ( :json = > { :message = > " section is not cross-listed " } , :status = > :bad_request ) if @new_course . nil?
2011-08-16 06:34:57 +08:00
if authorized_action ( @section , @current_user , :update ) && authorized_action ( @new_course , @current_user , :manage )
2011-04-16 14:22:55 +08:00
@section . uncrosslist
respond_to do | format |
2011-06-22 02:38:28 +08:00
flash [ :notice ] = t ( 'section_decrosslisted' , " Section successfully de-cross-listed! " )
2011-04-16 14:22:55 +08:00
format . html { redirect_to named_context_url ( @new_course , :context_section_url , @section . id ) }
2013-08-23 06:22:14 +08:00
format . json { render :json = > ( api_request? ? section_json ( @section , @current_user , session , [ ] ) : @section ) }
2011-04-16 14:22:55 +08:00
end
end
end
2012-11-10 06:32:42 +08:00
# @API Edit a section
2016-03-08 20:17:20 +08:00
# Modify an existing section.
#
# @argument course_section[name] [String]
# The name of the section
#
# @argument course_section[sis_section_id] [String]
# The sis ID of the section
#
# @argument course_section[start_at] [DateTime]
# Section start date in ISO8601 format, e.g. 2011-01-01T01:00Z
#
# @argument course_section[end_at] [DateTime]
# Section end date in ISO8601 format. e.g. 2011-01-01T01:00Z
#
# @argument course_section[restrict_enrollments_to_section_dates] [Boolean]
# Set to true to restrict user enrollments to the start and end dates of the section.
2012-11-10 06:32:42 +08:00
#
# @returns Section
2011-02-01 09:57:29 +08:00
def update
2012-11-10 06:32:42 +08:00
params [ :course_section ] || = { }
2011-02-01 09:57:29 +08:00
if authorized_action ( @section , @current_user , :update )
2012-11-10 06:32:42 +08:00
params [ :course_section ] [ :sis_source_id ] = params [ :course_section ] . delete ( :sis_section_id ) if api_request?
2011-06-01 04:47:28 +08:00
if sis_id = params [ :course_section ] . delete ( :sis_source_id )
if sis_id != @section . sis_source_id && @section . root_account . grants_right? ( @current_user , session , :manage_sis )
if sis_id == ''
@section . sis_source_id = nil
else
@section . sis_source_id = sis_id
end
end
end
2011-02-01 09:57:29 +08:00
respond_to do | format |
2017-01-11 05:28:14 +08:00
if @section . update_attributes ( course_section_params )
2013-06-28 22:44:31 +08:00
@context . touch
2011-06-22 02:38:28 +08:00
flash [ :notice ] = t ( 'section_updated' , " Section successfully updated! " )
2011-02-01 09:57:29 +08:00
format . html { redirect_to course_section_url ( @context , @section ) }
2013-08-23 06:22:14 +08:00
format . json { render :json = > ( api_request? ? section_json ( @section , @current_user , session , [ ] ) : @section ) }
2011-02-01 09:57:29 +08:00
else
2011-06-22 02:38:28 +08:00
flash [ :error ] = t ( 'section_update_error' , " Section update failed " )
2011-02-01 09:57:29 +08:00
format . html { redirect_to course_section_url ( @context , @section ) }
2013-08-23 06:22:14 +08:00
format . json { render :json = > @section . errors , :status = > :bad_request }
2011-02-01 09:57:29 +08:00
end
end
end
end
2012-01-24 08:21:55 +08:00
2012-07-10 23:23:33 +08:00
# @API Get section information
# Gets details about a specific section
#
2016-09-20 23:53:28 +08:00
# @argument include[] [String, "students"|"avatar_url"|"enrollments"|"total_students"|"passback_status"]
# - "students": Associations to include with the group. Note: this is only
# available if you have permission to view users or grades in the course
# - "avatar_url": Include the avatar URLs for students returned.
# - "enrollments": If 'students' is also included, return the section
# enrollment for each student
# - "total_students": Returns the total amount of active and invited students
# for the course section
# - "passback_status": Include the grade passback status.
#
2012-07-10 23:23:33 +08:00
# @returns Section
2011-02-01 09:57:29 +08:00
def show
2012-09-20 00:49:03 +08:00
if authorized_action ( @section , @current_user , :read )
respond_to do | format |
format . html do
add_crumb ( @section . name , named_context_url ( @context , :context_section_url , @section ) )
2013-03-08 07:49:24 +08:00
@enrollments_count = @section . enrollments . not_fake . where ( :workflow_state = > 'active' ) . count
@completed_enrollments_count = @section . enrollments . not_fake . where ( :workflow_state = > 'completed' ) . count
@pending_enrollments_count = @section . enrollments . not_fake . where ( :workflow_state = > %w{ invited pending } ) . count
@student_enrollments_count = @section . enrollments . not_fake . where ( :type = > 'StudentEnrollment' ) . count
2016-12-06 05:54:21 +08:00
can_manage_students = @context . grants_right? ( @current_user , session , :manage_students ) || @context . grants_right? ( @current_user , session , :manage_admin_users )
2012-09-20 00:49:03 +08:00
js_env (
:PERMISSIONS = > {
2016-12-06 05:54:21 +08:00
:manage_students = > can_manage_students ,
2012-09-20 00:49:03 +08:00
:manage_account_settings = > @context . account . grants_right? ( @current_user , session , :manage_account_settings )
} )
2017-01-24 03:02:20 +08:00
if @context . grants_right? ( @current_user , session , :manage )
js_env STUDENT_CONTEXT_CARDS_ENABLED : @domain_root_account . feature_enabled? ( :student_context_cards )
end
2012-09-20 00:49:03 +08:00
end
2016-09-20 23:53:28 +08:00
format . json { render :json = > section_json ( @section , @current_user , session , Array ( params [ :include ] ) ) }
2012-07-10 23:23:33 +08:00
end
end
2011-02-01 09:57:29 +08:00
end
2012-01-24 08:21:55 +08:00
2012-11-10 06:32:42 +08:00
# @API Delete a section
# Delete an existing section. Returns the former Section.
#
# @returns Section
2011-02-01 09:57:29 +08:00
def destroy
if authorized_action ( @section , @current_user , :delete )
respond_to do | format |
2016-08-23 04:15:57 +08:00
if @section . deletable?
2011-02-01 09:57:29 +08:00
@section . destroy
2013-06-28 22:44:31 +08:00
@context . touch
2011-06-22 02:38:28 +08:00
flash [ :notice ] = t ( 'section_deleted' , " Course section successfully deleted! " )
2011-08-31 04:32:58 +08:00
format . html { redirect_to course_settings_url ( @context ) }
2013-08-23 06:22:14 +08:00
format . json { render :json = > ( api_request? ? section_json ( @section , @current_user , session , [ ] ) : @section ) }
2011-02-01 09:57:29 +08:00
else
2011-06-22 02:38:28 +08:00
flash [ :error ] = t ( 'section_delete_not_allowed' , " You can't delete a section that has enrollments " )
2011-02-01 09:57:29 +08:00
format . html { redirect_to course_section_url ( @context , @section ) }
2013-08-23 06:22:14 +08:00
format . json { render :json = > ( api_request? ? { :message = > " You can't delete a section that has enrollments " } : @section ) , :status = > :bad_request }
2011-02-01 09:57:29 +08:00
end
end
end
end
2017-01-11 05:28:14 +08:00
protected
def course_section_params
params [ :course_section ] ? params [ :course_section ] . permit ( :name , :start_at , :end_at , :restrict_enrollments_to_section_dates ) : { }
end
2011-02-01 09:57:29 +08:00
end