Clear iCal recurrence and other issues

It does not seem to be possible to clear an iCal recurrence once set.

Make a new event on calendar “home” with summary “Test recur” then run this script:

tell application "iCal"
	set e to item 1 of (every event of calendar "Home" whose summary contains "Test Recur")
	set p to recurrence of e
	set recurrence of e to "FREQ=YEARLY;INTERVAL=1" as Unicode text
	set p2 to recurrence of e
	set recurrence of e to "" -- theRecurrence	
	set p3 to recurrence of e
end tell

set result to {p, p2, p3}

It won’t clear it. Is there a work around? Deleting the event and adding it back to the same calendar produces some unexpected results relating to UID I won’t go into unless necessary.

Other issues I’m having with iCal scripting (about to release a new application) are:

  1. seems to be no way to set the email for attendees
  2. seems to be no way to set the email for mail alarms
  3. lots of properties return no results and no error, such as:

set loc to location of the_event

If the location is blank, loc is undefined and missing value or “” is not returned. This seems to apply to summary and description and some properties of the event class.

I haven’t found any way to delete one. They’re easy enough to set, but the only workaround I’ve found to get rid of them is to delete and reset. Your workaround will have to be to deal with the changed uid.

You have to work around the failures to inform you of no entry by forcing an error with the lack of data.

tell application "iCal" -- Edited for slightly better form
		set L to (location of (first item of (events of calendar "Normal" whose summary is "Car Service" as Unicode text)))
		set tst to (first character of L)
	on error
		set L to missing value
	end try
end tell

If the email is in the Address Book, you can get it from the Address Book and use an email client to mail it.

tell application "iCal"
	set A to display name of attendees of (first item of (events of calendar "Normal" whose summary is "Car Service" as Unicode text))
end tell
tell application "Address Book"
	set N to first item of (every person whose name is A as Unicode text)
	set E to value of (emails of N whose label is "Work")
end tell

Thanks for your replies, Adam. They are pretty much as I expected. Two comments and one question:

  1. with regards to the work around dealing with changed UID, I have tried to use that approach already. The problem is, at least in the context of an AS Studio app, any attempt to reference the UID of the original event after adding a new event to the SAME calendar fails with an NS Receiver error unless I wait at least 3 - 4 seconds first (delay). I haven’t worked this out exactly but I’m scratching my head. I have routines to create an identical event on another calendar and then delete the original, which works fine. But if I create an “identical” event on the same calendar as the original, I can’t delete the original because I can’t get its UID. I’ll keep working on it though. (I do have to create the new one before deleting the original for reasons that are involved.)

  2. Although this create new & delete old method is sort of ok, because of the limitations of iCal its annoying – you can’t properly recreate alarms 100% of the time due to the missing alarm and attendee properties.

  3. When iCal creates a mail alarm, it apparently defaults to the address book “me” card, but which email? You say work but is that the default? In my case it seems to default to the last one used.

I can answer the last question off the top of my head:

set E to value of (emails of N whose label is “Work”) returns a list of all of them and I don’t think you can assume that the first one will be work – they are probably stored in the order entered.

iCal is a work in progress, WB – always requires a lot of “frigging” around to find the path to what you want.

mail alarm just tells you that there is one, when, and its trigger time before the event. Perhaps a way to go here would be to use a script and get its reference from open file alarm.

Hey here’s an hysterical one… If you delete an event you can still get its properties – only they are the properties of the wrong event. There may be other situations where event object references just “change” without any apparent reason.

tell application "iCal"
	set e to first event of calendar "home" whose summary contains "test"
	set t to summary of e
	set z to uid of e
	delete e
	set t2 to summary of e
	set e2 to e
	set z2 to uid of e
end tell

Yes, you have to do the filter every time. Not even this works:

tell application "iCal"
	set e to first event of calendar "Normal" whose summary contains "test"
	set t to summary of e
	set z to uid of e
	delete e
	reload calendars -- should have updated, no?
	set t2 to summary of e
	set e2 to e
	set z2 to uid of e
end tell

Hi Adam. Thanks again for the hint. Adding that line in all over the place seems to fix some problems. Now the problem is taking it out out when not necessary because it slows things down. Is it only necessary immediately after deleting an event, or is it also necessary when adding events? To be more precise, here’s the situation:

I am generating an array of {UID,event,calendar} and a table in AS Studio of {summary,start date,notes,calendar,UID}. In the table the last column (UID) is in the data source but not visible on the table.

I expected to be able to get the selected UID from the table, then loop through my array to find the UID and get the actual event object. But that event object does not seem to be stable – with or without deletes. Other modifications to iCal seems to change the event object so the wrong one is fetched – sometimes even ones on different calendars from the original!

So now I have to see if the event’s UID matches its own “stored” UID in the array, and if not tell iCal to find the event again in the calendar. This slows things down a whole bunch.

So the remaining questions are:

  1. does “reload calendars” help here or will I have to do these lookups anyway? (I suppose I will).
  2. if it doesn’t necessarily help when is it actually necessary?

Thanks a bunch. I think you all will like the utility I’m working on.

You may know that Safari Bookmarks, Address Book entries, and iCal calendars are not single-file based, i.e., that an individual iCal event, A/B entry, or Safari Bookmark is not a single file; it’s in a database file. Safari’s Bookmarks (and similarly Camino’s) are stored in a single plist file (~/Library/Safari/Bookmarks.plist), Address Book uses a data file and two index files (~/Library/Application Support/AddressBook/, ABPerson.skIndexInverted, and ABSubscribedPerson.skIndexInverted, where sk stands for Search Kit), and finally, iCal maintains a directory for each Calendar (~/Library/Application Support/iCal/Sources/.calendar, where UUID identifies the user and calendar).

This means that iCal has to use Spotlight (mdfind) to find stuff and when you have made a change to iCal there’s a timing issue with the metadata record. After iCal’s mdimporter has run again, the metadata, apparently including the uuids of events has changed. Then when you use an old UUID, it leads you to the wrong record, hence the need for a new search in the refreshed metadata database. mdutil can be used to turn the spotlight mdimport for an app on and off, so you might have to play with that or figure out how to use mdfind to locate your events directly.

You can see these metadata files like so:

set IC to do shell script "ls ~/Library/Caches/Metadata/iCal/"
-- Long hex-coded file names, one for each calendar.
-- Assuming you have at least one uuid:
set C to paragraph 1 of IC
set aCal to do shell script "ls ~/Library/Caches/Metadata/iCal/" & C
-- a string of files ending in "-.icalevent"

To find a calendar cache for an event, try playing with this:

-- Find the calendar cache for the event containing the name (of my dentist, e.g.) after today
set D to paragraph -1 of (do shell script "mdfind 'kMDItemTitle == \"Dr. Penwell\" && kMDItemDueDate > $'") as Unicode text
-- Get the value of Date and Time for this event (not combined to keep it clearer but recall they can be)
set DT to paragraph -1 of (do shell script "mdls -name kMDItemDueDate " & D)
-- Prepare a notification (in AppleScript rather than the shell)
set tid to AppleScript's text item delimiters
set AppleScript's text item delimiters to "kMDItemDueDate = " as Unicode text
set Apt to text item 2 of DT
set AppleScript's text item delimiters to "/"
set tDay to (reverse of (words 1 thru 3 of Apt)) as text
set AppleScript's text item delimiters to ":"
set tTime to (words 4 thru -3 of Apt) as text
set AppleScript's text item delimiters to tid
set msg to "See Dentist on " & tDay & " at " & tTime
--> "See Dentist on 17/04/2007 at 11:45"

You can do the same sort of thing with Address Book metadata:

set tName to text returned of (display dialog "Enter a known Address Book entry as it appears there" default answer "Cadabra Abra" with title "Look Up Phone Numbers") as Unicode text
-- defaults to the well-known genie and the entry in my Address Book that I use for testing. ;-)
try -- combined form for one call, errors for 0 or more than 1 value
	set dataAB to (do shell script "P=`mdfind 'kMDItemContentType == && kMDItemDisplayName == \"" & tName & "\"'`;mdls -name kMDItemPhoneNumbers \"$P\"")
	set tid to AppleScript's text item delimiters
	set AppleScript's text item delimiters to "kMDItemPhoneNumbers ="
	set PN to text item 2 of dataAB
	set AppleScript's text item delimiters to tid
	set PhNums to text 3 thru -2 of PN
	display dialog PhNums
on error -- Either no such name found or too many found if the name appears more than once.
	display dialog "Sorry, no such name appears." & return & return & "Did you enter it as it is in your Address" & return & return & "Normally: Last_Name First_Name" with icon 0
end try