Zieglers

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

SharePoint 2010 – one million items in a list

Posted by zieglers on November 21, 2009

Just like most people, i downloaded and installed SharePoint 2010 last week. Unlike mentioned, I had no issues while installing on Windows 2008 R2 environment. It’s been a week and still no issues.

Looks like it’s going to take a while for me to get used to new ribbon interface. Definitely you get to see and do more with ribbon, but will take a some time to remember what is where.

Actually, main focus of this post is a lot mentioned new feature of SharePoint 2010. ”Limitation on number of items stored in a list”  problem seems to be solved – Now, SharePoint lists can handle millions of items.

In order to see this, I decided to do a little experiment. My aim is firstly to insert one million items in a list successfully. As I write this post items are still being inserted, 731371 and still counting… So far it’s been 6 hours and still inserting.

I have to also mention that my experiment environment is done on a  Win 2008 R2 with 2 GB Ram and using an external usb hdd. So this waiting time can be way less if you try with a powerful workstation. Anyhow, how do I insert 1 million items to a list? Of course using PowerShell script..

Here is a very simple PowerShell script which inserts items to a list. This script is just assigning ‘Title’ column values: Item – ItemNo

========================================================================================

// Set site url
$siteurl = “http://yourservername”

// get site obj
$mysite=new-object Microsoft.SharePoint.SPSite($siteurl)

// open root web
$spWeb = $mysite.OpenWeb()
 
// get test list obj
$spList = $spWeb.Lists["TestList"]

// Insert 1 million items
$i = 1001

do { Write-Host “Item – “$i
         $spitem = $spList.Items.Add() 
         $spitem["Title"] = $i.tostring()
         $spitem.Update()
         $i++
}
while ($i -le 1000000)

========================================================================================

Will update this post once insert operation is completed…

(next morning)

Ok, now i see insert operation is completed. As you can see below, one million list items are inserted into a list using PowerShell.

At this point I’d like to do some test re item select/update/delete operations. Also wondering how filtering is going to work in such a large list. If you have suggestions for other tests or things to check, please let me know, i’ll try to apply them as well. At the end, I’d like to have a some sort of comfort level while suggesting a client ‘one million item is ok in a SharePoint 2010 list’.

… to be continued…

zieglers

Posted in IT Stuff, MOSS & WSS 3.0 | Tagged: , , , | Leave a Comment »

Reading QueryString paramater values using JavaScript in SharePoint

Posted by zieglers on November 13, 2009

Here is a nice javascript snippet which allows you to read querystring parameter values. Actually i used this code in a SharePoint application page to read ListId and ItemId values that i was passing from previous page.

// ************************************************************
// Create an array to keep query string variables
var qsParm = new Array();
// This function gets query string values from current page
// and stores them in above defined array
function qs()
    {
            var query = window.location.search.substring(1);
            var parms = query.split(‘&’);
           
            for (var i=0; i<parms.length; i++) {
                var pos = parms[i].indexOf(‘=’);
                if (pos > 0)
                {
                    var key = parms[i].substring(0,pos);
                    var val = parms[i].substring(pos+1);
                    qsParm[key] = val;
                }
            }
    }

qsParm['ListId'] = null;
qsParm['ItemId'] = null;
// Call function – get query strings
qs();
// For example show ListId
alert(qsParm['ListId'])
// ************************************************************

zieglers

Posted in IT Stuff, MOSS & WSS 3.0 | Tagged: , , , , | Leave a Comment »

SAP IDES ERP 6.0 EHP4 / NetWeaver 7.01 Installation Guide on Windows 2003 64-bit – Part 2

Posted by zieglers on November 12, 2009

In this 2nd part of the article, I’ll mention installation environment details. Although SAP doesn’t recommend installation on laptops, I did my test installation on a virtual machine. As host machine I used Windows 7 Ultimate 64bit and VMware Workstation. Here are guest 64bit operation system details:

=========================================================================================

Installation Guest Machine details

* Windows Server 2003 R2 – Standard x64 Edition

19

* MS SQL Server 2008 x64 with collation CP850_BIN2 (this is very important. you can only install SAP with this collation)

23

* Java JDK 1.4.2_17 windows-amd 64bit. If you have problems finding a 64bit version for Java 1.4.2_? SDK, here is the link: http://java.sun.com/j2se/1.4.2/SAPsite/download.html

