In JavaScript (which doesn’t error if there is no custom meta data), we would, FWIW, have been able expand a little to give the user a bit more clarity about what is happening.
(Very unusually for an osascript (Apple Events) context, and somewhat against the grain of Apple’s own guidelines, Smart Rules script actions only allow for AppleScript, which makes things, alas, more difficult in this one part of the otherwise very JS-friendly DT3)
A JS approach:
Starting with the JS equivalent of a vanilla on run ... end run
wrapping:
(() => {
'use strict';
// Define what we need here
})();
(In which 'use strict'
is optional, but enables more informative messages to the user)
We could define some of the the constant values that we need:
- The metadata key that interests us.
- A reference to DEVONthink itself,
- and its current selection, as well as:
- The
md
prefixed version of metaData names that is used by macOS.
(() => {
'use strict';
const myKey = 'citation';
const
devonThink = Application('DEVONthink 3'),
selns = devonThink.selection(),
mdKeyName = 'md' + myKey;
})();
Then, if it turns out that no records are selected, we can just feed that back (for example to the Messages panel of Script Editor) and have done with it:
(() => {
'use strict';
const myKey = 'citation';
const
devonThink = Application('DEVONthink 3'),
selns = devonThink.selection(),
mdKeyName = 'md' + myKey;
if (selns.length > 0) {
// Fill this out for the case where something *is* selected.
} else {
console.log('Nothing selected')
return ''
}
})();
If any records are selected, then we are ready to define some more of the values that we will need:
(() => {
'use strict';
const myKey = 'citation';
const
devonThink = Application('DEVONthink 3'),
selns = devonThink.selection(),
mdKeyName = 'md' + myKey;
if (selns.length > 0) {
const
theRecord = selns[0],
dictMD = theRecord.parents.at(0).customMetaData() || {},
keyValue = dictMD[mdKeyName];
} else {
console.log('Nothing selected')
return ''
}
})();
- The first selected item (JS list indexes begin at zero)
- The Meta Data record (key-value ‘dictionary’) of the first parent of that item, or an empty dictionary if that parent doesn’t yet have one. (In JS,
or
is written as ||
)
- The value (if any) for the key that interests us (e.g.
mdcitation
) in that dictionary.
(() => {
'use strict';
const myKey = 'citation';
const
devonThink = Application('DEVONthink 3'),
selns = devonThink.selection(),
mdKeyName = 'md' + myKey;
if (selns.length > 0) {
const
theRecord = selns[0],
dictMD = theRecord.parents.at(0).customMetaData() || {},
keyValue = dictMD[mdKeyName];
} else {
console.log('Nothing selected')
return ''
}
})();
At this stage ,where AppleScript would have thrown up its hands and errored (if the parent turned out to have no value for the mdcitation
key), JS just defines keyValue
either:
- as a String, if an
mdcitation
value was found,
- or as the special value
undefined
, if there was no mdcitation
value in the parent’s metadata dictionary.
In JS, is not
or ≠
, is written as !==
(() => {
'use strict';
const myKey = 'citation';
const
devonThink = Application('DEVONthink 3'),
selns = devonThink.selection(),
mdKeyName = 'md' + myKey;
if (selns.length > 0) {
const
theRecord = selns[0],
dictMetaData = theRecord.parents.at(0).customMetaData() || {},
keyValue = dictMetaData[mdKeyName];
if (keyValue !== undefined) {
// Expand here to make us of the citation string that was found.
} else {
console.log('Key not found in parent metaData: "' + mdKeyName + '"')
return ''
}
} else {
console.log('Nothing selected')
return ''
}
If a string value was defined for citation, then we can pass it on from the parent to the selected child:
(() => {
'use strict';
const myKey = 'citation';
const
devonThink = Application('DEVONthink 3'),
selns = devonThink.selection(),
mdKeyName = 'md' + myKey;
if (selns.length > 0) {
const
theRecord = selns[0],
dictMetaData = theRecord.parents.at(0).customMetaData() || {},
keyValue = dictMetaData[mdKeyName];
if (keyValue !== undefined) {
theRecord.customMetaData = Object.assign(
{},
theRecord.customMetaData,
{
[mdKeyName]: keyValue
}
)
console.log(JSON.stringify(theRecord.customMetaData()))
return keyValue
} else {
console.log('Key not found in parent metaData: "' + mdKeyName + '"')
return ''
}
} else {
console.log('Nothing selected')
return ''
}
})();
The key expression here is Object.assign
, which lets us copy as many key-value pairs as we like from one dictionary to another, (or even copy them them from right to left along a list of several dictionaries).
The updated dictionary which we are defining here copies values (bottom towards top, in this layout):
Object.assign(
{},
theRecord.customMetaData,
{
[mdKeyName]: keyValue
}
)
- A new dictionary with just the key-value pair we want,
- is copied up into any
customMetaData
that the DT record may already have,
- but that may be
undefined
, if the record doesn’t yet have any meta data,
so we add a fresh empty dictionary {}
at the top, as a final target, just in case.
Finally, with the meta-data inherited from the parent, we can send informative feedback,
both to the Script Editor Messages
panel (using the built-in JSON stringify, which gives a readable
representation of dictionaries - often also called ‘Objects’ in JS reference materials)
console.log(JSON.stringify(theRecord.customMetaData()))
and to the Script Editor Results
panel:
return keyValue