Just little about C#, .NET, SQL Server, SharePoint and SAP

SharePoint Alerts Export Import add-in Part – 2

Posted by zieglers on June 5, 2009

In this second part of the article, I’ll mention implementation details of Alerts Export/Import add-in.

Most important decision I took as for implementation was not to use code-behind. Simply I coded everything within .aspx page. Why did I do so? Well, for couple of reasons.

Firstly, this allows faster development by avoiding building VS solution and then deploying the solution. A simple page refresh will force the page recompile. If you are developing just couple of application pages, I’d say don’t waste time by building a solution and deploying it each time code-behind changes. You’ll see coding in .aspx page is much faster. Make sure you know C# syntax well 😉

Secondly, no deployment at all. Since you are directly working on page in 12 hive ADMIN folder, once you save your changes, they are already there and page will be recompiled next time you request the page.

Of course, taking this development approach or not is totally up to you. But if you haven’t tried yet, I’d say give it a try and you’ll see that your C# syntax skills will improve drastically.

Ok, Let’s get started!!!

1. First we need a blank ‘Hello World’ application page.

Since we are developing a blank application page for Central Administration Site, masterpage of our application page must be ‘admin.master‘. Also there are some specific SharePoint controls that will be used, so those must be registered as well.

Now, open up NotePad and create an empty text file and save it as Test.aspx.

a. Adding required assembly and SharePoint control references

Include following snippet to Test.aspx.

<%@ Assembly Name=”Microsoft.SharePoint.ApplicationPages.Administration, Version=, Culture=neutral, PublicKeyToken=71e9bce111e9429c”%>
<%@ Page Language=”C#” AutoEventWireup=”true” Inherits=”Microsoft.SharePoint.ApplicationPages.GlobalAdminPageBase” MasterPageFile=”~/_admin/admin.master” %>
<%@ Import Namespace=”System.Net” %>
<%@ Import Namespace=”Microsoft.SharePoint” %>
<%@ Import Namespace=”Microsoft.SharePoint.Administration” %>
<%@ Import Namespace=”Microsoft.SharePoint.Utilities” %>
<%@ Import Namespace=”Microsoft.SharePoint.ApplicationPages” %>
<%@ Register Tagprefix=”SharePoint” Namespace=”Microsoft.SharePoint.WebControls” Assembly=”Microsoft.SharePoint, Version=, Culture=neutral, PublicKeyToken=71e9bce111e9429c” %>
<%@ Register Tagprefix=”Utilities” Namespace=”Microsoft.SharePoint.Utilities” Assembly=”Microsoft.SharePoint, Version=, Culture=neutral, PublicKeyToken=71e9bce111e9429c” %>
<%@ Register TagPrefix=”wssawc” Namespace=”Microsoft.SharePoint.WebControls” Assembly=”Microsoft.SharePoint, Version=, Culture=neutral, PublicKeyToken=71e9bce111e9429c”%>
<%@ Register TagPrefix=”wssuc” TagName=”ToolBar” src=”~/_controltemplates/ToolBar.ascx” %>
<%@ Register TagPrefix=”wssuc” TagName=”InputFormSection” src=”~/_controltemplates/InputFormSection.ascx” %>
<%@ Register TagPrefix=”wssuc” TagName=”InputFormControl” src=”~/_controltemplates/InputFormControl.ascx” %>
<%@ Register TagPrefix=”wssuc” TagName=”ButtonSection” src=”~/_controltemplates/ButtonSection.ascx” %> 

b. Adding ContentPlaceHolders

Include PlaceHolderPageTitle and PlaceHolderPageTitleInTitleArea content placeholders. Former determines browser page title, latter determines application page title.

 <asp:Content ID=”PageTitle” runat=”server” ContentPlaceHolderID=”PlaceHolderPageTitle”>
    Test Page Title – AAA
<asp:Content ID=”PageTitleInTitleArea” runat=”server” ContentPlaceHolderID=”PlaceHolderPageTitleInTitleArea”>
    Test Page Title – BBB

c. Adding Main PlaceHolder

Include PlaceHolderMain content placeholder. This placeholder will hold all controls in the main page area. For our test page we only include an AP.NET Literal control to display HelloWorld message.

<asp:Content ID=”Main” ContentPlaceHolderID=”PlaceHolderMain” runat=”server”>
 <!– Use a ASP.NET Literal Control to display messages –>
 <asp:Literal ID=”litMessages” runat=”server” />

d. Adding code for page logic

