Monday, November 24, 2008

Process to Remove Delete Permissions On SharePoint List

I got a requirement from my client asking me to remove delete permissions on SharePoint List except for SharePoint site Owners group. Thought for a while for the solution, went ahead and create a new SharePoint permission set without delete permission. That's it, task has became very easy. Now go in to the list settings and select list permissions. In the Actions menu select Manage Permissions. you will be prompted with the message that "You are about to create unique permissions for this List. changes made to parent site will no longer refer to this list.". Select Ok. Now select the appropriate SharePoint groups and remove the existing permissions and add the permissions which you have just created. your task is done.

Thursday, November 13, 2008

Adding BreadCrumb Navigation to SharePoint Application Pages

With the help of following Blogs I was able to get BreadCrumb on to the SharePoint Applications pages.
http://weblogs.asp.net/jan/archive/2008/04/16/adding-breadcrumb-navigation-to-sharepoint-application-pages-the-easy-way.aspx

http://www.novolocus.com/category/sharepoint/branding/breadcrumbs/
And some additional links which can be done through object model code.
http://blog.thekid.me.uk/archive/2007/01/17/configuring-the-breadcrumb-for-pages-in-layouts.aspx

http://www.mikhaildikov.com/2007/05/creating-dynamic-breadcrumbs-in.html

But the requirement from the client was that they want to see default.master with the SharePoint Application Pages and not Application.master. as we all know that all SharePoint Application pages uses application.master and not default.master page. I have googled for some time and found this wonderful Blog from Jan Tielens which helped me to change the master page from application.master to default.master.
http://weblogs.asp.net/soever/archive/2006/11/14/SharePoint-2007_3A00_-using-the-masterpage-from-your-site-in-custom-_5F00_layouts-pages.aspx

This took me in to new problem. The above technique for adding BreadCrumb navigation bar does not work. Then I have played around for some time and figured out the problem. BreadCrumb Navigation bar was not comming since I was changing the master page on the fly through code on page OnPreInit event. Technique mentioned in the above blog posts works for SahrePoint Application Pages only if the master page is application.master and not default.master. So I have learned that application.master uses "SPXmlContentMapProvider" as SiteMapProviser and default.master uses "SPContentMapProvider" as SiteMapPrivider. This helped me to solve the problem. I have just provided "SPXmlContentMapProvider" as SiteMapProviser as source for my default.master in the following way.

SharePoint Application Page Template



Code Behind

using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
namespace YourNameSpace
{
public class ClassName : PageBase
{
///
/// Variables declaration which includes control names in the .aspx page template
///

///
protected override void OnLoad(EventArgs e)
{
}
}
}

Friday, October 24, 2008

Mysterious bug in SharePoint People Editor Control

I have used People Editor Control which is also know an People Picker in SharePoint in one of my custom application pages development. I have noticed a peculiar Bug in the control. Let me explain you more in detail about this. I had a requirement which says that, base on the selected members in the people editor control I have to load their departments. So I have to execute client script which submit the form to load the respective departments of selected members in people editor control in this way.
SharePoint:PeopleEditor
AllowEmpty="false"
ValidatorEnabled="true"
id="requesterPicker"
runat="server" ShowCreateButtonInActiveDirectoryAccountCreationMode="true"
SelectionSet="User"
AfterCallbackClientScript="javascript:return getDepartmentsForEmployees();"
Width="300px" />


and the JavaScript function

function getDepartmentsForEmployees()
{
var dropDownListDepartment = document.getElementById(GetClientId("dropDownListDepartment"));
__doPostBack(dropDownListDepartment.id,'');
}
So in the page load of the form I would check the forms "__EventTarget" in the following way.

if (Request.Form["__EventTarget"].ToString().IndexOf("dropDownListDepartment") > 0)
{
bindDepartmentsByEmployeeNumber();
hiddenPeopleEditorNoMatchText.Value = "Y";
}

On my page load it loads the current logged in User in the People Editor Control.


Now if I add a user to the control and press Ctrl+K then it queries the active directory and if is not able to find that user then it displays like this.


If I chose to remove that user and click on check names then it comes to normal state.



Here comes the problem, Now if you do a post back on the page again it again displays "No exact match was found." like the one blow.



I took much time for me to figure out why the control is acting like this. I have even Googled and could not find much. The root cause for this problem is executing a JavaScript on " AfterCallbackClientScript="javascript:return getDepartmentsForEmployees();". If we remove this statement then everything works fine.
I guess this is a Bug in the control and has to be fixed.


Here is the workaround for this Issue. People Editor Control uses "errorLabel" to Display "No exact match was found.". So I have written a JavaScript function to avid this message and called it at the end of the .ASPX page in the script tag
peopleEditorNoMatchTextDisplay();


And the JavaScript Function
function peopleEditorNoMatchTextDisplay()
{
/*This function corrects the Bug from microsoft i.e If we are calling a function like this "AfterCallbackClientScript="javascript:return getDepartmentsForEmployees();" "
* and sometimes if an entry is not resolved in the people picker, we get a message "No exact match was found." and we make it resolved then after every postback on that page
* we still keep getting this message "No exact match was found.". to avoid this message this is the workaround.*/
var hiddenPeopleEditorNoMatchText = getTagFromIdentifierAndTitle("input", "hiddenPeopleEditorNoMatchText", "");
//document.getElementById("ctl00_PlaceHolderMain_hiddenPeopleEditorNoMatchText");
if(hiddenPeopleEditorNoMatchText.value == "N")
{
var requesterPicker_errorLabel = getTagFromIdentifierAndTitle("span", "errorLabel", "");
//document.getElementById("ctl00_PlaceHolderMain_requesterPicker_errorLabel");
requesterPicker_errorLabel.style.display = "none";
}
}

Do let me know if you have figured out better workaround then this.

JavaScript Validation on SharePoint List Item forms

I have got a requirement to write client side validation using JavaScript, which says that if a user checks a Checkbox on the List item entry from then I should enable a control on the list entry form otherwise the control should be disabled when the form loads. I was wondering how to write JavaScript on the list item forms and finally found couple of intresting things on SharePoint List item forms validations on google.
We can write JavaScript on the list item forms when the from is loading using the method "_spBodyOnLoadFunctionNames". I have found this interesting article on following blogs.


http://blogs.msdn.com/sharepointdesigner/archive/2007/06/13/using-javascript-to-manipulate-a-list-form-field.aspx

http://www.cleverworkarounds.com/2008/02/07/more-sharepoint-branding-customisation-using-javascript-part-1/

http://nqthinh.spaces.live.com/blog/cns!CA6B1F6382490EF7!227.entry

With the Help help of above Blogs I was able to write JavaScript on the list items forms.


Now my another problem is to perform client side validation on the "Submit" or "OK" button. I found this Blogs helpful for solving my problem.


http://edinkapic.blogspot.com/2007/10/add-javascript-date-validation-into.html

http://jamestsai.net/Blog/post/How-to-add-a-new-event-to-controls-in-ListFromWebPart-on-the-EditFromaspx-NewFromaspx-pages.aspx

http://phani-madhav.blogspot.com/2008/03/custom-script-while-submiting-list-item.html

Thursday, October 23, 2008

Managing permissions on SharePoint list items

As we know that, permissions in SharePoint are inherited only up to SharePoint list and are applied on all SharePoint list items. If we want to control the permissions on the list item based on a particular field on the list entry/edit from then it is not possible through SharePoint Out Of Box feature.
I got a requirement from my client, asking me that, certain list items should be only be visible to particular SharePoint groups based on the selected condition on the list items entry/edit form. Let's say I have a checkbox named "Is Confidential Project". If the user selects this checkbox in the list entry/edit form then this particular list item should only be visible to Administrators of the SharePoint site and Confidential Projects Access SharePoint group. To implement this requirement I have decided to write an event handler on the SharePoint list in "ItemAdded" and "ItemUpdated" methods.
const string confidentialProjectsAccessGroupName = "Confidential Projects Access";
const string administratorsGroupName = "Owners";
private void setListItemPermissions(SPItemEventProperties properties)
{
Guid webGuid = Guid.Empty;
using (SPWeb webInUserContext = properties.OpenWeb())
{
webGuid = webInUserContext.ID;
}
Guid siteGuid = properties.SiteId;

SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite siteCollection = new SPSite(siteGuid))
{
SPWeb oWebsite = siteCollection.OpenWeb(webGuid);
oWebsite.AllowUnsafeUpdates = true;
SPList oList = oWebsite.Lists[properties.ListId];
bool isConfidentialProject = false;
if (!string.IsNullOrEmpty(properties.AfterProperties["Confidential _x0020_ Project "].ToString().Trim()))
{
isConfidentialProject = Convert.ToBoolean(properties.AfterProperties["Confidential _x0020_ Project "].ToString().Trim());
}
//string projectAccess = properties.AfterProperties["Project_x0020_Access"].ToString().Trim();
SPListItem listItem = oList.GetItemById(properties.ListItemId);

if (isConfidentialProject)
{
if (!listItem.HasUniqueRoleAssignments)
{
listItem.BreakRoleInheritance(true);
}

//Loops over all the roles that exist in the item, removes the bindings
foreach (SPRoleAssignment roleAssignment in listItem.RoleAssignments)
{
//delete the existing permissions
roleAssignment.RoleDefinitionBindings.RemoveAll();
roleAssignment.Update();
}

oWebsite.AllowUnsafeUpdates = true;
/*When any object that implements ISecurable (those are SPWeb, SPList and SPListItem) breaks or reverts their role
* definition inheritance. This means every time you call SPRoleDefinitionCollection.BreakInheritance(),
* BreakRoleInheritance(), ResetRoleInheritance() or set the value of HasUniquePerm the AllowUnsafeUpdates
* property of the parent web will reset to its default value and you may need to set it back to true in order to
* do further updates to the same objects. http://hristopavlov.wordpress.com/2008/05/16/what-you-need-to-know-about-allowunsafeupdates/*/


SPRoleDefinition RoleDefinitionContributor = oWebsite.RoleDefinitions.GetByType(SPRoleType.Contributor);
SPRoleDefinition RoleDefinitionAdministrator = oWebsite.RoleDefinitions.GetByType(SPRoleType.Administrator);
/*SPRoleAssignment RoleAssignment = new SPRoleAssignment("\\", "email", "name", "notes");
RoleAssignment.RoleDefinitionBindings.Add(RoleDefinition);*/

SPGroup spGroupOwners = oWebsite.Groups[administratorsGroupName];
SPRoleAssignment RoleAssignmentOwners = new SPRoleAssignment(spGroupOwners);
RoleAssignmentOwners.RoleDefinitionBindings.Add(RoleDefinitionAdministrator);

SPGroup spGroupconfidentialProjectsAccess = oWebsite.Groups[confidentialProjectsAccessGroupName];
SPRoleAssignment RoleAssignmentContributor = new SPRoleAssignment(spGroupconfidentialProjectsAccess);
RoleAssignmentContributor.RoleDefinitionBindings.Add(RoleDefinitionContributor);

listItem.RoleAssignments.Add(RoleAssignmentOwners);
listItem.RoleAssignments.Add(RoleAssignmentContributor);
}
else
{
if (listItem.HasUniqueRoleAssignments)
{
listItem.ResetRoleInheritance();
}
}

this.DisableEventFiring();
listItem.Update();
this.EnableEventFiring();
oWebsite.AllowUnsafeUpdates = false;
oWebsite.Dispose();
}
});
}

