From 80024d44beed7b7966d1f4c99426cc89b712857f Mon Sep 17 00:00:00 2001 From: Arthur Barrett <abarrett@fas.harvard.edu> Date: Wed, 11 May 2016 17:07:24 -0400 Subject: [PATCH 1/5] Fixes error in console relating to ENV.COURSE_WIZARD undefined. --- js/checklist.js | 296 +++++++++++++++++++++++++----------------------- 1 file changed, 152 insertions(+), 144 deletions(-) diff --git a/js/checklist.js b/js/checklist.js index c92c3da..51d7876 100644 --- a/js/checklist.js +++ b/js/checklist.js @@ -1,158 +1,166 @@ -/** - * This section adds some text about migrating content from iSites to the "Import Content" page. - * - * Due to the fact that the "Import Content" form is dynamically generated at page render time, - * there is some extra complexity in the code to poll for the existence of the DOM element - * before trying to add the text. Otherwise, this could be simplified to a single jquery call. - */ -require(['jquery'], function($) { - - // Holds the content that will be added to the page - var html = "<p>If you would like to incorporate content from a previous iSite, please contact the Academic Technology Group at <a href=\"mailto:atg@fas.harvard.edu\">atg@fas.harvard.edu</a>.</p>"; - - // Holds a boolean to indicate if this is the "Import Content" page, - // because this code should only be executed there - var is_content_migration_page = /^\/courses\/\d+\/content_migrations/.test(window.location.pathname); - - // Holds a function that when executed, will call its callback when the selector returns an element. - // The assumption is that the element may not exist in the DOM on the first try. - var poll_for_element = pollForElement("#migrationConverterContainer > h1", 20, 100, function($el) { - $el.after(html); - }); - - if (is_content_migration_page) { - poll_for_element(); +(function() { + // Ensure that these scripts only run on course pages + if ("/courses/" == window.location.pathname.substr(0, "/courses/".length)) { + require(['jquery', 'jsx/course_wizard/ListItems'], modify_setup_checklist); + require(['jquery'], modify_import_content_page); } - + /** - * Poll the DOM for the existence of an element and then execute - * the "success" callback when/if the element is found to exist. + * This function adds some text about migrating content from iSites to the "Import Content" page. * - * @param {(string|jQuery)} el the element to find - * @param {integer} num_tries the number of times to test for existence - * @param {integer} timeout the interval between tries - * @param {function} success the callback to execute when/if the el is found - * @returns {function} a function that will initiate the polling process + * Due to the fact that the "Import Content" form is dynamically generated at page render time, + * there is some extra complexity in the code to poll for the existence of the DOM element + * before trying to add the text. Otherwise, this could be simplified to a single jquery call. */ - function pollForElement(el, num_tries, timeout, success) { - var callback = function() { - var exists = $(el).length != 0; - --num_tries; - if (exists) { - success($(el)); - } else { - if (num_tries > 0) { - window.setTimeout(callback, timeout) + function modify_import_content_page($) { + + // Holds the content that will be added to the page + var html = "<p>If you would like to incorporate content from a previous iSite, please contact the Academic Technology Group at <a href=\"mailto:atg@fas.harvard.edu\">atg@fas.harvard.edu</a>.</p>"; + + // Holds a boolean to indicate if this is the "Import Content" page, + // because this code should only be executed there + var is_content_migration_page = /^\/courses\/\d+\/content_migrations/.test(window.location.pathname); + + // Holds a function that when executed, will call its callback when the selector returns an element. + // The assumption is that the element may not exist in the DOM on the first try. + var poll_for_element = pollForElement("#migrationConverterContainer > h1", 20, 100, function($el) { + $el.after(html); + }); + + if (is_content_migration_page) { + poll_for_element(); + } + + /** + * Poll the DOM for the existence of an element and then execute + * the "success" callback when/if the element is found to exist. + * + * @param {(string|jQuery)} el the element to find + * @param {integer} num_tries the number of times to test for existence + * @param {integer} timeout the interval between tries + * @param {function} success the callback to execute when/if the el is found + * @returns {function} a function that will initiate the polling process + */ + function pollForElement(el, num_tries, timeout, success) { + var callback = function() { + var exists = $(el).length != 0; + --num_tries; + if (exists) { + success($(el)); + } else { + if (num_tries > 0) { + window.setTimeout(callback, timeout) + } } - } - }; - return callback; + }; + return callback; + } } -}); - -/** - * This section overrides/modifies the the data structure that defines the course setup checklist. - * See the comments inside for more details. - */ -require(['jquery', 'jsx/course_wizard/ListItems'], function($, ListItems) { + /** - * SYNOPSIS: - * - * This module modifies the checklist items data source that populate - * the "Setup Checklist" for course instructors. - * - * DESCRIPTION: - * - * The "Setup Checklist", or CourseWizard, as it is called in Canvas, is a component built - * using ReactJS and a syntax extension called JSX. The CourseWizard is composed of several - * sub-components, each of which is contained in a separate JSX file, which compiles down - * to native JS (the compilation step happens on the server). - * - * The components are passed environment values from the Courses Controller, and these values - * are accessed in the global ENV namespace. To better understand how the CourseWizard works, - * refer to these source files: - * - * 1) https://github.com/instructure/canvas-lms/blob/master/app/jsx/course_wizard/ListItems.jsx - * 2) https://github.com/instructure/canvas-lms/blob/master/app/jsx/course_wizard/ChecklistItem.jsx - * 3) https://github.com/instructure/canvas-lms/blob/master/app/jsx/course_wizard/CourseWizard.jsx - * 4) https://github.com/instructure/canvas-lms/blob/master/app/controllers/courses_controller.rb - * - * To customize the list of items that appear in the CourseWizard, we load the ListItems - * module and then modify the desired items. ListItems is a reference to an array of objects, - * and CourseWizard uses this same reference at render time, so any changes we make here are - * visible to the CourseWizard component. - * - * NOTE ABOUT EXTERNAL TOOL LINKS: - * - * The external tool links have the tool ID hard coded for the "Harvard College/GSAS" - * account (account_id=39), since it would be too cumbersome to obtain the tool ID - * using the Canvas API. Ideally, these would be provided to the JS as environment - * variables, but since we don't have the ability to modify the server-side controller, - * that's not an option. - * - * Here's an easy way to get the list of external tools if you know the account ID. Just - * run this code from the course home page, and then inspect the objects to find the "id" - * of the tool you want: - * - * $.getJSON("/api/v1/accounts/39/external_tools", $.proxy(console.log, console)); - * - * TECHNICAL RISKS: - * - * 1) Instructure does not support this kind of modification to the checklist, and Instructure - * could release a breaking change to the CourseWizard code at any time. - * - * 2) The external tool links could break if this code is executed from a different account, - * or if the external tools themselves are modified such that the IDs are no longer valid. - * + * This function overrides/modifies the the data structure that defines the course setup checklist. + * See the comments inside for more details. */ - - // Hard-coded external tool IDs - var POLICY_WIZARD_TOOL_ID = 1509; // Tool ID for account_id=39 - var MANAGE_PEOPLE_TOOL_ID = 3958; // Tool ID for account_id=39 - - // Base course URL (i.e. /courses/1234) - var BASE_COURSE_URL = window.location.pathname; - var DEBUG = false; // (window.location.pathname == "/courses/39"); - - //----- CHANGE #1 ----- - // REMOVE: Modify "Import Content" item - ListItems[0].text = "If you've been using another course management system, you probably have stuff in there that you're going to want moved over to Canvas. We can walk you through the process of easily migrating your content into Canvas. If you would like to incorporate content from a previous iSite, please contact the Academic Technology Group at atg@fas.harvard.edu"; + function modify_setup_checklist($, ListItems) { + /** + * SYNOPSIS: + * + * This module modifies the checklist items data source that populate + * the "Setup Checklist" for course instructors. + * + * DESCRIPTION: + * + * The "Setup Checklist", or CourseWizard, as it is called in Canvas, is a component built + * using ReactJS and a syntax extension called JSX. The CourseWizard is composed of several + * sub-components, each of which is contained in a separate JSX file, which compiles down + * to native JS (the compilation step happens on the server). + * + * The components are passed environment values from the Courses Controller, and these values + * are accessed in the global ENV namespace. To better understand how the CourseWizard works, + * refer to these source files: + * + * 1) https://github.com/instructure/canvas-lms/blob/master/app/jsx/course_wizard/ListItems.jsx + * 2) https://github.com/instructure/canvas-lms/blob/master/app/jsx/course_wizard/ChecklistItem.jsx + * 3) https://github.com/instructure/canvas-lms/blob/master/app/jsx/course_wizard/CourseWizard.jsx + * 4) https://github.com/instructure/canvas-lms/blob/master/app/controllers/courses_controller.rb + * + * To customize the list of items that appear in the CourseWizard, we load the ListItems + * module and then modify the desired items. ListItems is a reference to an array of objects, + * and CourseWizard uses this same reference at render time, so any changes we make here are + * visible to the CourseWizard component. + * + * NOTE ABOUT EXTERNAL TOOL LINKS: + * + * The external tool links have the tool ID hard coded for the "Harvard College/GSAS" + * account (account_id=39), since it would be too cumbersome to obtain the tool ID + * using the Canvas API. Ideally, these would be provided to the JS as environment + * variables, but since we don't have the ability to modify the server-side controller, + * that's not an option. + * + * Here's an easy way to get the list of external tools if you know the account ID. Just + * run this code from the course home page, and then inspect the objects to find the "id" + * of the tool you want: + * + * $.getJSON("/api/v1/accounts/39/external_tools", $.proxy(console.log, console)); + * + * TECHNICAL RISKS: + * + * 1) Instructure does not support this kind of modification to the checklist, and Instructure + * could release a breaking change to the CourseWizard code at any time. + * + * 2) The external tool links could break if this code is executed from a different account, + * or if the external tools themselves are modified such that the IDs are no longer valid. + * + */ - //----- CHANGE #2 ----- - // REMOVE: "Add Students" item - ListItems.splice(2, 1); + // Hard-coded external tool IDs + var POLICY_WIZARD_TOOL_ID = 1509; // Tool ID for account_id=39 + var MANAGE_PEOPLE_TOOL_ID = 3958; // Tool ID for account_id=39 - //----- CHANGE #3 ----- - // CHANGE: "Add TAs" item text and move up near the top of the list - var add_tas = ListItems.splice(6, 1)[0]; - ListItems.splice(1, 0, add_tas); - $.each(['text', 'title'], function(idx, prop) { - add_tas[prop] = add_tas[prop].replace(/TA(s)?/g, "TF$1"); - }); - add_tas.url = BASE_COURSE_URL + "/external_tools/" + MANAGE_PEOPLE_TOOL_ID; + // Base course URL (i.e. /courses/1234) + var BASE_COURSE_URL = window.location.pathname; + var DEBUG = false; // (window.location.pathname == "/courses/39"); - - //----- CHANGE #4 ----- - // INSERT: Academic Integrity Policy tool - ListItems.splice(7, 0, { - key:'policy_wizard', - complete: false, - title: "Customize academic integrity policy", - text: "Customize the academic integrity policy for your course.", - url: BASE_COURSE_URL + "/external_tools/" + POLICY_WIZARD_TOOL_ID, - iconClass: 'icon-educators' - }); + //----- CHANGE #1 ----- + // REMOVE: Modify "Import Content" item + ListItems[0].text = "If you've been using another course management system, you probably have stuff in there that you're going to want moved over to Canvas. We can walk you through the process of easily migrating your content into Canvas. If you would like to incorporate content from a previous iSite, please contact the Academic Technology Group at atg@fas.harvard.edu"; + + //----- CHANGE #2 ----- + // REMOVE: "Add Students" item + ListItems.splice(2, 1); + + //----- CHANGE #3 ----- + // CHANGE: "Add TAs" item text and move up near the top of the list + var add_tas = ListItems.splice(6, 1)[0]; + ListItems.splice(1, 0, add_tas); + $.each(['text', 'title'], function(idx, prop) { + add_tas[prop] = add_tas[prop].replace(/TA(s)?/g, "TF$1"); + }); + add_tas.url = BASE_COURSE_URL + "/external_tools/" + MANAGE_PEOPLE_TOOL_ID; + - - //----- DEBUG ----- - if(DEBUG) { - $.getJSON("/api/v1/accounts/39/external_tools").done(function(data) { - console.log("List of tools for account_id 39:"); - $.each(data, function(idx, tool) { - console.log("tool consumer key:", tool.consumer_key, "tool id:", tool.id); - }); + //----- CHANGE #4 ----- + // INSERT: Academic Integrity Policy tool + ListItems.splice(7, 0, { + key:'policy_wizard', + complete: false, + title: "Customize academic integrity policy", + text: "Customize the academic integrity policy for your course.", + url: BASE_COURSE_URL + "/external_tools/" + POLICY_WIZARD_TOOL_ID, + iconClass: 'icon-educators' }); - console.log("customized setup checklist: ", ListItems); + + + //----- DEBUG ----- + if(DEBUG) { + $.getJSON("/api/v1/accounts/39/external_tools").done(function(data) { + console.log("List of tools for account_id 39:"); + $.each(data, function(idx, tool) { + console.log("tool consumer key:", tool.consumer_key, "tool id:", tool.id); + }); + }); + console.log("customized setup checklist: ", ListItems); + } + } - -}); +})(); \ No newline at end of file From 6d23edad422940801ed3249f6b5b2bb0f420abf5 Mon Sep 17 00:00:00 2001 From: Arthur Barrett <abarrett@fas.harvard.edu> Date: Wed, 11 May 2016 17:23:10 -0400 Subject: [PATCH 2/5] Detect whether we can modify the setup checklist by the presence of the COURSE_WIZARD env variable. --- js/checklist.js | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/js/checklist.js b/js/checklist.js index 51d7876..2baa9c0 100644 --- a/js/checklist.js +++ b/js/checklist.js @@ -1,8 +1,13 @@ (function() { - // Ensure that these scripts only run on course pages - if ("/courses/" == window.location.pathname.substr(0, "/courses/".length)) { - require(['jquery', 'jsx/course_wizard/ListItems'], modify_setup_checklist); - require(['jquery'], modify_import_content_page); + // Ensure that these scripts only run on the appropriate pages + var is_course_page = ("/courses/" == window.location.pathname.substr(0, "/courses/".length)); + if (is_course_page) { + if(window.ENV.COURSE_WIZARD) { + require(['jquery', 'jsx/course_wizard/ListItems'], modify_setup_checklist); + } + if (/^\/courses\/\d+\/content_migrations/.test(window.location.pathname)) { + require(['jquery'], modify_import_content_page); + } } /** @@ -16,21 +21,15 @@ // Holds the content that will be added to the page var html = "<p>If you would like to incorporate content from a previous iSite, please contact the Academic Technology Group at <a href=\"mailto:atg@fas.harvard.edu\">atg@fas.harvard.edu</a>.</p>"; - - // Holds a boolean to indicate if this is the "Import Content" page, - // because this code should only be executed there - var is_content_migration_page = /^\/courses\/\d+\/content_migrations/.test(window.location.pathname); - + // Holds a function that when executed, will call its callback when the selector returns an element. // The assumption is that the element may not exist in the DOM on the first try. var poll_for_element = pollForElement("#migrationConverterContainer > h1", 20, 100, function($el) { $el.after(html); }); - - if (is_content_migration_page) { - poll_for_element(); - } - + + poll_for_element(); + /** * Poll the DOM for the existence of an element and then execute * the "success" callback when/if the element is found to exist. From 25668e2c2c5b7262f075349e658f985ad1a3efca Mon Sep 17 00:00:00 2001 From: Arthur Barrett <abarrett@fas.harvard.edu> Date: Wed, 10 Aug 2016 15:15:34 -0400 Subject: [PATCH 3/5] Updates to the course setup checklist for FAS. --- js/checklist.js | 57 +++++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/js/checklist.js b/js/checklist.js index 2baa9c0..8c7452a 100644 --- a/js/checklist.js +++ b/js/checklist.js @@ -1,4 +1,8 @@ (function() { + // Hard-coded external tool IDs for account_id=39 (i.e. Harvard College/GSAS) + var POLICY_WIZARD_TOOL_ID = 1509; + var MANAGE_COURSE_TOOL_ID = 17079; + // Ensure that these scripts only run on the appropriate pages var is_course_page = ("/courses/" == window.location.pathname.substr(0, "/courses/".length)); if (is_course_page) { @@ -9,7 +13,8 @@ require(['jquery'], modify_import_content_page); } } - + + /** * This function adds some text about migrating content from iSites to the "Import Content" page. * @@ -20,7 +25,9 @@ function modify_import_content_page($) { // Holds the content that will be added to the page - var html = "<p>If you would like to incorporate content from a previous iSite, please contact the Academic Technology Group at <a href=\"mailto:atg@fas.harvard.edu\">atg@fas.harvard.edu</a>.</p>"; + var BASE_COURSE_URL = window.location.pathname.replace("/content_migrations", ""); + var url = BASE_COURSE_URL + "/external_tools/" + MANAGE_COURSE_TOOL_ID; + var html = '<p>If you would like to incorporate content from a previous iSite, click <a href="'+url+'">here</a>.</p>'; // Holds a function that when executed, will call its callback when the selector returns an element. // The assumption is that the element may not exist in the DOM on the first try. @@ -111,34 +118,42 @@ * or if the external tools themselves are modified such that the IDs are no longer valid. * */ - - // Hard-coded external tool IDs - var POLICY_WIZARD_TOOL_ID = 1509; // Tool ID for account_id=39 - var MANAGE_PEOPLE_TOOL_ID = 3958; // Tool ID for account_id=39 - + // Base course URL (i.e. /courses/1234) var BASE_COURSE_URL = window.location.pathname; - var DEBUG = false; // (window.location.pathname == "/courses/39"); - - //----- CHANGE #1 ----- - // REMOVE: Modify "Import Content" item - ListItems[0].text = "If you've been using another course management system, you probably have stuff in there that you're going to want moved over to Canvas. We can walk you through the process of easily migrating your content into Canvas. If you would like to incorporate content from a previous iSite, please contact the Academic Technology Group at atg@fas.harvard.edu"; + + //--------------------------------- + // CHANGE: "Import Content" item + ListItems[0].text = "If you've been using another course management system, you probably have stuff in there that you're going to want moved over to Canvas. We can walk you through the process of easily migrating your content into Canvas."; - //----- CHANGE #2 ----- + //--------------------------------- // REMOVE: "Add Students" item ListItems.splice(2, 1); - //----- CHANGE #3 ----- + //--------------------------------- + // CHANGE: "Add Files" item + // Remove the text that says "We'll show you how." When you click the button to go to the file upload page, + // nothing actually happens so removing this text will avoid any confusion. + ListItems[2].text = ListItems[2].text.replace("We'll show you how.", ""); + + //--------------------------------- + // CHANGE: "Select Navigation Links" item + ListItems[3].url += "#tab-navigation"; + + //--------------------------------- + // CHANGE: "Choose a Course Home Page" item + ListItems[4].text = ListItems[4].text.replace('The default is the course activity stream.', 'The default is the Syllabus Page with course description.') + + //--------------------------------- // CHANGE: "Add TAs" item text and move up near the top of the list var add_tas = ListItems.splice(6, 1)[0]; ListItems.splice(1, 0, add_tas); $.each(['text', 'title'], function(idx, prop) { add_tas[prop] = add_tas[prop].replace(/TA(s)?/g, "TF$1"); }); - add_tas.url = BASE_COURSE_URL + "/external_tools/" + MANAGE_PEOPLE_TOOL_ID; - - - //----- CHANGE #4 ----- + add_tas.url = BASE_COURSE_URL + "/external_tools/" + MANAGE_COURSE_TOOL_ID; + + //--------------------------------- // INSERT: Academic Integrity Policy tool ListItems.splice(7, 0, { key:'policy_wizard', @@ -148,10 +163,10 @@ url: BASE_COURSE_URL + "/external_tools/" + POLICY_WIZARD_TOOL_ID, iconClass: 'icon-educators' }); - + //----- DEBUG ----- - if(DEBUG) { + if(false) { $.getJSON("/api/v1/accounts/39/external_tools").done(function(data) { console.log("List of tools for account_id 39:"); $.each(data, function(idx, tool) { @@ -162,4 +177,4 @@ } } -})(); \ No newline at end of file +})(); From ff30a6c2c79628296288aa0d4b7462789121a090 Mon Sep 17 00:00:00 2001 From: Arthur Barrett <abarrett@fas.harvard.edu> Date: Wed, 10 Aug 2016 15:39:25 -0400 Subject: [PATCH 4/5] Fixed jslint warnings. --- js/checklist.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/checklist.js b/js/checklist.js index 8c7452a..89f1495 100644 --- a/js/checklist.js +++ b/js/checklist.js @@ -49,13 +49,13 @@ */ function pollForElement(el, num_tries, timeout, success) { var callback = function() { - var exists = $(el).length != 0; + var exists = $(el).length !== 0; --num_tries; if (exists) { success($(el)); } else { if (num_tries > 0) { - window.setTimeout(callback, timeout) + window.setTimeout(callback, timeout); } } }; @@ -142,7 +142,7 @@ //--------------------------------- // CHANGE: "Choose a Course Home Page" item - ListItems[4].text = ListItems[4].text.replace('The default is the course activity stream.', 'The default is the Syllabus Page with course description.') + ListItems[4].text = ListItems[4].text.replace('The default is the course activity stream.', 'The default is the Syllabus Page with course description.'); //--------------------------------- // CHANGE: "Add TAs" item text and move up near the top of the list From 8c0a4b605abe0e62f9537720283ecaaeeaf0440b Mon Sep 17 00:00:00 2001 From: Arthur Barrett <abarrett@fas.harvard.edu> Date: Thu, 11 Aug 2016 13:47:32 -0400 Subject: [PATCH 5/5] Added title attribute to link on import content page to improve accessibility per @Vittorio2015 and @elliottyates suggestion. --- js/checklist.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/checklist.js b/js/checklist.js index 89f1495..f187e7f 100644 --- a/js/checklist.js +++ b/js/checklist.js @@ -27,7 +27,7 @@ // Holds the content that will be added to the page var BASE_COURSE_URL = window.location.pathname.replace("/content_migrations", ""); var url = BASE_COURSE_URL + "/external_tools/" + MANAGE_COURSE_TOOL_ID; - var html = '<p>If you would like to incorporate content from a previous iSite, click <a href="'+url+'">here</a>.</p>'; + var html = '<p>If you would like to incorporate content from a previous iSite, click <a href="'+url+'" title="Import iSites Content">here</a>.</p>'; // Holds a function that when executed, will call its callback when the selector returns an element. // The assumption is that the element may not exist in the DOM on the first try.