Java SDK versions provided in above link are specific builds for SAP customers. For my installation, I used following version.

20

Also, you should download Java Cryptography Extension 1.4.2 since SAP Installer will ask for it during installation.

==========================================================================================

Starting Installation Process

If you have downloaded all necessary installation media as described in part 1(http://zieglers.wordpress.com/2009/11/10/sap-ides-erp-6-0-ehp4-netweaver-7-01-installation-guide/) and prepared your installation guest machine as described above, now we can start installing SAP IDES ERP 6.0.

… to be continued…

zieglers

Posted in IT Stuff, SAP | Tagged: , , , , , , , | Leave a Comment »

SAP IDES ERP 6.0 EHP4 / NetWeaver 7.01 Installation Guide on Windows 2003 64-bit – Part 1

Posted by zieglers on November 10, 2009

ECC Chart

Recently i was checking the posts I wrote about SAP IDES installation, and realized it’s been a while since I wrote the last one: http://zieglers.wordpress.com/2007/08/26/sap-r3-ides-47200-sr1-installation-on-windows-2003-r3-and-sql-server-2005/

Well, I think a brand new one is almost necessary: Here I give u, SAP IDES ECC 6.0 Installation on Windows 2003 with SQL Server 2008. I’ll try to explain the process with screenshots and try to keep it really simple. Hope it works!

=========================================================================================================

Step – 1: Download installation DVDs / files from SAP Service Marketplace:

Navigate to installation media

https://websmp101.sap-ag.de/~SAPIDP/002006825000000234912001E

Once you are at SAP Service Marketplace homepage, first click ‘Downloads‘ in first line navigation and then ‘SAP Installations & Upgrades‘.

1-) 1  2-) 2  

On the left navigation, select ‘Installations and Upgrades‘, then ‘SAP Application Components‘.

3-) 3   4-)4

Then, select ‘SAP ERP‘, ‘SAP ERP Enhance Package‘ and ‘EHP for SAP ERP 6.0 / NW 7.01‘ respectively, as shown below. 

5-) 5 6-) 6 7-) 7

Since we’ll be installing IDES version, select ‘IDES-Version‘, and then ‘Windows Server – SQL Server‘.

8-) 8 9-) 9

Download media for Export, SAP Installer, Java Component, UC Kernel and SAP GUI

Installation media necessary for SAP ERP 6.0 can be categorized in 5 main titles. This means once you start installing SAP ERP 6.0, SAP installer will ask you installation DVDs for 3 parts. Those are 1. Export DVDs, 2. Java Component, 3.Kernel. Additionally, you’ll need SAP Installer media itself and also SAP GUI. Now, let’s take a look which ones to download.

Export DVDs: You need to download 27 export parts named ‘IDES ERP 6.04/NW 7.01 Inst. Export‘. Once you extract those, there will be 4 DVDs, as shown below. Normal SAP ERP 6.0 installer comes with 2 export DVDs. Extra 2 DVDs result from the fact that IDES version includes many sample companies and related data to be installed.

10   11

Note from installation experience: Once installer finishes import jobs, you’ll have almost 160 GB sized database files and 10GB log file in total. No kiddin’! :) (See below)

12     13

Java Component: You need to download 4 parts for ‘NW 7.0 EHP1 SR1 Java based SW Components‘ as shown below. This will be only 1 DVD. Depending on your installation choice – ABAP vs. JAVA based – you might not need those during installation.

14

Also you need 3 parts BS Java components, ‘Java Comp. 7.0 SR1: ERP,CRM,SRM,SCM‘, as shown below.

15

SAP GUI 7.10: You need to download 2 parts for ‘NW 7.0 Presentation – 7.10 Compilation 4 Present.‘, as shown below.

16

UC Kernel: You need to download 1 part for ‘BS 7 SR1 UC-Kernel 7.01 Windows Server on x64 64bit‘, as shown below.

17

SAP Installation Master: You need to download 3 parts for ‘IDES Inst. Mst. 7.01: ERP, CRM, SCM‘, as shown below. This is basically our SAP NetWeaver Software Installer.

18

=========================================================================================================

This completes 1st part of this article – SAP IDES ERP 6.0 Installation – Gathering Installation Media. We’ll continue with installation environment in 2nd part of the article and start the installation.

If you didn’t recieve your installation media as DVDs, above mentioned approach was an alternate way to get your installation media. Hope this helps and let me know if you have questions.

zieglers (runs SAP) :)

Posted in IT Stuff, SAP | Tagged: , , , , , , , | 1 Comment »

SharePoint Alerts Export Import add-in Part – 3

Posted by zieglers on November 9, 2009

Alerts

Here is the 3rd part of the article – SharePoint Alerts Export Import add-in. I realized that it’s been a while that i wrote first and second articles. Taking a quick look at those, i see that “importing alerts” logic is left for this article.

Importing alerts can be done for a selected site collection and also individual subsites can also be selected explicitly. Alerts export logic was exporting all alerts of a site collection, not only root site, but also all subsites. Also those alert details were stored in an xml file in a structured manner. However, for import operation we need a selective import logic. Not all the time we might need to import alerts for a whole site collection. That’s why logic will be a little more complicated than exporting alerts.  

Moreover, an import operation can take some time based on number of alerts being imported and number of subsites exist. That’s why we need to have an SP Long operation so that user knows operation is being executed. Here there is a nice thing to note, that is, if user somehow closes the browser, import operation is not interrupted and goes on in the background until finished.Another important thing is the difference between type of alerts:  sp list alerts and search alerts. Although both of them are using SPAlert class, based on attributes being set a generic list alert can become a search alert. (p_query is the most important attribute for a search alert, basically which holds the query of the search alert)Here is a look how our Alerts Import application page ui is going to be like.   

1

After this intro, it’s time to get started.  Firstly let’s take a look at UI design. 

Alerts Import Page – UI Design

As for site collection selector seen above in first subsection of UI, we’ll use OOTB SharePoint SiteAdministrationSelector control. This control is frequently used in many application pages of Central Admin. Then, in order to list all subsites (in subsection 2) after a site collection is selected, we’ll have a CheckBoxList control. Finally, there is going to be a FileUpload control to select import file – the file which is exported using Alerts Export page. In addition, there is a “Verify” button to validate selected import file.  

SiteAdministrationSelector control 

<!-- *********************************************************
DISPLAY THE SITE COLLECTION SELECTOR USING THE InputFormSecton AND SiteAdministrationSelector CONTROLS. -->
<wssuc:InputFormSection runat="server"
   Title="<%$Resources:spadmin, multipages_sitecollection_title%>"
   Description="<%$Resources:spadmin, multipages_sitecollection_desc%>" >
   <Template_InputFormControls>
      <tr><td>
         <!-- Put your control here-->

<SharePoint:SiteAdministrationSelector id="SiteCollectionSelector" runat="server" OnContextChange="OnContextChange" /></td></tr></Template_InputFormControls> </wssuc:InputFormSection><!-- ********************************************************** -->   

Subsites CheckBoxList 
 
<!-- **********************************************************
DISPLAY ALL SUBSITES USING THE InputFormSecton AND ASP:CheckBoxList CONTROLS -->
<wssuc:InputFormSection runat="server"
   Title="Web Sites"
   Description="Check web sites for which you want to import alerts" >

   <Template_InputFormControls>
   <tr><td>
      <asp:CheckBoxList ID="cbWebs" runat="server" CssClass="ms-listheaderlabel" EnableViewState="true"   />
   </td></tr>        
   </Template_InputFormControls>
</wssuc:InputFormSection>
<!-- *********************************************************** -->

FileUpload control for Alerts Import file and Verify Button
  
<!-- File name section -->
<wssuc:InputFormSection
    Id="SelectFileSection"
    Title="Select Alerts File"
    Description="Browse to the alerts file you intend to import."
    runat="server">
    <Template_InputFormControls>

        <wssuc:InputFormControl runat="server" LabelText="Name :">
         <Template_Control>
         <span dir="ltr"> 
          <asp:FileUpload ID="FileUpload1" runat="server" />
         </span>
         </Template_Control>
        </wssuc:InputFormControl>

        <wssuc:InputFormControl LabelText="Before importing, you can check this alerts import file for detailed errors, warnings, and other information." runat="server">
         <Template_Control>
          <asp:button class="ms-ButtonHeightWidth" runat="server"
           text="Verify"
           OnClick="ButtonVerifyClick"
           id="ButtonVerify">
       </asp:button>
         </Template_Control>
        </wssuc:InputFormControl>

        <wssuc:InputFormControl runat="server">
         <Template_Control>
          <div><asp:Label id="lblFileValidation" runat="server"/></div>
         </Template_Control>
        </wssuc:InputFormControl> 
    </Template_InputFormControls>