In the above I have used "SPSecurity.RunWithElevatedPrivileges(delegate()" delegate since the logged in user may not have permissions to modify permissions on the SharePoint list items. RunWithElevatedPrivileges allows the logged in user to gain full control irrespective of his permissions on this list. I am using "listItem.BreakRoleInheritance(true);", which breaks the present inherited permissions on the list. Next I had removed all the existing permissions on the list and added my own permissions in the SharePoint list items.

Wednesday, October 22, 2008

Attach an event handler to a specific SharePoint list

When we attach an event handler through Features in SharePoint using “ListTypeId”, it attaches event handlers to all the lists of that particular type. This will result in a large performance hit. To execute the written code for a particular list we will have to check either with ListId or ContentTypeId.
So, here is a way of attaching an event handler to a specific list on “FeatureActivated” and to remove the event handler from the list on “FeatureDeactivating”. This is the best method I can find as of now for attaching and removeing the event handler to a specific SharePoint List.

const string assembly = "ListItemPermissions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9f4da00116c38ec5";
const string listReceiverName = "ListItemPermissions.ListItemPermissionsItemEventReceiver";
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
try
{
// get a reference to the current SPWeb
SPWeb _SPWeb = SPContext.Current.Web;
_SPWeb.AllowUnsafeUpdates = true;
// get a reference to the "Projects" list
SPList _projectsList = (SPList)_SPWeb.Lists["Projects"];

// if the "projectsList" list exists
if (_projectsList != null)
{
// create an empty Guid
Guid _ItemUpdatedGuid = Guid.Empty;
Guid _ItemAddedGuid = Guid.Empty;

// enumerate thru all of the event receiver definitions, attempting to
// locate the one we are adding
foreach (SPEventReceiverDefinition _SPEventReceiverDefinition in _projectsList.EventReceivers)
{
// if we find the event receiver we are about to add
// record its Guid
if (_SPEventReceiverDefinition.Type == SPEventReceiverType.ItemUpdated &&
_SPEventReceiverDefinition.Assembly == assembly &&
_SPEventReceiverDefinition.Class == listReceiverName)
{
_ItemUpdatedGuid = _SPEventReceiverDefinition.Id;
}

if (_SPEventReceiverDefinition.Type == SPEventReceiverType.ItemAdded &&
_SPEventReceiverDefinition.Assembly == assembly &&
_SPEventReceiverDefinition.Class == listReceiverName)
{
_ItemAddedGuid = _SPEventReceiverDefinition.Id;
}
}

// if we did not find the event receiver we are adding, add it
if (_ItemUpdatedGuid == Guid.Empty)
{
_projectsList.EventReceivers.Add(SPEventReceiverType.ItemUpdated, assembly, listReceiverName);
}
if (_ItemAddedGuid == Guid.Empty)
{
_projectsList.EventReceivers.Add(SPEventReceiverType.ItemAdded, assembly, listReceiverName);
}

_projectsList.Update();
_SPWeb.Update();
_SPWeb.AllowUnsafeUpdates = false;
}
}

catch (System.Exception ex)
{
PortalLog.LogString(ex.StackTrace);
throw new SPException(ex.Message);
}
}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
try
{
SPWeb _SPWeb = SPContext.Current.Web;
_SPWeb.AllowUnsafeUpdates = true;
// get a reference to the "Projects" list
SPList _projectsList = (SPList)_SPWeb.Lists["Projects"];
while(_projectsList.EventReceivers.Count > 0)
{
if (_projectsList.EventReceivers[_projectsList.EventReceivers.Count-1].Assembly.Equals(assembly))
{
_projectsList.EventReceivers[_projectsList.EventReceivers.Count-1].Delete();
}
} // looping thru event receivers.

_projectsList.Update();
_SPWeb.Update();
_SPWeb.AllowUnsafeUpdates = false;
}
catch (System.Exception ex)
{
PortalLog.LogString(ex.StackTrace);
throw new SPException(ex.Message);
}
}