Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Outlook calendar

17 views
Skip to first unread message

happy-jack

unread,
Sep 26, 2005, 2:17:15 PM9/26/05
to
I am new to ruby and I thougt that it would be a great experance to
create a ruby script to parse the outlook calendar. I have found some
scripts to write to the calendar but nothing much about getting
information out of the calendar. I have programmed in perl and can do
this with that language but I want to see how to do it in ruby. Can
anyone help start me off??

Thanks

Jack

gregarican

unread,
Sep 26, 2005, 2:25:00 PM9/26/05
to
If you are interested I can e-mail you some .rb files I have that
push/pull data from Outlook/Exchange sources. One word of warning.
You'll have to come up with some mechanisms for trying to pull null
data.

For example if I was to pull contact data for an existing contact and I
wanted their birthday, if the birthday field hasn't been populated in
Outlook/Exchange then the MAPI call returns an error code that has to
be dealt with. It doesn't return nil/null. It errors out. I had to
write a bunch of begin-rescue-end routines into my Ruby files in order
to account for this. Microsoft justifies this behavior by stating they
were looking to streamline their database storage by not storing
anything for nil/null values. It's more like the database columns
aren't even defined at all in these cases!

In any event, if you are interested I can either post some sample code
here or e-mail you the files. Probably better the doing the latter
since they might be kind of long ...

Walter

unread,
Sep 26, 2005, 2:34:01 PM9/26/05
to
.. [snip] ...

> In any event, if you are interested I can either post some sample code
> here or e-mail you the files. Probably better the doing the latter
> since they might be kind of long ...
>


Please post to the group.

I would like to see them as well

Thanks,

Walt

--
No virus found in this outgoing message.
Checked by AVG Anti-Virus.
Version: 7.0.344 / Virus Database: 267.11.4/109 - Release Date: 9/21/2005


Gallagher Timothy-TIMOTHYG

unread,
Sep 26, 2005, 2:41:11 PM9/26/05
to

That would be great, my email is tim...@gmail.com

Thanks again



-----Original Message-----
From: gregarican [mailto:greg....@gmail.com]
Sent: Monday, September 26, 2005 2:27 PM
To: ruby...@ruby-lang.org
Subject: Re: Outlook calendar

If you are interested I can e-mail you some .rb files I have that
push/pull data from Outlook/Exchange sources. One word of warning.
You'll have to come up with some mechanisms for trying to pull null
data.

For example if I was to pull contact data for an existing contact and I
wanted their birthday, if the birthday field hasn't been populated in
Outlook/Exchange then the MAPI call returns an error code that has to
be dealt with. It doesn't return nil/null. It errors out. I had to
write a bunch of begin-rescue-end routines into my Ruby files in order
to account for this. Microsoft justifies this behavior by stating they
were looking to streamline their database storage by not storing
anything for nil/null values. It's more like the database columns
aren't even defined at all in these cases!

In any event, if you are interested I can either post some sample code

James Britt

unread,
Sep 26, 2005, 2:43:56 PM9/26/05
to


This may be of help:

http://www.jamesbritt.com/index.rb/Development@Outlook_Appointments_with_Ruby.txt


James


--

http://www.ruby-doc.org - The Ruby Documentation Site
http://www.rubyxml.com - News, Articles, and Listings for Ruby & XML
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://www.jamesbritt.com - Playing with Better Toys


gregarican

unread,
Sep 26, 2005, 3:06:19 PM9/26/05
to
Walter wrote:

> Please post to the group.
>
> I would like to see them as well
>
>
> Thanks,
>
>
> Walt

You got it. The line/space formatting might not be exact since I'm
pasting from an e-mail I sent happy-jack, but here goes:

------------------------------------------------
First of all, check out the CDO Live website for details on how to
interface with all of the MAPI properties that correspond to calendar,
contact, message, etc. data. Here's a page that I relied upon -->
http://www.cdolive.com/cdo10.htm. If you wrote Perl scripts that
interfaced with MAPI objects then you probably are familiar with what
is involved.

