WP Manifestindependent plugin directory
manifest / forms / quiz-wp

WP Lead Capture Quiz

Plugin to make simple 2-answers quizzes for WordPress

by WP Quiz Team · github.com/chriscarias/quiz-wp · website

0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/chriscarias/quiz-wp/archive/refs/heads/main.zip

Readme

WP Lead Capture Quiz

A native WordPress plugin that lets administrators create dynamic, interactive quizzes to capture leads. Results are gated behind a GDPR-compliant lead capture form, and lead data is stored in a dedicated WordPress custom table so it can be exported and managed from the admin dashboard.


Table of Contents

  1. Features
  2. Installation
  3. How It Is Made (Architecture)
  4. How It Works
  5. File Structure
  6. Custom Database Table
  7. Usage
  8. REST API Reference
  9. Development & Testing

Features

  • Custom Quiz Builder – Create unlimited questions with multiple answers right on the post edit screen.
  • Answer Weights – Each answer carries an integer weight (points) that drives result scoring.
  • Optional Images – Add images to answers and result brackets via the WordPress Media Library.
  • Result Brackets – Define outcomes with title, description, image, and a min_score/max_score range.
  • GDPR Lead Gate – Scores are hidden until the visitor submits name, email, and consent.
  • Vanilla JS Frontend – Instant next/previous transitions with CSS class toggling (zero jQuery on the frontend).
  • REST API Submission – A single POST request handles scoring, lead capture, and result delivery.
  • Leads Management – Paginated, sortable, searchable admin table built on WP_List_Table.
  • CSV Export – One-click, filtered download of all captured leads.

Installation

From a ZIP file (recommended)

  1. In the WordPress admin, go to Plugins → Add New → Upload Plugin.
  2. Choose wp-lead-capture-quiz.zip and click Install Now.
  3. Click Activate.

Activation creates the custom wp_quiz_leads database table automatically.

Manual install

  1. Upload the wp-lead-capture-quiz folder to wp-content/plugins/.
  2. Activate the plugin from the Plugins screen.

How It Is Made (Architecture)

The plugin uses a hybrid frontend/backend architecture to stay fast on the client side while keeping scoring and data capture secure on the server.

Core concepts

  • Custom Post Type (wp_quiz) – Every quiz is a CPT entry. This gives you the native post editor, publishing workflow, and admin-styling for free.
  • Serialized JSON config in post_meta – Questions, answers, weights, and result brackets are stored as a single serialized JSON array under the _wp_quiz_config meta key. This data is always read and written together, so a heavyweight relational schema would be unnecessary overhead.
  • Custom leads table (wp_quiz_leads) – Leads are high-volume, frequently queried, and better served by a dedicated table than river-of-posts meta.
  • REST API endpointPOST /wp-json/wp-quiz/v1/submit is the single server-side entry point for scoring and storing leads.
  • Class-based organization – Each responsibility lives in its own class hooked into WordPress actions/filters.

Module breakdown

Class Responsibility
WP_Quiz_Activator Creates the wp_quiz_leads table on plugin activation (via dbDelta).
WP_Quiz_CPT Registers the wp_quiz CPT, renders the Quiz Builder meta box, and persists the JSON config.
WP_Quiz_Leads_Table WP_List_Table subclass – pagination, sorting, search, quiz filter, bulk delete.
WP_Quiz_Leads_Admin Registers the Leads admin submenu and handles filtered CSV export.
WP_Quiz_Shortcode Registers [lead_quiz id="N"], enqueues frontend assets, and renders the quiz DOM.
WP_Quiz_REST_API Validates submissions, scores answers, writes leads, matches brackets, and returns result HTML.

The hybrid data flow

  1. Initial load (frontend): The shortcode renders all questions and answers into the DOM. No result logic or scoring data ever reaches the browser.
  2. Interaction (frontend): Vanilla JavaScript toggles CSS classes to move between question steps instantly.
  3. Gate (frontend): After the last question the visitor reaches a lead capture form (name, email, GDPR consent).
  4. Submission (backend): On submit, one POST request goes to the REST API with the quiz ID, lead info, consent flag, and the array of selected answer IDs.
  5. Resolution (backend): The server sanitizes input, computes the total score from answer weights, matches it against the result brackets, persists the lead, and returns HTML for the earned result.

How It Works

1. Admin: Building a quiz

  1. Go to Quizzes → Add New Quiz, give it a title, and open the Quiz Builder Configuration meta box.
  2. Click + Add Question and fill in the question text.
  3. Each question has at least 2 answers. For each answer set:
    • Answer Text – the option label.
    • Weight (Points) – the integer score awarded when this answer is chosen.
    • Image (Optional) – pick a media library image.
  4. Add any number of Result Brackets. Each bracket defines:
    • Title & Description
    • Optional Image
    • Min Score / Max Score – the score range that maps to this outcome.
  5. Publish the quiz. The meta box shows the ready-to-use shortcode.

2. Frontend: Visitor taking the quiz

  • The shortcode renders a progress bar, a series of question steps, and a final form-gate step.
  • Visitors click an answer card to select it, then Next. Back returns to the previous step. Transitions are pure CSS class toggles, so navigation is instant.
  • The visitor never sees or interacts with scoring data.

3. The GDPR gate & results

  • After the final question the visitor must provide Name, a valid Email, and tick the GDPR consent checkbox.
  • The browser POSTs the answer IDs to the REST API and shows a loader.
  • The server calculates the score, stores the lead, picks the matching result bracket, and returns rendered result HTML, which the frontend drops into the container.

4. Admin: Managing leads

  • Under Quizzes → Leads all captured leads are listed in a paginated table.
  • Filter by a specific quiz, search by name/email/result, and sort by the sortable columns.
  • The Download CSV button exports the currently filtered result set directly as .csv.

File Structure

wp-lead-capture-quiz/
├── wp-lead-capture-quiz.php              # Main plugin entry, bootstraps all classes
├── includes/
│   ├── class-wp-quiz-activator.php       # Activation: creates wp_quiz_leads table
│   ├── class-wp-quiz-cpt.php             # CPT + Quiz Builder meta box + saving
│   ├── class-wp-quiz-leads-table.php     # WP_List_Table for leads
│   ├── class-wp-quiz-leads-admin.php     # Admin menu page + CSV export
│   ├── class-wp-quiz-shortcode.php       # [lead_quiz] shortcode + asset loading
│   └── class-wp-quiz-rest-api.php        # POST /wp-json/wp-quiz/v1/submit
├── admin/
│   ├── css/admin-quiz-builder.css        # Quiz Builder meta box styling
│   └── js/admin-quiz-builder.js          # Add/remove questions, answers, media picker
└── public/
    ├── css/public-quiz.css               # Frontend quiz styles
    └── js/public-quiz.js                 # Vanilla JS: steps, selection, submission

Custom Database Table

Created on activation with dbDelta():

Table: wp_quiz_leads (prefixed on multi-site installs)

Column Type Notes
id BIGINT UNSIGNED, PK, AUTO_INCREMENT Unique lead ID
quiz_id BIGINT UNSIGNED Post ID of the wp_quiz CPT (KEY quiz_id)
name VARCHAR(255) Lead name
email VARCHAR(255) Lead email
score INT Computed total score
result_id VARCHAR(255) Matched result bracket title/ID
consent_given TINYINT(1) 1 = GDPR consent granted
created_at DATETIME Submission timestamp

Quiz configuration (serialized JSON in post_meta, key _wp_quiz_config):

{
  "questions": [
    {
      "id": "id_abc123",
      "text": "How often do you blog?",
      "answers": [
        { "id": "id_def456", "text": "Daily", "weight": 3, "image": "https://..." },
        { "id": "id_ghi789", "text": "Rarely", "weight": 1, "image": "" }
      ]
    }
  ],
  "results": [
    {
      "id": "id_jkl012",
      "title": "You're a Blogging Pro!",
      "description": "...",
      "min_score": 10,
      "max_score": 20,
      "image": "https://..."
    }
  ]
}

Usage

Embed any published quiz with:

[lead_quiz id="123"]

Replace 123 with the quiz's post ID. The shortcode enqueues the frontend stylesheet and script only when a quiz is actually present on the page.


REST API Reference

POST /wp-json/wp-quiz/v1/submit

Public endpoint used by the frontend during the submission step. Requires a valid WordPress REST nonce in the X-WP-Nonce header.

Request body (JSON):

{
  "quiz_id": 123,
  "name": "Jane Doe",
  "email": "jane@example.com",
  "consent_given": true,
  "answers": [
    { "question_id": "id_abc123", "answer_id": "id_def456" },
    { "question_id": "id_ghi789", "answer_id": "id_jkl012" }
  ]
}

Response 200 OK:

{
  "success": true,
  "score": 7,
  "result": "You're a Blogging Pro!",
  "html": "<div class=\"wp-quiz-result\">...</div>"
}

Common error responses:

HTTP Error code Meaning
400 missing_params Empty request body
400 invalid_quiz_id Missing/invalid quiz ID
404 invalid_quiz Quiz post not found
400 missing_name Name is required
400 invalid_email Email invalid or missing
400 missing_consent GDPR consent not given
500 db_error Failed to insert lead row

Development & Testing

PHP syntax can be lint-checked on any machine with PHP installed:

for f in wp-lead-capture-quiz.php includes/*.php; do php -l "$f"; done

Rebuilding the installable ZIP

# From the project root
mkdir -p /tmp/opencode/wp-lead-capture-quiz
cp -r wp-lead-capture-quiz.php includes admin public /tmp/opencode/wp-lead-capture-quiz/
cd /tmp/opencode && zip -r /home/demoniodojo/Projects/quiz-wp/wp-lead-capture-quiz.zip wp-lead-capture-quiz

The ZIP contains the wp-lead-capture-quiz/ folder at its top level, so WordPress can unpack it cleanly into wp-content/plugins/.


Notes

  • The frontend uses vanilla JavaScript – no frontend jQuery dependency.
  • All quiz scoring and bracket-matching logic stays server-side, so visitors cannot tamper with results.
  • result_id in the leads table stores the matched bracket's id value as currently implemented.
  • This plugin is GPLv2-or-later licensed.

Read the full README on GitHub →