How to send Real Meeting Request From Sharepoint 2010 – Part 2

Hello Everyone ,

How to send Real Meeting Request From Sharepoint 2010 article continues with part2

Here is the links for How to send Real Meeting Request From Sharepoint 2010 Series:
Lets have a look for internally whats happening in a Meeting Workspace.

If you open a meeting workspace web with Sharepoint Manager 2010 you can brows all components in a basic meeting workspace template.
Some important list are Attendees , Meeting Serries , Workspace Pages ,

The Meeting Serries list contains Event information when you related a calender item with a meeting workspace instance. In this demo we have already attached two calender event this this meeting workpaced named “Scrum Meetings” . Every item has in Meeting Series an InstanceID so when you open a Event item and click linked workspace url
you can see this instanceID has been added end of this url . For example:

That means sharepoint sperates data according this Instance Parameter. As you noticed if you look carefully above image we have 6 attendees . This number is sum of all attendees count .But when you connect a workspace with an InstanceID  and if you compare fallowing properties for an Attendees list.
SPList attendeesList = web.Lists[“Attendees”];
attendeesList.ItemCount  is 6 : means all attendees count contains other meeting instances counts
attendeesList.Items.Count is 2 : meens this instance attendees count.

Sharepoint is differantiate the attendees data automatically and internally for which meeting workspace instance in use.

So be careful when you are working with in Meeting Workspaces on InstaceID property and differantiation of data according to this property.

Sharepoint Manager 2010 is a very useful tool when you programming with Sharepoint. You can use it with define Column Names and properties for serval objects in hiearchically.

But what if you dont want to use it , the powershell can also makes some help in this issue. For example if you want to learn which internal columns of an Meeting Series List item . you can get this information via powershell and write in a file in your drive like:

$Site = Get-SPList
$web = $site.OpenWeb(“ScrumMeetings”)
$mList = $web.Lists[“Meeting Series”]
$mList.Fields |ft title , internalname > c:\MeetingSeriesFields.txt

One advantage of powershell that you can also check values of created properties which SPM2010 does not. For example you may wonder how sharepoint keeps EventURL of an Related Event item:
$Site = Get-SPList
$web = $site.OpenWeb(“ScrumMeetings”)
$mList = $web.Lists[“Meeting Series”]
$item = $mList.Items[1];
$item[“EventURL”] and press enter
Results is: [URL], [List Title] -> There is a space char after “,” so you should use trim() function when you parsing this data., Meeting Calendar

After we complete understanding on Meeting Workspace  now contine with our project

MeetingRequestWebPart -> Page Load
  1. protected void Page_Load(object sender, EventArgs e)
  2. {
  3.     #region GUI operations
  4.     //Some GUI operations
  5.     #endregion
  6.     //This webpart is only usable for Meeting Workspace Sites.
  7.     if (!SPMeeting.IsMeetingWorkspaceWeb(SPContext.Current.Web))
  8.     {
  9.         InformationText.Text = “This webpart is only addable for a Meeting Workspace Web Site”;
  10.         AdminPanel.Visible = false;
  11.         return;
  12.     }
  13.     //Get Current Meeting information
  14.     SPMeeting currentMeeting = SPMeeting.GetMeetingInformation(SPContext.Current.Web);
  15.     MeetingInfo info = MeetingInfo.GetMeetingInfo(currentMeeting);
  16.     //Only the meeting organizer manage meetigs.
  17.     if (!info.OrgnaizerAccount.Equals(SPContext.Current.Web.CurrentUser.LoginName, StringComparison.InvariantCultureIgnoreCase))
  18.     {
  19.         InformationText.Text = “Only Organizer of this meeting \”” + info.OrganizerName + “\” can manage the requests”;
  20.         AdminPanel.Visible = false;
  21.     }
  22.     else
  23.     {
  24.         if (IsAppointmentExists(info.MeetingIdentifier)) //if Meeting request is already sent.
  25.         {
  26.             SavedMeetingInfo = LoadAppointment(info.MeetingIdentifier);
  27.             btnSendMeetingRequest.Visible = false;
  28.             btnReSendMeetingRequest.Visible = true;
  29.             btnCancelMeeting.Visible = true;
  30.             btnRefreshAttendeesStatus.Visible = true;
  31.             if(!_ByPassClearInfo)
  32.                 InformationText.Text = “Meeting requests have been send at” + SavedMeetingInfo.RequestSentDate.ToLongDateString() + ” “ + SavedMeetingInfo.RequestSentDate.ToLongTimeString();
  33.         }
  34.         else // if not sent before
  35.         {
  36.             btnSendMeetingRequest.Visible = true;
  37.             btnReSendMeetingRequest.Visible = false;
  38.             btnCancelMeeting.Visible = false;
  39.             btnRefreshAttendeesStatus.Visible = false;
  40.         }
  41.         //assign button events.
  42.         btnSendMeetingRequest.Click += new EventHandler(btnSendMeetingRequest_Click);
  43.         btnReSendMeetingRequest.Click += new EventHandler(btnReSendMeetingRequest_Click);
  44.         btnCancelMeeting.Click += new EventHandler(btnCancelMeeting_Click);
  45.         btnRefreshAttendeesStatus.Click += new EventHandler(btnRefreshAttendeesStatus_Click);
  46.     }
  47. }

