From 3cf30bf463bede3f69a0208e530b81ca03cf7958 Mon Sep 17 00:00:00 2001 From: Aaron Opell Date: Thu, 3 Mar 2022 21:28:09 -0800 Subject: [PATCH] add new api access confirmation page --- css/all.css | 103 +++++++++++++++++++++++++++++++++++++++++ imgs/logo-full.png | Bin 0 -> 12847 bytes js/all.js | 65 +++++++++++++++----------- js/api-key.js | 90 +++++++++++++++++++++++++++++++++++ js/grades.js | 63 ++++++++++++++++--------- js/preload.js | 47 +++++++++---------- js/user.js | 7 ++- js/version-specific.js | 39 ++++------------ manifest.json | 14 +++++- 9 files changed, 322 insertions(+), 106 deletions(-) create mode 100644 imgs/logo-full.png create mode 100644 js/api-key.js diff --git a/css/all.css b/css/all.css index 47a918d..6db4a6a 100644 --- a/css/all.css +++ b/css/all.css @@ -889,4 +889,107 @@ a._3_bfp { border-radius: 5px; color: white; background: var(--primary-color); +} + +#center-wrapper.splus-api-key-page { + padding-top: 20px; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} + +#center-inner.splus-api-key-page { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + max-width: 600px; + border: 2px solid black; + border-radius: 10px; +} + +#center-inner.splus-api-key-page #center-top .page-title, +#center-inner.splus-api-key-page #content-wrapper form p.description.warning, +#center-inner.splus-api-key-page .submit-span-wrapper { + display: none; +} + +.splus-allow-access { + display: block !important; + margin: 10px 0 !important; +} + +#edit-reveal { + width: 100%; +} + +.splus-permissions-icon { + width: 256px; + padding: 10px; +} + +.splus-permissions-icon-wrapper { + display: flex; + justify-content: center; +} + +.splus-permissions-wrapper { + padding: 10px; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} + +.splus-permissions-box { + width: 500px; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} + +.splus-permissions-header { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} + +.splus-permissions-title { + text-align: center; + padding-bottom: 10px; + width: 400px; +} + +.splus-permissions-description { + text-align: center; + font-size: 14px; + width: 400px; +} + +.splus-permissions-description strong { + font-size: 16px; + text-decoration: underline; +} + +.splus-permissions-section { + padding-top: 10px; +} + +.splus-permissions-features-list { + text-align: left; + font-size: 14px; + padding: 0 100px; + list-style: disc !important; + list-style-position: inside !important; +} + +.splus-permissions-never-list { + text-align: left; + font-size: 14px; + padding: 0 10%; + list-style: disc !important; + list-style-position: inside !important; } \ No newline at end of file diff --git a/imgs/logo-full.png b/imgs/logo-full.png new file mode 100644 index 0000000000000000000000000000000000000000..eff6735dbb6ab4a5a12d93acdee61fe63de4206e GIT binary patch literal 12847 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 A*Z=?k literal 0 HcmV?d00001 diff --git a/js/all.js b/js/all.js index 1081c96..81f8c37 100644 --- a/js/all.js +++ b/js/all.js @@ -1091,36 +1091,47 @@ async function createQuickAccess() { ]) ); - let sectionsList = (await fetchApiJson(`users/${getUserId()}/sections`)).section; + try { + let sectionsList = (await fetchApiJson(`users/${getUserId()}/sections`)).section; - if (!sectionsList || sectionsList.length == 0) { - wrapper.appendChild(createElement("p", ["quick-access-no-courses"], { textContent: "No courses found" })); - } else { - let courseOptionsButton; - let iconImage; - let courseIconsContainer; - for (let section of sectionsList) { - wrapper.appendChild(createElement("div", ["quick-access-course"], {}, [ - (iconImage = createElement("div", ["splus-course-icon"], { dataset: { courseTitle: `${section.course_title}: ${section.section_title}` } })), - createElement("a", ["splus-track-clicks", "quick-course-link"], { textContent: `${section.course_title}: ${section.section_title}`, href: `/course/${section.id}`, dataset: { splusTrackingTarget: "quick-access-course-link", splusTrackingLabel: "Quick Access" } }), - (courseIconsContainer = createElement("div", ["icons-container"], {}, [ - createElement("a", ["icon", "icon-grades", "splus-track-clicks"], { href: `/course/${section.id}/student_grades`, title: "Grades", dataset: { splusTrackingTarget: "quick-access-grades-link", splusTrackingLabel: "Quick Access" } }), - createElement("a", ["icon", "icon-mastery", "splus-track-clicks"], { href: `/course/${section.id}/student_mastery`, title: "Mastery", dataset: { splusTrackingTarget: "quick-access-mastery-link", splusTrackingLabel: "Quick Access" } }), - (courseOptionsButton = createElement("a", ["icon", "icon-settings", "splus-track-clicks"], { href: "#", dataset: { splusTrackingTarget: "quick-access-settings-link", splusTrackingLabel: "Quick Access" } })) - ])) - ])); - - let quickLink = Setting.getNestedValue("courseQuickLinks", section.id); - if (quickLink && quickLink !== "") { - courseIconsContainer.prepend(createElement("a", ["icon", "icon-quicklink", "splus-track-clicks"], { href: quickLink, title: `Quick Link \n(${quickLink})`, dataset: { splusTrackingTarget: "quick-access-quicklink-link", splusTrackingLabel: "Quick Access" } })) - } + if (!sectionsList || sectionsList.length == 0) { + wrapper.appendChild(createElement("p", ["quick-access-no-courses"], { textContent: "No courses found" })); + } else { + let courseOptionsButton; + let iconImage; + let courseIconsContainer; + for (let section of sectionsList) { + wrapper.appendChild(createElement("div", ["quick-access-course"], {}, [ + (iconImage = createElement("div", ["splus-course-icon"], { dataset: { courseTitle: `${section.course_title}: ${section.section_title}` } })), + createElement("a", ["splus-track-clicks", "quick-course-link"], { textContent: `${section.course_title}: ${section.section_title}`, href: `/course/${section.id}`, dataset: { splusTrackingTarget: "quick-access-course-link", splusTrackingLabel: "Quick Access" } }), + (courseIconsContainer = createElement("div", ["icons-container"], {}, [ + createElement("a", ["icon", "icon-grades", "splus-track-clicks"], { href: `/course/${section.id}/student_grades`, title: "Grades", dataset: { splusTrackingTarget: "quick-access-grades-link", splusTrackingLabel: "Quick Access" } }), + createElement("a", ["icon", "icon-mastery", "splus-track-clicks"], { href: `/course/${section.id}/student_mastery`, title: "Mastery", dataset: { splusTrackingTarget: "quick-access-mastery-link", splusTrackingLabel: "Quick Access" } }), + (courseOptionsButton = createElement("a", ["icon", "icon-settings", "splus-track-clicks"], { href: "#", dataset: { splusTrackingTarget: "quick-access-settings-link", splusTrackingLabel: "Quick Access" } })) + ])) + ])); + + let quickLink = Setting.getNestedValue("courseQuickLinks", section.id); + if (quickLink && quickLink !== "") { + courseIconsContainer.prepend(createElement("a", ["icon", "icon-quicklink", "splus-track-clicks"], { href: quickLink, title: `Quick Link \n(${quickLink})`, dataset: { splusTrackingTarget: "quick-access-quicklink-link", splusTrackingLabel: "Quick Access" } })) + } - iconImage.style.backgroundImage = `url(${chrome.runtime.getURL("imgs/fallback-course-icon.svg")})`; + iconImage.style.backgroundImage = `url(${chrome.runtime.getURL("imgs/fallback-course-icon.svg")})`; - courseOptionsButton.addEventListener("click", () => openModal("course-settings-modal", { - courseId: section.id, - courseName: `${section.course_title}: ${section.section_title}` - })); + courseOptionsButton.addEventListener("click", () => openModal("course-settings-modal", { + courseId: section.id, + courseName: `${section.course_title}: ${section.section_title}` + })); + } + } + } catch (err) { + if (err === "noapikey") { + wrapper.appendChild(createElement("div", ["quick-access-no-api"], { }, [ + createElement("p", [], { textContent: "Please grant access to your enrolled courses in order to use this feature." }), + createButton("quick-access-grant-access", "Grant Access", () => {location.pathname = "/api"; }), + ])); + } else { + throw err; } } diff --git a/js/api-key.js b/js/api-key.js new file mode 100644 index 0000000..68510fe --- /dev/null +++ b/js/api-key.js @@ -0,0 +1,90 @@ +(async function () { + // Wait for loader.js to finish running + while (!window.splusLoaded) { + await new Promise(resolve => setTimeout(resolve, 10)); + } + await loadDependencies("api-key", ["all"]); +})(); + +(function () { + let currentKey = document.getElementById("edit-current-key"); + let currentSecret = document.getElementById("edit-current-secret"); + currentKey.parentElement.style.display = "none"; + currentSecret.parentElement.style.display = "none"; + + if (currentSecret.value.indexOf("*") === -1) { + let key = currentKey.value; + let secret = currentSecret.value; + + trackEvent("Change Access", "allowed", "API Key"); + + Setting.setValue("apikey", key, () => { + Setting.setValue("apisecret", secret, () => { + Setting.setValue("apiuser", getUserId(), () => { + Setting.setValue("apistatus", "allowed", () => { + location.pathname = "/"; + }); + }); + }); + }); + } + + let centerInner = document.getElementById("center-inner"); + centerInner.classList.add("splus-api-key-page"); + centerInner.parentElement.classList.add("splus-api-key-page"); + + centerInner.prepend(createElement("div", ["splus-permissions-wrapper"], {}, [ + createElement("div", ["splus-permissions-box"], {}, [ + createElement("div", ["splus-permissions-icon-wrapper"], {}, [ + createElement("img", ["splus-permissions-icon"], { src: chrome.runtime.getURL("/imgs/logo-full.png") }), + ]), + createElement("div", ["splus-permissions-header"], {}, [ + createElement("h2", ["splus-permissions-title"], { textContent: "Schoology Plus Needs Access to Your Account" }), + createElement("p", ["splus-permissions-description"], {}, [ + createElement("span", [], { textContent: "Due to a new security feature, Schoology Plus needs access to your Schoology API Key for the following features to work correctly:" }), + createElement("div", ["splus-permissions-section"], {}, [ + createElement("ul", ["splus-permissions-features-list"], {}, [ + createElement("li", [], { textContent: "What-If Grades" }), + createElement("li", [], { textContent: "Assignment Checkmarks" }), + createElement("li", [], { textContent: "Quick Access" }), + createElement("li", [], { textContent: "Courses in Common" }), + ]), + ]), + createElement("span", [], { textContent: "By providing access to your API key, Schoology Plus can view extra details about the courses you're enrolled in." }), + createElement("div", ["splus-permissions-section"], {}, [ + createElement("strong", [], { textContent: "Schoology Plus will never:" }), + createElement("ul", ["splus-permissions-never-list"], {}, [ + createElement("li", [], { textContent: "Collect or store any personal information" }), + createElement("li", [], { textContent: "Have access to your account's password" }), + ]), + ]), + createElement("div", ["splus-permissions-section"], {}, [ + createElement("span", [], { textContent: "If you have any questions, you can" }), + createElement("a", [], { textContent: " view our code on Github", href: "https://github.com/aopell/SchoologyPlus" }), + createElement("span", [], { textContent: " or" }), + createElement("a", [], { textContent: " contact us on Discord", href: "https://discord.schoologypl.us" }), + createElement("span", [], { textContent: ". You can change this setting at any time in the Schoology Plus settings menu." }), + ]), + ]), + ]), + ]) + ])); + + let submitButton = document.getElementById("edit-reveal"); + submitButton.parentElement.classList.add("splus-allow-access"); + submitButton.value = "Allow Access"; + + submitButton.parentElement.insertAdjacentElement("afterend", createElement("div", ["splus-api-key-footer"], {style: {textAlign: "center"}}, [ + createElement("a", [], { + textContent: "Deny Access", onclick: () => { + alert("API key access was denied. Please keep in mind many Schoology Plus features will not work correctly with this disabled. You can change this at any time from the Schoology Plus settings menu."); + trackEvent("Change Access", "denied", "API Key"); + Setting.setValue("apiuser", getUserId(), () => { + Setting.setValue("apistatus", "denied", () => { + location.pathname = "/"; + }); + }); + } + }), + ])); +})(); \ No newline at end of file diff --git a/js/grades.js b/js/grades.js index 7036ecc..f2154b7 100644 --- a/js/grades.js +++ b/js/grades.js @@ -12,12 +12,14 @@ const SINGLE_COURSE = window.location.href.includes("/course/"); var editDisableReason = null; var invalidCategories = []; -function addEditDisableReason(err = "Unknown Error", causedBy403 = false) { +function addEditDisableReason(err = "Unknown Error", causedBy403 = false, causedByNoApiKey = false) { if (!editDisableReason) { - editDisableReason = { version: chrome.runtime.getManifest().version, errors: [], allCausedBy403: causedBy403 }; + editDisableReason = { version: chrome.runtime.getManifest().version, errors: [], allCausedBy403: causedBy403, causedByNoApiKey: causedByNoApiKey }; } editDisableReason.errors.push(err); editDisableReason.allCausedBy403 = editDisableReason.allCausedBy403 && causedBy403; + editDisableReason.causedByNoApiKey = editDisableReason.causedByNoApiKey || causedByNoApiKey; + Logger.debug(editDisableReason, err, causedBy403, causedByNoApiKey); } $.contextMenu({ @@ -106,7 +108,7 @@ var fetchQueue = []; } else { try { let finalGradeArray = (await fetchApiJson(`users/${getUserId()}/grades?section_id=${courseId}`)).section[0].final_grade; - courseGrade = createElement("span", [], {textContent: `${finalGradeArray[finalGradeArray.length - 1].grade.toString()}%`}); + courseGrade = createElement("span", [], { textContent: `${finalGradeArray[finalGradeArray.length - 1].grade.toString()}%` }); } catch { courseGrade = null; } @@ -298,26 +300,30 @@ var fetchQueue = []; try { await processAssignment(assignment); } catch (err) { - if (!assignment.classList.contains("dropped") && assignment.querySelector(".missing")) { - // consequential failure: our denominator is invalid - invalidateCatTotal = true; - invalidCategories.push(category.dataset.id); - - if ("status" in err && err.status === 403) { - addEditDisableReason({ - error: { - message: err.error, - status: err.status - }, - courseId, - course: title.textContent, - assignment: assignment.textContent - }, true); - continue; + if (err === "noapikey") { + addEditDisableReason({ error: { message: err, name: err, stack: undefined, full: JSON.stringify(err) }, courseId, course: title.textContent, assignment: assignment.textContent }, false, true); + } else { + if (!assignment.classList.contains("dropped") && assignment.querySelector(".missing")) { + // consequential failure: our denominator is invalid + invalidateCatTotal = true; + invalidCategories.push(category.dataset.id); + + if ("status" in err && err.status === 403) { + addEditDisableReason({ + error: { + message: err.error, + status: err.status + }, + courseId, + course: title.textContent, + assignment: assignment.textContent + }, true, err === "noapikey"); + continue; + } } - } - addEditDisableReason({ error: { message: err.message, name: err.name, stack: err.stack, full: JSON.stringify(err) }, courseId, course: title.textContent, assignment: assignment.textContent }); + addEditDisableReason({ error: { message: err.message, name: err.name, stack: err.stack, full: JSON.stringify(err) }, courseId, course: title.textContent, assignment: assignment.textContent }, false, err === "noapikey"); + } Logger.error("Error loading assignment for " + courseId + ": ", assignment, err); } } @@ -384,7 +390,7 @@ var fetchQueue = []; } } } catch (err) { - addEditDisableReason({ error: JSON.stringify(err, Object.getOwnPropertyNames(err)), category: category.textContent }) + addEditDisableReason({ error: JSON.stringify(err, Object.getOwnPropertyNames(err)), category: category.textContent }, false, err === "noapikey") } } @@ -555,7 +561,18 @@ var fetchQueue = []; let droppedAssignRClickSelector = ".item-row.dropped:not(.grade-add-indicator)"; // any state change when editing has been disabled - if (editDisableReason && !editDisableReason.allCausedBy403) { + if (Setting.getValue("apistatus") === "denied") { + if (confirm("This feature requires access to your Schoology API Key, which you have denied. Would you like to enable access?")) { + trackEvent("api-denied-popup", "go-to-enable", "What-If Grades"); + location.pathname = "/api"; + } else { + trackEvent("api-denied-popup", "keep-disabled", "What-If Grades"); + } + } + else if (editDisableReason && editDisableReason.causedByNoApiKey) { + location.pathname = "/api"; + } + else if (editDisableReason && !editDisableReason.allCausedBy403) { Logger.error("Editing disabled due to error", editDisableReason); if (confirm("Grade editing has been disabled due to an error. If you are trying to use What If Grades on the grade report page, try going to an individual class gradebook instead. Would you like to report this issue? (It will help us fix it faster!)")) { diff --git a/js/preload.js b/js/preload.js index f4f9e29..866d447 100644 --- a/js/preload.js +++ b/js/preload.js @@ -347,31 +347,23 @@ function getUserId() { * Gets the user's API credentials from the Schoology API key webpage, bypassing the cache. */ async function getApiKeysDirect() { - let userId = getUserId(); - var apiKeys = null; - Logger.log(`Fetching API key for user ${userId}`); - let html = await (await fetch(`https://${Setting.getValue("defaultDomain")}/api/`, { credentials: "same-origin" })).text(); - let docParser = new DOMParser(); - let doc = docParser.parseFromString(html, "text/html"); - - let key; - let secret; - if ((key = doc.getElementById("edit-current-key")) && (secret = doc.getElementById("edit-current-secret"))) { - Logger.log("API key already generated - storing"); - apiKeys = [key.value, secret.value, userId]; - } else { - Logger.log("API key not found - generating and trying again"); - let submitData = new FormData(doc.getElementById("s-api-register-form")); - let generateFetch = await fetch(`https://${Setting.getValue("defaultDomain")}/api/`, { - credentials: "same-origin", - body: submitData, - method: "post" - }); - Logger.log(`Generatekey response: ${generateFetch.status}`); - return await getApiKeysDirect(); + let apiKey = Setting.getValue("apikey"); + let apiSecret = Setting.getValue("apisecret"); + let apiUserId = Setting.getValue("apiuser"); + let currentUser = getUserId(); + let apiStatus = Setting.getValue("apistatus"); + + if (apiStatus === "denied" && apiUserId === currentUser) { + throw "apidenied"; + } + + if (apiKey && apiSecret && apiUserId === currentUser) { + // API keys already exist + return [apiKey, apiSecret, apiUserId]; } - return apiKeys; + // API keys do not exist + throw "noapikey"; } /** @@ -818,12 +810,19 @@ function updateSettings(callback) { undefined, element => element.value ).control, + createElement("div", ["setting-entry"], {}, [ + createElement("h2", ["setting-title"], {}, [ + createElement("a", [], { href: "#", textContent: "Change Schoology Account Access", onclick: () => {location.pathname = "/api";}, style: { fontSize: "" } }) + ]), + createElement("p", ["setting-description"], { textContent: "Grant Schoology Plus access to your Schoology API Key so many features can function, or revoke that access." }) + ]), getBrowser() !== "Firefox" ? createElement("div", ["setting-entry"], {}, [ createElement("h2", ["setting-title"], {}, [ createElement("a", [], { href: "#", textContent: "Anonymous Usage Statistics", onclick: () => openModal("analytics-modal"), style: { fontSize: "" } }) ]), createElement("p", ["setting-description"], { textContent: "[Reload required] Allow Schoology Plus to collect anonymous information about how you use the extension. We don't collect any personal information per our privacy policy." }) - ]) : noControl + ]) : noControl, + ]), createElement("div", ["settings-buttons-wrapper"], undefined, [ createButton("save-settings", "Save Settings", () => Setting.saveModified()), diff --git a/js/user.js b/js/user.js index 8e24b01..19afa82 100644 --- a/js/user.js +++ b/js/user.js @@ -79,5 +79,10 @@ function populateCourseList(targetListElem, loadCourseFunction) { } Theme.setProfilePictures(listElem.getElementsByTagName("img")); }) - .catch(err => Logger.error("Error building courses in common: ", err)); + .catch(err => { + Logger.error("Error building courses in common: ", err); + let listElem = document.getElementById(targetListElem); + clearNodeChildren(listElem); + listElem.appendChild(createElement("li", [], { textContent: "Failed to load courses in common." })); + }); } diff --git a/js/version-specific.js b/js/version-specific.js index 59b4131..c5feb9d 100644 --- a/js/version-specific.js +++ b/js/version-specific.js @@ -242,15 +242,6 @@ let migrationsTo = { } }, "7.1": function (currentVersion, previousVersion) { - saveBroadcasts([ - createBroadcast( - 710, - "Course Nicknames Under Maintenance", - "Due to performance concerns, course nicknames may not show up in every place you're used to seeing them. We're working on a fix so hopefully this can be resolved as soon as possible. Thanks for your patience.", - new Date(2021, 0 /* January */, 16) - ) - ]); - var modalExistsInterval = setInterval(function () { if (document.readyState === "complete" && openModal && document.getElementById("choose-theme-modal") && !document.querySelector(".splus-modal-open")) { clearInterval(modalExistsInterval); @@ -258,27 +249,15 @@ let migrationsTo = { } }, 50); }, - "7.2": function (currentVersion, previousVersion) { - saveBroadcasts([ - createBroadcast( - 720, - "Quick Links and What-If Grades Bug Fixes", - ` -
- Add Zoom links or course websites to Quick Access! - -

