Skip to content

abouterf/PersonaPress

Repository files navigation

PersonaPress

WordPress plugin for personality assessments — MBTI, DISC, EQ, and Bar-On — with results tracking, shareable result links, and a customizable frontend UI.

Requirements

  • WordPress 6.0+
  • PHP 8.1+
  • Composer (for installing dependencies)

Installation

  1. Copy or clone this folder into wp-content/plugins/PersonaPress.

  2. Install PHP dependencies:

    composer install --no-dev

    For local development:

    composer install
  3. Activate PersonaPress in the WordPress admin under Plugins.

Usage

Shortcode

Embed an assessment on any page or post:

[personapress type="mbti"]

The legacy shortcode [personality_test] is also supported.

Attribute Default Description
type mbti Assessment slug: mbti, disc, eq, bar-on
show_progress global Show the progress bar (true / false)
show_header global Show the assessment header (true / false)
title Custom header title
description Custom header description
option_layout global auto, scale, list, pills, or cards
questions_per_page global 0 = all questions on one page
redirect URL to redirect to after completion

Example:

[personapress type="disc" show_progress="true" questions_per_page="5"]

Admin

After activation, open PersonaPress in the WordPress admin:

  • Dashboard — overview, shortcode copy buttons, quick links
  • Results — completed sessions and CSV export
  • Questions — edit assessment questions
  • Result Profiles — customize result copy per profile
  • Settings — global colours, typography, behaviour, and per-assessment style overrides

Sharing results

When sharing is enabled in settings, users can copy a public link after completing a test. Shared URLs use the pp_result query parameter:

https://yoursite.test/?pp_result=TOKEN

Project structure

personapress.php          Plugin bootstrap
bootstrap.php             Autoload and init hook
src/                      PHP application code
templates/                PHP view templates
assets/                   Frontend and admin CSS/JS
languages/                Translations (text domain: personapress)

Development

Composer scripts

composer test      # PHPUnit (tests/Unit)
composer analyse   # PHPStan (level 8)

Test coverage focuses on core domain logic: questions, scoring, settings resolution, repositories, helpers, and shortcode error handling. Run vendor/bin/phpunit from the plugin root (uses phpunit.xml.dist).

Translations

Persian (fa_IR) strings are maintained in languages/fa_IR-translations.php. Rebuild the .po and .mo files with:

php languages/build-fa-IR.php

Extending

PersonaPress is built around a small assessment registry, template layer, and WordPress hooks. You can add custom tests, override data, swap templates, and hook into the full request lifecycle without forking core files.

Register a custom assessment

Extend PersonaPress\Assessment\AbstractAssessment and add your class via pp_default_assessments:

<?php
use PersonaPress\Assessment\AbstractAssessment;
use PersonaPress\Result\Result;

final class MyCustomTest extends AbstractAssessment {

    public function slug(): string {
        return 'my-test';
    }

    public function name(): string {
        return __( 'My Custom Test', 'my-plugin' );
    }

    public function description(): string {
        return __( 'A short description shown in the assessment header.', 'my-plugin' );
    }

    protected function define_questions(): array {
        return [
            [
                'id'        => 'q1',
                'dimension' => 'MAIN',
                'text'      => __( 'I enjoy solving puzzles.', 'my-plugin' ),
                'type'      => 'likert',
                'options'   => [
                    1 => __( 'Strongly Disagree', 'my-plugin' ),
                    2 => __( 'Disagree', 'my-plugin' ),
                    3 => __( 'Neutral', 'my-plugin' ),
                    4 => __( 'Agree', 'my-plugin' ),
                    5 => __( 'Strongly Agree', 'my-plugin' ),
                ],
            ],
        ];
    }

    public function default_result_profiles(): array {
        return [
            'high' => [
                'title'       => __( 'High scorer', 'my-plugin' ),
                'description' => __( 'You scored above average.', 'my-plugin' ),
            ],
            'low' => [
                'title'       => __( 'Low scorer', 'my-plugin' ),
                'description' => __( 'You scored below average.', 'my-plugin' ),
            ],
        ];
    }

