=== DWD Classifieds ===
Contributors: dwd
Requires at least: 6.0
Requires PHP: 7.4
Stable tag: 1.8.42
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Digital classified listings: sections taxonomy, pending moderation, run length and automatic expiry, submit/browse/user-dashboard shortcodes, Stripe listing fees, terms of service, admin dashboard stats, tools + debug, payment history.

== Description ==

* Custom post type `dwdc_listing` with hierarchical section taxonomy `dwdc_section` (shown as **Categories** on the browse page). Default section terms are created on install / upgrade; you may delete or rename them in **Classifieds → Categories**.
* Optional **address** on each listing; public templates show a **Get directions** link (Google Maps) for mobile/desktop.
* Logged-in users submit pending listings; editors publish. Expiry is set from run length (7/14/30 days) when published.
* Shortcodes: `[dwdc_submit]` (optional `layout="cards"` default sectioned form, or `layout="plain"`), `[dwdc_list]` (default: **Submit a listing** links to the full submit page; optional `submit_in_modal="1"` for a modal), `[dwdc_new_ads]` (**New ads** — last five published, non-expired listings by date; optional `posts_per_page`, `title`, `show_title="0"`), `[dwdc_user_dashboard]`, `[dwdc_claim_free_listings]` (optional `title=""` — one-time **3 free listings** claim link for logged-in users). **Browse** defaults to **drill-down**: top-level **Categories** → **Subcategories** (when terms are nested under **Classifieds → Categories**) → **Listings**; use `browse_mode="flat"` for the previous single-row category chips + mixed list. Optional `show_suggest_category="0"` hides the category suggestion on browse. Required pages are created on activation (repair under Classifieds → Tools).
* Optional **Stripe** listing fee: configure under Classifieds → Payments; users pay from **My classifieds**; editors record fee manually or rely on Stripe return URL.
* **PayPal** credentials can be stored for a future checkout flow (REST placeholder registered).
* Daily cron moves expired listings to draft.
* **Contact seller**: Inquiries from the listing contact form are **emailed** to the seller and **copied to My classifieds** so sellers can read them and open **Reply by email** even when mail delivery is unreliable.
* **Privacy**: Suggested policy text for **Settings → Privacy**; personal data **export** and **erase** hooks for listings you authored and contact messages you sent (WordPress **Tools → Export / Erase Personal Data**). After a plugin update from **Plugins → Update**, migrations and page sync run only for this plugin, and the deployed plugin version is recorded like **Classifieds → Tools**.

== Changelog ==

= 1.8.42 =
* Add Multisite-safe `uninstall.php` (loop blogs; remove known options, tables, required pages, cron).

= 1.8.40 =
* Listing image uploads store in DWD Gallery first when that plugin is active; WordPress Media Library is the fallback.

= 1.8.37 =
* **Privacy (Core):** `wp_add_privacy_policy_content` suggested text; registered **personal data exporters** (listings, contact messages) and **eraser** (messages by email/user; listing cleanup / meta clearing in batches).
* **Updates:** `upgrader_process_complete` runs migrations, page sync, and **record_deployed_plugin_version** only when **DWD Classifieds** was the plugin updated (not on every plugin update).
* **Tools / debug:** Debug link lists include **Plugins list** and **WordPress Dashboard** (`index.php`), aligned with the admin debug panel.

= 1.8.36 =
* **My classifieds**: **Messages from buyers** — stored copies of contact-form submissions (same as email to the seller), with date, listing link, full text, and a **Reply by email** button. New database table `wp_*dwdc_contact_messages` (created on upgrade).

= 1.8.35 =
* **Mobile / touch**: Public CSS uses a **~44px minimum touch target** for primary plugin buttons and form fields; **safe-area** padding for modals, lightbox, and footer credit on notched phones; **scrollable** payment history table; **stacked** “My classifieds” nav on narrow screens; **full-viewport** photo lightbox under 600px with larger prev/next controls; browse grid and report CTA tuned for small viewports.
* **Admin (pending review)**: Listing preview modal uses a **bottom-sheet** layout and **safe-area** padding on narrow screens.

