iCal - Import .ics-event

Hi,

it all started this afternoon with a rather odd tasl: I simply wanted to automatically add/import a single .ics-event sent via email to an existing calender of my iCal (2.0.5). I never thought this would get me involved into MacScripting but here I am.
I tried (as an email rule) a script I found in a forum somewhere with the following line:


tell application "iCal" to open (theAttachmentPath & n)

Everything seemed to work fine, attachment was downloaded on the Desktop, iCal opened, attachment was deleted shortly after. The problem: “open” does open iCal but does not import the event (no entry at all). However, if I manually import the .ics-file it does work.

I tried to edit the script but didn’t get very far. Somewhere I found the hint to do “guiscripting”.
So I tried:


property theAttachmentPath : (path to desktop) as Unicode text

using terms from application "Mail"
	on perform mail action with messages theMessages for rule theRule
		tell application "Mail"
			repeat with This_Message in theMessages
				repeat with ma in mail attachments of This_Message
					set n to name of ma
					if n ends with ".ics" then
						save ma in (theAttachmentPath & n)
						tell application "iCal" to open (theAttachmentPath & n)
						tell application "System Events"
							tell process "iCal"
								tell window "Importieren"
									click pop up button 1
									click menu item "myCalendar" of menu 1 of pop up button 1
									click button "OK"
								end tell
							end tell
						end tell
						tell application "Finder" to delete file (theAttachmentPath & n)
					end if
				end repeat
			end repeat
		end tell
	end perform mail action with messages
end using terms from

As expected, this didn’t work either. Do you have an idea of how to change this tell application “System Event” part? How do I know which is button 1 of which window? Do I have to use the English or German description for the menues (I am running the German version of iCal)?

Thank you very much in advance for any answer and solution.

Best, deMo

Hi.

The button clicking in iCal’s “Add Events” dialog is part of the process of opening the file, but the script won’t go on to that bit until iCal signals that it’s finished opening the file! You can use ‘ignoring application responses’ to make the script continue without waiting for the ‘open’ command to complete. Conversely, with GUI Scripting, there’s no direct feedback from the application receiving the clicks, so delay loops (may) need to be built in.

I haven’t tried the Mail part of the script but the import stuff below works on my machine with multiple single-event files and iCal 2.0.5:

property theAttachmentPath : (path to desktop) as Unicode text
property add_events : "Importieren" -- The title of the "Add Events" dialog on your system.
property cal_name : "myCalendar" -- The name of the target calendar.

using terms from application "Mail"
	on perform mail action with messages theMessages for rule theRule
		tell application "Mail"
			repeat with This_Message in theMessages
				repeat with ma in mail attachments of This_Message
					set n to name of ma
					if n ends with ".ics" then
						save ma in file (theAttachmentPath & n)
						my import_to_iCal(theAttachmentPath & n)
					end if
				end repeat
			end repeat
		end tell
	end perform mail action with messages
end using terms from

on import_to_iCal(file_path)
	tell application "iCal"
		activate
		set c to (count events of calendar cal_name)
		ignoring application responses
			open file file_path
		end ignoring
	end tell
	tell application "System Events"
		tell application process "iCal"
			set frontmost to true
			tell window add_events
				repeat until (it exists)
					delay 0.2
				end repeat
				click pop up button 1
				repeat until (menu 1 of pop up button 1 exists)
					delay 0.2
				end repeat
				click menu item cal_name of menu 1 of pop up button 1
				repeat while (menu 1 of pop up button 1 exists)
					delay 0.2
				end repeat
				click button "OK"
			end tell
		end tell
	end tell
	tell application "iCal"
		repeat until ((count events of calendar cal_name) > c)
			delay 0.2
		end repeat
	end tell
	tell application "Finder" to delete file file_path
end import_to_iCal

Edit: ‘import_to_iCal’ handler debugged and tested with multiple single-event calendar files.

Hi Nigel,
By any chance do you know how to import an .ics file using a newer version of ICal? I am using iCal version 4.0.4. The script I included is not understanding the “Add events”. I am wondering if there is a way to now accomplish what was possible with earlier versions of iCal?

