NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Task Bar // @namespace CCAU // @description Automate course copies // @match https://*.instructure.com/courses/* // @version 0.1.4 // @author CIDT // @grant none // @license GPL-3.0-or-later // ==/UserScript== "use strict"; (() => { // out/env.js var ROOT_URL = "https://se.instructure.com"; var TASKS = { "tasks": [{ "help": "Click the --> button to start the copy", "name": "Begin Course Copy", "path": "" }, { "help": "If there is any content, don't do the copy", "name": "Check for Content", "path": "modules" }, { "help": "Delete 'Student Introductions' and 'Questions and Answers'", "name": "Delete Discussions", "path": "discussion_topics" }, { "help": "Delete all pages except University Information", "name": "Delete Pages", "path": "pages" }, { "help": "'Restrict students from viewing course before term start date' should be enabled and 'Participation' should be 'Course'", "name": "Verify Settings", "path": "settings" }, { "help": "Double check the email to make sure you copy the right course", "name": "Copy Content", "path": "content_migrations" }, { "help": "Copy the current holiday from the SE University Holidays course (select content)", "name": "Copy Holiday", "path": "content_migrations" }, { "help": "Delete any duplicate menu items. The original item is the first one listed", "name": "Delete Duplicate Menu Items", "path": "settings/configurations#tab-tools" }, { "help": "Move the items in the navigation menu to this order:\n\nHome\nAnnouncements\nSyllabus\nModules\nGrades\nSubmit Grades\nPeople\nSE Email\nHenry G. Bennett Library\nTech Support\nTutor.com\nYuJa\nPanorama\nLockdown Browser\nItem Banks\nCredentials\nFollett Discover\n(whatever else happens to be there)\n", "name": "Fix Menu Order", "path": "settings#tab-navigation" }, { "help": "Click the Auto-Move button, then the arrow in the bottom left", "name": "Move Content", "path": "modules" }, { "help": "Click the Remove Empty button, then the arrow in the bottom left", "name": "Delete Empty Modules", "path": "modules" }, { "help": "Click the Add Dates button, wait for them to publish, then drag them to the top of their respective modules", "name": "Add Date Headers", "path": "modules" }, { "help": "Delete any GOLD Orientation and the University Information items which are NOT at the bottom of the START HERE module", "name": "Delete Old GOLD", "path": "modules" }, { "help": "Move the holiday module into place", "name": "Move Holiday", "path": "modules" }, { "help": "Delete any duplicate Question and Answer or Student Introductions discussions", "name": "Delete Duplicate Discussions", "path": "discussion_topics" }, { "help": "Delete any undeployed GOLD Orientation assignments and empty categories", "name": "Delete Duplicate Assignments", "path": "assignments" }, { "help": "Use https://k.ngn.tf/8775a to fix broken images; manually fix broken module links", "name": "Fix Homepage", "path": "wiki" }, { "help": "Make sure the dates are correct, especially regarding the holiday-adjacent weeks", "name": "Check Assignment Dates", "path": "assignments" }, { "help": "Check for Blackboard references, as well as getting the grade total / weights", "name": "Check Syllabus", "path": "assignments/syllabus" }, { "help": "Make sure it matches the syllabus", "name": "Check Gradebook", "path": "gradebook" }, { "help": "If any real links are broken, inform the professor", "name": "Run Link Validator", "path": "link_validator" }, { "help": "Ensure that all the steps on the Google Sheet have been followed. If something there isn't covered in the automation, lmk", "name": "Check Google Sheet", "path": "" }, { "help": "Excellent work, 47. The money has been wired to your account", "name": "Done", "path": "" }] }; // out/live.js function getCourseID() { return window.location.href.match(/courses\/(\d+)/)?.[1] ?? "NO_COURSE_ID"; } async function isLiveCourse() { const response = await fetch(ROOT_URL + "/api/v1/courses/" + getCourseID()); const data = await response.json(); return new Date(data["start_at"]) < /* @__PURE__ */ new Date(); } // out/task.js function getTasks() { return TASKS.tasks; } // out/utils.js function addArrowButton(task, fn, label) { const bar = document.querySelector(".right-of-crumbs"); const btn = document.createElement("a"); btn.textContent = label; btn.setAttribute("title", task.help); btn.classList.add("btn"); btn.setAttribute("tabindex", "0"); btn.addEventListener("click", fn); bar?.insertAdjacentElement("beforebegin", btn); } function prevArrow(task, fn) { addArrowButton(task, fn, "<--"); } function nextArrow(task, fn) { addArrowButton(task, fn, "-->"); } function displayCurrent(task) { const bar = document.querySelector(".right-of-crumbs"); const btn = document.createElement("a"); btn.textContent = task.name; btn.setAttribute("title", task.help); btn.classList.add("btn"); btn.setAttribute("tabindex", "0"); bar?.insertAdjacentElement("beforebegin", btn); } function getCourseID2() { return window.location.href.match(/courses\/(\d+)/)?.[1] ?? "NO_COURSE_ID"; } function goto(path_) { const id = getCourseID2(); const newLoc = `${ROOT_URL}/courses/${id}/${path_}`; if (window.location.href === newLoc) { window.location.reload(); } else { window.location.href = newLoc; } } function curTask() { const id = getCourseID2(); const str = localStorage.getItem(`ccau_${id}_task`); const num = Number(str); return isNaN(num) ? null : num; } function setTask(task) { const id = getCourseID2(); localStorage.setItem(`ccau_${id}_task`, task.toString()); } // out/index.js async function main() { if (!document.querySelector("#global_nav_accounts_link")) { throw new Error("Only admins can use CCAU"); } if (await isLiveCourse()) { throw new Error("CCAU is disabled in live courses"); } const tasks = getTasks(); const taskNum = curTask() ?? 0; const task = tasks[taskNum]; const prevNum = Math.max(0, taskNum - 1); const prev = tasks[prevNum]; const nextNum = (taskNum + 1) % tasks.length; const next = tasks[nextNum]; prevArrow(prev, () => prevTask(tasks)); displayCurrent(task); nextArrow(next, () => nextTask(tasks)); } function prevTask(tasks) { const taskNum = curTask() ?? 0; const prevNum = Math.max(0, taskNum - 1); const prev = tasks[prevNum]; setTask(prevNum); goto(prev.path); } function nextTask(tasks) { const taskNum = curTask() ?? 0; const nextNum = (taskNum + 1) % tasks.length; const next = tasks[nextNum]; if (nextNum === 0) { return; } setTask(nextNum); goto(next.path); } main(); })();