WP Manifestindependent plugin directory
manifest / editor / interactive-quiz-poll

Interactive Quiz & Poll

Native WordPress quiz & poll Gutenberg blocks built on the Interactivity API — a free, self-hosted alternative to third-party embeds. Server-validated responses, per-post tallies, results dashboard with CSV export. No external requests.

★ 0stars

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/dhanendran/interactive-quiz-poll/archive/refs/heads/main.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

First-party quiz and poll Gutenberg blocks built on the native WordPress Interactivity API — a free, self-hosted replacement for third-party quiz/poll embeds (Typeform, Opinion Stage, PollDaddy).

Everything runs on your own site: content is authored as blocks, votes are recorded in your database, and every response is validated and tallied on the server.

Features

  • Poll block — one question, several answers, a live aggregate breakdown after voting, and an optional explanation.
  • Quiz block — one or more questions with correct/incorrect answers, per-question explanations, a "your answer" marker, a score result card, and an optional (editor-gated) retake.
  • Answer explanations — shown after responding, and scopeable to any / only-correct / only-incorrect answers, so a quiz can give different feedback for a right vs. wrong choice. Served from the server after answering, so correct answers never leak into the page source.
  • Author once, embed anywhere — quizzes and polls live in their own admin area (custom post types) and drop into any post/page via an embed block. The questions are a single source of truth; responses are tracked per embedding post, so the same quiz on two posts keeps two independent tallies.
  • Results for editors — a Results panel on the edit screen, plus a dedicated Results admin page with a per-post breakdown ranked by volume and a CSV export.
  • Server-authoritative — the browser only sends which answer was picked; correctness, tallying and results HTML are decided on the server against the quiz's own block tree.
  • Race-safe counts — atomic SQL increments, so concurrent responses never lose updates.
  • Privacy-friendly abuse guards — per-browser de-duplication (a random first-party cookie) and per-IP rate limiting using salted, expiring hashes (the raw IP is never stored); optional logged-in-only voting.
  • No external requests, no tracking, no upsell walls.

Architecture

Editor (React)                Storage                        Frontend (Interactivity API)
quiz  ─ question ─ answer  →   CPT: d9qp_quiz / d9qp_poll  →  click answer → POST {refId, contextId, ...}
poll  ─ question ─ answer      post meta: atomic counts,      server validates IDs against the quiz's
quiz-embed / poll-embed ──────→ reference a CPT by ID         keyed per embedding post → returns results

refId is the quiz/poll being answered (used to validate IDs against its authored blocks); contextId is the post it's displayed on (used to scope the tally). Counters live on the CPT keyed by the display post, so one quiz embedded on many posts keeps a separate tally per post, plus an overall total for the admin.

The trust boundary: the client is untrusted and near-stateless. It never sends totals, correctness or markup — only which answer was chosen. Validation, tallying, correct-answer lookup and results HTML all happen on the server.

Blocks

Block Role
interactive-quiz-poll/quiz Multi-question container (rendered in the quiz CPT)
interactive-quiz-poll/poll Single-question container (rendered in the poll CPT)
interactive-quiz-poll/question A question wrapping its answers + optional details
interactive-quiz-poll/answer One answer option (with isCorrect for quizzes)
interactive-quiz-poll/question-details Feedback revealed after responding (server-rendered on demand)
interactive-quiz-poll/quiz-embed / poll-embed Drop a saved quiz/poll into any post by ID

REST endpoints (interactive-quiz-poll/v1)

Each request carries a contextId (the display post) in the body; {id} in the path is the quiz/poll.

Route Purpose
POST /poll/respond/{id} Record a vote, return the per-post breakdown
POST /quiz/respond/{id} Grade one answer, return correctness + explanation + attempt token
POST /quiz/complete/{id} Record a finished attempt (gated by the attempt token)

Development

npm install
npm run build      # or: npm run start

Blocks are built with @wordpress/scripts. The frontend stores are script modules (viewScriptModule), so the build sets WP_EXPERIMENTAL_MODULES=true (handled by the npm scripts). Compiled output lands in build/ (git-ignored) and is what WordPress registers.

Symlink the plugin folder into a WordPress install's wp-content/plugins/ to test.

Filters

Filter Default Purpose
d9qp_abuse_guards_enabled true Master switch for dedup + rate limiting
d9qp_rate_limit_per_minute 30 Per-IP request cap per minute
d9qp_dedup_window DAY_IN_SECONDS Cooldown for "one response per question, per post, per browser"
d9qp_require_login false Require a logged-in user to respond

Define D9QP_DELETE_DATA_ON_UNINSTALL as true (e.g. in wp-config.php) to remove all quizzes, polls and responses on uninstall. Data is kept by default.

License

GPL-2.0-or-later


Built by Dhanendran Rajagopal · D9 Labs