=== Leadovation Aero Users ===
Contributors: wptechnology
Tags: flight school, roles, permissions, licences, aviation training
Requires at least: 6.3
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 4.1.2
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

The people file of a flight school: who is who, what each may do, and the credentials and expiry dates that say who may fly.

== Description ==

A flight school keeps a file on every person who walks through the door: student, pilot, instructor, examiner. The first thing that file has to answer is who is who, and what each of them is allowed to do.

That is what this release answers. It shares its roles and its registry with the other Aero extensions of the same range, so a school running several of them sees one directory, not one per plugin. It also holds the credentials of those people and their expiry dates, and shows each pilot where they stand on a page of the site.

= The directory =

Every account of the site on one screen, with the roles each person holds, when they last signed in, and a drawer that edits those roles one person at a time. Views by role, a search on name and e-mail, sorting by first or last name, and a bulk gesture that adds a role to everybody ticked, or takes it back.

**A role is added, never substituted.** A pilot who goes back to being a student for a type rating keeps the booking rights of a pilot, and a school that has its own WordPress roles keeps them untouched.

**Only the roles of the range are handed out here.** The screen never offers to make somebody an administrator: that belongs to WordPress, where the gesture carries its own warnings. It follows the same rule WordPress does, and asks for the same permission.

= The file of one person =

Open a person from the directory and their file opens in this plugin, not in the WordPress profile: first and last name, badge number, e-mail address, phone, date of birth, postal address, town, the person to call in an emergency, a photo, and the roles they hold, all on one page and saved in one gesture.

**The same screen adds a person.** It creates the WordPress account and lays down everything else at once, so a school does not have to create an account first and come back to fill in the file. The login is derived from the e-mail address, the password is drawn at random, and the standard WordPress welcome e-mail is offered rather than imposed.

**What WordPress already knows is written where WordPress keeps it.** First name, last name and e-mail address stay in the account itself: a person edited here stays readable by the theme and by the other extensions of the site.

= Fields the school defines =

A school always follows something we did not foresee: a membership number, the date a subscription was paid, a helmet size, the employer of a student on a company scheme. It adds the field itself, on a screen of its own, with a name it writes, a kind, a place in the order of the file, a flag when the field is expected, and who is allowed to see it.

**Expected flags, and never refuses.** A file with an empty expected field still saves, and simply says what is missing.

**A field kept to the school never reaches the public side of the site**, not in the "My details" block and nowhere else the person concerned can read it, even when the block names it explicitly.

**Switch a field off before deleting it.** Switching off takes it out of every screen and keeps what people have entered; switch it back on and everything is there again. Deleting says how many values leave with it.

What expires, or has a history, is not a field: it is a credential, and it belongs in the catalogue of tracked credentials, where it gets dates, reminders and a document.

= Roles and permissions =

The roles of the site, each with what it is allowed to do, in the words a school uses rather than in technical identifiers. A school creates its own roles by naming them, the identifier being derived rather than asked for, and five regulatory roles are offered in one click, with a stable identifier so an export stays readable in an audit.

**The grid is not written in advance.** Each extension of the range declares what it brings, with a readable sentence for every permission. Install another one and its column appears here, without this plugin knowing anything about it beforehand.

**Nothing disappears without a word.** A role that came from WordPress or from another plugin carries no delete button at all, and deleting one of yours asks first, saying how many people hold it and will lose it.

= Credentials and expiry dates =

A medical certificate, a type rating, a language proficiency, an instructor certificate: a school has to know what each person holds, until when, and who to chase this month. Both dates of the document are entered as they are printed on it, the examination and the expiry, and the expiry entered is the truth everywhere. **Nothing is worked out**: software that recalculates a date printed on a certificate is wrong the day the two differ, and it is wrong in front of an inspector.

**A panel for the wide gesture, a column for the series.** The panel opens beside the directory and holds one credential at a time: its two dates, its reference, who issued it, its class or level, the aircraft it is tied to, and its whole history. The pinned column of the directory takes an expiry in place, twenty in a row without leaving the list, which is how a school enters a batch of medicals with the paper in front of it.