    protected function compute_result( array $answers ): Result {
        $avg = (float) ( $answers['q1'] ?? 3 );
        $key = $avg >= 3 ? 'high' : 'low';
        $profile = $this->default_result_profiles()[ $key ];

        return new Result(
            key:         $key,
            title:       $profile['title'],
            description: $profile['description'],
            scores:      [ 'MAIN' => $avg ],
            meta:        [ 'score_labels' => [ 'MAIN' => __( 'Overall', 'my-plugin' ) ] ],
        );
    }
}

add_filter( 'pp_default_assessments', function ( array $assessments ): array {
    $assessments[] = new MyCustomTest();
    return $assessments;
} );

Embed it with [personapress type="my-test"].

AbstractAssessment handles answer sanitization, scoring hooks, and question caching. Implement:

Method Purpose
slug() Unique key used in shortcodes and the database
name() / description() Header copy on the frontend
define_questions() Built-in question definitions
default_result_profiles() Built-in result titles and descriptions keyed by result key
compute_result( $answers ) Scoring logic; return a Result object

Question format

Each question in define_questions() accepts:

Field Type Notes
id string Unique within the assessment
text string Question label
type string likert, radio, checkbox, or text
options array Choice labels keyed by value (required for likert/radio/checkbox)
dimension string Grouping key used during scoring
required bool Default true
reverse_scored bool Flip likert values on a 1–5 scale

Admin-edited question overrides are stored in the pp_questions_{slug} option and merged on top of built-in definitions.

Override questions programmatically

add_filter( 'pp_question_overrides_mbti', function ( array $overrides ): array {
    $overrides['ei_01']['text'] = 'Custom wording for question 1.';
    return $overrides;
} );

Use pp_question_overrides for all assessments, or pp_question_overrides_{slug} for one. Filters run when questions are loaded; admin saves go through pp_save_question_overrides.

Override result copy

Result profile text can be customized in PersonaPress → Result Profiles, or via filters:

add_filter( 'pp_result_copy_overrides_mbti', function ( array $overrides ): array {
    $overrides['INTJ']['title'] = 'My custom INTJ title';
    return $overrides;
} );

Built-in profiles come from each assessment’s default_result_profiles() method.

Modify scoring or results

Hook into the scoring pipeline per assessment or globally:

Filter When
pp_sanitize_answers Raw POST answers are normalized
pp_{slug}_before_score Answers before dimension scoring (per assessment)
pp_before_score Answers before scoring (all assessments)
pp_{slug}_result Result object after compute_result()
pp_result Final Result before it is stored or returned
pp_result_array Serialised payload sent to the frontend / REST API
add_filter( 'pp_mbti_result', function ( $result, $answers, $assessment ) {
  // Adjust titles, scores, or meta before display.
  return $result;
}, 10, 3 );

Template overrides

Copy any file from templates/ into your theme:

wp-content/themes/your-theme/personapress/assessment/wrapper.php
wp-content/themes/your-theme/personapress/result/placeholder.php

Theme templates are checked before plugin templates. Use pp_locate_template_paths to add more search locations, or pp_template_data / pp_template_data_{name} to inject variables.

Available templates:

