2011-11-04 05:51:29 +08:00
|
|
|
#
|
|
|
|
# Copyright (C) 2011 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 Discussion Topics
|
|
|
|
class DiscussionTopicsApiController < ApplicationController
|
|
|
|
include Api::V1::DiscussionTopics
|
2012-06-04 22:56:39 +08:00
|
|
|
include Api::V1::User
|
2011-11-04 05:51:29 +08:00
|
|
|
|
2015-02-05 23:44:37 +08:00
|
|
|
before_filter :require_context_and_read_access
|
2011-11-04 05:51:29 +08:00
|
|
|
before_filter :require_topic
|
2013-08-21 11:00:50 +08:00
|
|
|
before_filter :require_initial_post, except: [:add_entry, :mark_topic_read,
|
|
|
|
:mark_topic_unread, :show,
|
|
|
|
:unsubscribe_topic]
|
2014-08-13 05:10:48 +08:00
|
|
|
before_filter :check_differentiated_assignments, only: [:replies, :entries,
|
|
|
|
:add_entry, :add_reply,
|
|
|
|
:show, :view, :entry_list,
|
|
|
|
:subscribe_topic]
|
2011-11-04 05:51:29 +08:00
|
|
|
|
2012-05-16 06:05:00 +08:00
|
|
|
# @API Get a single topic
|
|
|
|
#
|
|
|
|
# Returns data on an individual discussion topic. See the List action for the response formatting.
|
|
|
|
#
|
|
|
|
# @example_request
|
|
|
|
#
|
2015-06-12 06:12:08 +08:00
|
|
|
# curl https://<canvas>/api/v1/courses/<course_id>/discussion_topics/<topic_id> \
|
2012-05-16 06:05:00 +08:00
|
|
|
# -H 'Authorization: Bearer <token>'
|
|
|
|
def show
|
2013-08-21 11:00:50 +08:00
|
|
|
render(json: discussion_topics_api_json([@topic], @context,
|
|
|
|
@current_user, session).first)
|
2012-05-16 06:05:00 +08:00
|
|
|
end
|
|
|
|
|
2012-05-04 00:14:19 +08:00
|
|
|
# @API Get the full topic
|
2012-03-28 02:41:55 +08:00
|
|
|
# Return a cached structure of the discussion topic, containing all entries,
|
|
|
|
# their authors, and their message bodies.
|
discussion topics materialized view api, refs #7567
This is a specialized, optimized view of the entire discussion,
including a nested view on all the entries and participants, and the
current user's unread entry list.
An upcoming commit will cache these views to the database, and generate
them asynchronously, rather than in-request.
test plan: No UI yet. GET /api/v1/courses/X/discussion_topics/Y/view ,
and verify the formatting of the response, including the nesting of
arbitrarily nested discussion entires (also only creatable via the api,
right now). verify that deleted entries are returned, but without a
user_id or summary and with a deleted flag.
Change-Id: Ib7332743f92cca40cc2a861973bf492b1f294a02
Reviewed-on: https://gerrit.instructure.com/9305
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-03-09 03:53:58 +08:00
|
|
|
#
|
|
|
|
# May require (depending on the topic) that the user has posted in the topic.
|
|
|
|
# If it is required, and the user has not posted, will respond with a 403
|
|
|
|
# Forbidden status and the body 'require_initial_post'.
|
|
|
|
#
|
2012-03-28 02:41:55 +08:00
|
|
|
# In some rare situations, this cached structure may not be available yet. In
|
discussion topics materialized view api, refs #7567
This is a specialized, optimized view of the entire discussion,
including a nested view on all the entries and participants, and the
current user's unread entry list.
An upcoming commit will cache these views to the database, and generate
them asynchronously, rather than in-request.
test plan: No UI yet. GET /api/v1/courses/X/discussion_topics/Y/view ,
and verify the formatting of the response, including the nesting of
arbitrarily nested discussion entires (also only creatable via the api,
right now). verify that deleted entries are returned, but without a
user_id or summary and with a deleted flag.
Change-Id: Ib7332743f92cca40cc2a861973bf492b1f294a02
Reviewed-on: https://gerrit.instructure.com/9305
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-03-09 03:53:58 +08:00
|
|
|
# that case, the server will respond with a 503 error, and the caller should
|
|
|
|
# try again soon.
|
|
|
|
#
|
|
|
|
# The response is an object containing the following keys:
|
2012-03-31 00:21:58 +08:00
|
|
|
# * "participants": A list of summary information on users who have posted to
|
discussion topics materialized view api, refs #7567
This is a specialized, optimized view of the entire discussion,
including a nested view on all the entries and participants, and the
current user's unread entry list.
An upcoming commit will cache these views to the database, and generate
them asynchronously, rather than in-request.
test plan: No UI yet. GET /api/v1/courses/X/discussion_topics/Y/view ,
and verify the formatting of the response, including the nesting of
arbitrarily nested discussion entires (also only creatable via the api,
right now). verify that deleted entries are returned, but without a
user_id or summary and with a deleted flag.
Change-Id: Ib7332743f92cca40cc2a861973bf492b1f294a02
Reviewed-on: https://gerrit.instructure.com/9305
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-03-09 03:53:58 +08:00
|
|
|
# the discussion. Each value is an object containing their id, display_name,
|
|
|
|
# and avatar_url.
|
2012-03-31 00:21:58 +08:00
|
|
|
# * "unread_entries": A list of entry ids that are unread by the current
|
discussion topics materialized view api, refs #7567
This is a specialized, optimized view of the entire discussion,
including a nested view on all the entries and participants, and the
current user's unread entry list.
An upcoming commit will cache these views to the database, and generate
them asynchronously, rather than in-request.
test plan: No UI yet. GET /api/v1/courses/X/discussion_topics/Y/view ,
and verify the formatting of the response, including the nesting of
arbitrarily nested discussion entires (also only creatable via the api,
right now). verify that deleted entries are returned, but without a
user_id or summary and with a deleted flag.
Change-Id: Ib7332743f92cca40cc2a861973bf492b1f294a02
Reviewed-on: https://gerrit.instructure.com/9305
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-03-09 03:53:58 +08:00
|
|
|
# user. this implies that any entry not in this list is read.
|
2014-10-15 18:38:00 +08:00
|
|
|
# * "entry_ratings": A map of entry ids to ratings by the current user. Entries
|
|
|
|
# not in this list have no rating. Only populated if rating is enabled.
|
2013-05-07 03:38:50 +08:00
|
|
|
# * "forced_entries": A list of entry ids that have forced_read_state set to
|
2014-10-15 18:38:00 +08:00
|
|
|
# true. This flag is meant to indicate the entry's read_state has been
|
2013-05-07 03:38:50 +08:00
|
|
|
# manually set to 'unread' by the user, so the entry should not be
|
|
|
|
# automatically marked as read.
|
2012-03-31 00:21:58 +08:00
|
|
|
# * "view": A threaded view of all the entries in the discussion, containing
|
2012-03-28 02:41:55 +08:00
|
|
|
# the id, user_id, and message.
|
2012-03-31 00:21:58 +08:00
|
|
|
# * "new_entries": Because this view is eventually consistent, it's possible
|
|
|
|
# that newly created or updated entries won't yet be reflected in the view.
|
|
|
|
# If the application wants to also get a flat list of all entries not yet
|
|
|
|
# reflected in the view, pass include_new_entries=1 to the request and this
|
|
|
|
# array of entries will be returned. These entries are returned in a flat
|
|
|
|
# array, in ascending created_at order.
|
discussion topics materialized view api, refs #7567
This is a specialized, optimized view of the entire discussion,
including a nested view on all the entries and participants, and the
current user's unread entry list.
An upcoming commit will cache these views to the database, and generate
them asynchronously, rather than in-request.
test plan: No UI yet. GET /api/v1/courses/X/discussion_topics/Y/view ,
and verify the formatting of the response, including the nesting of
arbitrarily nested discussion entires (also only creatable via the api,
right now). verify that deleted entries are returned, but without a
user_id or summary and with a deleted flag.
Change-Id: Ib7332743f92cca40cc2a861973bf492b1f294a02
Reviewed-on: https://gerrit.instructure.com/9305
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-03-09 03:53:58 +08:00
|
|
|
#
|
|
|
|
# @example_request
|
|
|
|
#
|
2013-10-26 01:51:23 +08:00
|
|
|
# curl 'https://<canvas>/api/v1/courses/<course_id>/discussion_topics/<topic_id>/view' \
|
discussion topics materialized view api, refs #7567
This is a specialized, optimized view of the entire discussion,
including a nested view on all the entries and participants, and the
current user's unread entry list.
An upcoming commit will cache these views to the database, and generate
them asynchronously, rather than in-request.
test plan: No UI yet. GET /api/v1/courses/X/discussion_topics/Y/view ,
and verify the formatting of the response, including the nesting of
arbitrarily nested discussion entires (also only creatable via the api,
right now). verify that deleted entries are returned, but without a
user_id or summary and with a deleted flag.
Change-Id: Ib7332743f92cca40cc2a861973bf492b1f294a02
Reviewed-on: https://gerrit.instructure.com/9305
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-03-09 03:53:58 +08:00
|
|
|
# -H "Authorization: Bearer <token>"
|
|
|
|
#
|
|
|
|
# @example_response
|
|
|
|
# {
|
|
|
|
# "unread_entries": [1,3,4],
|
2014-10-15 18:38:00 +08:00
|
|
|
# "entry_ratings": {3: 1},
|
2013-05-07 03:38:50 +08:00
|
|
|
# "forced_entries": [1],
|
discussion topics materialized view api, refs #7567
This is a specialized, optimized view of the entire discussion,
including a nested view on all the entries and participants, and the
current user's unread entry list.
An upcoming commit will cache these views to the database, and generate
them asynchronously, rather than in-request.
test plan: No UI yet. GET /api/v1/courses/X/discussion_topics/Y/view ,
and verify the formatting of the response, including the nesting of
arbitrarily nested discussion entires (also only creatable via the api,
right now). verify that deleted entries are returned, but without a
user_id or summary and with a deleted flag.
Change-Id: Ib7332743f92cca40cc2a861973bf492b1f294a02
Reviewed-on: https://gerrit.instructure.com/9305
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-03-09 03:53:58 +08:00
|
|
|
# "participants": [
|
2012-06-04 22:56:39 +08:00
|
|
|
# { "id": 10, "display_name": "user 1", "avatar_image_url": "https://...", "html_url": "https://..." },
|
|
|
|
# { "id": 11, "display_name": "user 2", "avatar_image_url": "https://...", "html_url": "https://..." }
|
discussion topics materialized view api, refs #7567
This is a specialized, optimized view of the entire discussion,
including a nested view on all the entries and participants, and the
current user's unread entry list.
An upcoming commit will cache these views to the database, and generate
them asynchronously, rather than in-request.
test plan: No UI yet. GET /api/v1/courses/X/discussion_topics/Y/view ,
and verify the formatting of the response, including the nesting of
arbitrarily nested discussion entires (also only creatable via the api,
right now). verify that deleted entries are returned, but without a
user_id or summary and with a deleted flag.
Change-Id: Ib7332743f92cca40cc2a861973bf492b1f294a02
Reviewed-on: https://gerrit.instructure.com/9305
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-03-09 03:53:58 +08:00
|
|
|
# ],
|
|
|
|
# "view": [
|
2012-03-28 02:41:55 +08:00
|
|
|
# { "id": 1, "user_id": 10, "parent_id": null, "message": "...html text...", "replies": [
|
|
|
|
# { "id": 3, "user_id": 11, "parent_id": 1, "message": "...html....", "replies": [...] }
|
discussion topics materialized view api, refs #7567
This is a specialized, optimized view of the entire discussion,
including a nested view on all the entries and participants, and the
current user's unread entry list.
An upcoming commit will cache these views to the database, and generate
them asynchronously, rather than in-request.
test plan: No UI yet. GET /api/v1/courses/X/discussion_topics/Y/view ,
and verify the formatting of the response, including the nesting of
arbitrarily nested discussion entires (also only creatable via the api,
right now). verify that deleted entries are returned, but without a
user_id or summary and with a deleted flag.
Change-Id: Ib7332743f92cca40cc2a861973bf492b1f294a02
Reviewed-on: https://gerrit.instructure.com/9305
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-03-09 03:53:58 +08:00
|
|
|
# ]},
|
2012-03-28 02:41:55 +08:00
|
|
|
# { "id": 2, "user_id": 11, "parent_id": null, "message": "...html..." },
|
|
|
|
# { "id": 4, "user_id": 10, "parent_id": null, "message": "...html..." }
|
discussion topics materialized view api, refs #7567
This is a specialized, optimized view of the entire discussion,
including a nested view on all the entries and participants, and the
current user's unread entry list.
An upcoming commit will cache these views to the database, and generate
them asynchronously, rather than in-request.
test plan: No UI yet. GET /api/v1/courses/X/discussion_topics/Y/view ,
and verify the formatting of the response, including the nesting of
arbitrarily nested discussion entires (also only creatable via the api,
right now). verify that deleted entries are returned, but without a
user_id or summary and with a deleted flag.
Change-Id: Ib7332743f92cca40cc2a861973bf492b1f294a02
Reviewed-on: https://gerrit.instructure.com/9305
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-03-09 03:53:58 +08:00
|
|
|
# ]
|
|
|
|
# }
|
|
|
|
def view
|
2015-03-25 07:16:40 +08:00
|
|
|
return unless authorized_action(@topic, @current_user, :read_replies)
|
|
|
|
|
2014-01-11 08:44:27 +08:00
|
|
|
structure, participant_ids, entry_ids, new_entries = @topic.materialized_view(:include_new_entries => params[:include_new_entries] == '1')
|
2012-03-31 00:21:58 +08:00
|
|
|
|
discussion topics materialized view api, refs #7567
This is a specialized, optimized view of the entire discussion,
including a nested view on all the entries and participants, and the
current user's unread entry list.
An upcoming commit will cache these views to the database, and generate
them asynchronously, rather than in-request.
test plan: No UI yet. GET /api/v1/courses/X/discussion_topics/Y/view ,
and verify the formatting of the response, including the nesting of
arbitrarily nested discussion entires (also only creatable via the api,
right now). verify that deleted entries are returned, but without a
user_id or summary and with a deleted flag.
Change-Id: Ib7332743f92cca40cc2a861973bf492b1f294a02
Reviewed-on: https://gerrit.instructure.com/9305
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-03-09 03:53:58 +08:00
|
|
|
if structure
|
2015-03-26 07:03:01 +08:00
|
|
|
structure = resolve_placeholders(structure)
|
2014-01-11 08:44:27 +08:00
|
|
|
|
|
|
|
# we assume that json_structure will typically be served to users requesting string IDs
|
|
|
|
unless stringify_json_ids?
|
|
|
|
entries = JSON.parse(structure)
|
|
|
|
Api.recursively_stringify_json_ids(entries, reverse: true)
|
|
|
|
structure = entries.to_json
|
|
|
|
end
|
|
|
|
|
2014-02-13 08:15:58 +08:00
|
|
|
participants = Shard.partition_by_shard(participant_ids) do |shard_ids|
|
|
|
|
User.find(shard_ids)
|
2014-08-25 23:13:19 +08:00
|
|
|
end
|
2014-02-13 08:15:58 +08:00
|
|
|
|
|
|
|
participant_info = participants.map do |participant|
|
|
|
|
user_display_json(participant, @context.is_a_context? && @context)
|
discussion topics materialized view api, refs #7567
This is a specialized, optimized view of the entire discussion,
including a nested view on all the entries and participants, and the
current user's unread entry list.
An upcoming commit will cache these views to the database, and generate
them asynchronously, rather than in-request.
test plan: No UI yet. GET /api/v1/courses/X/discussion_topics/Y/view ,
and verify the formatting of the response, including the nesting of
arbitrarily nested discussion entires (also only creatable via the api,
right now). verify that deleted entries are returned, but without a
user_id or summary and with a deleted flag.
Change-Id: Ib7332743f92cca40cc2a861973bf492b1f294a02
Reviewed-on: https://gerrit.instructure.com/9305
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-03-09 03:53:58 +08:00
|
|
|
end
|
2014-02-13 08:15:58 +08:00
|
|
|
|
discussion topics materialized view api, refs #7567
This is a specialized, optimized view of the entire discussion,
including a nested view on all the entries and participants, and the
current user's unread entry list.
An upcoming commit will cache these views to the database, and generate
them asynchronously, rather than in-request.
test plan: No UI yet. GET /api/v1/courses/X/discussion_topics/Y/view ,
and verify the formatting of the response, including the nesting of
arbitrarily nested discussion entires (also only creatable via the api,
right now). verify that deleted entries are returned, but without a
user_id or summary and with a deleted flag.
Change-Id: Ib7332743f92cca40cc2a861973bf492b1f294a02
Reviewed-on: https://gerrit.instructure.com/9305
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-03-09 03:53:58 +08:00
|
|
|
unread_entries = entry_ids - DiscussionEntryParticipant.read_entry_ids(entry_ids, @current_user)
|
2014-01-11 08:44:27 +08:00
|
|
|
unread_entries = unread_entries.map(&:to_s) if stringify_json_ids?
|
2013-05-07 03:38:50 +08:00
|
|
|
forced_entries = DiscussionEntryParticipant.forced_read_state_entry_ids(entry_ids, @current_user)
|
2014-01-11 08:44:27 +08:00
|
|
|
forced_entries = forced_entries.map(&:to_s) if stringify_json_ids?
|
2014-10-15 18:38:00 +08:00
|
|
|
entry_ratings = {}
|
|
|
|
|
|
|
|
if @topic.allow_rating?
|
|
|
|
entry_ratings = DiscussionEntryParticipant.entry_ratings(entry_ids, @current_user)
|
|
|
|
entry_ratings = Hash[entry_ratings.map { |k, v| [k.to_s, v] }] if stringify_json_ids?
|
|
|
|
end
|
|
|
|
|
discussion topics materialized view api, refs #7567
This is a specialized, optimized view of the entire discussion,
including a nested view on all the entries and participants, and the
current user's unread entry list.
An upcoming commit will cache these views to the database, and generate
them asynchronously, rather than in-request.
test plan: No UI yet. GET /api/v1/courses/X/discussion_topics/Y/view ,
and verify the formatting of the response, including the nesting of
arbitrarily nested discussion entires (also only creatable via the api,
right now). verify that deleted entries are returned, but without a
user_id or summary and with a deleted flag.
Change-Id: Ib7332743f92cca40cc2a861973bf492b1f294a02
Reviewed-on: https://gerrit.instructure.com/9305
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-03-09 03:53:58 +08:00
|
|
|
# as an optimization, the view structure is pre-serialized as a json
|
|
|
|
# string, so we have to do a bit of manual json building here to fit it
|
|
|
|
# into the response.
|
2013-05-07 03:38:50 +08:00
|
|
|
fragments = {
|
|
|
|
:unread_entries => unread_entries.to_json,
|
|
|
|
:forced_entries => forced_entries.to_json,
|
2014-10-15 18:38:00 +08:00
|
|
|
:entry_ratings => entry_ratings.to_json,
|
2014-01-11 08:44:27 +08:00
|
|
|
:participants => json_cast(participant_info).to_json,
|
2013-05-07 03:38:50 +08:00
|
|
|
:view => structure,
|
2014-01-11 08:44:27 +08:00
|
|
|
:new_entries => json_cast(new_entries).to_json,
|
2013-05-07 03:38:50 +08:00
|
|
|
}
|
|
|
|
fragments = fragments.map { |k, v| %("#{k}": #{v}) }
|
|
|
|
render :json => "{ #{fragments.join(', ')} }"
|
discussion topics materialized view api, refs #7567
This is a specialized, optimized view of the entire discussion,
including a nested view on all the entries and participants, and the
current user's unread entry list.
An upcoming commit will cache these views to the database, and generate
them asynchronously, rather than in-request.
test plan: No UI yet. GET /api/v1/courses/X/discussion_topics/Y/view ,
and verify the formatting of the response, including the nesting of
arbitrarily nested discussion entires (also only creatable via the api,
right now). verify that deleted entries are returned, but without a
user_id or summary and with a deleted flag.
Change-Id: Ib7332743f92cca40cc2a861973bf492b1f294a02
Reviewed-on: https://gerrit.instructure.com/9305
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-03-09 03:53:58 +08:00
|
|
|
else
|
|
|
|
render :nothing => true, :status => 503
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-05-04 00:14:19 +08:00
|
|
|
# @API Post an entry
|
2011-11-04 05:51:29 +08:00
|
|
|
# Create a new entry in a discussion topic. Returns a json representation of
|
|
|
|
# the created entry (see documentation for 'entries' method) on success.
|
|
|
|
#
|
2013-08-14 07:02:38 +08:00
|
|
|
# @argument message [String] The body of the entry.
|
2011-11-04 05:51:29 +08:00
|
|
|
#
|
2014-08-06 23:10:20 +08:00
|
|
|
# @argument attachment a multipart/form-data form-field-style
|
2011-11-04 05:51:29 +08:00
|
|
|
# attachment. Attachments larger than 1 kilobyte are subject to quota
|
|
|
|
# restrictions.
|
|
|
|
#
|
2011-12-16 06:39:58 +08:00
|
|
|
# @example_request
|
|
|
|
#
|
2013-10-26 01:51:23 +08:00
|
|
|
# curl 'https://<canvas>/api/v1/courses/<course_id>/discussion_topics/<topic_id>/entries.json' \
|
2015-06-12 06:12:08 +08:00
|
|
|
# -F 'message=<message>' \
|
|
|
|
# -F 'attachment=@<filename>' \
|
2011-12-30 10:27:18 +08:00
|
|
|
# -H "Authorization: Bearer <token>"
|
2011-11-04 05:51:29 +08:00
|
|
|
def add_entry
|
|
|
|
@entry = build_entry(@topic.discussion_entries)
|
|
|
|
if authorized_action(@topic, @current_user, :read) && authorized_action(@entry, @current_user, :create)
|
2012-03-24 04:47:02 +08:00
|
|
|
save_entry
|
2011-11-04 05:51:29 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-05-04 00:14:19 +08:00
|
|
|
# @API List topic entries
|
2011-11-04 05:51:29 +08:00
|
|
|
# Retrieve the (paginated) top-level entries in a discussion topic.
|
|
|
|
#
|
|
|
|
# May require (depending on the topic) that the user has posted in the topic.
|
|
|
|
# If it is required, and the user has not posted, will respond with a 403
|
|
|
|
# Forbidden status and the body 'require_initial_post'.
|
|
|
|
#
|
|
|
|
# Will include the 10 most recent replies, if any, for each entry returned.
|
|
|
|
#
|
|
|
|
# If the topic is a root topic with children corresponding to groups of a
|
|
|
|
# group assignment, entries from those subtopics for which the user belongs
|
|
|
|
# to the corresponding group will be returned.
|
|
|
|
#
|
|
|
|
# Ordering of returned entries is newest-first by posting timestamp (reply
|
|
|
|
# activity is ignored).
|
|
|
|
#
|
|
|
|
# @response_field id The unique identifier for the entry.
|
|
|
|
#
|
|
|
|
# @response_field user_id The unique identifier for the author of the entry.
|
|
|
|
#
|
discussion topics materialized view api, refs #7567
This is a specialized, optimized view of the entire discussion,
including a nested view on all the entries and participants, and the
current user's unread entry list.
An upcoming commit will cache these views to the database, and generate
them asynchronously, rather than in-request.
test plan: No UI yet. GET /api/v1/courses/X/discussion_topics/Y/view ,
and verify the formatting of the response, including the nesting of
arbitrarily nested discussion entires (also only creatable via the api,
right now). verify that deleted entries are returned, but without a
user_id or summary and with a deleted flag.
Change-Id: Ib7332743f92cca40cc2a861973bf492b1f294a02
Reviewed-on: https://gerrit.instructure.com/9305
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-03-09 03:53:58 +08:00
|
|
|
# @response_field editor_id The unique user id of the person to last edit the entry, if different than user_id.
|
|
|
|
#
|
2011-11-04 05:51:29 +08:00
|
|
|
# @response_field user_name The name of the author of the entry.
|
|
|
|
#
|
|
|
|
# @response_field message The content of the entry.
|
|
|
|
#
|
2012-02-14 04:09:41 +08:00
|
|
|
# @response_field read_state The read state of the entry, "read" or "unread".
|
|
|
|
#
|
2013-05-07 03:38:50 +08:00
|
|
|
# @response_field forced_read_state Whether the read_state was forced (was set manually)
|
|
|
|
#
|
2011-11-04 05:51:29 +08:00
|
|
|
# @response_field created_at The creation time of the entry, in ISO8601
|
|
|
|
# format.
|
|
|
|
#
|
2011-12-10 05:17:39 +08:00
|
|
|
# @response_field updated_at The updated time of the entry, in ISO8601 format.
|
|
|
|
#
|
2011-11-04 05:51:29 +08:00
|
|
|
# @response_field attachment JSON representation of the attachment for the
|
|
|
|
# entry, if any. Present only if there is an attachment.
|
|
|
|
#
|
2011-12-10 05:17:39 +08:00
|
|
|
# @response_field attachments *Deprecated*. Same as attachment, but returned
|
|
|
|
# as a one-element array. Present only if there is an attachment.
|
|
|
|
#
|
2011-11-04 05:51:29 +08:00
|
|
|
# @response_field recent_replies The 10 most recent replies for the entry,
|
|
|
|
# newest first. Present only if there is at least one reply.
|
|
|
|
#
|
|
|
|
# @response_field has_more_replies True if there are more than 10 replies for
|
|
|
|
# the entry (i.e., not all were included in this response). Present only if
|
|
|
|
# there is at least one reply.
|
|
|
|
#
|
|
|
|
# @example_response
|
2011-11-10 05:29:07 +08:00
|
|
|
# [ {
|
2011-11-10 04:56:32 +08:00
|
|
|
# "id": 1019,
|
|
|
|
# "user_id": 7086,
|
|
|
|
# "user_name": "nobody@example.com",
|
|
|
|
# "message": "Newer entry",
|
2012-02-14 04:09:41 +08:00
|
|
|
# "read_state": "read",
|
2013-05-07 03:38:50 +08:00
|
|
|
# "forced_read_state": false,
|
2011-11-10 04:56:32 +08:00
|
|
|
# "created_at": "2011-11-03T21:33:29Z",
|
|
|
|
# "attachment": {
|
|
|
|
# "content-type": "unknown/unknown",
|
|
|
|
# "url": "http://www.example.com/files/681/download?verifier=JDG10Ruitv8o6LjGXWlxgOb5Sl3ElzVYm9cBKUT3",
|
|
|
|
# "filename": "content.txt",
|
2011-11-10 05:29:07 +08:00
|
|
|
# "display_name": "content.txt" } },
|
|
|
|
# {
|
2011-11-10 04:56:32 +08:00
|
|
|
# "id": 1016,
|
|
|
|
# "user_id": 7086,
|
|
|
|
# "user_name": "nobody@example.com",
|
|
|
|
# "message": "first top-level entry",
|
2012-02-14 04:09:41 +08:00
|
|
|
# "read_state": "unread",
|
2013-05-07 03:38:50 +08:00
|
|
|
# "forced_read_state": false,
|
2011-11-10 04:56:32 +08:00
|
|
|
# "created_at": "2011-11-03T21:32:29Z",
|
|
|
|
# "recent_replies": [
|
2011-11-10 05:29:07 +08:00
|
|
|
# {
|
2011-11-10 04:56:32 +08:00
|
|
|
# "id": 1017,
|
|
|
|
# "user_id": 7086,
|
|
|
|
# "user_name": "nobody@example.com",
|
|
|
|
# "message": "Reply message",
|
2011-12-03 07:09:07 +08:00
|
|
|
# "created_at": "2011-11-03T21:32:29Z"
|
2011-11-10 05:29:07 +08:00
|
|
|
# } ],
|
|
|
|
# "has_more_replies": false } ]
|
2011-11-04 05:51:29 +08:00
|
|
|
def entries
|
2013-06-12 03:55:33 +08:00
|
|
|
@entries = Api.paginate(root_entries(@topic).newest_first, self, entry_pagination_url(@topic))
|
|
|
|
render :json => discussion_entry_api_json(@entries, @context, @current_user, session)
|
2011-11-04 05:51:29 +08:00
|
|
|
end
|
|
|
|
|
2012-05-04 00:14:19 +08:00
|
|
|
# @API Post a reply
|
2012-03-02 07:49:24 +08:00
|
|
|
# Add a reply to an entry in a discussion topic. Returns a json
|
2011-11-04 05:51:29 +08:00
|
|
|
# representation of the created reply (see documentation for 'replies'
|
|
|
|
# method) on success.
|
|
|
|
#
|
|
|
|
# May require (depending on the topic) that the user has posted in the topic.
|
|
|
|
# If it is required, and the user has not posted, will respond with a 403
|
|
|
|
# Forbidden status and the body 'require_initial_post'.
|
|
|
|
#
|
2013-08-14 07:02:38 +08:00
|
|
|
# @argument message [String] The body of the entry.
|
2011-11-04 05:51:29 +08:00
|
|
|
#
|
2014-08-06 23:10:20 +08:00
|
|
|
# @argument attachment a multipart/form-data form-field-style
|
2012-03-24 04:47:02 +08:00
|
|
|
# attachment. Attachments larger than 1 kilobyte are subject to quota
|
|
|
|
# restrictions.
|
|
|
|
#
|
2011-12-16 06:39:58 +08:00
|
|
|
# @example_request
|
|
|
|
#
|
2013-10-26 01:51:23 +08:00
|
|
|
# curl 'https://<canvas>/api/v1/courses/<course_id>/discussion_topics/<topic_id>/entries/<entry_id>/replies.json' \
|
2015-06-12 06:12:08 +08:00
|
|
|
# -F 'message=<message>' \
|
|
|
|
# -F 'attachment=@<filename>' \
|
2011-12-30 10:27:18 +08:00
|
|
|
# -H "Authorization: Bearer <token>"
|
2011-11-04 05:51:29 +08:00
|
|
|
def add_reply
|
2012-03-02 07:49:24 +08:00
|
|
|
@parent = all_entries(@topic).find(params[:entry_id])
|
2011-11-04 05:51:29 +08:00
|
|
|
@entry = build_entry(@parent.discussion_subentries)
|
2013-06-12 03:55:33 +08:00
|
|
|
if authorized_action(@entry, @current_user, :create)
|
2012-03-24 04:47:02 +08:00
|
|
|
save_entry
|
2011-11-04 05:51:29 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-05-04 00:14:19 +08:00
|
|
|
# @API List entry replies
|
2011-11-04 05:51:29 +08:00
|
|
|
# Retrieve the (paginated) replies to a top-level entry in a discussion
|
|
|
|
# topic.
|
|
|
|
#
|
|
|
|
# May require (depending on the topic) that the user has posted in the topic.
|
|
|
|
# If it is required, and the user has not posted, will respond with a 403
|
|
|
|
# Forbidden status and the body 'require_initial_post'.
|
|
|
|
#
|
|
|
|
# Ordering of returned entries is newest-first by creation timestamp.
|
|
|
|
#
|
|
|
|
# @response_field id The unique identifier for the reply.
|
|
|
|
#
|
|
|
|
# @response_field user_id The unique identifier for the author of the reply.
|
|
|
|
#
|
discussion topics materialized view api, refs #7567
This is a specialized, optimized view of the entire discussion,
including a nested view on all the entries and participants, and the
current user's unread entry list.
An upcoming commit will cache these views to the database, and generate
them asynchronously, rather than in-request.
test plan: No UI yet. GET /api/v1/courses/X/discussion_topics/Y/view ,
and verify the formatting of the response, including the nesting of
arbitrarily nested discussion entires (also only creatable via the api,
right now). verify that deleted entries are returned, but without a
user_id or summary and with a deleted flag.
Change-Id: Ib7332743f92cca40cc2a861973bf492b1f294a02
Reviewed-on: https://gerrit.instructure.com/9305
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Simon Williams <simon@instructure.com>
2012-03-09 03:53:58 +08:00
|
|
|
# @response_field editor_id The unique user id of the person to last edit the entry, if different than user_id.
|
|
|
|
#
|
2011-11-04 05:51:29 +08:00
|
|
|
# @response_field user_name The name of the author of the reply.
|
|
|
|
#
|
|
|
|
# @response_field message The content of the reply.
|
|
|
|
#
|
2012-02-14 04:09:41 +08:00
|
|
|
# @response_field read_state The read state of the entry, "read" or "unread".
|
|
|
|
#
|
2013-05-07 03:38:50 +08:00
|
|
|
# @response_field forced_read_state Whether the read_state was forced (was set manually)
|
|
|
|
#
|
2011-11-04 05:51:29 +08:00
|
|
|
# @response_field created_at The creation time of the reply, in ISO8601
|
|
|
|
# format.
|
|
|
|
#
|
|
|
|
# @example_response
|
2011-11-10 05:29:07 +08:00
|
|
|
# [ {
|
2011-11-10 04:56:32 +08:00
|
|
|
# "id": 1015,
|
|
|
|
# "user_id": 7084,
|
|
|
|
# "user_name": "nobody@example.com",
|
|
|
|
# "message": "Newer message",
|
2012-02-14 04:09:41 +08:00
|
|
|
# "read_state": "read",
|
2013-05-07 03:38:50 +08:00
|
|
|
# "forced_read_state": false,
|
2011-12-03 07:09:07 +08:00
|
|
|
# "created_at": "2011-11-03T21:27:44Z" },
|
2011-11-10 05:29:07 +08:00
|
|
|
# {
|
2011-11-10 04:56:32 +08:00
|
|
|
# "id": 1014,
|
|
|
|
# "user_id": 7084,
|
|
|
|
# "user_name": "nobody@example.com",
|
|
|
|
# "message": "Older message",
|
2012-02-14 04:09:41 +08:00
|
|
|
# "read_state": "unread",
|
2013-05-07 03:38:50 +08:00
|
|
|
# "forced_read_state": false,
|
2011-12-03 07:09:07 +08:00
|
|
|
# "created_at": "2011-11-03T21:26:44Z" } ]
|
2011-11-04 05:51:29 +08:00
|
|
|
def replies
|
|
|
|
@parent = root_entries(@topic).find(params[:entry_id])
|
2015-07-17 04:36:55 +08:00
|
|
|
@replies = Api.paginate(reply_entries(@parent).newest_first, self, reply_pagination_url(@topic, @parent))
|
2013-06-12 03:55:33 +08:00
|
|
|
render :json => discussion_entry_api_json(@replies, @context, @current_user, session)
|
2011-11-04 05:51:29 +08:00
|
|
|
end
|
|
|
|
|
2012-05-04 00:14:19 +08:00
|
|
|
# @API List entries
|
2012-03-09 06:33:08 +08:00
|
|
|
# Retrieve a paginated list of discussion entries, given a list of ids.
|
|
|
|
#
|
|
|
|
# May require (depending on the topic) that the user has posted in the topic.
|
|
|
|
# If it is required, and the user has not posted, will respond with a 403
|
|
|
|
# Forbidden status and the body 'require_initial_post'.
|
|
|
|
#
|
2013-08-14 07:02:38 +08:00
|
|
|
# @argument ids[] [String]
|
|
|
|
# A list of entry ids to retrieve. Entries will be returned in id order,
|
|
|
|
# smallest id first.
|
2012-03-09 06:33:08 +08:00
|
|
|
#
|
|
|
|
# @response_field id The unique identifier for the reply.
|
|
|
|
#
|
|
|
|
# @response_field user_id The unique identifier for the author of the reply.
|
|
|
|
#
|
|
|
|
# @response_field user_name The name of the author of the reply.
|
|
|
|
#
|
|
|
|
# @response_field message The content of the reply.
|
|
|
|
#
|
|
|
|
# @response_field read_state The read state of the entry, "read" or "unread".
|
|
|
|
#
|
2013-05-07 03:38:50 +08:00
|
|
|
# @response_field forced_read_state Whether the read_state was forced (was set manually)
|
|
|
|
#
|
2012-03-09 06:33:08 +08:00
|
|
|
# @response_field created_at The creation time of the reply, in ISO8601
|
|
|
|
# format.
|
|
|
|
#
|
|
|
|
# @response_field deleted If the entry has been deleted, returns true. The
|
|
|
|
# user_id, user_name, and message will not be returned for deleted entries.
|
|
|
|
#
|
|
|
|
# @example_request
|
|
|
|
#
|
2013-10-26 01:51:23 +08:00
|
|
|
# curl 'https://<canvas>/api/v1/courses/<course_id>/discussion_topics/<topic_id>/entry_list?ids[]=1&ids[]=2&ids[]=3' \
|
2012-03-09 06:33:08 +08:00
|
|
|
# -H "Authorization: Bearer <token>"
|
|
|
|
#
|
|
|
|
# @example_response
|
|
|
|
# [
|
|
|
|
# { ... entry 1 ... },
|
|
|
|
# { ... entry 2 ... },
|
|
|
|
# { ... entry 3 ... },
|
|
|
|
# ]
|
|
|
|
def entry_list
|
2013-06-12 03:55:33 +08:00
|
|
|
ids = Array(params[:ids])
|
2016-01-20 04:23:25 +08:00
|
|
|
entries = @topic.discussion_entries.order(:id).find(ids)
|
2013-06-12 03:55:33 +08:00
|
|
|
@entries = Api.paginate(entries, self, entry_pagination_url(@topic))
|
|
|
|
render :json => discussion_entry_api_json(@entries, @context, @current_user, session, [])
|
2012-03-09 06:33:08 +08:00
|
|
|
end
|
|
|
|
|
2012-05-04 00:14:19 +08:00
|
|
|
# @API Mark topic as read
|
2012-02-14 04:09:41 +08:00
|
|
|
# Mark the initial text of the discussion topic as read.
|
|
|
|
#
|
|
|
|
# No request fields are necessary.
|
|
|
|
#
|
|
|
|
# On success, the response will be 204 No Content with an empty body.
|
|
|
|
#
|
|
|
|
# @example_request
|
|
|
|
#
|
2013-10-26 01:51:23 +08:00
|
|
|
# curl 'https://<canvas>/api/v1/courses/<course_id>/discussion_topics/<topic_id>/read.json' \
|
2015-06-12 06:12:08 +08:00
|
|
|
# -X PUT \
|
|
|
|
# -H "Authorization: Bearer <token>" \
|
2012-02-14 04:09:41 +08:00
|
|
|
# -H "Content-Length: 0"
|
|
|
|
def mark_topic_read
|
|
|
|
change_topic_read_state("read")
|
|
|
|
end
|
|
|
|
|
2012-05-04 00:14:19 +08:00
|
|
|
# @API Mark topic as unread
|
2012-02-14 04:09:41 +08:00
|
|
|
# Mark the initial text of the discussion topic as unread.
|
|
|
|
#
|
|
|
|
# No request fields are necessary.
|
|
|
|
#
|
|
|
|
# On success, the response will be 204 No Content with an empty body.
|
|
|
|
#
|
|
|
|
# @example_request
|
|
|
|
#
|
2013-10-26 01:51:23 +08:00
|
|
|
# curl 'https://<canvas>/api/v1/courses/<course_id>/discussion_topics/<topic_id>/read.json' \
|
2015-06-12 06:12:08 +08:00
|
|
|
# -X DELETE \
|
2012-02-14 04:09:41 +08:00
|
|
|
# -H "Authorization: Bearer <token>"
|
|
|
|
def mark_topic_unread
|
|
|
|
change_topic_read_state("unread")
|
|
|
|
end
|
|
|
|
|
2012-05-04 00:14:19 +08:00
|
|
|
# @API Mark all entries as read
|
2012-02-14 04:09:41 +08:00
|
|
|
# Mark the discussion topic and all its entries as read.
|
|
|
|
#
|
|
|
|
# No request fields are necessary.
|
|
|
|
#
|
2014-08-06 23:10:20 +08:00
|
|
|
# @argument forced_read_state [Boolean]
|
2013-08-14 07:02:38 +08:00
|
|
|
# A boolean value to set all of the entries' forced_read_state. No change
|
|
|
|
# is made if this argument is not specified.
|
2015-06-12 06:12:08 +08:00
|
|
|
#
|
2012-02-14 04:09:41 +08:00
|
|
|
# On success, the response will be 204 No Content with an empty body.
|
|
|
|
#
|
|
|
|
# @example_request
|
|
|
|
#
|
2013-10-26 01:51:23 +08:00
|
|
|
# curl 'https://<canvas>/api/v1/courses/<course_id>/discussion_topics/<topic_id>/read_all.json' \
|
2015-06-12 06:12:08 +08:00
|
|
|
# -X PUT \
|
|
|
|
# -H "Authorization: Bearer <token>" \
|
2012-02-14 04:09:41 +08:00
|
|
|
# -H "Content-Length: 0"
|
|
|
|
def mark_all_read
|
2013-05-07 03:38:50 +08:00
|
|
|
change_topic_all_read_state('read')
|
2012-02-14 04:09:41 +08:00
|
|
|
end
|
|
|
|
|
2012-05-04 00:14:19 +08:00
|
|
|
# @API Mark all entries as unread
|
2012-02-14 04:09:41 +08:00
|
|
|
# Mark the discussion topic and all its entries as unread.
|
|
|
|
#
|
|
|
|
# No request fields are necessary.
|
|
|
|
#
|
2014-08-06 23:10:20 +08:00
|
|
|
# @argument forced_read_state [Boolean]
|
2013-08-14 07:02:38 +08:00
|
|
|
# A boolean value to set all of the entries' forced_read_state. No change is
|
|
|
|
# made if this argument is not specified.
|
2015-06-12 06:12:08 +08:00
|
|
|
#
|
2012-02-14 04:09:41 +08:00
|
|
|
# On success, the response will be 204 No Content with an empty body.
|
|
|
|
#
|
|
|
|
# @example_request
|
|
|
|
#
|
2013-10-26 01:51:23 +08:00
|
|
|
# curl 'https://<canvas>/api/v1/courses/<course_id>/discussion_topics/<topic_id>/read_all.json' \
|
2015-06-12 06:12:08 +08:00
|
|
|
# -X DELETE \
|
2012-02-14 04:09:41 +08:00
|
|
|
# -H "Authorization: Bearer <token>"
|
|
|
|
def mark_all_unread
|
2013-05-07 03:38:50 +08:00
|
|
|
change_topic_all_read_state('unread')
|
2012-02-14 04:09:41 +08:00
|
|
|
end
|
|
|
|
|
2012-05-04 00:14:19 +08:00
|
|
|
# @API Mark entry as read
|
2012-02-14 04:09:41 +08:00
|
|
|
# Mark a discussion entry as read.
|
|
|
|
#
|
|
|
|
# No request fields are necessary.
|
|
|
|
#
|
2014-08-06 23:10:20 +08:00
|
|
|
# @argument forced_read_state [Boolean]
|
2013-08-14 07:02:38 +08:00
|
|
|
# A boolean value to set the entry's forced_read_state. No change is made if
|
|
|
|
# this argument is not specified.
|
2013-05-07 03:38:50 +08:00
|
|
|
#
|
2012-02-14 04:09:41 +08:00
|
|
|
# On success, the response will be 204 No Content with an empty body.
|
|
|
|
#
|
|
|
|
# @example_request
|
|
|
|
#
|
2013-10-26 01:51:23 +08:00
|
|
|
# curl 'https://<canvas>/api/v1/courses/<course_id>/discussion_topics/<topic_id>/entries/<entry_id>/read.json' \
|
2015-06-12 06:12:08 +08:00
|
|
|
# -X PUT \
|
|
|
|
# -H "Authorization: Bearer <token>"\
|
2012-02-14 04:09:41 +08:00
|
|
|
# -H "Content-Length: 0"
|
|
|
|
def mark_entry_read
|
|
|
|
change_entry_read_state("read")
|
|
|
|
end
|
|
|
|
|
2012-05-04 00:14:19 +08:00
|
|
|
# @API Mark entry as unread
|
2012-02-14 04:09:41 +08:00
|
|
|
# Mark a discussion entry as unread.
|
|
|
|
#
|
|
|
|
# No request fields are necessary.
|
|
|
|
#
|
2014-08-06 23:10:20 +08:00
|
|
|
# @argument forced_read_state [Boolean]
|
2013-08-14 07:02:38 +08:00
|
|
|
# A boolean value to set the entry's forced_read_state. No change is made if
|
|
|
|
# this argument is not specified.
|
2013-05-07 03:38:50 +08:00
|
|
|
#
|
2012-02-14 04:09:41 +08:00
|
|
|
# On success, the response will be 204 No Content with an empty body.
|
|
|
|
#
|
|
|
|
# @example_request
|
|
|
|
#
|
2013-10-26 01:51:23 +08:00
|
|
|
# curl 'https://<canvas>/api/v1/courses/<course_id>/discussion_topics/<topic_id>/entries/<entry_id>/read.json' \
|
2015-06-12 06:12:08 +08:00
|
|
|
# -X DELETE \
|
2012-02-14 04:09:41 +08:00
|
|
|
# -H "Authorization: Bearer <token>"
|
|
|
|
def mark_entry_unread
|
|
|
|
change_entry_read_state("unread")
|
|
|
|
end
|
|
|
|
|
2014-10-15 18:38:00 +08:00
|
|
|
# @API Rate entry
|
|
|
|
# Rate a discussion entry.
|
|
|
|
#
|
|
|
|
# @argument rating [Integer]
|
|
|
|
# A rating to set on this entry. Only 0 and 1 are accepted.
|
|
|
|
#
|
|
|
|
# On success, the response will be 204 No Content with an empty body.
|
|
|
|
#
|
|
|
|
# @example_request
|
|
|
|
#
|
|
|
|
# curl 'https://<canvas>/api/v1/courses/<course_id>/discussion_topics/<topic_id>/entries/<entry_id>/rating.json' \
|
|
|
|
# -X POST \
|
|
|
|
# -H "Authorization: Bearer <token>"
|
|
|
|
def rate_entry
|
|
|
|
require_entry
|
|
|
|
rating = params[:rating].to_i
|
|
|
|
unless [0, 1].include? rating
|
|
|
|
return render(:json => { :message => "Invalid rating given" }, :status => :bad_request)
|
|
|
|
end
|
|
|
|
|
|
|
|
if authorized_action(@entry, @current_user, :rate)
|
|
|
|
render_state_change_result @entry.change_rating(rating, @current_user)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-06-12 03:55:33 +08:00
|
|
|
# @API Subscribe to a topic
|
|
|
|
# Subscribe to a topic to receive notifications about new entries
|
|
|
|
#
|
|
|
|
# On success, the response will be 204 No Content with an empty body
|
|
|
|
#
|
|
|
|
# @example_request
|
2013-10-26 01:51:23 +08:00
|
|
|
# curl 'https://<canvas>/api/v1/courses/<course_id>/discussion_topics/<topic_id>/subscribed.json' \
|
2015-06-12 06:12:08 +08:00
|
|
|
# -X PUT \
|
|
|
|
# -H "Authorization: Bearer <token>" \
|
2013-06-12 03:55:33 +08:00
|
|
|
# -H "Content-Length: 0"
|
|
|
|
def subscribe_topic
|
|
|
|
render_state_change_result @topic.subscribe(@current_user)
|
|
|
|
end
|
2015-06-12 06:12:08 +08:00
|
|
|
|
2013-06-12 03:55:33 +08:00
|
|
|
# @API Unsubscribe from a topic
|
|
|
|
# Unsubscribe from a topic to stop receiving notifications about new entries
|
|
|
|
#
|
|
|
|
# On success, the response will be 204 No Content with an empty body
|
|
|
|
#
|
|
|
|
# @example_request
|
2013-10-26 01:51:23 +08:00
|
|
|
# curl 'https://<canvas>/api/v1/courses/<course_id>/discussion_topics/<topic_id>/subscribed.json' \
|
2015-06-12 06:12:08 +08:00
|
|
|
# -X DELETE \
|
|
|
|
# -H "Authorization: Bearer <token>"
|
2013-06-12 03:55:33 +08:00
|
|
|
def unsubscribe_topic
|
|
|
|
render_state_change_result @topic.unsubscribe(@current_user)
|
|
|
|
end
|
2015-06-12 06:12:08 +08:00
|
|
|
|
2011-11-04 05:51:29 +08:00
|
|
|
protected
|
|
|
|
def require_topic
|
2014-01-31 03:23:21 +08:00
|
|
|
@topic = @context.all_discussion_topics.active.find(params[:topic_id])
|
2011-11-04 05:51:29 +08:00
|
|
|
return authorized_action(@topic, @current_user, :read)
|
|
|
|
end
|
|
|
|
|
2014-10-15 18:38:00 +08:00
|
|
|
def require_entry
|
|
|
|
@entry = @topic.discussion_entries.find(params[:entry_id])
|
|
|
|
end
|
|
|
|
|
2011-11-04 05:51:29 +08:00
|
|
|
def require_initial_post
|
2012-03-24 04:47:02 +08:00
|
|
|
return true if !@topic.initial_post_required?(@current_user, @context_enrollment, session)
|
2011-11-04 05:51:29 +08:00
|
|
|
|
|
|
|
# neither the current user nor the enrollment user (if any) has posted yet,
|
|
|
|
# so give them the forbidden status
|
|
|
|
render :json => 'require_initial_post', :status => :forbidden
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
def build_entry(association)
|
2013-04-13 05:46:13 +08:00
|
|
|
params[:message] = process_incoming_html_content(params[:message])
|
2012-05-23 05:13:06 +08:00
|
|
|
@topic.save! if @topic.new_record?
|
2011-11-04 05:51:29 +08:00
|
|
|
association.build(:message => params[:message], :user => @current_user, :discussion_topic => @topic)
|
|
|
|
end
|
|
|
|
|
|
|
|
def save_entry
|
2015-06-12 06:12:08 +08:00
|
|
|
has_attachment = params[:attachment].present? && params[:attachment].size > 0 &&
|
2012-03-24 04:47:02 +08:00
|
|
|
@entry.grants_right?(@current_user, session, :attach)
|
2016-02-08 22:40:58 +08:00
|
|
|
return if has_attachment && !@topic.for_assignment? && params[:attachment].size > 1.kilobytes &&
|
2015-12-12 04:35:37 +08:00
|
|
|
quota_exceeded(@current_user, named_context_url(@context, :context_discussion_topic_url, @topic.id))
|
2012-03-24 04:47:02 +08:00
|
|
|
if @entry.save
|
|
|
|
@entry.update_topic
|
|
|
|
log_asset_access(@topic, 'topics', 'topics', 'participate')
|
|
|
|
if has_attachment
|
2013-10-19 01:24:11 +08:00
|
|
|
@attachment = (@current_user || @context).attachments.create(:uploaded_data => params[:attachment])
|
2012-03-24 04:47:02 +08:00
|
|
|
@entry.attachment = @attachment
|
|
|
|
@entry.save
|
|
|
|
end
|
2012-03-28 02:41:55 +08:00
|
|
|
render :json => discussion_entry_api_json([@entry], @context, @current_user, session, [:user_name]).first, :status => :created
|
2012-03-24 04:47:02 +08:00
|
|
|
else
|
2012-03-02 07:49:24 +08:00
|
|
|
render :json => @entry.errors, :status => :bad_request
|
2011-11-04 05:51:29 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-03-02 07:49:24 +08:00
|
|
|
def visible_topics(topic)
|
2011-11-04 05:51:29 +08:00
|
|
|
# conflate entries from all child topics for groups the user can access
|
|
|
|
topics = [topic]
|
2014-04-11 06:39:45 +08:00
|
|
|
if topic.for_group_discussion? && !topic.child_topics.empty?
|
|
|
|
groups = topic.group_category.groups.active.select do |group|
|
2011-11-04 05:51:29 +08:00
|
|
|
group.grants_right?(@current_user, session, :read)
|
|
|
|
end
|
|
|
|
topic.child_topics.each{ |t| topics << t if groups.include?(t.context) }
|
|
|
|
end
|
2012-03-02 07:49:24 +08:00
|
|
|
topics
|
|
|
|
end
|
|
|
|
|
|
|
|
def all_entries(topic)
|
|
|
|
DiscussionEntry.all_for_topics(visible_topics(topic)).active
|
|
|
|
end
|
|
|
|
|
|
|
|
def root_entries(topic)
|
|
|
|
DiscussionEntry.top_level_for_topics(visible_topics(topic)).active
|
2011-11-04 05:51:29 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def reply_entries(entry)
|
2012-03-02 07:49:24 +08:00
|
|
|
entry.flattened_discussion_subentries.active
|
2011-11-04 05:51:29 +08:00
|
|
|
end
|
2012-02-14 04:09:41 +08:00
|
|
|
|
|
|
|
def change_topic_read_state(new_state)
|
2013-06-12 03:55:33 +08:00
|
|
|
render_state_change_result @topic.change_read_state(new_state, @current_user)
|
2012-02-14 04:09:41 +08:00
|
|
|
end
|
|
|
|
|
2013-05-07 03:38:50 +08:00
|
|
|
def get_forced_option()
|
|
|
|
opts = {}
|
2015-06-12 06:12:08 +08:00
|
|
|
opts[:forced] = value_to_boolean(params[:forced_read_state]) if params.has_key?(:forced_read_state)
|
2013-05-07 03:38:50 +08:00
|
|
|
opts
|
|
|
|
end
|
|
|
|
|
|
|
|
def change_topic_all_read_state(new_state)
|
|
|
|
opts = get_forced_option
|
|
|
|
|
2013-06-12 03:55:33 +08:00
|
|
|
@topic.change_all_read_state(new_state, @current_user, opts)
|
|
|
|
render :json => {}, :status => :no_content
|
2013-05-07 03:38:50 +08:00
|
|
|
end
|
|
|
|
|
2012-02-14 04:09:41 +08:00
|
|
|
def change_entry_read_state(new_state)
|
2014-10-15 18:38:00 +08:00
|
|
|
require_entry
|
2013-05-07 03:38:50 +08:00
|
|
|
opts = get_forced_option
|
|
|
|
|
2012-02-14 04:09:41 +08:00
|
|
|
if authorized_action(@entry, @current_user, :read)
|
2013-06-12 03:55:33 +08:00
|
|
|
render_state_change_result @entry.change_read_state(new_state, @current_user, opts)
|
2012-02-14 04:09:41 +08:00
|
|
|
end
|
|
|
|
end
|
2013-06-12 03:55:33 +08:00
|
|
|
|
2014-08-13 05:10:48 +08:00
|
|
|
def check_differentiated_assignments
|
2016-02-26 23:44:19 +08:00
|
|
|
return render_unauthorized_action if @topic.for_assignment? && !@topic.assignment.visible_to_user?(@current_user)
|
2014-08-13 05:10:48 +08:00
|
|
|
end
|
|
|
|
|
2013-06-12 03:55:33 +08:00
|
|
|
# the result of several state change functions are the following:
|
|
|
|
# nil - no current user
|
|
|
|
# true - state is already set to the requested state
|
|
|
|
# participant with errors - something went wrong with the participant
|
|
|
|
# participant with no errors - the change went through
|
|
|
|
# this function renders a 204 No Content for a success, or a Bad Request
|
|
|
|
# for failure with participant errors if there are any
|
|
|
|
def render_state_change_result(result)
|
|
|
|
if result == true || result.try(:errors).blank?
|
|
|
|
render :nothing => true, :status => :no_content
|
|
|
|
else
|
2013-08-23 06:22:14 +08:00
|
|
|
render :json => result.try(:errors) || {}, :status => :bad_request
|
2013-06-12 03:55:33 +08:00
|
|
|
end
|
2015-03-26 07:03:01 +08:00
|
|
|
end
|
2011-11-04 05:51:29 +08:00
|
|
|
end
|