github Leantime/leantime v3.9.0
Leantime v3.9.0

latest releases: latest, v3.9.1
4 hours ago

Version: 3.9.0

Highlights

Native Permission Engine

A brand-new permission engine now governs access across the entire application, replacing ad-hoc role checks with a centralized, fail-closed authorization layer. The engine was rolled out domain by domain — Tickets, Comments, Users, Clients, Settings, Sprints, Wiki, Ideas, Goals, Blueprints, Canvas, Calendar, Timesheets, Files, Reports, and the Projects service — closing numerous cross-user IDORs and information-disclosure oracles along the way. (#3461, #3469, #3471, #3472, #3473)

Unified Blueprints Domain

The 16 separate canvas variant domains have been consolidated into a single unified Blueprints domain with native Laravel controllers, dramatically reducing duplicated code while preserving every canvas type. Legacy Canvas and Strategy code now lives as thin deprecated adapters over Blueprints. (#3483, #3486)

JSON-RPC API

A new JSON-RPC API layer replaces the legacy REST/JSON controllers, with migrated endpoints for Tickets, Users, Ideas, Calendar, Notifications, Reactions, Sessions, Submenu, and more, plus a plugin gate attribute and capability-discovery endpoint for extensions. (#3457)

New Features

  • Mobile Push Notifications - Mobile devices can now register Expo/FCM push tokens against access tokens and receive ticket and unread-count notifications via a new dispatcher (#3398, #3401, #3457)
  • Logic Model Board - Added a Logic Model canvas board with WorkStructure orchestration (#3455)
  • WorkStructure Meta-Model - New WorkStructure meta-model infrastructure underpinning structured work hierarchies (#3454)
  • Task Collaborators - Completed multi-collaborator support for tasks, ensuring collaborators always receive ticket notifications and appear with full metadata across list and widget views (#1099)
  • "Not Assigned" Milestone Filter - Added a "Not assigned" option to the tickets milestone filter (#3452)
  • Stage Row Hover Spotlight - Hovering a stageflow row now spotlights it by dimming non-hovered stages (#3481)
  • Blade Component Tiers - Introduced tiered Blade components with a central HTMX event convention (#3441)

Bug Fixes

  • Postgres Compatibility - Fixed write limits, sequences, pdo_pgsql handling, and JS escaping for PostgreSQL deployments (#3447)
  • Auth & Dashboard 500s - Hardened authentication and the dashboard against server errors and offline hangs (#3448)
  • Marketplace 500s - Stopped marketplace errors by coercing API data into typed model properties (#3446)
  • Ticket Editor Role on Save - Enforced the editor role when saving and correctly honored milestone project changes (#3445)
  • Ticket Modal Milestone Crash - Fixed a crash in the ticket modal when handling milestone-type items
  • Milestone & Wiki Dialogs - Restored data binding in the milestone and wiki dialogs (#3444)
  • Save Notes from All Notes Grid - Notes saved from the All Notes grid now land in a default notebook (#3451)
  • Weekly Timesheet Grid - Blank cells are now skipped when saving the weekly grid (#3449)
  • Comment Edit/Reply Box - The edit and reply box now opens above the replies thread (#3450)
  • Comments Template Variable - Stopped the comments template from shadowing the controller's comments array (#3459)
  • Client Discussion Count - Fixed an inaccurate discussion count on the client detail page (#3426)
  • Client Detail 500 - Resolved an undefined constant crash on the client detail page
  • Value Proposition Canvas - Fixed an undefined variable on the Value Proposition Canvas (#3402)
  • Logic Model Status Filter - Left-aligned the status filter dropdown menu on the Logic Model board (#3484)
  • Ticket List Variables - Guarded undefined groupBy/newField variables in ticket list views
  • Helm Session Expiration Units - Corrected session expiration units in the Helm chart (#3487, #3378)
  • Mobile Responsiveness - Responsive stabilization sweep for phones and tablets (#3442)

Improvements

  • Dashboard Performance - Eliminated full-page repaints and cross-widget loading-indicator churn and sped up hot paths via session locking, query dedupe, and improved event patterns (#3439, #3443)
  • Thin Controllers - Modernized every domain controller from run() to get()/post() thin-controller patterns across the entire codebase
  • Typed Exceptions - Added a typed exception hierarchy and JSON-RPC response types, with RPC-only service methods throwing typed exceptions on denial
  • Consolidated Permission Migrations - Merged the 14 permission-engine migrations into a single install migration (#3506)
  • Static Analysis - Raised PHPStan static analysis to level 1 (#3440)

Security

  • projectIdParam Validation - Project ID parameters are now validated as real positive integers and fail closed when a mandatory ID can't be resolved (#3507, #3509)
  • Projects Service Hardening - Gated the Projects god-service with recursion-safe manager+ management checks (#3505)
  • Calendar IDOR Fixes - Gated the Calendar domain and closed cross-user IDORs (#3504)
  • Reports Surface Reduction - Gated the Reports domain and de-exposed the system/telemetry RPC surface (#3501)
  • Files IDOR Fixes - Gated the Files domain and closed RPC IDORs (#3492)
  • Timesheets Authorization - Gated the Timesheets domain with own-time vs manage separation and matrix edit (#3490)
  • Goals & Canvas Authorization - Gated the Goals and consolidated Canvas domains and closed by-id IDORs (#3483, #3485, #3486)
  • Comment Authorization - Scoped comment authorization to the host entity's real project and closed the comment-existence oracle in reaction methods
  • Tags IDOR - Closed the Tags getTags IDOR and removed dead REST controllers
  • CSRF Hardening - Excluded install routes from CSRF verification while forms are tokenized
  • Comprehensive Remediation - Comprehensive remediation across seven vulnerability families

Dependency Updates

  • Bumped shell-quote from 1.8.2 to 1.8.4 to clear a critical advisory (#3502)
  • Bumped icalendar and dotlottie-wc, removed Dependabot config (#3438)
  • Bumped webpack-cli from 5.1.4 to 7.0.3 (#3413)
  • Bumped katex from 0.16.21 to 0.17.0 (#3410)
  • Bumped fullcalendar from 6.1.17 to 6.1.20 (#3417)
  • Bumped @fullcalendar/luxon3 from 6.1.17 to 6.1.20 (#3409)
  • Switched html2canvas and jsPDF to npm-managed dependencies (#3482)

What's Changed

Other Changes

  • Fix case sensitivity in secondary color setting by @Delvar in #3348
  • Fix syntax for Referrer-Policy header assignment by @Delvar in #3347
  • build(deps): bump brace-expansion by @dependabot[bot] in #3340
  • Update ko-KR.ini — complete Korean translation by @madrobotnet in #3326
  • build(deps-dev): bump yauzl from 3.2.0 to 3.2.1 by @dependabot[bot] in #3320
  • make MarketplacePlugin model properties nullable for PHP 8.x by @shaunchokshi in #3356
  • Add collaborator support to task assignment and UI (list + kanban) by @juarezsousa-ctrl in #3337
  • fix: Postgres compat + plugin install null-safety (bundles 4 issues) by @marcelfolaron in #3359
  • fix: moveTicket, KPI linking, backlog filter on Postgres, tiptap list rendering by @marcelfolaron in #3360
  • fix: wiki sidebar width, dark-mode editor dropdowns, emoji ESC propagation by @marcelfolaron in #3361
  • Backend API surface for Leantime Mobile (TestFlight) by @gloriafolaron in #3395
  • Fix three bugs: isset on null param, redundant limit(1), malformed URL by @YoussefMansour9 in #3393
  • Implement Arabic language by @Mohd-PH in #3370
  • fix: add error403.blade.php to resolve crash on permission-gated pages by @Copilot in #3365
  • fix: Resend Invite button silently no-ops due to isset(null) check by @mojotaker in #3392
  • Fix favorites star spinning indefinitely after click on Project Hub by @Copilot in #3364
  • refactor: migrate all .tpl.php/.sub.php/.inc.php templates to Blade by @marcelfolaron in #3362
  • fix: 6 high-impact bugs — PDO crash, commenter role, Wiki Postgres, Ideas canvasId, Kanban scroll by @marcelfolaron in #3397
  • fix(calendar): tolerate MySQL zero-date sentinel in getCalendar by @gloriafolaron in #3396
  • Security: comprehensive remediation across 7 vulnerability families by @marcelfolaron in #3399
  • Modernize all controllers from run() to get()/post() by @marcelfolaron in #3408
  • fix: Value Proposition Canvas 500 (Undefined variable $currentCanvas) (#3402) by @marcelfolaron in #3422
  • Refactor: thin controllers / fat services across all non-canvas domains by @marcelfolaron in #3420
  • refactor: consolidate canvas domains into a unified Blueprints domain (YAML-driven) by @marcelfolaron in #3421
  • feat(api): JSON-RPC migration foundation + Tickets pilot by @marcelfolaron in #3424
  • feat(api): JSON-RPC migration batch 2 — Submenu, Sessions, Reactions, Notifications by @marcelfolaron in #3425
  • Fix 500 on client detail page: undefined constant generalComment by @Roark1138 in #3423
  • fix(clients): show accurate discussion count on client detail page by @marcelfolaron in #3428
  • feat(api): JSON-RPC migration batch 3 — Calendar (+ delete dead Ideation) by @marcelfolaron in #3426
  • feat(api): JSON-RPC migration — Ideas (with project-access authorization) by @marcelfolaron in #3427
  • feat(api): JSON-RPC migration — Users JSON endpoints by @marcelfolaron in #3429
  • Retire the legacy Api REST controllers (binary/upload → domains, Projects sort → JSON-RPC, delete dead Tags/Timer) by @marcelfolaron in #3430
  • refactor(blueprints): native Laravel controllers, drop the dispatch bridge by @marcelfolaron in #3432
  • feat(core): typed exception hierarchy + JSON-RPC response types by @marcelfolaron in #3431
  • fix(tickets): guard undefined $groupBy/$newField in list views by @marcelfolaron in #3433
  • cleanup: remove dead $x = $x; Blade self-assign artifacts by @marcelfolaron in #3434
  • refactor: merge Strategy domain into Blueprints by @marcelfolaron in #3435
  • feature: RPC-only service methods throw typed exceptions on denial by @marcelfolaron in #3436
  • Fix #1099 by @duongynhi000005-oss in #3405
  • feat(tickets): finish collaborators feature — notifications, persistence & hardening (#1099) by @marcelfolaron in #3437
  • build(deps): bump @fullcalendar/luxon3 from 6.1.17 to 6.1.20 by @dependabot[bot] in #3409
  • build(deps): bump fullcalendar from 6.1.17 to 6.1.20 by @dependabot[bot] in #3417
  • build(deps): bump katex from 0.16.21 to 0.17.0 by @dependabot[bot] in #3410
  • build(deps-dev): bump webpack-cli from 5.1.4 to 7.0.3 by @dependabot[bot] in #3413
  • chore(deps): bump icalendar + dotlottie-wc, remove Dependabot by @marcelfolaron in #3438
  • perf: speed up dashboard & hot paths (session locking, query dedupe, event patterns) by @marcelfolaron in #3439
  • ci(phpstan): raise static analysis to level 1 by @marcelfolaron in #3440
  • fix(mobile): responsive stabilization sweep for phones & tablets by @marcelfolaron in #3442
  • perf(dashboard): stop full-page repaints & cross-widget loading-indicator churn by @marcelfolaron in #3443
  • feat(components): Blade component tiers + central HTMX event convention by @marcelfolaron in #3441
  • fix: restore data binding in milestone & wiki dialogs by @marcelfolaron in #3444
  • fix: repair #3441 regressions blocking acceptance (app.js bundle syntax + stale install step) by @marcelfolaron in #3453
  • fix(tickets): enforce editor role on save + honor milestone project change by @marcelfolaron in #3445
  • fix(plugins): stop marketplace 500s by coercing API data to typed model props by @marcelfolaron in #3446
  • fix(db): PostgreSQL compatibility (write limits, sequences, pdo_pgsql, JS escaping) by @marcelfolaron in #3447
  • fix(ldap): return a normal auth error instead of a 500 on bad credentials by @marcelfolaron in #3448
  • fix(comments): open the edit/reply box above the replies thread by @marcelfolaron in #3450
  • feat(tickets): add "Not assigned" milestone filter option (#3252) by @marcelfolaron in #3452
  • feat(core): WorkStructure meta-model infrastructure by @marcelfolaron in #3454
  • fix(timesheets): skip blank cells when saving the weekly grid by @marcelfolaron in #3449
  • fix(wiki): save notes from the All Notes grid into a default notebook by @marcelfolaron in #3451
  • feat(notifications): mobile push device tokens (Expo + FCM) + getUnreadCount RPC (#3398) by @gloriafolaron in #3401
  • feat(logicmodelcanvas): Logic Model board + WorkStructure orchestration by @marcelfolaron in #3455
  • feat(notifications): mobile push tokens on access_tokens + FCM/Expo dispatcher by @gloriafolaron in #3457
  • fix(comments): stop shadowing controller $comments array in Discussion template by @gloriafolaron in #3459
  • feat(core): native permission engine foundation (Phase 0) + console addCommand fix by @marcelfolaron in #3461
  • feat(permissions): enforce Tickets + Comments via the engine — full @api coverage by @marcelfolaron in #3469
  • feat(permissions): roll the engine across the Users domain (company-scope) by @marcelfolaron in #3471
  • feat(permissions): roll the engine across the Clients domain (company-scope) by @marcelfolaron in #3472
  • feat(permissions): roll the engine across the Setting domain (company + project scopes) by @marcelfolaron in #3473
  • feat(permissions): roll the engine across the Sprints domain (first content domain) by @marcelfolaron in #3474
  • feat(api): plugin gate attribute + capability discovery endpoint by @gloriafolaron in #3460
  • fix(tickets): ticket modal crashes on milestone type by @gloriafolaron in #3468
  • security(comments): scope comment authz to the host entity's real project by @marcelfolaron in #3476
  • security(ideas): gate Ideas domain on the native permission engine by @marcelfolaron in #3478
  • security(wiki+ideas): gate the Wiki and Ideas domains on the native permission engine by @marcelfolaron in #3479
  • security(blueprints): gate the consolidated canvas domain + close by-id IDORs by @marcelfolaron in #3483
  • security(goalcanvas): gate the Goals domain + close by-id IDORs by @marcelfolaron in #3485
  • security(canvas): gate the legacy Canvas-base controllers (Logicmodelcanvas) + Api\Canvas by @marcelfolaron in #3486
  • fix(helm): correct session expiration units in Helm chart (fixes #3378) by @dashitongzhi in #3487
  • fix(logicmodelcanvas): left-align status filter dropdown menu by @gloriafolaron in #3484
  • feat(stageflow): spotlight non-hovered stages on row hover by @gloriafolaron in #3481
  • feat(libs): npm-manage html2canvas + jsPDF by @gloriafolaron in #3482
  • security(timesheets): gate the Timesheets domain (own-time vs manage) + matrix edit by @marcelfolaron in #3490
  • security(files): gate the Files domain + close RPC IDORs by @marcelfolaron in #3492
  • chore(logicmodelcanvas): drop unused snapshot mount div by @gloriafolaron in #3491
  • security(reports): gate the Reports domain + de-expose system/telemetry RPC surface by @marcelfolaron in #3501
  • fix(deps): bump shell-quote 1.8.2 → 1.8.4 (clear critical advisory) by @marcelfolaron in #3502
  • security(calendar): gate the Calendar domain + close cross-user IDORs by @marcelfolaron in #3504
  • security(projects): gate the Projects god-service (recursion-safe, manager+ management) by @marcelfolaron in #3505
  • refactor(install): consolidate the 14 permission-engine migrations into one by @marcelfolaron in #3506
  • security(permissions): fail closed when a mandatory projectIdParam can't be resolved by @marcelfolaron in #3507
  • ci(release): one-dispatch release pipeline (version bump, AI changelog, PR gate, auto-publish) by @marcelfolaron in #3508
  • security(permissions): validate projectIdParam is a real positive integer by @marcelfolaron in #3509
  • ci(release): swap changelog generation from GitHub Models to the Claude API by @marcelfolaron in #3510
  • Release v3.9.0 by @marcelfolaron in #3511
  • ci(release): set up PHP before make package (fixes v3.9.0 release build) by @marcelfolaron in #3512

New Contributors

Full Changelog: v3.7.3...v3.9.0

Don't miss a new leantime release

NewReleases is sending notifications on new releases.