Releases: AmirHosseinMoloudi/mammute_result_lookup
Release list
1.7.0
1.7.0
Added optional direct HTTPS certificate URLs throughout participant editing, public rendering, imports, exports and backups.
Download plugin 1.7.0 (dist/mammute-result-lookup-1.7.0.zip)
Certificate source behavior
- Valid direct URL takes priority over local media.
- Both values remain saved; clearing the URL restores local-media behavior.
- Without either source, existing no-certificate behavior remains.
- An unavailable remote image shows fallback guidance and keeps its links.
Admin UI
Added a labeled URL input beside the existing media picker, with concise precedence/privacy hints and field-level validation. Invalid submissions retain entered values. Persian RTL, keyboard interaction and narrow layouts were tested.
URL validation and security
HTTPS only, maximum 2,048 bytes. Rejects credentials, malformed URLs, controls, unsafe schemes and protocol-relative URLs. Valid path/query/fragment encoding is preserved.
Output is escaped. External actions retain opener/referrer protections. No server-side fetching, DNS lookup, mirroring or proxying was added.
Public rendering
Same-site and external JPEG/PNG/WebP paths receive responsive previews. Other HTTPS resources receive links without embedded execution. Invalid legacy URLs fall back to local media.
Search, publication, privacy and certificate-resolution checks remain intact. Certificate URLs are not searchable.
Import/export/backup compatibility
Format 2 adds certificate_url to CSV, Excel 2003 XML and JSON backups.
- Version 1 merge imports preserve existing URLs.
- Version 1 restores reproduce the original empty-URL state.
- Explicit blank version 2 values clear URLs.
- Import and restore use canonical URL validation.
- Backups retain references, not remote files.
Files changed
Modified:
- PLAN.md — implementation decisions.
- README.md — operation, compatibility and recovery guidance.
- readme.txt — release metadata and disclosures.
- mammute-result-lookup.php — version and privacy guidance.
- includes/class-domain.php — canonical URL validation.
- includes/class-store.php — storage migration and persistence.
- includes/class-frontend.php — certificate-source resolution.
- includes/class-spreadsheet.php — versioned file compatibility.
- includes/class-bulk.php — legacy import preservation.
- includes/class-admin.php — URL input direction.
- includes/admin-view.php — URL control and hints.
- includes/admin-records.php — source status.
- assets/admin.js — reset URL when adding another participant.
- assets/lookup.css — responsive direct-image frame.
- languages/mammute-result-lookup-fa_IR.po — Persian translations.
- languages/mammute-result-lookup-fa_IR.mo — compiled translations.
- tests/integration.php — future-schema fixture.
- tests/bulk-integration.php — malformed-header fixture.
- tests/bulk-browser.mjs — format 2 fixtures.
- tests/directory-unit.php — current-version fixtures.
- tools/package.py — release target.
- tools/verify.py — certificate suites.
Created:
- tests/certificate-unit.php — URL boundary tests.
- tests/certificate-integration.php — migration/source/data tests.
- tests/certificate-browser.mjs — admin/public browser tests.
- docs/certificate-url-review.md — governance audit and verification ledger.
- dist/mammute-result-lookup-1.7.0.zip — installable package.
Data and compatibility
Added one empty-default column through schema migration 2. Existing records and attachment IDs remain compatible. No search indexes, shortcode parameters or widget contracts changed. Older manual clients preserve an omitted URL.
Performance
No remote server requests or dependencies added. Source selection adds bounded local validation; existing batch processing remains unchanged.
Verification
Passed:
- Unit suites: domain 38, domain/URL 61, search 52, public 96, directory/CSV 1,020.
- Full tools/verify.py runs on WordPress 6.5 and 7.1.
- Final certificate integration: 35 checks on each version.
- Certificate browser: 18 English and 19 Persian RTL checks.
- PHP/JavaScript syntax, PHP 7.4 compatibility checks, msgfmt --check, git diff --check.
- Reproducible packaging, source/archive comparison and exact ZIP installation.
- Plugin Check: 0 errors, 135 warnings, with triage recorded.
Command/results ledger and 100-rule review (docs/certificate-url-review.md)
Covered flows and edge cases
Verified local-only, URL-only, both sources, clearing, invalid schemes, encoded URLs, same-site selection, external preview/404 handling, imports, exports, legacy/current backups, migration failure/retry, publication rejection, mobile
widths, keyboard labels and Persian RTL.
Deployment notes
Back up the database and media first. Visit wp-admin after updating to run migration. Clear page/asset caches and permit intended external image hosts in CSP.
Deactivation retains data. Downgrading requires a full pre-upgrade database and matching plugin backup; do not edit the schema marker.
Remaining risks
- Remote availability, CSP and cross-origin download behavior depend on the host/browser.
- Safari, Firefox, real assistive technology and full database downgrade restoration were not tested.
- Plugin Check warnings remain triaged; WordPress.org approval is not guaranteed.
How to test
- Open a participant without a certificate.
- Paste a valid HTTPS image URL.
- Save.
- Verify its public result.
- Test an external URL.
- Test a same-site URL.
- Enter an unsafe URL.
- Confirm rejection preserves entered data.
- Select local media.
- Populate both sources; verify URL priority.
- Clear the URL; verify local fallback.
- Import certificate_url.
- Export and compare its value.
- Create a backup.
- Restore and verify URL preservation.
- Restore a version 1 backup on staging.
- Check publication and ambiguity protections.
- Confirm no server-side certificate fetch.
- Test narrow wp-admin layouts.
- Test keyboard and Persian RTL.
- Confirm existing local certificates still render.
1.5.0
Mammute Result Lookup
A standalone WordPress plugin for publication-approved participant results and
certificates. It provides private administrative records and a flexible participant
lookup component; it never creates a public participant directory.
Requirements and installation
- WordPress 6.5+, PHP 7.4+, MySQL/MariaDB with CREATE/ALTER and
GET_LOCKsupport. - No Composer, Node, ACF, theme edits or search plugin required at runtime.
- Elementor is optional. The native widget uses
elementor/widgets/register; tested
with Elementor 3.32.0. ACF is not required; the plugin uses its own canonical data tables. - Install
dist/mammute-result-lookup-1.5.0.zipthrough Plugins → Add New → Upload,
then activate. Or copy the runtime plugin folder intowp-content/plugins/. - Activate separately on each multisite site. Network activation is deliberately
rejected. Storage is site-prefixed; activation does not iterate other sites. - Use HTTPS in production. Test with the actual site's caching, proxy, theme and
Elementor configuration before publishing.
Administrative workflow
Open Participant results (requires manage_options, normally administrators).
Create a record with an event key, full name and mobile number. Optional fields are
team/group, score, placement, evaluation summary, certificate identifier and a media
attachment. Score and placement are text so event-specific grading works; 0 is
preserved. Empty optional fields are omitted from the card. Summaries are plain text.
Choose an existing event/team from the editable suggestions, or type a new value.
Suggestions come from saved records, are capped at 30, and narrow as you type. Teams
are scoped to the selected event. Changing the event does not silently change the team.
Score and placement deliberately remain text: grades and arbitrary placements are valid.
Choose a certificate with the native Media Library picker. Preview/remove controls are
inline; Use a media ID exposes the existing numeric reference (0 means none).
Evaluation prose and embedding help use expandable sections. Publication approval stays
visible and required; all result fields are public-facing, never confidential notes.
Save and add another saves without leaving the page, keeps only event/team in the
current form, clears all participant-specific fields/media/approval, and focuses the
next name. Save participant keeps the saved record open. No batch selections or
participant details are written to browser storage. Pending saves lock the controls.
Validation errors preserve entered values and focus the relevant field. A network
failure can leave the write outcome unknown: check saved records before retrying.
Duplicate mobiles within an event and stale edits remain server-authoritative errors.
Without JavaScript, normal form submission works and validation failures retain the
form; adding another retains the event through the redirect, but not the team.
The saved-participant list has an exact event filter, 20-row pagination, masked mobile
numbers, a certificate-reference status and an explicit Edit participant action.
File selected indicates a stored reference, not continuous file availability; the
edit form and public renderer validate the actual file. On narrow screens rows become
cards. Delete remains a separate disclosure with explicit permanent-delete confirmation;
it removes only the result, never media.
Event keys contain 1–64 lowercase ASCII letters/digits/hyphens/underscores, beginning
with a letter or digit. Each event permits one record per normalized mobile number;
the same person may have a separate record in another event. Event keys are public
page scopes, never passwords. There is no score threshold or participant data in code.
Spreadsheet data management
Open Participant results → Data management (or its shortcut above the individual
form). Download a template or current data, edit it, upload once, inspect the preview,
then confirm. Manual editing remains available. Bulk operations support 500 stored
records per site, 500 incoming rows, and 2 MiB per file/canonical dataset.
This deliberate bound permits a single recoverable request; sites above it retain the
manual workflow and should use a separately planned migration/export process.
Formats:
- UTF-8 comma-separated CSV, with optional BOM. Headers must match exactly; order may
change. Export adds a BOM and an apostrophe to every nonempty value, including the
version marker. This prevents formula interpretation and protects leading zeros.
Keep the prefixes when editing exported CSV; the importer removes exactly one when
format_versionis'1. Raw templates useformat_version=1. Keep identifiers as
Text in your spreadsheet editor. If it rewrites prefixes, use Excel XML instead. - Excel 2003 XML SpreadsheetML (
.xml): text cells, one contiguous worksheet. The
template sets Text formatting for all columns. Save back as XML Spreadsheet 2003,
keeping text cells. Empty cells and bounded cell-index gaps are supported; row gaps,
merged cells, hyperlinks, formulas, DTD/entities and nontext cells are rejected.
This is not XLSX. No XLS/XLSX/ZIP/macro parser or spreadsheet library is bundled. - Versioned JSON (
.json) is reserved for full data backups/restores. Its checksum
detects accidental corruption, not authenticity. Use trusted plugin-generated backups.
Canonical column order (all represented as strings):
format_version,event,full_name,mobile,team,score,placement,summary,certificate_id,attachment_id,public_consent
format_version is 1; event, full_name, mobile and public_consent=1 are
required. attachment_id=0 means no certificate. Optional text may be empty; numeric
zero is retained. Score/placement remain open text, including grades. Publication
approval must be explicit in the file and in confirmation; importing cannot make
unapproved records. Mobile normalization is identical to manual entry. A phone whose
leading zero was stripped is rejected rather than guessed. Changing an event/mobile
key creates a new record: use individual editing to deliberately change an identity.
Safe merge is the only ordinary import mode. Matching is exact by normalized
(event,mobile). Missing records are created, changed matches updated, unchanged
matches not rewritten, and records absent from the file left untouched. New valid
event keys need no separate definition. Optional event scope requires every accepted
row to use that event. Every occurrence of a duplicate key in the file is rejected,
including a duplicate with another invalid field. Unknown/missing/duplicate columns,
unsupported versions, malformed CSV/XML and incompatible backups stop file processing.
Row errors are reported by spreadsheet row number without repeating private values;
the screen shows the first 20 and offers all errors as protected CSV. Preview counts
include create/update/unchanged/invalid/conflict/delete and normalization warnings.
Confirmation and recovery. The server retains the canonical plan, never trusts a
browser-reconstructed dataset, and compares all stored rows/versions against preview.
Any intervening manual/bulk change requires a new preview. Confirmation saves a full
pre-operation recovery backup first. Valid merge rows are then applied in one InnoDB
transaction; invalid/conflict rows are skipped. Unexpected execution failure rolls back
all batch writes, including earlier successful rows. Each changed row uses the same
Store validation and versioned write service as manual editing. Matching is preloaded
once; media metadata is primed together. There is no per-row browser request or queue.
The transaction pins its MySQLi connection to prevent wpdb reconnect/retry from silently
resuming in autocommit. Execution checks a 20-second budget between mutations; hosting
must allow normal database calls and at least a 30-second request. A lost commit reply
can have an uncertain outcome: inspect current records/recovery before retrying.
Completed confirmation replays are harmless; an interrupted/failed attempt is not
blindly restarted. Upload again after reviewing its result and recovery snapshot.
Backup and restore. One click downloads all plugin business data across events,
including approval, certificate identifiers and attachment references. Backups contain
format/version/scope, canonical records and an integrity checksum. They exclude counters,
nonces, name hashes, internal IDs/versions/timestamps, temporary reports and unrelated
WordPress data. Events are implicit in records; there is no separate business settings
model. A data backup is not a media/site backup. Back up uploads and the WordPress
attachment database separately. Restores require compatible, accessible attachment IDs
on the receiving site and never fetch remote files. Missing attachments must be repaired
before restoring an intact backup; ordinary import rows with invalid media are rejected.
Restore always previews a full synchronization across all events: absent records
are deleted, existing keys updated, missing keys created. Every input row must validate;
any invalid/duplicate row blocks confirmation. An empty valid backup explicitly previews
deleting all records. The confirmation text names replacement/deletion and publication.
A pre-restore backup is saved before mutation. Canonical values are restored; IDs and
concurrency versions are operational references and are not restored. Download the
recovery backup and preview it as a restore to undo a completed operation.
Private artifacts and limits. Uploads use PHP temporary files, not the Media Library,
and are deleted after parsing. One preview/report and one latest recovery snapshot per
administrator/site live in non-autoloaded private...