My thoughts and findings on Microsoft Information worker technologies,
including MCMS and SharePoint 2007 (MOSS). |
|
|
25/08/2009I was planning to attend the SharePoint UK User Group on Thursday for the Best Practices in Gathering Requirements for SharePoint Projects. The event had Dux speaking at the event. Important news, Dux can't make the event so its now going to be a virtual event instead of being held at Microsoft's offices in Victoria London. Please see an update here http://suguk.org/forums/thread/20502.aspx. 22/07/2009Microsoft Ramp Up http://msdn.microsoft.com/en-us/rampup/default.aspx has been about for a while now but not everyone knows about this training. Microsoft Ramp Up offers free online community based learning to help you build up on your development skills. There are many tracks which include: - Move from ASP to ASP.NET
- Learn ASP.NET: for PHP Developers
- Web Development with ASP.NET
- Develop Windows Mobile 6 Applications
- SharePoint for Developers Part 1
- SharePoint for Developers Part 2
- Visual Studio 2008
- Developer Basics
- Java Developer: Learn .NET
- VB6 Developer: Lean VB 2005
- VS 2002/2003 Developer: Learn VS 2005
Something for everyone. The SharePoint track is available here: part 1 http://msdn.microsoft.com/en-us/rampup/dd221355.aspx and part 2 http://msdn.microsoft.com/en-us/rampup/dd320759.aspx 07/07/2009Central Administration has a screen allow users to change the application pool identity, but doesn't allow you to change the application pool which the web application uses. You can modify manually by opening IIS, changing the application pool and making changes to the metabase.xml. But how do I make these changes programmatically? Theory An IIS Web Site in SharePoint is represented by the SPWebApplication class http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.administration.spwebapplication.aspx this has a property called ApplicationPool which allows to be retrieved and set http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.administration.spwebapplication.applicationpool.aspx. Code System.Uri webAppToChangeUri = new System.Uri("http://intranet"); SPWebApplication webAppToChange = SPWebApplication.Lookup(webAppToChangeUri); webAppToChange.ApplicationPool.Name = "ALL MOSS"; webAppToChange.Update(true); webAppToChange.Provision(); Demo I ran this code inside a windows form, you can use this or a console application. The current application pool and the one I want to change it to has the same identity, so I just changed the ApplicationPool Name. If your identify is different you will need to set the Username and Password properties as well. Before running the code the intranet web application has the following properties
After I run the code, right click IIS Manager and click refresh. I open the properties of the intranet web application and check the settings have came through.
That's it. Why change the application pool? For every application pool a w3wp process will be launched and can consume large amounts of memory. You need to find a good balance of isolation (provided by having many application pools) and consumption of memory. Have a look at Joel's article here http://blogs.msdn.com/joelo/archive/2007/10/29/sharepoint-app-pool-settings.aspx 28/05/2009When you create a column on a list or site column in SharePoint through the user interface you enter the column name, type and any other properties. The name is used for the internal name and display name. The internal name is escaped to remove any special characters and spaces. The table below displays the list of characters and their escaped version. | Character | Hex Code | Escaped Version | | [space] | 20 | _X0020_ | | | | 7C | _X007C_ | | - | 2D | _X002D_ | | \ | 5C | _X005C_ | | ( | 28 | _X0028_ | | ) | 29 | _X0029_ | | ' | 27 | _X0027_ | | , | 2C | _X002C_ | | ! | 21 | _X0021_ | | % | 25 | _X0025_ | | & | 26 | _X0026_ | | ? | 3F | _X003F_ | | $ | 24 | _X0024_ | | £ | A3 | _X00A3_ | | " | 22 | _X0022_ | | < | 3C | _X003C_ | | > | 3E | _X003E_ | | = | 3D | _X003D_ | | # | 23 | _X0023_ | The following are examples of this escaping: | Name entered | Escaped Version | | Published Date | Published_X0020_Date | | Buyer's Name | Buyer_X0027_s_X0020_Name | | Zip / Postal Code | Zip_X0020__X002F__X0020_Postal_X0020_Code | Please note: The internal name has a maximum length which is calculated after the internal name has been escaped. There are a few way's to check the internal name, one of which is the through the User Interface, here's how: 1. Open a web browser. 2. If you have created a site column open the site column gallery, if you created a column on a list open the list settings. 3. Click on the column name. 4. Check the URL for the "field" querystring parameter, as you see from the screenshot below it contains the internal name. Other ways include ... using .NET You can also find out the escaped version using the System.Xml namespace and the XmlConvert.EncodeName. For example: using System.Xml; // encode the column name string escapedString = XmlConvert.EncodeName("Published Date"); // decode the escaped column name string normalString = XmlConvert.DecodeName(escapedString); which produces escapedString = Published_X0020_Date normalString = Published Date Content Types Viewer Download it here MOSS Content Types Viewer - Home More about the Content Type Viewer http://www.davehunter.co.uk/Blog/Lists/Posts/Post.aspx?ID=100. You can browse to the SharePoint environment, browse the content types and investigate the columns which are associated to the content type chosen, using the "show fields" button. Controlling the internal name You can control the internal names when you provision the site columns using a feature. You can also do this using the User Interface by creating a field without any spaces or special characters and then re-edit the column to change the display name with any characters you wish. Hope this helps 24/04/2009I've seen many posts on forums with features deploying site collection features scoped at the web level. Admittedly there is confusing factors, articles freely use the term site for a site collection and site for a sub site. I hope by the end of reading this article it will be more clear. The feature framework can deploy the following: - Masterpages
- Page Layouts
- Stylesheets
- Images
- Documents
- WebPart Pages
- WebPart Definitions
- List Definitions
- List Instances
- Content Types
- Site Columns
- Custom Actions
Features that should be scoped at the Site level (Site Collection). - Site Columns
- Content Types
- Provisioning Masterpages
- Feature Receivers
- Stapler
- WebPart Definitions (.dwp or .webpart) to WebPart Gallery
Features that should be scoped at the Web level (SPWeb) - List Instances
- List Definitions
- Custom Actions / Hide Custom Actions
- Web Part Pages
- Document, Images provisioned to libraries
- Content Type Bindings
- Event Receivers
- Feature Receivers
Features that should be scoped at the WebApplication level (Web Application) - Custom Actions / Hide Custom Actions
- Stapler
Features that should be scoped at the Farm level - Stapler
- Custom Actions / Hide Custom Actions
Examples: - Provisioning Site Columns
- Provisioning Content Types
- Provisioning List Instances
- Provisioning List Templates
- Provisioning Web Part Pages including Web Parts
- Provisioning Folders
- Provisioning Documents
- Provisioning Custom Actions and Hiding
- Provisioning MasterPages
- Provisioning Page Layouts
- Site Stapling
- Provisioning WebParts in the WebPart Gallery
- Content Type Bindings
With your typical intranet with MySites scenario you have two front-end web applications, one hosting your intranet and the other hosting your MySites. These are two separate components. MySites are a shared service, they are designed to be consumed by one or many web applications. Out of the box they aren't linked to a web application hosting your intranet.
When a user creates a MySite they may ask you "how do I get back to the intranet?". You can do this by setting the Portal Connection.
First take a look at my environment
- Browse to the MySite host settings page. In my case this is http://mysites/_layouts/settings.aspx
- Click on the "Portal Connection" link
- Enter the URL to the site you wish to connect to. In my case this is http://intranet
- Enter a descriptive name
- Click OK.
12/03/2009A while ago I wrote the article Delving into SharePoint 2007 Event Receivers its a popular article but lacks something, which I believe are some real world examples people can see in action and relate to ... Introducing the next series of articles SharePoint Event Receivers By Example. One of the out of the box libraries in SharePoint is the contact list. This holds contact information such as First Name, Last Name, Telephone, Email etc. The email column is a standard single line of text site column. I wanted to validate the email address users whilst creating contacts. Event Receivers have before or after events. You would use a before event to capture users creating contacts and updating contacts. You need to use the SPItemEventReceiver class and utilise the ItemAdding and ItemUpdating events. To check the values that the user enters during a before event you should check the properties.AfterProperties. This is a collection of name value pairs. You can retrieve a value using the internal name of a column (not the display name). For example: string emailMessage = properties.AfterProperties["Email"].ToString(); You can cancel the event using: properties.Status = SPEventReceiverStatus.CancelWithError;
properties.Cancel = true;
properties.ErrorMessage = "Please enter a valid email address";
The Status is set to cancel and display an error. properties.Cancel determines if the event should be cancelled. The default is false but once you set it to true it will cancel the event when the current execution ends. An error message is set using the ErrorMessage property. The following is displayed to the user:
Here's the code:
#region namespaces
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.SharePoint;
using System.Collections;
#endregion
namespace Dhunter.SharePoint.EventReceivers
{
public class ValidEmailEventReceiver : SPItemEventReceiver
{
#region events
/// <summary>
/// Test the email expression when the user creates a new item
/// </summary>
/// <param name="properties"></param>
public override void ItemAdding(SPItemEventProperties properties)
{
base.ItemAdding(properties);
// only perform if we have an Email column
if (properties.AfterProperties["Email"] != null)
{
// test to see if the email is valid
if (!IsValidEmailAddress(properties.AfterProperties["Email"].ToString()))
{
// email validation failed, so display an error
properties.Status = SPEventReceiverStatus.CancelWithError;
properties.Cancel = true;
properties.ErrorMessage = "Please enter a valid email address";
}
}
}
/// <summary>
/// Test the email expression when the user updates an existing item
/// </summary>
/// <param name="properties"></param>
public override void ItemUpdating(SPItemEventProperties properties)
{
base.ItemUpdating(properties);
// only perform if we have an Email column
if (properties.AfterProperties["Email"] != null)
{
// test to see if the email is valid
if (!IsValidEmailAddress(properties.AfterProperties["Email"].ToString()))
{
// email validation failed, so display an error
properties.Status = SPEventReceiverStatus.CancelWithError;
properties.Cancel = true;
properties.ErrorMessage = "Please enter a valid email address";
}
}
}
#endregion
#region private methods
/// <summary>
/// Test the email address to see if it meets the validation
/// </summary>
/// <param name="email"></param>
/// <returns></returns>
private bool IsValidEmailAddress(string email)
{
bool isValid = false;
// regular expression to test the email
string emailRegEx = @"^(([^<>()[\]\\.,;:\s@\""]+(\.[^<>()[\]\\.,;:\s@\""]+)*)| (\"".+\""))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$";
// run the test
Regex reStrict = new Regex(emailRegEx);
isValid = reStrict.IsMatch(email);
// flag to indicate if its valid
return isValid;
}
#endregion
}
}
My findings during creating this event receiver
- BeforeProperties are only available in Event Receivers bound to a document library. For more information please see http://msdn.microsoft.com/en-us/library/aa979520.aspx
- BeforeProperties and AfterProperties contain a collection of DictionaryEntry's you should use the internal name of the column. I tried this using the email address. The internal name can be found by editing a site column and looking in the url, for example: /_layouts/fldedit.aspx?field=EMail. I tried using properties.AfterProperties["EMail"] always returned null. After looking into the collection the field was actually called "Email". You can check these names using:
foreach (DictionaryEntry de in properties.AfterProperties)
{
string key = de.Key.ToString();
string value = de.Value.ToString();
}
- Cancel doesn't stop the ItemAdding or ItemUpdating unless base.ItemAdding(properties); is called at the top of the event.
- I used the SharePoint Tips Utility Pack http://www.codeplex.com/spstipsUtilityPack to bind the event receiver to the list. The tool has a event handler management section that allows you to quickly register your event receiver to a list.
Happy coding. 11/03/2009I had a requirement to create a tree view control to rollup document libraries into a single view. I chose to use the System.Web.UI.WebControls.TreeView web control. Once you define the tree view control you add nodes, for example: TreeNode childNode = new TreeNode(file.Name, "", "~/_layouts/images/" + file.IconUrl, file.ServerRelativeUrl.ToString(), "");
treeView.ChildNodes.Add(childNode);
I wanted to get all document libraries for a current web, I used the SPWeb.GetListsOfType method, this takes in a SPBaseType enumeration. For example:
currentWeb.GetListsOfType(SPBaseType.DocumentLibrary)
I wrote a recursive function which loops through all the root document libraries and loops through their folders. This looked like:
foreach(SPList list in currentWeb.GetListsOfType(SPBaseType.DocumentLibrary))
{
// build the tree
rootNode = new System.Web.UI.WebControls.TreeNode(list.Title, "", "~/_layouts/images/itdl.gif", list.RootFolder.ServerRelativeUrl.ToString(), "");
// loop down the tree
TraverseFolder(list.RootFolder, rootNode);
// add the root node to tree view
treeView.Nodes.Add(rootNode);
}
The completed webpart looks like:
Here's the code:
#region namespaces
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Web.UI.WebControls.WebParts;
using AspControls = System.Web.UI.WebControls;
using System.Web.Caching;
#endregion
namespace Dhunter.SharePoint.Controls
{
public class DocumentLibraryViewerWebPart : System.Web.UI.WebControls.WebParts.WebPart
{
#region controls
protected System.Web.UI.WebControls.TreeView treeView;
private AspControls.TreeNode rootNode;
#endregion
#region members
private string listNamesToExclude = "Site Template Gallery;Converted Forms;Form Templates; Images;Pages;Reporting Templates;Master Page Gallery;Site Collection Images;Site Collection Documents; Style Library;List Template Gallery;PublishingImages;Web Part Gallery";
private string userDefinedListNamesToExclude = "";
#endregion
#region public properties
/// <summary>
/// List Name Exclusions
/// </summary>
[WebBrowsable(true),
WebDisplayName("List Names to Exclude"),
WebDescription("Names of lists that shouldn't be shown in the viewer in this site"),
Personalizable(PersonalizationScope.Shared)]
public string UserDefinedListNamesToExclude
{
get { return userDefinedListNamesToExclude; }
set { userDefinedListNamesToExclude = value; }
}
#endregion
#region constructor
/// <summary>
/// Constructor
/// </summary>
public DocumentLibraryViewerWebPart()
{
// set the export mode
this.ExportMode = System.Web.UI.WebControls.WebParts.WebPartExportMode.All;
}
#endregion
#region utility methods
/// <summary>
/// Checks if the List should be excluded (hidden)
/// </summary>
/// <param name="listName"></param>
/// <returns></returns>
private bool ExcludeListName(string listName)
{
string[] arr = listNamesToExclude.Split(Convert.ToChar(";"));
bool isPresent = arr.Contains(listName);
if (isPresent == false)
{
string[] userDefArr = UserDefinedListNamesToExclude.Split(Convert.ToChar(";"));
isPresent = userDefArr.Contains(listName);
}
if (isPresent)
Console.WriteLine(listName);
return isPresent;
}
#endregion
#region overriden methods
/// <summary>
/// Create the child controls
/// </summary>
protected override void CreateChildControls()
{
// get the current site
SPSite currentSite = SPContext.Current.Site;
using (SPWeb currentWeb = currentSite.OpenWeb())
{
// set the tree view properties
treeView = new System.Web.UI.WebControls.TreeView();
treeView.ShowLines = true; // show lines
treeView.ExpandDepth = 0; // expand non
treeView.CssClass = "docLibViewer"; // set the class
// loop through the lists of the current web
// only show document libraries
foreach (SPList list in currentWeb.GetListsOfType(SPBaseType.DocumentLibrary))
{
// only show document libraries that haven't been excluded by the system document libraries or by the user exclusion (via the webpart properties)
if (!ExcludeListName(list.Title))
{
// build the tree
rootNode = new System.Web.UI.WebControls.TreeNode(list.Title, "", "~/_layouts/images/itdl.gif", list.RootFolder.ServerRelativeUrl.ToString(), "");
// loop down the tree
TraverseFolder(list.RootFolder, rootNode);
// add the root node to tree view
treeView.Nodes.Add(rootNode);
}
}
}
this.Controls.Add(treeView);
base.CreateChildControls();
}
/// <summary>
/// Render Contents
/// </summary>
/// <param name="writer"></param>
protected override void RenderContents(System.Web.UI.HtmlTextWriter writer)
{
// render the control
base.RenderContents(writer);
}
#endregion
#region public methods
/// <summary>
/// Loop through the folders
/// </summary>
/// <param name="folder"></param>
/// <param name="node"></param>
public void TraverseFolder(SPFolder folder, AspControls.TreeNode node)
{
// create a new node
AspControls.TreeNode newNode = new System.Web.UI.WebControls.TreeNode(folder.Name, "", "~/_layouts/images/itdl.gif", folder.ServerRelativeUrl.ToString(), "");
try
{
// don't add the forms folder
if (folder.Name != "Forms")
{
// loop through all child folders
foreach (SPFolder childFolder in folder.SubFolders)
{
// don't add the forms folder
if (childFolder.Name != "Forms")
{
// create a new node and recurse
AspControls.TreeNode trn = new System.Web.UI.WebControls.TreeNode(childFolder.Name, "", "~/_layouts/images/itdl.gif", childFolder.ServerRelativeUrl.ToString(), "");
newNode = TraverseFiles(childFolder, trn);
// add the new node to the tree
rootNode.ChildNodes.Add(newNode);
}
}
// loop through the files
foreach (SPFile childFile in folder.Files)
{
// create a new node and add to the tree
AspControls.TreeNode childNode = new System.Web.UI.WebControls.TreeNode(childFile.Name + " (" + childFile.TimeLastModified.ToShortDateString() + ")", "", "~/_layouts/images/" + childFile.IconUrl, childFile.ServerRelativeUrl.ToString(), "");
rootNode.ChildNodes.Add(childNode);
}
}
}
catch (Exception e)
{
//TODO: handle error
}
}
/// <summary>
/// Loop through the files
/// </summary>
/// <param name="fldr"></param>
/// <param name="node"></param>
/// <returns></returns>
public AspControls.TreeNode TraverseFiles(SPFolder folder, AspControls.TreeNode node)
{
try
{
// add the files contained in this folder
foreach (SPFile file in folder.Files)
{
// create a new node and add to the tree
AspControls.TreeNode childNode = new System.Web.UI.WebControls.TreeNode(file.Name + " (" + file.TimeLastModified.ToShortDateString() + ")", "", "~/_layouts/images/" + file.IconUrl, file.ServerRelativeUrl.ToString(), "");
node.ChildNodes.Add(childNode);
}
// test if we have child folders
bool blnRecurseFolders = folder.SubFolders.Count > 0 ? true : false;
// if we have sub folders then loop through them
if (blnRecurseFolders)
{
// loop through the child folders
foreach (SPFolder childFolder in folder.SubFolders)
{
// create a new node and loop through the files
AspControls.TreeNode childNode = new System.Web.UI.WebControls.TreeNode(childFolder.Name);
node.ChildNodes.Add(TraverseFiles(childFolder, childNode));
}
}
}
catch (Exception e)
{
//TODO: handle error
}
return node;
}
#endregion
}
}
|
|
|
Copyright
|
This work is licenced under a Creative Commons Attribution-Noncommercial-No Derivative Works 2.0 UK: England & Wales License
|
|
Site Map
|
|
|
|
|
|
|