=== GKQuizz Marks Manager ===
Contributors: gkquizz
Tags: exams, marks, school, education, grades
Requires at least: 5.8
Tested up to: 6.6
Requires PHP: 7.4
Stable tag: 1.6.1
License: GPLv2 or later

Frontend-first Student Marks Collection & Examination Management for GKQuizz.in.

== Description ==

Subject teachers submit marks online through a unique, secure link - one link per
class + section + subject + examination. The Examination In-Charge manages
students, classes, sections, subjects, examinations and teachers, locks/unlocks
submissions, and generates printable / exportable reports. Everything happens
on the WordPress frontend via shortcodes; no daily use of wp-admin is required.

= Features =
* Custom database tables for classes, sections, subjects, examinations, students, teacher assignment links, marks, and an audit log.
* Secure, unique tokenised marks-entry links per class/section/subject/exam.
* Draft -> Submit -> Final Submit workflow with admin lock/unlock.
* Student import from .csv or .xlsx (no external libraries required).
* Manual add/edit/delete for students, classes, sections, subjects, examinations.
* Teacher accounts are real WordPress users (role: Subject Teacher), so login, sessions, and password resets use WordPress core security.
* Printable, browser-based "PDF" reports (Print > Save as PDF) and CSV export that opens correctly in Excel/Google Sheets/LibreOffice.
* Nonce verification, capability checks, and prepared SQL statements throughout.

= Shortcodes =
* `[gkqm_admin_dashboard]` - Examination In-Charge control panel (admin-only).
* `[gkqm_marks_entry]` - Teacher marks entry page (opened via the unique link you generate per assignment).
* `[gkqm_teacher_login]` - Standalone login form.
* `[gkqm_marks_print]` - Printable / exportable reports (admin-only).
* `[gkqm_student_search]` - Quick student lookup (admin-only).

== Installation ==

1. Upload the `gkquizz-marks-manager` folder to `/wp-content/plugins/`.
2. Activate the plugin through the "Plugins" screen in WordPress.
3. Go to "Marks Manager" in the wp-admin sidebar for a one-time setup checklist.
4. Create three pages and add one shortcode to each:
   - "Exam Office" -> `[gkqm_admin_dashboard]`
   - "Teacher Marks Entry" -> `[gkqm_marks_entry]`
   - "Reports" -> `[gkqm_marks_print]`
5. From the Exam Office page: add classes/sections/subjects, import students, create teacher accounts, and generate a marks-entry link per class+section+subject+exam. Share each link with the relevant teacher.

== Notes on PDF / Excel ==

To keep the plugin a single, dependency-free folder (no Composer, no extra
server software required on your host), reports are produced as:
* **Print-ready HTML** for PDFs - use the browser's Print dialog and choose
  "Save as PDF". This looks correct on any host without extra setup.
* **CSV** for "Excel Export" - opens natively and correctly formatted in
  Excel, Google Sheets, and LibreOffice Calc.

If you later want native binary .pdf/.xlsx generation on the server (e.g. via
mPDF or PhpSpreadsheet through Composer), the `GKQM_Reports` class is the
single place to extend - see `includes/class-gkqm-reports.php`.

== SaaS Licensing (selling this to multiple schools) ==

This plugin can be sold as a monthly/yearly subscription. It talks to a
companion plugin called "GKQuizz License Manager" that you install on ONE
site you control (your billing server) - schools only ever install this
plugin, never that one.

1. Install & configure "GKQuizz License Manager" on your own site (see its
   own readme for the Razorpay setup steps).
2. In `gkquizz-marks-manager.php`, change the `GKQM_LICENSE_SERVER_URL`
   constant to that site's URL before distributing this plugin to schools.
3. Each school activates their license from wp-admin -> Marks Manager ->
   License, using the key they received by email after subscribing. The
   moment activation succeeds, the plugin automatically creates their first
   Exam In-Charge account (using the school name/email they subscribed with)
   and both displays it once on that screen AND emails it to them - no
   wp-admin -> Users step needed. They can create more Exam In-Charge
   accounts later from the dashboard's own "Exam In-Charge Accounts" tab.
4. If a school's subscription lapses, the dashboard/marks-entry/reports show
   a "please renew" notice instead of an error - their data is never deleted,
   just locked until they renew.

== Security ==

* All state-changing requests require a valid WordPress nonce.
* Admin actions require the `gkqm_manage` capability (granted to Administrators on activation).
* Teacher marks entry requires the `gkqm_teacher_access` capability AND that the logged-in user matches the token's assigned teacher.
* All database queries use `$wpdb->prepare()`.
* All output is escaped with `esc_html()`/`esc_attr()`/`esc_url()`.
* File uploads for student import are restricted to .csv/.xlsx by MIME/extension check.

== Changelog ==

= 1.6.1 =
* Students tab: the "Search / Manage Students" table now shows an Edit button next to Delete for every student, letting you change their roll number, name, father's name, class and/or section (i.e. move them into a different class) directly from the same form used to add students - no separate re-import needed. Class and Section columns now show the actual class/section names instead of raw internal IDs.

= 1.6.0 =
* Fixed oversized, hard-to-fill Sessional sheet and Max Marks matrix: columns were stretching to fit long header text (e.g. "VARIABLE % VALUE (ROUNDED UP)") even though the actual input boxes stayed tiny, wasting huge amounts of horizontal space and making entry tedious. Both are now compact, fixed-width data-entry grids where the input boxes fill their columns, with a sticky header while scrolling through long class lists. The settings bar above the Sessional sheet is now a tight grid instead of a sprawling row.

= 1.5.6 =
* Sessional Marks: the Variable % Value (exam contribution) and the final Total Sessional Marks are now rounded UP to the next whole mark instead of showing decimals - e.g. 7.2 becomes 8, 7.0 stays 7. Applied consistently on the Sessional sheet, its PDF/Excel export, and the Result Card's Sessional column.

= 1.5.5 =
* Fixed the actual Excel export crash: it called wp_tempnam(), a WordPress admin-only function (lives in wp-admin/includes/file.php) that isn't loaded on frontend requests - which is exactly the context exports now run in since 1.5.3's fix. Switched to plain PHP tempnam(), with a CSV fallback if the system temp directory is ever unavailable. Verified end-to-end in the same "no wp-admin loaded" conditions that caused the crash.

= 1.5.4 =
* The 1.5.3 download intercept required detecting the [gkqm_marks_print] shortcode inside the page's saved content, which doesn't work for every page-building setup (page builders, template-embedded shortcodes, widgets). Removed that requirement - the intercept now runs for any export request and safely no-ops if it isn't actually a valid one.
* If an export ever does fail now, instead of WordPress's generic "critical error" screen you get the actual PHP error message on screen (for Exam Office/Admin accounts) plus a copy written to the server's PHP error log - so a real fix can be made instead of guessing.

= 1.5.3 =
* Fixed the actual cause of "critical error" on Excel export and corrupted/unopenable PDF downloads: file downloads were being streamed from inside the_content() (a shortcode), which runs after the theme has already sent HTML output to the browser - calling header() and echoing binary data at that point corrupts the response (the PDF ends up with the page's own HTML prepended to it, which is exactly what "file could not be opened / wasn't correctly decoded" means) and can fatal on some hosts for Excel. Downloads now intercept on template_redirect, before any theme output begins, so the response is clean binary from the first byte. Also added an output-buffer-clearing safety net in the PDF/Excel/CSV writers themselves as defense in depth.

= 1.5.2 =
* Fixed A4 print sizing: the @page rule now registers as a proper stylesheet inline style (in <head>, via wp_add_inline_style) instead of a raw <style> tag inside the shortcode's own content - some themes/WordPress content filters can strip or mangle a <style> tag sitting in post content, which is the likely cause of print not matching A4 for some sites. Also updates live in <head> when switching Portrait/Landscape.
* wp-admin: new "Troubleshooting" section with a one-click "Re-sync Database Tables" button and a Plugin Version vs Database Version indicator, in case an earlier automatic upgrade ever silently failed to add a newer version's tables/columns.

= 1.5.1 =
* Full responsive pass across the dashboard, marks-entry, and report pages: proper spacing/sizing at tablet and phone widths, wide tables scroll horizontally with a visible scrollbar instead of breaking the layout, buttons/forms stack cleanly on narrow screens.
* Print/report pages now have a Portrait/Landscape selector next to "Print" and "Download PDF" - applies to both the real PDF download and the browser's Print dialog (via @page). Page size is fixed to true A4 (210 x 297mm) either way.

= 1.5.0 =
* Sessional Marks tab redesigned into a single combined sheet per Class/Section/Subject: First Test, Second Test and Half Yearly are pulled in automatically from the exam Marks module (no re-entry), followed by an auto-calculated Total and an editable "Variable % of Total" that converts it into a contribution toward the sessional score. Attendance, Project, Behavioural, Plantation and a Manual entry column are entered right there too - each with an editable Max Marks - and a Total Sessional Marks column sums everything automatically. Result Cards' Sessional column now reflects this same combined total.

= 1.4.1 =
* Teacher portal: visiting the bare marks-entry page while logged in now shows a list of every subject/class/exam assigned to that teacher, each opening straight into its entry grid - instead of a generic "use your link" message. The entry page itself now links back to this list.

= 1.4.0 =
* Subjects: Max Marks matrix cells can now be split into Written + Oral marks (e.g. Half-Yearly = 35 Written + 15 Oral = 50), matching the official mark-distribution format. Teachers then see two entry boxes for that exam instead of one, with the total calculated automatically. Reports/exports for that subject show Written, Oral and Total as separate columns.
* Full visual redesign of the frontend (dashboard, marks-entry, login, reports) into a professional ERP-style look: card panels, hover feedback on tables/buttons, consistent spacing and status badges, responsive layout for mobile. No functional/markup changes required - CSS only.

= 1.3.0 =
* Teachers are now kept entirely inside the plugin's own frontend portal: any attempt to reach wp-admin redirects straight back to the marks-entry page, the WordPress admin toolbar never shows for a teacher account, and login always returns to the page they came from.
* Fully self-service, on-page password reset for teachers: "Forgot your password?" and "set a new password" both happen on the same frontend page - the reset email links back there too, never to wp-login.php.

= 1.2.1 =
* Teachers & Links: Delete button for marks-entry links, so a link (and any marks recorded under it) can be removed - this is what un-blocks deleting a subject that already has links against it.

= 1.2.0 =
* Subjects: per-subject, per-exam Max Marks matrix (Unit Test/Half-Yearly/Annual etc. can each have a different Max Marks for the same subject) - editable from the "Subjects" tab.
* Subjects: Edit button (rename / change fallback Max Marks) alongside the existing Delete.

= 1.1.0 =
* New Sessional Marks module (Attendance/Behaviour/Plantation/Project/Manual components, weighted by %, editable per subject).
* New Result Card Setup (map exams to Unit Test/Half-Yearly/Annual, pass %, division cut-offs) plus class-wise Result Sheets and per-student Result Cards.
* Genuine downloadable PDF and Excel (.xlsx) exports for every report (no external library dependency).
* Teacher accounts: password reset (with automatic email) and one-click "Email Link" for any marks-entry link.

= 1.0.0 =
* Initial release.
