These scripts can be used to store tags that are no longer used and to restore them if necessary.
- The first script stores information about tags in Custom Meta Data and removes tags from selected records.
- The second script reassigns tags.
Considerations
-
A tag can be renamed which means if we only store its
name
and later restore it then we could end up with a new tag. -
A tag hierachy can be rearranged which means if we only store a tag’s
location
plus itsname
then we could end up with a new hierachy.
Using a tag’s UUID
would solve both situations, but
- A tag can be deleted which means if we only store its
UUID
then we couldn’t restore it if it was deleted in the meantime.
Approach
Therefore the script uses a mixed approach of storing,
- the
UUID
, in order to be prepared for the first two situations and - the
location
plus thename
, in order to be prepared for unintentionally and intentionally deleted tags.
This approach allows to
- remove tags from records, i.e. clean up a tag’s visible content, e.g. reducing a tag’s child count
- remove tags from records and (manually) delete the tags from the database, i.e. reduce the total number of a database’s tags
How it works
Two tags with the same name
are not treated as equal. What counts is whether their UUID
is equal.
Both scripts allow the choice what tags should be stored/restored, if property useChooseFromList
is
-
false
then all tags of selected records are immediately stored/restored without user interaction. -
true
then you can choose what to store/restore.
Script 1
This script stores the tag’s hierachy plus a delimiter plus its UUID
. Each tag’s information is stored on a new line.
Script 2
This script changes behaviour depending on what happened to an original tag since it was stored.
Scenario - Tag not deleted
- Restoring reassigns the original tag to the record
Scenario - Tag deleted
- Restoring creates a new tag
- Assigns the new tag to the record
- Sets the new tag’s
aliases
to the original tag’sname
plus itsUUID
Note: Do not delete the alias.
Usage Scenario
Usage Scenario 1
You’ve finished a project and don’t need or don’t want to see the tags used for this project anymore. Now
- move all used tags under a new tag, e.g. “Finished Project XY”
- select all its descendants,
- run script 1,
- finally delete the tag.
If you need to assign these tags again to the records in the future then running script 2 will rebuilt the tag hierachy nicely ordered under your “Finished Project XY” parent tag.
By the way, it’s possible to store and restore different “sets” or “projects” for a record. You can use set “A” today and set “B” tomorrow and set “C” next week - there’s no need to delete either of them. Just store what you currently don’t need and restore if it’s needed again.
Usage Scenario 2
You’re working on something that requires only a subset of tag “ABX”.
- select all records that are currently not needed
- run script 1
When you’re done do a toolbar search within your Custom Meta Data for “ABX”. This yields all records you’ve previously stored this tag for. Select the results and run script 2 to restore the tags.
Setup
In DEVONthink
- Open
Preferences > Data
- Add new Custom Meta Data
- Choose a meaningful name, e.g. “Ex Tags”
- Set the type to either
Single-line Text
orMulti-line Text
.
ChooseSingle-line Text
if you want to save space in the inspector.
ChooseMulti-line Text
if you want to be able to see the stored data in the inspector.
In the script
-
Set property
theMetaDataIdentifier
to your Custom Meta Data identifier that’s shown in DEVONthink’sPreferences > Data
. -
Set property
useChooseFromList
to true of you’re sure that you always want to store/restore all tags of a record instead of having the choice what to store/restore.
Scripts
1 - Store Tags in Custom Meta Data
-- Store Tags in Custom Meta Data
use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
property theMetaDataIdentifier : "extags" -- replace this with your identifier
property useChooseFromList : true -- if false all tags are stored without user interaction
tell application id "DNtp"
try
set theRecords to selected records whose tag type ≠ ordinary tag
if theRecords = {} then error "Please select some records"
set theTab to (character id 9)
repeat with thisRecord in theRecords
set theTags_active to (parents of thisRecord whose tag type = ordinary tag)
if theTags_active ≠ {} then
set theTags_inactive_compound_string to get custom meta data for theMetaDataIdentifier from thisRecord
if theTags_inactive_compound_string ≠ missing value and theTags_inactive_compound_string ≠ "" then
set theTags_inactive_compound to my tid(theTags_inactive_compound_string, linefeed)
else
set theTags_inactive_compound to {}
end if
set theChooseFromListItems to {}
set theTags_inactive_compound_new to {}
repeat with thisTag in theTags_active
set thisTag_UUID to uuid of thisTag
set thisTag_Hierachy to my tid(items 3 thru -1 in my tid(location of thisTag, "/"), "/") & name of thisTag
set end of theChooseFromListItems to thisTag_Hierachy & linefeed & thisTag_UUID
set end of theTags_inactive_compound_new to thisTag_Hierachy & theTab & thisTag_UUID
end repeat
if useChooseFromList = true then
set theChooseFromListItems to my sort(theChooseFromListItems)
set theChoice to choose from list theChooseFromListItems with prompt "Store Tags:" default items (item 1 of theChooseFromListItems) with title (name of thisRecord as string) with multiple selections allowed
if theChoice is false then return
set theTags_inactive_compound_updated to theTags_inactive_compound
set theTrashGroup to trash group of database of thisRecord
repeat with thisItem in theChoice
set thisTag_UUID to paragraph 2 of thisItem
set thisTag to (get record with uuid thisTag_UUID)
move record thisRecord from thisTag to theTrashGroup
set end of theTags_inactive_compound_updated to paragraph 1 of thisItem & theTab & thisTag_UUID
end repeat
else
set tags of thisRecord to (parents of thisRecord whose tag type ≠ ordinary tag)
set theTags_inactive_compound_updated to theTags_inactive_compound & theTags_inactive_compound_new
end if
add custom meta data (my tid(my sort(theTags_inactive_compound_updated), linefeed)) for theMetaDataIdentifier to thisRecord
end if
end repeat
on error error_message number error_number
if the error_number is not -128 then
activate
display alert "DEVONthink" message error_message as warning
return
end if
end try
end tell
on tid(theInput, theDelimiter)
set d to AppleScript's text item delimiters
set AppleScript's text item delimiters to theDelimiter
if class of theInput = text then
set theOutput to text items of theInput
else if class of theInput = list then
set theOutput to theInput as text
end if
set AppleScript's text item delimiters to d
return theOutput
end tid
on sort(theList)
try
return ((current application's NSArray's arrayWithArray:theList)'s sortedArrayUsingSelector:"localizedStandardCompare:") as list
on error error_message number error_number
activate
display alert "Error: Handler \"sort\"" message error_message as warning
error number -128
end try
end sort
2 - Restore Tags from Custom Meta Data
-- Restore Tags from Custom Meta Data
property theMetaDataIdentifier : "extags" -- replace this with your identifier
property useChooseFromList : true -- if false all tags are restored without user interaction
tell application id "DNtp"
try
set theRecords to selected records
if theRecords = {} then error "Please select some records"
set theTab to (character id 9)
repeat with thisRecord in theRecords
set theTags_inactive_compound_string to get custom meta data for theMetaDataIdentifier from thisRecord
if theTags_inactive_compound_string ≠ missing value and theTags_inactive_compound_string ≠ "" then
set theTags_inactive_compound to my tid(theTags_inactive_compound_string, linefeed)
set theChooseFromListItems to {}
set theTags_active_compound_new to {}
repeat with thisTag_inactive_compound in theTags_inactive_compound
set {thisTag_Hierachy, thisTag_UUID} to my tid(thisTag_inactive_compound, theTab)
set end of theChooseFromListItems to thisTag_Hierachy & linefeed & thisTag_UUID
set end of theTags_active_compound_new to thisTag_Hierachy & theTab & thisTag_UUID
end repeat
if useChooseFromList = true then
set theChoice to choose from list theChooseFromListItems with prompt "Restore Tags:" default items (item 1 of theChooseFromListItems) with title (name of thisRecord as string) with multiple selections allowed
if theChoice is false then return
set theTags_active_compound_new to {}
repeat with thisItem in theChoice
set thisTag_UUID to paragraph 2 of thisItem
set thisTag to (get record with uuid thisTag_UUID)
if thisTag = missing value then set thisTag to my getTag(thisTag_UUID, thisTag_Hierachy, thisRecord)
replicate record thisRecord to thisTag
set end of theTags_active_compound_new to paragraph 1 of thisItem & theTab & thisTag_UUID
end repeat
set theTags_inactive_compound_updated to {}
repeat with thisTag_inactive_compound in theTags_inactive_compound
if thisTag_inactive_compound as string is not in theTags_active_compound_new then set end of theTags_inactive_compound_updated to thisTag_inactive_compound as string
end repeat
add custom meta data (my tid(theTags_inactive_compound_updated, linefeed)) for theMetaDataIdentifier to thisRecord
else
repeat with thisTag_active_compound_new in theTags_active_compound_new
set {thisTag_Hierachy, thisTag_UUID} to my tid(thisTag_active_compound_new, theTab)
set thisTag to (get record with uuid thisTag_UUID)
if thisTag = missing value then set thisTag to my getTag(thisTag_UUID, thisTag_Hierachy, thisRecord)
replicate record thisRecord to thisTag
end repeat
add custom meta data "" for theMetaDataIdentifier to thisRecord
end if
end if
end repeat
on error error_message number error_number
if the error_number is not -128 then
activate
display alert "DEVONthink" message error_message as warning
return
end if
end try
end tell
on tid(theInput, theDelimiter)
set d to AppleScript's text item delimiters
set AppleScript's text item delimiters to theDelimiter
if class of theInput = text then
set theOutput to text items of theInput
else if class of theInput = list then
set theOutput to theInput as text
end if
set AppleScript's text item delimiters to d
return theOutput
end tid
on getTag(theTag_UUID, theTag_Hierachy, theRecord)
tell application id "DNtp"
try
set theTag_Name to item -1 of my tid(theTag_Hierachy, "/")
set theTag_Alias to theTag_Name & "_" & theTag_UUID
set theTags to (parents of database of theRecord whose tag type = ordinary tag and aliases contains theTag_Alias)
if theTags ≠ {} then
set theTag to item 1 of theTags
else
set theTag to create location (name of tags group of database of theRecord) & "/" & theTag_Hierachy & "_" & theTag_UUID
set name of theTag to theTag_Name
set aliases of theTag to theTag_Alias
end if
return theTag
on error error_message number error_number
activate
display alert "DEVONthink" message error_message as warning
error number -128
end try
end tell
end getTag