**Nothing is ever overwritten.** Every entry adds an event and the previous one stays readable: a correction says why, replaces the effect of an earlier entry, and leaves it in the history. That is what makes the file answer the question an auditor actually asks, which is not "is this valid today" but "was this person qualified on the fourteenth of March".

**Expiring is not the only state.** A credential can be valid, coming up, inside its renewal window, expired, or suspended because something it depends on has expired: a medical certificate that has run out does not make a type rating expire, it suspends its exercise, and the two are not repaired in the same way at all. Unfold a row of the directory and each credential shows a band that runs from the last examination to the expiry, with the window marked and today on it.

= Which credentials your school follows =

The catalogue lists the types a school follows, with the family each belongs to, the word it uses when one comes up for renewal, what it depends on, and how many days before the expiry it wants to be reminded. Ten types come ticked for a helicopter school, and a school adds its own, renames what we ship, or unticks what it does not hold.

**Unticking a type takes it off the screens, never out of a file.** A school whose pilots keep their own medical certificates unticks that type and stops seeing its columns; the certificates already on file keep suspending what depends on them, because it is reality that suspends, not the display.

= The documents at the file =

A medical certificate, a licence, an attestation: a school keeps the scan of each at the file of the person it belongs to. Filing one is two clicks in the panel of a credential, and what is filed is shown back with its name, the day it was filed and its size, with two gestures, view and replace.

**A filed document never leaves the media library, because it never goes in.** A file of the media library is served by the web server at a public, stable, guessable address that nothing checks. A medical certificate carries a name, a date of birth and a licence number: it is not health data, it is a **named document**, and a named document is not left at an address people try. Documents live in a folder of their own, outside the uploads directory where the hosting allows it, closed to the web server, and each carries a random name on disk that says nothing of what it is.

**The plugin serves the file, never the web server.** A request carries the identifier of the document and nothing else, no path anywhere: the plugin checks, reads and serves it. Two cases and two only: you are the person the document belongs to, or you hold the permission to keep licences. There is no shareable link, no link that expires, no third case, and every refused request is written down, because a repeated attempt is the only signal a school will get.

**A date is saved without a document, always.** A document is expected, never required: a school entering twenty dates after a group medical does not have the twenty scans in front of it, and it will have them next week, or never, and the twenty dates are worth more than nothing.

**Nothing is ever deleted on its own.** No document expires, none is cleaned up after a year or when somebody leaves: how long a school keeps a document is the school's call. Deleting the plugin keeps them too, unless the school asks otherwise beforehand, in one tick box on the catalogue screen.

= Reminders by e-mail =

A licence expires on a day nobody remembers. The plugin writes to the person before it does, and to the school every week, and it does both without ever becoming the sender everybody filters.

**They are off until you switch them on, and the screen says what will go out first.** Not a warning, a number: "switching them on: 22 people receive a message at the next pass, covering 37 expiry dates". A school of four hundred accounts gets to see that before anything leaves, and two buttons send you a sample of each message, to your own account and to nobody else.

**One e-mail per person and per pass.** Somebody whose four credentials cross a threshold on the same morning receives one message listing the four, never four messages. The thresholds are an attribute of each credential type, ninety, thirty and seven days as delivered, and a school that wants to be warned earlier on a badge changes one line of its catalogue. The day after an expiry has passed, one last message goes out, and then nothing: chasing somebody daily about a credential they know is expired is the shortest road to a mail filter.

**A reminder never goes out twice**, and it is the database that holds that, not the accuracy of a cron window. WordPress only runs its cron when somebody visits the site, so a pass that missed a day or two catches up; beyond that a missed threshold stays missed, because saying "expires in ninety days" about a credential expiring in forty-five would be worse than saying nothing.

**The weekly summary is what keeps the pressure without the harassment.** One message per recipient and per week, on the day the school picks, listing the expiry dates of the next ninety days and the expiries that have passed and have not been settled, grouped by person and sorted by urgency. An expired credential stays on that list until it is settled, and leaves it the day it is, never because somebody clicked.

**Nothing leaves the school.** The recipients are the person concerned and the accounts the school ticks, named on the screen and chosen by permission rather than by a free address field. There is no address field, and there will not be one. No message carries a filed document, nor a link to one: an e-mail gets forwarded.