</wssuc:InputFormSection>

Alerts Import Page – Import Logic Design

So far, we have our UI elements ready. Now, let’s take a look at application logic. Almost all page logic is executed after user clicks ok button. Here is BtnSubmit_Click logic:

Basic validations

  • Check if at least one web selected or not –> Display validation message if no web selected
  • Check uploaded file –>Restrict the user to upload only .xml file

Preparations

  • Assign import log file name and path
  • Get url, port, virtual path details

Import Operation

  • Read Alerts xml file for import operation
  • Initialize date and alert count
  • Iterate through each row of ‘Alerts’. This is also equivalent to looping through each web object.
    • Only import alerts for selected webs
    • Get alert property values from xml
    • Check Alert Template Type. This determines if an alert is a Search alert, List Alert, Item Alert …etc.
    • Create alerts

Alerts Import Page – Implementation

Basic validations

Before starting to import alerts, first we need to check alerts import file. Here we get some info about file using FileUpload control and only allow .xml files.

// Check uploaded file
if (FileUpload1.HasFile)
{
    // get the full path of your computer
    string strFileNameWithPath = FileUpload1.PostedFile.FileName;
    // get the extension name of the file
    string strExtensionName = System.IO.Path.GetExtension(strFileNameWithPath);
    // get the filename of user file
    string strFileName = System.IO.Path.GetFileName(strFileNameWithPath);
    // get the file size
    int intFileSize = FileUpload1.PostedFile.ContentLength / 1024; // convert into byte

    // Restrict the user to upload only .xml file
    strExtensionName = strExtensionName.ToLower();
    if (strExtensionName.Equals(“.xml”))
    {
        // upload the file on the server
        FileUpload1.PostedFile.SaveAs(AlertsDir + strFileName);

        strMessages += “Uploaded file details <hr />” +
            “File path on your Computer: ” + strFileNameWithPath + “<br />” +
            “File Name: ” + strFileName + “<br />” +
            “File Extension Name: ” + strExtensionName + “<br />” +
            “File Size: ” + intFileSize.ToString();
       
        // Assign Alerts File Path
        alertsFile = AlertsDir + strFileName;
    }
    else
        strMessages += “<br />Only <b>.xml</b> file is allowed, try again!<br />”;
}
else
    strMessages += “<br /><b>Select Alerts File: </b>You must select a valid alerts import file.<br />”;

Preparations

You can choose an import log file naming convention based on your needs. Here I used DateTime stamp.

// Assign import log file name and path

string logfile = logDir + “ImportLog_” + DateTime.Today.Day.ToString() + DateTime.Today.Month + DateTime.Today.Year.ToString() + “.txt”;

Import Operation

  • Read Alerts xml file for import operation

// Read Alerts xml file for import operation
System.Data.DataSet ds = new System.Data.DataSet();

// Read selected alerts import xml file
ds.ReadXml(alertsFile);
ds.Locale = System.Globalization.CultureInfo.CurrentUICulture;

// Read xml document
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
doc.Load(alertsFile);

// Keep a list of selected webs in an arraylist
ArrayList selectedWebsList = new ArrayList();
foreach (ListItem item in cbWebs.Items)
    if (item.Selected)
        selectedWebsList.Add(item.Text);

  • Initialize date and alert count and check datatable loaded by xml file.

// Initialize date and alert count
long AlertCount = 0;
DateTime start = DateTime.Now;
DateTime finish = DateTime.Now;

// Check if xml file has tables for alerts data
if (ds.Tables.Count < 2)
{
    LogMessageToFile(logDir + “ErrorLog.txt”, “Not a valid Alerts xml file”);
    strMessages += “<br /><font color=red><b>File Error: </b></font>Not a valid Alerts xml file! <br />”;
}

  • Iterate through each row of ‘Alerts’. This is also equivalent to looping through each web object.
    • Only import alerts for selected webs
    • Get alert property values from xml
    • Check Alert Template Type. This determines if an alert is a Search alert, List Alert, Item Alert …etc.
    • Create alerts