Now we can start writing code for our test page. Our C# code will be in between script tags of the page. All we need to do is to assign ‘Hello World’ to messages literal control in PageLoad event.

<script runat=”server”>
    protected override void OnLoad(EventArgs e)
        // Validate the page request to avoid
        // any malicious posts
        if (Request.HttpMethod == “POST”)
        // Validate the page request to avoid
        // any malicious posts
        if (Request.HttpMethod == “POST”)
        // Initialize the controls on the page
        // if its not a PostBack request
        if (!(Page.IsPostBack))
            litMessages.Text = “Hello World!”;
        } // end – IsPostPack

e. Copy Test.aspx to /12/ADMIN/ folder.

That’s all for our Test application page. No deployment, no building solution.. All we need to do is to copy Test.aspx to ~/12/ADMIN/ folder. Now open your browser. Go to Central Admin site, and test your Test.aspx. (http://yourservername:centraladminportnumber/_admin/Test.aspx)

You can download Test.aspx application page here: TestASPXCode

2. Adding UI controls – SharePoint Web Application Selector

Now we have a blank application page, we can start adding UI elements one by one. It’s always a good practice to keep logically related controls in a panel. Since providing a web application is the only input for export alerts operation, we’ll have only SharePoint Web Application selector control and submit buttons on ExportAlerts page, and will keep those in an input panel as follows.

Add the following snippet in PlaceHolderMain below litMessages literal control.

<!– Use a ASP.NET Panel Control to show or hide the form from code –>
 <asp:Panel ID=”inputForm” runat=”server”>
     <table border=”0″ cellspacing=”0″ cellpadding=”0″ width=”100%”>
             <!– *********************************************************
                 USING THE InputFormSecton AND WebApplicationSelector CONTROLS.
                 THEMSELVES ARE PLACED INSIDE THE InputFormControl SECTION –>
            <wssuc:InputFormSection runat=”server”
          Title=”Web Application”
          Description=”Select a Web Application” >
                    <SharePoint:WebApplicationSelector id=”Selector” runat=”server”
                      AllowAdministrationWebApplication=”true” />
            <!– ********************************************************** –>
         <!– ****************************
         <wssuc:ButtonSection runat=”server” TopButtons=”false” BottomSpacing=”5″ ShowSectionLine=”true”>
           <asp:Button UseSubmitBehavior=”false” runat=”server” OnClick=”BtnSubmit_Click” Text=”<%$Resources:wss,multipages_okbutton_text%>” id=”BtnSubmitBottom” accesskey=”<%$Resources:wss,okbutton_accesskey%>” Enabled=”true”/>
         <!– **************************** –>

One thing to note here is that we need to provide code for ButtonClick event. So add the following to script section of the page.

    // This method is called when the user clicks the “OK” button
    // to activate the site feature package.
    protected void BtnSubmit_Click(object sender, EventArgs e)
        // Your code here

 So far so good! If you have done everything w/o any mistakes your page should look as follows.


3. Adding ‘Export Alerts’ logic.

We have our application page. We have our UI elements (web application selector and submit buttons). Now it’s time to get our hands dirty! All ‘Export Alerts’ logic will be implemented in BtnSubmit_Click event.

 Ok, at this point let’s take a look at our design details once again:

A. Crawl whole web application, which means loop through all site collections and all webs underneath.

foreach –> Site Collection in WebApplication

   foreach –> Web object in Site Collection

      foreach –> alert in Alert Collection of web

               Export alert details to xml file.

B. Xml helper functions. We need helper functions to get alert property details and write them in Alerts Export xml file in a structured way. Those functions are:

CreateXmlDocument, AddChildElement, StringValueOfObject, StringValueOfAlerts

I’m not going to mention details about those helper functions since they are out of our scope. I also didn’t spend time on them to reflect a better coding practice. As a result they are very straight-forward and represent a trivial functional coding. Be my guest if you want to refactor them and make them look better 🙂

C. Output messages

I haven’t provided logging functionality for export alerts, since exported xml file sort of acts as log itself. Only we need to display some statistics to UI such as number of exported alerts, urls of crawled site collections and webs, execution time, … etc. For this purpose we’ll have only one string variable, namely strMessages. We’ll append any sort of execution messages to this string and then eventually assign it to litMessages.

Here is the BtnSubmit_Click code:

    // This method is called when the user clicks the “OK” button
    // to export alerts of a selected web application.
    protected void BtnSubmit_Click(object sender, EventArgs e)
        //Prepare a string object to display the result
        //of the export alerts operation for each site
        string strMessages = “”;
        // Hide input panel
        inputForm.Visible = false;
            // Execution ‘start’ and ‘finish’ time variables
            DateTime start = DateTime.Now;
            DateTime finish = DateTime.Now;
            // Construct Exported Alerts Xml file name
            // Format: ddmmyyyy_hhmmss.xml
            alertsFileName = DateTime.Today.Day.ToString() +
                                    DateTime.Today.Month + DateTime.Today.Year.ToString() +
                                    “_” +
                                    DateTime.Now.Hour.ToString() +
                                    DateTime.Now.Minute.ToString() +
                                    DateTime.Now.Second.ToString() +
            // add all the alert info to an XML document
            System.Xml.XmlDocument Document = CreateXmlDocument();
            //Iterate through each of the site collections
            //in the selected web applications
            foreach (SPSite site in Selector.CurrentItem.Sites)
                //Disable the CatchAccessDeniedException
                //of the site collection to avoid being redirected
                //to the “Access Denied” page if access is denied.
                site.CatchAccessDeniedException = false;
                // Display site url
                strMessages += “<br /><b> === Exporting Alerts for site: ” + site.Url + ” === </b><br />”;
                //Iterate through each site in the site collection
                foreach (SPWeb web in site.AllWebs)
                    // Get all users of the web
                    SPUserCollection collUsers = web.SiteUsers;
                    // Check if there are any alerts to be exported for this web object
                    if (web.Alerts.Count > 0)
                        // create the alerts root node
                        System.Xml.XmlElement AlertsNode = AddChildElement(Document.DocumentElement, “Alerts”);
                        // Add Web Url attribute
                        System.Xml.XmlAttribute NewAttribute = AlertsNode.OwnerDocument.CreateAttribute(“WebUrl”);
                        NewAttribute.InnerText = Convert.ToString(web.Url);
                        // Add IsRootWeb attribute
                        NewAttribute = AlertsNode.OwnerDocument.CreateAttribute(“IsRootWeb”);
                        // Display web object info
                        strMessages += “<br /> + Web : ” + web.Url + ” + <br />”;
                        // Save Alerts to xml
                        // iterate through all the alerts for every user of a site
                        foreach (SPUser oUser in collUsers)
                            SPAlertCollection collAlerts = oUser.Alerts;
                            if (oUser.Alerts.Count > 0)
                                // Check if this web is a root web or not
                                if (site.Url == web.Url)
                                    NewAttribute.InnerText = “true”;
                                    NewAttribute.InnerText = “false”;
                                foreach (SPAlert oAlert in collAlerts)
                                    // Get alert properties
                                    StringValueOfAlerts(AlertsNode, oAlert);
                                } // end – foreach SPAlert
                            } // end -if
                        } // end – foreach SPUser
                    } // end – if AlertsCount
                } // end – foreach SPWeb
                port = site.Port;
                AlertsFile = AlertsDir + port.ToString() + “_” + alertsFileName;
                //Allow the site collection to continue handling
                //access denied exceptions
                site.CatchAccessDeniedException = true;
            } // end – foreach SPSite
            // Save Alerts Export file
            strMessages += “<br /> ————————————————————————————– <br />”;
            strMessages += “<br /><b>” + AlertCount.ToString() + “</b> alerts <b><font color=green>successfully</font></b> exported for web application: <b>” + Selector.CurrentName + “</b><br />”;
            strMessages += “<br />Export file: <b>” + port.ToString() + “_” + alertsFileName + “</b><br />”;
            finish = DateTime.Now;
            TimeSpan elapsedTime = finish.Subtract(start);
            strMessages += “<br /> Exported in <b>” + elapsedTime.Minutes.ToString() + “</b> minute(s) and <b>” + elapsedTime.Seconds.ToString() + “</b> seconds and <b>” + elapsedTime.Milliseconds.ToString() + “</b> miliseconds <br />”;
        catch (Exception AlertsSaveException)
            //if an error occurs during export alerts operation;
            //capture the message to display it to the user
            //after iterating through all the sites
            strMessages += “<br />Alerts Save Error: ” + AlertsSaveException.Message + “<br />”;
        // Display messages if there are any
        litMessages.Text = strMessages;

If you are not interested in details of ‘Export Alerts‘ and just want to use it as soon as possible, you can download “AlertsSave.aspx” from this link: AlertsSaveASPX

(Copy and paste word doc contents in a text file and rename it to AlertsSave.aspx. Then copy it to ~/12/ADMIN/ folder.)

Please let me know if you run into any difficulties while trying to implement/run ‘export alerts’ functionality..



Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: