13 Commits

Author SHA1 Message Date
ranger 9df37a35e8 fix: Restore actually works now — handler was checking wrong $_POST key (v3.4.2)
v3.4.1 added a redirect to the Restore handler thinking that was
the missing piece. It wasn't — the entire if-block was dead code.

The per-row Restore form in inc/wp-notes-display.php sends a
hidden $_POST['note_id'] (singular) when clicked. The handler in
wp_notes_page_callback() was checking for $_POST['done_ids']
(plural), an array of IDs from bulk-action checkboxes that were
removed back in v3.1.0. The mismatch meant the handler's
isset($_POST['done_ids']) guard was always false → handler body
never ran → clicking Restore was a no-op all the way back to
v3.1.0.

Pre-v3.4.0 this was masked because the page re-rendered with
both Active and Completed sections visible, so users might assume
they'd misclicked. v3.4.0's single-pane tab render made the
no-op symptom unmissable; v3.4.1's redirect was a phantom fix
because it lived inside the unreachable block.

Fix: rewrite the handler to match the working single-note
mark-done handler pattern that lives directly above it —
isset(note_id), absint, isset($done_notes[$note_id]) lookup,
move with restored_by annotation, redirect to Active tab. Adds
a load-bearing comment recording the bug history so future-
Claude doesn't reintroduce the dead-code structure.

Lesson recorded in the changelog: when a handler appears to
"do nothing", verify the $_POST keys match BEFORE assuming the
issue is downstream (missing redirect, failed update, etc.).
2026-05-26 09:10:57 +01:00
ranger 431c31a95b fix: Restore button now sends you to Active tab where the note lives (v3.4.1)
After v3.4.0 introduced the Active/Completed tab strip, clicking
Restore on a completed note appeared to do nothing — the restored
note never showed up. David: "the complete log does not return
to active."

Root cause: the Restore handler in wp_notes_page_callback() was
the only action handler without a wp_redirect() + exit; after its
update_option() calls. Every other handler (new note, mark-as-done
single, mark-as-done bulk) had one. So Restore relied on the page
falling through and re-rendering, which used to work when both
Active and Completed sections rendered on the same page (user
could glance up to see the restored note in the Active section).

v3.4.0's single-pane tab render exposed the latent bug: after
Restore, the URL still says ?tab=completed, so only Completed
re-renders (minus the now-restored note), and the restored note
is invisible on Active.

Fix: add the missing redirect to admin.php?page=wp-notes (defaults
to Active tab) after the restore completes. The restored note now
appears in its new home immediately.

Patch bump — bug fix only, no API or behavioural changes beyond
the fix itself. Other handlers, storage model, tab structure, and
CSS are unchanged.
2026-05-26 09:07:27 +01:00
ranger c5d8a34296 feat: Active/Completed tabs on My Log page (v3.4.0)
Replaces the two stacked sections (Active above, Completed below)
on the main Logbook page with a single-pane WP-admin tabbed view
using the native subsubsub pattern (same as Posts/Comments/Plugins).

- Two tabs: Active (N) | Completed (M) with live counts
- URL-driven state via ?tab=active|completed; bookmarkable,
  refresh-stable, back-button works, invalid values fall back to
  Active server-side via whitelist + sanitize_key
- No JavaScript — each tab is a hyperlink built with add_query_arg
- WP-admin core CSS handles .subsubsub + .current; only a small
  margin block added in wp-notes-styles.php
- Single-pane render: only the selected tab's section is in the
  DOM, so no flash-of-wrong-content
- "Add a Note" form stays visible on both tabs for UI consistency

