Skip to content

Releases: nikolaigauer/activity-builder

v1.4.3 — Pasted links behave like links

Choose a tag to compare

@nikolaigauer nikolaigauer released this 11 Sep 17:39

A bug fix for submissions containing pasted URLs. Safe to install over any 1.4.x.

A pasted link no longer wrecks the page

A student who answered a prompt with nothing but a link — exactly what an instructor asks for when the prompt says "provide links to the posts you reviewed" — ended up with a submission whose layout came apart: prompt labels stranded to one side, preview cards adrift on the other, and large empty gaps between them.

Pasted URLs now render as ordinary clickable links, sitting in the normal flow of the answer like any other text.

What was actually happening

Responses are written into post content as a paragraph. WordPress core deliberately watches for a URL sitting alone inside its own paragraph and replaces it with an oEmbed preview card — and because these links point at posts on the same network, that card is a <blockquote> followed by an <iframe>.

Neither is legal inside a paragraph, and two things went wrong as a result:

  • The browser closes the paragraph early when it meets the <blockquote>, so the iframe escapes the paragraph and two empty paragraphs are left behind for every URL.
  • The iframe is delivered as position: absolute; visibility: hidden, for WordPress's embed script to size and reveal. While absolutely positioned it sits outside the normal flow, so the surrounding text lays itself out as though the card were not there.

So this was never a margin or padding problem. The page structure simply wasn't the shape the stylesheet assumed, and no amount of spacing rules would have reached it.

Escaping each answer and then turning URLs into links means core's pattern no longer matches, and the answer stays ordinary flow content.

Accessibility

Plain links are a genuine improvement over the cards, not just a simpler fallback. The preview card is a nested document in an iframe whose visible fallback link is hidden by CSS, which makes it awkward to reach with a screen reader and clumsy to navigate by keyboard. A real link announces itself, takes focus, and can be opened or copied normally.

Scope

The same paragraph shape was emitted in four places — the current section-based form, the older pre-builder form, student-added paragraph blocks, and the New Post form — so all four are corrected together.

Verified against a resolvable network URL: a bare URL, a URL with a query string, a URL inline in prose, two URLs on separate lines, plain text with no link, and a script-tag injection attempt. In every case the embed declines, escaping holds, and query strings survive intact.

This only reproduces where the pasted URL actually resolves, which is why it appeared on the live network and never in local development.

Already-submitted work

This corrects submissions saved from now on. Posts already stored with a bare URL in their own paragraph keep the old markup and will still show the misaligned card until their content is rewritten — the fix changes how answers are saved, not what is already in the database.

Upgrading

Download activity-builder-1.4.3.zip below. It unpacks to activity-builder/, so WordPress treats it as an upgrade rather than installing a second copy — use Add New → Upload Plugin and choose Replace current with uploaded. Avoid the auto-generated Source code archives, which unpack to a version-named folder and install alongside your current plugin instead of replacing it.

v1.4.2 — Re-usable activities start clean

Choose a tag to compare

@nikolaigauer nikolaigauer released this 10 Sep 23:53

A bug fix for activity pages with Re-use Form enabled. No changes to behaviour anyone asked for, no migration, no settings to revisit. Safe to install over any 1.4.x.

Returning to a re-usable activity gives you an empty form again

A student who submitted an entry and came back to write another was met with their previous entry already filled into the fields, under a "Draft restored" banner. Clicking Start another entry from the confirmation screen did the same thing. The old text could be cleared with Discard & start fresh, but nobody should have to — a new entry should begin empty, and the banner made it look as though the student had left work unfinished when they had in fact handed it in.

The work itself was never at risk. Submissions saved correctly throughout; this only affected what the form showed afterwards.

What was actually wrong

The form keeps a local autosave in the browser so a closed tab or a stray refresh doesn't cost a student their writing. Once a submission lands, the server owns that text and the local copy has to go.

The check for "the submission landed" looked for =1 in the page address. But the address carries the new post's ID rather than a bare 1 — deliberately, so the confirmation screen can link straight at the work just saved. Being an unanchored match, =1 was satisfied only when the ID happened to begin with the digit 1. Post 10, 11 or 1337 cleared correctly; post 7, 250 or 2001 did not, and that entry's text waited in the browser for the next visit.

