canvas-lms/app/controllers/announcements_api_controlle...

142 lines
5.7 KiB
Ruby

#
# Copyright (C) 2016 - present 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/>.
#
# @API Announcements
#
# API for retrieving announcements. This API is Announcement-specific.
# See also the Discussion Topics API, which operates on Announcements also.
class AnnouncementsApiController < ApplicationController
include Api::V1::DiscussionTopics
before_action :parse_context_codes, :only => [:index]
before_action :get_dates, :only => [:index]
# @API List announcements
#
# Returns the paginated list of announcements for the given courses and date range. Note that
# a +context_code+ field is added to the responses so you can tell which course each announcement
# belongs to.
#
# @argument context_codes[] [Required]
# List of context_codes to retrieve announcements for (for example, +course_123+). Only courses
# are presently supported. The call will fail unless the caller has View Announcements permission
# in all listed courses.
# @argument start_date [Optional, Date]
# Only return announcements posted since the start_date (inclusive).
# Defaults to 14 days ago. The value should be formatted as: yyyy-mm-dd or ISO 8601 YYYY-MM-DDTHH:MM:SSZ.
# @argument end_date [Optional, Date]
# Only return announcements posted before the end_date (inclusive).
# Defaults to 28 days from start_date. The value should be formatted as: yyyy-mm-dd or ISO 8601 YYYY-MM-DDTHH:MM:SSZ.
# Announcements scheduled for future posting will only be returned to course administrators.
# @argument active_only [Optional, Boolean]
# Only return active announcements that have been published.
# Applies only to requesting users that have permission to view
# unpublished items.
# Defaults to false for users with access to view unpublished items,
# otherwise true and unmodifiable.
#
# @example_request
# curl https://<canvas>/api/v1/announcements?context_codes[]=course_1&context_codes[]=course_2 \
# -H 'Authorization: Bearer <token>'
#
# @example_response
# [{
# "id": 1,
# "title": "Hear ye",
# "message": "Henceforth, all assignments must be...",
# "posted_at": "2017-01-31T22:00:00Z",
# "delayed_post_at": null,
# "context_code": "course_2",
# ...
# }]
#
# @returns [DiscussionTopic]
def index
courses = api_find_all(Course, @course_ids)
return unless courses.all? { |course| authorized_action(course, @current_user, :read_announcements) }
scope = Announcement.where(:context_type => 'Course', :context_id => courses)
include_unpublished = courses.all? { |course| course.grants_right?(@current_user, :view_unpublished_items) }
scope = if include_unpublished && !value_to_boolean(params[:active_only])
scope.where.not(:workflow_state => 'deleted')
else
# workflow state should be 'post_delayed' if delayed_post_at is in the future, but check because other endpoints do
scope.where(:workflow_state => 'active').where('delayed_post_at IS NULL OR delayed_post_at<?', Time.now.utc)
end
@start_date ||= 14.days.ago.beginning_of_day
@end_date ||= @start_date + 28.days
scope = scope.where('COALESCE(delayed_post_at, posted_at) BETWEEN ? AND ?', @start_date, @end_date)
scope = scope.order('COALESCE(delayed_post_at, posted_at) DESC')
@topics = Api.paginate(scope, self, api_v1_announcements_url)
text_only = value_to_boolean(params[:text_only])
render :json => @topics.map { |topic|
discussion_topic_api_json(topic, topic.context, @current_user, session,
{ :user_can_moderate => false, :include_assignment => false,
:include_context_code => true, :text_only => text_only})
}
end
private
def parse_context_codes
context_codes = Array(params[:context_codes])
if context_codes.empty?
return render :json => { :message => 'Missing context_codes' }, :status => :bad_request
end
@course_ids = context_codes.inject([]) do |ids, context_code|
klass, id = ActiveRecord::Base.parse_asset_string(context_code)
unless klass == 'Course'
return render :json => { :message => 'Invalid context_codes; only `course` codes are supported' },
:status => :bad_request
end
ids << id
end
end
def get_dates
if params[:start_date].present?
if params[:start_date] =~ Api::DATE_REGEX
@start_date ||= Time.zone.parse(params[:start_date]).beginning_of_day
elsif params[:start_date] =~ Api::ISO8601_REGEX
@start_date ||= Time.zone.parse(params[:start_date])
else
render :json => { :message => 'Invalid start_date' }, :status => :bad_request
return false
end
end
if params[:end_date].present?
if params[:end_date] =~ Api::DATE_REGEX
@end_date ||= Time.zone.parse(params[:end_date]).end_of_day
elsif params[:end_date] =~ Api::ISO8601_REGEX
@end_date ||= Time.zone.parse(params[:end_date])
else
render :json => { :message => 'Invalid end_date' }, :status => :bad_request
return false
end
end
true
end
end