My script below repeats with:
"exists window “Add events” of process “iCal”
→ false?

If not, then that’s okay. I just figured I would ask since you seem to always pull through.

Thanks,
-Jeff

tell application "Finder"
	set flPth to ((path to current user folder as text) & "Desktop:")
end tell

set posixPath to (POSIX path of (flPth)) & "Personal.ics"
set thePath to POSIX file posixPath



tell application "iCal"
	activate
	repeat with i from 1 to count of calendars
		if title of calendar i is "Personal" then
			set dupCalName to "Personal"
			set replacementName to "Personal (old)"
			set title of calendar dupCalName to replacementName
			exit repeat
		end if
	end repeat
	
	repeat with i from 1 to count of calendars
		if title of calendar i is "Personal 2" then
			set dupCalName to "Personal 2"
			set replacementName to "Personal 2 (old)"
			set title of calendar dupCalName to replacementName
			exit repeat
		end if
	end repeat
end tell

delay 1

tell application "iCal"
	activate
	ignoring application responses
		open the thePath
	end ignoring
end tell


tell application "System Events"
	tell process "iCal"
		tell window "Add events"
			repeat until (it exists)
				delay 0.2
			end repeat
			click pop up button 1
			repeat until (menu 1 of pop up button 1 exists)
				delay 0.2
			end repeat
			click menu item "New Calendar" of menu 1 of pop up button 1
			repeat while (menu 1 of pop up button 1 exists)
				delay 0.2
			end repeat
			click button "OK"
		end tell
	end tell
end tell

--check if a Personal 2 exists after the import of new iCal and change its name
tell application "iCal"
	activate
	repeat with i from 1 to count of calendars
		if title of calendar i is "Personal 2" then
			set title of calendar "Personal 2" to "Personal"
			exit repeat
		end if
	end repeat
end tell

tell application "SystemUIServer"
	activate
	if dupCalName is not "" then display dialog "Found an existing calendar named " & dupCalName & ". It has been renamed " & replacementName & "." buttons {"OK"} default button "OK" with icon 2
end tell

Hi Jeffkr.

Sorry for the slow reply. I’ve had a busy weekend and testing your script required a bit of setting up.

I’m afraid I no longer have iCal 4.0.4. I do have iCal 3.0.8 on a PowerPC machine running Lion and Calendar 8.0 on an Intel machine running El Capitan.

Your script compiles on both machines. On the El Capitan one, it even compiles for application “Calendar” directly from the application “iCal” source code! But “iCal” still has to be changed to “Calendar” in the GUI Scripting section. Calendars have had ‘names’ rather than ‘titles’ since at least iCal 2.0.5, but ‘title’ still appears to work in both cases.

You didn’t mention your start conditions, so I’ve had to infer them from your script and the thread. With iCal 3.0.8, given the existence of calendars named “Personal” and “Personal 2” and a single-event file on the desktop named “Personal.ics”, the script does more or less what I’d expect. The calendars are renamed “Personal (old)” and “Personal 2 (old)” respectively, the event is imported to a new calendar named after the event’s summary, and a dialog is displayed saying that “Personal 2” was renamed “Personal 2 (old)”.

With Calendar 8.0, the calendars are renamed, but Calendar has to be quit and reopened for the changes to appear in the GUI. The ‘open’ command in the script appears to be ignored ” not even throwing an error ” so the “Add Events” window never opens and the repeat waits forever for it to do so. Maybe this is what’s happening with iCal 4.0.4 on your machine.

The equivalent manual actions still work with Calendar 8.0: double-clicking the file on the desktop opens an “Add Event” window and clicking “OK” in this with “New Calendar” selected imports the event to a new calendar. Here, though, the new calendar’s named “Personal” after the file rather than being named with the event’s summary.

I tried scripting the Finder, instead of Calendar, to open the file and got a security message saying that CoreServicesUIAgent wanted to access my calendars. I decided not to allow this the first time, after which even the Finder wouldn’t open the file in the script, although manually double-clicking still worked. But since the access permission had been asked, I was able to allow it in the Security pane of System Preferences. After this ” and after ‘process “iCal”’ had been changed to ‘process “Calendar”’ and “Add Events” to “Add Event” ” the script successfully ran to completion. There was another difference with the calendar naming in that the new calendar was automatically given the next number in the seriesie. “Personal 3” ” and its creation encouraged Calendar to display the changed calendar names immediately.

