Tuesday, December 12, 2017

#1 2017-07-28 10:27:02 am

akim
Member
Registered: 2010-04-04
Posts: 44

calendar selection

Without using GUI methods, what would be an ASObjC method of accessing an event id from a Calendar selection, with the goal of finding its  properties?

Model: MacBook Pro
AppleScript: 2.5
Browser: Safari 600.5.17
Operating System: Mac OS X (10.10)

Offline

 

#2 2017-07-28 06:21:38 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5199

Re: calendar selection

You can't access any app's selection except via GUI scripting or an exposed scripting property. (Thankfully, or it would be a massive security risk.)


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Online

 

#3 2017-07-28 08:52:40 pm

akim
Member
Registered: 2010-04-04
Posts: 44

Re: calendar selection

I found this solution posted by johneday
http://www.johneday.com/1086/reference- … pplescript
where the author
1. detected  the event identification contained in  com.apple.ical SelectedEvents
2. SQL'ed calendar and event id's
3. to reference   Application "Calendar"'s calendar id's event id
4. and then to sqlite3 the ~/Library/Calendars/Calendar Cache database
4. After  the Calendar Cache database's  calendar and  event id  are identified, Calendar's event's properties and contents could be processed

Applescript:


------------------------------
-- ABOUT
------------------------------
--http://www.johneday.com/1086/reference-selected-calendar-events-applescript
-- Written and tested in Yosemite, Calendar Version 8.0
-- The plist may take several seconds to update after a new event has been selected.
-- Only the first instance of a recurring event will be referenced.

-------------------------------
-- MAIN CODE
------------------------------
set defaultsReply to (do shell script "defaults read com.apple.ical SelectedEvents")
set selectedEvents to parseDefaults(defaultsReply)

if selectedEvents = {} then
display notification "Please try again" with title "No Calendar Event Selected"
return
end if

set eventReferenceList to {}
repeat with sEvent in selectedEvents
set {eventID, calendarID} to sqlQuery(sEvent)
tell application "Calendar"
set eventReference to event id eventID of calendar id calendarID
-- INSERT YOUR CODE TO PROCESS EACH EVENT

-- Example of "Alert 15 minutes before start"
my addDisplayAlarm(eventReference, -15)

-- OR BUILD A LIST OF EVENTS
set end of eventReferenceList to eventReference
end tell
end repeat
return eventReferenceList

------------------------------
-- HANDLERS
------------------------------
on parseDefaults(resultText)
set localUIDs to {}
set {TID, text item delimiters} to {text item delimiters, quote}
set resultItems to text items of resultText
set text item delimiters to TID
repeat with i from 1 to (count resultItems)
if i mod 2 = 0 then set end of localUIDs to resultItems's item i
end repeat
return localUIDs
end parseDefaults


on sqlQuery(localUID)
local dateString, localUID
if localUID contains "/" then
set {TID, text item delimiters} to {text item delimiters, "/"}
set {dateString, localUID} to text items of localUID
set text item delimiters to TID
end if

set sqlText to "
SELECT DISTINCT zcalendaritem.zshareduid AS eventID
, znode.zuid as calID
FROM zcalendaritem
JOIN znode
ON znode.z_pk = zcalendaritem.zcalendar
AND zcalendaritem.zlocaluid = '"
& localUID & "'
;"


set sqlPath to POSIX path of (path to library folder from user domain) & "Calendars/Calendar Cache"
set {TID, text item delimiters} to {text item delimiters, "|"}
set {eID, cID} to text items of (do shell script "echo " & quoted form of sqlText & " | sqlite3 " & quoted form of sqlPath)
set text item delimiters to TID
return {eID, cID}
end sqlQuery


on addDisplayAlarm(myEvent, triggerInterval)
tell application "Calendar"
tell myEvent
if not (exists (display alarms whose trigger interval = triggerInterval)) then
set myAlarm to make new display alarm at end of display alarms with properties {trigger interval:triggerInterval}
end if
end tell
end tell
end addDisplayAlarm

Might an alternative ASObjC method be substituted for the SQL command in johneday's script?

Offline

 

#4 2017-07-29 12:14:25 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5199

Re: calendar selection

akim wrote:

Might an alternative ASObjC method be substituted for the SQL command in johneday's script?



No -- it's accessing the backing store using SQLite *because* there's no legitimate way. It's a hack.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Online

 

