2011-02-01 09:57:29 +08:00
#
# Copyright (C) 2011 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
2012-07-07 03:45:00 +08:00
# @API Files
# An API for managing files and folders
# See the File Upload Documentation for details on the file upload workflow.
#
# @object File
# {
2013-08-28 07:05:23 +08:00
# "size": 4,
# "content-type": "text/plain",
# "url": "http://www.example.com/files/569/download?download_frd=1\u0026verifier=c6HdZmxOZa0Fiin2cbvZeI8I5ry7yqD7RChQzb6P",
# "id": 569,
# "display_name": "file.txt",
# "created_at": "2012-07-06T14:58:50Z",
# "updated_at": "2012-07-06T14:58:50Z",
# "unlock_at": null,
# "locked": false,
# "hidden": false,
# "lock_at": null,
# "locked_for_user": false,
# "lock_info": {
# "asset_string": "file_569",
# "unlock_at": "2013-01-01T00:00:00-06:00",
# "lock_at": "2013-02-01T00:00:00-06:00",
# "context_module": {},
# "manually_locked": true
2013-06-05 07:51:27 +08:00
# },
2013-08-28 07:05:23 +08:00
# "lock_explanation": "This assignment is locked until September 1 at 12:00am",
# "hidden_for_user": false,
# "thumbnail_url": null
2012-07-07 03:45:00 +08:00
# }
2011-02-01 09:57:29 +08:00
class FilesController < ApplicationController
2012-03-31 05:20:30 +08:00
before_filter :require_user , :only = > :create_pending
2013-03-13 04:18:34 +08:00
before_filter :require_context , :except = > [ :full_index , :assessment_question_show , :image_thumbnail , :show_thumbnail , :preflight , :create_pending , :s3_success , :show , :api_create , :api_create_success , :api_show , :api_index , :destroy , :api_update , :api_file_status ]
2011-05-10 05:20:58 +08:00
before_filter :check_file_access_flags , :only = > [ :show_relative , :show ]
2011-03-08 05:02:56 +08:00
prepend_around_filter :load_pseudonym_from_policy , :only = > :create
2012-03-21 06:08:20 +08:00
skip_before_filter :verify_authenticity_token , :only = > :api_create
include Api :: V1 :: Attachment
2012-03-28 06:53:55 +08:00
include Api :: V1 :: Avatar
2011-02-01 09:57:29 +08:00
before_filter { | c | c . active_tab = " files " }
def quota
get_quota
if authorized_action ( @context . attachments . new , @current_user , :create )
h = ActionView :: Base . new
h . extend ActionView :: Helpers :: NumberHelper
result = {
:quota = > h . number_to_human_size ( @quota ) ,
:quota_used = > h . number_to_human_size ( @quota_used ) ,
:quota_full = > ( @quota_used > = @quota )
}
render :json = > result . to_json
end
end
2012-12-05 06:22:59 +08:00
2011-02-01 09:57:29 +08:00
def check_file_access_flags
2011-05-14 23:04:35 +08:00
if params [ :user_id ] && params [ :ts ] && params [ :sf_verifier ]
2011-04-27 11:55:24 +08:00
user = User . find_by_id ( params [ :user_id ] ) if params [ :user_id ] . present?
2011-05-14 23:04:35 +08:00
if user && user . valid_access_verifier? ( params [ :ts ] , params [ :sf_verifier ] )
2011-02-01 09:57:29 +08:00
# attachment.rb checks for this session attribute when determining
# permissions, but it should be ignored by the rest of the models'
# permission checks
session [ 'file_access_user_id' ] = user . id
session [ 'file_access_expiration' ] = 1 . hour . from_now . to_i
2011-08-25 03:40:15 +08:00
session [ :session_affects_permissions ] = true
2011-02-01 09:57:29 +08:00
end
end
# These sessions won't get deleted when the user logs out since this
# is on a separate domain, so we've added our own (stricter) timeout.
if session && session [ 'file_access_user_id' ] && session [ 'file_access_expiration' ] . to_i > Time . now . to_i
session [ 'file_access_expiration' ] = 1 . hour . from_now . to_i
end
true
end
protected :check_file_access_flags
2012-12-05 06:22:59 +08:00
2011-02-01 09:57:29 +08:00
def index
2011-03-02 03:17:42 +08:00
if request . format == :json
2013-01-16 05:59:18 +08:00
if authorized_action ( @context . attachments . build , @current_user , :read )
2011-03-02 03:17:42 +08:00
@current_folder = Folder . find_folder ( @context , params [ :folder_id ] )
if ! @current_folder || authorized_action ( @current_folder , @current_user , :read )
2011-02-01 09:57:29 +08:00
if params [ :folder_id ]
2011-03-02 03:17:42 +08:00
if @context . grants_right? ( @current_user , session , :manage_files )
2012-07-07 03:45:00 +08:00
@current_attachments = @current_folder . active_file_attachments . by_position_then_display_name
2011-03-02 03:17:42 +08:00
else
2012-07-07 03:45:00 +08:00
@current_attachments = @current_folder . visible_file_attachments . by_position_then_display_name
2011-03-02 03:17:42 +08:00
end
2013-03-08 07:49:24 +08:00
@current_attachments = @current_attachments . includes ( :thumbnail , :media_object )
optimize FoldersController#show
Don't make thumbnail_url automatically included with every
serialization of Attachment, since it requires loading Thumbnail, and
Context. Instead specifically include it where it is actually needed.
Then be very specific about what fields show up in
FoldersController#show, since we're returning a bunch of items, this
improves both render time, transfer time, and is also less data for
the javascript to manage.
Also fix FoldersController#index. It obviously wasn't used, since it
was very broken. I just fixed it because it was the easiest way for
me to find a folder id for a course to test #show.
With this and prior optimization, FoldersController#show for a a
folder with ~3000 items went from 10 minutes (!) to < 10 seconds.
Not perfect, but a huge improvemnt, and that many items in a single
folder is definitely an outlier. "Normal" folders are now quite
snappy.
Refs #3975
Change-Id: I2952ba886e2bc8ac7d8b230105caa508084841f1
Reviewed-on: https://gerrit.instructure.com/4646
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Brian Palmer <brianp@instructure.com>
2011-07-12 06:38:23 +08:00
render :json = > @current_attachments . to_json ( :methods = > [ :readable_size , :currently_locked , :thumbnail_url ] , :permissions = > { :user = > @current_user , :session = > session } )
2011-02-01 09:57:29 +08:00
else
2011-03-02 03:17:42 +08:00
render :json = > @context . file_structure_for ( @current_user ) . to_json ( :permissions = > { :user = > @current_user } , :methods = > [ :readable_size , :mime_class , :currently_locked , :collaborator_ids ] )
end
2011-02-01 09:57:29 +08:00
end
end
2011-03-02 03:17:42 +08:00
else
full_index
2011-02-01 09:57:29 +08:00
end
end
2011-07-12 02:25:54 +08:00
2012-07-07 03:45:00 +08:00
# @API List files
2013-07-30 03:57:27 +08:00
# Returns the paginated list of files for the folder or course.
2012-07-07 03:45:00 +08:00
#
2013-08-14 23:06:27 +08:00
# @argument content_types[] [Optional, String]
# Filter results by content-type. You can specify type/subtype pairs (e.g.,
# 'image/jpeg'), or simply types (e.g., 'image', which will match
# 'image/gif', 'image/jpeg', etc.).
#
# @argument search_term [Optional, String]
# The partial name of the files to match and return.
2013-06-27 04:57:14 +08:00
#
2012-07-07 03:45:00 +08:00
# @example_request
#
2013-06-27 04:57:14 +08:00
# curl 'https://<canvas>/api/v1/folders/<folder_id>/files?content_types[]=image&content_types[]=text/plain \
2012-07-07 03:45:00 +08:00
# -H 'Authorization: Bearer <token>'
#
# @returns [File]
def api_index
2013-07-30 03:57:27 +08:00
get_context
if @context
folder = Folder . root_folders ( @context ) . first
raise ActiveRecord :: RecordNotFound unless folder
context_index = true
else
folder = Folder . find ( params [ :id ] )
end
2012-07-07 03:45:00 +08:00
if authorized_action ( folder , @current_user , :read_contents )
2013-07-30 03:57:27 +08:00
@context = folder . context unless context_index
2013-01-03 06:48:52 +08:00
can_manage_files = @context . grants_right? ( @current_user , session , :manage_files )
2013-07-30 03:57:27 +08:00
if context_index
if can_manage_files
scope = @context . attachments . not_deleted
else
scope = @context . attachments . visible . not_hidden . not_locked . where (
:folder_id = > @context . active_folders . not_hidden . not_locked )
end
2012-07-07 03:45:00 +08:00
else
2013-07-30 03:57:27 +08:00
if can_manage_files
scope = folder . active_file_attachments
else
scope = folder . visible_file_attachments . not_hidden . not_locked
end
2012-07-07 03:45:00 +08:00
end
2013-07-30 03:57:27 +08:00
scope = Attachment . search_by_attribute ( scope , :display_name , params [ :search_term ] )
2012-07-07 03:45:00 +08:00
if params [ :sort_by ] == 'position'
scope = scope . by_position_then_display_name
else
scope = scope . by_display_name
end
2013-06-27 04:57:14 +08:00
if params [ :content_types ] . present?
scope = scope . by_content_types ( Array ( params [ :content_types ] ) )
end
2013-07-30 03:57:27 +08:00
url = context_index ? context_files_url : api_v1_list_files_url ( folder )
@files = Api . paginate ( scope , self , url )
2012-07-27 00:13:22 +08:00
render :json = > attachments_json ( @files , @current_user , { } , :can_manage_files = > can_manage_files )
2012-07-07 03:45:00 +08:00
end
end
2011-07-12 02:25:54 +08:00
def images
if authorized_action ( @context . attachments . new , @current_user , :read )
2013-01-03 06:48:52 +08:00
if Folder . root_folders ( @context ) . first . grants_right? ( @current_user , session , :read_contents )
if @context . grants_right? ( @current_user , session , :manage_files )
@images = @context . active_images . paginate :page = > params [ :page ]
else
@images = @context . active_images . not_hidden . not_locked . where ( :folder_id = > @context . active_folders . not_hidden . not_locked ) . paginate :page = > params [ :page ]
end
else
@images = [ ] . paginate
end
2011-07-12 02:25:54 +08:00
headers [ 'X-Total-Pages' ] = @images . total_pages . to_s
render :partial = > " shared/wiki_image " , :collection = > @images
2011-02-01 09:57:29 +08:00
end
end
def full_index
get_context
get_quota
2011-08-05 02:09:00 +08:00
add_crumb ( t ( '#crumbs.files' , " Files " ) , named_context_url ( @context , :context_files_url ) )
2011-02-01 09:57:29 +08:00
@contexts = [ @context ]
if ! @context . is_a? ( User ) || ( @context == @current_user && params [ :show_all_contexts ] )
2011-09-28 07:43:58 +08:00
get_all_pertinent_contexts ( true )
2011-02-01 09:57:29 +08:00
end
@too_many_contexts = @contexts . length > 15
@contexts = @contexts [ 0 , 15 ]
2013-01-16 05:59:18 +08:00
if @contexts . length < = 1 && ! authorized_action ( @context . attachments . build , @current_user , :read )
2011-02-01 09:57:29 +08:00
return
end
2013-01-16 05:59:18 +08:00
2011-02-01 09:57:29 +08:00
return unless tab_enabled? ( @context . class :: TAB_FILES )
2011-03-02 03:17:42 +08:00
log_asset_access ( " files: #{ @context . asset_string } " , " files " , 'other' ) if @context
2011-02-01 09:57:29 +08:00
respond_to do | format |
2011-03-02 03:17:42 +08:00
if @contexts . empty?
2011-02-01 09:57:29 +08:00
format . html { redirect_to ! @context || @context == @current_user ? dashboard_url : named_context_url ( @context , :context_url ) }
else
format . html { render :action = > 'full_index' }
end
format . json { render :json = > @file_structures . to_json }
end
end
2012-12-05 06:22:59 +08:00
2011-02-01 09:57:29 +08:00
def text_show
@attachment = @context . attachments . find ( params [ :file_id ] )
if authorized_action ( @attachment , @current_user , :read )
if @attachment . grants_right? ( @current_user , nil , :download )
@headers = false
render
else
show
end
end
end
2012-12-05 06:22:59 +08:00
2011-02-01 09:57:29 +08:00
def assessment_question_show
@context = AssessmentQuestion . find ( params [ :assessment_question_id ] )
@attachment = @context . attachments . find ( params [ :id ] )
@skip_crumb = true
if @attachment . deleted?
2011-06-16 00:41:22 +08:00
flash [ :notice ] = t 'notices.deleted' , " The file %{display_name} has been deleted " , :display_name = > @attachment . display_name
2011-02-01 09:57:29 +08:00
redirect_to dashboard_url
end
show
end
2012-12-05 06:22:59 +08:00
2011-03-09 03:12:38 +08:00
# this is used for the google docs preview of a document
def public_url
respond_to do | format |
format . json do
@attachment = Attachment . find ( params [ :id ] )
# if the attachment is part of a submisison, its 'context' will be the student that submmited the assignment. so if @current_user is a
# teacher authorized_action(@attachment, @current_user, :download) will be false, we need to actually check if they have perms to see the
# submission.
2013-07-17 01:49:32 +08:00
@submission = Submission . find ( params [ :submission_id ] ) if params [ :submission_id ]
# verify that the requested attachment belongs to the submission
return render_unauthorized_action ( @attachment ) if @submission && ! @submission . attachments . where ( :id = > params [ :id ] ) . any?
2011-03-09 03:12:38 +08:00
if @submission ? authorized_action ( @submission , @current_user , :read ) : authorized_action ( @attachment , @current_user , :download )
2013-03-08 03:28:42 +08:00
render :json = > { :public_url = > @attachment . authenticated_s3_url ( :secure = > request . ssl? ) }
2011-03-09 03:12:38 +08:00
end
end
end
end
2012-07-07 03:45:00 +08:00
# @API Get file
# Returns the standard attachment json object
#
#
# @example_request
#
2012-07-17 22:36:56 +08:00
# curl 'https://<canvas>/api/v1/files/<file_id>' \
2012-07-07 03:45:00 +08:00
# -H 'Authorization: Bearer <token>'
#
# @returns File
def api_show
@attachment = Attachment . find ( params [ :id ] )
raise ActiveRecord :: RecordNotFound if @attachment . deleted?
if authorized_action ( @attachment , @current_user , :read )
2012-07-27 00:13:22 +08:00
render :json = > attachment_json ( @attachment , @current_user )
2012-07-07 03:45:00 +08:00
end
end
2012-12-05 06:22:59 +08:00
2011-02-01 09:57:29 +08:00
def show
2011-08-11 04:41:55 +08:00
original_params = params . dup
2011-02-01 09:57:29 +08:00
params [ :id ] || = params [ :file_id ]
2011-05-10 05:20:58 +08:00
get_context
2011-10-21 06:17:33 +08:00
# note that the /files/XXX URL implicitly uses the current user as the
# context, even though it doesn't search for the file using
# @current_user.attachments.find , since it might not actually be a user
# attachment.
# this implicit context magic happens in ApplicationController#get_context
2011-05-10 05:20:58 +08:00
if @context && ! @context . is_a? ( User )
@attachment = @context . attachments . find ( params [ :id ] )
else
@attachment = Attachment . find ( params [ :id ] )
2011-10-21 06:17:33 +08:00
@skip_crumb = true unless @context
2011-05-10 05:20:58 +08:00
end
2011-02-01 09:57:29 +08:00
params [ :download ] || = params [ :preview ]
2011-06-16 00:41:22 +08:00
add_crumb ( t ( '#crumbs.files' , " Files " ) , named_context_url ( @context , :context_files_url ) ) unless @skip_crumb
2011-02-01 09:57:29 +08:00
if @attachment . deleted?
2011-06-16 00:41:22 +08:00
flash [ :notice ] = t 'notices.deleted' , " The file %{display_name} has been deleted " , :display_name = > @attachment . display_name
2011-02-01 09:57:29 +08:00
if params [ :preview ] && @attachment . mime_class == 'image'
redirect_to '/images/blank.png'
elsif request . format == :json
render :json = > { :deleted = > true } . to_json
else
redirect_to named_context_url ( @context , :context_files_url )
end
return
end
2013-07-17 01:49:32 +08:00
if ( params [ :download ] && params [ :verifier ] && params [ :verifier ] == @attachment . uuid ) ||
@attachment . attachment_associations . where ( :context_type = > 'Submission' ) . any? { | aa | aa . context . grants_right? ( @current_user , session , :read ) } ||
authorized_action ( @attachment , @current_user , :read )
2011-02-01 09:57:29 +08:00
if params [ :download ]
if ( params [ :verifier ] && params [ :verifier ] == @attachment . uuid ) || ( @attachment . grants_right? ( @current_user , session , :download ) )
disable_page_views if params [ :preview ]
begin
2011-03-15 03:51:44 +08:00
send_attachment ( @attachment )
2011-02-01 09:57:29 +08:00
rescue = > e
2011-03-15 03:51:44 +08:00
@headers = false if params [ :ts ] && params [ :verifier ]
2011-06-16 00:41:22 +08:00
@not_found_message = t 'errors.not_found' , " It looks like something went wrong when this file was uploaded, and we can't find the actual file. You may want to notify the owner of the file and have them re-upload it. "
2011-02-01 09:57:29 +08:00
logger . error " Error downloading a file: #{ e } - #{ e . backtrace } "
render :template = > 'shared/errors/404_message' , :status = > :bad_request
end
return
elsif authorized_action ( @attachment , @current_user , :read )
2011-03-15 03:51:44 +08:00
render_attachment ( @attachment )
2011-02-01 09:57:29 +08:00
end
2011-03-15 03:51:44 +08:00
# This action is a callback used in our system to help record when
# a user views an inline preview of a file instead of downloading
# it, since this should also count as an access.
2011-02-01 09:57:29 +08:00
elsif params [ :inline ]
generate_new_page_view
2011-03-15 03:51:44 +08:00
@attachment . context_module_action ( @current_user , :read ) if @current_user
2011-02-01 09:57:29 +08:00
log_asset_access ( @attachment , 'files' , 'files' )
2013-05-22 03:02:44 +08:00
@attachment . record_inline_view
2011-02-01 09:57:29 +08:00
render :json = > { :ok = > true } . to_json
else
2011-03-15 03:51:44 +08:00
render_attachment ( @attachment )
end
end
end
2012-12-05 06:22:59 +08:00
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
def scribd_render
# ApplicationController#get_context doesn't support Assignment
if @context . is_a? ( User ) && params [ :assignment_id ] . present?
@context = Assignment . find ( params [ :assignment_id ] )
end
@attachment = @context . attachments . find ( params [ :file_id ] )
if @attachment . attachment_associations . where ( :context_type = > 'Submission' ) . any? { | aa | aa . context . grants_right? ( @current_user , session , :read ) } || authorized_action ( @attachment , @current_user , :read )
@attachment . check_rerender_scribd_doc
render :json = > { :ok = > true }
end
end
2011-03-15 03:51:44 +08:00
def render_attachment ( attachment )
respond_to do | format |
if params [ :preview ] && attachment . mime_class == 'image'
format . html { redirect_to '/images/lock.png' }
else
if @files_domain
@headers = false
@show_left_side = false
2011-02-01 09:57:29 +08:00
end
2011-03-15 03:51:44 +08:00
format . html { render :action = > 'show' }
end
if request . format == :json
2011-03-09 03:12:38 +08:00
options = { :permissions = > { :user = > @current_user } }
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
if attachment . grants_right? ( @current_user , session , :download )
2011-03-09 03:12:38 +08:00
# Right now we assume if they ask for json data on the attachment
# which includes the scribd doc data, then that means they have
# viewed or are about to view the file in some form.
2013-05-22 03:02:44 +08:00
if @current_user && ( ( feature_enabled? ( :scribd ) && attachment . scribd_doc ) ||
( service_enabled? ( :google_docs_previews ) && attachment . authenticated_s3_url ) )
attachment . context_module_action ( @current_user , :read )
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
attachment . record_inline_view
2013-05-22 03:02:44 +08:00
end
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
options [ :methods ] = [ ]
options [ :methods ] << :authenticated_s3_url if service_enabled? ( :google_docs_previews ) && attachment . authenticated_s3_url
options [ :methods ] << :scribd_render_url if attachment . scribd_doc_missing?
log_asset_access ( attachment , " files " , " files " )
2011-03-09 03:12:38 +08:00
end
2011-02-01 09:57:29 +08:00
end
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
format . json { render :json = > attachment . to_json ( options ) }
2011-02-01 09:57:29 +08:00
end
end
2011-03-15 03:51:44 +08:00
protected :render_attachment
2011-02-01 09:57:29 +08:00
def show_relative
path = params [ :file_path ]
2013-06-18 06:08:36 +08:00
file_id = params [ :file_id ]
file_id = nil unless file_id . to_s =~ Api :: ID_REGEX
2011-02-01 09:57:29 +08:00
#if the relative path matches the given file id use that file
2013-06-18 06:08:36 +08:00
if file_id && @attachment = @context . attachments . find_by_id ( file_id )
unless @attachment . matches_full_display_path? ( path ) || @attachment . matches_full_path? ( path )
2011-02-01 09:57:29 +08:00
@attachment = nil
end
end
2011-08-11 04:41:55 +08:00
@attachment || = Folder . find_attachment_in_context_with_path ( @context , path )
2011-02-01 09:57:29 +08:00
2011-05-20 23:38:33 +08:00
raise ActiveRecord :: RecordNotFound if ! @attachment
params [ :id ] = @attachment . id
2011-02-01 09:57:29 +08:00
params [ :download ] = '1'
show
end
2012-12-05 06:22:59 +08:00
2011-02-01 09:57:29 +08:00
# checks if for the current root account there's a 'files' domain
# defined and tried to use that. This way any files that we stream through
# a canvas URL are at least on a separate subdomain and the javascript
# won't be able to access or update data with AJAX requests.
def safer_domain_available?
2012-04-24 05:38:31 +08:00
if ! @files_domain && request . host_with_port != HostUrl . file_host ( @domain_root_account , request . host_with_port )
2012-11-09 04:10:01 +08:00
@safer_domain_host = HostUrl . file_host_with_shard ( @domain_root_account , request . host_with_port )
2011-02-01 09:57:29 +08:00
end
! ! @safer_domain_host
end
protected :safer_domain_available?
2012-12-05 06:22:59 +08:00
2011-02-01 09:57:29 +08:00
def attachment_content
@attachment = @context . attachments . active . find ( params [ :file_id ] )
if authorized_action ( @attachment , @current_user , :update )
# The files page lets you edit text content inline by firing off a json
# request to get the data.
2011-04-29 01:16:26 +08:00
# Protect ourselves against reading huge files into memory -- if the
# attachment is too big, don't return it.
if @attachment . size > Setting . get_cached ( 'attachment_json_response_max_size' , 1 . megabyte . to_s ) . to_i
2011-06-16 00:41:22 +08:00
render :json = > { :error = > t ( 'errors.too_large' , " The file is too large to edit " ) } . to_json
2011-04-29 01:16:26 +08:00
return
2011-02-01 09:57:29 +08:00
end
2011-04-29 01:16:26 +08:00
stream = @attachment . open
render :json = > { :body = > stream . read } . to_json
2011-02-01 09:57:29 +08:00
end
end
2012-12-05 06:22:59 +08:00
2011-03-15 03:51:44 +08:00
def send_attachment ( attachment )
2011-12-14 06:34:46 +08:00
# check for download_frd param and, if it's present, force the user to download the
# file and don't display it inline. we use download_frd instead of looking to the
# download param because the download param is used all over the place to mean stuff
# other than actually download the file. Long term we probably ought to audit the files
# controller, make download mean download, and remove download_frd.
if params [ :inline ] && ! params [ :download_frd ] && attachment . content_type && ( attachment . content_type . match ( / \ Atext / ) || attachment . mime_class == 'text' || attachment . mime_class == 'html' || attachment . mime_class == 'code' || attachment . mime_class == 'image' )
2011-03-15 03:51:44 +08:00
send_stored_file ( attachment )
2011-12-14 06:34:46 +08:00
elsif attachment . inline_content? && ! params [ :download_frd ] && ! @context . is_a? ( AssessmentQuestion )
2011-02-01 09:57:29 +08:00
if params [ :file_path ] || ! params [ :wrap ]
2011-03-15 03:51:44 +08:00
send_stored_file ( attachment )
2011-02-01 09:57:29 +08:00
else
# If the file is inlineable then redirect to the 'show' action
# so we can wrap it in all the Canvas header/footer stuff
redirect_to ( named_context_url ( @context , :context_file_url , attachment . id ) )
end
else
2011-03-15 03:51:44 +08:00
send_stored_file ( attachment , false , true )
end
end
protected :send_attachment
2012-12-05 06:22:59 +08:00
2011-03-15 03:51:44 +08:00
def send_stored_file ( attachment , inline = true , redirect_to_s3 = false )
2012-06-26 05:07:28 +08:00
user = @current_user
user || = User . find_by_id ( params [ :user_id ] ) if params [ :user_id ] . present?
attachment . context_module_action ( user , :read ) if user && ! params [ :preview ]
2011-03-15 03:51:44 +08:00
log_asset_access ( @attachment , " files " , " files " ) unless params [ :preview ]
2012-12-19 04:17:41 +08:00
set_cache_header ( attachment )
2011-03-15 03:51:44 +08:00
if safer_domain_available?
2011-12-14 06:34:46 +08:00
redirect_to safe_domain_file_url ( attachment , @safer_domain_host , params [ :verifier ] , ! inline )
2011-03-15 03:51:44 +08:00
elsif Attachment . local_storage?
@headers = false if @files_domain
2011-10-25 01:20:42 +08:00
send_file ( attachment . full_filename , :type = > attachment . content_type_with_encoding , :disposition = > ( inline ? 'inline' : 'attachment' ) )
2011-03-15 03:51:44 +08:00
elsif redirect_to_s3
2012-01-23 15:09:38 +08:00
redirect_to ( inline ? attachment . cacheable_s3_inline_url : attachment . cacheable_s3_download_url )
2011-03-15 03:51:44 +08:00
else
2013-03-08 03:28:42 +08:00
send_file_headers! ( :length = > attachment . s3object . content_length , :filename = > attachment . filename , :disposition = > 'inline' , :type = > attachment . content_type_with_encoding )
2011-03-15 03:51:44 +08:00
render :status = > 200 , :text = > Proc . new { | response , output |
2013-03-08 03:28:42 +08:00
attachment . s3object . read do | chunk |
2011-03-15 03:51:44 +08:00
output . write chunk
end
}
2011-02-01 09:57:29 +08:00
end
end
2011-03-15 03:51:44 +08:00
protected :send_stored_file
2012-12-05 06:22:59 +08:00
2012-12-19 04:17:41 +08:00
def set_cache_header ( attachment )
unless attachment . content_type . match ( / \ Atext / ) || attachment . extension == '.html' || attachment . extension == '.htm'
cancel_cache_buster
#set cache to expoire in 1 day, max-age take seconds, and Expires takes a date
response . headers [ " Cache-Control " ] = " private, max-age=86400 "
response . headers [ " Expires " ] = 1 . day . from_now . httpdate
end
end
2011-02-01 09:57:29 +08:00
# GET /files/new
def new
@attachment = @context . attachments . build
if authorized_action ( @attachment , @current_user , :create )
end
end
2012-12-05 06:22:59 +08:00
2011-08-16 06:16:36 +08:00
def preflight
@context = Context . find_by_asset_string ( params [ :context_code ] )
if authorized_action ( @context , @current_user , :manage_files )
@current_folder = Folder . find_folder ( @context , params [ :folder_id ] )
if @current_folder
params [ :filenames ] = [ ] if params [ :filenames ] . blank?
return render :json = > {
:duplicates = > @current_folder . active_file_attachments . map ( & :display_name ) & params [ :filenames ]
}
end
end
render :json = > { }
end
2011-02-02 02:50:10 +08:00
def create_pending
@context = Context . find_by_asset_string ( params [ :attachment ] [ :context_code ] )
2011-06-29 03:06:25 +08:00
@asset = Context . find_asset_by_asset_string ( params [ :attachment ] [ :asset_string ] , @context ) if params [ :attachment ] [ :asset_string ]
2013-05-31 23:36:55 +08:00
@attachment = @context . attachments . build
2011-06-29 03:06:25 +08:00
@check_quota = true
2011-02-02 02:50:10 +08:00
permission_object = @attachment
permission = :create
2011-07-12 03:27:33 +08:00
intent = params [ :attachment ] [ :intent ]
2012-12-05 06:22:59 +08:00
2011-02-02 02:50:10 +08:00
# Using workflow_state we can keep track of the files that have been built
# but we don't know that there's an s3 component for yet (it's still being
# uploaded)
workflow_state = 'unattached'
# There are multiple reasons why we could be building a file. The default
# is to upload it to a context. In the other cases we need to check the
# permission related to the purpose to make sure the file isn't being
# uploaded just to disappear later
2011-07-12 03:27:33 +08:00
if @asset . is_a? ( Assignment ) && intent == 'comment'
2011-06-29 03:06:25 +08:00
permission_object = @asset
2011-02-02 02:50:10 +08:00
permission = :attach_submission_comment_files
2011-06-29 03:06:25 +08:00
@context = @asset
@check_quota = false
2011-07-12 03:27:33 +08:00
elsif @asset . is_a? ( Assignment ) && intent == 'submit'
permission_object = @asset
permission = ( @asset . submission_types || " " ) . match ( / online_upload / ) ? :submit : :nothing
2012-06-27 00:00:08 +08:00
@group = @asset . group_category . group_for ( @current_user ) if @asset . has_group_category?
@context = @group || @current_user
2011-06-29 03:06:25 +08:00
@check_quota = false
2012-09-29 08:05:56 +08:00
@attachment . submission_attachment = true
2011-07-12 03:27:33 +08:00
elsif @context && intent == 'attach_discussion_file'
2011-02-02 02:50:10 +08:00
permission_object = @context . discussion_topics . new
permission = :attach
2011-07-12 03:27:33 +08:00
elsif @context && intent == 'message'
2011-02-02 02:50:10 +08:00
permission_object = @context
permission = :send_messages
2011-06-29 03:06:25 +08:00
@check_quota = false
2011-07-12 03:27:33 +08:00
elsif @context && intent && intent != 'upload'
2011-02-02 02:50:10 +08:00
# In other cases (like unzipping a file, extracting a QTI, etc.
# we don't actually want the uploaded file to show up in the context's
# file listings. If you set its workflow_state to unattached_temporary
# then it will never be activated.
workflow_state = 'unattached_temporary'
2011-06-29 03:06:25 +08:00
@check_quota = false
2011-02-02 02:50:10 +08:00
end
2012-12-05 06:22:59 +08:00
2011-06-29 03:06:25 +08:00
@attachment . context = @context
2011-02-02 02:50:10 +08:00
if authorized_action ( permission_object , @current_user , permission )
2011-06-29 03:06:25 +08:00
if @context . respond_to? ( :is_a_context? ) && @check_quota
2011-02-02 02:50:10 +08:00
get_quota
return if quota_exceeded ( named_context_url ( @context , :context_files_url ) )
end
@attachment . filename = params [ :attachment ] [ :filename ]
@attachment . file_state = 'deleted'
@attachment . workflow_state = workflow_state
if @context . respond_to? ( :folders )
2011-04-27 11:55:24 +08:00
if params [ :attachment ] [ :folder_id ] . present?
@folder = @context . folders . active . find_by_id ( params [ :attachment ] [ :folder_id ] )
end
2011-02-02 02:50:10 +08:00
@folder || = Folder . unfiled_folder ( @context )
@attachment . folder_id = @folder . id
end
@attachment . content_type = Attachment . mimetype ( @attachment . filename )
@attachment . save!
2011-05-20 04:14:01 +08:00
res = @attachment . ajax_upload_params ( @current_pseudonym ,
2011-08-16 06:16:36 +08:00
named_context_url ( @context , :context_files_url , :format = > :text , :duplicate_handling = > params [ :attachment ] [ :duplicate_handling ] ) ,
s3_success_url ( @attachment . id , :uuid = > @attachment . uuid , :duplicate_handling = > params [ :attachment ] [ :duplicate_handling ] ) ,
2011-05-20 04:14:01 +08:00
:no_redirect = > params [ :no_redirect ] ,
:upload_params = > {
'attachment[folder_id]' = > params [ :attachment ] [ :folder_id ] || '' ,
2011-08-06 02:57:48 +08:00
'attachment[unattached_attachment_id]' = > @attachment . id ,
'check_quota_after' = > @check_quota ? '1' : '0'
2011-05-20 04:14:01 +08:00
} ,
:ssl = > request . ssl? )
2011-03-08 05:02:56 +08:00
render :json = > res . to_json
2011-02-02 02:50:10 +08:00
end
end
2012-12-05 06:22:59 +08:00
2011-02-02 02:50:10 +08:00
def s3_success
2011-04-27 11:55:24 +08:00
if params [ :id ] . present?
@attachment = Attachment . find_by_id_and_workflow_state_and_uuid ( params [ :id ] , 'unattached' , params [ :uuid ] )
end
2013-03-08 03:28:42 +08:00
details = @attachment . s3object . head rescue nil
2011-02-02 02:50:10 +08:00
if @attachment && details
2011-08-16 06:16:36 +08:00
deleted_attachments = @attachment . handle_duplicates ( params [ :duplicate_handling ] )
2011-05-20 04:14:01 +08:00
@attachment . process_s3_details! ( details )
2012-09-14 03:30:29 +08:00
render_attachment_json ( @attachment , deleted_attachments )
2011-02-02 02:50:10 +08:00
else
2012-12-08 02:02:57 +08:00
render :json = > { :errors = > [ { :attribute = > 'attachment' , :message = > 'upload failed' } ] }
2011-02-02 02:50:10 +08:00
end
end
2011-02-01 09:57:29 +08:00
2012-03-21 06:08:20 +08:00
# for local file uploads
def api_create
@policy , @attachment = Attachment . decode_policy ( params [ :Policy ] , params [ :Signature ] )
if ! @policy
return render ( :nothing = > true , :status = > :bad_request )
end
@context = @attachment . context
@attachment . workflow_state = nil
2013-06-13 07:26:49 +08:00
@attachment . uploaded_data = params [ :file ] || params [ :attachment ] && params [ :attachment ] [ :uploaded_data ]
2012-03-21 06:08:20 +08:00
if @attachment . save
# for consistency with the s3 upload client flow, we redirect to the success url here to finish up
2012-07-08 06:01:34 +08:00
redirect_to api_v1_files_create_success_url ( @attachment , :uuid = > @attachment . uuid , :on_duplicate = > params [ :on_duplicate ] , :quota_exemption = > params [ :quota_exemption ] )
2012-03-21 06:08:20 +08:00
else
render ( :nothing = > true , :status = > :bad_request )
end
end
def api_create_success
@attachment = Attachment . find_by_id_and_uuid ( params [ :id ] , params [ :uuid ] )
return render ( :nothing = > true , :status = > :bad_request ) unless @attachment . try ( :file_state ) == 'deleted'
2013-03-29 03:02:06 +08:00
duplicate_handling = check_duplicate_handling_option ( request . params )
2012-03-27 04:07:54 +08:00
return unless duplicate_handling
2012-07-08 06:01:34 +08:00
return unless check_quota_after_attachment ( request )
2012-03-21 06:08:20 +08:00
if Attachment . s3_storage?
return render ( :nothing = > true , :status = > :bad_request ) unless @attachment . state == :unattached
2013-03-08 03:28:42 +08:00
details = @attachment . s3object . head
2012-03-21 06:08:20 +08:00
@attachment . process_s3_details! ( details )
else
@attachment . file_state = 'available'
@attachment . save!
end
2012-03-27 04:07:54 +08:00
@attachment . handle_duplicates ( duplicate_handling )
2013-03-29 03:02:06 +08:00
if @attachment . context . respond_to? ( :file_upload_success_callback )
@attachment . context . file_upload_success_callback ( @attachment )
end
2013-03-22 00:10:33 +08:00
2013-05-07 03:16:06 +08:00
json = attachment_json ( @attachment , @current_user )
# render as_text for IE, otherwise it'll prompt
# to download the JSON response
render :json = > json , :as_text = > in_app?
2012-03-21 06:08:20 +08:00
end
2012-08-14 05:13:20 +08:00
def api_file_status
@attachment = Attachment . find_by_id_and_uuid! ( params [ :id ] , params [ :uuid ] )
if @attachment . file_state == 'available'
render :json = > { :upload_status = > 'ready' , :attachment = > attachment_json ( @attachment , @current_user ) }
elsif @attachment . file_state == 'deleted'
render :json = > { :upload_status = > 'pending' }
else
render :json = > { :upload_status = > 'errored' , :message = > @attachment . upload_error_message }
end
end
2012-12-05 06:22:59 +08:00
2011-02-01 09:57:29 +08:00
def create
2011-04-27 11:55:24 +08:00
if ( folder_id = params [ :attachment ] . delete ( :folder_id ) ) && folder_id . present?
@folder = @context . folders . active . find_by_id ( folder_id )
end
2011-02-01 09:57:29 +08:00
@folder || = Folder . unfiled_folder ( @context )
params [ :attachment ] [ :uploaded_data ] || = params [ :attachment_uploaded_data ]
2011-02-02 02:50:10 +08:00
params [ :attachment ] [ :uploaded_data ] || = params [ :file ]
2011-02-01 09:57:29 +08:00
params [ :attachment ] [ :user ] = @current_user
params [ :attachment ] . delete :context_id
params [ :attachment ] . delete :context_type
2011-08-16 06:16:36 +08:00
duplicate_handling = params . delete :duplicate_handling
2011-04-27 11:55:24 +08:00
if ( unattached_attachment_id = params [ :attachment ] . delete ( :unattached_attachment_id ) ) && unattached_attachment_id . present?
@attachment = @context . attachments . find_by_id_and_workflow_state ( unattached_attachment_id , 'unattached' )
end
2013-05-31 23:36:55 +08:00
@attachment || = @context . attachments . build
2011-02-01 09:57:29 +08:00
if authorized_action ( @attachment , @current_user , :create )
get_quota
2011-08-06 02:57:48 +08:00
return if ( params [ :check_quota_after ] . nil? || params [ :check_quota_after ] == '1' ) &&
quota_exceeded ( named_context_url ( @context , :context_files_url ) )
2011-02-01 09:57:29 +08:00
respond_to do | format |
2011-02-02 02:50:10 +08:00
@attachment . folder_id || = @folder . id
@attachment . workflow_state = nil
@attachment . file_state = 'available'
2011-02-01 09:57:29 +08:00
success = nil
if params [ :attachment ] && params [ :attachment ] [ :source_attachment_id ]
a = Attachment . find ( params [ :attachment ] . delete ( :source_attachment_id ) )
if a . root_attachment_id && att = @folder . attachments . find_by_id ( a . root_attachment_id )
@attachment = att
success = true
elsif a . grants_right? ( @current_user , session , :download )
@attachment = a . clone_for ( @context , @attachment )
success = @attachment . save
end
end
2011-02-02 02:50:10 +08:00
if params [ :attachment ] [ :uploaded_data ]
success = @attachment . update_attributes ( params [ :attachment ] )
2011-06-16 00:41:22 +08:00
@attachment . errors . add_to_base ( t ( 'errors.server_error' , " Upload failed, server error, please try again. " ) ) unless success
2011-02-02 02:50:10 +08:00
else
2011-06-16 00:41:22 +08:00
@attachment . errors . add_to_base ( t ( 'errors.missing_field' , " Upload failed, expected form field missing " ) )
2011-02-02 02:50:10 +08:00
end
2011-08-16 06:16:36 +08:00
deleted_attachments = @attachment . handle_duplicates ( duplicate_handling )
2012-01-23 15:09:38 +08:00
unless @attachment . downloadable?
2011-02-01 09:57:29 +08:00
success = false
if ( params [ :attachment ] [ :uploaded_data ] . size == 0 rescue false )
2011-06-16 00:41:22 +08:00
@attachment . errors . add_to_base ( t ( 'errors.empty_file' , " That file is empty. Please upload a different file. " ) )
2011-02-01 09:57:29 +08:00
else
2011-06-16 00:41:22 +08:00
@attachment . errors . add_to_base ( t ( 'errors.upload_failed' , " Upload failed, please try again. " ) )
2011-02-01 09:57:29 +08:00
end
unless @attachment . new_record?
@attachment . destroy rescue @attachment . delete
end
end
if success
@attachment . move_to_bottom
format . html { return_to ( params [ :return_to ] , named_context_url ( @context , :context_files_url ) ) }
force rendering json as text in file-related actions
refs #6459
these actions were previously rendering json as text, before our recent
change to prefix the "while(1);" protection to json responses. in that
commit, they got changed to rendering the json as application/json,
which causes some browsers (IE, FF, maybe others) to try to download the
response. this adds an explicit as_text parameter to render to force
rendering as text, even if content-type isn't multipart, and adds
as_text to all the actions that used to render json as text.
testplan: in firefox, uploading to s3, try submitting homework and
uploading a content migration package (such as .imscc). the uploads
should succeed, and you shouldn't see any browser download prompt.
this fixes existing selenium specs for file_uploads_spec and others.
Change-Id: Iacf9093d233c8253a5dd5b30ecb9fcd860c5fcbf
Reviewed-on: https://gerrit.instructure.com/7190
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Brian Palmer <brianp@instructure.com>
2011-11-30 03:49:26 +08:00
format . json do
2012-09-14 03:30:29 +08:00
render_attachment_json ( @attachment , deleted_attachments , @folder )
force rendering json as text in file-related actions
refs #6459
these actions were previously rendering json as text, before our recent
change to prefix the "while(1);" protection to json responses. in that
commit, they got changed to rendering the json as application/json,
which causes some browsers (IE, FF, maybe others) to try to download the
response. this adds an explicit as_text parameter to render to force
rendering as text, even if content-type isn't multipart, and adds
as_text to all the actions that used to render json as text.
testplan: in firefox, uploading to s3, try submitting homework and
uploading a content migration package (such as .imscc). the uploads
should succeed, and you shouldn't see any browser download prompt.
this fixes existing selenium specs for file_uploads_spec and others.
Change-Id: Iacf9093d233c8253a5dd5b30ecb9fcd860c5fcbf
Reviewed-on: https://gerrit.instructure.com/7190
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Brian Palmer <brianp@instructure.com>
2011-11-30 03:49:26 +08:00
end
format . text do
2012-09-14 03:30:29 +08:00
render_attachment_json ( @attachment , deleted_attachments , @folder )
force rendering json as text in file-related actions
refs #6459
these actions were previously rendering json as text, before our recent
change to prefix the "while(1);" protection to json responses. in that
commit, they got changed to rendering the json as application/json,
which causes some browsers (IE, FF, maybe others) to try to download the
response. this adds an explicit as_text parameter to render to force
rendering as text, even if content-type isn't multipart, and adds
as_text to all the actions that used to render json as text.
testplan: in firefox, uploading to s3, try submitting homework and
uploading a content migration package (such as .imscc). the uploads
should succeed, and you shouldn't see any browser download prompt.
this fixes existing selenium specs for file_uploads_spec and others.
Change-Id: Iacf9093d233c8253a5dd5b30ecb9fcd860c5fcbf
Reviewed-on: https://gerrit.instructure.com/7190
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Brian Palmer <brianp@instructure.com>
2011-11-30 03:49:26 +08:00
end
2011-02-01 09:57:29 +08:00
else
format . html { render :action = > " new " }
format . json { render :json = > @attachment . errors . to_json }
format . text { render :json = > @attachment . errors . to_json }
end
end
end
end
def update
@attachment = @context . attachments . find ( params [ :id ] )
@folder = @context . folders . active . find ( params [ :attachment ] [ :folder_id ] ) rescue nil
@folder || = @attachment . folder
@folder || = Folder . unfiled_folder ( @context )
if authorized_action ( @attachment , @current_user , :update )
respond_to do | format |
just_hide = params [ :attachment ] [ :just_hide ]
hidden = params [ :attachment ] [ :hidden ]
2012-09-05 23:44:45 +08:00
params [ :attachment ] . delete_if { | k , v | ! [ :display_name , :locked , :lock_at , :unlock_at , :uploaded_data , :hidden ] . include? ( k . to_sym ) }
2011-02-01 09:57:29 +08:00
# Need to be careful on this one... we can't let students turn in a
# file and then edit it after the fact...
params [ :attachment ] . delete ( :uploaded_data ) if @context . is_a? ( User )
@attachment . attributes = params [ :attachment ]
if just_hide == '1'
@attachment . locked = false
@attachment . hidden = true
elsif hidden && ( hidden . empty? || hidden == " 0 " )
@attachment . hidden = false
end
@attachment . folder = @folder
@folder_id_changed = @attachment . folder_id_changed?
if @attachment . save
@attachment . move_to_bottom if @folder_id_changed
2011-06-16 00:41:22 +08:00
flash [ :notice ] = t 'notices.updated' , " File was successfully updated. "
2011-02-01 09:57:29 +08:00
format . html { redirect_to named_context_url ( @context , :context_files_url ) }
format . json { render :json = > @attachment . to_json ( :methods = > [ :readable_size , :mime_class , :currently_locked ] , :permissions = > { :user = > @current_user , :session = > session } ) , :status = > :ok }
else
format . html { render :action = > " edit " }
format . json { render :json = > @attachment . errors . to_json , :status = > :bad_request }
end
end
end
end
2012-07-07 03:45:00 +08:00
# @API Update file
# Update some settings on the specified file
#
2013-08-14 23:06:27 +08:00
# @argument name [String]
# The new display name of the file
#
# @argument parent_folder_id [String]
# The id of the folder to move this file into.
2012-07-07 03:45:00 +08:00
# The new folder must be in the same context as the original parent folder.
# If the file is in a context without folders this does not apply.
2013-08-14 23:06:27 +08:00
#
# @argument lock_at [DateTime]
# The datetime to lock the file at
#
# @argument unlock_at [DateTime]
# The datetime to unlock the file at
#
# @argument locked [Boolean]
# Flag the file as locked
#
# @argument hidden [Boolean]
# Flag the file as hidden
2012-07-07 03:45:00 +08:00
#
# @example_request
#
2012-07-17 22:36:56 +08:00
# curl -XPUT 'https://<canvas>/api/v1/files/<file_id>' \
2012-07-07 03:45:00 +08:00
# -F 'name=<new_name>' \
# -F 'locked=true' \
# -H 'Authorization: Bearer <token>'
#
# @returns File
def api_update
@attachment = Attachment . find ( params [ :id ] )
if authorized_action ( @attachment , @current_user , :update )
@context = @attachment . context
if @context && params [ :parent_folder_id ]
folder = @context . folders . active . find ( params [ :parent_folder_id ] )
if authorized_action ( folder , @current_user , :update )
@attachment . folder = folder
else
return
end
end
@attachment . attributes = process_attachment_params ( params )
if @attachment . save
2012-07-27 00:13:22 +08:00
render :json = > attachment_json ( @attachment , @current_user )
2012-07-07 03:45:00 +08:00
else
render :json = > @attachment . errors . to_json , :status = > :bad_request
end
end
end
2012-12-05 06:22:59 +08:00
2011-02-01 09:57:29 +08:00
def reorder
@folder = @context . folders . active . find ( params [ :folder_id ] )
if authorized_action ( @context , @current_user , :manage_files )
2012-07-07 03:45:00 +08:00
@folders = @folder . active_sub_folders . by_position
2011-02-01 09:57:29 +08:00
@folders . first && @folders . first . update_order ( ( params [ :folder_order ] || " " ) . split ( " , " ) )
2012-07-24 05:21:54 +08:00
@folder . file_attachments . by_position_then_display_name . first && @folder . file_attachments . first . update_order ( ( params [ :order ] || " " ) . split ( " , " ) )
2011-02-01 09:57:29 +08:00
@folder . reload
render :json = > @folder . subcontent . to_json ( :methods = > :readable_size , :permissions = > { :user = > @current_user , :session = > session } )
end
end
2012-07-07 03:45:00 +08:00
# @API Delete file
# Remove the specified file
#
2012-07-17 22:36:56 +08:00
# curl -XDELETE 'https://<canvas>/api/v1/files/<file_id>' \
2012-07-07 03:45:00 +08:00
# -H 'Authorization: Bearer <token>'
2011-02-01 09:57:29 +08:00
def destroy
@attachment = Attachment . find ( params [ :id ] )
if authorized_action ( @attachment , @current_user , :delete )
@attachment . destroy
respond_to do | format |
2012-07-07 03:45:00 +08:00
format . html {
require_context
redirect_to named_context_url ( @context , :context_files_url )
}
if api_request?
2012-07-27 00:13:22 +08:00
format . json { render :json = > attachment_json ( @attachment , @current_user ) }
2012-07-07 03:45:00 +08:00
else
format . json { render :json = > @attachment . to_json }
end
2011-02-01 09:57:29 +08:00
end
end
end
2012-12-05 06:22:59 +08:00
2011-02-01 09:57:29 +08:00
def image_thumbnail
cancel_cache_buster
2012-05-17 06:06:29 +08:00
url = Rails . cache . fetch ( [ 'thumbnail_url' , params [ :uuid ] , params [ :size ] ] . cache_key , :expires_in = > 30 . minutes ) do
2012-05-10 06:47:48 +08:00
attachment = Attachment . active . find_by_id_and_uuid ( params [ :id ] , params [ :uuid ] ) if params [ :id ] . present?
2012-05-17 06:06:29 +08:00
thumb_opts = params . slice ( :size )
url = attachment . thumbnail_url ( thumb_opts ) rescue nil
2011-02-01 09:57:29 +08:00
url || = '/images/no_pic.gif'
url
end
redirect_to url
end
2011-05-05 22:30:05 +08:00
# when using local storage, the image_thumbnail action redirects here rather
# than to a s3 url
def show_thumbnail
if Attachment . local_storage?
2011-05-25 13:27:43 +08:00
cancel_cache_buster
2011-05-05 22:30:05 +08:00
thumbnail = Thumbnail . find_by_id_and_uuid ( params [ :id ] , params [ :uuid ] ) if params [ :id ] . present?
raise ActiveRecord :: RecordNotFound unless thumbnail
send_file thumbnail . full_filename , :content_type = > thumbnail . content_type
else
image_thumbnail
end
end
2012-09-14 03:30:29 +08:00
private
2012-12-05 06:22:59 +08:00
2012-09-14 03:30:29 +08:00
def render_attachment_json ( attachment , deleted_attachments , folder = attachment . folder )
json = { :attachment = > attachment ,
:deleted_attachment_ids = > deleted_attachments . map ( & :id ) }
if folder . name == 'profile pictures'
json [ :avatar ] = avatar_json ( @current_user , attachment , { :type = > 'attachment' } )
end
render :json = > json . to_json ( :allow = > :uuid ,
:methods = > [ :uuid , :readable_size , :mime_class , :currently_locked , :scribdable? , :thumbnail_url ] ,
2012-10-11 00:57:35 +08:00
:permissions = > { :user = > @current_user , :session = > session } , :include_root = > false ) ,
:as_text = > true
2012-09-14 03:30:29 +08:00
end
2012-12-05 06:22:59 +08:00
2011-02-01 09:57:29 +08:00
end