= The audit document =

An inspection asks for the state of a given day, and the export answers it: every person, every credential, its reference, who issued it, both dates, its state on that day, the last event and the day the school actually recorded it. In CSV for a spreadsheet, or as a page made to print, both carrying the name of the school and the day they describe.

**It is retrospective, and that is the whole point.** An entry recorded after the date asked for does not count, even when it carries an earlier date: on the fourteenth of March, the school did not have it, and that is what an auditor wants to know.

= Pages for pilots =

Five blocks, each with a shortcode of the same name, put a pilot's own situation on a page of the site: whether they can fly and what is missing when they cannot, their credentials and the state of each, the dates coming up, their own details, and, for an instructor or the office, how a group stands.

**A block only ever shows the person who is signed in.** No attribute names anybody else, there is none, and there will be none: a public page that took an identifier as a parameter would let anyone read someone else's file by changing a digit in the address. Signed out, a block invites you to sign in rather than showing an empty box.

**The answer never authorises a flight.** Every "can I fly" block carries, and cannot be made to drop, the sentence that says so: the summary repeats what the school has recorded, it does not replace checking your credentials before the flight, and it authorises nothing by itself.

= In your language =

The plugin is written in English and translated into French by hand, in the words a flight school actually uses. Translations come through translate.wordpress.org and install themselves; a school can also drop its own file in wp-content/languages/plugins/.

= What it bundles, and what it never calls =

The plugin calls no external service. Nothing about a school or its people leaves the site: no tracking, no usage reporting, no remote asset, no call to any address of ours or of anybody else's.

Its three type families, Archivo, Barlow Condensed and IBM Plex Mono, travel with it as WOFF2 files and are served from the site itself, never from a font network. All three are published under the SIL Open Font License 1.1, which is compatible with the GPL. Their licence and attribution are in `assets/fonts/LICENSE.txt`.

== Screenshots ==

1. The people of the school on one screen: roles, compliance at a glance, the next expiry of each person, and a pinned column of your choice.
2. A person's credentials unfolded from the directory: each one on its own timeline, the states in colour and in words, and the verdict of the day deduced from them.
3. The credentials the school tracks: validity, what happens at expiry, the reminders, all editable, with the starting profile of the school.
4. Reminders: a message to each person before an expiry, and a weekly summary to the office, nothing sent until the school switches it on.
5. Roles and permissions: the roles of the range, the regulatory roles suggested, and what each role may do in this plugin.
6. On the site, signed in: whether the pilot can fly today and why, and the state of each credential, in a block or a shortcode.

== Installation ==

1. Upload the plugin to `/wp-content/plugins/`, or install it from the WordPress plugin directory.
2. Activate it through the "Plugins" screen in WordPress.
3. Open "Aero Users" in the admin menu: the directory is the first screen, roles and permissions the second.

== Frequently Asked Questions ==

= Does it replace the WordPress users screen? =

No. An account edited here stays an ordinary WordPress account: what WordPress already knows is written where WordPress keeps it, and a link on every file opens the WordPress profile. This plugin adds what a school needs on top: who is who, and what each of them may do.

= Can I hand out the administrator role from the directory? =

No, in either direction. Roles that neither WordPress nor this plugin created are shown but never assigned or removed here.

= Do I need the other plugins of the range? =

No. Aero Users works on its own, and carries its own copy of the shared core. When another one of the range is installed, the two share their roles rather than each keeping a list of its own.

== Changelog ==

= 4.1.2 =
* Changing someone's e-mail address or name from a person file now also takes the WordPress permission to edit that account (edit_user), the same barrier as the Accounts screen; the school's own fields still save without it.
* The five front blocks and shortcodes pass their whole output through wp_kses_post() on return.
* The printable credential audit registers its stylesheet through the WordPress styles API.
* Every translatable string is a literal in the code that runs; the translation files leave the package, translate.wordpress.org distributes them.

= 4.1.1 =
* Ready for the WordPress.org directory: the code passes the WordPress coding standards with nothing left to explain. Posted integers and names are sanitised where they are read, not only downstream; the readme reads as paragraphs instead of one line break every eighty characters.