zieglers

Posted in IT Stuff, MOSS & WSS 3.0 | Tagged: , , , , , | Leave a Comment »

Assigning Item Level Priviliges to SPListItem

Posted by zieglers on October 6, 2009

SPPermissions

Sometimes you might wanna assign item level permissions to a list item instead of using Elevated Priviliges block throughout the whole code. This might happen in case you need to temporarily give a user higher priviliges than she has.

Typical example would be to provide a site reader exceptional functionality on a specific list item. In this case you break role inheritance and give the permissions you want. Here is a little snippet for that:

======================================================================

RunWithElevatedPriv

 …using SPSite…
    …using SPWeb…

// Get current user obj
SPUser user = SPContext.Current.Web.CurrentUser;
// Get current user token
SPUserToken token = user.UserToken;
// Get current user principal
SPPrincipal principal = (SPPrincipal)SPContext.Current.Web.CurrentUser;
// Get current user role assignment
SPRoleAssignment RoleAssignment = new SPRoleAssignment(principal);
// Get Your Custom Role definition
int SiteContributorId = -1;
SPRoleDefinition RoleDefSiteContributor = new SPRoleDefinition();

foreach (SPRoleDefinition roleDef in web.RoleDefinitions)
   // Find Your Custom Role Definition id
   if (roleDef.Name == “Your Custom Role Name”)
      SiteContributorId = roleDef.Id;

if (SiteContributorId != -1) {
   RoleDefSiteContributor = web.RoleDefinitions.GetById(SiteContributorId);
   // Add Your Custom Role to role assignments
   RoleAssignment.RoleDefinitionBindings.Add(RoleDefSiteContributor);
}

if (!impersonatedListItem.HasUniqueRoleAssignments)
   // Break role inheritance
   impersonatedListItem.BreakRoleInheritance(true);

// Add Your Custom Role assignment to list item
impersonatedListItem.RoleAssignments.Add(RoleAssignment);

 // Update changes
impersonatedListItem.Update(); 

   …close using SPWeb…
…close using SPSite…

======================================================================

zieglers

Posted in IT Stuff, MOSS & WSS 3.0 | Tagged: , , , , , | Leave a Comment »

Document Library Search Web Part (Optional)

Posted by zieglers on September 13, 2009

This post contains the optional part of Document Library Search Web Part post: http://zieglers.wordpress.com/2009/09/08/document-library-search-web-part/

In this post, I’ll try to show,

  • How to bind a SharePoint document library to ListName instead of ListId,
  • How to modify SPDataSource, so that subsite document libraries can be searched recursively,
  • How to extend search scope to other columns by adding more parameters.

Binding a SharePoint document library to ListName instead of ListId

In order to do this, you can simply follow this post: http://blog.solanite.com/keith/Lists/Posts/Post.aspx?ID=3

Idea is very simple: Search web part definition for ListID, replace it ListName. Then look for GUID and replace it with the list name you want to bind to.

Replace ListID with ListName

Replace ListID

Replace GUID with list name you want (For our demo, list name was Documents in the previous post)

List GUID

Replace ListName

Note: In some cases, there might be brackets around GUID value. If this is the case, make sure your list name is not in brackets after you perform replace operation.

That’s it. Now, your data view web part is bound to ListName instead of ListID.

Modifying SPDataSource so that subsite document libraries can be searched recursively

to be continued.

zieglers

Posted in IT Stuff, MOSS & WSS 3.0 | Tagged: , , , , , | Leave a Comment »

Document Library Search Web Part

Posted by zieglers on September 8, 2009

In this post, I’ll try to show how to develop a web part which you can use to search document library contents by their field values. The advantage of this web part is that results will be displayed as you type in search box. Since AJAX configuration for SharePoint is not straigh-forward, I chose to use some simple JavaScript functions instead.

By the end of the implementation, we are expecting to see a screen as follows:

Search Documents in Library

‘Add’ button is just an optional button in case you’d like to add searched documents into a gridview or something for further processing.

My next post will actually combine Search Document Library wp with Select and Stitch functionality, using which you can;

  1. Search Documents in one or more libraries (same type)
  2. Select documents from those libraries
  3. Stitch selected documents and convert to single PDF file.
  4. Send stitched pdf file to Records Center.