For the most part I just looked at the VB samples on the CDO Live
website and turned the syntax around so that they worked in Ruby. Below
is a sample method of a MAPI query that I created to pull contact
information from a Sales Contacts folder that's in the Exchange Public
Folders at my company. I use the last name and the last 4 digits of the
home telephone number as selection criteria. I create the Sales
Contacts folder object in Ruby using win32ole and my own mapi profile
housed in Outlook. Since this folder object creation is used in other
areas of my program I saved it as a method that can be reused.

def getSalesContacts
# create the Exchange server session
session = WIN32OLE.new('Mapi.Session')
session.logon('Greg Kujawa')

# navigate to the Sales Contacts entry underneath All Public Folders
publicFolders = String.new
recordCount = session.InfoStores.count
recordCount.times { |i|
publicFolders = session.InfoStores.Item(i+1).RootFolder.Folders if
session.InfoStores.Item(i+1).name == "Public Folders"
}
allPublicFolders = String.new
recordCount = publicFolders.count
recordCount.times { |i|
allPublicFolders = publicFolders.Item(i+1).Folders if
publicFolders.Item(i+1).name == "All Public Folders"
}
salesContacts = String.new
recordCount = allPublicFolders.count
recordCount.times { |i|
salesContacts = allPublicFolders.Item(i+1).messages if
allPublicFolders.Item(i+1).name == "Sales Contacts"
}
return salesContacts
end

def getMAPIRecordset(lastName, homePhone)
@allContacts = getSalesContacts
@recordCount = @allContacts.count
@resultSet = Array.new
lastName.gsub!(/\s+/, "")
@lastName = lastName.downcase
homePhone.gsub!(/\s+|\D+/, "")
homePhone =~ /(\d\d\d\d)$/
@homePhone = $1
@recordCount.times do |i| # iterate through the record count to find
the record based on given criteria
begin
@contactLastName = String.new
@contactLastName <<
@allContacts.Item(i+1).Fields.Item(@cdoPR_SURNAME).Value.to_s.downcase
rescue
@contactLastName = "Invalid"
end

begin
@contactHomePhone = String.new
@contactHomePhone <<
@allContacts.Item(i+1).Fields.Item(@cdoPR_HOME_TELEPHONE_NUMBER).Value
rescue
@contactHomePhone = "Invalid"
end
if @contactLastName == @lastName and @contactHomePhone[-4..-1] ==
@homePhone
begin
@resultSet[0] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_DISPLAY_NAME).value.to_s
rescue
@resultSet[0] = " "
end

begin
@resultSet[1] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_COMPANY_NAME).value.to_s
rescue
@resultSet[1] = " "
end

begin
@resultSet[2] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_SURNAME).value.to_s
rescue
@resultSet[2] = " "
end

begin
@resultSet[3] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_MIDDLE_NAME).value.to_s
rescue
@resultSet[3] = " "
end

begin
@resultSet[4] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_GIVEN_NAME).value.to_s
rescue
@resultSet[4] = " "
end

begin
@resultSet[5] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_STREET_ADDRESS).value.to_s
rescue
@resultSet[5] = " "
end

begin
@resultSet[6] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_LOCALITY).value.to_s
rescue
@resultSet[6] = " "
end

begin
@resultSet[7] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_STATE_OR_PROVINCE).value.to_s

rescue
@resultSet[7] = " "
end

begin
@resultSet[8] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_POSTAL_CODE).value.to_s
rescue
@resultSet[8] = " "
end

begin
@resultSet[9] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_BODY).value.to_s
rescue
@resultSet[9] = " "
end

begin
@resultSet[10] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_CUSTOMER_NUMBER).value.to_s
rescue
@resultSet[10] = " "
end

begin
@resultSet[11] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_BIRTHDAY).value.to_s
rescue
@resultSet[11] = " "
end

begin
@resultSet[12] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_WEDDING_ANNIVERSARY).value.to_s
rescue
@resultSet[12] = " "
end

begin
@resultSet[13] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_SPOUSE_NAME).value.to_s
rescue
@resultSet[13] = " "
end

begin
@resultSet[14] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_HOME_ADDRESS_CITY).value.to_s
rescue
@resultSet[14] = " "
end

begin
@resultSet[15] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_HOME_ADDRESS_POSTAL_CODE).value.to_s
rescue
@resultSet[15] = " "
end

