Move issue from detail page with redirect stub for old references #2988
Service Desk opened 1 day ago

Motivation

Issues are sometimes filed in the wrong project after discussion has already started. Users need to move an issue while keeping comments/history, and keep old references (issue links, commit messages, bookmarks) resolving to the new location.

Today, move is only available from the Issues list Operations menu (Move Selected Issues To... / Move All Queried Issues To...). That flow forces leaving the issue detail page and re-selecting the issue, which loses context and risks moving the wrong issue. There is also no redirect left behind at the old project/number, so old project#number / KEY-number references and URLs break after renumbering.

Related discussion: issue #2982.

Goals

  1. Redirect stub for the original issue so links to the old project/number (and old issue URL) redirect to the moved issue's new location.
  2. Move operation on the issue detail side panel, next to the existing copy-reference control.
  3. Do not rewrite existing textual references in other issues/PRs/comments. Scanning all markdown is too slow; the stub already makes old references resolve.

Non-goals (for this issue)

  • Rewriting markdown references across the instance after a move
  • Rewriting immutable commit messages (covered by stub redirect instead)
  • Changing the existing list Operations move entry points (they should keep working; stub behavior should apply there too)

Requirements

A. Redirect stub (required)

When an issue is moved from project S (number N) to project T (new number M):

  • Leave a stub at S / N (and corresponding project key form if applicable) that redirects to the issue at T / M.
  • Opening the old issue URL in S must land on (or clearly redirect to) the issue in T.
  • Resolving references such as S#N, S/path#N, and SKEY-N must reach the moved issue.
  • The old number N remains reserved / occupied by the stub in S so it is not reused by a new issue.
  • Comments, attachments, and discussion remain on the moved issue record (same as today's move); the stub exists for reference/URL continuity, not as a second discussion thread.
  • Stub lifetime: permanent unless a later design explicitly adds cleanup (none required for v1).
  • Permissions: users who can view the destination issue should be able to follow the redirect; exact stub visibility rules should match normal issue read access as closely as practical.

B. Detail-page move UX (required)

On the issue detail side info panel, beside the copy-reference control in EntityReferencePanel / IssueSidePanel:

  • Add an icon with tooltip "Move issue" (no extra vertical space).
  • Clicking opens the standard project selector dropdown.
  • After choosing a destination project, use the same confirmation pattern as the list move (confirm by typing yes).
  • On success, navigate to the issue in the destination project (and ensure a stub remains at the old location per A).

C. Permissions

  • Require issue management on the source project (consistent with today's list move).
  • Also require appropriate rights on the destination project (issue management / ability to have issues managed there — destination must have issue management enabled and be accessible), so users cannot move issues into projects they cannot manage.

D. Existing list move

  • Keep list Operations move actions.
  • After this work, both detail-page move and list move must create the redirect stub (A). Same backend path preferred.

E. What stays as today

  • Issue entity id, comments, attachments, watches, votes, and id-based links remain on the moved issue.
  • Issue is renumbered in the destination project via existing IssueService.move(...) numbering.
  • #number references inside the moved issue's own description/comments may continue to be migrated for the batch being moved (existing behavior).
  • Schedules/iterations that are invalid for the destination continue to be dropped as today.

Architectural decisions (from issue #2982)

TopicDecision
Old reference integrityStub/redirect at original project+number (compulsory)
Rewrite refs in other issues/PRsOut of scope — too slow to scan all markdown; stub is sufficient
Detail-page controlIn scope — side panel icon + project selector
List moveRemains; must share stub semantics
PermissionsSource issue management + destination rights

Implementation notes (starting points)

  • Backend move: IssueService.move / DefaultIssueService.move
  • List UI: IssueListPanel Operations menu
  • Detail side panel: IssueSidePanel + EntityReferencePanel (reference + copy icon only today)
  • New work needed: stub entity or equivalent redirect record; reference/URL resolution against stubs; detail-page move affordance wired to move + stub creation

Acceptance criteria

  1. From an issue detail page, a user with the permissions in (C) can move the issue via the side-panel control without going through the list.
  2. After move, the issue opens in the destination project with history preserved.
  3. Old URL and old textual references to source#oldNumber / SOURCEKEY-oldNumber resolve to the moved issue via stub/redirect.
  4. Old number is not reused for a different issue in the source project.
  5. Other issues/PRs/comments are not bulk-rewritten; they still work because of the stub.
  6. List Operations move also leaves a stub.
  7. Users lacking source or destination rights cannot perform the move.

Test plan

  • Move via detail side panel across projects; verify navigation, history, attachments, comments.
  • Move via list Operations; verify same stub behavior.
  • Hit old URL and resolve old references from another issue description, a PR description, and a commit message link.
  • Confirm old number cannot be assigned to a newly created issue in the source project.
  • Negative: user without destination manage rights cannot move.
  • Negative: destination with issue management disabled is not selectable / move is rejected.
1/1
Type
Improvement
Priority
Normal
Assignee
Labels
No labels
Issue Votes (0)
Watchers (2)
Reference
OD-2988
Please wait...
Connection lost or session expired, reload to recover
Page is in error, reload to recover