The check now tests whether the parameter is present at all, which is what the server-side code has always done.

Why it survived this long

Two conditions had to coincide, and routine testing broke the second one. The local autosave is debounced by two seconds, so typing a few characters and submitting immediately never writes a draft — and therefore never reproduces this, whatever the post ID. Testing by hand tends to be far quicker than two seconds. Real students pause while they write.

Small local databases hid the other half: post IDs there are typically 10, 11, 12, which the old check happened to accept.

Verified both ways before release, on the same page and the same account: with the previous code at post 258 the draft survived and was restored into the next entry; with the fix at post 260 it was cleared and the form came up empty.

Upgrading

Download activity-builder-1.4.2.zip below and install it over your existing copy — it unpacks to activity-builder/, so WordPress treats it as an upgrade rather than a second plugin. (The auto-generated Source code archives unpack to a version-named folder and will install alongside your current plugin instead of replacing it.)

Cached assets are versioned by file timestamp, so students pick up the corrected script on their next page load without a hard refresh.

One thing worth knowing: a student who already has a stale draft stored from before this release will see the "Draft restored" banner once more, since the leftover copy is still in their browser. Discard & start fresh clears it for good, and it will not come back.

v1.4.1 — Visibility gets its own place in My Submissions

Choose a tag to compare

@nikolaigauer nikolaigauer released this 06 Aug 18:01

A presentation pass over the visibility control introduced in 1.4.0. No behaviour or permission changes — what students and instructors can do is identical. Safe to install over any 1.3.x or 1.4.0.

Visibility now has its own place in the card

In 1.4.0 the control shipped as a third button in the row: View · Make public · Edit, all styled identically. Two of those take you somewhere; the third changes who can read the student's writing, and nothing in the design said so. The two directions looked the same too, though publishing is consequential and outward while making something private again is a safe retreat.

The state was scattered as well — a "Private" tag in the meta line, a sentence below it, and a button on the right were three separate elements describing one thing.

Visibility now sits in its own band at the foot of each submission card, mirroring the Instructor Feedback band already there: the state, what it means for who can read the work, and the control that changes it, together in one place. View and Edit stay above as navigation.

  • The status tag has moved out of the meta line — the band is now its only home, so it appears for every submission including drafts, not only the ones a student can change.
  • Publish to my blog is a full button; Make private is a quiet text button. Neither should read as the default, but the outward step is the one worth pausing over.
  • The label names the destination rather than an abstract state. Students already "submit" work, so "publish" on its own invites confusion.

Consistent wording

A single source now defines what each status means for who can read a submission, so the My Submissions band and every post-submission confirmation screen describe the same state identically instead of drifting apart.

The wording stays accurate under any site configuration. What "published" actually reaches depends on the site's privacy setting and the student's own portfolio setting, so the note describes the boundary it can be sure of — your blog — rather than asserting a reach it cannot verify.

Under the hood

Styling moved out of inline attributes into the plugin stylesheet, using the existing status colour tokens, which already matched the four states one for one.

v1.4.0 — Students choose whether their own work is public

Choose a tag to compare

@nikolaigauer nikolaigauer released this 06 Aug 17:36

Students can now decide whether their own submitted work is public. Builds on the visibility fixes in 1.3.1. No database changes — safe to install over 1.3.0 or 1.3.1.

Students can publish or unpublish their own submissions

Submission Privacy on an activity page decides the status a submission starts in. Until now that was also where it stayed: a student whose work landed as Private had no way to put it on their blog later except WordPress's Quick Edit — which means knowing Quick Edit exists, and that "Private" is a checkbox hidden inside it.

My Submissions now shows a Make public / Make private button on each submission the student is allowed to change. This is the intended workflow: an instructor sets an activity to Private by default, and the student chooses later whether to publish it to their portfolio blog.

Nothing new was granted to make this work. WordPress already gives an Author full control over their own posts; this only surfaces it somewhere they will actually find it.