= 1.8.34 =
* **Single listing**: **Report this listing** (and related messages) appear at the **top** of the listing content, **right-aligned**, instead of after the listing body.

= 1.8.33 =
* **Single listing**: Description (post content) comes first; photos are **thumbnail strip** only (no huge featured image from the theme). **Lightbox** on photo click with prev/next (buttons + keyboard), themed “Written by … in …” row hidden when possible.
* **Contact seller**: Sends to the listing **contact email** first (with author email as fallback). Logged-in users without a valid account email get a clear error. Mail subject/body avoid fragile `sprintf` on translations (fixes common “critical error” white screens).

= 1.8.32 =
* **Labels**: The taxonomy admin submenu is **Categories** (was “Sections”); list table, pending review, category suggestions, and front-end submit/saved-search field labels say **Category** where they previously said Section.

= 1.8.31 =
* **Category suggestions**: Admin submenu shows a red pending count badge (same style as Pending listings) when the queue is not empty.

= 1.8.30 =
* **Documentation**: Added `docs/ADMIN-HELP.md` (full administrator guide: menus, workflows, tasks) and **Classifieds → Administration guide** in wp-admin (plus **Dashboard → Classifieds admin guide** for administrators). Shortcode help doc links to it.

= 1.8.29 =
* **Plugins screen**: “Visit plugin site” uses the Classifieds project URL on dizwebdesign.com; author By DWD links to dizwebdesign.com (Plugin URI and Author URI in the main plugin header).

= 1.8.28 =
* **Submit flow**: After submitting a new listing, users are redirected to **My classifieds** with a success message (pending review). If that page is not configured, behavior falls back to the listing preview / submit page as before.

= 1.8.27 =
* **Footer credit**: Public pages include a short “Powered by DWD Classifieds copyright dizwebdesign.com” line with “Report issues here” (the word “here” links to the Classifieds project on dizwebdesign.com).

= 1.8.26 =
* **Category suggestions** (admin): **Approve & add category** creates the section term and removes the row; **Dismiss** removes without creating. **My classifieds** shortcode shell defaults to **100%** width of the content column.
* **Tools / plugin updates**: Running **database & page sync** (Tools), **plugin upgrade** follow-up migrations, and **activation** briefly enable WordPress **maintenance mode** (root `.maintenance`) during migrations/sync, then clear it—skipped if another maintenance file is already present (e.g. core update).

= 1.8.25 =
* **Block editor**: Auto-created required pages use **block markup** (Group, Columns, Shortcode blocks) with neutral borders and padding so you can edit layout in the page editor. **Patterns → DWD Classifieds** adds the same layouts for reuse on other pages.

= 1.8.24 =
* **Suggest a category**: users may submit **up to 3 suggestions per hour** (per account). The previous 24-hour cooldown and separate daily cap are removed; messaging matches the new limit.

= 1.8.23 =
* **My classifieds** (`[dwdc_user_dashboard]`): output is wrapped in `.dwdc-user-dashboard-shell` with a default width of **`min(60vw, 100%)`** (about 60% of the viewport on wide screens, but never wider than the theme content column), centered. Form fields use full shell width. Customize via **Additional CSS**: `:root { --dwdc-user-dashboard-width: 60%; }` or `60vw`, `100%`, etc.

= 1.8.22 =
* **Layout**: Shortcode UIs (browse panel, submit form, new ads, free-promo, etc.) default to **width 100%** of the WordPress block/theme container instead of fixed `rem` max-widths. Optional global cap: set `--dwdc-shell-max-width` on `:root` (e.g. `72rem`) if you want a maximum content width site-wide.