= 4.1.0 =
* **The two sentences the plugin used to add under everything a pilot reads are now the school's to keep, to cut, or to rewrite.** They still go out by default, word for word as before, so nothing changes for a school that never opens the screen. What changed is that they can be turned off, and that a school can write its own instead: the plugin says what a file holds and what is missing from it, and what that means for a flight is said by the school, on its own page.
* An empty box is not silence: it sends our sentence, with your school name in it. Only the tick box makes a sentence go away.
* The person file follows the order of the validated mockup again: badge number third, e-mail fourth. Since the order became a property of each field, the three fields that belong to WordPress rather than to us needed a place in that same order.

= 4.0.0 =
* **A school decides what a person file holds.** A membership number, the date a subscription was paid, a helmet size, the employer of a student on a company scheme: a school always follows something we did not foresee, and it can now say so itself, without writing to us and without opening a file.
* A field carries a name the school writes, a kind, a place in the order of the file, a flag when it is expected, and who is allowed to see it. Nine kinds are offered: one line of text, free text, a number, a date, yes or no, one value from a list the school writes, an e-mail address, a phone number, a web address.
* **Expected flags, and never refuses.** A file with an empty expected field still saves, and simply says what is missing. An arbitrary required field is a way back to the spreadsheet.
* **Three visibilities**, and the cautious one is the default. A field kept to the school never reaches the public side of the site, not in the "My details" block and nowhere else the person concerned can read it, even when the block names it explicitly.
* **Switch a field off before deleting it.** Switching off takes it out of every screen and keeps what people have entered; switch it back on and everything is there again. Deleting says how many values leave with it, and the number is read at that moment, never guessed.
* **Deciding the shape of a file is a permission of its own**, next to the five others. Defining a field does not change one file: it changes every file of the site, present and to come. Entering a value in one still takes the permission to edit a person file.
* **The eight fields the file already carried are now definitions like the others.** Badge number, phone, date of birth, postal address, town, the person to call in an emergency, the photo and the last sign-in: a school renames them and moves them, and only their name and kind stay as they are, because the plugin reads them by name.
* **Nothing entered is lost.** The values are carried over on update, and the old rows leave so there is one truth rather than two. What the rest of the site read keeps working: `get_user_meta( $id, 'aero_badge', true )` still returns the badge number, read from the plugin's own tables.

= 3.3.0 =
* **The journal can be read at last.** It had been filling since documents could be filed, and the only way to see a line of it was to open the database. A journal nothing reads is not a journal: the point of one is to exist on the day you need it, and you have to be able to open it that day.
* Three kinds of entry, one screen: a document asked for and refused, a message the site's mail service turned down, a sweep of documents past the period. They have nothing in common except the moment you go looking, which is when something is off and you do not yet know what.
* Why a document was refused is said here and nowhere else. What comes back to whoever asked never says whether the document exists; the journal is read by the school and by nobody else, so it can say.
* An account asking for a document that is not its own stands out from the rest: it is the only entry in this table that can be an attempt. An address tried with no account is more often an expired session.
* **Nothing is deleted from this screen, and there is no button to do it.** A journal you can empty from its own screen proves nothing on the day it would have to prove something.

= 3.2.0 =
* **How long documents are kept is now the school's to set, and the setting flags rather than deletes.** Past the period, a document is marked, stays readable and stays in the audit export. Nothing is ever deleted on a timer: a plugin that erases regulatory documents on its own, on a Sunday, with no witness, is the one thing it must not do.
* The period runs from the day a credential expired, not from the day the scan was filed: a certificate uploaded yesterday but out of date for four years is four years old. A credential still in force, or one with no expiry at all, is never past the period.
* **Sweeping is a hand on a button, and it names what it takes first.** The screen lists every document by person, by credential and by the day it expired, before any of them leaves. What leaves is the scan and nothing else: every credential keeps its dates and its history, so the audit export still answers what was valid on a given day.
* Every sweep is written down, with the count and the period, and without naming what it took.
* Nothing is limited by default, and the help says where the number comes from: your own manual, not ours. How long a document must be kept depends on the authority you answer to, and it differs between a student record and an instructor's.