Publishing asks for confirmation. Making something private again does not, because that direction is always recoverable.

What students cannot do

  • Work awaiting instructor review stays awaiting review. A submission in Pending Review cannot be self-published — that would defeat the review step entirely. Only an instructor approving it changes that.
  • Unsubmitted drafts are unaffected. Visibility is not a question until work is handed in.
  • Only their own work. Ownership, capability and status are all re-checked when the change is submitted, not merely when the button is drawn.

Smaller changes

  • Published submissions now say "Published to your blog." alongside the existing Private and Pending notes. With students moving work between the two, both directions should state plainly what they mean.
  • When Approve is refused, the explanation now points to Quick Edit rather than the block editor, whose status control is genuinely hard to find.

v1.3.1 — Students can view their own private work; Approve no longer publishes it

Choose a tag to compare

@nikolaigauer nikolaigauer released this 06 Aug 17:19

Two visibility and privacy fixes found during production testing. No new features, no database changes — safe to install over 1.3.0.

Students can view their own private and pending submissions

A student could see their private submissions listed in My Submissions but had no way to open them. The same was true of work awaiting instructor review.

This was never an access problem — WordPress has always let a post's own author read their private posts, and the theme's privacy gate does not apply to logged-in users. The plugin simply rendered no link for anything that was not published, and the post-submission success screen pointed only at the student's author archive, where private and pending work correctly never appears. Both routes dead-ended.

  • Private submissions now offer View; submissions awaiting review offer Preview.
  • Each says who can see it, so "why isn't this on my blog?" answers itself.
  • After submitting, students get a direct View your submission → link plus All my submissions →, instead of an archive link that could not show their work.
  • Unsubmitted drafts are unchanged — they still offer Continue.

The author archive is deliberately untouched. It is the student's public face, so private and pending work should not appear there; My Submissions is the private route, and that is what needed fixing.

Approve can no longer publish private or draft submissions

Approve performed an unconditional publish on any submission. One click on a private submission turned confidential student work public, silently overriding the Submission Privacy setting chosen on the activity page. Drafts — work a student has not handed in — could be published the same way.

Approve is now limited to submissions genuinely awaiting review. The check runs in the request handlers as well as on the buttons, so a replayed form cannot bypass it. Refusals are explained in the interface and point to the block editor for a deliberate status change.

Instructors giving feedback on a private or draft submission still have their feedback saved as normal; only the publish is refused.

Note for instructors: the Approve button no longer appears on private submissions. To publish one deliberately, open it in the block editor and change its status there.

v1.3.0 — Save as Draft, room to write, and a safer submission gate

Choose a tag to compare

@nikolaigauer nikolaigauer released this 05 Aug 23:31

Student-facing release focused on removing friction before the first real cohort: drafts, room to write, and a submission flow that doesn't hand students off to an editor they've never seen.

Save as Draft

Students can now save unfinished work to the site and come back to it on another day or another device — the affordance anyone used to the WordPress post editor expects.

  • Drafts stay invisible to the instructor until submitted.
  • Saving a draft keeps you on the form with your text intact, so "save and keep writing" isn't interrupted.
  • Submitting a draft promotes it to whatever status the activity page is configured for.
  • Work that has already been submitted can never be pushed back to draft — the button is withheld, and the handler refuses it regardless.
  • Required fields don't block saving a draft. A draft is meant to be incomplete.

Every surface that could mistake a draft for a submission was corrected: the "you have already submitted" notice, the Progress grid (drafts get a hollow marker, not a check), the Submissions list (no Approve on unsubmitted work), and My Submissions (a Continue button rather than Edit).

Room to write

  • Auto-growing response fields — grow to fit the content up to a ceiling, then scroll internally. A fixed six-row box is fine for a paragraph and hostile for an essay.
  • Live word count on every prompt, not only when the instructor set a limit.

Security: submissions now require the right role

The submission handler previously checked that a user was logged in and a member of the site, but never checked a capability — so a Subscriber could submit work. Since visitors are auto-provisioned as Subscribers on these sites, this was live exposure rather than a theoretical gap.