In Page Load,

We are doing some gui operations.
Checking webpart added correct web site template , should be a meeting workspace template
Getting Current and latest meeting information
Checking the owner mean organizer beacuse only orginazer manages meetings.
Prepare GUI according to if meeting is already send or not.
Also we set here the SavedMeetinfo property of control via fallowing line
-> SavedMeetingInfo = LoadAppointment(info.MeetingIdentifier);
MeetingInfo LoadAppointment(string identifer)  helper function brings us the Saved MeetingInfo in previous state from web.Properies bag.
That information is imported because we are storing Exchange Appointment object’s ID in this object.

MeetingRequestWebPart -> Refresh Attendees Status
  1. public void RefreshAttendeesStatus()
  2. {
  3.     if (SavedMeetingInfo == null) return;
  4.     ExchangeServiceConnection conn = new ExchangeServiceConnection(SPContext.Current.Web.CurrentUser);
  5.     SPMeeting currentMeeting = SPMeeting.GetMeetingInformation(SPContext.Current.Web);
  6.     Appointment app = Appointment.Bind(conn.Service, SavedMeetingInfo.AppointmentID);
  7.     Dictionary<string, MeetingResponseType> aStatus = new Dictionary<string, MeetingResponseType>();
  8.     foreach (Attendee att in app.RequiredAttendees)
  9.     {
  10.         if (att.ResponseType != null)
  11.         {
  12.             aStatus.Add(att.Address, (MeetingResponseType)att.ResponseType);
  13.         }
  14.     }
  15.     foreach (Attendee att in app.OptionalAttendees)
  16.     {
  17.         if (att.ResponseType != null)
  18.         {
  19.             aStatus.Add(att.Address, (MeetingResponseType)att.ResponseType);
  20.         }
  21.     }
  22.     RunAsAdmin.Run((site, web) =>
  23.     {
  24.         SPList Attendees = web.Lists[“Attendees”];
  25.         foreach (SPListItem item in Attendees.Items)
  26.         {
  27.             SPUser user = web.EnsureUser(item.Title);
  28.             if (aStatus.ContainsKey(user.Email))
  29.             {
  30.                 MeetingResponseType rtype = aStatus[user.Email];
  31.                 item[“Response”] = ExchangeHelper.ConvertSPMeetingResponse(rtype);
  32.                 item.Update();
  33.             }
  34.         }
  35.     });
  36. }

In refresh attendees function,

We are getting current Meeting information and already created Appointment object. For updating attendees status a meeting request must already be created.
Collecting all Attedees and thier responses in a dictionary (considering performance )
Updating Attendees status in web.Lists[“Attendees”] list . We are converting Response type beacuse Exchange.MeetingResponseType has 6 option but Response (Choice)
Column in attendees list only 4 option that none,Accepted,Declined,Tentative

MeetingRequestWebPart -> Update Meeting Request
  1. public void UpdateMeetingRequest()
  2. {
  3.     if (SavedMeetingInfo == null) return;
  4.     ExchangeServiceConnection conn = new ExchangeServiceConnection(SPContext.Current.Web.CurrentUser);
  5.     SPMeeting currentMeeting = SPMeeting.GetMeetingInformation(SPContext.Current.Web);
  6.     Appointment app = Appointment.Bind(conn.Service, SavedMeetingInfo.AppointmentID);
  7.     MeetingInfo info = MeetingInfo.GetMeetingInfo(currentMeeting);
  8.     ExchangeHelper.UpdateAppointment(app, info);
  9.     //Clear All Attendees
  10.     app.RequiredAttendees.Clear();
  11.     app.OptionalAttendees.Clear();
  12.     //Add Atteendees from Workspace Attendees List.
  13.     AddAttendees(app);
  14.     try
  15.     {
  16.         app.Update(ConflictResolutionMode.AutoResolve);
  17.         info.AppointmentID = SavedMeetingInfo.AppointmentID;
  18.         InfoMessage = “Meeting Request sent successfully”;
  19.     }
  20.     catch (Exception ex)
  21.     {
  22.         ULSLoggingService.LogError(ULSLoggingService.SMR_ERROR, ex.Message + Environment.NewLine + ex.StackTrace);
  23.         InfoMessage = “An Error occured.Please contact your administrator”;
  24.     }
  25.     UpdateAppointment(info);
  26. }

