Thursday, April 7, 2011

Using Exchange Web Services API to create calendar email reminders (similar to google calendar e-mail notification reminders)

One of the coolest and simplest ideas for company calendar alerts is implemented in gmail e-mail calendar reminders. Before moving to Exchange 2010, our company briefly used a shared gmail calendar for company wide events, employee appointments, employee vacation and employee work from home days. Natively Exchange doesn't support alerts for public calendars and also doesn't support e-mail calendar reminders.

After digging around the web I created a script which utilizes the Exchange web service API, Powershell and a scheduled task. I borrowed heavily from this script http://gsexdev.blogspot.com/2009/08/twitter-your-exchange-calendar.html. After some exchange mailing list configurations,  a little tweaking of the twitter script, and adding a function for email, I was able to come up with something that seems to work pretty well.

In addition to e-mailed reminders you can also share the CompanyReminder  calendar to ALL of your employees and use it as a company calendar.

To enter a CompanyReminder event the user only has to do the following:

1. Create the appointment
2. Add CompanyReminder  as the recipient
3. And save

Every morning around 8am the script runs and pulls all of the upcoming appointments for that day and sends it to everyone in an email.

Things you will need to configure in Exchange:
  1. Install the  Exchange web service API from here http://www.microsoft.com/downloads/en/details.aspx?FamilyID=c3342fb3-fbcc-4127-becf-872c746840e1
  2. You will need 1 exchange e-mail account, moving forward this account will be referred to as the CompanyReminder account.
    • The sole purpose of this account is for receiving company reminders
    • The CompanyReminder account is setup to automatically send a copy of every email to all employees and also to automatically accept all  calendar invites.
  3. You will need a groupmailing list setup to email the intended employees (for our company it was a company wide email list), moving forward this group email list as the CompanyWide e-mail list.
The Script:

$MailboxName = CompanyReminders@yourcompany.com

function SendCompanyEmail($PostString){

   $smtp = New-Object System.Net.Mail.SMTPClient -ArgumentList MAILSERVERIP   $smtp.Send($MailboxName, 'CompanyWide@yourcompany.com','Upcoming  Company Reminders', $PostString)

}

$dllpath = 'C:\Program Files\Microsoft\Exchange\Web Services\1.1\Microsoft.Exchange.WebServices.dll'
[reflection.assembly]::loadfile($dllpath)
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2007_SP1)

$uri=[system.URI] ("https://dayman.cyber-balance.com/ews/exchange.asmx")
$service.Url = $uri
$service.Credentials = New-Object System.Net.NetworkCredential(CompanyReminder@yourcompany.com,"CompanyReminderPASSWORD","COMPANYDOMAIN")

$folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar,$MailboxName)
$CalendarFolder = [Microsoft.Exchange.WebServices.Data.CalendarFolder]::Bind($service,$folderid)

$cvCalendarview = new-object Microsoft.Exchange.WebServices.Data.CalendarView([System.DateTime]::Now, [System.DateTime]::Now.AddDays(14))
$cvCalendarview.MaxItemsReturned = 200;
$cvCalendarview.PropertySet = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)

$frCalendarResult = $CalendarFolder.FindAppointments($cvCalendarview)
$appointmentString = ""
foreach ($apApointment in $frCalendarResult.Items){


if ($apApointment.Sensitivity -ne [Microsoft.Exchange.WebServices.Data.Sensitivity]::Private -band $apApointment.IsReminderSet -eq $true){
#$ReminderSendTime = $apApointment.Start.AddMinutes(-$apApointment.ReminderMinutesBeforeStart)
$ReminderSendTime = $apApointment.Start

if ($ReminderSendTime -ge [System.DateTime]::Now -band $ReminderSendTime -le [System.DateTime]::Now.AddMinutes(1500) -band !$apApointment.IsCancelled )
{
$appointmentString = $appointmentString + $apApointment.Organizer.Name + " " + $apApointment.Organizer.Address + "`n`n" + $apApointment.Subject.ToString() + "`n`nStarts : " + $apApointment.Start.ToString("ddd MM/dd/yyyy - HH:mm:ss") + "`n`Ends : " + $apApointment.End.ToString("ddd MM/dd/yyyy - HH:mm:ss") + "`n`n" + $apApointment.Location

if(!$apApointment.Body.Text -eq "")
{
$appointmentString = $appointmentString + "`n`n" + $apApointment.Body.Text
}


$appointmentString = $appointmentString + "`n`n---------------------------------------------------`n`n"
}

}

}
if(!$appointmentString -eq "")
{
SendCompanyEmail($appointmentString)
}
Script Schedule Execution Steps:

