UNPKG

@instructure/canvas-rce

Version:

A component wrapping Canvas's usage of Tinymce

610 lines (608 loc) 14.8 kB
/* * Copyright (C) 2018 - present Instructure, Inc. * * This file is part of Canvas. * * Canvas is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License as published by the Free * Software Foundation, version 3 of the License. * * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. */ const FAKE_TIMEOUT = process.env.NODE_ENV === 'test' ? 0 : 500; const PAGES = { // page of announcements announcements: { links: [{ href: '/courses/1/announcements', title: 'Announcements List', date: '2018-04-22T13:00:00Z', date_type: 'posted' }, { href: '/courses/1/announcements/2', title: 'Announcement 2', date: '2018-04-22T13:00:00Z', date_type: 'delayed_post' }, { href: '/courses/1/announcements/1', title: 'Announcement 1' }], bookmark: null }, // first page of assignments assignments: { links: [{ href: '/courses/1/assignments', title: 'Assignment List', published: true, date: '2018-04-22T13:00:00Z', date_type: 'due' }, { href: '/courses/1/assignments/1', title: 'Assignment 1', published: false, date: '2018-04-22T13:00:00Z', date_type: 'due' }, { href: '/courses/1/assignments/2', title: 'Assignment 2', published: false, date: null, date_type: null }, { href: '/courses/1/assignments/3', title: 'Assignment 3', published: true, date: '2018-04-22T13:00:00Z', date_type: 'due' }, { href: '/courses/1/assignments/4', title: 'Quiz 1', published: true }], // refers to second page of assignments bookmark: 'assignments2' }, // second page of assignments assignments2: { links: [{ href: '/courses/1/assignments/5', title: 'Quiz 2', published: false, date: '2018-04-22T13:00:00Z', date_type: 'due' }, { href: '/courses/1/assignments/6', title: 'Quiz 3', published: true, date: '2018-04-22T13:00:00Z', date_type: 'due' }], bookmark: null }, // page of discussions discussions: { links: [{ href: '/courses/1/discussion_topics', title: 'Discussion Index', published: false, date: '2018-04-22T13:00:00Z', date_type: 'due' }, { href: '/courses/1/discussion_topics/4', title: 'Discussion 2', published: true, date: '2018-04-22T13:00:00Z', date_type: 'todo' }, { href: '/courses/1/discussion_topics/3', title: 'Discussion 1', published: true, date: '2018-04-22T13:00:00Z', date_type: 'todo' }, { href: '/courses/1/discussion_topics/2', title: 'Announcement 2', published: false, date: null, date_type: null }, { href: '/courses/1/discussion_topics/1', title: 'Announcement 1', published: true, date: '2018-04-22T13:00:00Z', date_type: 'due' }], // intentionally referring to page that doesn't exist to test failure mode bookmark: 'discussions2' }, // page of modules modules: { links: [{ href: '/courses/1/modules', title: 'Modules List', published: true, date: '2018-04-22T13:00:00Z', date_type: 'published' }, { href: '/courses/1/modules/2', title: 'Module 2', published: false, date: null, date_type: 'published' }, { href: '/courses/1/modules/1', title: 'Module 1', published: true, date: '2018-04-22T13:00:00Z', date_type: 'published' }], bookmark: null }, // page of quizzes quizzes: { links: [], bookmark: null }, // page of wiki pages wikiPages: { links: [{ href: '/courses/1/pages/wiki-page-1', title: 'Wiki Page 1', published: true, date: '2018-04-22T13:00:00Z', date_type: 'todo' }, { href: '/courses/1/pages/wiki-page-2', title: 'Wiki Page 2', published: false, date: null, date_type: 'todo' }], bookmark: null }, // root files files: { files: [{ id: 1, type: 'text/plain', name: 'File 1', url: '/files/1' }, { id: 2, type: 'text/plain', name: 'File 2', url: '/files/2' }], bookmark: 'files2' }, // root files page 2 files2: { files: [{ id: 3, type: 'text/plain', name: 'File 3', url: '/files/3' }], bookmark: null }, // files in folder 1 folder1files: { files: [{ id: 4, type: 'text/plain', name: 'File 4', url: '/files/4' }, { id: 5, type: 'text/plain', name: 'File 5', url: '/files/5' }], bookmark: null }, // result for folder with no files emptyfiles: { files: [], bookmark: null }, // root folders folders: { folders: [{ id: 1, name: 'Folder 1', parentId: 42, filesUrl: 'folder1files', foldersUrl: 'folder1folders' }], bookmark: 'folders2' }, // root folders page 2 folders2: { folders: [{ id: 2, name: 'Folder 2', parentId: 42, filesUrl: 'emptyfiles', foldersUrl: 'emptyfolders' }], bookmark: null }, // sub folders for folder 1 folder1folders: { folders: [{ id: 3, name: 'Folder 1 Sub', parentId: 1, filesUrl: 'emptyfiles', foldersUrl: 'emptyfolders' }], bookmark: null }, // folders result for a folder with no sub folders emptyfolders: { folders: [], bookmark: null } }; const FOLDERS = [{ id: 1, name: 'Folder 1', filesUrl: 'filesurl', foldersUrl: 'foldersurl' }, { id: 2, name: 'Folder 2', filesUrl: 'filesurl', foldersUrl: 'foldersurl' }, { id: 3, name: 'Folder 3', filesUrl: 'filesurl', foldersUrl: 'foldersurl' }]; export function buildImage(index, name, height, width) { const id = 123000001 + index; const url = `https://www.fillmurray.com/${height}/${width}`; return { display_name: name, href: url, id, preview_url: url, thumbnail_url: url, content_type: 'image/png', date: '2023-05-23T20:05:17Z', filename: name }; } const imageNames = ['bill_murray', 'bill_is_the_best', 'bill_who?', 'love_this_guy!!11!']; const images = []; for (let i = 0; i < 30; i++) { const name = imageNames[i % imageNames.length] + i + '.jpeg'; /* * This is just to get some variety to the example images. * We get some Bills, each with unique dimensions following this pattern: * 100x200, 201x101, 302x202, 203x303, 304x404, 405x305, * 100x206, 201x107, 302x208, 203x309, 304x410, 405x311, etc. */ const height = [100, 200, 200, 300, 300, 400][i % 6] + i; const width = [200, 100, 300, 200, 400, 300][i % 6] + Math.round(i / 6); images.push(buildImage(i, name, height, width)); } const brokenImage = { id: 123000000, display_name: 'broken_image.jpeg', filename: 'broken_image.jpeg', href: 'http://canvas/files/123000000/download', preview_url: 'http://canvas/files/123000000/download', thumbnail_url: 'http://does.not/exist.png' }; const IMAGE_RESPONSES = [{ bookmark: 'http://canvas/images/2', files: [images[0], brokenImage, ...images.slice(1, 10)] }, { bookmark: 'http://canvas/images/3', bookmarkForThis: 'http://canvas/images/2', files: images.slice(10, 20) }, { bookmark: null, bookmarkForThis: 'http://canvas/images/3', files: images.slice(20) }]; const FLICKR_RESULTS = { go: [{ id: '1', href: 'https://farm9.static.flickr.com/8491/8297692520_4e7a43ffcf_s.jpg', title: 'Game of Go in our club.' }, { id: '2', href: 'https://farm1.static.flickr.com/5/7270219_6d3f41bc71_s.jpg', title: 'Another game of Go' }, { id: '3', href: 'https://farm1.static.flickr.com/8/9686480_c726bf6c5d_s.jpg', title: 'the fourth game' }], chess: [{ id: '4', href: 'https://farm5.static.flickr.com/4051/4627936161_39df5d616a_s.jpg', title: 'Chess.' }, { id: '5', href: 'https://farm8.static.flickr.com/7428/9646564428_0e359a1092_s.jpg', title: 'chess' }, { id: '6', href: 'https://farm9.static.flickr.com/8309/7961751980_66333f83cf_s.jpg', title: 'champion chess' }] }; function makeFiles(bookmark_base, extension, content_type) { return { [`${bookmark_base}1`]: { files: [1, 2, 3].map(i => { return { id: i, filename: `file${i}.${extension}`, content_type, display_name: `file${i}`, href: `http://the.net/${i}`, date: `2019-05-25T13:0${i}:00Z` }; }), bookmark: `${bookmark_base}2`, hasMore: true }, [`${bookmark_base}2`]: { files: [4, 5, 6].map(i => { return { id: i, filename: `file${i}.${extension}`, content_type, display_name: `file${i}`, href: `http://the.net/${i}`, date: `2019-05-25T13:0${i}:00Z` }; }), bookmark: null, hasMore: false } }; } const DOCUMENTS = makeFiles('documents', 'txt', 'text/plain'); const MEDIA = makeFiles('media', 'mp3', 'audio/mp3'); export function getSession() { return Promise.resolve({ contextType: 'course', contextId: 47, canUploadFiles: true, usageRightsRequired: true }); } export function initializeFolders() { return {}; } export function initializeImages(props) { return { [props.contextType]: { files: [], bookmark: undefined, hasMore: true, isLoading: false } }; } export function initializeUpload() { return { uploading: false, folders: {}, formExpanded: false }; } export function initializeFlickr() { return { searchResults: [], searching: false, formExpanded: false }; } export function initializeCollection(endpoint) { return { links: [], bookmark: endpoint, loading: false }; } export function initializeDocuments(_props) { return { course: { files: [], bookmark: 'documents1', isLoading: false, hasMore: true }, user: { files: [], bookmark: 'documents1', isLoading: false, hasMore: true } }; } export function initializeMedia(_props) { return { course: { files: [], bookmark: 'media1', isLoading: false, hasMore: true }, user: { files: [], bookmark: 'media1', isLoading: false, hasMore: true } }; } export function fetchFolders() { return new Promise(resolve => { setTimeout(() => { resolve({ folders: FOLDERS }); }, 1000); }); } export function fetchFiles(uri = 'files') { return Promise.resolve(PAGES[uri]); } export function fetchImages(props) { return new Promise((resolve, reject) => { setTimeout(() => { let response; const bookmark = props.images[props.contextType] && props.images[props.contextType].bookmark; if (bookmark) { response = IMAGE_RESPONSES.find(response => response.bookmarkForThis === props.bookmark); } else { response = IMAGE_RESPONSES[0]; } if (response) { resolve(response); } else { reject(new Error('Invalid bookmark')); } }, FAKE_TIMEOUT); }); } export function fetchMediaFolder() { return Promise.resolve(PAGES.folders); } export function preflightUpload() { return new Promise(resolve => { setTimeout(() => { resolve({}); }, FAKE_TIMEOUT); }); } export function uploadFRD() { return new Promise(resolve => { setTimeout(() => { resolve(images[0]); }, 250); }); } export function fetchRootFolder(props) { return Promise.resolve({ folders: [{ id: 0, name: `${props.contextType} files`, filesUrl: 'files', foldersUrl: 'folders' }] }); } export function fetchPage(uri) { return new Promise((resolve, reject) => { setTimeout(() => { if (PAGES[uri]) { resolve(PAGES[uri]); } else { reject(new Error('bad page!')); } }, 1000); }); } export function searchFlickr(term) { return new Promise((resolve, reject) => { setTimeout(() => { if (FLICKR_RESULTS[term]) { resolve(FLICKR_RESULTS[term]); } else { reject(new Error('No search results!')); } }, 1000); }); } export function setUsageRights(id, usageRights) { const msg = 'Setting the following usage rights for file (id: %s):'; console.log(msg, id, usageRights); // eslint-disable-line no-console } export function getFile(id) { return Promise.resolve({ id, type: 'text/plain', name: 'Test File', url: 'test.txt', embed: { type: 'file' } }); } export function fetchDocs(state) { return new Promise((resolve, reject) => { setTimeout(() => { const bookmark = state.documents[state.contextType] && state.documents[state.contextType].bookmark || 'documents1'; const response = DOCUMENTS[bookmark]; if (response) { resolve(response); } else { reject(new Error('Invalid bookmark')); } }, FAKE_TIMEOUT); }); } export function fetchMedia(state) { return new Promise((resolve, reject) => { setTimeout(() => { let response; const bookmark = state.media[state.contextType] && state.media[state.contextType].bookmark || 'media1'; if (bookmark) { response = MEDIA[bookmark]; } if (response) { resolve(response); } else { reject(new Error('Invalid bookmark')); } }, FAKE_TIMEOUT); }); } export function updateMediaObject(state, { media_object_id, title }) { // eslint-disable-next-line @typescript-eslint/no-unused-vars return new Promise((resolve, reject) => { setTimeout(() => { resolve({ id: media_object_id, title, media_type: 'video', date: '2019-10-29T13:08:36Z', published: true }); }, FAKE_TIMEOUT); }); } export function updateMediaObjectFailure() { return new Promise((resolve, reject) => { setTimeout(() => { reject(); }, FAKE_TIMEOUT); }); } export function updateClosedCaptions(/* apiProps, {media_object_id, subtitles} */ ) { return new Promise(resolve => { setTimeout(() => { resolve([]); }, FAKE_TIMEOUT); }); }