Bulk Page Operations

Bulk Page Operations for Confluence

Move, copy, label and archive up to 500 selected Confluence pages in one job, after a dry run shows you what will happen to each page.

Bulk Page Operations is a Forge app for Confluence Cloud. It adds a page to each space's settings. You pick pages with CQL or by choosing a parent page, pick what to do with them, check the dry run, and start the job. The job runs from the open page, a few pages at a time and as you, while you watch its progress. You can cancel it, and you can undo label changes later.

What it does

OperationWhat happens in Confluence
MoveEach top-level selected page moves under the target page, children included
CopyEach page is copied under the target page, either on its own or with all its children
Add labelsUp to 10 labels are added to every selected page
Remove labelsUp to 10 labels are removed from every selected page
ArchiveEach selected page is archived with Confluence's archive API

You can select pages in two ways.

The dry run

Nothing changes until you've seen a preview. It lists every selected page with one of three results.

The preview also reads your permissions in the space (or in the target page's space for move and copy) and warns you if Confluence says you can't edit, add or archive pages there. It's a warning, not a block. Confluence still decides page by page when the job runs, because page restrictions can differ.

When you start the job, the server runs the same search and plan again. If any selected page or its result differs from the preview, the job doesn't start and you're asked to preview again. That holds even when the count is the same, for example when one page drops out of a CQL date range and another comes in. Children that aren't selected aren't part of this check, so a copy with children copies the children a page has when the job runs.

Running jobs

Jobs run from the settings page of the person who started them. The page calls the app's runJobBatch resolver again and again. Each call handles up to 5 pages (it starts no new page after about 12 seconds) and saves progress after every page. These are normal user-invoked resolver calls, so every Confluence request runs as you (asUser()) with your permissions. Keep the page open while the job runs. If you close it, the job pauses where it was. Open the page again and choose "Resume job", or cancel it. A paused job stops blocking new jobs in the space after 15 minutes, and you can still resume it later if no other job has started there. Only the person who started a job can run or resume it.

The app used to run jobs in the background with Forge async events. Forge doesn't give asUser() a usable user there. On a live site every page failed with PROXY_ERR. So there's no background step any more.

The page shows a progress bar, the counts and the first 100 failures with Confluence's own error message for each. Details in the job history lists every page of a job with its result (only for the person who started it).

You can always see and cancel the jobs you started. Other people's jobs are listed only for space admins, and only a space admin can cancel someone else's job. The server checks this with Confluence as you each time (your space operations must include administer on the space). If Confluence doesn't confirm it, you see only your own jobs. Admins see the operation, status and counts of other people's jobs. Page titles, the target page's title and failure details are shown only to the person who started the job.

Undo for label jobs

Label jobs keep an undo log for 30 days. It records only what the job changed. A label that was already on a page before an add job isn't removed by undo, and a page that never had the label isn't touched.

The job notes each label change in the log before it sends it. If Confluence's answer gets lost (a server error after the change was made, or a batch cut off mid-request), the next attempt reads the page's labels again and logs the change if it happened. If the page fails for good, the job reads the labels once more and keeps only the changes it finds. Undo also checks each page before it changes anything, so it only reverses a label that's still in the state the job left it in. Only the person who started the job can undo it. Undo runs as a new job you can watch like any other, and if it fails or is cancelled you can run it again.

Move, copy and archive jobs can't be undone by the app. The confirmation dialog says so before you start.

Permissions

Every call to Confluence runs as the person using the app (asUser()), from resolver calls made by the open settings page. That includes the job batches. There are no background steps and no asApp() calls. The app never calls Confluence as itself. So a job can only do what that person could do by hand, and the server runs a job's batches only for the person who started it.

ScopeWhy
search:confluenceCQL page search
read:confluence-content.summaryRead search results and Confluence task status
write:confluence-contentMove, copy, archive, add and remove labels (v1 label POST and DELETE; v2 has no label write)
read:page:confluenceCheck the target page and the parent page, read page labels (v2)
read:space:confluenceFind the space and read your permissions in it
storage:appJob records, progress and undo logs (30 days)

The app has no external permissions, no remote back end and no egress. All code runs on Atlassian's Forge platform, so it's built to qualify for Runs on Atlassian. Atlassian decides eligibility, and forge eligibility checks it after deploy.

Once a week a scheduled trigger reports the stored account IDs to Atlassian with the Forge personal data reporting API (privacy.reportPersonalData). Job records of accounts that Atlassian reports as closed lose their account ID.

PRIVACY.md lists what's stored and for how long.

Limits

WhatLimitWhat you see
Pages per job500 selected pages (copy with children also copies every child of each page)More than 500 matches blocks the job with "narrow the selection"
CQL length2,000 charactersInput error
Labels per job10, each up to 100 characters (letters with accents, digits, _, -, +)Input error
Copy title prefix50 charactersInput error
Jobs per space1 running at a time"Another job is still running in this space"
Preview timeabout 20 seconds of searching"Confluence was too slow…", the job can't start
Failures listedfirst 100 per jobThe count still includes every failure
Pages per batch5 per resolver call, no new page after about 12 secondsThe page calls again at once
Job history20 most recent jobs per space, kept 30 days

Not included

Known limitations

Support Zovo

Zovo builds and maintains this app. If it saves you time, you can support the work on GitHub Sponsors. Sponsorship is optional, and the tool works the same without it.

Zovo is made by theluckystrike on GitHub.

More tools by Zovo

License

MIT, see LICENSE.

Support this work

If this app saves you time, you can support the work on GitHub Sponsors. Monthly tiers start at $5, and one-time tiers start at $15. Sponsorship is optional, and the tool works the same without it.

Sponsor on GitHubZovo on GitHub