Submitting now requires edit_posts (what the Author role grants and Subscribers lack), enforced on the request handler, both render paths, and the function that creates the post.

A clearer path back to your own work

  • My Submissions is now the top-level student menu, with New Post beneath it. "Did my work go through?" is asked far more often than "start a new post."
  • Edit links point at the activity form, not the block editor. A student clicking Edit on their own post used to land in Gutenberg, seeing their answers as raw blocks with the prompt structure invisible.
  • The block editor is not taken away: a Block editor action sits alongside Edit in the Posts list, it's untouched for administrators, and anyone who opens one there gets a link back to the activity form.

Fixes

  • A submitted entry's text no longer bleeds into the next one. The browser-local autosave was never cleared after a successful submit, so on activity pages that allow multiple entries the previous entry's text was restored into the next one. The draft key is now derived from server-provided values rather than from the form element, which does not exist on the confirmation screen — the reason an earlier attempt at this fix didn't take.
  • "Submit another" is now "Start another entry" — it always led to a blank form, but the old wording read as re-sending the same work.

Verified with 84 assertions across six isolated test harnesses covering the draft state machine, render states, role gating, edit-link behaviour, private-submission access, and the client-side draft lifecycle.

v1.2.2 — Prompt styling on archive/author views

Choose a tag to compare

@nikolaigauer nikolaigauer released this 03 Jul 21:51

Fix