#5 2017-07-29 11:38:13 pm

akim
Member
Registered: 2010-04-04
Posts: 44

Re: calendar selection

Shane,
It appears that in my attempt to find an ASobjC method, I have run afoul of some rule, of which I was not familiar. 
If an  applescript runs a shell script calling sql on my calendar's database , from your perspective it would  be illegitimate or hacking my own computer. I, however, do not clearly comprehend your analysis.

Are you saying that
1. the applescript sql method is  illegitimate as it  places my calendar library cache  at  risk for  destruction or corruption?
2. although I am accessing  my own calendar's  library information, it is illegal?
3. something else altogether different?

If  this data is on my computer, I do not understand where illegitimacy arises, in an  attempt to extract it.
If you might explain your thoughts further, I would greatly appreciate  your insights.

Offline

 

#6 2017-07-30 03:12:51 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5199

Re: calendar selection

Calendar data is handled by the Core Data framework, which is also used by lots of other parts of the OS, as well as many apps. In turn, Core Data generally uses SQLite to serialize the actual data to disk.

You can't do any harm reading that data directly -- only if you write to it. However, there's no guarantee that the SQLite database won't change. It can change because Apple decides to modify the behavior of the relevant framework, but it can also change if they update the Core Data framework.

So it's not legitimate in the sense of being a back-door approach, and therefore potentially fragile. But that doesn't mean you can't use it.

That said, now that I look closely at the code, it's in two parts: reading the real ID (which is different from the iCal scripting ID) of the selected event via defaults, and then getting the iCal event ID and calendar ID via sqlite3. It seems to me that you can use defaults to get the real ID, and then use one of my calendar libs and a bit of ASObjC to get the iCal event ID and calendar ID -- no need for sqlite3.

So something like this:

Applescript:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "EventKit"
use script "CalendarLib EC" version "1.1.1"
use scripting additions