= 1.8.21 =
* **Pending listings**: **Approve** publishes immediately with no browser confirmation dialog.
* **Browse / New ads UI**: Refreshed public styling to match a clean classifieds layout — light bordered panels, gray toolbar strip, primary blue **#2e74b5** (overridable via `:root { --dwdc-color-primary: ... }`). `[dwdc_list]` output is wrapped in `.dwdc-browse-panel`; `[dwdc_new_ads]` uses a matching sidebar-style panel.

= 1.8.20 =
* **Submit form**: **Upload selected photos** runs uploads only (saves to draft / staged revision) so authors can finish title, description, and other fields afterward; **Submit for review** sends the listing without re-uploading if photos were already saved. Sequential upload state merges across multiple upload batches.
* **Submit form UI**: Sectioned **card** layout (default) with clear steps: listing details, category & run length, photos, review & send. Shortcode: `[dwdc_submit layout="plain"]` for a lighter layout. Themes/filters: `dwdc_submit_form_layout`.

= 1.8.19 =
* **Submit / edit with photos**: status shows **Upload complete. Saving your listing…** after every photo finishes uploading and before the final save request (progress bar stays at 100%).
* **Rate limits**: new listings and **save listing** (including edits with sequential photo uploads) use **separate** per-hour counters so testing or many edits no longer competes with the “new submission” limit. Per-photo AJAX uploads allow more requests per hour.

= 1.8.18 =
* **Delete listing**: when a listing is **permanently** deleted (Trash → Delete permanently, or empty Trash), attachments uploaded for that ad are removed from the Media Library. Moving an ad to Trash alone does not delete its images, so **Restore** still brings the listing back with photos.
* **Listing photos**: uploads use WordPress `media_handle_upload()` (same core path as the Media Library). Subscribers who can submit listings are granted `upload_files` when missing so uploads match core expectations.

= 1.8.17 =
* **Submit / edit listing photos**: each image uploads in its **own request** (AJAX to `admin-ajax.php`), so PHP processes **one file per POST** — avoids hitting `post_max_size` when submitting several large photos at once. After all uploads succeed, the form submits **without** file fields (text + nonces only). New listings use the existing buffer draft; published edits pass staged attachment IDs into the pending-revision flow.

= 1.8.16 =
* **Submit with multiple large photos**: clear message when the **total** size of all images in one submission exceeds PHP **post_max_size** (often 8–32 MB on hosts). Five ~7 MB files need roughly **35+ MB** allowed for that setting; otherwise the request fails before WordPress runs. **Classifieds → Listing images** now explains this limit.
* **Submit XHR**: more specific error text for payload-too-large (413), permission/size issues (400/403), and server errors.

= 1.8.15 =
* **Submit / edit listing**: when you attach photos and submit, the form shows an **upload progress bar** (XHR upload progress) and a **Saving…** state while the server finishes, then follows the normal redirect.

= 1.8.14 =
* **Submit listing**: after **Submit for review**, redirects to the listing view (preview URL for pending posts so the author does not get a 404).
* **Submit listing**: removed **Upload photos now** — images upload once on final submit only.
* **Listing images**: if `wp_handle_upload` fails on the host (e.g. strict `is_uploaded_file` checks), listing images fall back to `wp_upload_bits` so attachments and the gallery still save.
* **Single listing**: featured image and gallery render reliably on block themes and when `the_content` runs outside the classic main loop (still only for the primary queried listing).

= 1.8.13 =
* **Submit listing**: after **Upload photos now**, submitting the form no longer uploads the same images again (submit handler skips XHR when photos are already on the server; server only processes multipart files when a valid upload is present).
* **Submit listing**: **Remove uploaded photos** clears images stored by **Upload photos now** (draft listings or staged photos on published edits).

= 1.8.12 =
* **Submit photos**: front-end file-size check now respects admin **Listing images** limits when `wp_localize_script` passes numbers as strings (previously fell back to 3 MB).

