# PKO @ 50 : 50 Shades of Love

Bilingual RSVP page (French / English) with a MySQL backed admin dashboard.
Plain PHP, no build step, no Composer. Made to be dropped into a cPanel account.

Requirements: PHP 7.4 or newer with PDO MySQL, and a MySQL / MariaDB database.

## 1. The poster

Already in place. `assets/img/pk50.png` is the original artwork, and three
optimised copies are served to visitors:

    pko50.jpg       1040 x 1477   304 KB   large screens
    pko50-760.jpg    760 x 1079   178 KB   phones
    pko50-520.jpg    520 x  738    99 KB   small phones

The browser picks the right one on its own. A phone downloads 178 KB instead of
the 2.2 MB original, which matters on mobile data.

To replace the artwork later, drop the new file in as `assets/img/pk50.png` and
regenerate the three copies:

    sips -s format jpeg -s formatOptions 60 --resampleWidth 1040 assets/img/pk50.png --out assets/img/pko50.jpg
    sips -s format jpeg -s formatOptions 60 --resampleWidth 760  assets/img/pk50.png --out assets/img/pko50-760.jpg
    sips -s format jpeg -s formatOptions 62 --resampleWidth 520  assets/img/pk50.png --out assets/img/pko50-520.jpg

If all the jpg files are absent the page draws a typographic fallback instead,
so it is never broken.

`pk50.png` itself is only the source. You can leave it out of the upload if you
want to save space on the server.

## 2. Create the database in cPanel

1. cPanel > MySQL Databases. Create a database, for example `pko50`.
2. Create a user, give it a strong password, and add it to the database with
   ALL PRIVILEGES.
3. cPanel > phpMyAdmin, select the database, Import tab, upload `sql/schema.sql`.

## 3. Fill in the configuration

Edit `includes/config.php` and replace the three `CHANGE_ME` values with the
database name, user and password from step 2. cPanel prefixes both the database
and the user with your account name, for example `pkouser_pko50`.

## 4. Upload

Upload the whole folder into `public_html` (cPanel File Manager, or FTP).
The structure must stay as it is:

    index.php            the invitation and the form
    api/submit.php       receives the answers
    api/event.ics.php    the add to calendar file
    admin/               the dashboard
    includes/            configuration, database, translations, country list
    assets/              stylesheet, script, poster
    sql/schema.sql       the tables

## 5. Turn on HTTPS

cPanel > SSL/TLS Status, run AutoSSL. The `.htaccess` file already redirects
http to https. If the certificate is not issued yet, comment out the two
`RewriteCond` / `RewriteRule` lines at the top of `.htaccess` until it is.

## Admin dashboard

    https://your-domain/admin/

Username `birthday`, password `Oct@08`.

Change the password before the invitations go out. Generate a new hash and paste
it into `ADMIN_PASS_HASH` in `includes/config.php`:

    php -r 'echo password_hash("YOUR NEW PASSWORD", PASSWORD_BCRYPT), "\n";'

If you have no command line access, use any online bcrypt generator, or ask for
a fresh hash.

The dashboard shows total answers, yes, no, today and the last seven days, a bar
chart of answers per day over the last thirty days, and the full list with
search, status and date filters, sorting, pagination, click to call, WhatsApp
links, row deletion and a CSV export of whatever is currently filtered.

## Notes

* A guest who answers twice from the same number updates their first answer
  rather than creating a duplicate. The page tells them so.
* Every answer stores its submission date and time, which is what feeds the
  statistics and the chart.
* The language is guessed from the visitor's browser, French otherwise, and can
  be switched at any time from the top right. The choice is remembered.
* Three seconds after the page opens it glides down on its own to the form, so
  a guest who only looks at the poster still discovers there is something to
  answer. Two settings in `includes/config.php` control it:
  `AUTOSCROLL_DELAY_MS` is how long the poster stays alone, and
  `AUTOSCROLL_DURATION_MS` is how long the movement itself takes. Set the delay
  to 0 to switch the automatic scroll off.
  The movement is eased, slow at both ends and quickest in the middle, and it is
  abandoned the moment the guest scrolls, taps or types, whether that happens
  during the wait or in the middle of the movement. It never runs for someone
  whose phone is set to reduce motion, nor for someone who has already scrolled.
* Spam protection: a hidden honeypot field, a CSRF token, and a limit of
  15 answers per address per 10 minutes. Those values are in `includes/config.php`.
* The admin login locks an address for 15 minutes after 5 wrong attempts.
* Timezone is Africa/Lome. Change `APP_TIMEZONE` in `includes/config.php` if
  the host reports a different time.

## Changing the event details

Date, venue, time and the two enquiry numbers live in two places:
`includes/config.php` for the calendar file and the countdown, and
`includes/i18n.php` for the two languages shown on the page.