In UpdateMeetingRequest Function ;
First we checking SavedMeetingInfo object is exists.
Creating an exchange connection.
Getting current meeting.
Appointment app = Appointment.Bind(conn.Service, SavedMeetingInfo.AppointmentID); // with this line we are binding already created Appointment object in Exchange. SavedMeetingInfo object contains AppointmentID which i mentioned above.
We are setting old Appointment object with new values by calling ExchangeHelper.UpdateAppointment(app, info);

(Basically we are doing in ExchangeHelper.UpdateAppointment something like below:
app.Subject = info.Title;
app.Start = info.EventDate;
app.End = info.EndDate;
app.Location = info.Location;
…. return app; )
Clearing All Attendeess
Add all attendees again

Update Appointment object that will resend all meeting request again via app.Update() command.
Set AppointmentID to new MeetingInfo class instance.
Update new MeetingInfo class via overriding old MeetingInfoClass (SavedMeetingInfo)

MeetingRequestWebPart -> Cancel Meeting Request
  1. public void CancelMeetingRequest()
  2. {
  3.     if (SavedMeetingInfo == null) return;
  4.     ExchangeServiceConnection conn = new ExchangeServiceConnection(SPContext.Current.Web.CurrentUser);
  5.     SPMeeting currentMeeting = SPMeeting.GetMeetingInformation(SPContext.Current.Web);
  6.     try
  7.     {
  8.         //Get existed appointment.
  9.         Appointment app = Appointment.Bind(conn.Service, SavedMeetingInfo.AppointmentID);
  10.         app.CancelMeeting();
  11.         ClearAppointment(SavedMeetingInfo);
  12.         InfoMessage = “Meeting has been canceled successfully.”;
  13.     }
  14.     catch (Exception ex)
  15.     {
  16.         ULSLoggingService.LogError(ULSLoggingService.SMR_ERROR, ex.Message + Environment.NewLine + ex.StackTrace);
  17.         InfoMessage = “An error occured.Please contact your administrator”;
  18.     }
  19. }

In CancelMeetingRequest function:
Like before we are checking SavedMeetInfo is exists , creating an exchange connection object and getting currentmeeting object.
Appointment app = Appointment.Bind(conn.Service, SavedMeetingInfo.AppointmentID); // with this line we are binding already created Appointment object in Exchange.
And Canceling Meeting via app.CancelMeeting() function.
Clearing previously saved MeetingInfo instance from
PS: There is a trick when you try removing a property correctly from SPWeb object .Check the fallowing link:

Next Article: Part 3 Deployment.

Getting null reference exception when try to open list settings (Listedit.aspx) for Sharepoint 2010

This is a typical  error scenario that you have if you move or copy some sharepoint core dll’s which is referenced fr0m another folder.There is common mistake when developing custom applications that moving and copying sharepoint core dll’s to where is not in original folder (for example coping Microsoft.Sharepoint.dll to BIN folder of web application)  or unnecesserly register in GAC (for example registering Microsoft.Sharepoint.ApplicationPages.dll to GAC).

Why is this dangerous ? i can simply say when you upgrade or install any patch on sharepoint , the copied dlls form original location  can not be upgraded  by default .So this cause a huge anomality in your system. DLL version integrity is very important issue for every Microsoft product. So be careful for this operations , after all if you still need to copy or move operation, that upgrading copied dlls is your responsibility.

So lets keep going with our error :

when you click the one of list’s list settings (listedit.aspx) you can probably getting fallowing error in application event logs:

Exception information:
Exception type: NullReferenceException
Exception message: Object reference not set to an instance of an object.

Stack trace:    at Microsoft.SharePoint.ApplicationPages.ListEditPage.SetSectionLinks()
at Microsoft.SharePoint.ApplicationPages.ListEditPage.OnLoad(EventArgs e)
at ASP._layouts_listedit_aspx.OnLoad(EventArgs e) in c:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\listedit.aspx:line 25
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

And if you enable custom errors from  in your web.config which is in Layouts folder.You can see the error in browser:

NullReferenceException: Object reference not set to an instance of an object.]
Microsoft.SharePoint.ApplicationPages.ListEditPage.SetSectionLinks() +426
Microsoft.SharePoint.ApplicationPages.ListEditPage.OnLoad(EventArgs e) +896
ASP._layouts_listedit_aspx.OnLoad(EventArgs e) +290
System.Web.UI.Control.LoadRecursive() +65
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2427