= 1.8.11 =
* **Listing images**: new **Classifieds → Listing images** settings for **max upload size (MB per image)** and **target size after optimization (KB)**. Defaults remain 3 MB / 500 KB; filters `dwdc_listing_image_max_upload_bytes` and `dwdc_listing_image_target_max_bytes` still apply. Database migration v8 seeds options on upgrade.

= 1.8.10 =
* **Submit listing / photos**: each image may be up to **3 MB** before upload; the server **resizes and compresses** toward about **500 KB** per file (JPEG/WebP quality + dimensions; large PNGs may convert to JPEG). Front-end validation matches the upload limit.

= 1.8.9 =
* **Submit listing**: After **Upload photos now**, the form marks photos as already on the server and **clears the file input** before **Submit for review**, so images are not uploaded a second time. Finalizing a photo draft now passes **`post_type`** to `wp_update_post`, surfaces **errors** instead of creating a duplicate listing, and **never inserts a second post** when the buffer draft fails to move to **pending**.

= 1.8.8 =
* **Submit listing**: **Upload photos now** sends images to the server immediately via AJAX (progress bar), without submitting the whole listing. New listings use a short-lived **draft** to store uploads until **Submit for review**. Editing **published** listings saves staged photos into the **pending revision** (same as a full save).

= 1.8.7 =
* **Submit listing / edit**: after choosing **photos**, the form shows **thumbnails** of the selected images (first labeled **Main photo**). If more than five files are chosen, a note explains that only the first five are uploaded.

= 1.8.6 =
* **My classifieds**: listing owners can **Delete listing** (moves the ad to **Trash**; confirm dialog). **Subscriber** and **Contributor** roles receive delete caps for their own listings so `delete_post` checks succeed. Pending-revision staged images are cleaned the same way as moderator **Discard** before trashing.

= 1.8.5 =
* **Edit published listings**: owners can **edit** from **My classifieds** (submit page with `?dwdc_edit=`); changes to **published** ads are stored as a **pending revision** until a moderator **applies** or **discards** them under **Classifieds → Pending listings** (second table). **Draft/pending** listings still update directly. Discarding removes **staged** images that are not on the live ad.

= 1.8.4 =
* **Submit listing**: when one or more **photos** are attached, the form submits via **XHR** so a **progress bar** and status text show upload progress, then “Saving…” while the server finishes, then the browser follows the normal success redirect.

= 1.8.3 =
* **Pending listings**: clicking the listing **title** opens a **modal** with the full ad (description via `the_content`, section, price, location, email, run length, featured image, gallery, and body text). **Edit in admin** remains a separate link under the title.

= 1.8.2 =
* **Pending listings**: new Classifieds submenu **Pending listings** — queue of `pending` posts with **Approve** (publish) or **Deny & return to author** (moves to draft). Denial includes an optional note stored for the author on **My classifieds** (moderator feedback).
* **Admin menu**: Classifieds submenu pages whose slugs start with `dwdc` are ordered **alphabetically** by title (core items like All listings / Add New / Categories stay first).

= 1.8.1 =
* Removed the front-end **Admin** dropdown (fixed corner and browse toolbar) for administrators; use wp-admin and the admin bar as usual.

= 1.8.0 =
* **Saved searches & email alerts**: on **My classifieds**, save a section and optional keywords; optional **email when a new listing is published** that matches (daily send cap per account).
* **Listing lifecycle** on **My classifieds**: clearer status text for pending, published (with run-until date when set), draft, and expired listings.
* **Submit photos**: optional **multi-image upload** on submit (up to five, ~3 MB each before optimization); first image is the featured image, others show in a **gallery** on the single listing.
* **Contact seller**: optional **contact form** on single listings (toggle under **Classifieds → Payments** — “Public listing contact”) instead of exposing a `mailto:` link; messages email the author with Reply-To set.
* **Rate limits** on listing submit, reports, category suggestions, and saved-search changes (per user / IP via transients).
* **Tools**: **site health** checklist (DB version, pages, cron, masked Stripe keys) and **CSV export** of all listings + key meta.
* **SEO / sharing**: **JSON-LD** (`ClassifiedAd`) and **Open Graph / Twitter** tags on single listings (uses featured or first gallery image when present).