Apart from the calendar naming differences, the version of your script below works for me with both iCal 3.0.8 and Calendar 8.0, so there’s a reasonable chance it’ll work with iCal 4.0.4 too.

set dupCalName to ""

set thePath to (path to desktop as text) & "Personal.ics"

tell application id "com.apple.iCal"
	activate
	-- Get the application's name (presumably either "iCal" or "Calendar") for the GUI Scripting later.
	set iCalName to its name
	
	-- Since it's possible for more than one calendar to have the same name, the repeat loop idea is better than simple name references here. But only one loop is necessary in this part of the script.
	repeat with i from 1 to count of calendars
		set thisCalendar to calendar i -- Gets an id reference to the calendar.
		if (name of thisCalendar is "Personal") then
			set dupCalName to "Personal"
			set replacementName to "Personal (old)"
			set name of thisCalendar to replacementName
		else if (name of thisCalendar is "Personal 2") then
			set dupCalName to "Personal 2"
			set replacementName to "Personal 2 (old)"
			set name of thisCalendar to replacementName
		end if
	end repeat
end tell

delay 1

-- "Double-click" the file, since iCal/Calendar's 'open' doesn't work in later versions.
tell application "Finder" to open file thePath

tell application "System Events"
	tell process iCalName
		-- Hedge bets over the name of the "Add Event" or "Add Events" window.
		tell (first window whose title begins with "Add event")
			repeat until (it exists)
				delay 0.2
			end repeat
			click pop up button 1
			repeat until (menu 1 of pop up button 1 exists)
				delay 0.2
			end repeat
			click menu item "New Calendar" of menu 1 of pop up button 1
			repeat while (menu 1 of pop up button 1 exists)
				delay 0.2
			end repeat
			click button "OK"
		end tell
	end tell
end tell

--check if a Personal 2 exists after the import of new iCal and change its name
tell application id "com.apple.iCal"
	activate
	repeat with i from 1 to count of calendars
		set thisCalendar to calendar i
		if (name of thisCalendar) is "Personal 2" then
			set name of thisCalendar to "Personal"
			exit repeat
		end if
	end repeat
end tell

tell application "SystemUIServer"
	activate
	if dupCalName is not "" then display dialog "Found an existing calendar named " & dupCalName & ". It has been renamed " & replacementName & "." buttons {"OK"} default button "OK" with icon 2
end tell

More than a reasonable chance. IT WORKED PERFECTLY! - Thank you very much Nigel :slight_smile:

The ‘open’ command in the script appears to be ignored ” not even throwing an error ” so the “Add Events” window never opens and the repeat waits forever for it to do so. Maybe this is what’s happening with iCal 4.0.4 on your machine. —> Yes, this is exactly what was happening.

I’m sorry, my start conditions involves an application bundle in which the .ics file is tucked away within the bundle’s folder called “DoNotOpen”

tell application "Finder"
	set flPth to ((path to me as text) & "Contents:MacOS:DoNotOpen:")
end tell

set posixPath to (POSIX path of (flPth)) & "Valpak.ics"
set thePath to POSIX file posixPath

I switched the path to the Desktop in my previous example script in order to prevent any unnecessary confusion.

Up until your explanation, I did not realize that the “Add Events” window is the equivalent of manually double-clicking a file. Knowing this logic really helps, and I assume your “Finder” solution was key, because it now works like a charm. Your other safety measures and script comments are also excellent additions.

I am very grateful for you taking the time to look into this and get back to me. You haven been a tremendous help once again. Thanks again and Happy Holidays!
-Jeff

Hi Jeff. Glad the modifications solved the problem.

It’s telling the Finder to open the file which is the equivalent of double-clicking. The Finder invokes system processes which identify the default application for the kind of file and open the file in it. I can’t think of a good reason why this should work better than a scripted command to the application itself, but there you go… :confused: