canvas-lms/config/routes.rb

1719 lines
87 KiB
Ruby
Raw Normal View History

full_path_glob = '(/*full_path)'
# allow plugins to prepend routes
Dir["{gems,vendor}/plugins/*/config/pre_routes.rb"].each { |pre_routes|
load pre_routes
}
CanvasRails::Application.routes.draw do
resources :submission_comments, only: :destroy
get 'inbox' => 'context#inbox'
get 'oauth/redirect_proxy' => 'oauth_proxy#redirect_proxy'
get 'conversations/unread' => 'conversations#index', as: :conversations_unread, redirect_scope: 'unread'
get 'conversations/starred' => 'conversations#index', as: :conversations_starred, redirect_scope: 'starred'
get 'conversations/sent' => 'conversations#index', as: :conversations_sent, redirect_scope: 'sent'
get 'conversations/archived' => 'conversations#index', as: :conversations_archived, redirect_scope: 'archived'
get 'conversations/find_recipients' => 'search#recipients'
get 'search/recipients' => 'search#recipients'
post 'conversations/mark_all_as_read' => 'conversations#mark_all_as_read'
post 'conversations/watched_intro' => 'conversations#watched_intro'
get 'conversations/batches' => 'conversations#batches', as: :conversation_batches
post 'conversations/toggle_new_conversations' => 'conversations#toggle_new_conversations', as: :toggle_new_conversations
resources :conversations, only: [:index, :show, :update, :create, :destroy] do
post :add_recipients
post :add_message
post :remove_messages
end
2011-02-01 09:57:29 +08:00
# So, this will look like:
refactor user creation/invitations closes #5833 fixes #5573, #5572, #5753 * communication channels are now only unique within a single user * UserList changes * Always resolve pseudonym#unique_ids * Support looking up by SMS CCs * Option to either require e-mails match an existing CC, or e-mails that don't match a Pseudonym will always be returned unattached (relying on better merging behavior to not have a gazillion accounts created) * Method to return users, creating new ones (*without* a Pseudonym) if necessary. (can't create with a pseudonym, since Pseudonym#unique_id is still unique, I can't have multiple outstanding users with the same unique_id) * EnrollmentsFromUserList is mostly gutted, now using UserList's functionality directy. * Use UserList for adding account admins, removing the now unused Account#add_admin => User#find_by_email/User#assert_by_email codepath * Update UsersController#create to not worry about duplicate communication channels * Remove AccountsController#add_user, and just use UsersController#create * Change SIS::UserImporter to send out a merge opportunity e-mail if a conflicting CC is found (but still create the CC) * In /profile, don't worry about conflicting CCs (the CC confirmation process will now allow merging) * Remove CommunicationChannelsController#try_merge and #merge * For the non-simple case of CoursesController#enrollment_invitation redirect to /register (CommunicationsChannelController#confirm) * Remove CoursesController#transfer_enrollment * Move PseudonymsController#registration_confirmation to CommunicationChannelsController#confirm (have to be able to register an account without a Pseudonym yet) * Fold the old direct confirm functionality in, if there are no available merge opportunities * Allow merging the new account with the currently logged in user * Allow changing the Pseudonym#unique_id when registering a new account (since there might be conflicts) * Display a list of merge opportunities based on conflicting communication channels * Provide link(s) to log in as the other user, redirecting back to the registration page after login is complete (to complete the merge as the current user) * Remove several assert_* methods that are no longer needed * Update PseudonymSessionsController a bit to deal with the new way of dealing with conflicting CCs (especially CCs from LDAP), and to redirect back to the registration/confirmation page when attempting to do a merge * Expose the open_registration setting; use it to control if inviting users to a course is able to create new users Change-Id: If2f38818a71af656854d3bf8431ddbf5dcb84691 Reviewed-on: https://gerrit.instructure.com/6149 Tested-by: Hudson <hudson@instructure.com> Reviewed-by: Jacob Fugal <jacob@instructure.com>
2011-10-13 04:30:48 +08:00
# http://instructure.com/register/5R32s9iqwLK75Jbbj0
match 'register/:nonce' => 'communication_channels#confirm', as: :registration_confirmation, via: [:get, :post]
refactor user creation/invitations closes #5833 fixes #5573, #5572, #5753 * communication channels are now only unique within a single user * UserList changes * Always resolve pseudonym#unique_ids * Support looking up by SMS CCs * Option to either require e-mails match an existing CC, or e-mails that don't match a Pseudonym will always be returned unattached (relying on better merging behavior to not have a gazillion accounts created) * Method to return users, creating new ones (*without* a Pseudonym) if necessary. (can't create with a pseudonym, since Pseudonym#unique_id is still unique, I can't have multiple outstanding users with the same unique_id) * EnrollmentsFromUserList is mostly gutted, now using UserList's functionality directy. * Use UserList for adding account admins, removing the now unused Account#add_admin => User#find_by_email/User#assert_by_email codepath * Update UsersController#create to not worry about duplicate communication channels * Remove AccountsController#add_user, and just use UsersController#create * Change SIS::UserImporter to send out a merge opportunity e-mail if a conflicting CC is found (but still create the CC) * In /profile, don't worry about conflicting CCs (the CC confirmation process will now allow merging) * Remove CommunicationChannelsController#try_merge and #merge * For the non-simple case of CoursesController#enrollment_invitation redirect to /register (CommunicationsChannelController#confirm) * Remove CoursesController#transfer_enrollment * Move PseudonymsController#registration_confirmation to CommunicationChannelsController#confirm (have to be able to register an account without a Pseudonym yet) * Fold the old direct confirm functionality in, if there are no available merge opportunities * Allow merging the new account with the currently logged in user * Allow changing the Pseudonym#unique_id when registering a new account (since there might be conflicts) * Display a list of merge opportunities based on conflicting communication channels * Provide link(s) to log in as the other user, redirecting back to the registration page after login is complete (to complete the merge as the current user) * Remove several assert_* methods that are no longer needed * Update PseudonymSessionsController a bit to deal with the new way of dealing with conflicting CCs (especially CCs from LDAP), and to redirect back to the registration/confirmation page when attempting to do a merge * Expose the open_registration setting; use it to control if inviting users to a course is able to create new users Change-Id: If2f38818a71af656854d3bf8431ddbf5dcb84691 Reviewed-on: https://gerrit.instructure.com/6149 Tested-by: Hudson <hudson@instructure.com> Reviewed-by: Jacob Fugal <jacob@instructure.com>
2011-10-13 04:30:48 +08:00
# deprecated
get 'pseudonyms/:id/register/:nonce' => 'communication_channels#confirm', as: :registration_confirmation_deprecated
post 'confirmations/:user_id/re_send(/:id)' => 'communication_channels#re_send_confirmation', as: :re_send_confirmation, id: nil
match 'forgot_password' => 'pseudonyms#forgot_password', as: :forgot_password, via: [:get, :post]
get 'pseudonyms/:pseudonym_id/change_password/:nonce' => 'pseudonyms#confirm_change_password', as: :confirm_change_password
post 'pseudonyms/:pseudonym_id/change_password/:nonce' => 'pseudonyms#change_password', as: :change_password
2011-02-01 09:57:29 +08:00
# callback urls for oauth authorization processes
get 'oauth' => 'users#oauth'
get 'oauth_success' => 'users#oauth_success'
get 'mr/:id' => 'info#message_redirect', as: :message_redirect
get 'help_links' => 'info#help_links'
concern :question_banks do
resources :question_banks do
post :bookmark
post :reorder
get :questions
post :move_questions
resources :assessment_questions
end
end
2011-02-01 09:57:29 +08:00
concern :groups do
resources :groups
resources :group_categories, only: [:create, :update, :destroy]
get 'group_unassigned_members' => 'groups#unassigned_members'
end
concern :files do
resources :files do
get 'inline' => 'files#text_show', as: :text_inline
get 'download' => 'files#show', download: '1'
get 'download.:type' => 'files#show', as: :typed_download, download: '1'
get 'preview' => 'files#show', preview: '1'
post 'inline_view' => 'files#show', inline: '1'
get 'contents' => 'files#attachment_content', as: :attachment_content
file preview endpoint test plan: 0. upload the following types of files into a course, and note their IDs: a) document of some type (such as Word, PDF) b) image (such as JPEG, PNG, GIF) c) media (such as MP3, MP4, MOV) 1. enable canvadocs and google docs previews in account settings. 2. test the document preview by hitting the following in a new browser tab: /courses/X/files/Y/file_preview (where X is the course ID and Y is the file ID of the document file) - You should see a Canvadocs preview of the document. - There should be no Canvas chrome in the window, just the document preview. 3. disable canvadocs but leave google doc previews enabled. 4. hit the URL from step 2. - you should see a Google preview of the document 5. disable Google docs previews. 6. hit the URL from step 2. - you should see a message indicating that no preview is available, with a link to download the file. 7. hit the URL from step 2, but substitute the file ID with the ID of an image instead of a document. - you should see the image (and nothing else). 8. hit the URL from step 2, but substitute the file ID with the ID of a media file. - You should see a functioning media player (or a message indicating the media has not been converted yet) 9. add a document to a module, and set a "must view" completion requirement 10. use this endpoint to preview the document, and confirm that the module completion requirement is fulfilled fixes CNVS-15827 Change-Id: Id0ecaa7f003248cb3d8f163e48c3b16631ee59cf Reviewed-on: https://gerrit.instructure.com/42438 Reviewed-by: Ryan Shaw <ryan@instructure.com> Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Jahnavi Yetukuri <jyetukuri@instructure.com> Product-Review: Cosme Salazar <cosme@instructure.com>
2014-10-09 05:27:45 +08:00
get 'file_preview' => 'file_previews#show'
collection do
get "folder#{full_path_glob}" => 'files#react_files', format: false
get "search" => 'files#react_files', format: false
get :quota
post :reorder
end
get ':file_path' => 'files#show_relative', as: :relative_path, file_path: /.+/ #needs to stay below react_files route
end
end
concern :file_images do
get 'images' => 'files#images'
end
concern :relative_files do
get 'file_contents/:file_path' => 'files#show_relative', as: :relative_file_path, file_path: /.+/
end
concern :folders do
resources :folders do
get :download
end
end
concern :media do
get 'media_download' => 'users#media_download'
end
concern :users do
get 'users' => 'context#roster'
get 'user_services' => 'context#roster_user_services'
get 'users/:user_id/usage' => 'context#roster_user_usage', as: :user_usage
get 'users/:id' => 'context#roster_user', as: :user
end
concern :announcements do
resources :announcements
post 'announcements/external_feeds' => 'announcements#create_external_feed'
delete 'announcements/external_feeds/:id' => 'announcements#destroy_external_feed', as: :announcements_external_feed
end
concern :discussions do
resources :discussion_topics, only: [:index, :new, :show, :edit, :destroy]
get 'discussion_topics/:id/:extras' => 'discussion_topics#show', as: :map, extras: /.+/
resources :discussion_entries
end
concern :pages do
resources :wiki_pages, path: :pages, except: [:update, :destroy], constraints: { id: %r{[^\/]+} } do
get 'revisions' => 'wiki_pages#revisions', as: :revisions
end
get 'wiki' => 'wiki_pages#front_page', as: :wiki
get 'wiki/:id' => 'wiki_pages#show_redirect', id: /[^\/]+/
get 'wiki/:id/revisions' => 'wiki_pages#revisions_redirect', id: /[^\/]+/
get 'wiki/:id/revisions/:revision_id' => 'wiki_pages#revisions_redirect', id: /[^\/]+/
end
concern :conferences do
resources :conferences do
match :join, via: [:get, :post]
match :close, via: [:get, :post]
get :settings
end
end
concern :zip_file_imports do
resources :zip_file_imports, only: [:new, :create, :show]
get 'imports/files' => 'content_imports#files', as: :import_files
end
2011-02-01 09:57:29 +08:00
# There are a lot of resources that are all scoped to the course level
# (assignments, files, wiki pages, user lists, forums, etc.). Many of
# these resources also apply to groups and individual users. We call
# courses, users, groups, or even accounts in this setting, "contexts".
# There are some helper methods like the before_filter :get_context in application_controller
# and the application_helper method :context_url to make retrieving
# these contexts, and also generating context-specific urls, easier.
resources :courses do
# DEPRECATED
get 'self_enrollment/:self_enrollment' => 'courses#self_enrollment', as: :self_enrollment
post 'self_unenrollment/:self_unenrollment' => 'courses#self_unenrollment', as: :self_unenrollment
post :restore
post :backup
post :unconclude
get :students
post :enrollment_invitation
# this needs to come before the users concern, or users/:id will preempt it
get 'users/prior' => 'context#prior_users', as: :prior_users
concerns :users
get :statistics
delete 'unenroll/:id' => 'courses#unenroll_user', as: :unenroll
post 'move_enrollment/:id' => 'courses#move_enrollment', as: :move_enrollment
delete 'unenroll/:id.:format' => 'courses#unenroll_user', as: :formatted_unenroll
post 'limit_user_grading/:id' => 'courses#limit_user', as: :limit_user_grading
delete 'conclude_user/:id' => 'courses#conclude_user', as: :conclude_user_enrollment
post 'unconclude_user/:id' => 'courses#unconclude_user', as: :unconclude_user_enrollment
resources :sections, except: [:index, :edit, :new] do
get 'crosslist/confirm/:new_course_id' => 'sections#crosslist_check', as: :confirm_crosslist
post :crosslist
delete 'crosslist' => 'sections#uncrosslist', as: :uncrosslist
end
get 'undelete' => 'context#undelete_index', as: :undelete_items
post 'undelete/:asset_string' => 'context#undelete_item', as: :undelete_item
get "settings#{full_path_glob}", action: :settings
get :settings
get 'details' => 'courses#settings'
post :re_send_invitations
post :enroll_users
post :link_enrollment
post :update_nav
resource :gradebook do
post 'submissions_upload/:assignment_id' => 'gradebooks#submissions_zip_upload', as: :submissions_upload
collection do
get :change_gradebook_version
get :blank_submission
get :speed_grader
post :speed_grader_settings
get :history
post :update_submission
post :change_gradebook_column_size
post :save_gradebook_column_order
end
end
resource :gradebook_csv, only: [:show]
get 'gradebook2' => "gradebooks#gradebook2"
get 'attendance' => 'gradebooks#attendance'
get 'attendance/:user_id' => 'gradebooks#attendance', as: :attendance_user
concerns :zip_file_imports
# DEPRECATED old migration emails pointed the user to this url, leave so the controller can redirect
get 'imports/list' => 'content_imports#index', as: :import_list
# DEPRECATED
get 'imports' => 'content_imports#intro'
resource :gradebook_upload do
get 'data' => 'gradebook_uploads#data'
end
get 'grades' => 'gradebooks#grade_summary', id: nil
get 'grading_rubrics' => 'gradebooks#grading_rubrics'
get 'grades/:id' => 'gradebooks#grade_summary', as: :student_grades
concerns :announcements
get 'calendar' => 'calendars#show2', as: :old_calendar
get :locks
concerns :discussions
resources :assignments do
resources :submissions do
post 'turnitin/resubmit' => 'submissions#resubmit_to_turnitin', as: :resubmit_to_turnitin
get 'turnitin/:asset_string' => 'submissions#turnitin_report', as: :turnitin_report
end
get :rubric
resource :rubric_association, path: :rubric do
resources :rubric_assessments, path: :assessments
2011-02-01 09:57:29 +08:00
end
get :peer_reviews
post :assign_peer_reviews
delete 'peer_reviews/:id' => 'assignments#delete_peer_review', as: :delete_peer_review
post 'peer_reviews/:id' => 'assignments#remind_peer_review', as: :remind_peer_review
post 'peer_reviews/users/:reviewer_id' => 'assignments#assign_peer_review', as: :assign_peer_review
put 'mute' => 'assignments#toggle_mute'
collection do
get :syllabus
get :submissions
2011-02-01 09:57:29 +08:00
end
member do
get :list_google_docs
2011-02-01 09:57:29 +08:00
end
end
resources :grading_standards, only: [:index, :create, :update, :destroy]
resources :assignment_groups do
post 'reorder' => 'assignment_groups#reorder_assignments', as: :reorder_assignments
collection do
post :reorder
end
end
get 'external_tools/sessionless_launch' => 'external_tools#sessionless_launch'
resources :external_tools do
get :resource_selection
get :homework_submission
get :finished
collection do
get :retrieve
get :homework_submissions
end
end
get 'lti/basic_lti_launch_request/:message_handler_id', controller: 'lti/message', action: 'basic_lti_launch_request', as: :basic_lti_launch_request
get 'lti/tool_proxy_registration', controller: 'lti/message', action: 'registration', as: :tool_proxy_registration
get 'lti/registration_return', controller: 'lti/message', action: 'registration_return', as: :registration_return
resources :submissions
resources :calendar_events
concerns :files, :file_images, :relative_files, :folders
concerns :groups
concerns :pages
concerns :conferences
concerns :question_banks
post 'quizzes/publish' => 'quizzes/quizzes#publish'
post 'quizzes/unpublish' => 'quizzes/quizzes#unpublish'
resources :quizzes, controller: 'quizzes/quizzes' do
get :managed_quiz_data
get :submission_versions
get :history
get :statistics
get :read_only
get :submission_html
resources :quiz_submissions, controller: 'quizzes/quiz_submissions', path: :submissions do
collection do
put :backup
end
member do
get :record_answer
post :record_answer
end
resources :events, controller: 'quizzes/quiz_submission_events', path: "log#{full_path_glob}"
end
post 'extensions/:user_id' => 'quizzes/quiz_submissions#extensions', as: :extensions
resources :quiz_questions, controller: 'quizzes/quiz_questions', path: :questions, only: [:create, :update, :destroy, :show]
resources :quiz_groups, controller: 'quizzes/quiz_groups', path: :groups, only: [:create, :update, :destroy] do
member do
post :reorder
end
end
match 'take' => 'quizzes/quizzes#show', take: '1', via: [:get, :post]
get 'take/questions/:question_id' => 'quizzes/quizzes#show', as: :question, take: '1'
get :moderate
get :lockdown_browser_required
end
resources :collaborations
resources :gradebook_uploads
resources :rubrics
resources :rubric_associations do
post 'remind/:assessment_request_id' => 'rubric_assessments#remind', as: :remind_assessee
resources :rubric_assessments, path: 'assessments'
end
get 'outcomes/users/:user_id' => 'outcomes#user_outcome_results', as: :user_outcomes_results
resources :outcomes do
post 'alignments/reorder' => 'outcomes#reorder_alignments', as: :reorder_alignments
get 'alignments/:id' => 'outcomes#alignment_redirect', as: :alignment_redirect
post 'alignments' => 'outcomes#align', as: :align
delete 'alignments/:id' => 'outcomes#remove_alignment', as: :remove_alignment
get 'results' => 'outcomes#outcome_results'
get 'results/:id' => 'outcomes#outcome_result', as: :result
get :details
collection do
get :list
post :add_outcome
end
end
resources :outcome_groups, only: [:create, :update, :destroy] do
post :reorder
end
resources :context_modules, path: :modules do
post 'items' => 'context_modules#add_item', as: :add_item
post 'reorder' => 'context_modules#reorder_items', as: :reorder
post 'collapse' => 'context_modules#toggle_collapse', as: :toggle_collapse
get 'prerequisites/:code' => 'context_modules#content_tag_prerequisites_needing_finishing', as: :prerequisites_needing_finishing
get 'items/last' => 'context_modules#module_redirect', as: :last_redirect, last: 1
get 'items/first' => 'context_modules#module_redirect', as: :first_redirect, first: 1
collection do
post :reorder
get :progressions
end
end
resources :content_exports, only: [:create, :index, :destroy, :show]
get 'modules/items/assignment_info' => 'context_modules#content_tag_assignment_data', as: :context_modules_assignment_info
get 'modules/items/:id' => 'context_modules#item_redirect', as: :context_modules_item_redirect
get 'modules/items/sequence/:id' => 'context_modules#item_details', as: :context_modules_item_details
delete 'modules/items/:id' => 'context_modules#remove_item', as: :context_modules_remove_item
put 'modules/items/:id' => 'context_modules#update_item', as: :context_modules_update_item
get 'confirm_action' => 'courses#confirm_action'
get :copy, as: :start_copy
post 'copy' => 'courses#copy_course', as: :copy_course
concerns :media
get 'user_notes' => 'user_notes#user_notes'
get 'details/sis_publish' => 'courses#sis_publish_status', as: :sis_publish_status
post 'details/sis_publish' => 'courses#publish_to_sis', as: :publish_to_sis
resources :user_lists, only: :create
post 'reset' => 'courses#reset_content'
resources :alerts
post :student_view
delete 'student_view' => 'courses#leave_student_view'
delete 'test_student' => 'courses#reset_test_student'
get 'content_migrations' => 'content_migrations#index'
2011-02-01 09:57:29 +08:00
end
get 'quiz_statistics/:quiz_statistics_id/files/:file_id/download' => 'files#show', as: :quiz_statistics_download, download: '1'
resources :page_views, only: :update
post 'media_objects' => 'context#create_media_object', as: :create_media_object
get 'media_objects/kaltura_notifications' => 'context#kaltura_notifications', as: :kaltura_notifications
get 'media_objects/:id' => 'context#media_object_inline', as: :media_object
get 'media_objects/:id/redirect' => 'context#media_object_redirect', as: :media_object_redirect
get 'media_objects/:id/thumbnail' => 'context#media_object_thumbnail', as: :media_object_thumbnail
get 'media_objects/:media_object_id/info' => 'media_objects#show', as: :media_object_info
get 'media_objects/:media_object_id/media_tracks/:id' => 'media_tracks#show', as: :show_media_tracks
post 'media_objects/:media_object_id/media_tracks' => 'media_tracks#create', as: :create_media_tracks
delete 'media_objects/:media_object_id/media_tracks/:media_track_id' => 'media_tracks#destroy', as: :delete_media_tracks
get 'external_content/success/:service' => 'external_content#success', as: :external_content_success
get 'external_content/retrieve/oembed' => 'external_content#oembed_retrieve', as: :external_content_oembed_retrieve
get 'external_content/cancel/:service' => 'external_content#cancel', as: :external_content_cancel
%w(account course group user).each do |context|
match "#{context.pluralize}/:#{context}_id/external_content/success/:service" => 'external_content#success', as: "#{context}_external_content_success", via: [:get, :post]
end
2011-02-01 09:57:29 +08:00
# We offer a bunch of atom and ical feeds for the user to get
# data out of Instructure. The :feed_code attribute is keyed
# off of either a user, and enrollment, a course, etc. based on
# that item's uuid. In config/initializers/active_record.rb you'll
# find a feed_code method to generate the code, and in
2011-02-01 09:57:29 +08:00
# application_controller there's a get_feed_context to get it back out.
scope '/feeds' do
get 'calendars/:feed_code' => 'calendar_events_api#public_feed', as: :feeds_calendar
get 'calendars/:feed_code.:format' => 'calendar_events_api#public_feed', as: :feeds_calendar_format
get 'forums/:feed_code' => 'discussion_topics#public_feed', as: :feeds_forum
get 'forums/:feed_code.:format' => 'discussion_topics#public_feed', as: :feeds_forum_format
get 'topics/:discussion_topic_id/:feed_code' => 'discussion_entries#public_feed', as: :feeds_topic
get 'topics/:discussion_topic_id/:feed_code.:format' => 'discussion_entries#public_feed', as: :feeds_topic_format
get 'announcements/:feed_code' => 'announcements#public_feed', as: :feeds_announcements
get 'announcements/:feed_code.:format' => 'announcements#public_feed', as: :feeds_announcements_format
get 'courses/:feed_code' => 'courses#public_feed', as: :feeds_course
get 'courses/:feed_code.:format' => 'courses#public_feed', as: :feeds_course_format
get 'groups/:feed_code' => 'groups#public_feed', as: :feeds_group
get 'groups/:feed_code.:format' => 'groups#public_feed', as: :feeds_group_format
get 'enrollments/:feed_code' => 'courses#public_feed', as: :feeds_enrollment
get 'enrollments/:feed_code.:format' => 'courses#public_feed', as: :feeds_enrollment_format
get 'users/:feed_code' => 'users#public_feed', as: :feeds_user
get 'users/:feed_code.:format' => 'users#public_feed', as: :feeds_user_format
get 'eportfolios/:eportfolio_id.:format' => 'eportfolios#public_feed', as: :feeds_eportfolio
get 'conversations/:feed_code' => 'conversations#public_feed', as: :feeds_conversation
get 'conversations/:feed_code.:format' => 'conversations#public_feed', as: :feeds_conversation_format
2011-02-01 09:57:29 +08:00
end
resources :assessment_questions do
get 'files/:id/download' => 'files#assessment_question_show', as: :map, download: '1'
get 'files/:id/preview' => 'files#assessment_question_show', preview: '1'
get 'files/:id/:verifier' => 'files#assessment_question_show', as: :verified_file, download: '1'
2011-02-01 09:57:29 +08:00
end
resources :eportfolios, except: :index do
post :reorder_categories
post ':eportfolio_category_id/reorder_entries' => 'eportfolios#reorder_entries', as: :reorder_entries
resources :categories, controller: :eportfolio_categories
resources :entries, controller: :eportfolio_entries do
resources :page_comments, path: :comments, only: [:create, :destroy]
get 'files/:attachment_id' => 'eportfolio_entries#attachment', as: :view_file
get 'submissions/:submission_id' => 'eportfolio_entries#submission', as: :preview_submission
end
get :export, as: :export_portfolio
get ':category_name' => 'eportfolio_categories#show', as: :named_category
get ':category_name/:entry_name' => 'eportfolio_entries#show', as: :named_category_entry
end
resources :groups do
concerns :users
delete 'remove_user/:user_id' => 'groups#remove_user', as: :remove_user
post :add_user
get 'accept_invitation/:uuid' => 'groups#accept_invitation', as: :accept_invitation
get 'members' => 'groups#context_group_members'
get 'undelete' => 'context#undelete_index', as: :undelete_items
post 'undelete/:asset_string' => 'context#undelete_item', as: :undelete_item
concerns :announcements
concerns :discussions
resources :calendar_events
concerns :files, :file_images, :relative_files, :folders
concerns :zip_file_imports
resources :external_tools, only: :show do
collection do
get :retrieve
end
end
concerns :pages
concerns :conferences
concerns :media
resources :collaborations
get 'calendar' => 'calendars#show2', as: :old_calendar
resources :external_tools do
get :finished
get :resource_selection
collection do
get :retrieve
end
end
end
resources :accounts do
get "settings#{full_path_glob}", action: :settings
get :settings
get :admin_tools
post 'account_users' => 'accounts#add_account_user', as: :add_account_user
delete 'account_users/:id' => 'accounts#remove_account_user', as: :remove_account_user
resources :grading_standards, only: [:index, :create, :update, :destroy]
get :statistics
get 'statistics/over_time/:attribute' => 'accounts#statistics_graph', as: :statistics_graph
get 'statistics/over_time/:attribute.:format' => 'accounts#statistics_graph', as: :formatted_statistics_graph
get :turnitin_confirmation
resources :permissions, controller: :role_overrides, only: [:index, :create] do
collection do
post :add_role
delete :remove_role
end
end
resources :role_overrides, only: [:index, :create] do
collection do
post :add_role
delete :remove_role
end
end
resources :terms, except: [:show, :new, :edit]
resources :sub_accounts
get :avatars
get :sis_import
resources :sis_imports, only: [:create, :show, :index], controller: :sis_imports_api
post 'users' => 'users#create', as: :add_user
get 'users/:user_id/delete' => 'accounts#confirm_delete_user', as: :confirm_delete_user
delete 'users/:user_id' => 'accounts#remove_user', as: :delete_user
clean up user "deletion" fixes CNVS-1552 any time the UI/API tries to "delete" a user, it should only be trying to remove it from some root account (the @domain_root_account if not otherwise specified). if that root account was the last root account the user was associated with, then the remnants of the user are fully deleted, but only then. leave User#destroy as a short-cut to delete the user from all their accounts at once, but should not be invoked directly from any UI/API actions. test-plan: PERMISSIONS being able to remove a user from an account entails being able to: - DELETE http://accounts-domain/users/:user - DELETE /accounts/:account/users/:user both should fail or succeed together * given - Sally who's an admin with the :manage_user_logins permission on one account (Account1) and a student on another account (Account2) - Bob who's a student on both accounts - Alice who's an admin on Account1 with greater permissions than Sally * Sally should: - see "Delete My Account" on her Account1 profile - not see "Delete My Account" on her Account2 profile - not see "Delete My Account" on Bob's Account1 profile - not see "Delete My Account" on Alice's Account1 profile - see "Delete from Account1" at /users/:sally - see "Delete from Account1" at /users/:bob - not see "Delete from Account2" at /users/:sally - not see "Delete from Account2" at /users/:bob - not see "Delete from Account1" at /users/:alice - be able to remove herself from Account1 - be able to remove Bob from Account1 - not be able to remove herself from Account2 - not be able to remove Bob from Account2 - not be able to remove Alice from Account1 * given Sally's Account1 pseudonym has a SIS ID but her Account2 pseudonym doesn't, Sally should: - no longer see "Delete My Account" on her Account1 profile - no longer see "Delete from Account1" at /users/:sally - still see "Delete from Account1" at /users/:bob - no longer be able to remove herself from Account1 - still be able to remove Bob from Account1 EFFECTS * as Sally, remove Bob from Account1 via DELETE http://account1-domain/users/:bob - Bob's pseudonyms, enrollments, etc. in Account1 should be removed - Bob's pseudonyms, enrollments, etc. in Account2 should be untouched * repeat using DELETE /accounts/:account1/users/:bob, with the same expectations Change-Id: Ib7612f95d1c7e4cca36d8486950565ec096b4ab1 Reviewed-on: https://gerrit.instructure.com/41591 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: August Thornton <august@instructure.com> Reviewed-by: Cody Cutrer <cody@instructure.com> Product-Review: Jacob Fugal <jacob@instructure.com>
2014-09-23 07:04:03 +08:00
# create/delete are handled by specific routes just above
resources :users, only: [:index, :new, :edit, :show, :update]
resources :account_notifications, only: [:create, :destroy]
concerns :announcements
resources :assignments
resources :submissions
put 'account_authorization_configs' => 'account_authorization_configs#update_all', as: :update_all_authorization_configs
delete 'account_authorization_configs' => 'account_authorization_configs#destroy_all', as: :remove_all_authorization_configs
refactor views for unified AAC administration closes CNVS-20076 First, pull a presenter out of the AAC index This thing needs more flexibility before introducing multiple OAuth connectors. This commit adds a couple characterization specs for the AAC controller, then drives out a presenter to pull as much logic and config out of the nested views as possible. Then, this commit refactors the previously-somewhat-bespoke-and-presumptive sac configuration into a workflow that shows each aac in turn according to it's type, creating forms for each type at the bottom, and showing the relevant form for a new one based on selection on the right. Have regression tested in the browser to the level of CRUD functionality, but also deserves solid QA for SSO functionality post-configuration. DONE: -successful CAS creation/editing/deletion -successful LDAP creation/editing/deletion -proper differentiation between LDAP primary and secondary -proper SAML creation/editing -move away from "update_all" deprecated endpoint -Selenium Spec fixes -ensure discovery URL and debugging workflows for saml -remove duplication from views -tear down old JS workflow -apply appropriate tests for new behavior -remove presenter methods that are no longer valuable -Moved change_password_url and login_handle_name -up to account settings, removed them from AACs, and built -migrations to manage the transition. -Found and fixed all references to change_password_url on AACs -Found and fixes all references to login_handle_name on AACs -add datafixup for migrating AAC data to account settings -unify repetative individual files into single form delcarations \o/ -remove old SAML editing js -Make sure SAML still works -Make LDAP partial flow just like SAML/CAS -Unify position information across all types -update "acts_as_list" to support STI classes -move discovery URL into account auth form -remove discover URL js management -Unify form generation between new/existing aacs -deprecate discovery url API endpoints -update docs for authorization settings to deprecate their usage in AAC api and redirect their values to current settings for now -make delete links non-js-y to stop this silly page refresh on api completion -make form submissions actually submit the form rather than do this silly page refresh on api completion -See if anything needs “Edit Details” button, remove if not -Wire up removing account settings by blanking out form -Removed "cancel" button from form because fields are always open -placate gergich -Test removing config info -Test population fixup on real data -write docs for authorization settings -fix existing specs -fix routing and docs to not break doc generation -fix stupid jenkins task that thinks it can’t see controls -re-fix selenium -fix saml debugging workflow -write tests for acts_as_list behavior -write tests for authorization settings -remove auth_info types of things -clean up and unify styles where possible TEST PLAN: Regression test creating/deleting/editing and logging in with SSO solutions for CAS, LDAP, and SAML. Should be no functional behavior modification, though workflow will be a little more unified between the 3 currently supported types (each one will require using the menu in the right sidebar to add a new AAC). Also test setting and deleting account settings through the form underneath the configs when there are AACs in existence. Finally, make sure that the SAML Debugging workflow still works. Change-Id: I448db10185512d1b9469c2a425be0a3bcf9e6ebf Reviewed-on: https://gerrit.instructure.com/53448 Tested-by: Jenkins Reviewed-by: Cody Cutrer <cody@instructure.com> QA-Review: August Thornton <august@instructure.com> Product-Review: Ethan Vizitei <evizitei@instructure.com>
2015-04-28 05:23:04 +08:00
get 'sso_settings' => 'account_authorization_configs#show_sso_settings',
as: :sso_settings
put 'sso_settings' => 'account_authorization_configs#update_sso_settings',
as: :update_sso_settings
resources :account_authorization_configs
get 'test_ldap_connections' => 'account_authorization_configs#test_ldap_connection'
get 'test_ldap_binds' => 'account_authorization_configs#test_ldap_bind'
get 'test_ldap_searches' => 'account_authorization_configs#test_ldap_search'
match 'test_ldap_logins' => 'account_authorization_configs#test_ldap_login', via: [:get, :post]
get 'saml_testing' => 'account_authorization_configs#saml_testing'
get 'saml_testing_stop' => 'account_authorization_configs#saml_testing_stop'
get 'external_tools/sessionless_launch' => 'external_tools#sessionless_launch'
resources :external_tools do
get :finished
get :resource_selection
collection do
get :retrieve
end
end
get 'lti/basic_lti_launch_request/:message_handler_id', controller: 'lti/message', action: 'basic_lti_launch_request', as: :basic_lti_launch_request
get 'lti/tool_proxy_registration', controller: 'lti/message', action: 'registration', as: :tool_proxy_registration
get 'lti/registration_return', controller: 'lti/message', action: 'registration_return', as: :registration_return
get 'outcomes/users/:user_id' => 'outcomes#user_outcome_results', as: :user_outcomes_results
resources :outcomes do
get 'results' => 'outcomes#outcome_results'
get 'results/:id' => 'outcomes#outcome_result', as: :result
get :details
collection do
get :list
post :add_outcome
end
end
resources :outcome_groups, only: [:create, :update, :destroy] do
post :reorder
end
resources :rubrics
resources :rubric_associations do
resources :rubric_assessments, path: 'assessments'
end
concerns :files, :file_images, :relative_files, :folders
concerns :media
concerns :groups
resources :outcomes
get :courses
get 'courses/:id' => 'accounts#courses_redirect', as: :courses_redirect
get 'user_notes' => 'user_notes#user_notes'
resources :alerts
resources :question_banks do
post :bookmark
post :reorder
get :questions
post :move_questions
resources :assessment_questions
end
resources :user_lists, only: :create
member do
get :statistics
end
2011-02-01 09:57:29 +08:00
end
get 'images/users/:user_id' => 'users#avatar_image', as: :avatar_image
get 'images/thumbnails/:id/:uuid' => 'files#image_thumbnail', as: :thumbnail_image
get 'images/thumbnails/show/:id/:uuid' => 'files#show_thumbnail', as: :show_thumbnail_image
post 'images/users/:user_id/report' => 'users#report_avatar_image', as: :report_avatar_image
put 'images/users/:user_id' => 'users#update_avatar_image', as: :update_avatar_image
get 'all_menu_courses' => 'users#all_menu_courses'
get 'grades' => 'users#grades'
refactor PseudonymSessionsController fixes CNVS-20394 split it into appropriate concerns. main points are: * /login never renders a login form - it redirects forward to the default auth controller based on the first account authorization config (or discovery url on the account) * /login/canvas is the new home of the old login form. this form is never rendered in-situ anymore - other places that used to render it now redirect to /login (and then forward to here), reducing their knowledge of SSO * /login/ldap ends up at the same place (cause LDAP auth is handled transparently) * /login/cas and /login/saml redirect forward to the first SSO configuration of the appropriate type. /login/:auth_type/:id can be used to select a specific one * if an SSO fails, it redirects back to /login with flash[:error] set. this can forward to the discovery url appropriately, or render an error page appropriately (the old no_auto=1, but now it's not layered on top of the login partial that didn't show a login form) * ?canvas_login=1 is deprecated. just go directly to /login/canvas * /saml_consume, /saml_logout are deprecated. they are processed directly by /login/saml and /login/saml/logout * /login/:id is deprecated - it forwards to /login/:auth_type/:id as appropriate (presumably only saml, since that was the only one that previously should have been using these links) * OTP has been split into its own controller, and separated into multiple actions instead of one all-in-one action * /logout has been vastly simplified. the login controller should set session[:login_aac], and on logout it will check with that AAC for a url to redirect to after logout, instead of /login. SSO logout is handled by each controller if they support it test plan: * regression test the following functionality - * login with canvas auth * login with LDAP auth * login with SAML auth - and multiple SAMLs * login with CAS auth * MFA (configure, using, auto-setup) * Canvas as OAuth Provider flow * redirects to the login page when you're not logged in * failure of SAML/CAS (i.e. can't find user) show a decent error page and allows retry * "sticky" site admin auth (site admin is CAS/SAML, going directly to another domain logs you in with site admin) Change-Id: I1bb9d81a101939f812cbd5020e20749e883fdc0f Reviewed-on: https://gerrit.instructure.com/53220 QA-Review: August Thornton <august@instructure.com> Tested-by: Jenkins Reviewed-by: Ethan Vizitei <evizitei@instructure.com> Product-Review: Cody Cutrer <cody@instructure.com>
2015-05-01 03:58:57 +08:00
get 'login' => 'login#new'
delete 'logout' => 'login#destroy'
get 'logout' => 'login#logout_confirm'
get 'login/canvas' => 'login/canvas#new', as: :canvas_login
post 'login/canvas' => 'login/canvas#create'
# deprecated alias
post 'login' => 'login/canvas#create'
get 'login/ldap' => 'login/ldap#new'
post 'login/ldap' => 'login/ldap#create'
get 'login/cas' => 'login/cas#new'
get 'login/cas/:id' => 'login/cas#new', as: :cas_login
post 'login/cas' => 'login/cas#destroy', as: :cas_logout
post 'login/cas/:id' => 'login/cas#destroy'
get 'login/saml' => 'login/saml#new'
get 'login/saml/logout' => 'login/saml#destroy'
# deprecated alias
get 'saml_logout' => 'login/saml#destroy'
get 'login/saml/:id' => 'login/saml#new', as: :saml_login
post 'login/saml' => 'login/saml#create'
# deprecated alias
post 'saml_consume' => 'login/saml#create'
# the callback URL for all OAuth1.0a based SSO
get 'login/oauth/callback' => 'login/oauth#create', as: :oauth_login_callback
# the callback URL for all OAuth2 based SSO
get 'login/oauth2/callback' => 'login/oauth2#create', as: :oauth2_login_callback
# ActionController::TestCase can't deal with aliased controllers when finding
# routes, so we let this route exist only for tests
get 'login/oauth2' => 'login/oauth2#new' if Rails.env.test?
get 'login/facebook' => 'login/facebook#new', as: :facebook_login
get 'login/github' => 'login/github#new', as: :github_login
get 'login/twitter' => 'login/twitter#new', as: :twitter_login
refactor PseudonymSessionsController fixes CNVS-20394 split it into appropriate concerns. main points are: * /login never renders a login form - it redirects forward to the default auth controller based on the first account authorization config (or discovery url on the account) * /login/canvas is the new home of the old login form. this form is never rendered in-situ anymore - other places that used to render it now redirect to /login (and then forward to here), reducing their knowledge of SSO * /login/ldap ends up at the same place (cause LDAP auth is handled transparently) * /login/cas and /login/saml redirect forward to the first SSO configuration of the appropriate type. /login/:auth_type/:id can be used to select a specific one * if an SSO fails, it redirects back to /login with flash[:error] set. this can forward to the discovery url appropriately, or render an error page appropriately (the old no_auto=1, but now it's not layered on top of the login partial that didn't show a login form) * ?canvas_login=1 is deprecated. just go directly to /login/canvas * /saml_consume, /saml_logout are deprecated. they are processed directly by /login/saml and /login/saml/logout * /login/:id is deprecated - it forwards to /login/:auth_type/:id as appropriate (presumably only saml, since that was the only one that previously should have been using these links) * OTP has been split into its own controller, and separated into multiple actions instead of one all-in-one action * /logout has been vastly simplified. the login controller should set session[:login_aac], and on logout it will check with that AAC for a url to redirect to after logout, instead of /login. SSO logout is handled by each controller if they support it test plan: * regression test the following functionality - * login with canvas auth * login with LDAP auth * login with SAML auth - and multiple SAMLs * login with CAS auth * MFA (configure, using, auto-setup) * Canvas as OAuth Provider flow * redirects to the login page when you're not logged in * failure of SAML/CAS (i.e. can't find user) show a decent error page and allows retry * "sticky" site admin auth (site admin is CAS/SAML, going directly to another domain logs you in with site admin) Change-Id: I1bb9d81a101939f812cbd5020e20749e883fdc0f Reviewed-on: https://gerrit.instructure.com/53220 QA-Review: August Thornton <august@instructure.com> Tested-by: Jenkins Reviewed-by: Ethan Vizitei <evizitei@instructure.com> Product-Review: Cody Cutrer <cody@instructure.com>
2015-05-01 03:58:57 +08:00
get 'login/otp' => 'login/otp#new', as: :otp_login
post 'login/otp/sms' => 'login/otp#send_via_sms', as: :send_otp_via_sms
post 'login/otp' => 'login/otp#create'
# deprecated redirect
get 'login/:id' => 'login#new'
delete 'users/:user_id/mfa' => 'login/otp#destroy', as: :disable_mfa
get 'file_session/clear' => 'login#clear_file_session', as: :clear_file_session
get 'register' => 'users#new'
get 'register_from_website' => 'users#new'
get 'enroll/:self_enrollment_code' => 'self_enrollments#new', as: :enroll
get 'services' => 'users#services'
get 'search/bookmarks' => 'users#bookmark_search', as: :bookmark_search
get 'search/rubrics' => 'search#rubrics'
get 'search/all_courses' => 'search#all_courses'
delete 'tours/dismiss/:name' => 'tours#dismiss', as: :dismiss_tour
delete 'tours/dismiss/session/:name' => 'tours#dismiss_session', as: :dismiss_tour_session
resources :users do
match 'masquerade', via: [:get, :post]
delete :delete
concerns :files, :file_images
concerns :zip_file_imports
resources :page_views, only: :index
resources :folders do
get :download
end
resources :calendar_events
get 'external_tools/:id' => 'users#external_tool', as: :external_tool
resources :rubrics
resources :rubric_associations do
resources :rubric_assessments, path: :assessments
end
resources :pseudonyms, except: :index
resources :question_banks, only: :index
get :assignments_needing_grading
get :assignments_needing_submitting
get :admin_merge
post :merge
get :grades
resources :user_notes
get :manageable_courses
get 'outcomes' => 'outcomes#user_outcome_results'
get 'teacher_activity/course/:course_id' => 'users#teacher_activity', as: :course_teacher_activity
get 'teacher_activity/student/:student_id' => 'users#teacher_activity', as: :student_teacher_activity
get :media_download
resources :messages, only: [:index, :create] do
get :html_message
end
2011-02-01 09:57:29 +08:00
end
get 'show_message_template' => 'messages#show_message_template'
get 'message_templates' => 'messages#templates'
resource :profile, controller: :profile, only: [:show, :update] do
resources :pseudonyms, except: :index
resources :tokens, except: :index
member do
put :update_profile
get :communication
put :communication_update
get :settings
get :observees
end
2011-02-01 09:57:29 +08:00
end
scope '/profile' do
post 'toggle_disable_inbox' => 'profile#toggle_disable_inbox'
get 'profile_pictures' => 'profile#profile_pics', as: :profile_pics
delete 'user_services/:id' => 'users#delete_user_service', as: :profile_user_service
post 'user_services' => 'users#create_user_service', as: :profile_create_user_service
2011-02-01 09:57:29 +08:00
end
get 'about/:id' => 'profile#show', as: :user_profile
resources :communication_channels
2011-02-01 09:57:29 +08:00
get '' => 'users#user_dashboard', as: 'dashboard'
get 'dashboard-sidebar' => 'users#dashboard_sidebar', as: :dashboard_sidebar
post 'toggle_dashboard' => 'users#toggle_dashboard'
get 'styleguide' => 'info#styleguide'
get 'old_styleguide' => 'info#old_styleguide'
root to: 'users#user_dashboard', as: 'root', via: :get
# backwards compatibility with the old /dashboard url
get 'dashboard' => 'users#user_dashboard', as: :dashboard_redirect
2011-02-01 09:57:29 +08:00
# Thought this idea of having dashboard-scoped urls was a good idea at the
# time... now I'm not as big a fan.
resource :dashboard, only: [] do
resources :content_exports, path: :data_exports
end
2011-02-01 09:57:29 +08:00
scope '/dashboard' do
delete 'account_notifications/:id' => 'users#close_notification', as: :dashboard_close_notification
get 'eportfolios' => 'eportfolios#user_index', as: :dashboard_eportfolios
post 'comment_session' => 'services_api#start_kaltura_session', as: :dashboard_comment_session
delete 'ignore_stream_item/:id' => 'users#ignore_stream_item', as: :dashboard_ignore_stream_item
end
2011-02-01 09:57:29 +08:00
resources :plugins, only: [:index, :show, :update]
get 'calendar' => 'calendars#show2'
get 'calendar2' => 'calendars#show2'
get 'course_sections/:course_section_id/calendar_events/:id' => 'calendar_events#show', as: :course_section_calendar_event
get 'files' => 'files#index'
get "files/folder#{full_path_glob}", controller: 'files', action: 'react_files', format: false
get "files/search", controller: 'files', action: 'react_files', format: false
get 'files/s3_success/:id' => 'files#s3_success', as: :s3_success
get 'files/:id/public_url' => 'files#public_url', as: :public_url
get 'files/preflight' => 'files#preflight', as: :file_preflight
post 'files/pending' => 'files#create_pending', as: :file_create_pending
resources :assignments, only: :index do
resources :files, only: [] do
post 'inline_view' => 'files#show', inline: '1'
request scribd re-render when no preview available test plan: 1. work in an account with scribd enabled and google doc previews disabled (in console, add "-google_docs_previews" to the Accounts's allowed_services) 2. upload a scribd-previewable file type in the files tab (it needs to be a new file that is not already in the canvas instance) 3. make sure it previews properly (it may take a minute after uploading for the scribd rendering to complete) 4. delete the file 5. undelete the file (/courses/X/undelete) 6. reload the files tab, and click on the filename on the left side. - on the right, you should see a message like "the preview for this document is unavailable; please try again later". 7. reload the files tab again, then click on the filename. - if the scribd rendering has completed, you should see a scribd preview. if you don't, wait a few minutes and reload the files page again, to give scribd some time. 8. test other areas documents can be previewed. (it's permissible to simply delete the scribd_doc in the console via attachment.delete_scribd_doc, then try to preview, see that a "try again later" message appears, and the scribd preview appears in later page views.) a. documents embedded in rich text via the wiki sidebar b. student submissions, as viewed by the student ("submission details" button, "preview" icon) c. student submissions, as viewed by the teacher in SpeedGrader(TM) fixes CNVS-7019 Change-Id: I37be820a776637252e14b6a28a1be389b718ff9f Reviewed-on: https://gerrit.instructure.com/22438 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Bracken Mosbacker <bracken@instructure.com> QA-Review: August Thornton <august@instructure.com> Product-Review: Matt Goodwin <mattg@instructure.com>
2013-07-17 06:02:44 +08:00
end
end
2011-02-01 09:57:29 +08:00
resources :appointment_groups, only: [:index, :show]
2011-02-01 09:57:29 +08:00
resources :errors, only: [:show, :index, :create], path: :error_reports
2011-02-01 09:57:29 +08:00
get 'health_check' => 'info#health_check'
2011-02-01 09:57:29 +08:00
get 'browserconfig.xml', to: 'info#browserconfig', defaults: { format: 'xml' }
post 'object_snippet' => 'context#object_snippet'
get 'saml_meta_data' => 'accounts#saml_meta_data'
2011-02-01 09:57:29 +08:00
# Routes for course exports
get 'xsd/:version.xsd' => 'content_exports#xml_schema'
resources :jobs, only: [:index, :show] do
collection do
post 'batch_update'
end
end
2011-02-01 09:57:29 +08:00
get 'equation_images/:id' => 'equation_images#show', as: :equation_images, id: /.+/
# assignments at the top level (without a context) -- we have some specs that
# assert these routes exist, but just 404. I'm not sure we ever actually want
# top-level assignments available, maybe we should change the specs instead.
resources :assignments, only: [:index, :show]
resources :files do
get 'download' => 'files#show', download: '1'
end
resources :developer_keys, only: :index
resources :rubrics do
resources :rubric_assessments, path: :assessments
end
post 'selection_test' => 'external_content#selection_test'
resources :quiz_submissions do
concerns :files
end
get 'courses/:course_id/outcome_rollups' => 'outcome_results#rollups', as: 'course_outcome_rollups'
get 'terms_of_use' => 'legal_information#terms_of_use', as: 'terms_of_use_redirect'
get 'privacy_policy' => 'legal_information#privacy_policy', as: 'privacy_policy_redirect'
### API routes ###
# TODO: api routes can't yet take advantage of concerns for DRYness, because of
# the way ApiRouteSet works. For now we get around it by defining methods
# inline in the routes file, but getting concerns working would rawk.
ApiRouteSet::V1.draw(self) do
scope(controller: :courses) do
get 'courses', action: :index, as: 'courses'
put 'courses/:id', action: :update
get 'courses/:id', action: :show, as: 'course'
delete 'courses/:id', action: :destroy
post 'accounts/:account_id/courses', action: :create
get 'courses/:course_id/students', action: :students
get 'courses/:course_id/settings', action: :settings, as: 'course_settings'
put 'courses/:course_id/settings', action: :update_settings
get 'courses/:course_id/recent_students', action: :recent_students, as: 'course_recent_students'
get 'courses/:course_id/users', action: :users, as: 'course_users'
# this api endpoint has been removed, it was redundant with just courses#users
# we keep it around for backward compatibility though
get 'courses/:course_id/search_users', action: :users
get 'courses/:course_id/users/:id', action: :user, as: 'course_user'
get 'courses/:course_id/activity_stream', action: :activity_stream, as: 'course_activity_stream'
get 'courses/:course_id/activity_stream/summary', action: :activity_stream_summary, as: 'course_activity_stream_summary'
get 'courses/:course_id/todo', action: :todo_items
post 'courses/:course_id/preview_html', action: :preview_html
post 'courses/:course_id/course_copy', controller: :content_imports, action: :copy_course_content
get 'courses/:course_id/course_copy/:id', controller: :content_imports, action: :copy_course_status, as: :course_copy_status
get 'courses/:course_id/files', controller: :files, action: :api_index, as: 'course_files'
post 'courses/:course_id/files', action: :create_file, as: 'course_create_file'
get 'courses/:course_id/folders', controller: :folders, action: :list_all_folders, as: 'course_folders'
post 'courses/:course_id/folders', controller: :folders, action: :create
get 'courses/:course_id/folders/by_path/*full_path', controller: :folders, action: :resolve_path
get 'courses/:course_id/folders/by_path', controller: :folders, action: :resolve_path
get 'courses/:course_id/folders/:id', controller: :folders, action: :show, as: 'course_folder'
put 'accounts/:account_id/courses', action: :batch_update
post 'courses/:course_id/ping', action: :ping, as: 'course_ping'
get 'courses/:course_id/link_validation', action: :link_validation
post 'courses/:course_id/link_validation', action: :start_link_validation
post 'courses/:course_id/reset_content', :action => :reset_content
end
scope(controller: :account_notifications) do
post 'accounts/:account_id/account_notifications', action: :create, as: 'account_notification'
get 'accounts/:account_id/users/:user_id/account_notifications', action: :user_index, as: 'user_account_notifications'
delete 'accounts/:account_id/users/:user_id/account_notifications/:id', action: :user_close_notification, as: 'user_account_notification'
end
scope(controller: :tabs) do
get "courses/:course_id/tabs", action: :index, as: 'course_tabs'
get "groups/:group_id/tabs", action: :index, as: 'group_tabs'
put "courses/:course_id/tabs/:tab_id", action: :update
end
scope(controller: :sections) do
get 'courses/:course_id/sections', action: :index, as: 'course_sections'
get 'courses/:course_id/sections/:id', action: :show, as: 'course_section'
get 'sections/:id', action: :show
post 'courses/:course_id/sections', action: :create
put 'sections/:id', action: :update
delete 'sections/:id', action: :destroy
post 'sections/:id/crosslist/:new_course_id', action: :crosslist
delete 'sections/:id/crosslist', action: :uncrosslist
end
scope(controller: :enrollments_api) do
get 'courses/:course_id/enrollments', action: :index, as: 'course_enrollments'
get 'sections/:section_id/enrollments', action: :index, as: 'section_enrollments'
get 'users/:user_id/enrollments', action: :index, as: 'user_enrollments'
get 'accounts/:account_id/enrollments/:id', action: :show, as: 'enrollment'
post 'courses/:course_id/enrollments', action: :create
post 'sections/:section_id/enrollments', action: :create
delete 'courses/:course_id/enrollments/:id', action: :destroy
end
scope(controller: :terms_api) do
get 'accounts/:account_id/terms', action: :index, as: 'enrollment_terms'
end
scope(controller: :terms) do
post 'accounts/:account_id/terms', action: :create
put 'accounts/:account_id/terms/:id', action: :update
delete 'accounts/:account_id/terms/:id', action: :destroy
end
scope(controller: :authentication_audit_api) do
get 'audit/authentication/logins/:login_id', action: :for_login, as: 'audit_authentication_login'
get 'audit/authentication/accounts/:account_id', action: :for_account, as: 'audit_authentication_account'
get 'audit/authentication/users/:user_id', action: :for_user, as: 'audit_authentication_user'
Auditors::Authentication fixes CNVS-390 stores and allows querying by user/account/pseudonym of login/logout events. test-plan: [setup] - set up an 'auditors' keyspace in cassandra and run migrations - have shardX and shardY on one database server, and shardZ on a different database server - have accountW and accountX on shardX - have accountY and accountZ on shardY and shardZ, respectively - have userA on shardX with pseudonymAW in accountW and pseudonymAX in accountX (cross-account, single-shard user) - have userB on shardY with pseudonymBY in accountY and pseudonymBX in accountX (cross-shard user) - have userC on shardZ with pseudonymCZ in accountZ and pseudonymCX in accountX (cross-db-server user) - log in and out of each pseudonym above multiple times [index isolation] - /api/v1/audit/authentication/pseudonyms/<pseudonymAX> should include logins and logouts from pseudonymAX only - /api/v1/audit/authentication/accounts/<accountX> should include logins and logouts from pseudonymAX, pseudonymBX, and pseudonymCX but not pseudonymAW - /api/v1/audit/authentication/users/<userA> should include logins and logouts from both pseudonymAW and pseudonymAX but not pseudonymBX or pseudonymCX [permission isolation] (in each of these, either :view_statistics or :manage_user_logins on an account qualifies as "having permission") - /api/v1/audit/authentication/pseudonyms/<pseudonymAX> should be unauthorized if the current user doesn't have permission on accountX - /api/v1/audit/authentication/accounts/<accountX> should be unauthorized if the current user doesn't have permission on accountX - /api/v1/audit/authentication/users/<userA> should be unauthorized if the current user doesn't have permission on either of accountW or accountX - /api/v1/audit/authentication/users/<userA> should include logins and logouts from accountW but not from accountX if the current user has permission on accountW but not on accountX [sharding] - /api/v1/audit/authentication/users/<userB> should include logins and logouts from both pseudonymBY and pseudonymBX - /api/v1/audit/authentication/users/<userB> should not include duplicate logins and logouts from either pseudonymBY and pseudonymBX (potential for bug due to both pseudonyms' shards being on the same database server) - /api/v1/audit/authentication/users/<userC> should include logins and logouts from both pseudonymCZ and pseudonymCX Change-Id: I74b1573b346935f733fe5b07919d2d450cf07592 Reviewed-on: https://gerrit.instructure.com/21829 Reviewed-by: Brian Palmer <brianp@instructure.com> Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Jeremy Putnam <jeremyp@instructure.com> Product-Review: Jacob Fugal <jacob@instructure.com>
2013-06-28 06:43:15 +08:00
end
scope(controller: :grade_change_audit_api) do
get 'audit/grade_change/assignments/:assignment_id', action: :for_assignment, as: 'audit_grade_change_assignment'
get 'audit/grade_change/courses/:course_id', action: :for_course, as: 'audit_grade_change_course'
get 'audit/grade_change/students/:student_id', action: :for_student, as: 'audit_grade_change_student'
get 'audit/grade_change/graders/:grader_id', action: :for_grader, as: 'audit_grade_change_grader'
end
scope(controller: :course_audit_api) do
get 'audit/course/courses/:course_id', action: :for_course, as: 'audit_course_for_course'
end
scope(controller: :assignments_api) do
get 'courses/:course_id/assignments', action: :index, as: 'course_assignments'
get 'courses/:course_id/assignments/:id', action: :show, as: 'course_assignment'
post 'courses/:course_id/assignments', action: :create
put 'courses/:course_id/assignments/:id', action: :update
delete 'courses/:course_id/assignments/:id', action: :destroy, controller: :assignments
end
scope(controller: :assignment_overrides) do
get 'courses/:course_id/assignments/:assignment_id/overrides', action: :index
post 'courses/:course_id/assignments/:assignment_id/overrides', action: :create
get 'courses/:course_id/assignments/:assignment_id/overrides/:id', action: :show, as: 'assignment_override'
put 'courses/:course_id/assignments/:assignment_id/overrides/:id', action: :update
delete 'courses/:course_id/assignments/:assignment_id/overrides/:id', action: :destroy
get 'sections/:course_section_id/assignments/:assignment_id/override', action: :section_alias
get 'groups/:group_id/assignments/:assignment_id/override', action: :group_alias
end
scope(controller: :submissions_api) do
def submissions_api(context, path_prefix = context)
post "#{context.pluralize}/:#{context}_id/submissions/update_grades", action: :bulk_update
put "#{context.pluralize}/:#{context}_id/assignments/:assignment_id/submissions/:user_id/read", action: :mark_submission_read, as: "#{context}_submission_mark_read"
delete "#{context.pluralize}/:#{context}_id/assignments/:assignment_id/submissions/:user_id/read", action: :mark_submission_unread, as: "#{context}_submission_mark_unread"
get "#{context.pluralize}/:#{context}_id/assignments/:assignment_id/submissions", action: :index, as: "#{path_prefix}_assignment_submissions"
get "#{context.pluralize}/:#{context}_id/students/submissions", controller: :submissions_api, action: :for_students, as: "#{path_prefix}_student_submissions"
get "#{context.pluralize}/:#{context}_id/assignments/:assignment_id/submissions/:user_id", action: :show, as: "#{path_prefix}_assignment_submission"
post "#{context.pluralize}/:#{context}_id/assignments/:assignment_id/submissions", action: :create, controller: :submissions
post "#{context.pluralize}/:#{context}_id/assignments/:assignment_id/submissions/:user_id/files", action: :create_file
put "#{context.pluralize}/:#{context}_id/assignments/:assignment_id/submissions/:user_id", action: :update
post "#{context.pluralize}/:#{context}_id/assignments/:assignment_id/submissions/update_grades", action: :bulk_update
end
submissions_api("course")
submissions_api("section", "course_section")
end
post '/courses/:course_id/assignments/:assignment_id/submissions/:user_id/comments/files',
action: :create_file, controller: :submission_comments_api
scope(controller: :gradebook_history_api) do
get "courses/:course_id/gradebook_history/days", action: :days, as: 'gradebook_history'
get "courses/:course_id/gradebook_history/feed", action: :feed, as: 'gradebook_history_feed'
get "courses/:course_id/gradebook_history/:date", action: :day_details, as: 'gradebook_history_for_day'
get "courses/:course_id/gradebook_history/:date/graders/:grader_id/assignments/:assignment_id/submissions", action: :submissions, as: 'gradebook_history_submissions'
gradebook history api fixes #CNVS-2802 This provides a reimplementation of the functionality in 'lib/submission_list.rb'. That lib file has been used to produce a grade book history page, but it loads the entire history of the course at once, which is untenable. This take all the same data, and provides it through a paginated API endpoint. Using this API, a rich front end will be possible to create in order to replace the current large grade book history page, one that makes API calls to reveal data further d own the tree as it is requested. This is an unusual API endpoint in that it does not present data as it is in the database, there is a series of transformations that the submission data is put through to arrive at a versioned history where each node contains within itself some contextual knowledge of the flow of the submission progress (each version knows what the grade of the version before it was, and what the grade is today, for example). TEST PLAN: This is a new API endpoint and does not yet get used by any front end code yet. It can be played with by performing some valid API queries against the following paths: /courses/:course_id/gradebook_history/days /courses/:course_id/gradebook_history/:date ^ date is like "2013-01-31" ^ /courses/:course_id/gradebook_history/:date/graders/:grader_id/assignments/:assignment_id/submissions (yes, that last one is huge, but it does follow the nested structure described by the original grade book history page). The user that is selected for testing needs to have the "manage_grades" permission on the referenced course. API documentation is available on the controller class app/controllers/gradebook_history_api_controller.rb Change-Id: I18e0b4b967d6c20ad47b86e98adbc15b87276098 Reviewed-on: https://gerrit.instructure.com/17366 QA-Review: Clare Hetherington <clare@instructure.com> Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Brian Palmer <brianp@instructure.com>
2013-02-01 08:35:16 +08:00
end
get 'courses/:course_id/assignment_groups', controller: :assignment_groups, action: :index
scope(controller: :assignment_groups_api) do
resources :assignment_groups, path: "courses/:course_id/assignment_groups", name_prefix: "course_", except: :index
end
scope(controller: :discussion_topics) do
get 'courses/:course_id/discussion_topics', action: :index, as: 'course_discussion_topics'
get 'groups/:group_id/discussion_topics', action: :index, as: 'group_discussion_topics'
end
scope(controller: :content_migrations) do
%w(account course group user).each do |context|
get "#{context.pluralize}/:#{context}_id/content_migrations/migrators", action: :available_migrators, as: "#{context}_content_migration_migrators_list"
get "#{context.pluralize}/:#{context}_id/content_migrations/:id", action: :show, as: "#{context}_content_migration"
get "#{context.pluralize}/:#{context}_id/content_migrations", action: :index, as: "#{context}_content_migration_list"
post "#{context.pluralize}/:#{context}_id/content_migrations", action: :create, as: "#{context}_content_migration_create"
put "#{context.pluralize}/:#{context}_id/content_migrations/:id", action: :update, as: "#{context}_content_migration_update"
get "#{context.pluralize}/:#{context}_id/content_migrations/:id/selective_data", action: :content_list, as: "#{context}_content_migration_selective_data"
end
end
scope(controller: :migration_issues) do
%w(account course group user).each do |context|
get "#{context.pluralize}/:#{context}_id/content_migrations/:content_migration_id/migration_issues/:id", action: :show, as: "#{context}_content_migration_migration_issue"
get "#{context.pluralize}/:#{context}_id/content_migrations/:content_migration_id/migration_issues", action: :index, as: "#{context}_content_migration_migration_issue_list"
post "#{context.pluralize}/:#{context}_id/content_migrations/:content_migration_id/migration_issues", action: :create, as: "#{context}_content_migration_migration_issue_create"
put "#{context.pluralize}/:#{context}_id/content_migrations/:content_migration_id/migration_issues/:id", action: :update, as: "#{context}_content_migration_migration_issue_update"
end
end
scope(controller: :discussion_topics_api) do
def topic_routes(context)
get "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id", action: :show, as: "#{context}_discussion_topic"
post "#{context.pluralize}/:#{context}_id/discussion_topics", controller: :discussion_topics, action: :create
put "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id", controller: :discussion_topics, action: :update
post "#{context.pluralize}/:#{context}_id/discussion_topics/reorder", controller: :discussion_topics, action: :reorder
delete "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id", controller: :discussion_topics, action: :destroy
get "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id/view", action: :view, as: "#{context}_discussion_topic_view"
get "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id/entry_list", action: :entry_list, as: "#{context}_discussion_topic_entry_list"
post "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id/entries", action: :add_entry, as: "#{context}_discussion_add_entry"
get "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id/entries", action: :entries, as: "#{context}_discussion_entries"
post "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id/entries/:entry_id/replies", action: :add_reply, as: "#{context}_discussion_add_reply"
get "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id/entries/:entry_id/replies", action: :replies, as: "#{context}_discussion_replies"
put "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id/entries/:id", controller: :discussion_entries, action: :update, as: "#{context}_discussion_update_reply"
delete "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id/entries/:id", controller: :discussion_entries, action: :destroy, as: "#{context}_discussion_delete_reply"
put "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id/read", action: :mark_topic_read, as: "#{context}_discussion_topic_mark_read"
delete "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id/read", action: :mark_topic_unread, as: "#{context}_discussion_topic_mark_unread"
put "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id/read_all", action: :mark_all_read, as: "#{context}_discussion_topic_mark_all_read"
delete "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id/read_all", action: :mark_all_unread, as: "#{context}_discussion_topic_mark_all_unread"
put "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id/entries/:entry_id/read", action: :mark_entry_read, as: "#{context}_discussion_topic_discussion_entry_mark_read"
delete "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id/entries/:entry_id/read", action: :mark_entry_unread, as: "#{context}_discussion_topic_discussion_entry_mark_unread"
post "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id/entries/:entry_id/rating",
action: :rate_entry, as: "#{context}_discussion_topic_discussion_entry_rate"
put "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id/subscribed", action: :subscribe_topic, as: "#{context}_discussion_topic_subscribe"
delete "#{context.pluralize}/:#{context}_id/discussion_topics/:topic_id/subscribed", action: :unsubscribe_topic, as: "#{context}_discussion_topic_unsubscribe"
end
topic_routes("course")
topic_routes("group")
finish discussion topics API supports creating and listing top-level entries in a discussion topic, and creating and listing replies to a discussion entry. listing discussion topics was already supported. includes support for attachments on top-level entries. test-plan: creating an entry under a topic should allow creating an entry under a topic and create it correctly should return json representation of the new entry should allow creating a reply to an existing top-level entry should not allow reply-to-reply should allow including attachments on top-level entries should silently ignore attachments on replies to top-level entries should include attachment info in the json response listing top-level discussion entries should return top level entries for a topic should return attachments on top level entries should include replies on top level entries should sort top-level entries by descending created_at should sort replies included on top-level entries by descending created_at should paginate top-level entries should only include the first 10 replies for each top-level entry listing replies should return replies for an entry should sort replies by descending created_at should paginate replies require initial post should allow admins to see posts without posting shouldn't allow student who hasn't posted to see shouldn't allow student's observer who hasn't posted to see should allow student who has posted to see should allow student's observer who has posted to see fixes #4752 Change-Id: I0da83e6c301be74f1ac5d2d957ebb6338a98179c Reviewed-on: https://gerrit.instructure.com/6690 Tested-by: Hudson <hudson@instructure.com> Reviewed-by: Cody Cutrer <cody@instructure.com>
2011-11-04 05:51:29 +08:00
end
scope(controller: :collaborations) do
get 'collaborations/:id/members', action: :members, as: 'collaboration_members'
end
scope(controller: :external_tools) do
def et_routes(context)
get "#{context}s/:#{context}_id/external_tools/sessionless_launch", action: :generate_sessionless_launch, as: "#{context}_external_tool_sessionless_launch"
get "#{context}s/:#{context}_id/external_tools/:external_tool_id", action: :show, as: "#{context}_external_tool_show"
get "#{context}s/:#{context}_id/external_tools", action: :index, as: "#{context}_external_tools"
post "#{context}s/:#{context}_id/external_tools", action: :create, as: "#{context}_external_tools_create"
put "#{context}s/:#{context}_id/external_tools/:external_tool_id", action: :update, as: "#{context}_external_tools_update"
delete "#{context}s/:#{context}_id/external_tools/:external_tool_id", action: :destroy, as: "#{context}_external_tools_delete"
end
et_routes("course")
et_routes("account")
end
scope(controller: 'lti/lti_apps') do
def et_routes(context)
get "#{context}s/:#{context}_id/lti_apps/launch_definitions", action: :launch_definitions, as: "#{context}_launch_definitions"
get "#{context}s/:#{context}_id/lti_apps", action: :index, as: "#{context}_app_definitions"
end
et_routes("course")
et_routes("account")
end
scope(controller: 'lti/tool_proxy') do
def et_routes(context)
delete "#{context}s/:#{context}_id/tool_proxies/:tool_proxy_id", action: :destroy, as: "#{context}_delete_tool_proxy"
put "#{context}s/:#{context}_id/tool_proxies/:tool_proxy_id", action: :update, as: "#{context}_update_tool_proxy"
end
et_routes("course")
et_routes("account")
end
scope(controller: :external_feeds) do
def ef_routes(context)
get "#{context}s/:#{context}_id/external_feeds", action: :index, as: "#{context}_external_feeds"
post "#{context}s/:#{context}_id/external_feeds", action: :create, as: "#{context}_external_feeds_create"
delete "#{context}s/:#{context}_id/external_feeds/:external_feed_id", action: :destroy, as: "#{context}_external_feeds_delete"
new discussion topics/announcement index, edit and create closes: #7172 test plan: * open discussion topic index page: - see how it looks in blank course - full course - try graded & unread filters (make sure you see things you expect to and not those you don't) - do bulk actions by clicking checkbox for a few and hitting delete and lock buttons - verify infinite scroll works - verify that as a student you don't see posts that had delayed posting - click "create new" button to make a new one, make sure it works - do all the above in the announcement index page * while viewing announcements index: - verify teacher can create external feed on right - no right side unless external feeds exist or they are teacher - external feeds are listed - only teacher can delete external feed * while editing/creating new discussion/announcement - verify that announcement cant be made into assignment - for discussion topic, set as assignment and make sure the assignment settings set. - cant change discussion -> announcement (& vise versa) - type crazy & blank input, verify that it validates it for you - title cant be longer than 254 - make sure these features work: - podcast feed - student posts in podcast feed - delayed posting - toggling threaded/unthreaded - must post before seeing replies - attach file, remove file attachment, upload new attachment should work * make sure announcements/discussions look & behave right in other places they show up (like course, user dashboard) * if you can think of any other places where you can edit/create discussions/announcements, make sure that still works Change-Id: Ib0acaff8542bf09f99cd7aa99fb3ed16c999d224 Reviewed-on: https://gerrit.instructure.com/12655 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Jon Jensen <jon@instructure.com>
2012-08-01 07:22:48 +08:00
end
ef_routes("course")
ef_routes("group")
new discussion topics/announcement index, edit and create closes: #7172 test plan: * open discussion topic index page: - see how it looks in blank course - full course - try graded & unread filters (make sure you see things you expect to and not those you don't) - do bulk actions by clicking checkbox for a few and hitting delete and lock buttons - verify infinite scroll works - verify that as a student you don't see posts that had delayed posting - click "create new" button to make a new one, make sure it works - do all the above in the announcement index page * while viewing announcements index: - verify teacher can create external feed on right - no right side unless external feeds exist or they are teacher - external feeds are listed - only teacher can delete external feed * while editing/creating new discussion/announcement - verify that announcement cant be made into assignment - for discussion topic, set as assignment and make sure the assignment settings set. - cant change discussion -> announcement (& vise versa) - type crazy & blank input, verify that it validates it for you - title cant be longer than 254 - make sure these features work: - podcast feed - student posts in podcast feed - delayed posting - toggling threaded/unthreaded - must post before seeing replies - attach file, remove file attachment, upload new attachment should work * make sure announcements/discussions look & behave right in other places they show up (like course, user dashboard) * if you can think of any other places where you can edit/create discussions/announcements, make sure that still works Change-Id: Ib0acaff8542bf09f99cd7aa99fb3ed16c999d224 Reviewed-on: https://gerrit.instructure.com/12655 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Jon Jensen <jon@instructure.com>
2012-08-01 07:22:48 +08:00
end
scope(controller: :sis_imports_api) do
post 'accounts/:account_id/sis_imports', action: :create
get 'accounts/:account_id/sis_imports/:id', action: :show
get 'accounts/:account_id/sis_imports', action: :index
end
scope(controller: :users) do
get 'users/self/activity_stream', action: :activity_stream, as: 'user_activity_stream'
get 'users/activity_stream', action: :activity_stream # deprecated
get 'users/self/activity_stream/summary', action: :activity_stream_summary, as: 'user_activity_stream_summary'
delete 'users/self/activity_stream/:id', action: 'ignore_stream_item'
delete 'users/self/activity_stream', action: 'ignore_all_stream_items'
put "users/:user_id/followers/self", action: :follow
delete "users/:user_id/followers/self", action: :unfollow
get 'users/self/todo', action: :todo_items
get 'users/self/upcoming_events', action: :upcoming_events
delete 'users/self/todo/:asset_string/:purpose', action: :ignore_item, as: 'users_todo_ignore'
post 'accounts/:account_id/users', action: :create
get 'accounts/:account_id/users', action: :index, as: 'account_users'
get 'users/:id', action: :api_show
put 'users/:id', action: :update
post 'users/:user_id/files', action: :create_file
get 'users/:user_id/files', controller: :files, action: :api_index, as: 'user_files'
get 'users/:user_id/folders', controller: :folders, action: :list_all_folders, as: 'user_folders'
post 'users/:user_id/folders', controller: :folders, action: :create
get 'users/:user_id/folders/by_path/*full_path', controller: :folders, action: :resolve_path
get 'users/:user_id/folders/by_path', controller: :folders, action: :resolve_path
get 'users/:user_id/folders/:id', controller: :folders, action: :show, as: 'user_folder'
get 'users/:id/settings', controller: 'users', action: 'settings'
put 'users/:id/settings', controller: 'users', action: 'settings', as: 'user_settings'
get 'users/:id/colors', controller: 'users', action: 'get_custom_colors'
get 'users/:id/colors/:asset_string', controller: 'users', action: 'get_custom_color'
put 'users/:id/colors/:asset_string', controller: 'users', action: 'set_custom_color'
put 'users/:id/merge_into/:destination_user_id', controller: 'users', action: 'merge_into'
put 'users/:id/merge_into/accounts/:destination_account_id/users/:destination_user_id', controller: 'users', action: 'merge_into'
scope(controller: :user_observees) do
get 'users/:user_id/observees', action: :index, as: 'user_observees'
post 'users/:user_id/observees', action: :create
get 'users/:user_id/observees/:observee_id', action: :show, as: 'user_observee'
put 'users/:user_id/observees/:observee_id', action: :update
delete 'users/:user_id/observees/:observee_id', action: :destroy
end
end
scope(controller: :custom_data) do
glob = '(/*scope)'
add serializable hash of CustomData for User models fixes CNVS-11424 test plan: - rake db:migrate - if you don't already have one, set yourself up with a developer token. (you can do so from <canvas>/developer_keys) - $ curl -H "Authorization: Bearer <ACCESS-TOKEN>" \ -X GET -F 'ns=test' \ <canvas>/api/v1/users/self/custom_data #=> {"message":"no data for scope"} - $ curl -H "Authorization: Bearer <ACCESS-TOKEN>" \ -X PUT -F 'ns=test' \ -F 'data[apple]=so tasty' \ -F 'data[kiwi]=a bit sour' \ <canvas>/api/v1/users/self/custom_data/fruit #=> {"data":{"apple":"so tasty","kiwi":"a bit sour"}} - $ curl -H "Authorization: Bearer <ACCESS-TOKEN>" \ -X GET -F 'ns=test' \ <canvas>/api/v1/users/self/custom_data #=> {"data":{"fruit":{"apple":"so tasty","kiwi":"a bit sour"}}} - $ curl -H "Authorization: Bearer <ACCESS-TOKEN>" \ -X DELETE -F 'ns=test' \ <canvas>/api/v1/users/self/custom_data/fruit/kiwi #=> {"data":"a bit sour"} - $ curl -H "Authorization: Bearer <ACCESS-TOKEN>" \ -X GET -F 'ns=test' \ <canvas>/api/v1/users/self/custom_data #=> {"data":{"fruit":{"apple":"so tasty"}}} - see new API doc for more info about how it should work, but basically, you should be able to GET, PUT, and DELETE at will for any given scope, and PUTting JSON hashes creates referenceable scopes. (e.g. the DELETE above has 'kiwi' in its scope) Change-Id: If027ae4aeec14edf44275ba0372a68aef7e5600e Reviewed-on: https://gerrit.instructure.com/31173 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Matt Fairbourn <mfairbourn@instructure.com> Reviewed-by: Jon Jensen <jon@instructure.com> Product-Review: Jon Jensen <jon@instructure.com>
2014-03-04 03:55:14 +08:00
get "users/:user_id/custom_data#{glob}", action: 'get_data'
put "users/:user_id/custom_data#{glob}", action: 'set_data'
delete "users/:user_id/custom_data#{glob}", action: 'delete_data'
end
scope(controller: :pseudonyms) do
get 'accounts/:account_id/logins', action: :index, as: 'account_pseudonyms'
get 'users/:user_id/logins', action: :index, as: 'user_pseudonyms'
post 'accounts/:account_id/logins', action: :create
put 'accounts/:account_id/logins/:id', action: :update
delete 'users/:user_id/logins/:id', action: :destroy
end
scope(controller: :accounts) do
get 'accounts', action: :index, as: :accounts
get 'course_accounts', :action => :course_accounts, :as => :course_accounts
get 'accounts/:id', action: :show, as: :account
put 'accounts/:id', action: :update
get 'accounts/:account_id/courses', action: :courses_api, as: 'account_courses'
get 'accounts/:account_id/sub_accounts', action: :sub_accounts, as: 'sub_accounts'
get 'accounts/:account_id/courses/:id', controller: :courses, action: :show, as: 'account_course_show'
clean up user "deletion" fixes CNVS-1552 any time the UI/API tries to "delete" a user, it should only be trying to remove it from some root account (the @domain_root_account if not otherwise specified). if that root account was the last root account the user was associated with, then the remnants of the user are fully deleted, but only then. leave User#destroy as a short-cut to delete the user from all their accounts at once, but should not be invoked directly from any UI/API actions. test-plan: PERMISSIONS being able to remove a user from an account entails being able to: - DELETE http://accounts-domain/users/:user - DELETE /accounts/:account/users/:user both should fail or succeed together * given - Sally who's an admin with the :manage_user_logins permission on one account (Account1) and a student on another account (Account2) - Bob who's a student on both accounts - Alice who's an admin on Account1 with greater permissions than Sally * Sally should: - see "Delete My Account" on her Account1 profile - not see "Delete My Account" on her Account2 profile - not see "Delete My Account" on Bob's Account1 profile - not see "Delete My Account" on Alice's Account1 profile - see "Delete from Account1" at /users/:sally - see "Delete from Account1" at /users/:bob - not see "Delete from Account2" at /users/:sally - not see "Delete from Account2" at /users/:bob - not see "Delete from Account1" at /users/:alice - be able to remove herself from Account1 - be able to remove Bob from Account1 - not be able to remove herself from Account2 - not be able to remove Bob from Account2 - not be able to remove Alice from Account1 * given Sally's Account1 pseudonym has a SIS ID but her Account2 pseudonym doesn't, Sally should: - no longer see "Delete My Account" on her Account1 profile - no longer see "Delete from Account1" at /users/:sally - still see "Delete from Account1" at /users/:bob - no longer be able to remove herself from Account1 - still be able to remove Bob from Account1 EFFECTS * as Sally, remove Bob from Account1 via DELETE http://account1-domain/users/:bob - Bob's pseudonyms, enrollments, etc. in Account1 should be removed - Bob's pseudonyms, enrollments, etc. in Account2 should be untouched * repeat using DELETE /accounts/:account1/users/:bob, with the same expectations Change-Id: Ib7612f95d1c7e4cca36d8486950565ec096b4ab1 Reviewed-on: https://gerrit.instructure.com/41591 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: August Thornton <august@instructure.com> Reviewed-by: Cody Cutrer <cody@instructure.com> Product-Review: Jacob Fugal <jacob@instructure.com>
2014-09-23 07:04:03 +08:00
delete 'accounts/:account_id/users/:user_id', action: :remove_user
end
scope(controller: :sub_accounts) do
post 'accounts/:account_id/sub_accounts', action: :create
end
scope(controller: :role_overrides) do
get 'accounts/:account_id/roles', action: :api_index, as: 'account_roles'
get 'accounts/:account_id/roles/:id', action: :show
post 'accounts/:account_id/roles', action: :add_role
post 'accounts/:account_id/roles/:id/activate', action: :activate_role
put 'accounts/:account_id/roles/:id', action: :update
delete 'accounts/:account_id/roles/:id', action: :remove_role
add new permission and API endpoint for manage_catalog refs CAT-819, CAT-822 Test plan: 1. rake db:migrate and sign into Canvas as an admin 2. Visit the permissions page for your account hooked up to Catalog and switch to the Account Roles tab. 3. Note the "Manage catalog" permission does not show up. 4. Now, console in and update that account to have a setting of :catalog_enabled => true, e.g. a = Account.find(1) a.settings[:catalog_enabled] = true a.save! 5. Reload Canvas and note that "Manage catalog" now displays in the Account Roles tab. refs CAT-823 Test plan: 1. As a few different user types, make API requests to /api/v1/accounts/:id/permissions/manage_catalog For instance: curl 'http://canvas.dev:3000/api/v1/accounts/1/permissions/manage_catalog' -H 'Authorization: Bearer your-token' * Account admins should receive {granted: true} * Non-admin users should receive {granted: false} * Users with custom roles that have the Manage Catalog permission should receive {granted: true} * If :catalog_enabled is not set on the account in question, it should never return {granted: true} * Try changing up the permission name in the URL (to something other than manage_catalog). The response should be an error (status code: 400), since we only support checking manage_catalog for now. Change-Id: I4fa53665ff866f5c016f32ce72036e8b5a75bda5 Reviewed-on: https://gerrit.instructure.com/48119 Tested-by: Jenkins Reviewed-by: Ethan Gunderson <egunderson@instructure.com> Product-Review: Ethan Gunderson <egunderson@instructure.com> QA-Review: Ethan Gunderson <egunderson@instructure.com>
2015-02-03 13:43:14 +08:00
get 'accounts/:account_id/permissions/:permission', action: :check_account_permission
end
scope(controller: :account_reports) do
get 'accounts/:account_id/reports/:report', action: :index
get 'accounts/:account_id/reports', action: :available_reports
get 'accounts/:account_id/reports/:report/:id', action: :show
post 'accounts/:account_id/reports/:report', action: :create, as: 'account_create_report'
delete 'accounts/:account_id/reports/:report/:id', action: :destroy
end
scope(controller: :admins) do
post 'accounts/:account_id/admins', action: :create
delete 'accounts/:account_id/admins/:user_id', action: :destroy
get 'accounts/:account_id/admins', action: :index, as: 'account_admins'
end
scope(controller: :account_authorization_configs) do
get 'accounts/:account_id/account_authorization_configs/discovery_url', action: :show_discovery_url
put 'accounts/:account_id/account_authorization_configs/discovery_url', action: :update_discovery_url, as: 'account_update_discovery_url'
delete 'accounts/:account_id/account_authorization_configs/discovery_url', action: :destroy_discovery_url, as: 'account_destroy_discovery_url'
refactor views for unified AAC administration closes CNVS-20076 First, pull a presenter out of the AAC index This thing needs more flexibility before introducing multiple OAuth connectors. This commit adds a couple characterization specs for the AAC controller, then drives out a presenter to pull as much logic and config out of the nested views as possible. Then, this commit refactors the previously-somewhat-bespoke-and-presumptive sac configuration into a workflow that shows each aac in turn according to it's type, creating forms for each type at the bottom, and showing the relevant form for a new one based on selection on the right. Have regression tested in the browser to the level of CRUD functionality, but also deserves solid QA for SSO functionality post-configuration. DONE: -successful CAS creation/editing/deletion -successful LDAP creation/editing/deletion -proper differentiation between LDAP primary and secondary -proper SAML creation/editing -move away from "update_all" deprecated endpoint -Selenium Spec fixes -ensure discovery URL and debugging workflows for saml -remove duplication from views -tear down old JS workflow -apply appropriate tests for new behavior -remove presenter methods that are no longer valuable -Moved change_password_url and login_handle_name -up to account settings, removed them from AACs, and built -migrations to manage the transition. -Found and fixed all references to change_password_url on AACs -Found and fixes all references to login_handle_name on AACs -add datafixup for migrating AAC data to account settings -unify repetative individual files into single form delcarations \o/ -remove old SAML editing js -Make sure SAML still works -Make LDAP partial flow just like SAML/CAS -Unify position information across all types -update "acts_as_list" to support STI classes -move discovery URL into account auth form -remove discover URL js management -Unify form generation between new/existing aacs -deprecate discovery url API endpoints -update docs for authorization settings to deprecate their usage in AAC api and redirect their values to current settings for now -make delete links non-js-y to stop this silly page refresh on api completion -make form submissions actually submit the form rather than do this silly page refresh on api completion -See if anything needs “Edit Details” button, remove if not -Wire up removing account settings by blanking out form -Removed "cancel" button from form because fields are always open -placate gergich -Test removing config info -Test population fixup on real data -write docs for authorization settings -fix existing specs -fix routing and docs to not break doc generation -fix stupid jenkins task that thinks it can’t see controls -re-fix selenium -fix saml debugging workflow -write tests for acts_as_list behavior -write tests for authorization settings -remove auth_info types of things -clean up and unify styles where possible TEST PLAN: Regression test creating/deleting/editing and logging in with SSO solutions for CAS, LDAP, and SAML. Should be no functional behavior modification, though workflow will be a little more unified between the 3 currently supported types (each one will require using the menu in the right sidebar to add a new AAC). Also test setting and deleting account settings through the form underneath the configs when there are AACs in existence. Finally, make sure that the SAML Debugging workflow still works. Change-Id: I448db10185512d1b9469c2a425be0a3bcf9e6ebf Reviewed-on: https://gerrit.instructure.com/53448 Tested-by: Jenkins Reviewed-by: Cody Cutrer <cody@instructure.com> QA-Review: August Thornton <august@instructure.com> Product-Review: Ethan Vizitei <evizitei@instructure.com>
2015-04-28 05:23:04 +08:00
get 'accounts/:account_id/sso_settings',
action: :show_sso_settings,
as: 'account_show_sso_settings_url'
put 'accounts/:account_id/sso_settings',
action: :update_sso_settings,
as: 'account_update_sso_settings_url'
get 'accounts/:account_id/account_authorization_configs', action: :index
get 'accounts/:account_id/account_authorization_configs/:id', action: :show
post 'accounts/:account_id/account_authorization_configs', action: :create, as: 'account_create_aac'
put 'accounts/:account_id/account_authorization_configs/:id', action: :update, as: 'account_update_aac'
delete 'accounts/:account_id/account_authorization_configs/:id', action: :destroy, as: 'account_delete_aac'
end
get 'users/:user_id/page_views', controller: :page_views, action: :index, as: 'user_page_views'
get 'users/:user_id/profile', controller: :profile, action: :settings
get 'users/:user_id/avatars', controller: :profile, action: :profile_pics
# deprecated routes, second one is solely for YARD. preferred API is api/v1/search/recipients
get 'conversations/find_recipients', controller: :search, action: :recipients
get 'conversations/find_recipients', controller: :conversations, action: :find_recipients
scope(controller: :conversations) do
get 'conversations', action: :index, as: 'conversations'
post 'conversations', action: :create
post 'conversations/mark_all_as_read', action: :mark_all_as_read
get 'conversations/batches', action: :batches, as: 'conversations_batches'
get 'conversations/unread_count', action: :unread_count
get 'conversations/:id', action: :show
put 'conversations/:id', action: :update # stars, subscribed-ness, workflow_state
delete 'conversations/:id', action: :destroy
post 'conversations/:id/add_message', action: :add_message
post 'conversations/:id/add_recipients', action: :add_recipients
post 'conversations/:id/remove_messages', action: :remove_messages
put 'conversations', action: :batch_update
delete 'conversations/:id/delete_for_all', action: :delete_for_all
end
scope(controller: :communication_channels) do
get 'users/:user_id/communication_channels', action: :index, as: 'communication_channels'
post 'users/:user_id/communication_channels', action: :create
delete 'users/:user_id/communication_channels/:id', action: :destroy
delete 'users/:user_id/communication_channels/:type/:address', action: :destroy, constraints: { address: %r{[^/?]+} }
end
scope(controller: :notification_preferences) do
get 'users/:user_id/communication_channels/:communication_channel_id/notification_preferences', action: :index
get 'users/:user_id/communication_channels/:type/:address/notification_preferences', action: :index, constraints: { address: %r{[^/?]+} }
get 'users/:user_id/communication_channels/:communication_channel_id/notification_preferences/:notification', action: :show
get 'users/:user_id/communication_channels/:type/:address/notification_preferences/:notification', action: :show, constraints: { address: %r{[^/?]+} }
put 'users/self/communication_channels/:communication_channel_id/notification_preferences/:notification', action: :update
put 'users/self/communication_channels/:type/:address/notification_preferences/:notification', action: :update, constraints: { address: %r{[^/?]+} }
put 'users/self/communication_channels/:communication_channel_id/notification_preferences', action: :update_all
put 'users/self/communication_channels/:type/:address/notification_preferences', action: :update_all, constraints: { address: %r{[^/?]+} }
end
scope(controller: :comm_messages_api) do
get 'comm_messages', action: :index, as: 'comm_messages'
end
scope(controller: :services_api) do
get 'services/kaltura', action: :show_kaltura_config
post 'services/kaltura_session', action: :start_kaltura_session
end
scope(controller: :calendar_events_api) do
get 'calendar_events', action: :index, as: 'calendar_events'
post 'calendar_events', action: :create
get 'calendar_events/:id', action: :show, as: 'calendar_event'
put 'calendar_events/:id', action: :update
delete 'calendar_events/:id', action: :destroy
post 'calendar_events/:id/reservations', action: :reserve
post 'calendar_events/:id/reservations/:participant_id', action: :reserve, as: 'calendar_event_reserve'
end
scope(controller: :appointment_groups) do
get 'appointment_groups', action: :index, as: 'appointment_groups'
post 'appointment_groups', action: :create
get 'appointment_groups/:id', action: :show, as: 'appointment_group'
put 'appointment_groups/:id', action: :update
delete 'appointment_groups/:id', action: :destroy
get 'appointment_groups/:id/users', action: :users, as: 'appointment_group_users'
get 'appointment_groups/:id/groups', action: :groups, as: 'appointment_group_groups'
end
scope(controller: :groups) do
resources :groups, except: :index
get 'users/self/groups', action: :index, as: "current_user_groups"
get 'accounts/:account_id/groups', action: :context_index, as: 'account_user_groups'
get 'courses/:course_id/groups', action: :context_index, as: 'course_user_groups'
get 'groups/:group_id/users', action: :users, as: 'group_users'
post 'groups/:group_id/invite', action: :invite
post 'groups/:group_id/files', action: :create_file
post 'groups/:group_id/preview_html', action: :preview_html
post 'group_categories/:group_category_id/groups', action: :create
get 'groups/:group_id/activity_stream', action: :activity_stream, as: 'group_activity_stream'
get 'groups/:group_id/activity_stream/summary', action: :activity_stream_summary, as: 'group_activity_stream_summary'
put "groups/:group_id/followers/self", action: :follow
delete "groups/:group_id/followers/self", action: :unfollow
scope(controller: :group_memberships) do
resources :memberships, path: "groups/:group_id/memberships", name_prefix: "group_", controller: :group_memberships
resources :users, path: "groups/:group_id/users", name_prefix: "group_", controller: :group_memberships, except: [:index, :create]
end
get 'groups/:group_id/files', controller: :files, action: :api_index, as: 'group_files'
get 'groups/:group_id/folders', controller: :folders, action: :list_all_folders, as: 'group_folders'
post 'groups/:group_id/folders', controller: :folders, action: :create
get 'groups/:group_id/folders/by_path/*full_path', controller: :folders, action: :resolve_path
get 'groups/:group_id/folders/by_path', controller: :folders, action: :resolve_path
get 'groups/:group_id/folders/:id', controller: :folders, action: :show, as: 'group_folder'
end
scope(controller: :developer_keys) do
get 'developer_keys', action: :index
get 'developer_keys/:id', action: :show
delete 'developer_keys/:id', action: :destroy
put 'developer_keys/:id', action: :update
post 'developer_keys', action: :create
end
scope(controller: :search) do
get 'search/rubrics', action: 'rubrics', as: 'search_rubrics'
get 'search/recipients', action: 'recipients', as: 'search_recipients'
get 'search/all_courses', action: 'all_courses', as: 'search_all_courses'
end
post 'files/:id/create_success', controller: :files, action: :api_create_success, as: 'files_create_success'
get 'files/:id/create_success', controller: :files, action: :api_create_success
scope(controller: :files) do
post 'files/:id/create_success', action: :api_create_success
get 'files/:id/create_success', action: :api_create_success
modules api, closes #10404 also modifies the discussion topic and assignment API controllers to make sure "must_view" requirements are fulfilled test plan: * check the API documentation; ensure it looks okay * create a course with module items of each supported type * set completion criteria of each supported type * create another module, so you can set prerequisites * use the list modules API and verify its output matches the course and the documentation * as a teacher, "state" should be missing * as a student, "state" should be "locked", "unlocked", "started", or "completed" * use the show module API and verify the correct information is returned for a single module * use the list module items API and verify the output * as a teacher, the "completion_requirement" omits the "completed" flag * as a student, "completed" should be true or false, depending on whether the requirement was met * use the show module API and verify the correct information is returned for a single module item * last but not least, verify "must view" requirements can be fulfilled through the api_data_endpoints supplied for files, pages, discussions, and assignments * files are viewed when downloading their content * pages are viewed by the show action (where content is returned) * discussions are viewed when marked read via the mark_topic_read or mark_all_read actions * assignments are viewed by the show action (where description is returned). they are not viewed if the assignment is locked and the user does not have access to the content yet. Change-Id: I0cbbbc542f69215e7b396a501d4d86ff2f76c149 Reviewed-on: https://gerrit.instructure.com/13626 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Simon Williams <simon@instructure.com>
2012-09-12 01:16:48 +08:00
# 'attachment' (rather than 'file') is used below so modules API can use polymorphic_url to generate an item API link
get 'files/:id', action: :api_show, as: 'attachment'
delete 'files/:id', action: :destroy
put 'files/:id', action: :api_update
get 'files/:id/:uuid/status', action: :api_file_status, as: 'file_status'
get 'files/:id/public_url', action: :public_url
%w(course group user).each do |context|
get "#{context}s/:#{context}_id/files/quota", action: :api_quota
get "#{context}s/:#{context}_id/files/:id", action: :api_show, as: "#{context}_attachment"
end
end
scope(controller: :folders) do
get 'folders/:id', action: :show
get 'folders/:id/folders', action: :api_index, as: 'list_folders'
get 'folders/:id/files', controller: :files, action: :api_index, as: 'list_files'
delete 'folders/:id', action: :api_destroy
put 'folders/:id', action: :update
post 'folders/:folder_id/folders', action: :create, as: 'create_folder'
post 'folders/:folder_id/files', action: :create_file
post 'folders/:dest_folder_id/copy_file', action: :copy_file
post 'folders/:dest_folder_id/copy_folder', action: :copy_folder
end
scope(controller: :favorites) do
get "users/self/favorites/courses", action: :list_favorite_courses, as: :list_favorite_courses
post "users/self/favorites/courses/:id", action: :add_favorite_course, as: :add_favorite_course
delete "users/self/favorites/courses/:id", action: :remove_favorite_course, as: :remove_favorite_course
delete "users/self/favorites/courses", action: :reset_course_favorites
end
scope(controller: :wiki_pages_api) do
get "courses/:course_id/front_page", action: :show_front_page
get "groups/:group_id/front_page", action: :show_front_page
put "courses/:course_id/front_page", action: :update_front_page
put "groups/:group_id/front_page", action: :update_front_page
allow PUT requests to create wiki pages implicitly testing note: this change affects all wiki page endpoints: api, draft state, and non-draft state. basic functionality for all of these pages should be verified, specifically when dealing with non-existent or deleted pages. test plan: * PUT to /api/v1/courses/:course_id/front_page - variations: * wiki_page[title] - provided/not provided * wiki_page[published] - true/false/not provided * wiki_page[hide_from_students] - true/false/not provided (hiding/unpublishing the front page is not allowed) * PUT to /api/v1/courses/:course_id/pages/non-existent-page - same variations as front_page (above) * navigating to a non-existent/deleted page (draft state/non-draft state) - should redirect teachers to the edit page - should redirect students to the index page (with an alert message indicating why) * navigating to the 'Pages' tab (non-draft state) - if the 'Front Page' exists - shows the page - if the 'Front Page' has been deleted (with draft state enabled) - shows the edit page - if the 'Front Page' has never existed - shows the edit page * all other pages functionality should remain unchanged - non-draft state UI - show page - edit page - draft state UI - index page - show page - edit page - api - .../pages - GET (index) - POST (create page) - .../front_page - GET (show front page) - PUT (create/update front page) - .../pages/page-url - GET (show page) - PUT (create/update page) - DELETE (destroy page) fixes CNVS-8488 Change-Id: I563e6944e1602e0b21ab69c6fe2dcd643c06611d Reviewed-on: https://gerrit.instructure.com/23590 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Matt Fairbourn <mfairbourn@instructure.com> Reviewed-by: Jeremy Stanley <jeremy@instructure.com> Product-Review: Bracken Mosbacker <bracken@instructure.com>
2013-08-22 23:43:03 +08:00
get "courses/:course_id/pages", action: :index, as: 'course_wiki_pages'
get "groups/:group_id/pages", action: :index, as: 'group_wiki_pages'
get "courses/:course_id/pages/:url", action: :show, as: 'course_wiki_page'
get "groups/:group_id/pages/:url", action: :show, as: 'group_wiki_page'
get "courses/:course_id/pages/:url/revisions", action: :revisions, as: 'course_wiki_page_revisions'
get "groups/:group_id/pages/:url/revisions", action: :revisions, as: 'group_wiki_page_revisions'
get "courses/:course_id/pages/:url/revisions/latest", action: :show_revision
get "groups/:group_id/pages/:url/revisions/latest", action: :show_revision
get "courses/:course_id/pages/:url/revisions/:revision_id", action: :show_revision
get "groups/:group_id/pages/:url/revisions/:revision_id", action: :show_revision
post "courses/:course_id/pages/:url/revisions/:revision_id", action: :revert
post "groups/:group_id/pages/:url/revisions/:revision_id", action: :revert
post "courses/:course_id/pages", action: :create
post "groups/:group_id/pages", action: :create
put "courses/:course_id/pages/:url", action: :update
put "groups/:group_id/pages/:url", action: :update
delete "courses/:course_id/pages/:url", action: :destroy
delete "groups/:group_id/pages/:url", action: :destroy
end
scope(controller: :context_modules_api) do
get "courses/:course_id/modules", action: :index, as: 'course_context_modules'
get "courses/:course_id/modules/:id", action: :show, as: 'course_context_module'
put "courses/:course_id/modules", action: :batch_update
post "courses/:course_id/modules", action: :create, as: 'course_context_module_create'
put "courses/:course_id/modules/:id", action: :update, as: 'course_context_module_update'
delete "courses/:course_id/modules/:id", action: :destroy
put "courses/:course_id/modules/:id/relock", action: :relock
end
scope(controller: :context_module_items_api) do
get "courses/:course_id/modules/:module_id/items", action: :index, as: 'course_context_module_items'
get "courses/:course_id/modules/:module_id/items/:id", action: :show, as: 'course_context_module_item'
put "courses/:course_id/modules/:module_id/items/:id/done", action: :mark_as_done, as: 'course_context_module_item_done'
delete "courses/:course_id/modules/:module_id/items/:id/done", action: :mark_as_not_done, as: 'course_context_module_item_not_done'
get "courses/:course_id/module_item_redirect/:id", action: :redirect, as: 'course_context_module_item_redirect'
get "courses/:course_id/module_item_sequence", action: :item_sequence
post "courses/:course_id/modules/:module_id/items", action: :create, as: 'course_context_module_items_create'
put "courses/:course_id/modules/:module_id/items/:id", action: :update, as: 'course_context_module_item_update'
delete "courses/:course_id/modules/:module_id/items/:id", action: :destroy
post "courses/:course_id/modules/:module_id/items/:id/mark_read", action: :mark_item_read
end
scope(controller: 'quizzes/quiz_assignment_overrides') do
get "courses/:course_id/quizzes/assignment_overrides", action: :index, as: 'course_quiz_assignment_overrides'
quiz index optimizations Closes CNVS-15109, CNVS-15173 CHANGES ------- - "auto-grading" of due submissions that was previously done synchronously in the index action is now done in a DJ - when viewing the index page, you don't get to see due/available dates on load, instead the dates are fetched on the client-side and load progressively - new API endpoint for retrieving assignment overrides for a bunch of quizzes at [GET] /courses/:course_id/quizzes/assignment_overrides - we now cache the user's quiz permissions - Canvas AMS API serializer now accepts a new option, see docs - QuizSerializer behavior changed radically: - "takeable", "submitted_students", "unsubmitted_students" disabled - all associations disabled including the submission, assignment group, and any student participants - it can now utilize preloaded permissions Rationale behind disabling things in the serializer is that these were exclusive for the "show" action, so the next step forwards is to allow the serializer to recognize different "modes" for output (e.g, for index and one for show) and tailor the associations/fields accordingly. Using "#filter" right now isn't cutting it, because assocs get loaded anyway. REFACTORING ----------- - broke down index into three actions for visibility: 1. default, Draft-State version 2. legacy non-DS version that's not reachable in the UI, kept around until we upgrade the tests 3. ember version - legacy non-DS ERB code goes into its own file - moved code that used to grade due submissions inside index to SubmissionGrader in preparation to remove it from there entirely - cleaned up internal docs for the Canvas AMS api serializer TEST PLAN ---- ---- - create ~30 quizzes + make one of them have many questions + make a good number of submissions (i tested with 420 and 20 students) - create multiple sections + specify date overrides for certain sections, and have at least one student enrolled in that section - as a teacher and/or an observer, go to quizzes index + verify the page renders fine + verify that you see "loading indicators" in the due/available field which get replaced with actual dates when they're loaded + verify it's faster than the version in master (should be at least 60% faster) - as a student in the general section, go to quizzes index + verify the page renders + verify you see the same loading behavior for dates as in teacher view - as a student in one of the section with overrides, go to index: + verify you see the overridden date Change-Id: I741d89625da1b858148baa95e881fcc75c1802e5 Reviewed-on: https://gerrit.instructure.com/40350 Reviewed-by: Derek DeVries <ddevries@instructure.com> Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Trevor deHaan <tdehaan@instructure.com> Product-Review: Ahmad Amireh <ahmad@instructure.com>
2014-08-31 15:20:52 +08:00
end
scope(controller: 'quizzes/quizzes_api') do
get "courses/:course_id/quizzes", action: :index, as: 'course_quizzes'
post "courses/:course_id/quizzes", action: :create, as: 'course_quiz_create'
get "courses/:course_id/quizzes/:id", action: :show, as: 'course_quiz'
put "courses/:course_id/quizzes/:id", action: :update, as: 'course_quiz_update'
delete "courses/:course_id/quizzes/:id", action: :destroy, as: 'course_quiz_destroy'
post "courses/:course_id/quizzes/:id/reorder", action: :reorder, as: 'course_quiz_reorder'
end
scope(controller: 'quizzes/quiz_submission_users') do
get "courses/:course_id/quizzes/:id/submission_users", action: :index, as: 'course_quiz_submission_users'
post "courses/:course_id/quizzes/:id/submission_users/message", action: :message, as: 'course_quiz_submission_users_message'
end
scope(controller: 'quizzes/quiz_groups') do
post "courses/:course_id/quizzes/:quiz_id/groups", action: :create, as: 'course_quiz_group_create'
put "courses/:course_id/quizzes/:quiz_id/groups/:id", action: :update, as: 'course_quiz_group_update'
delete "courses/:course_id/quizzes/:quiz_id/groups/:id", action: :destroy, as: 'course_quiz_group_destroy'
post "courses/:course_id/quizzes/:quiz_id/groups/:id/reorder", action: :reorder, as: 'course_quiz_group_reorder'
end
scope(controller: 'quizzes/quiz_questions') do
get "courses/:course_id/quizzes/:quiz_id/questions", action: :index, as: 'course_quiz_questions'
get "courses/:course_id/quizzes/:quiz_id/questions/:id", action: :show, as: 'course_quiz_question'
post "courses/:course_id/quizzes/:quiz_id/questions", action: :create, as: 'course_quiz_question_create'
put "courses/:course_id/quizzes/:quiz_id/questions/:id", action: :update, as: 'course_quiz_question_update'
delete "courses/:course_id/quizzes/:quiz_id/questions/:id", action: :destroy, as: 'course_quiz_question_destroy'
end
scope(controller: 'quizzes/quiz_reports') do
post "courses/:course_id/quizzes/:quiz_id/reports", action: :create, as: 'course_quiz_reports_create'
Quiz Reports API - force regeneration Extends the quiz reports API with the ability to re-trigger failed CSV generation jobs, and to abort them completely. The UI is extended to utilize those new APIs. Closes CNVS-16525 TEST PLAN ---- ---- - create a quiz with a file upload question - take the quiz by a student and upload a file, then submit - turn on new stats and go to new stats page - click the "Student Analysis" report generator button and verify that the CSV file is generated and you get prompted to save it Now... we break the student's submission by removing the attachment and then the student analysis will start failing to generate. Launch a rails console and perform the following command: [ 'Quizzes::QuizSubmission', 'Quizzes::QuizStatistics' ].each do |type| Attachment.where({ context_type: type }).last.destroy! end - reload the stats page + the "Student Analysis" button should now read that it had never been generated, that's right because we just removed the CSV file attachment using the console + try generating the report again - verify that it blows up + you should now see a notification as in the screencast - clicking the "retry" link should retry generating the report (which will fail again) - clicking the "cancel" link should remove the notification - clicking "Dismiss" should dismiss the notification, but if you reload the page, it's still there Change-Id: I467a9030c3ef94d685ec20b31dd533e530e24758 Reviewed-on: https://gerrit.instructure.com/43862 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Trevor deHaan <tdehaan@instructure.com> Reviewed-by: Derek DeVries <ddevries@instructure.com> Product-Review: Derek DeVries <ddevries@instructure.com>
2014-10-29 00:11:34 +08:00
delete "courses/:course_id/quizzes/:quiz_id/reports/:id", action: :abort, as: 'course_quiz_reports_abort'
get "courses/:course_id/quizzes/:quiz_id/reports", action: :index, as: 'course_quiz_reports'
get "courses/:course_id/quizzes/:quiz_id/reports/:id", action: :show, as: 'course_quiz_report'
end
scope(controller: 'quizzes/quiz_submission_files') do
post 'courses/:course_id/quizzes/:quiz_id/submissions/self/files', action: :create, as: 'quiz_submission_files'
end
scope(controller: 'quizzes/quiz_submissions_api') do
get 'courses/:course_id/quizzes/:quiz_id/submissions', action: :index, as: 'course_quiz_submissions'
get 'courses/:course_id/quizzes/:quiz_id/submissions/:id', action: :show, as: 'course_quiz_submission'
get 'courses/:course_id/quizzes/:quiz_id/submissions/:id/time', action: :time, as: 'course_quiz_submission_time'
post 'courses/:course_id/quizzes/:quiz_id/submissions', action: :create, as: 'course_quiz_submission_create'
put 'courses/:course_id/quizzes/:quiz_id/submissions/:id', action: :update, as: 'course_quiz_submission_update'
post 'courses/:course_id/quizzes/:quiz_id/submissions/:id/complete', action: :complete, as: 'course_quiz_submission_complete'
end
scope(:controller => 'quizzes/outstanding_quiz_submissions') do
get 'courses/:course_id/quizzes/:quiz_id/outstanding_quiz_submissions', :action => :index, :path_name => 'outstanding_quiz_submission_index'
post 'courses/:course_id/quizzes/:quiz_id/outstanding_quiz_submissions', :action => :grade, :path_name => 'outstanding_quiz_submission_grade'
end
scope(controller: 'quizzes/quiz_extensions') do
post 'courses/:course_id/quizzes/:quiz_id/extensions', action: :create, as: 'course_quiz_extensions_create'
add api for quiz submission extensions quiz submissions can have their time limit or number of attempts extended. we can extend both existing quiz submissions, and also those that don't exist yet. adding this functionality to the existing quiz submissions api would muddle up responsibilities. So instead we post all extensions to a the quiz extensions api which is specifically meant for adding extensions to a submission whether it has been started yet or not. Also add 'manually_unlocked' to the quiz submissions api. this field lets us know if a student can take a quiz after it has been locked for everyone else. fixes CNVS-13165 test plan - There is a new attribute added to quiz_submissions objects in the api called 'manually_unlocked'. This attribute will now show up when returning results back from - GET /api/v1/courses/:course_id/quizzes/:quiz_id/submissions (index) - GET /api/v1/courses/:course_id/quizzes/:quiz_id/submissions/:id (show) - There is a new endpoint to create quiz extensions. This should work to create quiz extensions for users that both have existing quiz submissions started, and users who have not yet started a quiz: - POST /api/v1/courses/:course_id/quizzes/:quiz_id/extensions (create) - Check Permissions on the new quiz extension endpoint. Only teachers should be able to extend the quizzes. - Check that all the documentation looks okay for quiz extensions. Change-Id: Ie23113c1f30e139a1e376475fb35a2cf3ce0212c Reviewed-on: https://gerrit.instructure.com/35111 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Ahmad Amireh <ahmad@instructure.com> QA-Review: Caleb Guanzon <cguanzon@instructure.com> Product-Review: Derek DeVries <ddevries@instructure.com>
2014-05-20 04:04:00 +08:00
end
scope(controller: 'quizzes/course_quiz_extensions') do
post 'courses/:course_id/quiz_extensions', action: :create
end
scope(controller: "quizzes/quiz_submission_events_api") do
get "courses/:course_id/quizzes/:quiz_id/submissions/:id/events", action: :index, as: 'course_quiz_submission_events'
post "courses/:course_id/quizzes/:quiz_id/submissions/:id/events", action: :create, as: 'create_quiz_submission_events'
end
scope(controller: 'quizzes/quiz_submission_questions') do
get '/quiz_submissions/:quiz_submission_id/questions', action: :index, as: 'quiz_submission_questions'
post '/quiz_submissions/:quiz_submission_id/questions', action: :answer, as: 'quiz_submission_question_answer'
get '/quiz_submissions/:quiz_submission_id/questions/:id', action: :show, as: 'quiz_submission_question'
put '/quiz_submissions/:quiz_submission_id/questions/:id/flag', action: :flag, as: 'quiz_submission_question_flag'
put '/quiz_submissions/:quiz_submission_id/questions/:id/unflag', action: :unflag, as: 'quiz_submission_question_unflag'
Quiz Submission Questions API - Update This patch provides support for answering Quiz Questions via the API. closes CNVS-9844, CNVS-10225 TEST PLAN ---- ---- Testing this will be a bit rough because there are many variations and validations to cover. I'll spare the validations that are covered by specs from the test plan. Create a quiz with a question of *each* type except "Text" and "File Upload". There's a script that creates a quiz with its questions automatically for you if you don't want to keep doing this manually. See references. > Answering Questions Now you need to answer each question via the API. Most of them vary in formats, but they are fully specified in the API documentation page (along with examples). See DOCUMENTATION for more info. > Flagging Questions Flagging, and unflagging, a question is the same regardless of its type, see the "EXAMPLE REQUESTS" section. > Access Validations Here are some generic, non-question based validations to verify. You should NOT be able to answer a question if: - the quiz submission has been turned in - the quiz submission is overdue - the Access Code for the quiz is invalid - the IP filter of the Quiz prohibits you from taking the quiz - the quiz submission :validation_token is incorrectly specified (ie, other students shouldn't be able to answer your questions) - you don't specify the latest :attempt, so if the Quiz has multiple attempts, and this is your 2nd take, you specify an :attempt of 1, 3, or anything but 2 should fail - NEW: turn quiz into an OQAAT quiz with the "Can't go back" flag on; the API should not reject all requests to modify any of the questions with a 501 error saying that type of quizzes is not supported yet (support will come in CNVS-10224) > Grading Also, when you're done answering the questions, take a look at the grades and make sure everything gets graded just like it does when using the UI directly. > Verifying results in the browser While taking a quiz in the canvas UI, the scripts perform backups in the background that would overwrite any changes you do via the API. If you want to verify the changes you make via the API from the UI, you must append "?backup=false" to the take quiz page URL, something like this: http://localhost:3000/courses/1/quizzes/1/take?backup=false Setting that flag will (for now) disable the backup behaviour and should make things tick. EXAMPLE REQUESTS ------- -------- Don't forget to set the 'Content-Type' header to 'application/json'! > Answering a Multiple-Choice question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id { "attempt": 1, "validation_token": "1babd0...", "answer": 10 } > Flagging a question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id/flag { "attempt": 1, "validation_token": "1babd0..." } > Unflagging a question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id/unflag { "attempt": 1, "validation_token": "1babd0..." } DOCUMENTATION ------------- Run `bundle exec rake doc:api` and check out the Quiz Submission Questions page. There's an Appendix that contains example requests for each question type, as well as the errors produced by each handler. LINKS ----- - bootstrap script: https://gist.github.com/amireh/e7e8f835ffbf1d053e4c - direct link to the API documentation page: http://canvas.docs.kodoware.com/quiz_submission_questions.html Change-Id: I9a958323ece8854bc21a24c2affd8dc3972e46d5 Reviewed-on: https://gerrit.instructure.com/27206 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Derek DeVries <ddevries@instructure.com> QA-Review: Myller de Araujo <myller@instructure.com> Product-Review: Ahmad Amireh <ahmad@instructure.com>
2013-12-10 17:15:01 +08:00
end
scope(controller: 'quizzes/quiz_ip_filters') do
get 'courses/:course_id/quizzes/:quiz_id/ip_filters', action: :index, as: 'course_quiz_ip_filters'
Quiz IP Filters API controller - #index This patch moves rendering of IP filters from the QuizzesController into a separate, specialized API controller - QuizzesIpFilters. The rendered IP filter object is backwards-compatible, as in it provides the same three attributes: name, account, and filter. However, the endpoint output has been modified to scope the array of filters under "quiz_ip_filters". The front-end/JS part has been adjusted to accommodate this change. REFACTORING ----------- In an effort to reduce complexity, I've also taken to refactor all the :before_filters that were looking up Quiz in the current context across all Quiz controllers (where it was applicable), and that was done by introducing a new component set: API Helpers. API Helpers are modules that can be mixed-in, just like the API Modules, that are really just a place to hold routines common between the API and the regular controllers. I've put the first one in lib/api/v1/helpers, namespaced under Api::V1::Helpers... TEST PLAN ---- ---- - Create a Quiz. No questions. - Set an IP filter of 192.168.1.1 on the Quiz - the restriction should still hold like it usually does - Perform a GET request to this endpoint: /courses/:course_id/quizzes/:quiz_id/ip_filters - A list of filters should be returned in JSON-API format containing the filter you set for the Quiz - Add one or more IP Filters on the account-level, and: - Re-perform the GET query: - The list should contain both the Quiz and the Account filters - Using the IP Filter search box from the Quiz settings page: - It should still properly list the available filters refs CNVS-8988, CNVS-9586 Change-Id: I75c1b85024a58e6accd1627b7bee3da1185d2658 Reviewed-on: https://gerrit.instructure.com/26440 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Derek DeVries <ddevries@instructure.com> QA-Review: Myller de Araujo <myller@instructure.com> Product-Review: Ahmad Amireh <ahmad@instructure.com>
2013-11-19 17:06:49 +08:00
end
scope(controller: 'quizzes/quiz_statistics') do
get 'courses/:course_id/quizzes/:quiz_id/statistics', action: :index, as: 'course_quiz_statistics'
end
Quiz IP Filters API controller - #index This patch moves rendering of IP filters from the QuizzesController into a separate, specialized API controller - QuizzesIpFilters. The rendered IP filter object is backwards-compatible, as in it provides the same three attributes: name, account, and filter. However, the endpoint output has been modified to scope the array of filters under "quiz_ip_filters". The front-end/JS part has been adjusted to accommodate this change. REFACTORING ----------- In an effort to reduce complexity, I've also taken to refactor all the :before_filters that were looking up Quiz in the current context across all Quiz controllers (where it was applicable), and that was done by introducing a new component set: API Helpers. API Helpers are modules that can be mixed-in, just like the API Modules, that are really just a place to hold routines common between the API and the regular controllers. I've put the first one in lib/api/v1/helpers, namespaced under Api::V1::Helpers... TEST PLAN ---- ---- - Create a Quiz. No questions. - Set an IP filter of 192.168.1.1 on the Quiz - the restriction should still hold like it usually does - Perform a GET request to this endpoint: /courses/:course_id/quizzes/:quiz_id/ip_filters - A list of filters should be returned in JSON-API format containing the filter you set for the Quiz - Add one or more IP Filters on the account-level, and: - Re-perform the GET query: - The list should contain both the Quiz and the Account filters - Using the IP Filter search box from the Quiz settings page: - It should still properly list the available filters refs CNVS-8988, CNVS-9586 Change-Id: I75c1b85024a58e6accd1627b7bee3da1185d2658 Reviewed-on: https://gerrit.instructure.com/26440 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Derek DeVries <ddevries@instructure.com> QA-Review: Myller de Araujo <myller@instructure.com> Product-Review: Ahmad Amireh <ahmad@instructure.com>
2013-11-19 17:06:49 +08:00
scope(controller: 'polling/polls') do
get "polls", action: :index, as: 'polls'
post "polls", action: :create, as: 'poll_create'
get "polls/:id", action: :show, as: 'poll'
put "polls/:id", action: :update, as: 'poll_update'
delete "polls/:id", action: :destroy, as: 'poll_destroy'
end
scope(controller: 'polling/poll_choices') do
get "polls/:poll_id/poll_choices", action: :index, as: 'poll_choices'
post "polls/:poll_id/poll_choices", action: :create, as: 'poll_choices_create'
get "polls/:poll_id/poll_choices/:id", action: :show, as: 'poll_choice'
put "polls/:poll_id/poll_choices/:id", action: :update, as: 'poll_choice_update'
delete "polls/:poll_id/poll_choices/:id", action: :destroy, as: 'poll_choice_destroy'
add polling session and submission apis fixes CNVS-12474, CNVS-12477, CNVS-12478 this commit fleshes out the API endpoints for polling sessions and submissions, and solidifies the other polling endpoints. Test plan - Full tests on the following endpoints: - Polls are the basic data model of the polling app. They can take a question attribute and a description attribute. They are only creatable and modifiable by teachers. - GET /api/v1/polls (index action) - POST /api/v1/polls (create action) - GET /api/v1/polls/:id (show action) - PUT /api/v1/polls/:id (update action) - DELETE /api/v1/polls/:id (destroy action) - Poll choices belong to polls. They consist of the particular answers a submitter can choose when participating in a poll session. They have attributes of text (the answer text), their associated poll, and an 'is_correct' boolean attribute. The 'is_correct' attribute show not be accessible by students. Poll choices are only creatable and modifiable by the creator of the poll. - GET /api/v1/polls/:poll_id/poll_choices (index action) - POST /api/v1/polls/:poll_id/poll_choices (create action) - GET /api/v1/polls/:poll_id/poll_choices/:id (show action) - PUT /api/v1/polls/:poll_id/poll_choices/:id (update action) - DELETE /api/v1/polls/:poll_id/poll_choices/:id (destroy action) - Poll sessions are for publishing a poll so that it can accept submissions. They should only be createable / modifiable by teachers. Only students that are enrolled in the associated course of the poll session should be allowed to view them. The show action of a poll session acts differently for a teacher. They are able to see the results of the voting that has taken place by students since the session was published. - GET /api/v1/polls/:poll_id/poll_sessions (index action) - POST /api/v1/polls/:poll_id/poll_sessions (create action) - GET /api/v1/polls/:poll_id/poll_sessions/:id (show action) - PUT /api/v1/polls/:poll_id/poll_sessions/:id (update action) - DELETE /api/v1/polls/:poll_id/poll_sessions/:id (destroy action) - GET /api/v1/polls/:poll_id/poll_sessions/:id/publish (publish action) - GET /api/v1/polls/:poll_id/poll_sessions/:id/close (close action) - Poll submissions are for submitting an answer for a particular poll session. A student should only be allowed to submit a poll choice for a session they're able to view, and they can only submit one poll choice per poll session. - GET /api/v1/polls/:poll_id/poll_sessions/:poll_session_id/poll_submissions/:id (show action) - POST /api/v1/polls/:poll_id/poll_sessions/:poll_session_id/poll_submissions (create action) Change-Id: Ifcfd72ec30597e37fc54c687fb7d61a644d7348c Reviewed-on: https://gerrit.instructure.com/34605 Reviewed-by: Derek DeVries <ddevries@instructure.com> Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Caleb Guanzon <cguanzon@instructure.com> Product-Review: Josh Simpson <jsimpson@instructure.com>
2014-05-06 05:58:49 +08:00
end
scope(controller: 'polling/poll_sessions') do
get "polls/:poll_id/poll_sessions", action: :index, as: 'poll_sessions'
post "polls/:poll_id/poll_sessions", action: :create, as: 'poll_sessions_create'
get "polls/:poll_id/poll_sessions/:id", action: :show, as: 'poll_session'
put "polls/:poll_id/poll_sessions/:id", action: :update, as: 'poll_session_update'
delete "polls/:poll_id/poll_sessions/:id", action: :destroy, as: 'poll_session_destroy'
get "polls/:poll_id/poll_sessions/:id/open", action: :open, as: 'poll_session_publish'
get "polls/:poll_id/poll_sessions/:id/close", action: :close, as: 'poll_session_close'
get "poll_sessions/opened", action: :opened, as: 'poll_sessions_opened'
get "poll_sessions/closed", action: :closed, as: 'poll_sessions_closed'
add polling session and submission apis fixes CNVS-12474, CNVS-12477, CNVS-12478 this commit fleshes out the API endpoints for polling sessions and submissions, and solidifies the other polling endpoints. Test plan - Full tests on the following endpoints: - Polls are the basic data model of the polling app. They can take a question attribute and a description attribute. They are only creatable and modifiable by teachers. - GET /api/v1/polls (index action) - POST /api/v1/polls (create action) - GET /api/v1/polls/:id (show action) - PUT /api/v1/polls/:id (update action) - DELETE /api/v1/polls/:id (destroy action) - Poll choices belong to polls. They consist of the particular answers a submitter can choose when participating in a poll session. They have attributes of text (the answer text), their associated poll, and an 'is_correct' boolean attribute. The 'is_correct' attribute show not be accessible by students. Poll choices are only creatable and modifiable by the creator of the poll. - GET /api/v1/polls/:poll_id/poll_choices (index action) - POST /api/v1/polls/:poll_id/poll_choices (create action) - GET /api/v1/polls/:poll_id/poll_choices/:id (show action) - PUT /api/v1/polls/:poll_id/poll_choices/:id (update action) - DELETE /api/v1/polls/:poll_id/poll_choices/:id (destroy action) - Poll sessions are for publishing a poll so that it can accept submissions. They should only be createable / modifiable by teachers. Only students that are enrolled in the associated course of the poll session should be allowed to view them. The show action of a poll session acts differently for a teacher. They are able to see the results of the voting that has taken place by students since the session was published. - GET /api/v1/polls/:poll_id/poll_sessions (index action) - POST /api/v1/polls/:poll_id/poll_sessions (create action) - GET /api/v1/polls/:poll_id/poll_sessions/:id (show action) - PUT /api/v1/polls/:poll_id/poll_sessions/:id (update action) - DELETE /api/v1/polls/:poll_id/poll_sessions/:id (destroy action) - GET /api/v1/polls/:poll_id/poll_sessions/:id/publish (publish action) - GET /api/v1/polls/:poll_id/poll_sessions/:id/close (close action) - Poll submissions are for submitting an answer for a particular poll session. A student should only be allowed to submit a poll choice for a session they're able to view, and they can only submit one poll choice per poll session. - GET /api/v1/polls/:poll_id/poll_sessions/:poll_session_id/poll_submissions/:id (show action) - POST /api/v1/polls/:poll_id/poll_sessions/:poll_session_id/poll_submissions (create action) Change-Id: Ifcfd72ec30597e37fc54c687fb7d61a644d7348c Reviewed-on: https://gerrit.instructure.com/34605 Reviewed-by: Derek DeVries <ddevries@instructure.com> Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Caleb Guanzon <cguanzon@instructure.com> Product-Review: Josh Simpson <jsimpson@instructure.com>
2014-05-06 05:58:49 +08:00
end
scope(controller: 'polling/poll_submissions') do
post "polls/:poll_id/poll_sessions/:poll_session_id/poll_submissions", action: :create, as: 'poll_submissions_create'
get "polls/:poll_id/poll_sessions/:poll_session_id/poll_submissions/:id", action: :show, as: 'poll_submission'
end
scope(controller: 'live_assessments/assessments') do
%w(course).each do |context|
prefix = "#{context}s/:#{context}_id"
get "#{prefix}/live_assessments", action: :index, as: "#{context}_live_assessments"
post "#{prefix}/live_assessments", action: :create, as: "#{context}_live_assessment_create"
end
end
scope(controller: 'live_assessments/results') do
%w(course).each do |context|
prefix = "#{context}s/:#{context}_id"
get "#{prefix}/live_assessments/:assessment_id/results", action: :index, as: "#{context}_live_assessment_results"
post "#{prefix}/live_assessments/:assessment_id/results", action: :create, as: "#{context}_live_assessment_result_create"
end
end
scope(controller: :outcome_groups_api) do
def og_routes(context)
prefix = (context == "global" ? context : "#{context}s/:#{context}_id")
unless context == "global"
get "#{prefix}/outcome_groups", action: :index, as: "#{context}_outcome_groups"
get "#{prefix}/outcome_group_links", action: :link_index, as: "#{context}_outcome_group_links"
end
get "#{prefix}/root_outcome_group", action: :redirect, as: "#{context}_redirect"
get "#{prefix}/outcome_groups/account_chain", action: :account_chain, as: "#{context}_account_chain"
get "#{prefix}/outcome_groups/:id", action: :show, as: "#{context}_outcome_group"
put "#{prefix}/outcome_groups/:id", action: :update
delete "#{prefix}/outcome_groups/:id", action: :destroy
get "#{prefix}/outcome_groups/:id/outcomes", action: :outcomes, as: "#{context}_outcome_group_outcomes"
get "#{prefix}/outcome_groups/:id/available_outcomes", action: :available_outcomes, as: "#{context}_outcome_group_available_outcomes"
post "#{prefix}/outcome_groups/:id/outcomes", action: :link
put "#{prefix}/outcome_groups/:id/outcomes/:outcome_id", action: :link, as: "#{context}_outcome_link"
delete "#{prefix}/outcome_groups/:id/outcomes/:outcome_id", action: :unlink
get "#{prefix}/outcome_groups/:id/subgroups", action: :subgroups, as: "#{context}_outcome_group_subgroups"
post "#{prefix}/outcome_groups/:id/subgroups", action: :create
post "#{prefix}/outcome_groups/:id/import", action: :import, as: "#{context}_outcome_group_import"
post "#{prefix}/outcome_groups/:id/batch", action: :batch, as: "#{context}_outcome_group_batch"
end
og_routes('global')
og_routes('account')
og_routes('course')
end
scope(controller: :outcomes_api) do
get "outcomes/:id", action: :show, as: "outcome"
put "outcomes/:id", action: :update
delete "outcomes/:id", action: :destroy
end
scope(controller: :outcome_results) do
get 'courses/:course_id/outcome_rollups', action: :rollups, as: 'course_outcome_rollups'
get 'courses/:course_id/outcome_results', action: :index, as: 'course_outcome_results'
end
scope(controller: :group_categories) do
resources :group_categories, except: [:index, :create]
get 'accounts/:account_id/group_categories', action: :index, as: 'account_group_categories'
get 'courses/:course_id/group_categories', action: :index, as: 'course_group_categories'
post 'accounts/:account_id/group_categories', action: :create
post 'courses/:course_id/group_categories', action: :create
get 'group_categories/:group_category_id/groups', action: :groups, as: 'group_category_groups'
get 'group_categories/:group_category_id/users', action: :users, as: 'group_category_users'
post 'group_categories/:group_category_id/assign_unassigned_members', action: 'assign_unassigned_members', as: 'group_category_assign_unassigned_members'
end
scope(controller: :progress) do
get "progress/:id", action: :show, as: "progress"
end
scope(controller: :app_center) do
['course', 'account'].each do |context|
prefix = "#{context}s/:#{context}_id/app_center"
get "#{prefix}/apps", action: :index, as: "#{context}_app_center_apps"
get "#{prefix}/apps/:app_id/reviews", action: :reviews, as: "#{context}_app_center_app_reviews"
get "#{prefix}/apps/:app_id/reviews/self", action: :review, as: "#{context}_app_center_app_review"
post "#{prefix}/apps/:app_id/reviews/self", action: :add_review
end
end
feature flags infrastructure and API test plan: - install the test_features plugin (since no real features exist yet) - render and consult the feature flags documentation - have a test environment with a root account, sub-account, course in sub-account, and user - Use the "list features" endpoint as a root account admin (with no site admin privileges), on the root account context, and confirm that hidden features do not show up - Use the "list features" endpoint as a site admin user, on the root account context, and confirm that hidden features show up - Use the "list features" endpoint on the site admin account and confirm the hidden features show up - Use the "set feature flag" endpoint on a hidden feature on site admin and ensure the feature becomes visible in all root accounts - Use the "set feature flag endpoint" on a hidden feature on a single root account, and ensure the feature becomes visible to that root account and not others - Confirm that root_opt_in features appear "Off" by default in root accounts, after being "Allowed" in code or site admin - Confirm a feature flag that is set to "on" or "off" (vs. "allowed") cannot be overridden in a lower context (and the API returns locked=true for them) - Confirm that setting locking_account_id requires admin rights in the locking account - Confirm that a feature flag with locking_account_id cannot be changed without admin rights in the locking account (e.g., set a feature flag on a course, locked with the root account's id, and make sure a teacher who is not an account admin can't change it) - Confirm feature flags can be deleted with the "remove feature flag" endpoint (and they are only deleted where they are defined, not when called on an object that inherits a flag) Change-Id: I3e12e23b4454889b6e8b263f1315e82d8f2ada52 Reviewed-on: https://gerrit.instructure.com/25502 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Matt Fairbourn <mfairbourn@instructure.com> Product-Review: Matt Goodwin <mattg@instructure.com> Reviewed-by: Zach Pendleton <zachp@instructure.com>
2013-10-22 23:28:26 +08:00
scope(controller: :feature_flags) do
feature flags infrastructure and API test plan: - install the test_features plugin (since no real features exist yet) - render and consult the feature flags documentation - have a test environment with a root account, sub-account, course in sub-account, and user - Use the "list features" endpoint as a root account admin (with no site admin privileges), on the root account context, and confirm that hidden features do not show up - Use the "list features" endpoint as a site admin user, on the root account context, and confirm that hidden features show up - Use the "list features" endpoint on the site admin account and confirm the hidden features show up - Use the "set feature flag" endpoint on a hidden feature on site admin and ensure the feature becomes visible in all root accounts - Use the "set feature flag endpoint" on a hidden feature on a single root account, and ensure the feature becomes visible to that root account and not others - Confirm that root_opt_in features appear "Off" by default in root accounts, after being "Allowed" in code or site admin - Confirm a feature flag that is set to "on" or "off" (vs. "allowed") cannot be overridden in a lower context (and the API returns locked=true for them) - Confirm that setting locking_account_id requires admin rights in the locking account - Confirm that a feature flag with locking_account_id cannot be changed without admin rights in the locking account (e.g., set a feature flag on a course, locked with the root account's id, and make sure a teacher who is not an account admin can't change it) - Confirm feature flags can be deleted with the "remove feature flag" endpoint (and they are only deleted where they are defined, not when called on an object that inherits a flag) Change-Id: I3e12e23b4454889b6e8b263f1315e82d8f2ada52 Reviewed-on: https://gerrit.instructure.com/25502 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Matt Fairbourn <mfairbourn@instructure.com> Product-Review: Matt Goodwin <mattg@instructure.com> Reviewed-by: Zach Pendleton <zachp@instructure.com>
2013-10-22 23:28:26 +08:00
['course', 'account', 'user'].each do |context|
prefix = "#{context}s/:#{context}_id/features"
get "#{prefix}", action: :index, as: "#{context}_features"
get "#{prefix}/enabled", action: :enabled_features, as: "#{context}_enabled_features"
get "#{prefix}/flags/:feature", action: :show
put "#{prefix}/flags/:feature", action: :update
delete "#{prefix}/flags/:feature", action: :delete
feature flags infrastructure and API test plan: - install the test_features plugin (since no real features exist yet) - render and consult the feature flags documentation - have a test environment with a root account, sub-account, course in sub-account, and user - Use the "list features" endpoint as a root account admin (with no site admin privileges), on the root account context, and confirm that hidden features do not show up - Use the "list features" endpoint as a site admin user, on the root account context, and confirm that hidden features show up - Use the "list features" endpoint on the site admin account and confirm the hidden features show up - Use the "set feature flag" endpoint on a hidden feature on site admin and ensure the feature becomes visible in all root accounts - Use the "set feature flag endpoint" on a hidden feature on a single root account, and ensure the feature becomes visible to that root account and not others - Confirm that root_opt_in features appear "Off" by default in root accounts, after being "Allowed" in code or site admin - Confirm a feature flag that is set to "on" or "off" (vs. "allowed") cannot be overridden in a lower context (and the API returns locked=true for them) - Confirm that setting locking_account_id requires admin rights in the locking account - Confirm that a feature flag with locking_account_id cannot be changed without admin rights in the locking account (e.g., set a feature flag on a course, locked with the root account's id, and make sure a teacher who is not an account admin can't change it) - Confirm feature flags can be deleted with the "remove feature flag" endpoint (and they are only deleted where they are defined, not when called on an object that inherits a flag) Change-Id: I3e12e23b4454889b6e8b263f1315e82d8f2ada52 Reviewed-on: https://gerrit.instructure.com/25502 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Matt Fairbourn <mfairbourn@instructure.com> Product-Review: Matt Goodwin <mattg@instructure.com> Reviewed-by: Zach Pendleton <zachp@instructure.com>
2013-10-22 23:28:26 +08:00
end
end
scope(controller: :conferences) do
%w(course group).each do |context|
prefix = "#{context}s/:#{context}_id/conferences"
get prefix, action: :index, as: "#{context}_conferences"
post "#{prefix}/:conference_id/recording_ready", action: :recording_ready, as: "#{context}_conferences_recording_ready"
end
end
scope(controller: :custom_gradebook_columns_api) do
prefix = "courses/:course_id/custom_gradebook_columns"
get prefix, action: :index, as: "course_custom_gradebook_columns"
post prefix, action: :create
post "#{prefix}/reorder", action: :reorder,
as: "custom_gradebook_columns_reorder"
put "#{prefix}/:id", action: :update,
as: "course_custom_gradebook_column"
delete "#{prefix}/:id", action: :destroy
end
scope(controller: :custom_gradebook_column_data_api) do
prefix = "courses/:course_id/custom_gradebook_columns/:id/data"
get prefix, action: :index, as: "course_custom_gradebook_column_data"
put "#{prefix}/:user_id", action: :update, as: "course_custom_gradebook_column_datum"
end
scope(controller: :content_exports_api) do
zip content exports for course, group, user test plan: 1. use the content exports api with export_type=zip to export files from courses, groups, and users a. confirm only users who have permission to download files from these contexts can perform the export b. confirm that deleted files and folders do not show up in the downloaded archive c. confirm that students cannot download locked files or folders from courses this way d. check the progress endpoint and make sure it increments sanely 2. perform selective content exports by passing an array of ids in select[folders] and/or select[attachments]. for example, ?select[folders][]=123&select[folders][]=456 ?select[attachments][]=345 etc. a. any selected files, plus the full contents of any selected folders (that the caller has permission to see) should be included - that means locked files and subfolders should be excluded from the archive b. if all selected files and folders are descendants of the same subfolder X, the export should be named "X_export.zip" and all paths inside the zip should be relative to it. for example, if you are exporting A/B/1 and A/C/2, you should get "A_export.zip" containing files "B/1" and "C/2". 3. use the index and show endpoints to list and view content exports in courses, groups, and users a. confirm students cannot view non-zip course exports (such as common cartridge exports) b. confirm students cannot view other users' file (zip) exports, in course, group, and user context c. confirm teachers cannot view other users' file (zip) exports, in course, group, and user context (but can still view course [cc] exports initiated by other teachers) 4. look at /courses/X/content_exports (web, not API) a. confirm teachers see file exports they performed b. confirm teachers do not see file exports performed by other teachers c. confirm teachers see all non-zip course exports (cc/qti) including those initiated by other teachers 5. as a site admin user, perform a zip export of another user's files. then, as that other user, go to /dashboard/data_exports and confirm that the export performed by the site admin user is not shown. fixes CNVS-12706 Change-Id: Ie9b58e44ac8006a9c9171b3ed23454bf135385b0 Reviewed-on: https://gerrit.instructure.com/34341 Reviewed-by: James Williams <jamesw@instructure.com> QA-Review: Trevor deHaan <tdehaan@instructure.com> Tested-by: Jenkins <jenkins@instructure.com> Product-Review: Jon Willesen <jonw@instructure.com>
2014-07-18 04:00:32 +08:00
%w(course group user).each do |context|
context_prefix = "#{context.pluralize}/:#{context}_id"
prefix = "#{context_prefix}/content_exports"
get prefix, action: :index, as: "#{context}_content_exports"
post prefix, action: :create
get "#{prefix}/:id", action: :show
zip content exports for course, group, user test plan: 1. use the content exports api with export_type=zip to export files from courses, groups, and users a. confirm only users who have permission to download files from these contexts can perform the export b. confirm that deleted files and folders do not show up in the downloaded archive c. confirm that students cannot download locked files or folders from courses this way d. check the progress endpoint and make sure it increments sanely 2. perform selective content exports by passing an array of ids in select[folders] and/or select[attachments]. for example, ?select[folders][]=123&select[folders][]=456 ?select[attachments][]=345 etc. a. any selected files, plus the full contents of any selected folders (that the caller has permission to see) should be included - that means locked files and subfolders should be excluded from the archive b. if all selected files and folders are descendants of the same subfolder X, the export should be named "X_export.zip" and all paths inside the zip should be relative to it. for example, if you are exporting A/B/1 and A/C/2, you should get "A_export.zip" containing files "B/1" and "C/2". 3. use the index and show endpoints to list and view content exports in courses, groups, and users a. confirm students cannot view non-zip course exports (such as common cartridge exports) b. confirm students cannot view other users' file (zip) exports, in course, group, and user context c. confirm teachers cannot view other users' file (zip) exports, in course, group, and user context (but can still view course [cc] exports initiated by other teachers) 4. look at /courses/X/content_exports (web, not API) a. confirm teachers see file exports they performed b. confirm teachers do not see file exports performed by other teachers c. confirm teachers see all non-zip course exports (cc/qti) including those initiated by other teachers 5. as a site admin user, perform a zip export of another user's files. then, as that other user, go to /dashboard/data_exports and confirm that the export performed by the site admin user is not shown. fixes CNVS-12706 Change-Id: Ie9b58e44ac8006a9c9171b3ed23454bf135385b0 Reviewed-on: https://gerrit.instructure.com/34341 Reviewed-by: James Williams <jamesw@instructure.com> QA-Review: Trevor deHaan <tdehaan@instructure.com> Tested-by: Jenkins <jenkins@instructure.com> Product-Review: Jon Willesen <jonw@instructure.com>
2014-07-18 04:00:32 +08:00
end
get "courses/:course_id/content_list", action: :content_list, as: "course_content_list"
end
scope(controller: :grading_standards_api) do
post 'accounts/:account_id/grading_standards', action: :create
post 'courses/:course_id/grading_standards', action: :create
end
get '/crocodoc_session', controller: 'crocodoc_sessions', action: 'show', as: :crocodoc_session
get '/canvadoc_session', controller: 'canvadoc_sessions', action: 'show', as: :canvadoc_session
scope(controller: :grading_periods) do
%w(course account).each do |context|
content_prefix = "#{context.pluralize}/:#{context}_id"
prefix = "#{content_prefix}/grading_periods"
get prefix, action: :index, as: "#{context}_grading_periods"
get "#{prefix}/:id", action: :show, as: "#{context}_grading_period"
post prefix, action: :create, as: "#{context}_grading_period_create"
put "#{prefix}/:id", action: :update, as: "#{context}_grading_period_update"
delete "#{prefix}/:id", action: :destroy, as: "#{context}_grading_period_destroy"
end
end
scope(controller: :usage_rights) do
%w(course group user).each do |context|
content_prefix = "#{context.pluralize}/:#{context}_id"
put "#{content_prefix}/usage_rights", action: :set_usage_rights
delete "#{content_prefix}/usage_rights", action: :remove_usage_rights
get "#{content_prefix}/content_licenses", action: :licenses
end
end
scope(controller: 'bookmarks/bookmarks') do
get 'users/self/bookmarks/', action: :index, as: :bookmarks
get 'users/self/bookmarks/:id', action: :show
post 'users/self/bookmarks', action: :create
delete 'users/self/bookmarks/:id', action: :destroy
put 'users/self/bookmarks/:id', action: :update
end
scope(controller: :errors) do
post "error_reports", action: :create
end
2011-02-01 09:57:29 +08:00
end
# this is not a "normal" api endpoint in the sense that it is not documented
# or called directly, it's used as the redirect in the file upload process
# for local files. it also doesn't use the normal oauth authentication
# system, so we can't put it in the api uri namespace.
post 'files_api' => 'files#api_create', as: :api_v1_files_create
get 'login/oauth2/auth' => 'oauth2_provider#auth', as: :oauth2_auth
post 'login/oauth2/token' => 'oauth2_provider#token', as: :oauth2_token
get 'login/oauth2/confirm' => 'oauth2_provider#confirm', as: :oauth2_auth_confirm
post 'login/oauth2/accept' => 'oauth2_provider#accept', as: :oauth2_auth_accept
get 'login/oauth2/deny' => 'oauth2_provider#deny', as: :oauth2_auth_deny
delete 'login/oauth2/token' => 'oauth2_provider#destroy', as: :oauth2_logout
ApiRouteSet.draw(self, "/api/lti/v1") do
post "tools/:tool_id/grade_passback", controller: :lti_api, action: :grade_passback, as: "lti_grade_passback_api"
post "tools/:tool_id/ext_grade_passback", controller: :lti_api, action: :legacy_grade_passback, as: "blti_legacy_grade_passback_api"
post "xapi/:token", controller: :lti_api, action: :xapi_service, as: "lti_xapi"
post "caliper/:token", controller: :lti_api, action: :caliper_service, as: "lti_caliper"
post "logout_service/:token", controller: :lti_api, action: :logout_service, as: "lti_logout_service"
end
ApiRouteSet.draw(self, "/api/lti") do
['course', 'account'].each do |context|
prefix = "#{context}s/:#{context}_id"
get "#{prefix}/tool_consumer_profile/:tool_consumer_profile_id", controller: 'lti/ims/tool_consumer_profile', action: 'show', as: "#{context}_tool_consumer_profile"
post "#{prefix}/tool_proxy", controller: 'lti/ims/tool_proxy', action: :create, as: "create_#{context}_lti_tool_proxy"
end
#Tool Setting Services
get "tool_settings/:tool_setting_id", controller: 'lti/ims/tool_setting', action: :show, as: 'show_lti_tool_settings'
put "tool_settings/:tool_setting_id", controller: 'lti/ims/tool_setting', action: :update, as: 'update_lti_tool_settings'
#Tool Proxy Services
get "tool_proxy/:tool_proxy_guid", controller: 'lti/ims/tool_proxy', action: :show, as: "show_lti_tool_proxy"
end
2011-02-01 09:57:29 +08:00
end