Enough intro so far, let’s get started!

Document Library Search  web part functionality can be divided into two parts: Just-in-Time Search functionality and Display Results functionality.

Although in the screenshot above it’s not obvious, actually results are displayed in an IFrame, whose source is our Results.aspx page. In order to give JIT effect, we’ll use javascript which will force Results.aspx in IFrame to display results each time user types something. This javascript along with search box can be hosted in a content editor web part.

For displaying results we’ll use filtered dataview webpart. This is going to be parameter based filtering. Each time user types something, that search criteria is going to be new filter parameter for dataview wp data source.

So, we can summarize use case as follows:

  • User types in search criteria.
  • JavaScript code calls results page with new filter parameter.
  • Results page displays filtered results.

CEWP for Search Box, JavaScipt and IFrame

One of my previous posts can give you an idea re this part of the implementation. I’m providing link here just in case (subsection 10 & 11): http://zieglers.wordpress.com/search-contacts-web-part/

Idea is the same. Now add a new CEWP to any page you want and provide following source:

<div style=”height:400px;”>
<script type=”text/javascript”>
 var timeout = -1;
 function searchChange(textbox) {
  var text = textbox.value;
  if (timeout != -1) clearTimeout(timeout);
  timeout = setTimeout(“commitSearch(‘” + text + “‘)”, 400);
 }
 
 function commitSearch(text) {
  var frame = document.getElementById(“contactFrame”);
                if (text == “”) frame.src = “Results.aspx?ReturnAll=Yes”;
                else frame.src = “Results.aspx?s=” + text;
 }
</script>
<div>Use the text field below to search library contents by Title.</div>
 <input type=”text” style=”border:1px gray solid;” id=”contactSearch” name=”contactSearch” onkeyup=”searchChange(this);”/><br/>
 <iframe id=”contactFrame” style=”width:100%; height: 100%; margin-bottom:-30px; padding-bottom:30px;” src=”Results.aspx?ReturnAll=Yes” scrolling=”auto” frameborder=”no” >
 </iframe>
</div>

 After doing so, you should be seeing smth as below:

Search CEWP

So far so good… Now we have CEWP to host search box and results iframe. Let’s move on to 2nd part of the implementation, which is filtered dataview web part hosted in Results.aspx and displayed in iframe.

Results page implementation

As I also mentioned above, since this part is similar to the implementation I posted for search contacts web part (subsection 3 to 9): http://zieglers.wordpress.com/search-contacts-web-part/ , you can take a look at it to have a general idea.

However, for this implementation we need more flexibility, such as binding dataview wp to ListName instead of ListId. First we create an empty aspx file under root web.

Create new aspx in SharePoint Designer

Next, we open up Data Source Library and drag&drop the document library which you want to perform search against. (In design mode of Untitled_1.aspx). I use Documents library for this demo.

Drag drop Documents Library

Then, edit columns using Common Data View Tasks and add columns you want to be displayed in search results. I only have name, title, and modified columns for this demonstration.

Edit Columns

Then, set display text if no matching items are found.

Set Display Text

Finally, save you aspx file as Results.aspx.

Ok, so far let’s see what we have: Results.aspx page which is actually hosting a dataview web part for Documents library. No filter applied yet! So, it displays every document in Documents library now.

All Documents Displayed

Our next step would be modifying this data view so that it displays results based on a filter parameter passed by user’s search box entry. Also we need to bind it to ListName instead of ListId, so that you don’t need to update ListId in Results.aspx each and every time you create a new document library.

Create filter parameter

In order to add a parameter for filtering, go to Common Data View Tasks > Parameter, and create a new parameter (Name: SearchParam, Parameter Source: QueryString, Query String Variable: s)

 SNAG-0009

Create filter for DataFormWebPart

Now we need to create filter based on parameter we just created. As for this demo, i’ll show how to create filter for Title column. In case you need, you can add more filters in a similar way. Again go to Common Data View Tasks > Filter:

Add a new filter criteria with following values:

Field Name: Title

Comparison: Contains

Value: [SearchParam]

Filter Title

After doing so, you can see applied filter in Common Data View Tasks pane. Also, don’t forget to save your changes to Results.aspx.

Filter Set

That’s it!!! Now let’s go back to our CEWP and test it. I uploaded a test file, named TestDoc.doc and its title is My Test Document. As seen below, as i type ‘my’ in search box, my test document is displayed in search results.