Flagged in the 2026-05-25 UX audit as the highest-payoff next move
(Tier 3 item #7). When the v4 timer/time_logged field ships, the
structure can extend to 3 tabs (TODO/IN PROGRESS/Completed) in
the same place; without it, an IN PROGRESS tab would always be
empty, so v3.4.0 ships the 2-tab version matching the current
data model.

Storage model unchanged — Active and Completed remain in their
separate options (wp_notes, wp_done_notes). The
wp_notes_display_notes($type) function is unchanged.

MINOR bump: new feature, no breaking changes.
2026-05-26 09:00:15 +01:00
ranger bd8c5f2340 ux: replace generic cog admin-menu icon with closed-book (v3.3.5)
The WordPress admin sidebar showed "Logbook" with the generic
cog wheel icon (dashicons-admin-generic) — fine for a settings
page, off-brand for a plugin whose entire identity is "logbook".

Swapped to dashicons-book-alt (closed book) — the most literal
possible match in the Dashicons set, and visually reinforces the
brand at the first place users see the plugin every day.

Alternatives also viable (commented in the changelog for future
reference): dashicons-edit (pencil), dashicons-clipboard,
dashicons-welcome-write-blog, dashicons-format-aside.

VERSION
- wp-notes.php header 3.3.4 → 3.3.5
- WP_NOTES_VERSION constant 3.3.4 → 3.3.5
- About page version-history leads with v3.3.5; v3.3.4 demoted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 10:09:49 +01:00
ranger ead5bbcd2c release: 3.3.3 → 3.3.4 — rename Gitea repo a-wp-notes-v3 → a-logbook
The repo's old name was a holdover from the v3-of-A-WP-Notes
archival era. With the plugin firmly identifying as "Logbook" now,
the repo and the local working folder should match.

CHANGES IN-CODE
- inc/wp-notes-updater.php: WP_NOTES_GITEA_REPO constant
  a-wp-notes-v3 → a-logbook. Update checker now hits
  https://git.davidtkeane.com/api/v1/repos/ranger/a-logbook/...
  on every check. Override-able via define() in wp-config.php if
  the repo ever moves again.
- inc/wp-notes-about.php: "View the full CHANGELOG.md →" link on
  the version-history card now points at the new repo path.
- Local working folder on M3 renamed
  /Users/ranger/scripts/Gitea/a-wp-notes-v3-archive → a-logbook.
  .git/config survived; remote URL will be updated separately.

UNCHANGED (zero-migration commitment continues)
- Plugin slug 'wp-notes'.
- Plugin text domain 'a-wp-notes'.
- All wp_notes_* function names, WP_NOTES_* constants, DB option
  keys, user_meta keys, file names inside the plugin folder.
- Historical CHANGELOG references to a-wp-notes-v3 stay as
  truthful history of the v3.2.0 era.

VERSION BUMP
- wp-notes.php header Version: 3.3.3 → 3.3.4
- WP_NOTES_VERSION constant: 3.3.3 → 3.3.4
- About page version-history leads with v3.3.4; v3.3.3 demoted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 09:54:27 +01:00
ranger e51b087545 release: 3.3.2 → 3.3.3 — verification bump to test the "Check now" flow end-to-end
Pure version increment with no functional changes. Lets David's
Local install (currently on v3.3.2) hit the "Check now" button in
Settings → Updates and verify the panel correctly reports a real
version delta — should render "v3.3.3 available — Download .zip"
with a working download link to the source archive on Gitea.

After the test passes, `git pull` brings the local install to
v3.3.3 and the same panel should flip back to "You are up to date
(v3.3.3)".

VERSION
- wp-notes.php header 3.3.2 → 3.3.3
- WP_NOTES_VERSION constant 3.3.2 → 3.3.3
- About page version-history leads with v3.3.3 as latest (with a
  cheeky "if you can see this line, you successfully pulled the
  test release" note); v3.3.2 demoted to previous entry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 09:47:25 +01:00
ranger 1c93c82ef5 fix: update checker — fall back to /tags when no formal Gitea Release exists (v3.3.2)
The v3.3.0 update checker only queried Gitea's /releases/latest
endpoint, which requires a formal Release object (created via the
Gitea web UI with optional notes + zip assets attached). A plain
"git tag v3.3.x && git push --tags" from the terminal does NOT
create that Release object — so the checker kept returning "No
releases tagged on the Gitea repo yet" even when tags existed.

wp_notes_fetch_latest_release() now falls back to the
/tags?limit=1 endpoint when /releases/latest returns 404 (or any
non-200). It synthesises a release-like payload from the newest
tag — tag_name, html_url pointing at the tag view, tag message as
the body, empty assets[] so the existing download-URL logic falls
through to Gitea's source-archive URL pattern (/archive/<tag>.zip).

Net effect: the "Check now" button now finds the latest version
whether David creates formal Gitea Releases OR just pushes tags
with "git push --tags". No workflow change required.

Discovered while diagnosing why "Check now" wasn't seeing today's
v3.1.0/v3.2.0/v3.3.0/v3.3.1 tags (just pushed in this session)
— the tags were there, the formal Release objects were not.

KNOWN LIMITATION (not a bug — flagged)
The Gitea repo ranger/a-wp-notes-v3 is currently private. Anonymous
API requests get a 404 (Gitea's standard behaviour for private
repos). The updater code is correct but can't actually reach the
API on a private repo without authentication. Fix: change the
repo visibility to public on Gitea — appropriate anyway for a
GPL-licensed plugin headed for the WordPress.org marketplace.

VERSION BUMP
- wp-notes.php header 3.3.1 → 3.3.2
- WP_NOTES_VERSION constant 3.3.1 → 3.3.2
- About page version-history leads with v3.3.2; v3.3.1 demoted

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 09:29:33 +01:00
ranger 3667b7a154 release: 3.3.0 → 3.3.1 — drop "WP" prefix, plugin is now just "Logbook"
David's call after a short discussion about WordPress.org marketplace
considerations. WP.org's trademark policy historically discourages
plugins implying official endorsement via a "WP" prefix and has been
known to request rename during submission review. Dropping it now
makes the name cleaner AND sidesteps that future hurdle if/when the
plugin lands on the marketplace.

CHANGES
All user-facing brand mentions: WP Logbook → Logbook across:
- Plugin header (Plugin Name + docblock)
- Admin menu top-level
- Admin sidebar submenu label still "My Log" (already prefix-free)
- Admin bar count menu
- Dashboard widget title
- Settings page H1
- Main page H1
- About page intro card + "What Logbook does" card heading
- Email feedback subject + body intro
- Legacy feedback.php subject lines
- error_log() prefix [WP Logbook] → [Logbook]
- Updater panel description text
- styles.php docblock

VERSION
- wp-notes.php header Version: 3.3.0 → 3.3.1
- WP_NOTES_VERSION constant: 3.3.0 → 3.3.1
- About page version-history card gets new top entry for v3.3.1
  with green "latest" pill; v3.3.0 demoted to previous entry
- CHANGELOG header line tracks the full naming lineage now:
  A-WP-Notes (≤v3.1.0) → WP Logbook (v3.2.0-v3.3.0) → Logbook
  (v3.3.1+)

NOTABLY NOT CHANGED
- Historical CHANGELOG entries for v3.2.0 still say "WP Logbook" —
  that was the correct name at the time, rewriting would be
  revisionist.
- Same zero-migration commitment: internal function names,
  constants, DB option keys, user_meta keys, file paths, plugin
  slug 'wp-notes', and text domain 'a-wp-notes' all unchanged.
- Pure user-facing string change. No data migration, no behaviour
  change. Existing installs see "Logbook" appear on next page
  refresh.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 09:13:51 +01:00
ranger 5c8f1716a3 release: 3.2.0 → 3.3.0 — self-hosted update checker (Gitea API)
WP Logbook is hosted on the author's own Gitea instance
(git.davidtkeane.com), not on WordPress.org, so WP's built-in update
flow doesn't see new releases. This release adds a Settings →
Updates panel that polls Gitea's Releases API and reports whether
a newer version is available.

CHANGES
- inc/wp-notes-updater.php: full rewrite of the previous broken stub
  (it had a hard require on a non-existent vendor path and was never
  included from the main plugin file anyway).
- wp_notes_fetch_latest_release() hits
  /api/v1/repos/<owner>/<repo>/releases/latest, parses, normalises.
  Prefers a .zip asset attached to the release; falls back to
  Gitea's source-archive zip URL.
- wp_notes_update_status() compares against WP_NOTES_VERSION and
  returns 'available' | 'up-to-date' | 'unknown' (the last when no
  release has been tagged yet — graceful first-run UX since the
  repo currently has zero tags).
- New AJAX endpoint wp_notes_check_updates, capability-gated
  (manage_options) + nonce-protected. Force-refreshes the cache.
- Settings page now renders the Updates panel via
  wp_notes_render_updates_panel() — current status text, "Check
  now" button, View on Gitea / View all releases quick links,
  manual-install instructions, and a Download .zip button + View
  release notes link when an update is detected.
- wp-notes.php require_once chain now includes the updater file.

CACHING
- Successful fetches: 12h site transient.
- Negative responses (404 = no releases yet): 1h so a freshly-
  tagged release shows up quickly.

INSTALLATION FLOW (intentionally manual)
The panel does NOT auto-install. Manual path printed in the panel:
download .zip → deactivate → upload via Plugins → Add New → Upload
→ reactivate. Notes live in wp_options so they survive the upgrade.

ALSO IN 3.3.0
- Section heading rename carried from the previous unreleased
  block: "Add New Note" → "New Log Entry"; "Notes Todo List:" →
  "Log entries". Row-level "note" labels intentionally unchanged.

VERSION BUMP
- wp-notes.php header 3.2.0 → 3.3.0
- WP_NOTES_VERSION constant 3.2.0 → 3.3.0
- About page version history leads with v3.3.0 as latest, demotes
  v3.2.0 to previous entry

NOTES FOR FUTURE-CLAUDE
- Gitea repo currently has ZERO release tags. First run will show
  "No releases tagged on the Gitea repo yet." Tag v3.2.0 / v3.3.0
  on Gitea and the checker will start reporting versions.
- Repo coordinates live in three constants at the top of
  inc/wp-notes-updater.php (WP_NOTES_GITEA_HOST, _OWNER, _REPO).
  Override via define() in wp-config.php if the repo ever moves.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 09:09:40 +01:00
ranger eeaf8a0f87 ux: rename section headings on My Log page to match the logbook framing
The H1 says "WP Logbook" and the sidebar says "My Log" since the
v3.2.0 rebrand, but two section headings on the My Log page still
read "Add New Note" and "Notes Todo List:" — slightly jarring next
to the new identity.

CHANGES
- "Add New Note" (create form postbox) → "New Log Entry"
- "Notes Todo List:" (above active + completed lists) → "Log entries"

NOT CHANGED — intentionally
Row-level labels stay as "note" because that's the unit-of-work term
in the existing data model and the per-row UI:
- "Add Note" submit button
- "Mark as Done"
- "No active/completed notes found" empty-state notices
- Email subjects ("WP Logbook feedback…" stays, doesn't say note)

The headings are brand-y; the row-level strings are functional.
Different rename scope on purpose — minimises churn while still
making the page read consistently with its new name.

No version bump — too small to justify another release, lives in
[Unreleased] until the next phase bundles with it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 08:34:17 +01:00
ranger 0c20833fe5 release: 3.1.0 → 3.2.0 — rebrand A-WP-Notes → WP Logbook
The plugin's identity has shifted over the day's work — from a notes
pad to a work-logbook with time tracking, earnings, and a Wallet
tile on the v4 roadmap. "WP Notes" undersold what it had become and
collided semantically with WordPress's notes-as-memos connotation;
"WP Logbook" matches both the freelancer use case ("logbook for
clients") and the student use case ("logbook for teachers"), and
matches the exact word the plugin's own About-page intro had been
using all day.

USER-FACING CHANGES
- Plugin Name header: A-WP-Notes → WP Logbook
- Description header rewritten to reflect the work-logbook framing
- Admin menu top-level: WP Notes → WP Logbook
- Admin sidebar submenu: My Notes → My Log (matches new parent —
  reads cleanly as "WP Logbook → My Log")
- Admin bar count menu: WP Notes (N) → WP Logbook (N)
- Dashboard widget title: WP Notes → WP Logbook
- Settings page H1: WP Notes Settings → WP Logbook Settings
- Main page H1: WP Notes → WP Logbook
- About page: every brand mention updated; "Go to WP Notes →" CTA
  now reads "Go to My Log →" to match the new submenu
- About page version history now leads with v3.2.0 (this release)
  as latest, demotes v3.1.0 to the previous entry
- CPT menu_name label: WP Notes → WP Logbook (cosmetic only — CPT
  hidden from admin UI since the duplicate-form fix in v3.1.0)
- Migration notice text updated
- Email-feedback subject + body intro updated
- Legacy feedback.php subjects (WP Notes Feedback / Help Request)
  → WP Logbook variants
- error_log() prefix [WP Notes] → [WP Logbook]

ZERO-MIGRATION COMMITMENT — these stay unchanged
- All wp_notes_* function names
- All WP_NOTES_* constants
- All DB option keys (wp_notes, wp_done_notes, wp_notes_settings,
  wp_notes_migration_completed, wp_notes_version,
  wp_notes_dismissed_empty_active|completed)
- All user_meta keys
- Admin page slug 'wp-notes' (preserves bookmarks, admin-bar
  #new-note anchor, and the legacy ?page=wp-notes-create redirect)
- Plugin text domain 'a-wp-notes'
- File and directory names (wp-notes.php, inc/wp-notes-*.php,
  assets/wp-notes-banner.jpg)
- Gitea repo name (ranger/a-wp-notes-v3) — David can rename on the
  Gitea side separately if he wants

The rename is purely user-facing strings. Existing installs see the
new name appear after a plugin file refresh, with zero behaviour
change. No re-activation needed.

VERSION BUMP
- wp-notes.php header Version: 3.1.0 → 3.2.0
- WP_NOTES_VERSION constant: 3.1.0 → 3.2.0
- CHANGELOG: new [3.2.0] section that bundles the rename with the
  post-3.1.0 unreleased work (About-page rewrite, feedback form)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 08:28:50 +01:00
ranger bc7d6986a8 feat: Leave Feedback form — multi-select checkboxes + actually-working submit
The right-column "Leave Feedback" form on the About page has been
expanded from two radio buttons (user picked one of two) to seven
checkboxes (user can pick any), plus an optional message textarea.
The submit button — which previously called toggleSection() on
element IDs that never existed and did nothing — now AJAX-posts
to a new server-side handler that emails the site admin via
wp_mail().

FORM OPTIONS (checkboxes — multi-select)
- I have ideas to improve this plugin
- I need help with this plugin
- I found a bug
- I'd like to request a new feature
- I'd like to share my use case
- Just saying thanks
- Other

SUBMISSION FLOW
- Client-side: at least one checkbox OR a message is required; an
  inline hint shows otherwise.
- AJAX POST wp_notes_submit_feedback with topics[] + message + nonce.
- Server handler (manage_options capability + nonce checked)
  sanitizes input, allow-lists the seven topic keys, builds a
  plain-text email body (sender + site URL + plugin version +
  checked topics with pretty labels + message), and ships it to
  get_option('admin_email') via wp_mail() with Reply-To set to the
  submitting user.
- Inline success message replaces the form on success; inline error
  lets the user retry on failure.

The toggleSection() helper that the old broken handler used is kept
defined for now — it's no longer referenced anywhere on the About
page, so it's flagged in the changelog for a future Tier-2 removal
pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 08:13:52 +01:00
ranger 184a124315 ux: rewrite About page — readable cards, accurate version history
The About page had been showing "Version 2.0.3 (Current)" while the
plugin reports v3.1.0 — the on-page version log was years behind
the actual changelog. Both the About and Version History sections
were also hidden behind onclick=toggleSection() buttons by default,
which is anti-UX for a page users specifically navigate to in order
to READ content.

Rewrote the left column as three plain cards using the same visual
rhythm as the side-by-side intro row that landed earlier today:

1. What WP Notes does — two paragraphs of prose
2. Who it's for — clean bulleted list (dropped the "Teachers ←
   Email Notifications" entry since that feature isn't implemented
   in v3.x; the claim was misleading)
3. Version history — compact, accurate, latest-first summary:
   v3.1.0 (latest, with "latest" pill) → v3.0.2 → v2.x → v2.0.0.
   Plus a "View the full CHANGELOG.md →" link to the canonical
   history on Gitea so the on-page summary stays small.

Also fixed:
- Removed redundant duplicate paragraphs ("WP Notes is a versatile
  plugin that caters to a wide range of users" appeared twice in
  two consecutive lines).
- Removed invalid HTML (bare <li> outside <ul>, <p> wrapping <li>).
- Removed a buried banner image + buried Buy-Me-A-Coffee that
  were inside the hidden-by-default Version History toggle —
  visible to no-one. Both already exist in better positions on
  the page.

Added CSS classes for the new cards (.wp-notes-about-card,
.wp-notes-about-card--versions) including a green "latest" pill
on the current version and monospaced version labels in the
plugin's blue.

Right-column feedback box unchanged. toggleSection() helper kept
(still referenced by the feedback form's submit handler — its
broken behaviour is a separate Tier 2 problem).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 08:04:07 +01:00
7 changed files with 1063 additions and 233 deletions
+450 -1
View File
@@ -1,6 +1,6 @@
# Changelog
All notable changes to **A-WP-Notes** are documented here.
All notable changes to **Logbook** (formerly **A-WP-Notes** through v3.1.0, then **WP Logbook** in v3.2.0v3.3.0) are documented here.
Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/) — versioning: [SemVer](https://semver.org/).
---
@@ -9,6 +9,455 @@ Format: [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/) — versi
---
## [3.4.2] — 2026-05-27
### Fixed — Restore actually works now (v3.4.1 was incomplete)
David retested after v3.4.1 and reported the same symptom: *"the restore still not working."*
Root cause (the real one): the per-row Restore form in `inc/wp-notes-display.php` sends a hidden `$_POST['note_id']` (singular) when the button is clicked — but the handler in `wp_notes_page_callback()` was checking for `$_POST['done_ids']` (plural), an array of IDs that used to come from bulk-action checkboxes **removed in v3.1.0**. So the handler's `isset($_POST['done_ids'])` guard was always false, the handler body never ran, and clicking Restore was a no-op all the way back to v3.1.0. The bug was masked before v3.4.0 because the page re-rendered with both Active and Completed sections visible, so the user could see the (unchanged) completed note still sitting there and assume they'd misclicked. v3.4.0's single-pane render made the no-op symptom unmissable.
The v3.4.1 redirect added inside the handler was syntactically correct but never reached — the entire `if` block was dead code.
Fix: rewrite the handler to match the working **single-note** mark-done handler pattern that lives just above it:
- Check `isset($_POST['note_id'])` (singular) instead of `done_ids` (plural array)
- `absint()` the ID for sanitization
- `isset($done_notes[$note_id])` lookup instead of iterating with `in_array()`
- Move the note across with the existing `restored_by` annotation
- Keep the redirect-to-Active-tab from v3.4.1
The handler now mirrors the pattern of the mark-done handler that's been working all along.
### Why this took two attempts to fix
v3.4.1's diagnosis stopped at the missing redirect — which was real but not load-bearing because the handler body was unreachable. Lesson recorded: when a handler appears to "do nothing", verify it's being entered at all (matching `$_POST` keys) before assuming the issue is downstream (missing redirect, failed update, etc.).
### Files changed
- `wp-notes.php` — Restore handler rewritten (lines around 1340). The change replaces the old bulk-checkbox-era handler with a single-note handler matching the live form. Adds a load-bearing comment block recording the v3.4.1/v3.4.2 bug history so future-Claude understands why the structure mirrors mark-done.
- Plugin header `Version: 3.4.1 → 3.4.2`; `WP_NOTES_VERSION` constant updated to match. PATCH bump (bug fix).
### Not changed
- The display code in `inc/wp-notes-display.php` — the form was already correct; the handler was the one out of sync.
- The Active and Completed tabs, storage model, CSS, other handlers — all unchanged.
---
## [3.4.1] — 2026-05-27
### Fixed — Restore button now sends you to the Active tab where the restored note lives
After v3.4.0 introduced the Active/Completed tab strip, clicking **Restore** on a completed note appeared to "do something" but the restored note never showed up. David reported it as *"the complete log does not return to active."*
Root cause: the Restore action handler in `wp_notes_page_callback()` (line ~1340) was the **only** action handler in the file without a `wp_redirect() + exit;` after its `update_option()` calls — every other handler (new note, mark-as-done single, mark-as-done bulk) had one. So Restore was relying on the page falling through and re-rendering, which used to work when both Active and Completed sections rendered on the same page (the user could just glance up to see the restored note in the Active section). v3.4.0's single-pane tab render exposed the latent bug: after Restore, the URL still said `?tab=completed`, so only Completed re-rendered (minus the now-restored note), and the restored note was invisible on Active.
Fix: add the missing redirect to `admin.php?page=wp-notes` (which defaults to the Active tab) after the restore completes. The restored note now appears in its new home immediately.
### Files changed
- `wp-notes.php``wp_notes_page_callback()` Restore handler: added `wp_redirect(admin_url('admin.php?page=wp-notes')); exit;` after the two `update_option()` calls. Five-line change plus a load-bearing comment explaining the bug history so future-Claude doesn't reintroduce it.
- Plugin header `Version: 3.4.0 → 3.4.1`; `WP_NOTES_VERSION` constant updated to match. PATCH bump (bug fix, no API or behavioural changes beyond the fix itself).
### Not changed
- The other three action handlers (new note, mark-as-done) — they already had correct redirects; no need to touch.
- Storage model, tab structure, CSS — all unchanged from v3.4.0.
---
## [3.4.0] — 2026-05-27
### Added — Active / Completed tabs on the My Log page
The main Logbook page used to render two stacked sections — Active notes from `wp_notes` above, Completed notes from `wp_done_notes` below. As the lists grew, this became a "wall of stacked sections" with Completed pushing content off the visible fold and users having to scroll past it to scan their active work.
v3.4.0 replaces the stacked layout with a **single-pane tabbed view** using WordPress's native `subsubsub` filter-tab pattern (the same one Posts / Comments / Plugins admin pages use):
- **Two tabs**: `Active (N)` and `Completed (M)`. Counts in the labels match the actual list lengths.
- **URL-driven state**: `?page=wp-notes&tab=active` (default) and `?page=wp-notes&tab=completed`. Bookmarkable, refresh-stable, back-button works. Invalid tab values fall back to Active server-side.
- **No JavaScript**: each tab is a hyperlink. WP-admin core CSS handles the `.subsubsub` and `.current` styling; we just add a small top/bottom margin block.
- **Single-pane render**: only the selected tab's section is in the DOM, so no flash-of-wrong-content and no wasted markup.
- **"Add a Note" form stays visible on both tabs** — even from Completed you can think of something new to log.
This was flagged in the 2026-05-25 UX audit as the highest-payoff next move (Tier 3 item #7). When the v4 roadmap's timer / `time_logged` field ships, the tab structure can extend naturally to three tabs (TODO / IN PROGRESS / Completed) in the same place; for now, without that field, an IN PROGRESS tab would always be empty, so v3.4.0 ships the 2-tab version that matches the current data model.
### Files changed
- `wp-notes.php``wp_notes_page_callback()`: tab detection from `$_GET['tab']` (sanitized + whitelisted), `subsubsub` markup with link-builder via `add_query_arg`, single-pane conditional render of either the active or completed section. Replaces the previous two-stacked-section block.
- `inc/wp-notes-styles.php` — minor spacing (`margin: 12px 0 18px`) on `.subsubsub` so the tab strip has breathing room.
- Plugin header `Version: 3.3.5 → 3.4.0`; `WP_NOTES_VERSION` constant updated to match. MINOR bump (new feature, no breaking changes).
### Not changed
- Storage model — Active and Completed remain in their separate options (`wp_notes`, `wp_done_notes`).
- The `wp_notes_display_notes($type)` function in `inc/wp-notes-display.php` — already accepts the section type, no signature change needed.
- No new DB writes, schema changes, AJAX endpoints, or dependencies.
---
## [3.3.5] — 2026-05-25
### Changed — Admin-menu icon
The WordPress admin sidebar icon for Logbook is now
**`dashicons-book-alt`** (a closed book) instead of the generic
cog wheel (`dashicons-admin-generic`). The closed-book glyph is the
most literal possible match for the word "logbook" in the Dashicons
set — reinforces the brand identity at the first place users see
the plugin every day.
If you'd prefer a different icon, the line lives in
`wp-notes.php → wp_notes_admin_menu()` as the 7th arg to
`add_menu_page()`. Alternatives worth considering:
`dashicons-edit` (pencil), `dashicons-clipboard` (clipboard),
`dashicons-welcome-write-blog` (pencil over paper),
`dashicons-format-aside` (notes glyph). Full list at
https://developer.wordpress.org/resource/dashicons/
### Version bump
- wp-notes.php header 3.3.4 → 3.3.5
- WP_NOTES_VERSION constant 3.3.4 → 3.3.5
- About page version-history leads with v3.3.5; v3.3.4 demoted.
---
## [3.3.4] — 2026-05-25
**Repo renamed on Gitea: `a-wp-notes-v3` → `a-logbook`.** Also the
local working folder on M3: `/Users/ranger/scripts/Gitea/a-logbook`
(was `a-wp-notes-v3-archive`). The old repo name was a holdover
from the v3-of-A-WP-Notes archival era; with the plugin firmly
identifying as Logbook now, the repo and folder names should match.
### Changed
- **`inc/wp-notes-updater.php`** — `WP_NOTES_GITEA_REPO` constant
updated from `a-wp-notes-v3` to `a-logbook`. The update checker
now hits `https://git.davidtkeane.com/api/v1/repos/ranger/a-logbook/...`
on every check. (The constant remains override-able via
`define()` in `wp-config.php` if the repo ever moves again.)
- **`inc/wp-notes-about.php`** — "View the full CHANGELOG.md →"
link on the version-history card updated to the new repo path.
- **Local working folder on M3** renamed to `a-logbook` to match
the Gitea repo name. `.git/config` survived the move intact;
remote URL updated separately.
### Unchanged (zero-migration commitment continues)
- Plugin slug (`wp-notes`).
- Plugin text domain (`a-wp-notes`).
- All internal function names, constants (`WP_NOTES_*`), DB option
keys, user_meta keys, file names inside the plugin
(`wp-notes.php`, `inc/wp-notes-*.php`).
- Historical CHANGELOG references to `a-wp-notes-v3` (e.g. in the
v3.2.0 entry) stay as historical truth.
---
## [3.3.3] — 2026-05-25
**Verification bump.** Pure version increment to test the end-to-end
"Check now" flow against the now-publicly-hosted Gitea repo. No
functional changes. David's Local install (running v3.3.2) should
now see *"v3.3.3 available — Download .zip"* in the Settings →
Updates panel once "Check now" is clicked, confirming the update
mechanism works against a real version delta. After the test, he
can `git pull` to land on v3.3.3 and watch the same panel flip back
to *"You are up to date (v3.3.3)"*.
---
## [3.3.2] — 2026-05-25
### Fixed — update checker now works with tag-only workflows
The v3.3.0 update checker only queried Gitea's `/releases/latest`
endpoint, which requires a **formal Release object** (created via
the Gitea web UI with optional notes + zip assets). A plain
`git tag v3.3.x && git push --tags` from the terminal doesn't
create that Release object — so the checker kept returning *"No
releases tagged on the Gitea repo yet"* even when tags clearly
existed.
`wp_notes_fetch_latest_release()` now falls back to the
`/tags?limit=1` endpoint when `/releases/latest` returns 404 (or
any non-200). It synthesises a release-like payload from the
newest tag — `tag_name`, an `html_url` pointing at the tag view,
the tag message as the body, and an empty `assets[]` array so the
existing download-URL logic falls through to Gitea's source-archive
URL pattern (`/archive/<tag>.zip`).
**Net effect:** the "Check now" button now finds the latest version
whether David creates formal Gitea Releases OR just pushes tags
with `git push --tags`. No workflow change required.
### Known limitation (not a bug — flagged for awareness)
The Gitea repo `ranger/a-wp-notes-v3` is currently **private**.
Anonymous API requests get a 404 (Gitea's standard behaviour to
avoid leaking the existence of private repos). The updater code is
correct, but it can't actually reach the API on a private repo
without authentication. **Fix:** change the repo visibility to
public on Gitea (Settings → Visibility) — appropriate anyway for a
GPL-licensed plugin headed for the WordPress.org marketplace.
---
## [3.3.1] — 2026-05-25
**Naming: dropped the `WP` prefix. The plugin is now just `Logbook`.**
David's call after a short discussion about WordPress.org marketplace
considerations — WP.org's trademark policy historically discourages
plugins implying official endorsement via a `WP` prefix and has been
known to request a rename during submission review. Dropping it now
makes the name cleaner *and* sidesteps that future hurdle if/when
the plugin lands on the marketplace.
### Changed
- All user-facing brand mentions: `WP Logbook``Logbook` across
plugin header, admin menu, admin bar, dashboard widget, settings
H1, main page H1, About page intro, About page card heading,
feedback email subjects/body, error_log prefix, updater panel
copy, and styles docblock.
- About page version-history card gets a new top entry for v3.3.1
with the green "latest" pill; v3.3.0 demoted to the previous slot.
- CHANGELOG header line updated to track the full naming lineage:
*A-WP-Notes (≤v3.1.0) → WP Logbook (v3.2.0v3.3.0) → Logbook
(v3.3.1+)*.
### Notably NOT changed
- Historical CHANGELOG entries for v3.2.0 (the original "WP
Logbook" rebrand) still say "WP Logbook" — that was the correct
name at the time and rewriting it would be revisionist.
- Same zero-migration commitment as the v3.2.0 rebrand: internal
function names, constants, DB option keys, user_meta keys, file
paths, plugin slug, and text domain all unchanged. Pure
user-facing string change.
---
## [3.3.0] — 2026-05-25
**New feature: self-hosted update checker.** WP Logbook is hosted on
the author's own Gitea instance (`git.davidtkeane.com`), not on
WordPress.org, so WordPress's built-in update flow doesn't see new
releases. This release adds a small **Updates** panel to the
Settings page that polls the Gitea Releases API and tells you when
there's a newer version waiting.
### Added
- **`inc/wp-notes-updater.php`** — full rewrite of the previous
broken stub (which had a hard `require` on a non-existent vendor
path and was never included from the main plugin file anyway).
- **Settings → Updates panel** at the bottom of `Settings → WP
Logbook → Settings`. Shows the current status on load (cached),
with a **Check now** button that force-refreshes against the
Gitea API.
- **`wp_notes_fetch_latest_release()`** — hits
`/api/v1/repos/<owner>/<repo>/releases/latest` on Gitea, parses
the response, normalises into `{version, html_url,
download_url, body, published_at}`. Prefers a `.zip` asset
attached to the release; falls back to Gitea's source-archive
URL (`/archive/<tag>.zip`).
- **`wp_notes_update_status()`** — version-compares against
`WP_NOTES_VERSION` and returns one of `available`,
`up-to-date`, or `unknown` (the last when no release has been
tagged yet — graceful first-time UX).
- **AJAX endpoint `wp_notes_check_updates`** — capability gated
(`manage_options`) + nonce protected. Deletes the cache and
re-fetches.
- **Quick links** in the panel: *View on Gitea* and *View all
releases* — both open the Gitea web UI in a new tab.
### Cached
- Successful release fetches: **12 hours** in a site transient.
- Negative responses (e.g. HTTP 404 = no releases tagged yet):
**1 hour** so a freshly-tagged release shows up quickly.
### Installation flow (manual on purpose)
The panel does NOT auto-install. Manual path (printed in the
panel itself): *download the .zip → deactivate plugin → upload via
Plugins → Add New → Upload → reactivate*. Notes live in
`wp_options` so they survive the upgrade.
### Notes for future-Claude / future-David
- The Gitea repo currently has **zero release tags** — so the very
first run of this checker will show *"No releases tagged on the
Gitea repo yet."* That's by design. Tag the v3.2.0 / v3.3.0 / v4
releases on Gitea as we ship them and the checker will start
reporting versions on its own.
- The Gitea repo coordinates live in three constants at the top of
`inc/wp-notes-updater.php` (`WP_NOTES_GITEA_HOST`, `_OWNER`,
`_REPO`). Override-able via `define()` in `wp-config.php` if the
repo ever moves.
### Also in this release — heading rename carried over from the unreleased block
### Changed — Section headings on My Log page renamed to match the logbook framing
- `Add New Note` → **`New Log Entry`** on the create form postbox.
- `Notes Todo List:` → **`Log entries`** above the active/completed
lists.
- Row-level labels intentionally **unchanged** — buttons like *Add
Note* / *Mark as Done* / empty-state *No active notes found* still
say "note" because that's the unit-of-work term in the existing
data model and UI. The headings are brand-y; the row-level strings
are functional. Two different concerns, different rename scope.
---
## [3.2.0] — 2026-05-25
**Plugin rebrand: `A-WP-Notes` → `WP Logbook`.** Bundles the rename
with the About-page rewrite and the working Leave-Feedback form that
were sitting in the post-3.1.0 unreleased block.
The plugin's identity has shifted over the day's work — from a notes
pad to a work-logbook with time tracking, earnings, and a Wallet
tile on the v4 roadmap. "WP Notes" undersold what it had become and
collided semantically with WordPress's *notes-as-memos* connotation;
"WP Logbook" matches both the freelancer-proof-of-work use case
("logbook for clients") and the student-evidence-of-work use case
("logbook for teachers"), and matches the exact word the plugin's
own About-page intro had been using all day.
### Changed
- **Plugin Name** header: `A-WP-Notes` → `WP Logbook`.
- **Description** header rewritten from "A plugin to add your notes
to the WordPress dashboard with import/export functionality" to
*"A lightweight task &amp; logbook plugin for WordPress. Log your
daily work, mark tasks done, and keep a tidy record inside the
dashboard. Perfect for freelancers showing clients what's been
delivered and students proving work to teachers."*
- **Version bumped** v3.1.0 → v3.2.0 (header + `WP_NOTES_VERSION`
constant).
- **Admin menu top-level** `WP Notes` → `WP Logbook`.
- **Admin sidebar submenu** `My Notes` → `My Log` (matches the
new parent name; reads cleanly as "WP Logbook → My Log").
- **Admin bar count menu** `WP Notes (N)` → `WP Logbook (N)`.
- **Dashboard widget** title `WP Notes` → `WP Logbook`.
- **Settings page H1** `WP Notes Settings` → `WP Logbook Settings`.
- **Main page H1** `WP Notes` → `WP Logbook`.
- **About page** every brand mention updated (intro card, "What WP
Logbook does" heading, "Go to My Log →" CTA on the side-by-side
intro that used to say "Go to WP Notes →").
- **About page version history** now leads with v3.2.0 (this
release) as `latest` and demotes v3.1.0 to the previous entry.
- **CPT `menu_name` label** `WP Notes` → `WP Logbook` (cosmetic
only; CPT is hidden from admin UI since the duplicate-form fix in
v3.1.0).
- **Migration notice text** "WP Notes needs to migrate…" → "WP
Logbook needs to migrate…".
- **Email-feedback subject** `[Site] WP Notes feedback from X` →
`[Site] WP Logbook feedback from X`. Body intro line same change.
- **Legacy feedback.php subjects** (`WP Notes Feedback` /
`WP Notes Help Request`) → `WP Logbook Feedback` /
`WP Logbook Help Request`. These render only if the unused legacy
feedback file is ever required-in; brought along for hygiene.
- **`error_log()` prefix** `[WP Notes]` → `[WP Logbook]`.
### Notably NOT changed (zero-migration commitment)
- **All internal function names** keep `wp_notes_*` prefix.
- **All constants** keep `WP_NOTES_*` names.
- **All DB option keys** (`wp_notes`, `wp_done_notes`,
`wp_notes_settings`, `wp_notes_migration_completed`,
`wp_notes_version`, `wp_notes_dismissed_empty_active|completed`)
stay as-is. **No data migration runs on upgrade.**
- **All `user_meta` keys** stay as-is.
- **Admin page slug** `wp-notes` stays — preserves bookmarks, the
admin-bar `#new-note` anchor, and the legacy
`?page=wp-notes-create` → `?page=wp-notes` redirect added in
v3.1.0.
- **Plugin text domain** `a-wp-notes` stays — would otherwise
invalidate any future translation files.
- **File and directory names** unchanged (`wp-notes.php`,
`inc/wp-notes-*.php`, `assets/wp-notes-banner.jpg`). The Gitea
repo `ranger/a-wp-notes-v3` is unchanged too — David can rename
it on the Gitea side separately if he wants.
The rename is **purely user-facing strings**. Existing installs see
the new name appear after a plugin file refresh, with zero behaviour
change. No re-activation needed.
### Changed — Leave Feedback form (more options, multi-select, wired to email)
The right-column "Leave Feedback" form on the About page has been
expanded from two radio buttons to **seven checkboxes** (users can
pick more than one), a new optional message textarea, and a submit
button that **actually does something** — it AJAX-posts to a new
WP handler that emails the site admin via `wp_mail()`.
**Form options (checkboxes — multi-select):**
- I have ideas to improve this plugin
- I need help with this plugin
- I found a bug
- I'd like to request a new feature
- I'd like to share my use case
- Just saying thanks 🍀
- Other
**Submission flow:**
1. Client-side: at least one checkbox OR a message is required;
otherwise an inline hint shows.
2. AJAX POST `wp_notes_submit_feedback` with topics[] + message +
nonce.
3. Server-side handler (`manage_options` capability + nonce checked)
sanitizes input, allow-lists the topic keys, then builds a plain-
text email and ships it to `get_option('admin_email')` via
`wp_mail()`. Reply-To is set to the submitting user's email so
the admin can reply directly.
4. Email body includes: sender (display name + email + WP login),
site URL, plugin version, the checked topics (pretty-labelled),
and the message.
5. Inline success message replaces the form on success; inline
error message lets the user retry on failure.
The old radio-button + broken `toggleSection('feedback-form-...')`
logic that pointed at non-existent IDs has been replaced entirely.
The `toggleSection()` helper is kept defined but is now genuinely
unused on the About page — flagged for removal in a future Tier-2
pass.
### Changed — About page rewritten (content + layout)
The About page (`Settings → WP Notes → About`) has been rewritten
from "wall of nested toggle boxes with outdated content" to
"readable cards with accurate content". The left column is now
three plain cards: **What WP Notes does**, **Who it's for**, and a
**Version history** that actually matches the current plugin
version.
What changed:
- **Removed two `<button>onclick=toggleSection()</button>` wrappers**
around the About and Version History sections. Users came to the
About page to read content — hiding it behind a toggle was
anti-UX.
- **"Version 2.0.3 (Current)"** entry was lying — the plugin is now
v3.1.0. Replaced the whole version section with a compact
accurate summary (v3.1.0 → v3.0.2 → v2.x → v2.0.0) plus a
prominent **"View the full CHANGELOG.md →"** link to the canonical
history on Gitea so the on-page summary doesn't have to be
exhaustive.
- **Removed redundant duplicate paragraphs** ("WP Notes is a
versatile plugin that caters to a wide range of users" appeared
twice in two lines).
- **Removed invalid HTML** — bare `<li>` outside `<ul>`, `<p>`
wrapping `<li>`.
- **Removed buried banner image + Buy-Me-A-Coffee** that were
inside the (hidden-by-default) Version History toggle, never
seen by anyone. The banner already lives at the top of the page
in the side-by-side intro row; the support link already lives in
the right-column feedback card.
- **Dropped the "Teachers want progress with Email Notifications"
use-case** — email notifications aren't implemented in v3.x so
the claim was misleading.
- **Added three CSS card classes** (`.wp-notes-about-card`,
`.wp-notes-about-card--versions`) for visual rhythm with the
intro row above, plus styling for the version entries (latest
pill, monospace version labels).
The right-column feedback box is unchanged. `toggleSection()` is
also unchanged — still defined (no harm, used by the feedback
form's broken-since-forever submit handler which is its own
Tier 2 problem).
---
## [3.1.0] — 2026-05-25
A single-day UX polish + bug-fix release. Nine commits worth of
+2 -2
View File
@@ -40,10 +40,10 @@ function wp_notes_admin_bar_menu($wp_admin_bar) {
$notes = get_option('wp_notes', array());
$count = count($notes);
// Main WP Notes menu item
// Main Logbook menu item
$wp_admin_bar->add_node(array(
'id' => 'wp-notes',
'title' => sprintf('WP Notes <span class="count">%d</span>', $count),
'title' => sprintf('Logbook <span class="count">%d</span>', $count),
'href' => admin_url('admin.php?page=wp-notes'),
));
+162 -135
View File
@@ -2,7 +2,7 @@
// wp-notes-about.php
/**
* Function to display the "About" page for WP Notes plugin.
* Function to display the "About" page for Logbook plugin.
*/
function wp_notes_about_page() {
?>
@@ -53,150 +53,123 @@ function wp_notes_about_page() {
<div class="wp-notes-about-intro">
<div class="wp-notes-about-intro__img">
<img src="<?php echo esc_url(WP_NOTES_URL); ?>assets/wp-notes-banner.jpg"
alt="WP Notes banner">
alt="Logbook banner">
</div>
<div class="wp-notes-about-intro__body">
<h2>WP Notes <span style="color:#646970; font-weight:400;">v<?php echo esc_html(WP_NOTES_VERSION); ?></span></h2>
<p>WP Notes is a lightweight task &amp; logbook plugin for WordPress. Log your daily work, mark tasks as done, and keep a tidy history right inside the dashboard &mdash; perfect for freelancers showing clients what's been delivered.</p>
<h2>Logbook <span style="color:#646970; font-weight:400;">v<?php echo esc_html(WP_NOTES_VERSION); ?></span></h2>
<p>Logbook is a lightweight task &amp; logbook plugin for WordPress. Log your daily work, mark tasks as done, and keep a tidy record right inside the dashboard &mdash; perfect for freelancers showing clients what's been delivered.</p>
<p style="margin-bottom:0;">
<a href="<?php echo esc_url(admin_url('admin.php?page=wp-notes')); ?>" class="button button-primary">Go to WP Notes →</a>
<a href="<?php echo esc_url(admin_url('admin.php?page=wp-notes')); ?>" class="button button-primary">Go to My Log →</a>
</p>
</div>
</div>
<style>
/* Hide most plugin notices on the About page — they push the
long-form content down and the user came here to read, not
to dismiss admin chatter. Scoped to the About page only by
virtue of living in this file. */
.notice, .updated, .error {
display: none !important;
/* Hide most plugin notices on the About page — they push
the long-form content down and the user came here to
read, not to dismiss admin chatter. */
.notice, .updated, .error { display: none !important; }
/* About-page content cards — same visual rhythm as the
intro row above. */
.wp-notes-about-card {
background: #fff;
border: 1px solid #ccd0d4;
border-radius: 4px;
padding: 20px 24px;
margin: 0 0 22px;
}
.wp-notes-about-card h2 {
margin: 0 0 10px;
font-size: 16px;
}
.wp-notes-about-card p:last-child { margin-bottom: 0; }
.wp-notes-about-card ul {
margin: 8px 0 0 18px;
list-style: disc;
}
.wp-notes-about-card ul li { margin-bottom: 4px; }
.wp-notes-about-card--versions ul { list-style: none; margin-left: 0; }
.wp-notes-about-card--versions li { margin-bottom: 12px; }
.wp-notes-about-card--versions .ver { font-weight: 600; color: #2271b1; }
.wp-notes-about-card--versions .latest { display: inline-block; margin-left: 6px; padding: 1px 7px; background: #00a32a; color: #fff; border-radius: 9px; font-size: 11px; font-weight: 600; }
.wp-notes-about-changelog-link {
display: inline-block;
margin-top: 6px;
font-size: 13px;
color: #646970;
}
</style>
<!-- About and Versions Section -->
<h1>About WP Notes v <?php echo esc_html(WP_NOTES_VERSION); ?></h1>
<div class="wp-notes-welcome" style="background: #fff; padding: 20px; margin: 20px 0; border: 1px solid #ccc;">
<button onclick="toggleSection('about-section')" style="background: #0073aa; color: #fff; border: none; padding: 8px 12px; cursor: pointer;">
Toggle About Section
</button>
<div id="about-section" style="margin-top: 10px; display: none;">
<!-- Content for About and Versions -->
<h2>About WP Notes</h2>
<p>
Thank you for using WP Notes! This plugin helps you manage your WordPress tasks and notes efficiently.
Keep track of your todo's, mark them as complete, and export your data for safekeeping. I made this
plugin for my website to log my daily tasks and notes as a logbook for my clients, so they can see the work
done by me or you and so PAY, as every task is important and the customers need to know what I've done.
</p>
<ul>
<li>Log all your tasks and jobs so prove work is being done.</li>
<li>As you can see, this plugin is very flexible and can be used for a wide range of WordPress users.</li>
<li>I hope you find it useful too.</li>
</ul>
<h1>What WP Notes Offers</h1>
<p>WP Notes is a versatile plugin that caters to a wide range of users:</p>
<p>We're excited to offer our plugin to our customers.</p>
<p>WP Notes is a versatile plugin that caters to a wide range of users:</p>
<p>We have several features for you to choose from:</p>
<ul>
<li><strong>Freelancers and site developers</strong> tracking work done on client sites.</li>
<li><strong>Teams</strong> wanting to document changes or keep a changelog in the dashboard.</li>
<li><strong>WordPress power users</strong> wanting a straightforward dashboard notes system.</li>
<li><strong>Small businesses</strong> needing a simple task management solution for WordPress.</li>
<li><strong>Students</strong> wanting to keep track of their daily tasks to prove work is being done.</li>
<li><strong>Teachers</strong> wanting to keep track of their students' progress with Email Notifications.</li>
</ul>
<p>
<li><strong>If you have any questions or feedback</strong>, feel free to reach out to us. Enjoy your note-taking experience!</li>
</p>
<p>
<li><strong>Join our community</strong> and unlock the full potential of WP Notes.</li>
</p>
</div>
<!-- What Logbook does -->
<div class="wp-notes-about-card">
<h2>What Logbook does</h2>
<p>Logbook is a lightweight task &amp; logbook plugin that lives entirely inside the WordPress admin. Type a note, format it with colour / size / font / emoji, mark it done when the work is finished, restore it later if you need to revisit. Everything is stored in the site's own database &mdash; nothing leaves your server.</p>
<p>I built it for my own client work as a transparent &ldquo;here's what I did this week&rdquo; logbook, so customers can see clearly what they're paying for. It turned out to be just as useful for any small team or single user who wants notes attached to the WordPress dashboard rather than a separate app.</p>
</div>
<p>
<!-- About and Versions Section -->
<h1>Version History v <?php echo esc_html(WP_NOTES_VERSION); ?></h1>
<div class="wp-notes-versions" style="background: #fff; padding: 20px; margin: 20px 0; border: 1px solid #ccc;">
<button onclick="toggleSection('version-section')" style="background: #0073aa; color: #fff; border: none; padding: 8px 12px; cursor: pointer;">
Toggle Version Section
</button>
<div id="version-section" style="margin-top: 10px; display: none;">
<h3>Version 2.0.3 (Current)</h3>
<!-- Who it's for -->
<div class="wp-notes-about-card">
<h2>Who it's for</h2>
<ul>
<li>Added Error Logging: Introduced a comprehensive error logging system to capture plugin-related errors. The logs are now viewable within the admin bar for easy troubleshooting.</li>
<li>Added Welcome Section and Version history</li>
<li>Added Image to main menu</li>
<li>Edit Function is now working</li>
<li>Popup notification with "Saved" status added</li>
<li>Error controls incorporated into code for stability</li>
<li>Enhanced `wp-config.php` for error capturing</li>
<li>Ability to restore older notes</li>
<li>Displays "Created by," "Modified by," and "Edited by" fields</li>
<li>Improved task-tracking feature with a dashboard interface</li>
<li>Added color, font, and size customization for notes</li>
<li>Added edit and mark-as-done functions</li>
<li>AJAX-based updates for smoother note editing</li>
<li>Added Create WP Note into Tools in admin bar</li>
<li>Added Create WP Note into Wp-Notes in admin bar</li>
<li>Added Activity Chart for activity tracking in admin bar Still needs Work</li>
<li><strong>Freelancers &amp; site developers</strong> tracking work done on client sites.</li>
<li><strong>Small teams</strong> documenting changes or keeping a project changelog inside the dashboard.</li>
<li><strong>WordPress power users</strong> who'd rather not jump out to a separate notes app.</li>
<li><strong>Small businesses</strong> needing a simple task list tied to their website.</li>
<li><strong>Students &amp; teachers</strong> logging daily progress.</li>
</ul>
<div class="wp-notes-header" style="margin-bottom: 30px;">
<img src="<?php echo esc_url(WP_NOTES_URL); ?>assets/wp-notes-banner.jpg"
style="max-width: 1200px; width: 100%; height: auto; margin-bottom: 20px;"
alt="WP Notes Banner">
</div>
<p>
<h3>Version 2.0.4 (New) Additions</h3>
<ul>
<li>Ability to Export and Import Notes from a JSON file or a CSV file</li>
<li>Email notification when a note is marked as complete</li>
<li>Email the Notes Owner when a note is marked as complete</li>
<li>Email the current user's email when a note is created</li>
<li>Email notification when a note is edited</li>
<li>Enhanced `wp-config.php` for error capturing</li>
<li>Ability to close sections such as What's New, Version 2.0.3, and the Welcome Section</li>
<li>Welcome popup message to greet users</li>
<li>A page for ChatGPT API support, including other options like Claude-API</li>
<li>Add Emoji Support and icons</li>
</ul>
<h3>Version 2.0.2</h3>
<!-- Version history (compact, accurate) -->
<div class="wp-notes-about-card wp-notes-about-card--versions">
<h2>Version history</h2>
<ul>
<li>Added user attribution for notes</li>
<li>Enhanced timestamp functionality</li>
<li>Added email export option</li>
<li>Improved dashboard widget synchronization</li>
<li>Added welcome section and version history</li>
<li>
<span class="ver">v3.3.5</span> &mdash; 25 May 2026 <span class="latest">latest</span><br>
New admin-menu icon: closed-book (<code>dashicons-book-alt</code>) replaces the generic cog wheel. Visually reinforces the &ldquo;Logbook&rdquo; identity in the WP sidebar.
</li>
<li>
<span class="ver">v3.3.4</span> &mdash; 25 May 2026<br>
Repo renamed on Gitea: <code>a-wp-notes-v3</code> &rarr; <code>a-logbook</code>. The plugin&rsquo;s update checker and the &ldquo;View on Gitea&rdquo; / &ldquo;View all releases&rdquo; / &ldquo;View full CHANGELOG&rdquo; links now point at the new path. Local folder also renamed to <code>a-logbook</code>. The plugin&rsquo;s internal storage and slugs are unchanged &mdash; no data migration.
</li>
<li>
<span class="ver">v3.3.3</span> &mdash; 25 May 2026<br>
Pure version bump to verify the end-to-end &ldquo;Check now&rdquo; flow against a publicly-hosted Gitea repo. No functional changes. If you can see this line, you successfully pulled the test release.
</li>
<li>
<span class="ver">v3.3.2</span> &mdash; 25 May 2026<br>
Update checker now falls back to Gitea's <code>/tags</code> endpoint when no formal Release object exists for the latest tag. Means a plain <code>git tag &amp;&amp; git push --tags</code> is enough to make the &ldquo;Check now&rdquo; button report a new version &mdash; no need to manually create a Release in the Gitea web UI.
</li>
<li>
<span class="ver">v3.3.1</span> &mdash; 25 May 2026<br>
Dropped the <code>WP</code> prefix &mdash; the plugin is now just <strong>Logbook</strong>. Cleaner name, also clears a potential WordPress.org trademark-policy hurdle if/when the plugin ever lands on the marketplace.
</li>
<li>
<span class="ver">v3.3.0</span> &mdash; 25 May 2026<br>
Self-hosted update checker. Settings &rarr; Updates panel polls the Gitea repo via its JSON API, compares against the running version, and shows a download link when a new release is tagged. Includes &ldquo;View on Gitea&rdquo; and &ldquo;View all releases&rdquo; quick links. No auto-install &mdash; manual download keeps things safe.
</li>
<li>
<span class="ver">v3.2.0</span> &mdash; 25 May 2026<br>
Renamed to <strong>WP Logbook</strong> to match what the plugin is actually becoming &mdash; a work logbook for freelancers and students, not just a notes pad. Menu submenu renamed <em>My Notes</em> &rarr; <em>My Log</em>. Internal storage and slugs unchanged, no data migration.
</li>
<li>
<span class="ver">v3.1.0</span> &mdash; 25 May 2026<br>
UX polish + bug-fix pass. Single-H1 cleanup, banner moved to this About page, menu renamed to <em>My Notes</em>, duplicate forms removed, empty-state notice now persistently dismissible per-user via <code>user_meta</code>, Tools shortcut retired with a backward-compatible redirect.
</li>
<li>
<span class="ver">v3.0.2</span> &mdash; 10 May 2025<br>
The &ldquo;without all the crap&rdquo; trim-back from the v1.1.5 feature-creep era (AI chat, tamagotchi, journal, speedtest&hellip; gone). Baseline of what the plugin is today: notes, admin bar, settings, import/export, about, updater.
</li>
<li>
<span class="ver">v2.x</span> &mdash; 2024<br>
Editor improvements, AJAX edits, mark-as-done / restore, color &amp; font / size customisation, JSON + CSV import/export, user-attribution &amp; timestamps, dashboard widget.
</li>
<li>
<span class="ver">v2.0.0</span> &mdash; initial release<br>
Basic note functionality with the dashboard widget and styling options.
</li>
</ul>
<h3>Version 2.0.1</h3>
<ul>
<li>Enhanced admin bar integration</li>
<li>Added timestamp to notes</li>
<li>Improved data preservation</li>
</ul>
<h3>Version 2.0.0</h3>
<ul>
<li>Initial release with basic note functionality</li>
<li>Dashboard widget integration</li>
<li>Note styling options</li>
</ul>
<br>
<!-- Buy Me a Coffee Button -->
<a href="https://www.buymeacoffee.com/davidkeanek" target="_blank">
<img src="https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=davidkeanek&button_colour=FFDD00&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff" alt="Buy me a coffee">
</a>
</div>
<a class="wp-notes-about-changelog-link" href="https://git.davidtkeane.com/ranger/a-logbook/src/branch/main/CHANGELOG.md" target="_blank" rel="noopener">View the full CHANGELOG.md →</a>
</div>
</div>
@@ -219,9 +192,24 @@ function wp_notes_about_page() {
</a>
<!-- Feedback Options -->
<form id="wp-notes-feedback-options" style="margin-top: 20px;">
<label><input type="radio" name="feedback" value="improve"> I have ideas to improve this plugin</label><br>
<label><input type="radio" name="feedback" value="help"> I need help with this plugin</label><br>
<input type="submit" value="Submit Feedback" style="background: #0073aa; color: #fff; border: none; padding: 8px 12px; cursor: pointer; margin-top: 10px;">
<p style="margin: 0 0 8px; font-weight: 600;">What's on your mind? <span style="font-weight: 400; color: #646970;">(pick any)</span></p>
<label style="display:block; margin-bottom:6px;"><input type="checkbox" name="feedback_topics[]" value="improve"> I have ideas to improve this plugin</label>
<label style="display:block; margin-bottom:6px;"><input type="checkbox" name="feedback_topics[]" value="help"> I need help with this plugin</label>
<label style="display:block; margin-bottom:6px;"><input type="checkbox" name="feedback_topics[]" value="bug"> I found a bug</label>
<label style="display:block; margin-bottom:6px;"><input type="checkbox" name="feedback_topics[]" value="feature"> I'd like to request a new feature</label>
<label style="display:block; margin-bottom:6px;"><input type="checkbox" name="feedback_topics[]" value="use-case"> I'd like to share my use case</label>
<label style="display:block; margin-bottom:6px;"><input type="checkbox" name="feedback_topics[]" value="thanks"> Just saying thanks 🍀</label>
<label style="display:block; margin-bottom:10px;"><input type="checkbox" name="feedback_topics[]" value="other"> Other</label>
<p style="margin: 12px 0 4px; font-weight: 600;">Anything to add? <span style="font-weight: 400; color: #646970;">(optional)</span></p>
<textarea name="feedback_message" rows="4" style="width: 100%; box-sizing: border-box;" placeholder="A few sentences with more detail…"></textarea>
<?php wp_nonce_field('wp_notes_feedback_submit', 'wp_notes_feedback_nonce'); ?>
<p style="margin-top: 12px; margin-bottom: 0;">
<input type="submit" id="wp-notes-feedback-submit" value="Submit Feedback" class="button button-primary">
</p>
<p id="wp-notes-feedback-status" role="status" style="margin: 10px 0 0; min-height: 1.4em;"></p>
</form>
<br>
<!-- Buy Me a Coffee Button -->
@@ -251,15 +239,54 @@ function wp_notes_about_page() {
}
}
// JavaScript to handle feedback form toggling
// Submit the feedback form → AJAX → wp_mail() to the site admin.
// No toggleSection() shuffle anymore (the old code targeted IDs
// that never existed). Validates that at least one topic is
// checked, otherwise the submit is a no-op with a hint.
document.getElementById('wp-notes-feedback-options').addEventListener('submit', function(e) {
e.preventDefault();
var feedbackType = document.querySelector('input[name="feedback"]:checked').value;
if (feedbackType === 'improve') {
toggleSection('feedback-form-improve');
} else if (feedbackType === 'help') {
toggleSection('feedback-form-help');
var form = e.target;
var btn = document.getElementById('wp-notes-feedback-submit');
var statusEl = document.getElementById('wp-notes-feedback-status');
var topics = Array.from(form.querySelectorAll('input[name="feedback_topics[]"]:checked')).map(function (i) { return i.value; });
var message = (form.querySelector('textarea[name="feedback_message"]').value || '').trim();
var nonce = (form.querySelector('input[name="wp_notes_feedback_nonce"]').value || '');
if (topics.length === 0 && message === '') {
statusEl.textContent = 'Pick at least one option (or write a message) before submitting.';
statusEl.style.color = '#8a2424';
return;
}
btn.disabled = true;
btn.value = 'Sending…';
statusEl.textContent = '';
statusEl.style.color = '';
var fd = new FormData();
fd.append('action', 'wp_notes_submit_feedback');
fd.append('nonce', nonce);
topics.forEach(function (t) { fd.append('topics[]', t); });
fd.append('message', message);
fetch(ajaxurl, { method: 'POST', credentials: 'same-origin', body: fd })
.then(function (r) { return r.json(); })
.then(function (res) {
if (res && res.success) {
form.innerHTML = '<p style="color:#1e6e40; font-weight:600; margin:0;">Thanks — your feedback has been sent. 🍀</p>';
} else {
statusEl.textContent = (res && res.data) ? String(res.data) : 'Sorry, something went wrong. Please try again.';
statusEl.style.color = '#8a2424';
btn.disabled = false;
btn.value = 'Submit Feedback';
}
})
.catch(function () {
statusEl.textContent = 'Network error — please try again in a moment.';
statusEl.style.color = '#8a2424';
btn.disabled = false;
btn.value = 'Submit Feedback';
});
});
</script>
<?php
+3 -3
View File
@@ -1,6 +1,6 @@
<?php
/**
* Feedback Form for WP Notes Plugin
* Feedback Form for Logbook Plugin
*
* This file contains the HTML and AJAX handling for the feedback form.
*/
@@ -95,7 +95,7 @@ function wp_notes_submit_feedback() {
// Example: Send feedback via email
$to = 'david@icanhelp.ie';
$subject = 'WP Notes Feedback';
$subject = 'Logbook Feedback';
$body = "Name: $name\nEmail: $email\nMessage: $message";
$headers = array('Content-Type: text/plain; charset=UTF-8');
@@ -148,7 +148,7 @@ function wp_notes_submit_help() {
// Example: Send help request via email
$to = 'david@icanhelp.ie';
$subject = 'WP Notes Help Request';
$subject = 'Logbook Help Request';
$body = "Name: $name\nEmail: $email\nMessage: $message";
$headers = array('Content-Type: text/plain; charset=UTF-8');
+9 -1
View File
@@ -6,7 +6,7 @@ if (!defined('ABSPATH')) {
}
/**
* Add required styles for WP Notes
* Add required styles for Logbook
*/
function wp_notes_admin_styles() {
?>
@@ -198,6 +198,14 @@ function wp_notes_admin_styles() {
color: #3c763d;
}
/* v3.4.0 — Active/Completed tab strip on the My Log page.
WP-admin core already styles .subsubsub and .current;
we only need to add breathing room above + below. */
.wp-notes-page .subsubsub,
.wrap > .subsubsub {
margin: 12px 0 18px;
}
/* Responsive Design */
@media screen and (max-width: 782px) {
.wp-notes-formatting {
+260 -39
View File
@@ -1,51 +1,272 @@
<?php
// Add this to your main plugin file
require plugin_dir_path(__FILE__) . 'vendor/plugin-update-checker/plugin-update-checker.php';
/**
* Logbook — self-hosted update checker against the Gitea repo.
*
* Polls the Gitea Releases API for the latest tagged release and
* compares its tag (e.g. "v3.3.0") against WP_NOTES_VERSION.
* Renders the result on the Settings page. NO auto-install — users
* download manually from Gitea. Safer for a self-hosted plugin and
* less surprising for admins.
*
* Result cached for 12h in a site transient so we don't hammer the
* Gitea host on every settings load. A manual "Check now" button
* force-refreshes the cache.
*/
// Include update checker - Assuming the 'vendor' directory is at the plugin root
// WP_NOTES_PATH is defined in your main plugin file (wp-notes.php)
// and points to the plugin's root directory.
require WP_NOTES_PATH . 'vendor/plugin-update-checker/plugin-update-checker.php';
if ( ! defined( 'ABSPATH' ) ) { exit; }
function wp_notes_setup_updater() {
if (!class_exists('YahnisElsts\PluginUpdateChecker\v5\PucFactory')) {
return;
// Gitea repo coordinates — change here if the repo ever moves.
if ( ! defined( 'WP_NOTES_GITEA_HOST' ) ) { define( 'WP_NOTES_GITEA_HOST', 'https://git.davidtkeane.com' ); }
if ( ! defined( 'WP_NOTES_GITEA_OWNER' ) ) { define( 'WP_NOTES_GITEA_OWNER', 'ranger' ); }
if ( ! defined( 'WP_NOTES_GITEA_REPO' ) ) { define( 'WP_NOTES_GITEA_REPO', 'a-logbook' ); }
/**
* Convenience: full web URL of the repo / its releases page.
*/
function wp_notes_gitea_repo_url() {
return WP_NOTES_GITEA_HOST . '/' . WP_NOTES_GITEA_OWNER . '/' . WP_NOTES_GITEA_REPO;
}
function wp_notes_gitea_releases_url() {
return wp_notes_gitea_repo_url() . '/releases';
}
/**
* Hit Gitea's /api/v1/repos/<owner>/<repo>/releases/latest endpoint
* and return a normalised array, or null on irrecoverable error.
*
* Cached for 12h. Negative responses (404 = no releases yet) cached
* for 1h so a freshly-tagged release shows up quickly.
*
* @param bool $force_refresh skip the transient cache.
* @return array|null { version, html_url, download_url, body, published_at, error_code? }
*/
function wp_notes_fetch_latest_release( $force_refresh = false ) {
$cache_key = 'wp_notes_gitea_latest';
if ( ! $force_refresh ) {
$cached = get_site_transient( $cache_key );
if ( is_array( $cached ) ) { return $cached; }
}
$myUpdateChecker = YahnisElsts\PluginUpdateChecker\v5\PucFactory::buildUpdateChecker(
'https://github.com/your-username/wp-notes/', // Change this to your GitHub repository
__FILE__,
'wp-notes'
$base_api = WP_NOTES_GITEA_HOST . '/api/v1/repos/' . WP_NOTES_GITEA_OWNER . '/' . WP_NOTES_GITEA_REPO;
// Try /releases/latest first — that's the canonical endpoint when David
// has published a formal Gitea Release via the web UI (with notes + zip
// assets attached).
$response = wp_remote_get( $base_api . '/releases/latest', array( 'timeout' => 8 ) );
if ( is_wp_error( $response ) ) {
return null;
}
$code = (int) wp_remote_retrieve_response_code( $response );
$body = ( $code === 200 ) ? json_decode( wp_remote_retrieve_body( $response ), true ) : null;
// Fallback: if no formal Release exists (typical for tag-only workflows
// where you `git tag v3.3.1 && git push --tags` without using the web
// UI), hit /tags instead and synthesise a release-like payload from the
// newest tag. Gitea's /tags endpoint sorts by creation order, so [0] is
// the most recent.
if ( $code !== 200 || ! is_array( $body ) || empty( $body['tag_name'] ) ) {
$tags_response = wp_remote_get( $base_api . '/tags?limit=1', array( 'timeout' => 8 ) );
if ( ! is_wp_error( $tags_response )
&& (int) wp_remote_retrieve_response_code( $tags_response ) === 200 ) {
$tags = json_decode( wp_remote_retrieve_body( $tags_response ), true );
if ( is_array( $tags ) && ! empty( $tags[0]['name'] ) ) {
$body = array(
'tag_name' => $tags[0]['name'],
'html_url' => wp_notes_gitea_repo_url() . '/src/tag/' . rawurlencode( $tags[0]['name'] ),
'body' => isset( $tags[0]['message'] ) ? $tags[0]['message'] : '',
'published_at' => isset( $tags[0]['commit']['created'] ) ? $tags[0]['commit']['created'] : null,
'assets' => array(),
);
$code = 200;
}
}
}
// Still nothing usable? Surface a friendly status and short-cache.
if ( $code !== 200 || ! is_array( $body ) || empty( $body['tag_name'] ) ) {
$info = array(
'version' => null,
'html_url' => wp_notes_gitea_releases_url(),
'download_url' => null,
'body' => '',
'published_at' => null,
'error_code' => $code,
);
set_site_transient( $cache_key, $info, HOUR_IN_SECONDS );
return $info;
}
// "v3.3.0" → "3.3.0" so version_compare() against WP_NOTES_VERSION works cleanly.
$version = ltrim( (string) $body['tag_name'], 'vV' );
// Prefer an explicit .zip asset attached to the release; fall back to
// Gitea's source-archive zip URL pattern.
$download_url = null;
if ( ! empty( $body['assets'] ) && is_array( $body['assets'] ) ) {
foreach ( $body['assets'] as $asset ) {
if ( isset( $asset['name'], $asset['browser_download_url'] )
&& substr( strtolower( $asset['name'] ), -4 ) === '.zip' ) {
$download_url = $asset['browser_download_url'];
break;
}
}
}
if ( ! $download_url ) {
$download_url = wp_notes_gitea_repo_url() . '/archive/' . rawurlencode( $body['tag_name'] ) . '.zip';
}
$info = array(
'version' => $version,
'html_url' => isset( $body['html_url'] ) ? esc_url_raw( $body['html_url'] ) : '',
'download_url' => esc_url_raw( $download_url ),
'body' => isset( $body['body'] ) ? wp_strip_all_tags( $body['body'] ) : '',
'published_at' => isset( $body['published_at'] ) ? $body['published_at'] : null,
);
// Optional: Set the branch that contains the stable release
$myUpdateChecker->setBranch('main');
set_site_transient( $cache_key, $info, 12 * HOUR_IN_SECONDS );
return $info;
}
add_action('init', 'wp_notes_setup_updater');
// Add update checking functionality
function wp_notes_check_for_updates() {
// Check if the 'check_update' GET parameter is set and the user has the capability to manage plugin options.
if (isset($_GET['check_update']) && $_GET['check_update'] === '1' && current_user_can('manage_a_wp_notes_options')) {
// Verify the nonce. The nonce name 'a_wp_notes_check_update_nonce' and action 'a_wp_notes_check_update_action'
// should match what's generated in admin-bar.php.
if (!isset($_GET['a_wp_notes_check_update_nonce']) || !wp_verify_nonce(sanitize_key($_GET['a_wp_notes_check_update_nonce']), 'a_wp_notes_check_update_action')) {
wp_die('Nonce verification failed!', 'Error', array('response' => 403));
/**
* Compare a fetched release against WP_NOTES_VERSION and return a
* status payload suitable for the Settings UI.
*
* @return array { status: 'available'|'up-to-date'|'unknown', current, latest?, message, ... }
*/
function wp_notes_update_status( $force_refresh = false ) {
$current = defined( 'WP_NOTES_VERSION' ) ? WP_NOTES_VERSION : '0.0.0';
$latest = wp_notes_fetch_latest_release( $force_refresh );
if ( ! $latest || empty( $latest['version'] ) ) {
$msg = 'No releases tagged on the Gitea repo yet. Once a release is published, this check will start returning a version.';
if ( $latest && ! empty( $latest['error_code'] ) && (int) $latest['error_code'] !== 404 ) {
$msg = 'Could not reach Gitea (HTTP ' . (int) $latest['error_code'] . '). Try again in a few minutes.';
}
// Trigger a manual update check
wp_clean_plugins_cache();
wp_update_plugins();
add_settings_error(
'wp_notes',
'update_check',
'Update check completed.',
'updated'
return array(
'status' => 'unknown',
'current' => $current,
'message' => $msg,
'repo_url' => wp_notes_gitea_repo_url(),
);
}
}
add_action('admin_init', 'wp_notes_check_for_updates');
// Note: For the PucFactory::buildUpdateChecker, the second argument `__FILE__` currently refers to
// `wp-notes-updater.php`. For the library to work correctly, this should typically be the path
// to your main plugin file (e.g., `WP_PLUGIN_DIR . '/a-wp-notes/a-wp-notes.php'`).
// This is a setup detail for the update checker rather than a direct security fix from this review.
if ( version_compare( $latest['version'], $current, '>' ) ) {
return array(
'status' => 'available',
'current' => $current,
'latest' => $latest['version'],
'html_url' => $latest['html_url'],
'download_url' => $latest['download_url'],
'published_at' => $latest['published_at'],
'body' => $latest['body'],
'message' => sprintf( 'A new version (v%s) is available — you are on v%s.', $latest['version'], $current ),
);
}
return array(
'status' => 'up-to-date',
'current' => $current,
'latest' => $latest['version'],
'message' => sprintf( 'You are up to date (v%s).', $current ),
'repo_url' => wp_notes_gitea_repo_url(),
);
}
/**
* AJAX: force a fresh check, return the status payload.
*/
add_action( 'wp_ajax_wp_notes_check_updates', 'wp_notes_ajax_check_updates' );
function wp_notes_ajax_check_updates() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( 'Insufficient permissions.', 403 );
}
check_ajax_referer( 'wp_notes_check_updates', 'nonce' );
delete_site_transient( 'wp_notes_gitea_latest' );
wp_send_json_success( wp_notes_update_status( true ) );
}
/**
* Render the "Updates" panel on the Settings page. Called from the
* settings render function via wp_notes_settings_page().
*/
function wp_notes_render_updates_panel() {
$status = wp_notes_update_status( false ); // use the cached value on initial load
$nonce = wp_create_nonce( 'wp_notes_check_updates' );
$repo_url = wp_notes_gitea_repo_url();
$rel_url = wp_notes_gitea_releases_url();
?>
<div class="wp-notes-updates" style="max-width:720px; margin-top:24px; padding:18px 20px; background:#fff; border:1px solid #ccd0d4; border-radius:4px;">
<h2 style="margin-top:0;">Updates</h2>
<p style="margin:0 0 12px;">
Logbook is self-hosted on Gitea. Click <strong>Check now</strong> to ask the repo whether there's a newer release than the one you're running.
</p>
<p id="wp-notes-update-status" style="margin:0 0 12px;">
<strong>Status:</strong>
<span id="wp-notes-update-status-text"><?php echo esc_html( $status['message'] ); ?></span>
<?php if ( $status['status'] === 'available' && ! empty( $status['download_url'] ) ) : ?>
<br>
<a href="<?php echo esc_url( $status['download_url'] ); ?>" class="button button-primary" style="margin-top:8px;">
Download v<?php echo esc_html( $status['latest'] ); ?> (.zip)
</a>
<?php if ( ! empty( $status['html_url'] ) ) : ?>
<a href="<?php echo esc_url( $status['html_url'] ); ?>" target="_blank" rel="noopener" style="margin-left:8px;">View release notes →</a>
<?php endif; ?>
<?php endif; ?>
</p>
<p style="margin:0 0 4px;">
<button type="button" id="wp-notes-check-updates-btn" class="button" data-nonce="<?php echo esc_attr( $nonce ); ?>">
↻ Check now
</button>
<a href="<?php echo esc_url( $repo_url ); ?>" target="_blank" rel="noopener" class="button" style="margin-left:6px;">View on Gitea</a>
<a href="<?php echo esc_url( $rel_url ); ?>" target="_blank" rel="noopener" class="button" style="margin-left:6px;">View all releases</a>
</p>
<p style="margin:10px 0 0; color:#646970; font-size:12px;">
Manual update path: download the .zip, deactivate the plugin in WordPress, upload via Plugins → Add New → Upload, reactivate. Your notes are stored in <code>wp_options</code> and survive the upgrade.
</p>
</div>
<script>
(function () {
var btn = document.getElementById('wp-notes-check-updates-btn');
var statusText = document.getElementById('wp-notes-update-status-text');
if (!btn || !statusText) { return; }
btn.addEventListener('click', function () {
var nonce = btn.getAttribute('data-nonce');
btn.disabled = true;
var orig = btn.textContent;
btn.textContent = '↻ Checking…';
statusText.textContent = 'Asking Gitea…';
var fd = new FormData();
fd.append('action', 'wp_notes_check_updates');
fd.append('nonce', nonce);
fetch(ajaxurl, { method: 'POST', credentials: 'same-origin', body: fd })
.then(function (r) { return r.json(); })
.then(function (res) {
if (!res || !res.success) {
statusText.textContent = (res && res.data) ? String(res.data) : 'Check failed.';
} else {
statusText.textContent = res.data.message || 'Check complete.';
if (res.data.status === 'available' && res.data.download_url) {
// Soft reload so the download / release-notes links render with the fresh data.
setTimeout(function () { window.location.reload(); }, 400);
}
}
})
.catch(function () { statusText.textContent = 'Network error — try again in a moment.'; })
.finally(function () {
btn.disabled = false;
btn.textContent = orig;
});
});
})();
</script>
<?php
}
+161 -36
View File
@@ -1,11 +1,11 @@
<?php
/**
* A-WP-Notes - WordPress Task Management Plugin
* Logbook — WordPress work-logbook plugin
*
* Plugin Name: A-WP-Notes
* Plugin Name: Logbook
* Plugin URI: https://icanhelp.ie/wp-notes
* Description: A plugin to add your notes to the WordPress dashboard with import/export functionality
* Version: 3.1.0
* Description: A lightweight task &amp; logbook plugin for WordPress. Log your daily work, mark tasks done, and keep a tidy record inside the dashboard. Perfect for freelancers showing clients what's been delivered and students proving work to teachers.
* Version: 3.4.2
* Requires at least: 5.0
* Requires PHP: 7.2
* Author: IR240474
@@ -33,7 +33,7 @@ if (!isset($wp_notes_init)) {
$wp_notes_init = true;
// Plugin Constants
if (!defined('WP_NOTES_VERSION')) define('WP_NOTES_VERSION', '3.1.0');
if (!defined('WP_NOTES_VERSION')) define('WP_NOTES_VERSION', '3.4.2');
if (!defined('WP_NOTES_FILE')) define('WP_NOTES_FILE', __FILE__);
if (!defined('WP_NOTES_PATH')) define('WP_NOTES_PATH', plugin_dir_path(__FILE__));
if (!defined('WP_NOTES_URL')) define('WP_NOTES_URL', plugin_dir_url(__FILE__));
@@ -44,7 +44,7 @@ if (!isset($wp_notes_init)) {
if (!function_exists('wp_notes_log_error')) {
function wp_notes_log_error($message) {
if (defined('WP_DEBUG') && WP_DEBUG === true) {
error_log('[WP Notes] ' . $message);
error_log('[Logbook] ' . $message);
}
}
}
@@ -162,23 +162,24 @@ if (defined('WP_NOTES_PATH')) {
require_once WP_NOTES_PATH . 'inc/wp-notes-feedback.php';
require_once WP_NOTES_PATH . 'inc/wp-notes-display.php';
require_once WP_NOTES_PATH . 'inc/wp-notes-styles.php';
require_once WP_NOTES_PATH . 'inc/wp-notes-updater.php';
}
// Admin Menu
function wp_notes_admin_menu() {
add_menu_page(
'WP Notes',
'WP Notes',
'Logbook',
'Logbook',
'manage_options',
'wp-notes',
'wp_notes_page_callback',
'dashicons-admin-generic',
'dashicons-book-alt', // closed book — reinforces the "logbook" identity
3
);
// "My Notes" — the main landing submenu. Same slug as the parent
// menu so clicking either WP Notes or My Notes lands on the same
// "My Log" — the main landing submenu. Same slug as the parent
// menu so clicking either Logbook or My Log lands on the same
// central dashboard (the parent's wp_notes_page_callback already
// renders form + active list + completed list).
//
@@ -190,8 +191,8 @@ function wp_notes_admin_menu() {
// content. Empty callback means only the parent's renderer fires.
add_submenu_page(
'wp-notes', // Parent slug
'My Notes', // Page title (browser tab)
'My Notes', // Menu label (sidebar)
'My Log', // Page title (browser tab)
'My Log', // Menu label (sidebar)
'manage_options', // Capability
'wp-notes', // Menu slug (matches parent → same page)
'' // Empty callback — see comment above
@@ -217,7 +218,7 @@ function wp_notes_admin_menu() {
'wp_notes_import_export_page'
);
// About submenu — the WP Notes brand is already carried by the
// About submenu — the Logbook brand is already carried by the
// parent menu, so the submenu can be plain-spoken.
add_submenu_page(
'wp-notes',
@@ -290,7 +291,7 @@ function wp_notes_settings_page() {
settings_errors('wp_notes_settings');
?>
<div class="wrap">
<h1>WP Notes Settings</h1>
<h1>Logbook Settings</h1>
<form method="post" action="options.php">
<?php
settings_fields('wp_notes_settings');
@@ -298,13 +299,21 @@ function wp_notes_settings_page() {
submit_button();
?>
</form>
<?php
// Updates panel — checks the Gitea repo for a newer release.
// Defined in inc/wp-notes-updater.php.
if ( function_exists( 'wp_notes_render_updates_panel' ) ) {
wp_notes_render_updates_panel();
}
?>
</div>
<?php
}
// Settings section callback
function wp_notes_section_callback() {
echo '<p>Configure default settings for WP Notes.</p>';
echo '<p>Configure default settings for Logbook.</p>';
}
// Font setting callback
@@ -657,6 +666,74 @@ function wp_notes_add_inline_scripts() {
</script>";
}
// Receive the "Leave Feedback" form from the About page and forward
// it to the site admin via wp_mail(). Topics is an array of short
// keys (improve / help / bug / feature / use-case / thanks / other);
// message is an optional free-text textarea. Sender info is taken
// from wp_get_current_user() so we never trust client-supplied
// identity. Capability-gated to manage_options because the About
// page is admin-only.
add_action('wp_ajax_wp_notes_submit_feedback', 'wp_notes_ajax_submit_feedback');
function wp_notes_ajax_submit_feedback() {
if (!current_user_can('manage_options')) {
wp_send_json_error('Insufficient permissions.', 403);
}
check_ajax_referer('wp_notes_feedback_submit', 'nonce');
$allowed_topics = ['improve', 'help', 'bug', 'feature', 'use-case', 'thanks', 'other'];
$raw_topics = isset($_POST['topics']) && is_array($_POST['topics']) ? wp_unslash($_POST['topics']) : [];
$topics = array_values(array_intersect($allowed_topics, array_map('sanitize_key', $raw_topics)));
$message = isset($_POST['message']) ? sanitize_textarea_field(wp_unslash($_POST['message'])) : '';
if (empty($topics) && $message === '') {
wp_send_json_error('Pick at least one topic or write a message.', 400);
}
$user = wp_get_current_user();
$site = get_bloginfo('name');
$to = get_option('admin_email');
$topic_labels = [
'improve' => 'Ideas to improve the plugin',
'help' => 'Needs help with the plugin',
'bug' => 'Reporting a bug',
'feature' => 'New feature request',
'use-case' => 'Sharing a use case',
'thanks' => 'Saying thanks',
'other' => 'Other',
];
$topics_pretty = array_map(function ($t) use ($topic_labels) { return $topic_labels[$t] ?? $t; }, $topics);
$subject = sprintf('[%s] Logbook feedback from %s', $site, $user->display_name ?: $user->user_login);
$body = "Feedback received via Logbook → About page\n";
$body .= str_repeat('-', 48) . "\n\n";
$body .= 'From: ' . ($user->display_name ?: $user->user_login) . ' <' . $user->user_email . ">\n";
$body .= 'Site: ' . home_url() . "\n";
$body .= 'Plugin: v' . WP_NOTES_VERSION . "\n\n";
$body .= "Topics:\n";
if (!empty($topics_pretty)) {
foreach ($topics_pretty as $label) { $body .= ' - ' . $label . "\n"; }
} else {
$body .= " (none selected)\n";
}
$body .= "\nMessage:\n";
$body .= $message !== '' ? $message . "\n" : "(no message provided)\n";
$headers = [
'Content-Type: text/plain; charset=UTF-8',
'Reply-To: ' . sanitize_email($user->user_email),
];
$sent = wp_mail($to, $subject, $body, $headers);
if (!$sent) {
wp_send_json_error('Email could not be sent. Check the site mail configuration.', 500);
}
wp_send_json_success(['delivered_to' => $to]);
}
// Persist a user's dismissal of the "No active/completed notes found"
// empty-state notice so it doesn't reappear on the next page load.
// Triggered by inline JS in wp_notes_add_inline_scripts() when the
@@ -743,7 +820,7 @@ function wp_notes_save_edit() {
}
/**
* Main WP Notes Page - Central hub for note management
* Main Logbook page — central hub for note management
*/
function wp_notes_page_callback() {
if (!current_user_can('edit_posts')) {
@@ -755,6 +832,14 @@ function wp_notes_page_callback() {
$total_notes = count($notes);
$total_done = count($done_notes);
// v3.4.0 — which tab is selected? Default 'active'. Whitelist-only.
$current_tab = isset($_GET['tab']) ? sanitize_key(wp_unslash($_GET['tab'])) : 'active';
if (!in_array($current_tab, array('active', 'completed'), true)) {
$current_tab = 'active';
}
$active_tab_url = esc_url(add_query_arg(array('page' => 'wp-notes', 'tab' => 'active'), admin_url('admin.php')));
$completed_tab_url = esc_url(add_query_arg(array('page' => 'wp-notes', 'tab' => 'completed'), admin_url('admin.php')));
// Get user settings
$settings = get_option('wp_notes_settings', array(
'default_font' => 'Arial',
@@ -763,7 +848,7 @@ function wp_notes_page_callback() {
?>
<div class="wrap">
<!-- Header Section with WordPress Admin Styling -->
<h1 class="wp-heading-inline">WP Notes</h1>
<h1 class="wp-heading-inline">Logbook</h1>
<span class="page-title-action">v<?php echo esc_html(WP_NOTES_VERSION); ?></span>
<hr class="wp-header-end">
@@ -775,7 +860,7 @@ function wp_notes_page_callback() {
<!-- Note Creation Form with WordPress Admin Styling -->
<div class="postbox">
<div class="postbox-header">
<h2 class="hndle">Add New Note</h2>
<h2 class="hndle">New Log Entry</h2>
</div>
<div class="inside">
<form method="post" class="wp-notes-form">
@@ -927,16 +1012,37 @@ function wp_notes_page_callback() {
</style>
<!-- Notes List -->
<h2>Notes Todo List:</h2>
<!-- Active Notes List -->
<h2>Log entries</h2>
<!-- v3.4.0 — WP-native subsubsub tab strip. Single-pane: only the
selected tab's section is rendered below. URL-driven state
(?tab=active|completed) so it's bookmarkable + refresh-stable.
WP-admin core supplies .subsubsub and .current styling. -->
<ul class="subsubsub">
<li>
<a href="<?php echo $active_tab_url; ?>"<?php echo $current_tab === 'active' ? ' class="current"' : ''; ?>>
<?php esc_html_e('Active', 'a-wp-notes'); ?>
<span class="count">(<?php echo (int) $total_notes; ?>)</span>
</a> |
</li>
<li>
<a href="<?php echo $completed_tab_url; ?>"<?php echo $current_tab === 'completed' ? ' class="current"' : ''; ?>>
<?php esc_html_e('Completed', 'a-wp-notes'); ?>
<span class="count">(<?php echo (int) $total_done; ?>)</span>
</a>
</li>
</ul>
<div style="clear:both;"></div>
<?php if ($current_tab === 'active') : ?>
<div class="wp-notes-active" id="active-notes">
<?php wp_notes_display_notes('active'); ?>
</div>
<!-- Completed Notes List -->
<?php else : ?>
<div class="wp-notes-completed" id="completed-notes">
<?php wp_notes_display_notes('completed'); ?>
</div>
<?php endif; ?>
<!-- Footer: support link (lives at the BOTTOM, not the top) -->
<p class="wp-notes-footer-support">
@@ -1230,25 +1336,44 @@ function wp_notes_handle_actions() {
exit;
}
// Handle restore note action
if (isset($_POST['restore_note']) && isset($_POST['done_ids'])) {
// Handle restore note action (per-row form on the Completed tab).
//
// v3.4.2 fix: this handler used to expect $_POST['done_ids'] — an
// array of IDs from bulk-action checkboxes that were removed in
// v3.1.0. The per-row Restore form in inc/wp-notes-display.php
// sends a hidden $_POST['note_id'] (singular) instead, so the old
// handler's isset($_POST['done_ids']) check was always false and
// the handler body never executed. Clicking Restore therefore
// looked like it did nothing — David's "the complete log does not
// return to active." The v3.4.1 redirect added below was correct
// but never reached because the handler was already dead code.
//
// Rewritten to match the working mark-done single-note handler
// pattern: expect note_id (singular), absint() it, isset() lookup
// in $done_notes, move with the restored_by annotation, then
// redirect to the Active tab where the restored note now lives.
if (isset($_POST['restore_note']) && isset($_POST['note_id'])) {
$notes = get_option('wp_notes', array());
$done_notes = get_option('wp_done_notes', array());
$note_id = absint($_POST['note_id']);
$current_user = wp_get_current_user();
$new_done_notes = array();
foreach ($done_notes as $key => $note) {
if (in_array($key, $_POST['done_ids'])) {
if (isset($done_notes[$note_id])) {
$note = $done_notes[$note_id];
$note['last_modified'] = current_time('mysql');
$note['restored_by'] = $current_user->display_name;
$notes[] = $note;
} else {
$new_done_notes[] = $note;
}
}
unset($done_notes[$note_id]);
update_option('wp_notes', $notes);
update_option('wp_done_notes', $new_done_notes);
update_option('wp_done_notes', $done_notes);
// Redirect to the Active tab — without this, the page falls
// through and re-renders with the URL's current ?tab=completed,
// so the restored note (now on Active) would be invisible.
wp_redirect(admin_url('admin.php?page=wp-notes'));
exit;
}
}
// Handle edit note action
@@ -1319,7 +1444,7 @@ function wp_notes_dashboard_widget() {
function wp_notes_add_dashboard_widgets() {
wp_add_dashboard_widget(
'wp_notes_dashboard_widget',
'WP Notes',
'Logbook',
'wp_notes_dashboard_widget'
);
}
@@ -1330,7 +1455,7 @@ function wp_notes_register_cpt() {
$labels = array(
'name' => __('Notes', 'a-wp-notes'),
'singular_name' => __('Note', 'a-wp-notes'),
'menu_name' => __('WP Notes', 'a-wp-notes'),
'menu_name' => __('Logbook', 'a-wp-notes'),
'add_new' => __('Add New', 'a-wp-notes'),
'add_new_item' => __('Add New Note', 'a-wp-notes'),
'edit_item' => __('Edit Note', 'a-wp-notes'),
@@ -1563,7 +1688,7 @@ function wp_notes_migration_notice() {
?>
<div class="notice notice-info is-dismissible">
<p>
<?php _e('WP Notes needs to migrate your existing notes to the new storage system.', 'a-wp-notes'); ?>
<?php _e('Logbook needs to migrate your existing notes to the new storage system.', 'a-wp-notes'); ?>
<a href="<?php echo esc_url(admin_url('admin.php?page=wp-notes-settings&action=migrate')); ?>" class="button button-primary">
<?php _e('Start Migration', 'a-wp-notes'); ?>
</a>