Lws49/feat marketplace pr8 singleton attempt - #8521
Draft
LWS49 wants to merge 31 commits into
Draft
Conversation
Provisions a hands-on marketplace preview: duplicates the listed assessment into the singleton container course (linked via listing.preview_assessment_id, idempotent across re-launches), enrols the previewer as a course manager, and returns the absolute attempt URL on the preview host. Runs with system privileges by design — the Slice 2 content-freeze must not block its own provisioning; the caller (Slice 3's controller action, not yet added) is responsible for authorization.
POST /courses/:course_id/marketplace/listings/:id/launch_preview mirrors #show's authorization (published listing + preview_in_marketplace gate) and delegates provisioning to PreviewLaunchService, returning the attempt URL as JSON.
A previewer grades and publishes their own submission in a preview sandbox course; guard the graded-email chokepoint (the only notification path that doesn't already self-suppress for non-student roles) and the Cikgo task-completion push so a rehearsal never emails the previewer or reports a fake completion to their real LMS.
Aged marketplace preview submissions must be reaped so a previewer can restart a listing without waiting indefinitely (the unique assessment/creator index allows only one submission per pair), while still giving async autograding room to land before deletion. Mirrors DeletingJob's destroy!-per-row mechanics so the EXP after_destroy hook and MonitoringService parity aren't skipped by a bulk delete. The container course, assessment copies, and enrolments are never touched.
The local Postgres dumps this pre-existing index's WHERE clause with a different array-cast spelling; it rode along with the preview_assessment_id migration. Semantically identical, so restore master's rendering to keep the branch's schema delta to this branch's own migrations.
Previewers are enrolled as manager of a container course shared by every previewer in the instance. Revoke cross-previewer submission access, view_all_submissions, the gradebook, and the users roster inside a preview course, carving back exactly the previewer's own submission.
show.json.jbuilder emitted @assessment.id under the `id` key, so TryItHandsOnButton and DuplicateConfirmation posted back with the wrong id to endpoints keyed on Listing#id — the lookup missed, raised CanCan::AccessDenied, and the frontend's 403 handler nuked the whole page. index.json.jbuilder already used listing.id correctly; this brings show in line with it.
…test
tab.location was an empty object literal, so TS rejected the later .href
read once the redirect assertion was added. Cast it to { href?: string }.
RECORDER_COMPONENT_UNMOUNT unconditionally called recorderHelper.stopRecord()
whenever the last Voice-question component unmounted, even if it was never
recording. stopRecord() rejects in that case ("Recorder has already
stopped"), and nothing awaited or caught it, so it became an unhandled
rejection — silent in production, but rendered as a full-page dev-overlay
crash under React 18 StrictMode's mount/unmount/remount cycle, which fires on
every page load that renders an unanswered Voice question.
Guard on recorderHelper.isRecording() before calling stopRecord(), plus a
defensive .catch() against any residual race.
Once Fabric.js constructs the canvas, it wraps the raw <canvas> node in its own container div, re-parenting it out from under this div's direct children. The loading indicator was rendered as a PRECEDING sibling, so any later toggle back to "loading" (e.g. a redundant re-initialize) required React to insertBefore a sibling relative to that no-longer-direct-child node — which throws (surfaced as "Failed to execute 'insertBefore'... is not a child of this node"). Moving the indicator after the canvas means React only ever needs to append a trailing sibling, which needs no reference node and is safe regardless of what Fabric has done to the canvas by then. Added a regression test that reproduces the crash with a real Fabric canvas (a stubbed Image whose src setter fires `load` asynchronously, since jsdom never does) and confirms a redundant re-initialize no longer throws.
The sidebar already de-links entirely for any preview course; breadcrumbs did not, so a previewer could still click through the course-title/tab/ assessment-title crumbs to browse the rest of the shared sandbox. Adds a disableLinks prop to Breadcrumbs (renders every crumb as inert text via the same Link/Crumb de-linking mechanism already used for the last crumb), driven off CourseLayoutData.isPreview in CourseContainer.
…e page The container course's own home page is otherwise blank (no announcements, todos, or activity), yet it's the first thing a previewer sees when "Try it hands-on" enrolls them there before the assessment tab opens. Seed a description on the container course (blank-only, so an admin edit is never clobbered, and seeded on every provisioning call rather than only at creation since the container is created once and reused forever) and show it on CourseShow for previewers, who otherwise see nothing else on the page.
…preview banner Previewers share one submission per (assessment, previewer) — relaunching "Try it hands-on" resumes it rather than starting over — but until now the only way to reset it was a manual Rails console script that wipes the entire shared container course. Adds a "Reset submission" button to the preview banner, scoped strictly to the current user's own submission for the assessment on the current page. Backend: Submission#reset_preview! resets workflow/grading state back to a pristine `attempting` (works from any prior state, including `published`) and gives every question a genuinely blank current answer via `question.attempt(submission)` with no `last_attempt` — unlike `unsubmit`'s `recreate_current_answers`, this destroys the old answers outright (and, via their normal dependent: :destroy associations, any auto-grading and programming test-case results with them) rather than keeping them as non-current history, since a preview reset has no audit-trail requirement to preserve. New PreviewSubmissionsController#update authorizes via the `reset_own_preview_submission` ability (already scoped to the caller's own submission) and never accepts a submission id from the client — it's always looked up by (assessment, creator: current_user). Frontend: the button stays on the same page after resetting (no navigation away) and re-dispatches fetchSubmission so the now-blank state renders in place, since the submission row keeps its id.
…ished publish() already toasted a generic "update successful" message, but PublishButton also unmounts immediately on success (it only renders while workflowState === 'submitted'), so in a solo preview session — no student to observe the effect — publishing looked like it did nothing. When CourseLayoutData.isPreview is true, the success toast is replaced with copy explaining what publishing would do in a real course; the failure path is unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.