set selectedID to (((current application's NSUserDefaults's alloc()'s initWithSuiteName:"com.apple.ical")'s objectForKey:"SelectedEvents")'s objectForKey:"iCal")'s firstObject()
if selectedID = missing value then error "No selection found"
set theEKEventStore to fetch store
set theEvent to theEKEventStore's eventWithIdentifier:selectedID
set theEventID to theEvent's calendarItemExternalIdentifier() as text
set theCalID to theEvent's calendar()'s calendarIdentifier() as text

tell application id "com.apple.iCal" -- Calendar
   tell event id theEventID of calendar id theCalID
       --
   end tell
end tell


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Online

 

#7 2017-07-30 08:19:22 pm

akim
Member
Registered: 2010-04-04
Posts: 44

Re: calendar selection

Shane,
Thanks for the Core Data framework explanation and for the ASObjC example.
For reasons, that  I cannot explain, the following two scripts do not yield the same identifier.

1. NSUserDefaults's object for key

Applescript:

set selectedID to (((current application's NSUserDefaults's alloc()'s initWithSuiteName:"com.apple.ical")'s objectForKey:"SelectedEvents")'s objectForKey:"iCal")'s firstObject()

2. shell script defaults read

Applescript:

set defaultsReply to (do shell script "defaults read com.apple.ical SelectedEvents")

What might some of the possible reasons for these differing results?

Last edited by akim (2017-07-30 08:44:51 pm)

Offline

 

#8 2017-07-30 09:52:00 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5199

Re: calendar selection

akim wrote:

What might some of the possible reasons for these differing results?



One's returning a string and the other an NSString. As you're passing the result to a method that ultimately requires an NSString, coercing the NSString to a string is just a waste of time. But you can do so if you want to compare them.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Online

 

#9 2017-08-02 02:34:13 am

akim
Member
Registered: 2010-04-04
Posts: 44

Re: calendar selection

Shane,
I appreciate your explanations and understand that the ASObC script using NSUserDefaults returns an NS String.

Applescript:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "EventKit"
use script "CalendarLib E
set selectedID to (((current application's NSUserDefaults's alloc()'s initWithSuiteName:"
com.apple.ical")'s objectForKey:"SelectedEvents")'s objectForKey:"iCal")'s firstObject()

This ASObC script, however, returns missing value
The AS shell script using defaults read

Applescript:

set defaultsReply to (do shell script "defaults read com.apple.ical SelectedEvents")

as you said returns a more standard string…
"{
    iCal =     (
        \"3207A4D8-FA77-4DAF-8E50-75810465D860\"
    );
}"
It does not return a  missing value.

I understand your discussion of illegitimate access and that the underlying or Core Data framework might change without notice from Apple. In  your opinion, should the ASObjC return a a missing value when the AS shell script returns a string?

Offline

 

#10 2017-08-02 03:02:02 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5199

Re: calendar selection

What do you get from this:

Applescript:

use framework "Foundation"
set selectedID to ((current application's NSUserDefaults's alloc()'s initWithSuiteName:"com.apple.ical")'s objectForKey:"SelectedEvents") as record


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Online

 

#11 2017-08-04 12:32:01 am

akim
Member
Registered: 2010-04-04
Posts: 44

Re: calendar selection

Before I open the Calendar application or before I select an event in one of the calendars, the first Applescript:

Applescript:

set defaultsReply to (do shell script "defaults read com.apple.ical SelectedEvents"

yields
"{
    iCal =     (
    );
}"

and the second Applescript:

Applescript:

use framework "Foundation"
set selectedID to ((current application's NSUserDefaults's alloc()'s initWithSuiteName:"com.apple.ical")'s objectForKey:"SelectedEvents") as record

yields
{iCal:{"0A269956-5214-4A75-9D89-35483077DDDA"}}

After I select an event in one of the calendars, the first Applescript:

Applescript:

set defaultsReply to (do shell script "defaults read com.apple.ical SelectedEvents"

now yields
"{
    iCal =     (
        \"20170802T000000Z/B2F1BC7F-BB1B-406F-99A9-9EB8739EBF3A\"
    );
}"

but the second Applescript:

Applescript:

use framework "Foundation"
set selectedID to ((current application's NSUserDefaults's alloc()'s initWithSuiteName:"com.apple.ical")'s objectForKey:"SelectedEvents") as record

still yields
{iCal:{"0A269956-5214-4A75-9D89-35483077DDDA"}}

For some reason, the two commands appear to be deriving data from different sources.
I appreciate any of your  insights on this conundrum.

Offline

 

#12 2017-08-04 01:06:55 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5199

Re: calendar selection

I have no idea why they're different. You could try running this:

Applescript:

use framework "Foundation"
set selectedID to ((current application's NSUserDefaults's alloc()'s initWithSuiteName:"com.apple.ical")'s dictionaryRepresentation()) as record

and this:

Applescript:

set defaultsReply to (do shell script "defaults read com.apple.ical"

And comparing them.

FWIW, I suspect this is being used to pass information about the selection between Apple's apps, and it wouldn't surprise me if there's a little more involved.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Online

 

#13 2017-08-04 02:10:41 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5199

Re: calendar selection

So it looks like NSUserDefaults is taking a snapshot of the defaults, and they don't change until you quit the host app. I'm really not sure why -- it seems very odd.

So it looks like you need to use defaults to read the id -- but you should still be able to use the rest of the script I posted.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Online

 

#14 2017-08-04 02:27:45 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5199

Re: calendar selection

OK, I think I found the problem -- we're using the wrong name. It's not com.apple.ical, it's com.apple.iCal.

Try this:

Applescript:

use framework "Foundation"
set selectedID to ((current application's NSUserDefaults's alloc()'s initWithSuiteName:"com.apple.iCal")'s objectForKey:"SelectedEvents") as record


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Online

 

#15 2017-08-05 03:14:22 am

akim
Member
Registered: 2010-04-04
Posts: 44

Re: calendar selection

Shane, you are correct!
What a difference an upper case C iCal makes!
Now, both scripts …

Applescript:

use framework "Foundation"
use scripting additions
set defaultsReply to do shell script "defaults read com.apple.ical SelectedEvents"
set selectedID to (current application's NSUserDefaults's alloc()'s initWithSuiteName:"com.apple.iCal")'s objectForKey:"SelectedEvents"

…access the same object identification.

I wish I knew  the reason that might explain the  lower case c ical NS script yielding an NS string,rather than returning an  error.
If you could explain, I would appreciate the education.
In either regard, thanks for your great help.

Offline

 

#16 2017-08-05 06:56:31 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5199

Re: calendar selection

It seems that ical works to read the values, but that they're cached somewhere by the app, and presumably whatever process synchronizes the cache insists on iCal. So whatever you get the first time, you continue to get (until you reboot the script editor).


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Online

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)