begin
@resultSet[16] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_HOME_ADDRESS_STATE_OR_PROVINCE).value.to_s
rescue
@resultSet[16] = " "
end

begin
@resultSet[17] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_HOME_ADDRESS_STREET).value.to_s
rescue
@resultSet[17] = " "
end

begin
@resultSet[18] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_HOME_ADDRESS_FAX_NUMBER).value.to_s
rescue
@resultSet[18] = " "
end

begin
@resultSet[19] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_HOME_TELEPHONE_NUMBER).value.to_s
rescue
@resultSet[19] = " "
end

begin
@resultSet[20] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_BUSINESS_FAX_NUMBER).value.to_s
rescue
@resultSet[20] = " "
end

begin
@resultSet[21] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_BUSINESS_TELEPHONE_NUMBER).value.to_s
rescue
@resultSet[21] = " "
end

begin
@resultSet[22] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_CELLULAR_TELEPHONE_NUMBER).value.to_s
rescue
@resultSet[22] = " "
end

begin
@resultSet[23] =
@allContacts.Item(i+1).Fields.Item(@cdoPR_ENTRYID).value.to_s
rescue
@resultSet[23] = " "
end
end
end
if @resultSet == [] or homePhone !~ /\d\d\d\d\d\d\d/
return 1 # failure
else
return @resultSet # success
end
end

The begin-rescue-end statements should be better organized I'm sure.
The problem seems to be cases where the contact field hasn't been
defined. MAPI throws an error message rather than simply returning a
nill/null value. The @cdo... variables are the MAPI properties that I
pulled from the CDO Live website. A more in-depth reference can be
found at
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cdo/html/05406029-3df3-42f2-b186-b41a1097d1a2.asp.


I know my sample code doesn't directly deal with the Calendar MAPI
object but if you use my stuff as a guide and look at the CDO Live
website I'm sure you can get where you need to go. Hope this helps!

happy-jack

unread,
Sep 27, 2005, 12:29:06 PM9/27/05
to
Again I am learning, I need a start at parsing the calendar. Looking
at Ja,ses Britt's example of writing to an appointmentitem I was am
trying to parse the calendar. Please this is killing me, I have been
trying to do this for a week in perl and cant. Ruby is somthing that I
want to learn and this is the test for me.

Thanks

Timgerr

Kevin Brown

unread,
Sep 27, 2005, 2:09:29 PM9/27/05
to

I really don't want to be harsh here, believe me, but perhaps if you can't do
it in Perl the problem you're facing isn't a language related one. Why were
you unable to accomplish it in Perl? This would help me know where to start
helping you.


gregarican

unread,
Sep 27, 2005, 2:17:34 PM9/27/05
to
Whether you are working with Contact, Calendar, Tasks, etc. MAPI
objects the Ruby code is the same. Here's an example that will give you
the MAPI CDOPR_ENTRYID values for all calendar entries. In this case
it's a Vacation Calendar out in the Public Folders:

require 'win32ole'

CDOPR_ENTRYID = 0x0FFF0102 # the MAPI property ID for Entry ID's

# create the Exchange server session

@session = WIN32OLE.new('Mapi.Session')
@session.logon('Greg Kujawa')

# navigate to the Sales Contacts entry underneath All Public Folders
publicFolders = String.new

recordCount = @session.InfoStores.count
recordCount.times { |i|
publicFolders = @session.InfoStores.Item(i+1).RootFolder.Folders if
@session.InfoStores.Item(i+1).name == "Public Folders"


}
allPublicFolders = String.new
recordCount = publicFolders.count
recordCount.times { |i|
allPublicFolders = publicFolders.Item(i+1).Folders if
publicFolders.Item(i+1).name == "All Public Folders"
}

vacationCalendar = String.new


recordCount = allPublicFolders.count
recordCount.times { |i|

vacationCalendar = allPublicFolders.Item(i+1).messages if
allPublicFolders.Item(i+1).name == "Vacation Calendar"
}

recordCount = vacationCalendar.count
recordCount.times { |i|
p vacationCalendar.Item(i+1).Fields.Item(CDOPR_ENTRYID).value.to_s
}