Take the above script and save is as a c:\Scripts\CompanyReminder.ps1 file (powershell).

Setup a windows scheduled task to run whenever you want the daily notification e-mail sent with the following command Powershell.exe c:\Scripts\CompanyReminder.ps1

And that's it!

Sample E-Mail:


---------------------------------------------------



Created By: CompanyReminders



Testing 8am CompanyReminder



Starts : Fri 04/08/2011 - 10:30:00

Ends : Fri 04/08/2011 - 11:00:00



Location: My Fun Location



---------------------------------------------------



References:

Twitter Exchange Calendary Reminder with powershell
http://gsexdev.blogspot.com/2009/08/twitter-your-exchange-calendar.html

http://msdn.microsoft.com/en-us/library/dd633710%28v=exchg.80%29.aspx

Thursday, June 18, 2009

ASP Server.Execute vs Include, passing variables and dynamically loading different pages

Sometimes you have to work with legacy applications that utilize asp (classic) vb from the 90’s (ahh the days of .com where the internet was supposed to make everyone a millionaire and retire at 30).

Anyways…
So here is the issue I came across in ASP VB regarding dynamic layouts. The basic issue is that the client wants to make a template for the application so that it looks like their own website. Great idea, just swap out the include header and footer in your code right (hopefully you created include files for your header and footer in your application layout)? problem solved.

But lets take this a step further. Now that we know this is something that can happen sales/marketing will probably jump on this idea and say that the application can be customized to look like your website. Thinking like a developer (aka try to automate, reduce redundant work and save your sanity) rather then having to modify and maintain a ton of different client’s applications with the same functionality but slightly different HTML, why not create a template type of framework so that the application could read different templates dynamically without the need of developing the custom HTML for each page. Easy right? Just have some code be able to dynamically change the header and footer files?

In ASP.NET with the advent of usercontrols and Masterpages it gave the developer the opportunity to really make the website content layout dynamic and programmable. Basically the ability to swap different templates, control, html etc… in different areas programatically with great ease.

In ASP VB you cannot change the include file programatically. This is due to the way asp read include files. It processes the include first then executes the asp code. Basically you must have a hardcoded file in the include no variables.

This will NOT work:

dim FileToInclude
FileToInclude = “header.asp”


Well shoot, that means you can’t change the includes by coding. Well there is another thing that will solve this issue (although with a problem), The Server.Execute operation.

This WILL work:

dim FileToInclude
FileToInclude = “header.asp”
Server.Execute(”FileToInclude”)

Great right?
Well one major problem. Any page specific variables (ex. NOT application , session) that exists before the execute will not be passed.

You will not be able to see the the LookAtMe variable in the FileToInclude asp page

LookAtMe = false
FileToInclude = “header.asp”
Server.Execute(”FileToInclude”)
The only work around I found was to use a session variable (I know very very bad practice).

Why would I care about passing the variable? Well suppose you have a variable that is on the top of each page that contains a number that is critical to setting something in the header.asp file. For example maybe a flag that states whether part of the menu should be shown if you are an Admin or a User for the application.

After this fun little exercise it gave me a great appreciation for MasterPages and UserControls . When usercontrols first came out I was wondering what was the reason for their use, why not use an include, but here is a perfect example of why to use UserControls and MasterPages.

Tuesday, April 7, 2009

C# bool and Javascript Boolean True vs true

A little tid bit, in JavaScript a boolen is true while in C# it is True

When writing JavaScript from C# you must do this

strScript.Append("var IsSSL=" + this.m_oReportData.ReturnValueT.IsSSL.ToString().ToLower() + ";");

Note that you must do ToString().ToLower() or else javascript craps out and doesn’t know what True means.

SourceSafe saves SQL Stored Procedures as Binary

For some reason Source Safe only works with ASCII. SQL Manager 2005 by default saves everything as UNICODE (The standard) but source safe cannot recognize this. In order to fix this you must do the following:

The issue is that sql manager saves everything in UNICODE format by default. This is an issue because source safe doesn’t accept Unicode and saves the sql file as binary. When using sql manager and saving a sql file be sure to do save encoding choose US acsii.