1# Annotations Plugin for DokuWiki 2 3Word- and sentence-level annotations on wiki pages, stored out-of-band with threaded replies. Inspired by [Hypothes.is](https://hypothes.is/) and [ep_comments_page](https://github.com/ether/ep_comments_page). 4 5## Features 6 7- **Text-quote anchoring** — select any word or sentence; the annotation is tied to the exact quoted text plus surrounding context, so it survives minor edits. 8- **Threaded replies** — any reader can reply to an existing annotation. 9- **Open / Resolved status** — mark discussions closed; resolved annotations turn green. 10- **Gutter markers** — small icons in the left margin show at a glance where annotations live. 11- **Orphan detection** — when the annotated text is removed from the page, the annotation is flagged as orphaned and accessible via the counter. Admins can bulk-delete orphans. 12- **Per-user toggle** — users can turn the annotation overlay on or off via the usersettings plugin. 13- **No page revisions** — annotations are stored in a separate JSON file per page; the wiki changelog is never touched. 14 15## Requirements 16 17- DokuWiki Librarian 2025-05-14b (or compatible release) 18- PHP 8.0 or later with `mbstring` extension 19- [usersettings plugin](https://github.com/tracker-user/dokuwiki-usersettings) *(optional — adds per-user on/off toggle)* 20 21## Installation 22 231. Copy the `annotations/` directory into `{DokuWiki}/lib/plugins/`. 242. If you want the per-user toggle, install the usersettings plugin too. 253. No additional configuration is required. 26 27## Usage 28 29### Reading annotations 30 31Annotated text is highlighted in **amber** (open) or **green** (resolved). Click any highlight to open the thread panel inline below that paragraph. 32 33The counter bar above the page content shows the total number of annotations. If there are orphaned annotations (text was deleted), a clickable "N orphaned" link opens a drawer at the bottom of the page. 34 35### Creating an annotation 36 371. Select any text on the page. 382. Click the **Annotate** button that appears. 393. Type your comment and click **Annotate** to save. 40 41> Requires: logged in + at least read access on the page. 42 43### Replying 44 45Open the thread panel for any annotation and use the **Reply** field at the bottom. 46 47### Resolving / Reopening 48 49Click **Resolve** on any annotation to mark the discussion closed. Any reader can resolve or reopen. 50 51### Editing and deleting 52 53- Authors can edit or delete their own annotations and replies. 54- Admins can edit or delete any annotation or reply. 55 56### Admin: bulk operations 57 58Admins see two extra buttons in the counter bar: 59 60| Button | Effect | 61|--------|--------| 62| **Clear resolved** | Permanently deletes all resolved annotations on the page | 63| **Clear orphaned** | Server-side re-check, then permanently deletes orphaned annotations | 64 65### Disabling the overlay 66 67Via **User Preferences** (usersettings plugin) → uncheck **Enable annotations**. The overlay is hidden for that user; annotations are still stored. 68 69## Permission model 70 71| Action | Who | 72|--------|-----| 73| Create annotation / reply | Logged in + AUTH_READ on page | 74| Resolve / Reopen | Logged in + AUTH_READ on page | 75| Edit / delete own annotation or reply | The author | 76| Edit / delete any annotation or reply | Admins | 77| Clear resolved / Clear orphaned (per-page) | Admins | 78 79Note: **edit access is not required** to create an annotation. Groups whose page edit access is blocked can still annotate. 80 81## Storage 82 83Each page's annotations are stored at: 84 85``` 86{data}/meta/{namespace}/{page}.annotations 87``` 88 89Format (JSON): 90 91```json 92{ 93 "version": 1, 94 "annotations": [ 95 { 96 "id": "a1b2c3d4e5f6g7h8", 97 "anchor": { 98 "exact": "lossless source", 99 "prefix": "must use a ", 100 "suffix": ". Transcodes", 101 "start": 21 102 }, 103 "author": "alice", 104 "created": 1716336000, 105 "modified": 1716336000, 106 "body": "Does this cover remuxes?", 107 "status": "open", 108 "resolved_by": "", 109 "resolved_at": 0, 110 "replies": [ 111 { 112 "id": "x1y2z3a4b5c6d7e8", 113 "author": "bob", 114 "created": 1716336100, 115 "modified": 1716336100, 116 "body": "Yes, remuxes count." 117 } 118 ] 119 } 120 ] 121} 122``` 123 124## AJAX API 125 126All actions go to `/lib/exe/ajax.php?call=annotations`. 127 128| Action | Method | Token | Required fields | 129|--------|--------|-------|-----------------| 130| `load` | GET | — | `id` | 131| `create` | POST | ✓ | `id`, `anchor`, `body` | 132| `reply` | POST | ✓ | `id`, `annId`, `body` | 133| `edit_annotation` | POST | ✓ | `id`, `annId`, `body` | 134| `edit_reply` | POST | ✓ | `id`, `annId`, `replyId`, `body` | 135| `delete_annotation` | POST | ✓ | `id`, `annId` | 136| `delete_reply` | POST | ✓ | `id`, `annId`, `replyId` | 137| `resolve` | POST | ✓ | `id`, `annId`, `status` | 138| `clear_resolved` | POST | ✓ | `id` | 139| `clear_orphaned` | POST | ✓ | `id` | 140 141POST bodies are `application/json`. All responses: `{"success": true, ...}` or `{"success": false, "error": "..."}`. 142 143## Files 144 145``` 146annotations/ 147├── plugin.info.txt Plugin manifest 148├── helper.php Storage, CRUD, orphan detection, permission rules 149├── action.php Event hooks, JSINFO injection, AJAX endpoint 150├── script.js Front-end: selection, anchoring, highlights, panels, AJAX 151├── style.css Theme-compatible CSS (uses DokuWiki __token__ vars) 152├── lang/ 153│ └── en/ 154│ └── lang.php English strings 155├── README.md This file 156├── DESIGN.md Architecture & design reference (for developers) 157└── LICENSE GPL 2 158``` 159 160> **Developers:** [DESIGN.md](DESIGN.md) covers the architecture — anchoring, 161> storage, the JSINFO-injection mechanism, the permission model, and open gaps. 162 163## Browser compatibility 164 165The JavaScript targets Firefox 78 ESR and later (no `#private` fields, no `??=`, no `<dialog>`, no `Array.at`). Should work in all modern browsers. 166 167## License 168 169GPL 2, matching DokuWiki. 170