Version: 3.10.0
Highlights
Telegram Messenger Integration
Connect Leantime to Telegram to receive rich, auto-detected task notification cards directly in your chats, keeping your team informed without leaving their messenger. (#3726)
Redesigned Goals with Multi-Milestone Support
The goal dialog has been rebuilt with a split layout, monitor/manage tabs, and inline updates, and a single goal can now link to many milestones with a new chip UI and milestone rollup progress. (#3739, #3688, #3687, #3690)
New Features
- Period-Based Status Reports - Added period-based status report screens backed by a shared report engine and a period-aware actuals window for resources. (#3643, #3714)
- Mobile SSO - Introduced a generic OIDC mobile SSO bridge with one-time-code to bearer-token exchange, a public
/statusdiscovery endpoint advertising auth methods, and AdvancedAuth gating. (#3637, #3662, #3664, #3711) - User Capacity Fields - Added weekly hours and employment type to user profiles for better resource planning. (#3653)
- Resources Contract - Added a new Resources gateway surface with value objects, registry, and shared Blade primitives, including current-week actuals on the resource summary. (#3645, #3669)
- Canvas Events - Added a CanvasItemUpdated event as a foundation for change propagation. (#3691)
Bug Fixes
- Routing - Resolved plugin controllers whose folder name has an inner capital letter. (#3773)
- Ideas on Kanban - Fixed the "Edit" option for Ideas not working in the Kanban view. (#3752)
- Sidebar Projects - Admins and owners now see all projects they have access to, fixing an empty project sidebar. (#3710)
- Batch Fixes - Resolved a set of five reported bugs across the app. (#3738, #3632, #3685, #3636, #3692, #3746)
- Upgrades - The installer now self-heals a missing
zp_access_tokenstable and a stale session-cached db-version so updates can't get blocked. (#3745, #3735) - Blueprints - Restored board and item deletion by keeping the id on delete-confirm forms. (#3705)
- Files - File names now display in full with a title tooltip instead of being truncated to 10 characters. (#3734)
- Reports - The report deck no longer errors when a projectId is omitted, program supply now reflects capacity rather than booked hours, and fixed an HTML leak, decimal count, and empty burndown. (#3715, #3672, #3665)
- Telegram - Fixed a status handling bug so "Done" (status 0) is evaluated correctly. (#3747)
- Editor - Restored Backspace deletion of empty lines in the editor. (#3629)
- Timesheets - A user's own general-work (no-project) time now appears in account reads. (#3642)
- Tickets - Archived-project tickets are excluded from open ticket lists, status→Done history is logged with an accurate "last updated" time, and ticket parents are preserved on failed new-ticket submissions. (#3639, #3638, #3650, #3641)
- Onboarding - First-login completion now persists even when the user can't create tickets. (#3684)
- Content Templates - Fixed a slug bug, status label rendering, and typos. (#3644)
- Goals - Fixed invalid Goal input markup by using proper name=value pairs. (#3634)
Improvements
- Unified UI - Consolidated to one table design, one stat tile, and one period picker with report and button polish. (#3771)
- Navigation & Chrome - Standardized on the floating pill horizontal tab group, added a glass pageheader and gradient tabs, a Tasks board summary, a reusable subject switcher, and a single document-page mode. (#3728, #3716, #3680, #3676, #3677, #3717)
- Stakeholder Report - Refined the stakeholder report with a doc-shell header. (#3707)
- Performance - Batched capacity analyzer ticket reads into a single query. (#3697)
- Assets - CSS bundles are now cache-busted using the build mtime. (#3724)
Security
- JSON-RPC Authorization - Closed an account-takeover chain: any authenticated user could call the onboarding service over JSON-RPC to set another account's password and role, activate it, then disable its 2FA. The onboarding and 2FA services are no longer RPC-reachable, and a sweep of the remaining unguarded
@apimethods closed marketplace license-key exposure, cross-user dashboard/widget writes, unauthorized ticket-dependency rewrites, mention forgery, and installer/scheduler/queue triggers. (#3797) - Components - Escaped the statTile
subslot by default; markup now has to opt in via HtmlString. (#3774) - Open Redirects - Closed open redirect vulnerabilities in the TwoFA verify and POST flows and the Login POST handler. (#3770, #3750, #3658)
- Cross-Site Scripting - Escaped user-controlled group labels at the source and stripped markup from board/timeline tab aria-labels. (#3767, #3748, #3768)
- Blueprints - Validated the import file path to prevent SSRF/LFI and guarded phar:// plugin probes against open_basedir violations. (#3656, #3693, #3762)
- Access Control - Added a project ownership check to
getMilestone()to prevent IDOR. (#3657) - OIDC - Hardened the mobile SSO flow with a lock-guarded one-time-code consume, TTL, throttling, and atomic consumption. (#3662, #3663)
- Invitations - Stopped invite-email spam by sanitizing names and adding rate limits. (#3661)
- npm Advisories - Cleared all nine high-severity npm advisories and a critical websocket-driver finding. (#3769, #3679, #3667)
Localization
- Indonesian - Added an Indonesian (id-ID) translation. (#3764)
Dependency Updates
What's Changed
🎉 New Features
- feat(tickets): getMyClosedTicketsForDate @api + status-change primitive by @gloriafolaron in #3631
- feat(oidc): mobile SSO bridge — one-time-code → bearer token exchange (generic OIDC) by @gloriafolaron in #3637
- feat(status): public /status discovery endpoint — advertise auth methods for mobile SSO by @gloriafolaron in #3664
- feat(resources): ResourceSummary carries current-week actuals by @gloriafolaron in #3669
- feat(goals): activate the goal↔milestone edge model — helpers, dual-write, edge reads (PR 2) by @gloriafolaron in #3687
- feat(goals): multi-milestone chip UI — one goal, many milestones (PR 3) by @gloriafolaron in #3688
- feat(integrations): Add Telegram Messenger integration with auto-detection & rich task notification cards by @maruf-pfc in #3726
🐞 Bug Fixes
- fix(editor): delete empty lines with Backspace in the tiptap editor by @jeppekroghitk in #3629
- fix(projects): getProjectsByUserActivity excludes archived projects (projectStatus 'open') by @gloriafolaron in #3638
- fix(tickets): exclude archived-project tickets from getAllOpenUserTickets by @gloriafolaron in #3639
- fix(oidc): lock-guarded consumeCode — close the double-mint race (Copilot follow-up to #3662) by @gloriafolaron in #3663
- fix(reports): 3 audit findings — HTML leak, decimal count, empty burndown by @gloriafolaron in #3665
- fix(projects): admins/owners access all projects in getProjectsUserHasAccessTo (fixes empty sidebar) by @gloriafolaron in #3710
Other Changes
- Swap invalid "if" for valid name=value pairs in Goal input by @komali2 in #3634
- fix(timesheets): show a user's own general-work (no-project) time in account reads by @gloriafolaron in #3642
- fix(content-templates): polish batch — Phase 4 slug bug, status label rendering, typos by @gloriafolaron in #3644
- fix(oidc): mobile SSO hardening — TTL, throttle, body-only reads, atomic consume (follow-up to #3637) by @gloriafolaron in #3662
- feat(core): Resources contract surface — gateway, registry, value objects, shared blade primitives by @gloriafolaron in #3645
- fix(tickets): assign ticketParents on NewTicket POST validation-failure path (#3640) by @marcelfolaron in #3641
- feat(tickets): getMyClosedTicketsForRange for period close-lists by @gloriafolaron in #3647
- fix(tickets): record status→Done in history + report real last-updated by @gloriafolaron in #3650
- fix: add project ownership check to getMilestone() to prevent IDOR (CWE-639) by @javokhir-sec in #3657
- fix(security): stop invite-email spam via unsanitized names + missing rate limits by @marcelfolaron in #3661
- fix(security): resolve critical npm audit failure (websocket-driver) by @marcelfolaron in #3667
- feat(tickets): getMyCommentedTicketsForRange for Progress "Supported" by @gloriafolaron in #3649
- docs(oidc): reword consumeCode docblock to lead with the lock, not Cache::pull by @gloriafolaron in #3666
- feat(reports): period-based status report screens + shared report engine by @marcelfolaron in #3643
- feat(users): weekly_hours + employment_type on user (Slice 1) by @gloriafolaron in #3653
- feat(projects): additive addUserToProject() service method by @gloriafolaron in #3671
- fix(reports): program supply = capacity, not booked hours by @gloriafolaron in #3672
- fix(security): resolve new high npm audit findings (brace-expansion, shell-quote) by @marcelfolaron in #3679
- fix: validate file path in Blueprints::import() to prevent SSRF/LFI (CWE-918) by @javokhir-sec in #3656
- fix(blueprints): repair unit suite broken by import() path validation (#3656) by @marcelfolaron in #3693
- fix(help): readonly users can't complete first-login onboarding (Fixes #3683) by @marcelfolaron in #3684
- design(chrome): pageheader is a glass card (app-wide header modernization) by @gloriafolaron in #3676
- design(chrome): extract subjectSwitcher component + migrate To-Dos by @gloriafolaron in #3677
- design(chrome): nav-bar redesign — glass pageheader, gradient tabs, Tasks board summary by @gloriafolaron in #3680
- perf(reports): batch capacity analyzer ticket reads into one query by @gloriafolaron in #3697
- feat(oidc): gate the mobile OIDC bridge behind AdvancedAuth by @gloriafolaron in #3711
- design(reports): stakeholder report — doc-shell header + refinement pass by @gloriafolaron in #3707
- feat(goals): milestone rollup + getMilestonesByGoal @api for mobile Progress (PR 5) by @gloriafolaron in #3690
- feat(canvas): CanvasItemUpdated event (phase-2 propagation foundation) by @gloriafolaron in #3691
- style(reverse-wizard): move the reverse-flow stylesheet into the core bundle by @gloriafolaron in #3712
- style(reverse-wizard): rw-guide-sub class for muted guide sub-paragraphs by @gloriafolaron in #3713
- feat(resources): period-aware actuals window on the ResourcesGateway contract by @gloriafolaron in #3714
- fix(reports): stakeholder deck no longer 500s when the caller omits projectId by @gloriafolaron in #3715
- feat(components): navigation.tabs — one ARIA tab-group + RA chrome joins the core bundle by @gloriafolaron in #3716
- feat(css): body.lt-doc-page — one document-page mode for doc-shell pages by @gloriafolaron in #3717
- chore: bump plugins submodule to current main by @gloriafolaron in #3719
- style(css): drop the orphaned ra-page-active pageheader chrome by @gloriafolaron in #3723
- fix(assets): cache-bust the css bundles with the build mtime by @gloriafolaron in #3724
- test(install): cover update_sql_30524 + pin the TrackedBy edge as informational by @gloriafolaron in #3725
- design(nav): the floating pill group is THE horizontal tab standard by @gloriafolaron in #3728
- fix(goals): harden the goal↔milestone edge model (audit follow-up) by @gloriafolaron in #3729
- test(goals): pin the repository-level goal↔milestone link contracts by @gloriafolaron in #3730
- fix(core): self-heal the session-cached db-version after an update by @gloriafolaron in #3735
- feat(goals): goal dialog v2 — split layout, monitor/manage tabs, inline updates by @gloriafolaron in #3739
- fix(files): show full filename with title tooltip instead of 10-char truncation by @marcelfolaron in #3734
- fix(blueprints): delete-confirm forms drop the id — board/item delete silently no-ops (Fixes #3696) by @marcelfolaron in #3705
- fix: use resolveSafeRedirect() in Login POST handler to prevent Open Redirect (CWE-601) by @javokhir-sec in #3658
- chore(plugins): bump the submodule to plugins main (84 commits) by @marcelfolaron in #3742
- fix(install): self-heal a missing zp_access_tokens so 30504 can't block upgrades by @marcelfolaron in #3745
- fix: batch of five reported bugs (#3738, #3632, #3685, #3636, #3692) by @marcelfolaron in #3746
- fix(telegram): handle status 0 (Done) without empty() evaluation bug by @maruf-pfc in #3747
- Add Indonesian (id-ID) translation by @henryust12 in #3764
- fix: guard phar:// plugin probes against open_basedir violations by @plugmind-dev in #3762
- fix: "Edit" option for "Ideas" doesn't work with Kanban by @Roshan931 in #3752
- fix(tickets): escape user-controlled group labels at the source (XSS) by @marcelfolaron in #3767
- fix(tickets): strip markup from the board/timeline tab aria-labels (#3748) by @marcelfolaron in #3768
- changing rendering label to unescaping icon code by @mborecki in #3763
- fix(2fa): validate redirect in TwoFA Verify to close open redirect (#3741) by @chiliec in #3750
- chore(deps): clear all 9 high-severity npm advisories by @marcelfolaron in #3769
- fix(2fa): close the POST half of the TwoFA open redirect by @marcelfolaron in #3770
- fix(ui): one table design, one stat tile, one period picker; report + button polish by @marcelfolaron in #3771
- fix(routing): resolve plugin controllers whose folder has an inner capital by @marcelfolaron in #3773
- fix(components): escape statTile's
subby default, opt into markup via HtmlString by @marcelfolaron in #3774 - chore(plugins): bump the submodule to main for 3.10.0 by @marcelfolaron in #3775
- fix(api): close the JSON-RPC account-takeover chain and sweep unguarded @api methods by @marcelfolaron in #3797
- Release v3.10.0 by @marcelfolaron in #3772
New Contributors
- @komali2 made their first contribution in #3634
- @javokhir-sec made their first contribution in #3657
- @maruf-pfc made their first contribution in #3726
- @henryust12 made their first contribution in #3764
- @plugmind-dev made their first contribution in #3762
- @Roshan931 made their first contribution in #3752
- @mborecki made their first contribution in #3763
- @chiliec made their first contribution in #3750
Full Changelog: v3.9.8...v3.10.0