= 1.7.5 =
* **Submit form**: “terms of service” is a **link-style control** that opens the full terms in a **modal** instead of an inline scroll box.

= 1.7.4 =
* **Tools**: the **recorded** plugin version is no longer auto-updated on every page load during migrations. After you upload new plugin files, **Classifieds → Tools** shows the previous recorded version vs the version in the uploaded files until you click **Run database & page sync** (activation still records the current version).

= 1.7.3 =
* **Browse toolbar**: guests see **My Classifieds** as a button that opens a **pop-up modal** (log in, full-page login, lost password, register) instead of hiding the control. Logged-in users still get the direct link.

= 1.7.2 =
* **Login modal** (submit/browse): username and password use **placeholders** (no separate labels), layout **left-aligned**; optional links for full `wp-login` page, lost password, and register.

= 1.7.1 =
* **Shortcode help in wp-admin**: same guide is available under the main **Dashboard → Classifieds shortcodes** menu (administrators). **Classifieds → Dashboard** shows a notice with a button to open it.

= 1.7.0 =
* **User moderation**: administrators can **ban** a user from posting classifieds or **suspend** for a set number of days (1–365). **Users → Edit user** includes DWD Classifieds posting controls. Users with **`edit_others_dwdc_listings`** see a **Moderate seller** panel at the **bottom of each single listing** (not shown for administrator authors or self). Banned/suspended users are blocked at submit time; **My classifieds** shows a notice when applicable.

= 1.6.3 =
* **Shortcode help**: `docs/shortcodes-help.md` in the plugin package, shown under **Classifieds → Shortcode help** in wp-admin. Linked from the front-end **Admin** menu (for administrators) and from **Tools** / debug admin link lists.

= 1.6.2 =
* **My classifieds** (`[dwdc_user_dashboard]`): top **menu** with a link to the main **Browse classifieds** page when that page exists.

= 1.6.1 =
* **Browse toolbar**: **My Classifieds** label (capital C); link aligned to the **right** of the action menu on wide screens.
* Shortcode **`[dwdc_claim_free_listings]`**: registered users can **claim three free listings** once via the button (tracked per account).

= 1.6.0 =
* **Browse toolbar**: **My classifieds** link (when the user dashboard page exists and the visitor is logged in).
* **Entitlements**: admin **Entitlement groups** (per-role defaults: free listings, trial days) and per-user overrides on the user profile (group, bonus free ads, trial/membership end).
* **Payments**: pricing model (**per listing**, **membership required**, or **hybrid**), optional **monthly membership** fee and period (Stripe Checkout extends membership); **trials** and **free ad slots** from groups/overrides; submit flow and dashboard respect entitlements.

= 1.5.7 =
* New shortcode **`[dwdc_new_ads]`**: block titled **New ads** showing the most recently **published** listings (default **5**, not expired), same card layout as the browse list. Attributes: `posts_per_page` (default `5`, max `20`), `title`, `show_title="0"` to hide the heading.

= 1.5.6 =
* **Browse toolbar**: **Submit a listing** and **Suggest a category** are a single horizontal **menu** (`<nav>` + list) with matching primary/secondary button styles; modals load after the menu for valid markup.

= 1.5.5 =
* **Browse** (`[dwdc_list]`): **Submit a listing**, **Suggest a category**, and the **Admin** dropdown (for site administrators) appear in a toolbar **above** categories/listings. The fixed-corner admin menu is omitted on that page so it is not duplicated.