Instructor prompt styling (.reflsub-prompt-label — the bold/italic/coloured text that visually separates prompt/activity text from a student's own answer) was missing when a submitted post was viewed from an archive rather than its single-post permalink — most visibly the ePortfolio theme's /author/ and /portfolio/ views.

Cause: the reflsub-form stylesheet was enqueued only on is_singular('post'). Those archive views render the full post content (including the prompt-label markup) but aren't singular, so the CSS never loaded and the styling was stripped.

Fix: reflsub_maybe_enqueue_form_style() now also enqueues on is_author(), is_home(), and is_archive() — i.e. anywhere post content can appear. (Not caused by any recent theme change; the enqueue condition had always been too narrow.)

v1.2.1 — Activity Page duplicator

Choose a tag to compare

@nikolaigauer nikolaigauer released this 05 Jun 22:51

Activity Page duplicator

Adds a Duplicate button to the Actions column on Activity Builder › Activity Pages.

Instructors can now build one good activity page and clone it as a template instead of remaking similar pages by hand.

What it does

  • Clones the activity definition only (sections, prompts, privacy, upload toggles, content-type, student-blocks setting) — never student submissions.
  • Lands as a Draft titled … (Copy) with the parent preserved, so you edit before publishing.
  • Success notice links straight into the builder for the new draft.

Notes

  • Nonce-protected; requires the edit_pages capability.
  • Copies modern builder meta plus legacy ACF-era keys, so pages of any vintage duplicate correctly.
  • _reflsub_sections is copied byte-identical (no JSON-meta corruption).
  • Actions column widened to fit the new button.

v1.2.0 — In-browser audio recording

Choose a tag to compare

@nikolaigauer nikolaigauer released this 05 Jun 20:58

Second feature release since v1.1.0. Headline: in-browser audio recording, available everywhere students compose. No breaking changes — DB meta keys, the [reflection_form] shortcode, and the internal reflsub_ prefix are unchanged, so existing installs upgrade in place. Still zero required dependencies, no build step.

Added

  • Audio recording (record in the browser). Students record a spoken response with their device microphone using the native MediaRecorder API — no plugins, no external service, fully self-hosted. Single Record/Stop toggle, a Re-record button, a live timer with a configurable max length (default 5 min), and inline playback to review before submitting. Recordings save as a Gutenberg wp:audio block.
    • Instructor Audio section — add a required-or-optional audio prompt in the Activity Page builder.
    • Student Tools palette — students can add their own Audio block ("+ Add → Audio") alongside Paragraph/Image/Video/Embed/PDF.
    • New Post builder — an Audio section in the standalone post composer.
  • Upload fallback. Every recorder also exposes a real file picker — for students who can't record in their browser, or already have an audio file ready.

Fixed

  • Em dash / curly apostrophe corruption in prompts. // were being mangled to u2019/u2014 at save time (and a literal " could wipe a page's whole sections array). Root cause: update_post_meta() unslashes internally while wp_json_encode() emits \uXXXX escapes. Fixed by wrapping JSON-meta writes in wp_slash( wp_json_encode( … ) ).
  • Student Tools no longer flatten structured pages. Enabling Student Tools previously dropped the instructor's designed media slots; structured sections now always render in order, with the palette appending optional extras.
  • Content Type field works without pre-existing terms — it's now a combobox that resolves or creates the term on the fly.

Changed (internal)

  • Front-end form JavaScript externalized into assets/js/ and enqueued, instead of inlined in shortcode output. This removes a class of bug where WordPress's wptexturize corrupted inline && into &#038;&#038; (a < in the JS confused its <script> detection). The audio recorder is a single shared widget reused across all three surfaces. Assets are cache-busted by file mtime.

See ROADMAP.md for what's next (edit-mode media retention; Trix rich-text in submissions, v1.3.0).

v1.1.0 — Activity Builder rename, PDF, Entry Title, content-type tagging

Choose a tag to compare

@nikolaigauer nikolaigauer released this 29 May 15:46

First feature release since the initial v1.0.0 cut. The plugin was renamed from its earlier working title to Activity Builder in this cycle, and the section-based builder gained several new section types. No breaking changes — DB meta keys, the [reflection_form] shortcode, and the internal reflsub_ prefix are all unchanged, so existing installations upgrade in place without migration.

Added

  • Section type: PDF / File upload. Students can attach a PDF (≤ 15 MB) inside the structured prompt. Renders as a Gutenberg wp:file block with download button.
  • Section type: Entry Title. Students can name their submission; falls back to a date-stamped title when "Multiple Submissions" is enabled.
  • Content-type auto-tagging. Activity pages can be tagged with a content type that propagates to every submission. Honours the theme's content-type taxonomy when present.
  • Student block palette. "+ Add Paragraph / Image / Video / Embed / PDF" buttons in the submission form let students extend their post with additional blocks beyond the instructor's structured prompts.
  • Drag-drop image upload zone for student image blocks, with thumbnail previews, individual removal, and edit-mode round-trip (existing images are kept by default, removable per-thumbnail).
  • Per-page "Student Content Builder" toggle in the Activity Page builder so instructors can enable/disable the student-added blocks palette on a per-prompt basis.
  • Activity page intro now renders as a styled card above the form.

Changed

  • Plugin renamed to "Activity Builder" (was previously a working title). Main plugin file renamed to activity-builder.php; admin menu slug is activity-builder. The internal reflsub_ prefix and _reflsub_* meta keys are intentionally unchanged to keep the upgrade non-breaking.
  • Submissions are now saved as Gutenberg block markup (wp:paragraph, wp:image, wp:gallery, wp:embed, wp:file) rather than raw HTML. Posts edit cleanly in Gutenberg and round-trip through the editor without losing structure.
  • "Resubmission" renamed to "Multiple Submissions" with clearer description copy throughout the builder.
  • Admin menu structure reorganized: Activity Pages → Submissions → New Activity Page → Progress → Feedback → Setup.

Fixed

  • Images preserved across edit-mode round-trip in structured prompts. Editing the text of a submission that contained an image previously dropped the image silently from the post. The kept-image rebuild path now runs whenever an image section is present, not only when a new upload arrives.
  • Prompt label styling (italicized, weight 600) now lifts the prompt question off the response text on rendered posts.
  • Setup wizard now uses the correct default privacy on newly created child pages.

Removed

  • ACF dependency. The plugin no longer requires Advanced Custom Fields. All field configuration is read/written via get_post_meta() / update_post_meta() against the same meta keys ACF used, so existing data is unaffected.

Install

Download Source code (zip) below, then in WordPress admin go to Plugins → Add New → Upload Plugin and select the zip. Existing 1.0.0 installations can upgrade in place.

Requirements

  • WordPress 6.0+
  • PHP 8.0+