Auto-publish approved files to a public folder
Promote Northwind files once a status flips — from `drafts/` to `published/` automatically.
Published Nov 1, 2025
Northwind keeps work-in-progress files in a private drafts/ folder and moves
them to a public published/ folder once they clear review. Done by hand, that
move is easy to forget and easy to get wrong — a file gets moved but stays
private, or gets shared but never moved, and the public folder drifts out of
sync with what is actually signed off.
This script makes the queue the single source of truth. Editors tick an
approved box in a sheet; the script moves each approved file into the public
folder, opens up its sharing, and stamps the row so it is never processed
twice. The public folder then contains exactly the approved files and nothing
else.
What you’ll need
- A
Publishing queueGoogle Sheet with a header row and these columns:fileId,approved(a checkbox orTRUE/FALSE), andpublishedAt. - A Drive folder to act as the public destination, and its folder ID.
- The files referenced by
fileIdmust be owned by or shared with the account running the script, so it can move them and change their sharing.
The script
// The sheet that drives the queue, and the public destination folder.
const PUBLISH_QUEUE_ID = '1abcPublishQueueId';
const PUBLISHED_FOLDER_ID = '1abcPublishedFolderId';
/**
* Moves every approved-but-unpublished file into the public folder,
* makes it viewable by link, and records when it was published.
*/
function promoteApproved() {
const sheet = SpreadsheetApp.openById(PUBLISH_QUEUE_ID).getSheets()[0];
const values = sheet.getDataRange().getValues();
// Split the header off and map column names to their indexes.
const [header, ...rows] = values;
if (!rows.length) {
Logger.log('Publishing queue is empty — nothing to do.');
return;
}
const col = Object.fromEntries(header.map((k, i) => [k, i]));
const publishedFolder = DriveApp.getFolderById(PUBLISHED_FOLDER_ID);
let moved = 0;
rows.forEach((row, i) => {
// Skip rows that are not approved, or already published.
if (!row[col.approved] || row[col.publishedAt]) return;
const file = DriveApp.getFileById(row[col.fileId]);
// Move the file into the public folder...
file.moveTo(publishedFolder);
// ...and open it up so anyone with the link can view it.
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
// Stamp the row so this file is never processed again.
values[i + 1][col.publishedAt] = new Date();
moved++;
});
// One write commits every timestamp change at once.
sheet.getDataRange().setValues(values);
Logger.log(`Promoted ${moved} file(s) to the public folder.`);
}
How it works
promoteApprovedopens the queue sheet and reads every row with a singlegetDataRange()call.- It splits off the header row and builds a
collookup so columns are referenced by name, not by index. - If there are no data rows it logs a message and stops.
- It resolves the public destination folder once, before the loop, rather than re-fetching it for every file.
- For each row it skips anything not approved or already carrying a
publishedAttimestamp — that timestamp is the guard against moving a file twice. - For an eligible file it calls
moveToto relocate it into the public folder, thensetSharingto make it viewable by anyone with the link. - It writes the current time into the in-memory copy of the row, then commits
all timestamp changes with one
setValuescall after the loop.
Example run
The Publishing queue sheet before a run:
| fileId | approved | publishedAt |
|---|---|---|
| 1fileBrandKit | TRUE | |
| 1fileDraftSpec | FALSE | |
| 1fileCaseStudy | TRUE | 2025-10-30 11:15 |
After a run, only the first row is touched — 1fileDraftSpec is not approved
and 1fileCaseStudy already has a timestamp:
| fileId | approved | publishedAt |
|---|---|---|
| 1fileBrandKit | TRUE | 2025-11-01 08:00 |
| 1fileDraftSpec | FALSE | |
| 1fileCaseStudy | TRUE | 2025-10-30 11:15 |
1fileBrandKit now lives in published/ and is viewable by anyone with the
link.
Trigger it
Run this on a schedule so approvals are promoted without anyone watching:
- In the Apps Script editor open Triggers (the clock icon).
- Add a trigger for
promoteApproved, Time-driven, Hour timer, every hour. An approved file then moves to the public folder within the hour.
Watch out for
moveTorelocates the file rather than copying it — the original leavesdrafts/. If you need the draft kept, copy it first and queue the copy.ANYONE_WITH_LINKkeeps files link-only, not search-indexed. Switch toDriveApp.Access.ANYONEonly if you genuinely want them publicly discoverable.- The script never moves a file back. If something is unapproved after publishing, you must move it and reset its sharing by hand.
- The
publishedAttimestamp is the only thing preventing reprocessing. If a user clears that cell, the file is moved and reshared again on the next run. - Moving and resharing files counts against Drive quotas. Keep each run to a reasonable batch rather than thousands of files at once.
Related
Build a recurring file-delivery system
Drop a fresh report file into a Northwind client folder weekly — they don't even ask.
Updated Dec 15, 2025
Build a Drive search index in Sheets
Make Northwind's file metadata searchable in a Sheet — like Spotlight for Drive.
Updated Dec 7, 2025
Build a shared-folder onboarding kit
Auto-grant new Northwind hires the folders they need on day one.
Updated Nov 29, 2025
Route saved email attachments to project folders
File Gmail attachments into the right Northwind client folder based on subject keywords.
Updated Nov 25, 2025
Bundle a folder of images into one PDF
Combine Northwind scans into a single deliverable PDF using a generation service.
Updated Nov 17, 2025