One thing to note is that we used ‘Contains’ in our filter criteria clause. So, our search is more flexible. You can search based on any occurence of a search text.

Test Results

Give it a try and you’ll see that it’s very easy to implement, yet it has big potential of usage in different scenarios. In the next post, i’ll try to show how to bind this to a repeater control and have a document selection list based on search results.

zieglers

Posted in IT Stuff, MOSS & WSS 3.0 | Tagged: , , , , , , , | 1 Comment »

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=12.0.0.0, 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=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c” %>
<%@ Register Tagprefix=”Utilities” Namespace=”Microsoft.SharePoint.Utilities” Assembly=”Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c” %>
<%@ Register TagPrefix=”wssawc” Namespace=”Microsoft.SharePoint.WebControls” Assembly=”Microsoft.SharePoint, Version=12.0.0.0, 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>
<asp:Content ID=”PageTitleInTitleArea” runat=”server” ContentPlaceHolderID=”PlaceHolderPageTitleInTitleArea”>
    Test Page Title – BBB
</asp:Content>

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” />
</asp:Content>

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”)
            SPUtility.ValidateFormDigest();
        //****************************************
        // Validate the page request to avoid
        // any malicious posts
        if (Request.HttpMethod == “POST”)
            SPUtility.ValidateFormDigest();
        //****************************************
        // Initialize the controls on the page
        // if its not a PostBack request
        if (!(Page.IsPostBack))
        {
            litMessages.Text = “Hello World!”;
        } // end – IsPostPack
    }
   
</script>

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%”>
        <tr>
          <td>  
         
             <!– *********************************************************
                 DISPLAY THE WEB APPLICATION SELECTOR
                 USING THE InputFormSecton AND WebApplicationSelector CONTROLS.
                 
                 THE TITLE AND DESCRIPTION ARE SPECIFIED IN THE CORRESPONDING
                 ATTRIBUTES OF THE InputFormSection CONTROL, WHILE THE CONTROLS
                 THEMSELVES ARE PLACED INSIDE THE InputFormControl SECTION –>
            <wssuc:InputFormSection runat=”server”
          Title=”Web Application”
          Description=”Select a Web Application” >
                <Template_InputFormControls>
                <tr>
              <td>
                    <SharePoint:WebApplicationSelector id=”Selector” runat=”server”
                      TypeLabelCssClass=”ms-listheaderlabel”
                      AllowAdministrationWebApplication=”true” />
                    </td>
          </tr>
                </Template_InputFormControls>
         </wssuc:InputFormSection>        
            <!– ********************************************************** –>
           
         <!– ****************************
              OK AND CANCEL BUTTON SECTION –>
         <wssuc:ButtonSection runat=”server” TopButtons=”false” BottomSpacing=”5″ ShowSectionLine=”true”>
          <Template_Buttons>
           <asp:Button UseSubmitBehavior=”false” runat=”server” OnClick=”BtnSubmit_Click” Text=”<%$Resources:wss,multipages_okbutton_text%>” id=”BtnSubmitBottom” accesskey=”<%$Resources:wss,okbutton_accesskey%>” Enabled=”true”/>
          </Template_Buttons>
         </wssuc:ButtonSection>
         <!– **************************** –>
        
       </td>
     </tr>
        </table>
    </asp:Panel>

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.

 Alerts-4

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;
       
        try
        {
            // 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() +
                                    “.xml”;
               
            // 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”);
                        AlertsNode.Attributes.Append(NewAttribute);
                        NewAttribute.InnerText = Convert.ToString(web.Url);
                        // Add IsRootWeb attribute
                        NewAttribute = AlertsNode.OwnerDocument.CreateAttribute(“IsRootWeb”);
                        AlertsNode.Attributes.Append(NewAttribute);
                        // 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”;
                                else
                                    NewAttribute.InnerText = “false”;
                                foreach (SPAlert oAlert in collAlerts)
                                {
                                    // Get alert properties
                                    StringValueOfAlerts(AlertsNode, oAlert);
                                    AlertCount++;
                                } // 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
            Document.Save(AlertsFile);
            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..

zieglers

Posted in IT Stuff, MOSS & WSS 3.0 | Tagged: , , , , , , , , | Leave a Comment »