Action
/ slash tag migration
UPDATES
over 1 year ago
Migrate tags ending with a slash
created by @FlohGro / more on my Blog
/ slash tag migration
This action is a helper to migrate tags that use a slash /
at the start, end or in the middle of a tag.
See the forum entry for details why you need to migrate those tags.
Update Warning: Tags starting and ending with “/” no longer allowed
https://forums.getdrafts.com/t/update-warning-tags-starting-and-ending-with-no-longer-allowed/14516
The Action will search for tags with a slash at the start, end or any tag containing a slash and asks you to rename each of them. You have to do it for all tags that start or end with a slash (personally i used it to categorize tags). Tags containing slashes somewhere in between will be nested tags so it might be intended.
Just run the action and look through the tags - if you don‘t want to do all of your tags at once - you can simply abort the migration.
The tags will still be visible in the tag pane for a bit of time.
If you find this useful and want to support me you can donate or buy me a coffe
Steps
-
script
// slash tag migration // get all tags that contain a slash const slashTags = Tag.query("/") // get all tags that start with a slash const startingWithSlash = slashTags.filter(tag => tag.startsWith("/")) // get all tags that end with a slash const endingWithSlash = slashTags.filter(tag => tag.endsWith("/")) const mustChangeSlashTags = startingWithSlash.concat(endingWithSlash) // get all tags that contain a slash but NOT at the beginning let containsSlash = slashTags.filter(tag => !tag.startsWith("/")) let renamedDraftsCt = 0 let dbg = [] function run(){ alert("welcome to the slash tag migration\n\nthis action will help you to migrate tags that contain a \"/\". In the upcoming updates of Drafts this will result in nested Tags as announced in the forums.\nThe Action handles two different cases:\n1) tags starting and ending with a \"/\" must not be used and will be renamed to your preference\n2) tags that contain a \"/\" might need changes if you don't want them to be nested\n\nplease note, no renaming will be done automatically, you need to confirm each tag on its own") // iterate through startingWithSlash tags and ask to rename them alert("we start with the tags starting or ending with a \"/\".\n\nYou should at least remove the slash - the action will suggest that by default but you need to confirm it for each tag") for(tag of mustChangeSlashTags){ // due to drafts kind of caching the tags and not removing them instantly we have to double check that any draft contains a tag - e.g. it was renamed already in an earlier run of this action,... then we skip that tag if(!doesAnyDraftContainTag(tag)){ continue } let p = new Prompt() p.title = "rename tag \"" + tag + "\"!" let startsWithSlash = true if(tag.endsWith("/")){ startsWithSlash = false } p.message = "the tag must not " + startsWithSlash ? "start" : "end" + "with a \"\/\" please type the new name into the text field below" let newTagSuggest = "" if(startsWithSlash){ newTagSuggest = tag.slice(1) } else { newTagSuggest = tag.slice(tag.length) } p.addTextField("newTag","",newTagSuggest,{wantsFocus: true, autocapitalization: "none", autocorrect: false}) p.addButton("abort migration helper") p.addButton("rename tag") p.isCancellable = false let curRenamedDraftsAmountCt = renamedDraftsCt if(p.show()){ // button was pressed (not cancelled) check if abort is intended if(p.buttonPressed == "abort migration helper"){ return } let newTag = p.fieldValues["newTag"] if(newTag.contains("/")){ containsSlash.push(newTag) } renamedDraftsCt += renameTag(tag, newTag) app.displaySuccessMessage("updated " + (renamedDraftsCt - curRenamedDraftsAmountCt) + " drafts") } } alert("no more tags with a \"/\" at the beginning or end.\n\nlets see which tags contain a \"/\".\n not at the beginning.\nagain those will be nested tags - think about if you need it for that specific tag and rename it if you don't want to have a nested tag") for(tag of containsSlash){ // due to drafts kind of caching the tags and not removing them instantly we have to double check that any draft contains a tag - e.g. it was renamed already in an earlier run of this action,... then we skip that tag if(!doesAnyDraftContainTag(tag)){ continue } let p = new Prompt() p.title = "rename tag \"" + tag + "\"?" p.message = "tags with a \"\/\" will be nested tags - check if you want to have a nested tag for the following tag - otherwise pick a new name" p.addTextField("newTag","",tag,{wantsFocus: true, autocapitalization: "none", autocorrect: false}) p.addButton("abort migration helper") p.addButton("rename tag") p.addButton("keep tag as it is") p.isCancellable = false let curRenamedDraftsAmountCt = renamedDraftsCt if(p.show()){ // button was pressed (not cancelled) check if abort is intended if(p.buttonPressed == "abort migration helper"){ return } if(p.buttonPressed == "rename tag"){ let newTag = p.fieldValues["newTag"] if(newTag != tag){ renamedDraftsCt += renameTag(tag, newTag) app.displaySuccessMessage("updated " + (renamedDraftsCt - curRenamedDraftsAmountCt) + " drafts") } } } } } function doesAnyDraftContainTag(tag){ let drafts = Draft.query("", "all", [tag]) if(!drafts){ dbg.push("ignoring tag: " + tag) return false } if(drafts.length > 0){ return true } else { return false } } function renameTag(oldTag, newTag){ let drafts = Draft.query("", "all", [oldTag]); if (drafts.length == 0) { return 0 } else { let ct = 0; for (let d of drafts) { d.removeTag(oldTag); if (newTag.length > 0) { d.addTag(newTag); } d.update(); ct++; } return ct } } run() alert("finished tag migration.\nupdated " + renamedDraftsCt + " drafts in total")
Options
-
After Success Nothing Notification Info Log Level Info