For resolving this issue you should find the related copied assembly files which according this scenario is Microsoft.Sharepoint.ApplicationPages.dll in use for unfamiliar locations.

the orginal location for this dll is  Drive:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\CONFIG\BIN

1) First Check the GAC this dll should not be added to GAC if it is upgrade latest correct version or delete the dll.
2) Check other possible existance may it be used in BIN folders of web applications. For getting all list of any dll location you can use this script: this powershell command gives you a list that defined dll locations and versions.

For best practise if you need any reference to Sharepoint DLL’s use and reference them in original locations .if not you always remember to upgrade latest versions where it have been that is your resposibility.

See you next time.







Does Sharepoint 2010 support classic-mode for application pool ?

The Answer is No.
If you set your sharepoint web application’s application pool mode as classic mode you get the fallowing error :
“This operation requires IIS integrated pipeline mode.”

For more information about Sharepoint 2010 Architecture please read fallowing article :

For more information about Integrated-Pipeline:

TroubleShooting with Sharepoint 2010 Diagnostic Log Compression (DLC) v1.0

Here is the checklist.

1)      Check all WFE and APP servers gac folder that the assambly file of DLC named “DiagnosticLogCompression.dll” has registered.

2)      Check Sharepoint Timer Job Service is running and has correct account on every WFE and APP Servers.

3)      Check Diagnostic Log Compression feature is installed and enabled on Sharepoint Central Administration Application

CA-> Site Settings -> Manage Site Features  and check Diagnostic Log Compression Feature is activated.

4)      Check Sharepoint Timer Job Service Identity has enough rights to read/write to destination folder for log copy/move operations.

5)      If you are using UNC path check from every WFE and APP server havent any connection problem to reach defined UNC.

6)      Monitor from ULS Log via ULS viewer that Compression job is running as expected.

You can download ULS Viewer from this link :

Job Starting Message:

DLC -> Job:  Message:Job Starting

Directory Check Message:
DLC -> Job:  Message:Directory is OK! :   \\YOUR_NETWORK_PATH

The Log file is inuse message:

DLC -> Job:  Message:File in usage:C:\Program Files\Common Files\Microsoft Shared\Web Server

Several processed log file message:

DLC -> Job:  Message:Processing -> C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\LOGS\POSTPOINT2010-20110606-1744.log

Finish message:

DLC -> Job:  Message:Job completed successfully

7)      If you do any update or manuel installation don’t forget to reset Sharepoint Timer job Service on updated server. For example getting Error of in ULS Log:

06/10/2011 19:36:03.45               OWSTIMER.EXE (0x21BC)             0x2018  SharePoint Foundation               Topology            umbo    High       The type DiagnosticLogsHelper.JobLogCompress, DiagnosticLogCompression, Version=, Culture=neutral, PublicKeyToken=c1b6bc305019fff6 could not be found in its specified assembly.  Scanning all assemblies that have been loaded in the current app domain.      

end of list.

Configuration of Sharepoint 2010 Diagnostic Log Compression (DLC) v1.0

Server Selection

You can select by checking  required servers from checklist for operations.DLC creates one timer job per selected server.

Move Folder

Define a network path or local path for move/copy your log files.You have three options

  • No Action : Log files can not copy/or move to another path but if compression is active the file which has “log” extention has to be compressed in default sharepoint log folder.
  • Copy Action: If this option is selected than the Compressed Log files or Log files are copied to defined path.
  • Move Action : if this option is selected than the compressed log files or log files moved to the defined path.

IMPORTANT :  Don’t forget to check your folder permissions that Sharepoint timer service account on each server has read/write rights to this folder for prevent an unnecessary access denied error.

Log Options

Compress Log Files: If this check is selected every file in default sharepoint log path will be compressed. And the orginal log file will be deleted. If move action is set to copy or move option this compressed file be copied or be moved defined path.

IMPORTANT: By default original log files will be deleted in compression mode.

Don’t Delete Orginal Log Files : If this check is selected even if compression option is selected original log files will not be deleted. Compressed file will be created in same folder. Even if move action is set copy or move option orginal log files can not be copied or moved to defined path.

Timer Job Schedule: when dlc configuration is completed it creates a timer job for each selected server. ( If timer job already exists it updates)  You  can configure for a schedule these timer jobs according to your envoriment.
there are tree option for timer job avaible Daily,WeeklyMonthly.

Quick FAQ : What happen if i set compression mode to false than set move option and  “Don’t Delete Orginal Log Files” option true ? Answer is simple : instead of moving it just copies orginal log files not delete them.

Quick FAQ: Could i set different schedules to specific servers ? Answer: no.