Helping Organisations get organised!

Changelog

  • 1.19.3: convert-descriptions-to-blocks: the missing-word check now counts a fenced code block’s language (β€œ`php, β€œ`json), which the conversion keeps as a language-* class name rather than visible text, so tasks with labelled code fences are no longer held.
  • 1.19.2: convert-descriptions-to-blocks: the missing-word check no longer runs words from neighbouring blocks together. Tags are now replaced with spaces before the text is compared, so the last word of one block and the first word of the next (for example two list items) stay separate words instead of being reported missing and holding a correctly converted task.
  • 1.19.1: Adds lh-tasks/convert-descriptions-to-blocks, a temporary ability for converting task descriptions stored as raw markdown (tasks created over MCP before 1.14.1) into block markup, so they open cleanly in the block editor (#148851). Dry run by default. HTML entities that kses stored are decoded first. A task is held, not saved, if the conversion is degraded or any word of the source is missing from the result. Saving changes post_content only: the previous content is kept as a revision, post_modified is kept so follow-up reminder timing is unchanged, and status, claims, taxonomies, meta and the progress log are untouched. Requires install_plugins. To be removed once conversion is complete.
  • 1.19.0: Block editor support (#148850). Tasks, queues, task types and priorities are now in the REST API, so tasks open in the block editor for staff. Security fix: the read_post visibility filter granted any meta capability (edit_post, delete_post, and others on an ID collision) to a task’s author, assignee or claimant; it now grants read_post only. Term reads over REST for all three task taxonomies need the task capability (new LH_Tasks_rest_terms_controller in includes/lh-tasks-rest-class.php). A block editor save that sends status=private without custom_status keeps the task’s current status instead of making it private (WP Statuses only handled this for an exact Referer match). REST saves keep exactly one known priority. get-task and list-tasks description_markdown now converts block content through LH Content Converter’s blocks-to-markdown bridge, keeping tables, separators and code languages; HTML and raw-markdown content convert as before.
  • 1.18.0: The workflow status for a task that has used up its retries is now Failed, with the key lh_tasks-fail_task, instead of Blocked (lh_tasks-block_task), which was easily confused with a task blocked by an unfinished prerequisite. Existing rows are moved to the new key on every site the first time it loads (status table schema 2.1); the same step repairs two keys stored before 1.17.1 validated statuses, lh_tasks-can_task (to Cancelled) and lh_tasks-resolved (to Resolved). A task that becomes Failed now also has its claim released, since nobody is working on it any more; previously the agent that failed it kept a live claim token. edit-task gains reset_retries, which sets retry_count back to 0 and is noted in the progress log, so a Failed task can be requeued with its full retries by setting status to New with reset_retries true. Ability descriptions now say Failed wherever they said Blocked for exhausted retries.
  • 1.17.2: Fix task follow-up notifications showing the wrong title and link on other sites. BuddyPress notifications are shared across the network, but the reminder text and link were worked out on whichever site was displaying them, so a task’s ID was looked up as an unrelated local post (e.g. a GatherPress event, or nothing, giving a bare “Reminder:”). Each follow-up notification now records the site its task lives on and its Tasks-tab link; the text is built on that site, so the same reminder reads the same everywhere. Older notifications without that record are matched to a site that holds a task with that ID (this site first, then the main site), or shown as a generic reminder. Completing or cancelling a task now clears only that site’s follow-up notifications, not those of tasks with the same ID on other sites.
  • 1.17.1: Ability declarations brought fully up to convention. get-task now declares its whole output: every task field, typed, the same shape list-tasks returns, instead of only the blocking fields. claim-next-task now declares the description_markdown it returns, and that payload can be null. edit-task’s status and reassign-task’s new_status now accept only real workflow status keys, listed in each description; until now any string was stored as the task’s status, so a mistyped key left the task in a status that does not exist. update-task and edit-task are no longer marked idempotent, since every call adds a progress-log entry. The renew-claim description now explains that write abilities called with the task’s task_id and claim_token renew the claim automatically up to the maximum lease, and that renew-claim itself is not capped; claim-next-task’s description points to the same.
  • 1.17.0: Write abilities can now carry a task. Every ability annotated as not read-only gains two optional parameters, task_id and claim_token. When they are given, the call is refused before anything runs unless the caller holds that task’s claim with that token, and a successful call renews the claim, so a task worked through ordinary write abilities is no longer released by the stale-claim sweep mid-work. Renewal stops 4 hours after the task was claimed (filter lh_tasks_return_max_claim_lease_seconds), after which the claim lapses as normal and the task must be claimed again; an explicit renew-claim is not capped. Each call made with a task is counted on the task, with the last ability, its time and outcome. The parameters are removed before the ability’s own code runs. A task_id that is not a task on the current site is looked up on the main site, so abilities on any site can work a main-site task. Unannotated abilities, mcp-adapter’s proxy abilities and lh-tasks’ own abilities are left unchanged; the filter lh_tasks_ability_takes_task_context can change which abilities take part. Now requires WordPress 7.1, for the Abilities API hooks this relies on.
  • 1.16.0: Tasks assigned to people now get followed up (#148487). People work through assigned_to rather than claims (only agents claim, through claim-next-task, and the ten-minute stale-claim sweep applies only to claims), so nothing reminded someone about a task they had been given. A daily run on each site now looks at open, live tasks that are hard-assigned, not claimed and not self-assigned. Once such a task is past its due date and has had a day without activity, or has no due date and a week without activity, its assignee gets a reminder. If it is still quiet three days later, the person who created the task is told, unless they are the assignee or have no email address, in which case that is noted on the task instead. Activity means any edit, status change or progress-log entry, and resets the cycle. Each step is logged on the task and sent both as a BuddyPress notification linking to the person’s Tasks tab and as an email; a task’s notifications are removed when it is completed or cancelled. The day counts can be changed with the lh_tasks_return_follow_up_quiet_days, lh_tasks_return_follow_up_overdue_quiet_days and lh_tasks_return_follow_up_escalate_days filters, and lh_tasks_follow_up_task can exclude a task. Deactivating the plugin removes the daily run.
  • 1.15.1: Priority terms keep their intended order on every site (#148849 follow-up). LH Term Order Fixer gives each new term an order equal to its term ID, which replaced the seeded order (low 10, normal 20, high 30, urgent 40) when 1.15.0 created the priority terms. That was harmless where all four were created together, low first, but a site whose first task needed only one priority would have created the others later with higher IDs, so for example low could outrank high. A missing priority now creates the whole missing set, low first, and writes the seeded order after the insert so it wins. An upgrade (priority schema 2) resets the order on any site where the four ranks are not rising from low to urgent, keeping rising ranks set elsewhere, and recounts the terms. Term counts now include tasks in every status, since tasks are never published, so the Priority column and filter show real numbers.
  • 1.15.0: Task priority is now a taxonomy (#148849). Priority used to be post meta written once at creation; it is now the lh_tasks_priority taxonomy with a locked set of terms (low, normal, high, urgent) that nobody can add to, rename or delete, and each task carries one. A priority’s rank comes from its term’s ‘order’ meta (low 10, normal 20, high 30, urgent 40), the network’s usual term-ordering convention, and claim-next-task and list-tasks both read it through one shared rank query in the new LH_Tasks_priority_class; a task with no priority still ranks as normal. On upgrade each site moves every task’s priority meta onto the matching term (a missing or unknown value becomes normal) and deletes the meta, keyed on the class’s own schema version. The abilities are unchanged at the edges: create-task and update-task still take priority as low, normal, high or urgent, and get-task and list-tasks still return it as a string. The Tasks list screen gains a Priority column and a priority filter, and the classic edit screen a single-choice Priority box; a block-editor panel is left for #148852.
  • 1.14.1: Fixes descriptions and notes losing text in angle brackets (#148605). create-task now converts its description from Markdown by default, as its schema always said: the ability passed input straight to create_task(), which defaults to html for PHP callers, so Markdown descriptions were stored unconverted with literal backticks and no paragraphs, and wp_kses_post() then removed or rewrote anything in angle brackets (inline-code tags became real HTML tags, processing instructions vanished, a < b > c became a bold tag). Inline code, fenced code and a bare < are now escaped by the Markdown conversion. Progress-log notes (add-note, reassign, renew, edit-task, update-task and marking a task done from the My Tasks tab) are now kept exactly as written, angle brackets included, through a new LH_Tasks_task_actions_class::sanitize_note(), which removes only invalid UTF-8 and control characters; every place a note is shown already escapes it. Tasks created through create-task before this release still hold unconverted Markdown in their content.
  • 1.14.0: list-tasks now returns compact rows by default (#150452). A new fields input chooses which task fields each row carries; task_id is always included. Without fields, rows carry task_id, title, status, queue, task_type, priority, due_date, assignee, assigned_to, is_blocked, waiting_on, source_plugin and source_post_id. description, description_markdown, payload, result, progress_log and the other fields now come back only when named. This changes the default output for existing callers: anything that read descriptions or payloads from list-tasks must now ask for them in fields, or use get-task, which still returns every field. include_progress_log still works and adds progress_log, the same as naming it. format_task() now takes an optional field list and only runs the lookups the requested fields need, so the Markdown conversion, term lookups, status row and dependency queries are skipped for fields that are not asked for.
  • 1.13.0: list-tasks can now find and sort any task. status takes one workflow status key or an array of them, listed with their labels in the parameter description, and status_group picks open (the default), terminal or all; all is the only group that includes Deferred. New filters: priority (one or several; a task with no priority counts as normal), source_plugin, source_post_id, due_before, overdue, created_after, updated_after (content or workflow status changed), is_blocked (true for tasks an unfinished prerequisite is holding back, false for the rest) and search (title and description). orderby sorts by priority (the default: highest first, then oldest, the order claim-next-task uses), created, updated or due_date (soonest first, undated last), and order reverses the direction. Dates are site-local, as Y-m-d or Y-m-d H:i:s. An unknown status or priority, a malformed date, or status and status_group together are refused with an error instead of returning nothing. Listings are now compact: progress_log is left out unless include_progress_log is true. Each row still has is_blocked and waiting_on, and the output schema now describes every field of a task. The whole listing is one database query, so total counts every match and paging is exact. Priority ranking now comes from one shared definition used by both the claim query and list-tasks.
  • 1.12.0: The task queue’s MCP abilities now each live in their own class under includes/abilities/, on a shared LH_Tasks_ability_base, with LH_Tasks_abilities_class registering the category and loading them. Every ability description now ends with the capability it needs (“Requires edit_others_posts.” by default), generated from the same method the permission check uses, so the documented capability follows the lh_tasks_return_required_capability filter. Every input parameter is now described, including task_id, priority, description_format, reason_type, page and per_page. Inputs are closed, so a misspelt parameter name is now rejected instead of silently ignored. list-tasks’ output schema now lists tasks, total, page and total_pages. Behaviour, annotations and the other output schemas are unchanged.
  • 1.11.0: New Tasks tab on members’ own BuddyPress profiles, so people who are assigned tasks but have no staff access can see and finish them. The tab appears only to the member on their own profile, on sites running BuddyPress, and only while they have open tasks on that site. It lists the open tasks assigned to, claimed by or asked for by them, oldest first, with each task’s status, due date, description and five most recent notes. From the tab they can add a note to any of those tasks, and mark a task done when it is assigned to them, nobody else holds a claim on it and it is not waiting on an unfinished prerequisite. Marking done sets the task to Resolved, records them as its assignee, logs a marked_done entry with their optional note and fires lh_tasks_task_completed, the same action as complete-task. The forms post back to the profile, so members never go through wp-admin. Live tasks only: drafts and tasks pending review are left out. Filters: lh_tasks_return_slug (tab slug, default tasks), lh_tasks_show_tab, lh_tasks_return_max_tasks (100) and lh_tasks_return_max_notes_shown (5). Developers get LH_Tasks_task_actions_class::complete_assigned_task() and check_can_mark_done().
  • 1.10.0: New lh-tasks/add-note ability for adding a note to a task without claiming it, so anyone can add context to a task they are not working on. The note is appended to the task’s progress log as event “note” with the time and who added it, and shows in get-task. It does not change status, claim or assignment and works on any status, including finished tasks. Notes are plain text, up to 4000 characters; an empty or overlong note is refused. Each note fires lh_tasks_task_note_added. The ability is in its own class and its description and parameters follow the ability conventions.
  • 1.9.0: New lh-tasks/update-task ability for changing a task after it was created. It can change the title, description (markdown by default, or HTML), payload, priority, due date, task type, max retries and blocked_by, and reschedule a task that has not activated yet. Only the fields passed change, and an empty value clears payload, due date, task type or blocked_by. Everything is checked before anything is written: blocked_by uses the same validation as create-task and also refuses the task itself and any loop through other tasks, and an unknown parameter is rejected. It works on claimed tasks, and each call adds an “updated” progress-log entry naming the fields changed, who changed them and who held the claim, and fires lh_tasks_task_updated. Status, queue, assignment and the claim stay with edit-task and the claim-token abilities. Task posts now keep revisions, so an overwritten title or description can be recovered. The ability is in its own class, the first lh-tasks ability to follow the one class per ability convention.
  • 1.8.0: Blocked tasks no longer stop the queue, and claims now go by priority. Previously claim-next-task took the oldest New task first and only then checked whether it was blocked, releasing it again if so; one old blocked task at the head of a queue made every claim come back empty until its prerequisite finished. Task dependencies now live in their own table (wp_lh_tasks_dependencies) instead of the blocked_by post meta, which is moved across automatically on each site’s next request and then deleted, so the claim query skips blocked tasks in the same atomic step. Claims now take the highest priority first (urgent, high, normal, low; no priority counts as normal), then the oldest, and claim-next-task accepts an optional task_type to claim only one kind of work. When nothing is claimed it now says why: empty, race_lost, or for a task_id claim not_claimable (with the current status), assigned_elsewhere or blocked (with waiting_on, the unfinished prerequisites). get-task now returns blocked_by from the new table plus is_blocked and waiting_on. create-task checks blocked_by before creating anything: every ID must be an existing, untrashed task, duplicates are dropped, and a bad ID fails with an error naming it. A prerequisite counts as finished when it is Resolved, Closed or Cancelled, or trashed or deleted; Deferred and the retry-exhausted Blocked status keep holding their dependents. When a prerequisite is cancelled, trashed or deleted, tasks still waiting on it in New move to Pending with a progress-log note naming it, so a person decides whether they still make sense; setting one back to New goes ahead without it. Moves into Cancelled now fire lh_tasks_task_cancelled, and a flagged dependent fires lh_tasks_task_prerequisite_dropped. Uninstalling also removes the dependencies table.
  • 1.7.8: Now requires PHP 8.2, matching LH Content Converter, which this plugin requires and which needs PHP 8.2 for its bundled conversion library. No change to how the plugin works.
  • 1.7.7: Task description conversion now goes through LH Content Converter, the renamed LH Markdown Converter, which uses Automattic’s blocks engine. Markdown descriptions are now saved as the block markup the editor itself produces: list items and nested lists as real list-item blocks, quotes with inner paragraphs, and tables with header and body rows. The required plugin is now lh-content-converter, the plugin header declares PHP 8.1 which that plugin needs, and the degraded-mode notice names LH Content Converter. The version is now read from the plugin header.
  • 1.7.6: Version bump 1.7.5 -> 1.7.6 for the MCP ability annotations added earlier today (lhcp_f1b0b719). Bumped plugin header Version, plugin_version(), and readme.txt Stable tag; added a 1.7.6 changelog entry and trimmed readme.txt to the last 3 versions (dropped 1.7.3).
  • 1.7.5: Version bump 1.7.4 -> 1.7.5 (plugin header + plugin_version()) for this session’s fix: restored the missing vendor/imath/wp-statuses library and removed the class_exists duplicate-declaration guard from all 9 plugin files, which was silently preventing the CPT, hooks, and MCP abilities from ever registering due to PHP/OPcache compile-time early binding of the parentless LH_Tasks_plugin class.
  • 1.7.4: Wire in the new LH_Tasks_task_actions_class (already on disk) via require_once, and bump version 1.7.3 -> 1.7.4 (patch: structural extraction, no behavior change yet – the class is loaded but nothing calls it until the next step updates mcp-abilities-class’s call sites). Part of the lh-tasks god-class split, task-actions-class step 1 of 3 (create -> wire in -> update mcp-abilities-class call sites -> trim old class, done as separate applies to keep each change reviewable).
  • 1.7.3: Wire in the new LH_Tasks_admin_ui_class (already on disk) via require_once, and bump version 1.7.2 -> 1.7.3 (patch: pure structural extraction, no behavior change). Part of the lh-tasks god-class split, admin-ui-class step.
  • 1.7.2: Wire in the new LH_Tasks_visibility_class (already on disk) via require_once, and bump version 1.7.1 -> 1.7.2 (patch: pure structural extraction, no behavior change). Part of the lh-tasks god-class split, visibility-class step.
  • 1.7.1: Wire in the new LH_Tasks_migrations_class (already on disk from the prior file-creation) via require_once, and bump version 1.7.0 -> 1.7.1 (patch: pure structural extraction, no behavior change). Part of the lh-tasks god-class split, step 1.
  • 1.7.0: Version bump 1.6.2 -> 1.7.0 (minor: new renew-claim MCP ability) in plugin header and plugin_version(). Part of task 147122.
  • 1.6.2: Added a general-purpose one-time migration (maybe_migrate_orphaned_workflow_status_posts, hooked on init priority 25, gated by option lh_tasks_orphaned_workflow_status_migrated_1_6_2) that finds any task post whose native post_status is one of return_statuses()’s own workflow keys with no corresponding lh_tasks_status row – a distinct, older gap than the 1.6.1 legacy-publish fix, from before the 1.5.0 status-table introduction itself. Backfills a status-table row per orphan (using the post’s own post_status plus its real post_date_gmt/post_modified_gmt) and corrects native post_status to the active status. Fixed two orphaned posts (142646, 142647) and verified zero remain.
  • 1.6.1: Fixed a bug in claim_next() where the active post_status used in the atomic claim query was a hardcoded ‘publish’ literal, stale against the 1.6.0 custom-status migration (lh_tasks-active replaced publish as the live/claimable state) – meaning every task created after 1.6.0 shipped was silently unclaimable, while legacy pre-1.6.0 rows still on ‘publish’ kept working by coincidence. active_post_status is now an explicit required parameter to claim_next() rather than a hardcoded literal. Also added maybe_migrate_legacy_publish_status(), a one-time option-gated migration moving any task post still on native post_status ‘publish’ onto the active status.

If you like this plugin please consider making a donation to further development