- Bug: Fixed critical regression where saving an existing survey deleted all its questions permanently. The save handler now validates the incoming question JSON before deleting existing data; if the payload is invalid the save is aborted with an error instead of wiping the survey.
- Bug: Fixed question and answer text being partially stripped on save. sanitize_text_field() (which removes HTML tags) was replaced with wp_kses_post() for question and answer content, preserving HTML formatting in quiz questions and answer labels.
Security Fixes (internal audit):
- Critical: Added current_user_can('manage_options') capability check to manual plugin updater; previously any WP user could trigger a plugin overwrite.
- Critical: Removed print_r($this->temp_path) debug statement disclosing the absolute server filesystem path on the Update admin page.
- Critical: Added version downgrade protection to manual updater; uploading an older plugin version is now rejected.
- Security: Added esc_html() around version strings and esc_html() around changelog content read from uploaded ZIP in the updater.
- Security: Changed throw_error() to use wp_kses_post() instead of raw echo.
- Security: Added ['allowed_classes' => false] + is_array() guard to all unserialize() calls on participant .custom field data in settings_participants.php, settings.php (send_admin_email, send_autoresponse), and all four export modules (csv, xls, txt, xml).
- Security: Added wp_verify_nonce() CSRF protection to all three participant delete operations (delete_samesession, delete_incomplete, bulk delete_participants).
- Security: Escaped survey name with esc_html() in settings_savedforms.php survey title output.
- Security: Wrapped all custom-field configuration properties ($cf->id, $cf->name, $cf->warning, $cf->minlength, $cf->position) with esc_attr() in the survey editor.
- Security: Replaced plugin_dir_path() server path disclosure in import error message with a safe relative path.
Security & Bug Fixes:
- Critical: Fixed logic bug where assignment operator was used instead of equality check for survey style detection, causing all surveys to incorrectly use the 'click' branch.
- Critical: Fixed path traversal vulnerability in campaign module loader; allowed module names are now strictly whitelisted.
- Critical: Sanitized cookie-sourced survey_viewed arrays with absint() before use in SQL IN clauses to prevent injection via crafted cookie values.
- Security: All plugin cookies (ms-uid, ms-session, modal_survey) now set secure=is_ssl() and httponly=true, including legacy fallback setcookie() calls.
- Security: Replaced insecure mt_rand()-based session ID generation with cryptographically secure bin2hex(random_bytes(16)).
- Security: Escaped featured image URL with esc_url() in Open Graph meta output.
- Security: Fixed unsanitized direct $_COOKIE['ms-uid'] access in ajax_survey_back; now uses sanitize_text_field/wp_unslash with isset guard.
- Security: Reduced export directory permissions from 0777 to 0755.
- Bug: Fixed incorrect number_format call in survey_answers average-score path; third argument was the decimal count instead of the decimal separator, producing garbled output.
- Bug: Fixed undefined variable warnings in survey_open_answers, survey_records, and survey_compare_chart shortcode handlers when results are absent or labels/sessions are not set.
Completion Flow & PDF Export Improvements:
- Refactored survey completion flow to separate lightweight answer saving from mail/PDF finalization, reducing timeout risk for large exports.
- Added client-side canvas snapshot capture: survey result charts are now captured as base64 PNG data before mail/PDF generation begins.
- Implemented new `finalize` AJAX endpoint in settings.php to trigger mail and PDF exports after charts have been fully rendered on the client.
- Enhanced front-end chart initialization with MutationObserver fallback to handle late-injected markup and delayed script loading gracefully.
- Updated modal_survey.js to track pending completion mail state and conditionally defer mail sending until after chart rendering completes.
- Improved modal_survey_answer.js with `window.ModalSurveyCaptureChartData()` helper to extract chart canvases as base64 payloads for PDF embedding.
- Result charts in generated PDFs now display the exact chart rendered to the user, eliminating missing or placeholder images.
Documentation & Compatibility:
- Split completion request into two lightweight phases, improving reliability on servers with stricter timeout limits.
- Chart images are now properly embedded in PDF exports by capturing rendered Chart.js canvases on the client side.
- Bootstrap logic for answer charts is now resilient to DOM delays and late script injection, common in page builders and AJAX-heavy themes.
- Added a retry-based bootstrap loop (polling with timeout) instead of immediate execution.
- Added dependency checks before init: jQuery exists, plugin method exists (pmsresults or modalsurvey),
localized params exist (ms_answer_init_params or ms_init_params),
for answer init, waits for Chart only when chart styles are present,
- Added graceful timeout with console warning instead of throwing hard errors.
- Kept DOMContentLoaded-safe startup for both scripts.
- Added fallback mounting to body if .entry-content is missing (answer init).
- New Helper Method: Introduced a utility to safely read server variables using wp_unslash for improved data handling.
- Security Refactor: Replaced raw REQUEST_URI usage with the new helper method during constructor URL assembly to ensure consistent sanitization.
- Refactored Request Handling: Introduced helper methods to standardize $_REQUEST access.
- Code Cleanup & Modernization: Replaced raw $_REQUEST reads with helper methods
- Security & Consistency: Implemented consistent wp_unslash() and string sanitization across touched paths.
- Logic Simplification: Streamlined sspcmd=displaychart conditionals for improved readability without altering functional behavior.
- fix PHP 8.2 deprecation notices
- hardened the main AJAX answer flow
- replaced deprecated utf8_encode usage with a safe UTF-8 normalization helper
- declared legacy status properties to prevent dynamic-property deprecation warnings