View Single Post
Quote:
Originally Posted by Ken Case View Post
There's a sample "Publish to iCal" script which runs through all your contexts and creates a new calendar for each one (named "OF: [Context]"), adding any available tasks from that context to that calendar.
It's a great script, except for two minor annoyances — it omits nested contexts, and it crashes when you run it, unless you happen to already have calendars named OF: <context> for all of your contexts :-)

A somewhat improved script is provided below. There's an property up top you can edit to control whether the script exports available or remaining actions. It traverses into nested contexts. It does not print the full project name for a project nested in a folder; that will be a fine learning exercise for someone who needs it. No attempt is made to make the calendar events show the amount of time required for various due items, because I don't believe that is useful in most cases. If you feel otherwise, you know where the sources are, knock yourself out :-)

Code:
(*
	Copyright 2007 The Omni Group.  All rights reserved.
	
	$Header: svn+ssh://source.omnigroup.com/Source/svn/Omni/branches/OmniFocus/1.x/OmniGroup/Applications/OmniFocus/Extras/SampleScripts/Publish%20to%20iCal.applescript 93889 2007-11-02 18:06:50Z bungi $
*)

property pExportAvailableTasksOnly : false -- if false, export remaining tasks; if true, export available tasks

global currentTargetCalendar

tell first document of application "OmniFocus"
	
	repeat with aContext in flattened contexts
		set MyContextID to (id of aContext)
		set MyContext to context id MyContextID
		CreateContextCalendar of me from MyContext -- also deletes existing calendar
		if ((count of (my GetTasks(MyContext))) > 0) then
			PublishContext of me from MyContext
		end if
	end repeat
	
end tell

tell application "iCal" to activate

on GetTasks(SomeContext)
	using terms from application "OmniFocus"
		if (pExportAvailableTasksOnly is true) then
			return tasks of SomeContext where blocked is false
		else
			return tasks of SomeContext where completed is false
		end if
	end using terms from
end GetTasks

on CreateContextCalendar from SomeContext
	tell application "iCal"
		set CalendarName to "OF: " & my FullContextName(SomeContext)
		try
			delete (first calendar whose name is CalendarName)
		end try
		using terms from application "OmniFocus"
			set ContextTasks to my GetTasks(SomeContext)
		end using terms from
		set currentTargetCalendar to make calendar with properties {name:CalendarName}
	end tell
end CreateContextCalendar

on PublishContext from SomeContext
	using terms from application "OmniFocus"
		PublishContextTasks of me from SomeContext
		repeat with childContext in contexts of SomeContext
			PublishContext of me from childContext
		end repeat
	end using terms from
end PublishContext

on PublishContextTasks from SomeContext
	using terms from application "OmniFocus"
		set MyTasks to my GetTasks(SomeContext)
		repeat with aTask in MyTasks
			set taskName to name of aTask
			set taskProject to containing project of aTask
			set completionDate to completion date of aTask
			set dueDate to due date of aTask
			if (dueDate is not missing value) then
				set dueDate1 to dueDate + 60 -- if we make zero-length appointments, iCal puts them on top of each other.  Make them a minute long and it spreads them out a bit.
			end if
			
			local MyEvent
			using terms from application "iCal"
				tell currentTargetCalendar
					if completionDate is equal to missing value and dueDate is not missing value then
						if taskProject is not equal to missing value then
							set MyEvent to make new event at end of events with properties {summary:my FullContextName(SomeContext) & ": " & taskName & " [" & name of taskProject & "]", start date:dueDate, end date:dueDate1}
						else
							set MyEvent to make new event at end of events with properties {summary:my FullContextName(SomeContext) & ": " & taskName & " [Inbox]", start date:dueDate, end date:dueDate1}
						end if
					end if
				end tell
			end using terms from
		end repeat
	end using terms from
end PublishContextTasks

-- return full name of context, or "(None)"
-- uses ":" as hierarchy separator, as OmniFocus does not appear to expose 
-- the value conveniently
on FullContextName(SomeContext)
	using terms from application "OmniFocus"
		local oContext
		local strContextName
		
		set strContextName to ""
		set oContext to SomeContext
		if (oContext is missing value) then
			return "(None)" -- easy case!
		end if
		
		-- if there is a context set, we need to construct the full name, 
		-- climbing up the tree to the top.  We take the name of our starting
		-- point, then look at the container.  Each time the container is not
		-- of class document, there's another layer, and we tack its name on
		-- to the front of the string.  
		set strContextName to name of oContext
		repeat while (class of (container of oContext) is not document)
			set strContextName to ":" & strContextName
			set oContext to container of oContext
			set strContextName to name of oContext & strContextName
		end repeat
		return strContextName
	end using terms from
end FullContextName