= 3.1.0 =
* **The blocks and shortcodes are named in English.** These plugins are distributed worldwide, and an instructor in Norway does not type `[aero_mes_titres]`. The five blocks become `aero-users/my-status`, `my-credentials`, `my-expiries`, `my-profile` and `school-overview`, and their shortcodes follow: `aero_my_status`, `aero_my_credentials`, `aero_my_expiries`, `aero_my_profile`, `aero_school_overview`. Five for five, nothing added and nothing dropped.
* Their attributes are in English too, because they are words a person types into a shortcode: `fields`, `editable`, `title`, `passengers`, `count`, `days`, `empty`, `families`, `status`, `documents`, `columns`, `sort`. The CSS classes of the public markup follow the same words.
* Nothing else changes: the same five blocks show the same things, and every displayed sentence is still translated.

= 3.0.1 =
* The reminder greets a person by their first name, and no longer by the name the directory files them under. An account with no first name is greeted with no name at all, rather than by the name it logs in with.
* A credential that has passed its date is said in a whole sentence: what it is, how long ago, and what to do about it, with the word the catalogue uses. One that is approaching says the day from which it may be renewed, when its type has a window at all.
* Both messages carried two closing paragraphs saying much the same thing, one of them explaining why nothing is attached. One remains, the one that says the message replaces no check before a flight.
* The reminders screen said less than it wrote. What is left is the calendar, who receives what, and the two sample buttons.
* A message the site's mail refuses is written down. A reminder is marked as sent before it is handed over, so that a relay going down costs a missed message rather than three duplicates; the price is that a badly set up mail service swallows everything in silence. The line does not repair that, it makes it something the school can find.

= 3.0.0 =
* **Reminders by e-mail, and they are off until you switch them on.** The screen says the real number before anything leaves: how many people receive a message at the next pass, and how many expiry dates it covers. A school of four hundred accounts sees that first.
* **One e-mail per person and per pass.** Four credentials crossing a threshold on the same morning make one message listing the four. The thresholds come from each credential type, ninety, thirty and seven days as delivered, plus one last message the day after an expiry has passed, and then nothing.
* **A reminder never goes out twice**, and the database holds that rather than the accuracy of a cron window. A pass that missed a day or two catches up; beyond that, a missed threshold stays missed.
* **A weekly summary for the school**, on the day it picks, to the accounts it ticks. It lists the expiry dates of the next ninety days and the expiries that have passed and have not been settled, grouped by person and sorted by urgency. An expired credential stays on it until it is settled.
* **Two buttons send you a sample of each message**, to your own account and to nobody else, so a school can read what its pilots will receive before switching anything on.
* **No message carries a filed document, nor a link to one**, and no recipient is an address that does not belong to an account of the school. Two filters, `aero_users_reminder_message` and `aero_users_digest_message`, let a school rewrite either message entirely. There is no template editor and no template table.

= 2.0.1 =
* **The view button opens the document.** It answered 401 to everyone, the school's own manager included: the request went through an address WordPress only authenticates when a token travels with it, and a link never carries one. It now goes through the same door as the audit document, which authenticates by the session alone. Who may read what has not changed, and every refused request is still written down.
* The unfolded file no longer repeats "no document" on every line. On a real file of eleven credentials, ten said it, because nearly every tracked type expects one: ten identical reminders are no longer a reminder. What is missing is counted once, at the foot.
* The mark on a filed document reads as a sheet of paper rather than an empty tick box.

= 2.0.0 =
* **The documents of a file are kept in the file.** A medical certificate, a licence, an attestation: the scan is filed on the credential it belongs to, from the panel, and shown back with its name, the day it was filed and its size, with two gestures, view and replace. It is the one thing a school could not do here, and the reason a manager who tried the plugin kept the ring binder beside it.
* **A filed document never goes into the media library.** It lives in a folder of its own, outside the uploads directory where the hosting allows it, closed to the web server, and it carries a random name on disk that says nothing of what it is.
* **The plugin serves the file, never the web server.** A request carries the identifier of the document and no path at all. Two cases and two only: you are the person it belongs to, or you hold the permission to keep licences. No shareable link, no link that expires, and every refused request is written down.
* PDF, JPEG and PNG, up to ten megabytes or the limit of the server if it is lower. What a file actually is decides, never what it is called.
* **A date is still saved without a document**, and a document is filed even when the date beside it is refused: neither ever blocks the other.
* The unfolded file marks a document that is on file and opens it, and marks one that is expected and missing without raising the alarm. The audit document's "document on file" column reaches its three answers at last. The `pieces` attribute of the "My credentials" block adds a column where each person opens their own.
* **Deleting the plugin keeps the documents**, unless the school asks otherwise beforehand, in one tick box that is not ticked. Nothing else is ever deleted, and no document is ever deleted on its own.