For a listing off all of the MAPI property ID's check out
http://www.cdolive.com/cdo10.htm. You will have to translate the VB
some, as the &H0FFF0102 means 0x0FFF0102 or '0FFF0102'.hex. The trick
(as I have mentioned) when pulling data is that any nil/null field
values cause MAPI to return an error code. So you have to write a Ruby
routine that will catch this error and simply return a nil/null field
value instead of raising a runtime error. Does this help?

gregarican

unread,
Sep 27, 2005, 2:21:29 PM9/27/05
to
I also recommend the following book
(http://www.amazon.com/exec/obidos/tg/detail/-/156592665X/002-5175368-6769628?v=glance),
which I found browsing a used bookstore on my lunch break. Too bad it
was a month after I had to go through the manual pain of learning to
work with MAPI/CDO on my own :-)

Such a reference book will clue you into what you need to do in order
to work with the MAPI data store. It's not pretty, but at least it's
better than working with the win32api stuff.

happy-jack

unread,
Sep 27, 2005, 2:35:05 PM9/27/05
to
Because I am not that good of a programmer, I am learning and do not
understand the object orintated part of programming that well. I am a
network administrator and can write excelent scripts, not good
programs. BTW you are not being harsh.

James Britt

unread,
Sep 27, 2005, 2:43:15 PM9/27/05
to

A handy source of docs for this sort of thing is msdn.microsoft.com, and
general Googling for Outlook + VBScript.

When trying to manipulate Microsoft applications or ActiveX objects, I
typically go looking for examples in VBscript or JScript, and port the
code to Ruby.

gregarican

unread,
Sep 27, 2005, 3:27:03 PM9/27/05
to
Here's a final go at something. If you create a calendar called
Vacation Calendar out in the Public Folders in your Outlook/Exchange
environment and populate some entries this should work without a hitch.
Replace the MAPI session name with an Outlook profile on your client PC
that's running the Ruby script. Let me know how it goes!

----------------------------
require 'win32ole'

# these are some CDO property tag constants
CDOPROPSETID1= "0220060000000000C000000000000046"
CDOAPPT_STARTDATE = "0x820D"
CDOAPPT_ENDDATE = "0x820E"
CDOPR_BODY = 0x1000001F

# this starts a MAPI session with Outlook/Exchange


@session = WIN32OLE.new('Mapi.Session')

@session.logon('Greg Kujawa') # replace with your Outlook profile name

# this pages through the top level Outlook folders to find the Public
Folders object
publicFolders = String.new


recordCount = @session.InfoStores.count
recordCount.times { |i|
publicFolders = @session.InfoStores.Item(i+1).RootFolder.Folders if
@session.InfoStores.Item(i+1).name == "Public Folders"
}

# this pages through the next level of Outlook folders to find the All
Public Folders object


allPublicFolders = String.new
recordCount = publicFolders.count
recordCount.times { |i|
allPublicFolders = publicFolders.Item(i+1).Folders if
publicFolders.Item(i+1).name == "All Public Folders"
}

# this pages through the final level of Outlook folders to find the
Vacation Calendar object
vacationCalendar = String.new


recordCount = allPublicFolders.count
recordCount.times { |i|

vacationCalendar = allPublicFolders.Item(i+1).messages if
allPublicFolders.Item(i+1).name == "Vacation Calendar"
}

# now that we have a Vacation Calendar object we iterate through all
items in it,
# reporting back some property values


recordCount = vacationCalendar.count
recordCount.times { |i|

begin # get into the habit of rescuing MAPI/CDO reads, as items with
unassigned values raise runtime errors
puts "The calendar event is "+vacationCalendar.Item(i+1).Subject
puts "The start date is
"+vacationCalendar.Item(i+1).Fields.Item("{#{CDOPROPSETID1}}#{CDOAPPT_STARTDATE}").Value.to_s
puts "The end date is
"+vacationCalendar.Item(i+1).Fields.Item("{#{CDOPROPSETID1}}#{CDOAPPT_ENDDATE}").Value.to_s
puts "The calendar detail is
"+vacationCalendar.Item(i+1).Fields.Item(CDOPR_BODY).Value.to_s
rescue # here we just essentially report back nothing
puts "I'm blank!\n"
end
}

0 new messages