Quick Access has a new Quick Link feature, allowing you to add a link to quickly access a class website, Zoom meeting, - or any other URL related to your class right from your Schoology homepage! Additionally, this version of Schoology Plus - fixes some issues people were reporting with What-If Grades, so please check to see if your past issues are resolved. - Thank you for using Schoology Plus!

- -

Follow the instructions in the image below to add a Quick Link:

-
- - `, - new Date(2021, 2 /* March */, 2) - ) - ]); + "7.4": function (currentVersion, previousVersion) { + var accessToAccountInterval = setInterval(function () { + if (document.readyState === "complete" && openModal && !document.querySelector(".splus-modal-open")) { + clearInterval(accessToAccountInterval); + if (!Setting.getValue("apistatus")) { + location.pathname = "/api"; + } + } + }, 50); } }; diff --git a/manifest.json b/manifest.json index db8deb5..e527ca6 100644 --- a/manifest.json +++ b/manifest.json @@ -10,7 +10,7 @@ "update_url": "https://schoologypl.us/firefox_updates.json" } }, - "version": "7.3.6", + "version": "7.4", "icons": { "128": "imgs/icon@128.png", "64": "imgs/icon@64.png", @@ -240,6 +240,18 @@ ], "run_at": "document_end" }, + { + "matches": [ + "https://lms.lausd.net/api", + "https://lms.lausd.net/api/*", + "https://*.schoology.com/api", + "https://*.schoology.com/api/*" + ], + "js": [ + "js/api-key.js" + ], + "run_at": "document_end" + }, { "matches": [ "https://lms.lausd.net/user/*",