Template Used for
assessment/wrapper Main shortcode shell
assessment/question Single question row
result/placeholder Result card (live and SSR)
result/shared Public shared result page
admin/* Dashboard, settings, results, questions

REST API

Namespace: personapress/v1

Method Route Description
POST /sessions Create a session (assessment slug required)
POST /sessions/{id}/submit Submit answers (answers object required)
GET /results/{token} Fetch a shared result by guest token

Access to create/submit endpoints requires a logged-in user or the Allow guests setting. Use pp_rest_can_access to customize that check. Use pp_rest_result_response to alter the JSON returned after submission.

Register additional routes on pp_register_rest_routes.

Frontend configuration

pp_frontend_config exposes the ppConfig object used by assets/js/assessment.js (REST URL, colours, feature flags). pp_frontend_i18n controls user-facing button labels.

add_filter( 'pp_frontend_i18n', function ( array $i18n ): array {
    $i18n['submit'] = 'Show results';
    return $i18n;
} );

Enqueue extra styles or scripts on pp_enqueue_frontend_assets.

Settings and presentation

Filter Purpose
pp_settings_defaults Default option values
pp_settings Merged settings read from the database
pp_sanitize_settings Values saved from the admin settings form
pp_assessment_palettes Default colour palettes per slug
pp_assessment_styles Resolved colours and behaviour for rendering
pp_assessment_name / pp_assessment_description Frontend header copy
pp_assessment_wrapper_attrs HTML attributes on the assessment root element
pp_assessment_inline_style CSS custom properties for theming
pp_font_stacks / pp_font_family_key Typography
pp_result_share_url Public share link URL

Shortcode

Filter Purpose
pp_shortcode_tag Change the shortcode name (default personapress)
pp_shortcode_defaults Default attribute values
pp_shortcode_atts Parsed attributes before render
pp_shortcode_output Final HTML output

Actions: pp_before_shortcode_render, pp_after_shortcode_render.

Lifecycle hooks

Plugin boot

Hook Arguments
pp_before_boot $plugin
pp_before_register_assessments $registry, $defaults[]
pp_registry_loaded $registry
pp_booted $plugin
pp_activate / pp_deactivate

Sessions and REST

Hook Arguments
pp_session_created $session_id, $slug, $user_id
pp_before_save_answers $answers, $session_id
pp_before_score_session $answers, $session_id, $slug
pp_assessment_completed $result, $session_id, $slug
pp_session_complete_data $data, $session_id, $result

Frontend render (in templates/assessment/wrapper.php and result/placeholder.php)

Hook Arguments
pp_before_assessment / pp_after_assessment $assessment, $atts
pp_before_assessment_header / pp_after_assessment_header $assessment, $atts
pp_before_question / pp_after_question $question, $index, $assessment
pp_before_question_content / pp_after_question_content $question, $index
pp_before_assessment_form / pp_after_assessment_form $assessment, $atts
pp_before_assessment_nav / pp_after_assessment_nav $assessment, $atts
pp_before_result / pp_after_result $assessment
pp_before_result_score_row / pp_after_result_score_row $dim, $score, $result
pp_after_result_scores $result
pp_before_result_actions / pp_after_result_actions $result (optional)
pp_before_shared_result / pp_after_shared_result $session, $result_data

Admin

Hook Arguments
pp_admin_init $admin_loader
pp_admin_menu $admin_loader
pp_enqueue_admin_assets $hook_suffix
pp_before_admin_{screen} / pp_after_admin_{screen} Varies by screen (dashboard, results, questions, result_copy, settings)
pp_before_save_questions / pp_after_save_questions $slug, …
pp_before_reset_questions / pp_after_reset_questions $slug
pp_before_save_result_copy / pp_after_save_result_copy $slug, …
pp_before_reset_result_copy / pp_after_reset_result_copy $slug
pp_dashboard_before_stats / pp_dashboard_after_content $registry, $total
pp_settings_before_form / pp_settings_after_form $settings
pp_results_before_table / pp_results_after_table $rows, $filter_slug, $total

Data retrieval

Filter Purpose
pp_session_row Single session row by ID
pp_session_row_by_token Session row by guest token
pp_results_rows Paginated admin results list
pp_results_count Results count for admin pagination
pp_results_export_rows CSV export rows
pp_session_create_data Data used when inserting a new session

Quick reference

A compact index of every hook lives in src/Hooks/HookReference.php. When adding new hooks in core, update that file so extension authors have a single source of truth.

License

GPL-2.0-or-later. See LICENSE.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors