AppleScript Reference Guide for Entourage

M

Mark Aalyson

Would any of the MVP's know whether such a guide is in the works? The other
applications in Office 2004 sport this useful documentation. Entourage,
alas, doesn't.
And when will invitations be scriptable?
 
P

Paul Berkowitz

Would any of the MVP's know whether such a guide is in the works? The other
applications in Office 2004 sport this useful documentation. Entourage,
alas, doesn't.

Correct. The full reason is that the References for the other apps were all
mostly just "translated" from the VBA Editor Help entries, since the
AppleScript for Word, Excel and PPT 2004 is rooted in the same object model
as VBA. The Help there has existed for years. Only the introductory pages
were added specially for the Reference.

The obverse side of this is that those 3 apps needed a reference desperately
precisely because their object models and syntax are so weird, so unorthodox
compared to model AppleScript (again, precisely because they mirror the VBA
object model, whose syntax is very different, so the "translation" produces
some crazy anomalies impossible to understand without the References.) by
contrast, Entourage's AppleScript model is just about (well, almost) a model
dictionary, designed from the ground up for AppleScript. There are just a
few weird things (recipient, address) mostly due to following the old OS 7
Apple Mail Suite, which had these weirdnesses (first implemented for OE 4.5,
carried over to Entourage 2001).
And when will invitations be scriptable?

A very good question. There's a whole lot of stuff, mostly introduced in
Entourage (X and) 2004 that has not yet been made scriptable. (Finally, they
seem to have caught up to what the first version could do in the UI.) At
least, in SR 2 (Entourage 11.2), time zone of event (plus a huge amount of
stuff for Exchange calendar) has been implemented.

And - there's a new property 'to recipients' of event. If you set or get
that (it's a comma-delimited string, like

"Joe Schmoe <[email protected], Herbie Schmoe <[email protected]>"

) just like 'to recipients of 'draft window' - you set or get the attendees
of the invitation! (In fact, if you set it, the email goes out
automatically, just like that, without waiting for you to open the event or
send it.) So that's a start. I think we can take it from this development
that scripting of invitations will in fact be fleshed out in due course.
I've also been told that MacBU is planning to use AppleScript to automate
their own testing, which basically means that they'll be scripting
everything eventually - good news.

In the meantime, just ask here. There are quite a number of Entourage
scripting experts here.


--
Paul Berkowitz
MVP MacOffice
Entourage FAQ Page: <http://www.entourage.mvps.org/faq/index.html>
AppleScripts for Entourage: <http://macscripter.net/scriptbuilders/>

Please "Reply To Newsgroup" to reply to this message. Emails will be
ignored.

PLEASE always state which version of Microsoft Office you are using -
**2004**, X or 2001. It's often impossible to answer your questions
otherwise.
 
M

Mark Aalyson

Thank you, that explains much.

Without changing the topic title, I asked because I am fumbling around with
trying to get the syntax of the categories property of a contact. My project
involves modifying Microsoft's sample script for exporting Entourage
contacts into a tabbed file for import into Excel. I want to restrict the
list to target only contacts of a certain category. I can grab other
properties, but can't seem to get the syntax for category. Can anyone help?
 
P

Paul Berkowitz

This just shows you that you should include your failed attempt when asking
for help - the actual code.

See, I could answer your question directly:

"Note that 'category' property is a list, as the dictionary says, so you
always need to make it a list, even when there is only one category. E.g.

set category of contact "Joe Schmoe" to {category "Work"}

"

Maybe this solves your problem. But I doubt it. I suspect that when you say
I want to restrict the
list to target only contacts of a certain category

you may be doing it with a 'whose' clause, although you have omitted to say
so:

set theContacts to every contact whose category contains {category
"Work"}


Well, maybe you neglected the fact that category is a list and therefore
needs 'contains', and maybe you don't know that 'contains' requires the same
class (a list here) on both sides - so {category "Work"} needs to be in list
brackets too. (This fact about the 'contains' operator is part of
AppleScript language syntax, and is in the the AppleScript Language Guide
and all books on AppleScript, but you'd be forgiven for not knowing it: it's
one of those "non-English-like" aspects of AppleScript that usually has to
be learned the hard way.) Otherwise you get a strange error about "Can't
make category id 8 into a vector" or something like that. (I'll explain what
that error means if you're interested.)

However, you'll find that even the "correct"-looking statement above gets
you an empty list, not what you expect. This is due to a quirk of Entourage
(and of AppleScript), and is indeed something that should be spelled out in
any AppleScript Reference for Entourage.

The reason is that the Entourage AppleScript developer at the time made an
unfortunate judgment error in using the same term - 'category' - both for
the property (of contact, event, etc. etc.) and for the class itself
('category' is also a class, an element of the application). When the same
term is used for both a property and a class like this, AppleScript gets
confused in whose clauses and assumes you must mean the class. And of course
no contact has a category class, so the answer is 0.

The workaround is to use the synonym for 'whose', namely 'where its', that
includes the very powerful AppleScript term 'its'. When in doubt in
AppleScript, throw in an 'it' or 'its' which specifies the exact target. If
you use this statement:

set theContacts to every contact where its category contains {category
"Work"}


you will get the result you are looking for. The same problem rears its head
if you are ever looking for an account using the 'email address' property in
a whose clause, since 'email address' is also a class (in the Contacts
Suite). By the time they came to implement projects for Entourage 2004, they
had finally learned the lesson from 'category'. So that's why you'll find a
class called 'project' but a property (of contact, event, etc) called
'project list'. You still need the list braces when filtering on project
list property, but you can use 'whose'. It was too late for 'category' - so
use 'where its' and you'll be OK.


--
Paul Berkowitz
MVP MacOffice
Entourage FAQ Page: <http://www.entourage.mvps.org/faq/index.html>
AppleScripts for Entourage: <http://macscripter.net/scriptbuilders/>

Please "Reply To Newsgroup" to reply to this message. Emails will be
ignored.

PLEASE always state which version of Microsoft Office you are using -
**2004**, X or 2001. It's often impossible to answer your questions
otherwise.
 
M

Mark Aalyson

Thanks, for your thoughtful illustration, Paul. I didn't include previous
code because I've tried about a thousand versions, departing from the
original MS-provided script for Excel at
http://www.microsoft.com/mac/resources/resources.aspx?pid=asforoffice. I
have been able to return a list of lists, in which each record is showing
the category property to be a list and I did see the dictionary entry
showing the property to be a list, which is unusual indeed. Such as:

{{category id 11 of application "Microsoft Entourage"}, {}, {category id 19
of application "Microsoft Entourage"}, {}, {}}

I was close at one point, using "where" rather than "where its" so I'll get
back to it. Thanks again. I'm trying to work this out so that I can use the
name rather than the unique ID of the category.
 
P

Paul Berkowitz

Thanks, for your thoughtful illustration, Paul. I didn't include previous
code because I've tried about a thousand versions, departing from the
original MS-provided script for Excel at
http://www.microsoft.com/mac/resources/resources.aspx?pid=asforoffice. I
have been able to return a list of lists, in which each record is showing
the category property to be a list and I did see the dictionary entry
showing the property to be a list, which is unusual indeed. Such as:

{{category id 11 of application "Microsoft Entourage"}, {}, {category id 19
of application "Microsoft Entourage"}, {}, {}}

That's a list of lists. I can only imagine that it must be the result of
asking for

category of every contact [whose something is something]

You weren't asking here for the contacts, but for their categories. Whenever
you ask for a [singular] property of every element , with o with a whose
clause, the result result is a [plural] list containing that property
iterated over each element. So you get a list. Since the property in
question here is 'category', which is itself a list, so you get a list of
lists. Any contact which has no category (seen as "None" in the UI) does not
have a "None" category; it has an empty list, namely {} - accounting for
those {} in the outer list.
I was close at one point, using "where" rather than "where its" so I'll get
back to it. Thanks again. I'm trying to work this out so that I can use the
name rather than the unique ID of the category.


Although elements can be specified by various ways - the dictionary tells
you which - in most cases by name, by index, relative to another, and by a
whose filter - the "true" specification is by ID. In other words, since even
the name of an element can be changed (set) without the object losing its
"whatness", the solid way to identity something is by ID: only rebuilding
the database loses the ID. So that's why Entourage's AppleScript always
returns results using id numbers where possible. (One of the few exceptions
is for group entries - members of a group - known only by index, which can
change. Same with email addresses of a contact.)

So if you want to get the name of every POP account, say, rather than the
IDs, you'd ask for

name of every POP account

and that's what you'd get. You can also ask for

name of every category

meaning category as element of the application. If you do that, you'll get a
list of all your categories by name. (You'll notice again that there is no
such category called "None".)

But since 1) category of a contact is a list and 2) some contacts have no
category, there's no way of getting the 'name of category of every contact',
since there's no such thing. If there was any reason, you could get that
list of 'category of every contact [where its...]', and then iterate through
the list getting 'name of item 1' for each item of the list - but you'd need
an error handler for cases where the category is {} since in that case
there's no item 1. Something like this:

set theContactCategories to category of every contact ...[whose
something is something]

set primeNames to {}
repeat with i from 1 to (count theContactCategories)
set theCatList to item i of theContactCategories
try
set end of primeNames to name of item 1 of theCatList
on error
set end of primeNames to "None"
end try
end repeat
return primeNames

Or if you'd rather you could use

if theCatList ‚ {} then
set end of primeNames to name of item 1 of theCatList
else
set end of primeNames to "None"
end if

which does the same thing (a little slower).

(By the way, I trust you realize why the 'category' property of a contact
has to be of type list: a contact can have several categories. The
AppleScript list will always be in the same list order as in the UI, which
means that item 1 is always the Primary category - the one that displays the
color in the UI.)

Now that you know how to filter contacts by category using 'where its', you
probably won't need to do this at all. You can always refer to the category
you're filtering on by name - you don't need to know the ID. Entourage will
find the ID for the category you name by itself.

Just one more thing: most of the time, it's most useful to filter for every
contact which has that category, even if it's not the primary category.
That's why I gave you

set theContacts to every contact where its category contains {category
"Work"}

last time. But if you really want to check only for Primary category. you'd
do it this way:

set theContacts to every contact where its category starts with category
"Work"

Lists are ordered, so they start with a particular item. In the case of
contact (or other Entourage element) categories, the primary category is
always the one that 'starts' the category list. And 'starts with' does not
error on empty lists ({}).


--
Paul Berkowitz
MVP MacOffice
Entourage FAQ Page: <http://www.entourage.mvps.org/faq/index.html>
AppleScripts for Entourage: <http://macscripter.net/scriptbuilders/>

Please "Reply To Newsgroup" to reply to this message. Emails will be
ignored.

PLEASE always state which version of Microsoft Office you are using -
**2004**, X or 2001. It's often impossible to answer your questions
otherwise.
 
C

Corentin Cras-Méneur

Paul Berkowitz said:
Although elements can be specified by various ways - the dictionary tells
you which - in most cases by name, by index, relative to another, and by a
whose filter - the "true" specification is by ID. In other words, since even
the name of an element can be changed (set) without the object losing its
"whatness", the solid way to identity something is by ID: only rebuilding
the database loses the ID. So that's why Entourage's AppleScript always
returns results using id numbers where possible. (One of the few exceptions
is for group entries - members of a group - known only by index, which can
change. Same with email addresses of a contact.)

I've been playing around doing more or less the same thing lately
(saving the list of categories in one of the custom fields in the
contact itself instead of saving them in a separate file.
I used something like that:

tell application "Microsoft Entourage"
set MyContacts to every contact
repeat with OneContact in MyContacts
tell OneContact
try
set MyCategory to ""
set CategoryList to category of OneContact
repeat with OneCategory in CategoryList
set MyCategory to MyCategory & the ID of OneCategory
& "; "
end repeat
end try
set custom field eight to MyCategory
end tell
end repeat
activate
beep
display dialog "Done!"
end tell

I also had no choice but saving the category IDs, concatenated one after
the other.

Corentin
 
P

Paul Berkowitz

I've been playing around doing more or less the same thing lately
(saving the list of categories in one of the custom fields in the
contact itself instead of saving them in a separate file.
I used something like that:

tell application "Microsoft Entourage"
set MyContacts to every contact
repeat with OneContact in MyContacts
tell OneContact
try
set MyCategory to ""
set CategoryList to category of OneContact
repeat with OneCategory in CategoryList
set MyCategory to MyCategory & the ID of OneCategory
& "; "
end repeat
end try
set custom field eight to MyCategory
end tell
end repeat
activate
beep
display dialog "Done!"
end tell

I also had no choice but saving the category IDs, concatenated one after
the other.

Corentin

Sure you did. 'custom field eight' is a text field - it stores (Unicode)
text. You are setting it to a number - which will coerce to text when
concatenated to the "" at the beginning. Then you are tacking on other
numbers for other categories (if more than one). You are adding the ";"
separator even at the end, instead of only between items (one way to do that
is to concatenate the ";" before, not after, adding the next item. But see
below.)

Is there any particular reason why you are using IDs instead of intelligible
names in the first place? The simplest, and fastest, way is to use
AppleScript's text item delimiters and just coerce the list of names or IDs
to text all in one swoop - no repeat loop needed. It will work with either
name or with ID as text:


tell application "Microsoft Entourage"
set MyContacts to every contact
set AppleScript's text item delimiters to {";"}
repeat with OneContact in MyContacts
tell OneContact
try
set CategoryList to category of OneContact
repeat with i from 1 to (count CategoryList)
set OneCategory to item i of CategoryList
set item i of CategoryList to name of OneCategory -- or
ID
end repeat
set MyCategory to CategoryList as Unicode text
end try
set custom field eight to MyCategory
end tell
end repeat
set AppleScript's text item delimiters to {""} -- restore
activate
beep
display dialog "Done!"
end tell

--
Paul Berkowitz
MVP MacOffice
Entourage FAQ Page: <http://www.entourage.mvps.org/faq/index.html>
AppleScripts for Entourage: <http://macscripter.net/scriptbuilders/>

Please "Reply To Newsgroup" to reply to this message. Emails will be
ignored.

PLEASE always state which version of Microsoft Office you are using -
**2004**, X or 2001. It's often impossible to answer your questions
otherwise.
 
C

Corentin Cras-Méneur

Paul Berkowitz said:
Sure you did. 'custom field eight' is a text field - it stores (Unicode)
text. You are setting it to a number - which will coerce to text when
concatenated to the "" at the beginning. Then you are tacking on other
numbers for other categories (if more than one). You are adding the ";"
separator even at the end, instead of only between items (one way to do that
is to concatenate the ";" before, not after, adding the next item. But see
below.)

Very true. It was a quick and dirty way to do that. I know it wasn't
optimum and it was saving a useless ; at the end of the field.
Is there any particular reason why you are using IDs instead of intelligible
names in the first place?

Nope, no good reason, except for the fact that I quickly found all the
informations I needed about the way AppleScript deals with these IDs
from Entourage.
Extracting the category name was not as obvious for me (I guess I should
have read further in the Dictionary) and I wasn't sure about how to
write it back into the contact itself afterwards for the other script.
Since I needed to test the possibilities, I begun with IDs, with the
perspective of getting to something more sofisticated later.
It's clear that AppleScript in Entourage has no secret for you :) Now
that you showed me the script, the solution seems so obvious :-\
The simplest, and fastest, way is to use
AppleScript's text item delimiters and just coerce the list of names or IDs
to text all in one swoop - no rep

Indeed, much cleaner (and easier).
I wasn't worried about the delimiter. I already had an easy way to deal
with it: when I begun writing the other part of the script - the one I
never had time to finish - I piped a shell script in AppleScript.
Parsing the data and dealing with the delimiter was no challenge for me
with sed. You know I''m much more comfortable with shell scripts and
regular expressions ;-)
Your solution is zillions of times more elegant and efficient than mine
of course :)


Corentin
 
C

Corentin Cras-Méneur

Corentin Cras-Méneur said:
Your solution is zillions of times more elegant and efficient than mine
of course :)

I had a little more time to take a look at the script to write the
categories back to the contacts today. And then, reading through
Entourage's AppleScript dictionary, I realized that the category IDs are
read-only :->

Corentin
 
P

Paul Berkowitz

I had a little more time to take a look at the script to write the
categories back to the contacts today. And then, reading through
Entourage's AppleScript dictionary, I realized that the category IDs are
read-only :->

That's if you're making a new category (i.e. that will appear in
Edit/Categories). And you can't set the ID of a category to some other
number, no. But that's not what you'd be trying to do, is it? You'd be
trying to assign a category to a contact (or event, etc.) In that case you'd
simple make the contact, and then

set category of theContact to {category id 14}

for example. If you had saved the text "14" in a "Category ID" column in
Excel, it's just a matter of referring to that text as, say, theCategoryID,
so you'd then

set category of theContact to {category id (theCategoryID as integer)}

But I still don't advise it, for two reasons:

1) If you have in the meantime rebuilt your Entourage database, the IDs most
likely have changed. (Previously deleted categories will "drop out" and the
sequence of ID numbers will "move up" accordingly.) So you'll get the wrong
category, or an error ('can't get category id 34').

2) If you have deleted the category, or are importing into another identity
which doesn't have your categories yet, the ID number is useless for making
a new one. If you saved the named, say "Special" in the Excel field, you can
make a new category of that name now and assign it, should it not exist,
referring to the text of the Excel cell as categoryName here:

try
set category of theContact to {category categoryName}
on error
set newCategory to make new category with properties
{name:categoryName}
set category of theContact to {newCategory}
end try

(That version would create the category as black; you could change the color
later. Or you could give it a random color. Or you could even store the
color in another Excel field and recreate it with the same color.)


set newCategory to make new category with properties
{name:categoryName, color:(my MakeRandomColor())}



to MakeRandomColor()

set r to random number from 0 to 65535
set g to random number from 0 to 65535
set b to random number from 0 to 65535

return {r, g, b}
end MakeRandomColor



--
Paul Berkowitz
MVP MacOffice
Entourage FAQ Page: <http://www.entourage.mvps.org/faq/index.html>
AppleScripts for Entourage: <http://macscripter.net/scriptbuilders/>

Please "Reply To Newsgroup" to reply to this message. Emails will be
ignored.

PLEASE always state which version of Microsoft Office you are using -
**2004**, X or 2001. It's often impossible to answer your questions
otherwise.
 
C

Corentin Cras-Méneur

Paul Berkowitz said:
That's if you're making a new category (i.e. that will appear in
Edit/Categories). And you can't set the ID of a category to some other
number, no. But that's not what you'd be trying to do, is it? You'd be
trying to assign a category to a contact (or event, etc.) In that case you'd
simple make the contact, and then

Interesting. After reading the Dictionary, I didn't try any further. The
definition was a little abrupt and did not really provide any further
information.

set category of theContact to {category id 14}

Right!! This works just fine. I should have just tried it. The r/o
turned me off...
for example. If you had saved the text "14" in a "Category ID" column in
Excel, it's just a matter of referring to that text as, say, theCategoryID,
so you'd then

set category of theContact to {category id (theCategoryID as integer)}

But I still don't advise it, for two reasons:

1) If you have in the meantime rebuilt your Entourage database, the IDs most
likely have changed. (Previously deleted categories will "drop out" and the
sequence of ID numbers will "move up" accordingly.) So you'll get the wrong
category, or an error ('can't get category id 34').

Right, but I had tried to set the category name to something without
great success either. I obviously wasn't using the proper syntax.
2) If you have deleted the category, or are importing into another identity
which doesn't have your categories yet, the ID number is useless for making
a new one. If you saved the named, say "Special" in the Excel field, you can
make a new category of that name now and assign it, should it not exist,
referring to the text of the Excel cell as categoryName here:

try
set category of theContact to {category categoryName}
on error
set newCategory to make new category with properties
{name:categoryName}
set category of theContact to {newCategory}
end try

(That version would create the category as black; you could change the color
later. Or you could give it a random color. Or you could even store the
color in another Excel field and recreate it with the same color.)


set newCategory to make new category with properties
{name:categoryName, color:(my MakeRandomColor())}



to MakeRandomColor()

set r to random number from 0 to 65535
set g to random number from 0 to 65535
set b to random number from 0 to 65535

return {r, g, b}
end MakeRandomColor

Simply brilliant!!
It works like a cham (of course).
Writing a script for the second part of the project (restoring the
categories) was considerably easier then:


tell application "Microsoft Entourage"
set AppleScript's text item delimiters to {";"} -- set delimiter
try
set MyContacts to every contact
repeat with OneContact in MyContacts
try
set CategoryList to custom field eight
of OneContact -- extract the saved categories
if (count text items of CategoryList) =
1 then -- easy if there is only one
try
set category of
OneContact to {category CategoryList}
on error -- category doesn't
already exist: Paul's great option
set newCategory to make
new category with properties {name:CategoryList, color:(my
MakeRandomColor())}
set category of
OneContact to {newCategory}
end try
else -- more than one category
try
repeat with i from 2 to
(count text items of CategoryList) -- the first item has to be applied
last, the order of the other doesn't matter
set OneCategory
to text item i of CategoryList
set category of
OneContact to {category OneCategory}
end repeat
set OneCategory to text
item 1 of CategoryList
set category of
OneContact to {category OneCategory} -- applying the first one last to
make it the default category
on error -- Paul's option for
non-existing categories again
set newCategory to make
new category with properties {name:CategoryList, color:(my
MakeRandomColor())}
set category of
OneContact to {newCategory}
end try
end if
end try
end repeat
end try
set AppleScript's text item delimiters to {""} -- restore
delimiter
activate
beep
display dialog "Done!"
end tell


to MakeRandomColor() -- subroutine for random category color

set r to random number from 0 to 65535
set g to random number from 0 to 65535
set b to random number from 0 to 65535

return {r, g, b}
end MakeRandomColor



Lines are broken at the wrong place posting through newsgroups, but I'll
find a way to place both scripts online one way of the other one I can
confirm I haven't introduced a nasty bug somewhere.

With both scripts, I'll be able to bring my contacts back to my Exchange
account again :) Restoring the categories will be a simple matter fo
running the scripts :)



Corentin
 
Top