= 1.3.0 =
* **The directory opens on the school, not on the whole site.** On a flight school that also sells trial flights through an online shop, the customer accounts buried the flying staff: three hundred and sixty nine buyers for a hundred and fourteen people the school actually follows. The default view now shows those who hold a role of the range, plus administrators. Everyone on this site is one click away, and a filter lets a site say what its school is.

= 1.2.4 =
* A successful save no longer reopens the side panel on a blank add form, over the file you had just written to.

= 1.2.3 =
* **A window that carries nothing forward no longer promises what becomes of the validity.** The bar under the off-aerodrome landing qualification read « window open, the new validity runs from the examination ». The catalogue says the opposite: it says nothing at all, and nobody here knows what the authority does on that point.
* **Every open window now names the day to act on**, whatever it carries forward: « to be renewed from 7 August 2026 », with the verb of the catalogue, placed under the start of the hatching, where that day falls in time. On the off-aerodrome landing qualification, that is the day from which the renewal is filed, and it is more use than the sentence it replaces.
* A window that does carry forward keeps its promise, spelled out as before: an examination taken today carries the expiry from the old date, and nothing is lost. That is the trap this plugin exists to spell out, and it is said only where it is true.
* The catalogue column reads the same way: it spells out the carry forward where there is one, and says only how many days the window runs where there is not.

= 1.2.2 =
* **The helicopter landing site authorisation was never an authorisation for a place.** A helicopter pilot holds one permanent qualification that allows landings away from aerodromes, not one authorisation per site, and the delivered catalogue asked for a site before it would save anything. The credential is now called « Off-aerodrome landing qualification », it is tied to nothing, and no site is asked for, anywhere.
* It moves from « Authorisations », which gathers the papers a third party issues for a place or an access, to « Ratings », where it is taken and held like the others.
* It is issued by the local authority, the préfecture in France, and no longer suggests the school itself when you record one.
* **Its renewal is filed three months ahead**, so the bar now hatches its last three months and the file says « due for renewal » when that time comes. A first reminder at 120 days leaves a month to gather the papers before that date.
* Installed sites receive all of this: the site field disappears, the family and the issuer are put right, and a site left on a credential already entered is cleared. Nothing is deleted, and a type a school has taken over is left alone.

= 1.2.1 =
* The unfolded file no longer repeats the name of the person whose row you have just clicked.

= 1.2.0 =
* **The side panel opens when you click it.** It used to wait for the server before showing itself, so on a busy site nothing happened for three or four seconds, and the panel then appeared on its own long after you had given up. It now opens at once and fills in when the answer arrives, a request that is no longer wanted is dropped, and a stale answer opens nothing.
* **A refusal is no longer hidden behind the panel.** Adding a credential that needs a scope, a helicopter landing site for instance, was refused as it should be, but the panel reopened on top of the message: nothing was written and nothing said so. The refusal is now carried into the panel itself, and what you had typed comes back with it, with the field at fault pointed out.
* **What you typed no longer travels in the address.** Dates, licence numbers and doctors' names were passed in the query string on a refusal, and ended up in browser history and server logs. They stay on the server now, tied to the account that typed them, and the address carries nothing but an opaque token.
* **The unfolded file is a third shorter**, without losing anything: the verdict keeps its own line, family headings appear only when there are two or more, a missing reference is no longer written out, and a pill that claimed the screen was read only is gone.
* **The renewal window says what it is for, where it happens.** Instead of « window open on 20 June 2027 » on the left, the line now reads « to be renewed from 20 June 2027 », or revalidated, following the credential, and it sits under the hatched part of the bar, at its place in time.
* Last sign in dates kept by the When Last Login plugin are recognised.