= 1.5.4 =
* **Browse** (`[dwdc_list]`): default **drill-down** — **Categories** grid → **Subcategories** when the section taxonomy has child terms with listings → **Listings** at a leaf (or the whole branch if child terms exist but have no listings yet). Breadcrumb **All categories** / ancestors. Optional `browse_mode="flat"` restores the previous **All** + chip nav and mixed listing view.

= 1.5.3 =
* **Front-end admin** (`manage_options`): replaced the bottom link strip with a **top-corner Admin** dropdown.
* **Suggest a category** on the browse list: opens in a **modal** (link-style control) instead of an inline form block.

= 1.5.2 =
* **Tools** page: version table is now **Recorded in database (current site state)** vs **Uploaded plugin files (not applied until you run sync)** so new file versions do not read as already updated before sync.

= 1.5.1 =
* **Poster name** on each listing: browse list and single listing show **Listed by** / **Posted by** using the author’s display name (filter `dwdc_listing_poster_name`).
* **Submit a listing** for guests: primary button opens a **modal** with **Log in** and **Register** (if registration is enabled in WordPress), redirecting to the submit page after sign-in.

= 1.5.0 =
* **Tools**: shows installed vs current **plugin version** and **database schema**; after **Run database & page sync**, success notice includes previous → new versions.
* **Browse** (`[dwdc_list]`): default is a **full-page** “Submit a listing” button (no separate “full submit page” link). Modal submit is opt-in via `submit_in_modal="1"`.
* **Default terms of service** text seeded when terms are empty (migration to DB **v5**): liability disclaimer, user responsibility, no deceptive pricing (“up to” / “starting at” style), site may terminate classified access anytime. Editable under **Classifieds → Terms of service**.
* **Suggest a category** on the browse page (logged-in users): **Classifieds → Category suggestions** queue for moderators; one suggestion per user per 24 hours.

= 1.4.1 =
* Front-end admin strip: **Listing reports** link for users with `edit_others_dwdc_listings` (not only `manage_options`), so editors can open the queue from the public site.

= 1.4.0 =
* **Report listing** on single listing view (logged-in users): reason + optional details; queue under **Classifieds → Reports** for moderators (`edit_others_dwdc_listings`). One report per listing per user per 7 days (spam control). DB table `{prefix}dwdc_reports` (migration to DB v4).

= 1.3.1 =
* Listing **address / location** field (submit form + editor); **Get directions** uses Google Maps (mobile-friendly).
* **Browse** page: **category** list (sections with at least one visible listing); filter listings by `?dwdc_section=`; hide empty categories.
* **Default categories** (sections) seeded once on new install / DB v3; admins can rename or delete unused terms — removed terms are not recreated.
* DB version **3** (migration seeds default section terms).

= 1.2.0 =
* Admin **Dashboard** (stats: totals, new in 30 days, trash, pending, published).
* **Tools** page: **Debug** section with links to enable the admin debug panel (`?dwdc_debug=1`).
* **Terms of service** editor (admin); acceptance checkbox on submit when terms are non-empty.
* **Browse** page: “Submit a listing” opens submit form in a **modal** (full submit page linked as alternative).
* **My classifieds** page (`[dwdc_user_dashboard]`): author’s listings + **payment history** with receipt JSON.
* **Payments** settings (Stripe keys, PayPal placeholders, currency, listing fee in cents, optional require payment before publish).
* **Payment history** admin table; custom DB table `wp_*dwdc_payments` (migration on upgrade).
* Front-end **admin strip** (`manage_options`) expanded with dashboard, payments, TOS, browse, submit, my-classifieds links.
* Stripe Checkout redirect for listing fee; return handler verifies session and marks fee paid.

= 1.0.1 =
* Fix: register post type and taxonomy on `init` so rewrite registration runs after WordPress creates `$wp_rewrite` (avoids fatal error on activation).

= 1.0.0 =
* Initial release: CPT, sections, submit form, browse list, expiry cron, admin tools, debug (`?dwdc_debug=1`), duplicate-install warning.