= 1.1.0 =
* **The tables hold their shape on a busy site.** On a live install with a page builder theme and nineteen other plugins, the directory came apart: headers wrapped, cells drifted out of their columns, and the screen could not be used. Our table rules carried no weight of their own and any third party stylesheet won. Structure is now asserted, and a hostile stylesheet is part of the test suite.
* **Two roles sharing a label no longer look like a bug.** A school that kept its own pilot role beside the range's saw the same pill twice on every line. Each role now shows once, and identical labels are told apart.
* **Take over what the site already holds.** A new Recovery screen reads the meta keys this site actually carries, lets you say which one holds what, shows exactly what would be written, and writes nothing before you have seen it. Last sign in dates and expiry dates kept by a previous plugin come across with their history.
* An empty file no longer opens a screen and a half of nothing, and the screens stopped explaining themselves in paragraphs nobody asked for.

= 1.0.0 =
* First stable release. The plugin is complete, and the version number says so: the numbers below 1.0.0 belong to builds that are no longer in circulation.
* **The shared core no longer starves a newer copy of itself.** Installing a second plugin of the Aero range beside an older one could leave the shared registry unloaded, and the site fell over on activation with a fatal error. Each class of the core is now looked for on its own, the newest copy present is the one that loads, and a half loaded core completes itself instead of giving up.
* **The Aero range moves to the top of the admin menu**, right under the Dashboard and above Posts, with Aero Users first and the other plugins of the range under it in a fixed order. A school that would rather have it elsewhere moves the whole range with the `aero_core_menu_base` filter.

= 0.6.0 =
* Expiry dates are read, not worked out. A medical certificate, a type rating, a language proficiency and a landing site authorisation carry their expiry printed on the document: both dates are now entered, as they are written on the paper, and the one entered is the truth everywhere.
* The age and class scale of the medical certificate is gone, and with it the age brackets, the per-class durations and the birthday cut-offs. Six tables become five, and nothing already saved moves.
* While typing, the screen offers an expiry: from what this credential lasted for this person last time, then from the default duration of the type, then nothing at all. It fills a field you can see, and one keystroke replaces it.
* The pinned column now takes the expiry in place, which is what it was already showing.
* A landing site authorisation lasts ten years, and leaves its "expiry unknown" state.
* A panel beside the directory holds one credential at a time: both dates, reference, issuer, class or level, the aircraft it is tied to, and its whole history. The aircraft of a credential can now be corrected, which was written in stone before.
* Unfolding a row now reads rather than writes: what the person holds, a validity band per credential, and what they may do today, deduced and never entered.
* An audit document, in CSV or ready to print, giving the state of any past day.
* The school names itself, and its name is what the screens and the documents say, the site title being only a fallback.

= 0.5.0 =
* Medical certificates: the class 2 issued between forty and fifty now runs to the fifty-first birthday, and the class 1 is no longer cut at the sixtieth. Two bands of the scale were shortening a certificate by up to twenty-one months.
* A credential whose validity depends on a class or a level is no longer created without one, and one already saved without it can now be given one.
* The directory no longer loses an account whose last name is missing from the database.
* Each of the five permissions now guards something: reading the directory and a person file, editing that file, keeping licences, and handing out roles are four different things.
* "Can I fly" now says what the school has not entered, instead of a bare yes.
* The group follow-up sorts the whole school before it keeps the fifty most urgent.

= 0.1.0 =
* First release: the directory of people, with roles, views, search, sorting and bulk gestures.
* The file of one person, in the plugin: identity, contact details, photo and roles, on one page. The same screen creates an account and lays down everything else at once.
* The roles and permissions screen: create a role, take the regulatory ones in one click, grant or revoke a permission, delete a role you created.
* The permission grid is built from what each plugin of the range declares, so a neighbour appears there without a line of code changing here.
* The credentials of a person, their expiry dates worked out from the date of the check, and the directory columns that say who to chase and for what.
* Five public blocks, each doubled by a shortcode, that show a signed-in pilot where they stand, and never anybody else.
