Option1 : Using a custom HttpModule to redirect to a custom-mngsiteadmin page. This approach ensures :
The OOB page isn’t accessible, even if users put the actual url (layouts/mngsiteadmin.aspx") in the browser and try accessing it.
Is easy to implement and supported.
The following sample approach can be tested :
<Sample-Code>
public class MasterPageModuleHandler : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
}
void context_BeginRequest(object sender, EventArgs e)
{
// throw new Exception("The method or operation is not implemented.");
HttpApplication oApp = (HttpApplication)sender;
if (oApp.Request.RawUrl.Contains("/_layouts/mngsiteadmin.aspx"))
{
oApp.Response.Redirect(oApp.Request.RawUrl.Remove(oApp.Request.RawUrl.LastIndexOf("/")) + "/custom/SfMngSiteAdmin.aspx");
}
}
public void Dispose()
{
}
}
</Sample-Code>
1. Build the Custom HTTP Module DLL and place it in the GAC
2. Assuming that a custom folder has been created under Layouts folder, ensure that custom-mngsiteadmin.aspx is present there.
3. Add the web.config entry for the custom HTTPModule in the config file of Web application
Find the HttpModules section in the web.config and add the following entry
<add name="HTTPModuleSPFileCache" type="HTTPModuleSPFileCache.CSetFileCache, HTTPModuleSPFileCache, Version=1.0.0.0, Culture=neutral, PublicKeyToken=<your key here>" />
4. Do an IISRESET.
Note: By putting a custom httpmodule in place, instead of normal 200 HTTP status, we get a HTTP 302 (Moved) and then a 200. No issues of performance hit have been reported , in using this approach, and you might want to test this one.
Option 2: Unlike Document Libraries\Lists, where the ToolBar is rendered through Templates defined in ControlTemplates.ascx, the Settings ToolBar for User.aspx is defined in the .aspx like below :
<SharePoint:FeatureMenuTemplate id="MenuTemplateSettings" LargeIconMode="TRUE" runat="server"
Location="Microsoft.SharePoint.User"
GroupId="SettingsMenu"
>
<SharePoint:MenuItemTemplate id="MenuItemSiteAdmins" runat="server" Text="<%$Resources:wss,people_siteadmins%>"
Description="<%$Resources:wss,people_siteadminsdescription%>"
ClientOnClickNavigateUrl="~site/_layouts/mngsiteadmin.aspx" Visible=false MenuGroupId="300" Sequence="100" />
MenuItemSiteAdmins is the ID of the the template responsible for rendering below menu :

So, to be able to override this behavior, I have to create a custom User.aspx page and override the link url to point to custom- mngsiteadmin.aspx page,Like below :
<SharePoint:FeatureMenuTemplate id="MenuTemplateSettings" LargeIconMode="TRUE" runat="server"
Location="Microsoft.SharePoint.User"
GroupId="SettingsMenu"
>
<SharePoint:MenuItemTemplate id="MyMenuItemSiteAdmins" runat="server" Text="<%$Resources:wss,people_siteadmins%>"
Description="<%$Resources:wss,people_siteadminsdescription%>" ClientOnClickNavigateUrl="~site/_layouts/custommngsiteadmin.aspx" Visible=true MenuGroupId="300" Sequence="100" />
So, modifying ClientOnClickNavigateUrl and Visible both - does the trick.
Next step would be to correct all places where user.aspx appears, to actually point to custom user.aspx. (As I cannot modify OOB User.aspx, I basically have to create a custom copy of it, and then
Correct all those menu-items\links which point to User.aspx)
Two of such places ,that I can enumerate are :
1. Site Permissions link in QuickLaunch on People.aspx

2. Site Permissions link in QuickLaunch on groups.aspx
Just as an e.g, to add custom User.aspx to People.aspx – we again have to create a custom People.aspx, and replace this line :
<asp:MenuItem Text="<%$Resources:wss,people_sitepermissions%>" NavigateUrl="user.aspx"/>
With :
<asp:MenuItem Text="<%$Resources:wss,people_sitepermissions%>" NavigateUrl="cuser.aspx"/>
So, the approach requires wide customization and there is no easy way to achieve this (none- as easy as Option1 above).
§ list.Items.XmlDataSchema
§ list.Items.Xml
§ list.Items.QueryFieldNames
§ list.Items.NumberOfFields
§ list.Items.ListItemCollectionPosition
§ list.Items.Count
§ list.Items.GetDataTable()
§ list.Items.GetItemById(40)
§ list.Items.ReorderItems()
§ list.Items.Delete(40)
§ list.Items.DeleteById(40)
SPQuery without RowLimit property set would fire the same the query.
I have not listed all the API calls which would fire the above query.
Sample code
--------------
SiteData.SiteData srvSiteData = new SiteData.SiteData();
srvSiteData.Credentials = System.Net.CredentialCache.DefaultCredentials;
string items = srvSiteData.GetListItems("{48414507-4436-4D47-BE59-9834D33F2B52}","<Query><OrderBy><FieldRef Name=\"Modified\" Ascending=\"True\"></FieldRef></OrderBy></Query>",
"<FieldRef Name=\"Title\"/><FieldRef Name=\"Modified\"/>", 100);
If the Query looks like
<Query><OrderBy><FieldRef Name=\"Modified\" Ascending=\"True\"></FieldRef></OrderBy></Query> the orderby would not work.
Change the XML Query to
<OrderBy><FieldRef Name=\"Modified\" Ascending=\"True\"></FieldRef></OrderBy>
Any input control for that matter, a simple custom field control with a single text box into the DataFormWebPart. The field gets displayed well in the NewForm and EditForm. But on saving the form data is not saved.No errors thrown.
Build and deploy the project attached to this post.
Create a custom list and create a column of Custom field control added by the project. (SimpleTextFieldField)
3. Open SPD.
4. Go to File -- Open Project -- Select the URL of the site.
5. Expand the Lists tree in the Left pane and expand the List which was created in step 2
6. Double Click on NewForm.aspx of the list.
7. Switch to Split mode of NewForm.aspx in SPD.
8. Select the ListForm Webpart in NewForm.aspx page
9. After </WebPartPages:ListFormWebPart> tag hit enter.
10. Go to Insert Menu -- SharePoint Controls -- Custom List Form. If Custom List Form option is not enabled save the NewForm.aspx and try again.
11. A pop up window appear where you can select the List which we created in step 2 and let the Content Type to use for from be selected to Item, check the radio button "New item form (used to add new items to the list).
12. Right click on List Form webpart -- Webpart properties -- Layout -- Select both checkboxes "Hidden" and "Close the webpart" -- Click OK button
13. Save the NewForm.aspx page in SPD.
14. Open IE, browse to the list created -- Click on New -- Add entry for Title column and a value for the Custom field control which we have added and click OK
14. It will come back to AllItems.aspx where we can find that the value entered to the custom column is blank.
Schema of Title Field
---------------------------
<Field ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Type="Text" Name="Title" DisplayName="Title" Required="TRUE" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="Title" FromBaseType="TRUE" ColName="nvarchar1"/>
Schema of Custom Field control
-------------------------------------
<Field Type="SimpleTextFieldField" DisplayName="huha" Required="TRUE" ID="{9df3f794-4a49-4019-a41a-3d29ae500903}" SourceID="{e68e397e-fb36-4971-955e-904f56a57d10}" StaticName="huha" Name="huha" ColName="nvarchar3" RowOrdinal="0"/>
Schema of Field added through UI
---------------------------------------------
<Field Type="Text" DisplayName="BoomCol" Required="FALSE" MaxLength="255" ID="{ef0b772f-af19-467a-9afd-2920ba1b10d8}" SourceID="{e68e397e-fb36-4971-955e-904f56a57d10}" StaticName="BoomCol" Name="BoomCol" ColName="nvarchar4" RowOrdinal="0"/>
This behaviour was because, the TextBox we exposed is a child control, the main control is SPTextFieldFieldControl. When you convert to a DataForm webpart, these custom controls will be replaced by the SPForm controls. This control does not identify what is the original value. If you look inside the SPTextFieldFieldControl, I do have HtmlTable, LiteralControl, TextBox etc. So now we get the inner control value and set the “Text/Value” property of the main control(SPTextFieldFieldControl)
===========================================
Code changes for SimpleTextField attached below.
=======================================
using System;
using System.Runtime.InteropServices;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Web.UI.HtmlControls;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace SimpleTextFieldControl
{
// TODO: Replace, as needed, "TextField" with some other class derived from Microsoft.SharePoint.WebControls.BaseFieldControl.
[CLSCompliant(false)]
[Guid("f6fdae87-e10d-4aa6-bd80-db14d41a19d6")]
public class SimpleTextFieldFieldControl : TextField
{
private HtmlTable table;
private TextBox txtName;
private string _textValue;
private void RefreshValues()
{
if (txtName != null && this.Context != null && this.Context.Request != null)
{
foreach (string key in this.Context.Request.Form.Keys)
{
if (key.ToLower() == txtName.UniqueID.ToLower())
{
string[] enteredValue = this.Context.Request.Form.GetValues(key);
textBox.Text = enteredValue[0];
_textValue = textBox.Text;
}
}
}
}
protected override void CreateChildControls()
{
base.CreateChildControls();
this.table = new HtmlTable();
HtmlTableRow row = new HtmlTableRow();
table.Rows.Add(row);
HtmlTableCell cell = null;
if (this.ControlMode == SPControlMode.Edit ||
this.ControlMode == SPControlMode.New)
{
cell = new HtmlTableCell();
cell.ColSpan = 2;
cell.Attributes["class"] = "ms-formdescription";
cell.InnerText = "Enter a Text:";
row.Cells.Add(cell);
row = new HtmlTableRow();
cell = new HtmlTableCell();
// a text field
this.txtName = new TextBox();
// Get the current value of the field.
string currentValue = (string)this.ItemFieldValue;
if (currentValue != null && currentValue != string.Empty)
{
this.txtName.Text = currentValue;
}
cell.Controls.Add(this.txtName);
row.Cells.Add(cell);
table.Rows.Add(row);
}
cell = new HtmlTableCell();
LiteralControl literalControl = new LiteralControl();
string text = null;
object textObject = this.ItemFieldValue;
if (textObject != null)
{
text = (string)textObject;
}
if (text == null || text == string.Empty)
{
text = this.txtName.Text;
}
literalControl.Text = text;
cell.Controls.Add(literalControl);
row.Cells.Add(cell);
base.Controls.Add(table);
}
public override string Text
{
get
{
RefreshValues();
return _textValue;
}
set
{
base.Text = value;
}
}
public override void UpdateFieldValueInItem()
{
base.UpdateFieldValueInItem();
try
{
this.Value = this.txtName.Text;
this.ItemFieldValue = this.Value;
}
catch
{
throw new Exception("error in text field") ;
}
}
protected override void Render(HtmlTextWriter output)
{
this.table.RenderControl(output);
}
}
}
Item form pages are the pages created based on the "Forms.aspx" as the setup page
which is available under 12/Templates/Pages folder.
1. All the templates for the NewForm, EditForm and DispForm (Item Form pages) are
defined in the defaulttemplates.ascx
2. This templates are associated with the List through content type
3. The FormTemplates of the NewForm, EditForm and DispForm are definied in the
content type as folows
<ContentType ID="0x01"
Name="$Resources:Item"
Group="$Resources:List_Content_Types"
Description="$Resources:ItemCTDesc"
Version="0">
<FieldRefs>
<FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title"
Required="TRUE" ShowInNewForm="TRUE" ShowInEditForm="TRUE"/> <!-- Title -->
</FieldRefs>
<XmlDocuments>
<XmlDocument
NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
<FormTemplates
xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
<Display>ListForm</Display>
<Edit>ListForm</Edit>
<New>ListForm</New>
</FormTemplates>
</XmlDocument>
</XmlDocuments>
</ContentType>
4. The "ListForm" template which have been in the above Content Type you can find
it in the DefaultTemplates.ascx control
5. Create a custom Defaulttemplates.ascx and create the custom rendering template
based on the ListForm and make ur changes (Including or Excluding control) Note :
ListFieldIteratorControl is the one which renders the Fields for the List in the
item form pages
6. Following are the steps to create custom defaulttemplates.ascx
1. Create a new custom Template: say name: CustDefaultTemplates.ascx and save it
in the folder: C:\Program Files\Common Files\Microsoft Shared\web server
extensions\12\TEMPLATE\CONTROLTEMPLATES
a. Open DefaultTemplates.ascx file (located at the same path mentioned above). Copy
the following lines from the file and add to the CustDefaultTemplate.ascx
<%@ Control Language="C#" AutoEventWireup="false" %>
<%@Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral,
PublicKeyToken=71e9bce111e9429c" %>
<%@Register TagPrefix="SharePoint" Assembly="Microsoft.SharePoint,
Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
namespace="Microsoft.SharePoint.WebControls"%>
<%@Register TagPrefix="SPHttpUtility" Assembly="Microsoft.SharePoint,
Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
namespace="Microsoft.SharePoint.Utilities"%>
<%@ Register TagPrefix="wssuc" TagName="ToolBar"
src="~/_controltemplates/ToolBar.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="ToolBarButton"
src="~/_controltemplates/ToolBarButton.ascx" %>
b. Now, in DefaultTemplates.ascx look for line:
<SharePoint:RenderingTemplate ID="ListForm" runat="server">
c. Copy this complete node and paste in the CustDefaultTemplate.ascx below the
lines added above.
d. Now, change the ID="ListForm" to any other ID; example: ID="CustListForm" in
CustDefaultTemplate.ascx
e. Save the File CustDefaultTemplate.ascx
7. Create a custom content type and render the "CustomListForm" as the template for
the New, Edit and Display form.
8. Attache the custom content type with the List.
9. Now whenever you create new, Edit and Dispform for this List it will render your
custom template instead of OOb template.
Came across an interesting issue with the quick access area in the page editing toolbar last week. The problem is with the “Submit For Approval” console node in the quick access. The “Submit for Approval” works fine as long as the OOB approval workflow is attached with the “Pages” library. But when the custom workflow is attached which has the ASPX pages associated with it then this “Submit For Approval” button fails to load those aspx pages for the approval.
The following screenshot shows a page with “Submit for Approval” button enabled when any approval workflow is attached with the pages library :
The page editing toolbar is divided into two areas such “Editing menu” and “Quick Access”. As you can see in the screenshot, the editing menu consists “Page”, “Workflow” and “Tools” menu which consists sub nodes. The quick access consists the console nodes for the quick access which are already existing under the “Editing Menu” area. Like you see in the screenshot, the “Workflow” menu in the Editing Menu consists the “Submit For Approval” console node which also exists in the “Quick Access” menu.
These nodes are rendered from the XML data source from “EditingMenu.xml” and “QuickAccess.xml” for Editing Menu and Quick access area respectively. You can find this files under C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS\EditingMenu.
SharePoint also provides the custom editingmenu.xml and customquickaccess.xml under master page gallery. We can use these files to customize the Editing menu and Quick Access areas. You can find my blog post as well on customizing these areas using these files : http://blogs.msdn.com/syedi/archive/2008/07/18/how-to-customize-add-delete-or-replace-the-site-action-menu-items-in-publishing-site.aspx
While doing the further testing found that the “Submit For Approval” console node under the “Workflow” category in the EditingMenu works fine but the “Submit For Approval” in the Quick Access has the issues with the workflows associated with the ASPX pages.
While digging deeper into the OOB EditingMenu.xml and QuickAccess.xml file found that the console nodes rendered in the EditingMenu and the QuickAccess are different for “Submit For Approval”.
In the EditingMenu.xml, you can find the Console Node which renders the “SubmitForApproval” button as follows : This works fine with all the workflows irrespective of the association whether it is InfoPath form or ASPX pages.
<ConsoleNode Sequence="200" NavigateUrl="javascript:" DisplayText="cms,console_workflowsubmenu" UseResourceFile="true" UserRights="EmptyMask" RequiredStates="InSharedView" ID="saWorkflowSubMenu">
<ConsoleNode Action="cms:OneClickPublishAction" DisplayText="cms,console_publish" HideStates="ContentApprovalEnabledFalse" ImageUrl="/_layouts/images/cnspub16.gif" UseResourceFile="true" ID="saApprovePublish"/>
<ConsoleNode Action="cms:PublishWithCommentAction" UseResourceFile="true" ID="saPublishWithComment"/>
<ConsoleNode Action="cms:CancelApprovalRequestAction" DisplayText="cms,console_cancelapprovalrequest" HideStates="IsApprovalWorkflowConfiguredTrue" ImageUrl="/_layouts/images/cnsca16.gif" UseResourceFile="true" ID="saCancelApproval"/>
<ConsoleNode Action="cms:CancelApprovalWorkflowRequestAction" DisplayText="cms,console_cancelapprovalrequest" HideStates="IsApprovalWorkflowConfiguredFalse" ImageUrl="/_layouts/images/cnsca16.gif" UseResourceFile="true" ID="saCancelApprovalWorkflow"/>
<ConsoleNode Action="cms:CancelSchedulingAction" DisplayText="cms,console_cancelscheduling" UseResourceFile="true" ID="saCancelScheduling"/>
<ConsoleNode Action="cms:UnpublishAction" DisplayText="cms,console_unpublish" ImageUrl="/_layouts/images/unpub.gif" UseResourceFile="true" ID="saUnpublish" />
<ConsoleNode IsSeparatorImage="True" UserRights="EmptyMask" />
<ConsoleNode Action="cms:ApproveOrDeclineAction" DisplayText="cms,console_approveworkflow" ImageUrl="/_layouts/images/apprj.gif" UseResourceFile="true" ID="saApproveOrDecline"/>
<ConsoleNode Action="cms:WorkflowTasksAction" UseResourceFile="true" ImageUrl="/_layouts/images/cnsvt16.gif" ID="saWorkflowTasks"/>
<ConsoleNode IsSeparatorImage="True" UserRights="EmptyMask" />
<ConsoleNode Action="cms:WorkflowStatusAction" DisplayText="cms,console_workflowstatustracking" UseResourceFile="true" ID="saWorkflowStatus"/>
<ConsoleNode Action="cms:WorkflowStartAction" DisplayText="cms,console_workflowstart" ImageUrl="/_layouts/images/workflows.gif" UseResourceFile="true" ID="saWorkflowStart"/>
</ConsoleNode>
In the QuickAccess.xml file you can find the Console Node which renders the “SubmitForApproval” button as follows : This doesn’t work fine with the workflows associated with ASPX pages.
<ConsoleNode Sequence="20" Action="cms:SwitchToAuthoringModeAction" DisplayText="cms,siteactions_editpage_displaytext" UseResourceFile="true" ImageUrl="/_layouts/images/edit.gif" HideStates="PageHasCustomizableZonesFalse|PageHasFieldControlsFalse" RequiredStates="InEditModeFalse|IsFormPageFalse|IsDocLibListItemTrue|InSharedView|IsCheckedOutToOtherUserFalse" ID="qaEditPage" />
<ConsoleNode Sequence="40" Action="cms:SwitchToPublishedModeAction" DisplayText="cms,console_reviewmode" UseResourceFile="true" ImageUrl="/_layouts/images/saveitem.gif" RequiredStates="InEditModeTrue|IsDocLibListItemTrue|InSharedView|SaveConflictExistsFalse|CheckOutRequiredFalse" ID="qaReviewMode"/>
<ConsoleNode Sequence="60" Action="cms:CheckInAction" UseResourceFile="true" ImageUrl="/_layouts/images/checkin.gif" ID="qaCheckin"/>
<ConsoleNode Sequence="80" Action="cms:PublishAction" UseResourceFile="true" HideStates="MinorVersionsEnabledFalse" ID="qaPublish"/>
<ConsoleNode Sequence="100" Action="cms:ApproveAction" DisplayText="cms,console_approve" UseResourceFile="true" ImageUrl="/_layouts/images/cnsapp16.gif" ID="qaApprove"/>
<ConsoleNode Sequence="101" Action="cms:WorkflowApproveAction" DisplayText="cms,console_approve" UseResourceFile="true" ImageUrl="/_layouts/images/cnsapp16.gif" ID="qaWorkflowApprove"/>
<ConsoleNode Sequence="120" Action="cms:OneClickPublishAction" DisplayText="cms,console_publish" UseResourceFile="true" ImageUrl="/_layouts/images/cnspub16.gif" ID="qaApprovePublish"/>
<ConsoleNode Sequence="140" Action="cms:DeclineAction" DisplayText="cms,console_declineworkflow" UseResourceFile="true" ImageUrl="/_layouts/images/cnsrej16.gif" ID="qaDecline"/>
<ConsoleNode Sequence="141" Action="cms:WorkflowDeclineAction" DisplayText="cms,console_declineworkflow" UseResourceFile="true" ImageUrl="/_layouts/images/cnsrej16.gif" ID="qaWorkflowDecline"/>
<ConsoleNode Sequence="160" Action="cms:WorkflowTasksAction" UseResourceFile="true" ImageUrl="/_layouts/images/cnsvt16.gif" ID="qaWorkflowTasks"/>
<ConsoleNode Sequence="180" Action="cms:SharedViewAction" DisplayText="cms,console_sharedview" ImageUrl="/_layouts/images/allusr.gif" UseResourceFile="true" ID="qaSharedView"/>
In the above highlighted nodes you can find the difference that the Console Action are rendered from different class. The One which works fine is rendered from the cms:PublishWithCommentAction and the one which doesn’t work with the workflows associated with the ASPX pages is rendered from cms:PublishAction.
Note : CMS is the tag prefix used as follows in the EditingMenu.xml and QuickAccess.xml files
<references>
<reference TagPrefix="cms" assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" namespace="Microsoft.SharePoint.Publishing.WebControls.EditingMenuActions" />
</references>
So now we concluded that the node rendered from the cms:PublishWithCommentAction class works fine. So by using the customquickaccess.xml existing in the Mater Page gallery we can delete the existing node (cms:PublishAction) from the quick access and can “Add” the cms:PublishWithCommentAction in the quick access.
Following is the content of the customquickaccess.xml which will delete the non working node and will add the working node in the quick access area.
<?xml version="1.0" encoding="utf-8" ?>
<Console>
<references>
<reference TagPrefix="cms" assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral,
PublicKeyToken=71e9bce111e9429c" namespace="Microsoft.SharePoint.Publishing.WebControls.EditingMenuActions" />
</references>
<structure>
<ConsoleNode Action="cms:PublishWithCommentAction" ID="saPublishWithComment" Sequence="610" ConfigMenu="Add"/>
<ConsoleNode ConfigMenu="Delete" ChangedNodeID="qaPublish" />
</structure>
</Console>
In the above CustomQuickaccess.xml you can find that we are adding the cms:PublishWithCommentAction through ConfigMenu=Add option and deleting the “qaPublish” node through ConfigMenu=Delete which is cms:PublishAction console node existing in the quickaccess.xml. This is how we need to override the OOB Quick area nodes. In the same way you can customize the EditingMenu and Site Action nodes.
Once you save and publish the customquickaccess.xml file the problematic console node vanishes and the new console node for “SubmitForApproval” appears in the quickaccess which works fine for all the workflows irrespective of the associations.
SPSite
mySite = new SPSite("<SiteURL>");
SPWeb myWeb = mySite.OpenWeb();
SPFolder myList = myWeb.Folders["<DocLibName>"];
SPFile myFile = myList.Files[0];
SPUser
myUser = myWeb.Users["<User_Name>"];
CheckInFileByUser(myFile,
"Checkin Comments", SPCheckinType.MajorCheckIn, myUser);
public
static void CheckInFileByUser(SPFile file, string checkinComment,SPCheckinType checkinType,SPUser modifiedByUser)
{
MethodInfo mi = typeof(SPFile).GetMethod("CheckIn",BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(string), typeof(SPCheckinType), typeof(bool), typeof(SPUser) }, null);
try
{
mi.Invoke( file,
new object[] { checkinComment, checkinType, false, modifiedByUser });
}
catch (Exception ex)
{
MessageBox.Show(ex.StackTrace);
}
}
Before calling this code you will need to Check out the file, otherwise you will get an error.
Recently I came across an issue with a custom master page created based on the OOB MWSDefault.master page.
MWSDefault.Master is the master page used by the OOB meeting workspace definition "MPS". This master page has been used with all the configuration of the MPS meeting workspace site definition. You can find this master page under the C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\GLOBAL folder. All the modules in the MPS site definition refers the master page from this set up path to provision it to the master page gallery.
Created a custom master page based on this MWSDefault.master page and has successfully provisioned it to the master page gallery. But applying this master page to the meeting workspace site doesn't replicate the same behavior as the OOB MWSDefault.master.
The following screenshot shows you the multipage meeting workspace site with the OOB MWSDefault.master page applied :
The following screenshot shows you the multipage meeting workspace site with the Custom MWSDefault.master page applied :
From the above screenshots you can find the difference that the multi page meeting workspace with the custom master page appears with a "Meeting Navigator" in the quick launch. This meeting navigator should appear only if more than one meeting event is linked with the meeting workspace. There are no meeting events linked with the meeting workspace but still the meeting workspace shows the "Meeting Navigator" with the message that "There are no more meeting occurrences to select from" but with the OOB MWSDefault.master everything is fine.
If you have a look into the "Default.aspx" page then you can find that the "HideMtgNavigatorPane" JavaScript function has been called to hide the meeting navigator if the meeting count for the meeting workspace is less than or equal to one (<=1). The following is the snippet from the default.aspx page :
<div id="MeetingNavigatorPane" class="ms-quicklaunchouter">
<div class="ms-quickLaunch" style="width:100%">
<div>
<table height=100% cellspacing=0 cellpadding=2 class="ms-navSubMenu2">
<tr valign=top>
<td id="onetidMtgNavigator">
<script> if (typeof(g_meetingCount) != "undefined" && (g_meetingCount == 0 || g_meetingCount == 1)) HideMtgNavigatorPane(); </script>
<!-- Begin Date Picker Column -->
<WebPartPages:WebPartZone runat="server" Title="loc:MeetingNavigator" ID="MeetingNavigator" Orientation="Vertical" FrameType="None" AllowLayoutChange="False" AllowPersonalization="false"/>
<!-- End Meeting Date Picker -->
</td>
</tr>
</table>
</div>
The custom master page has the JavaScript reference but this JavaScript method (HideMtgNavigatorPane) was not called when we are applying the custom master page. While doing the further source code debugging with the variable "g_meetingCount" found an interesting thing which is the root cause of the behavior.
The variable "g_meetingCount" has been assigned with the value in the fly by registering a Client Script Block. This has been implemented in the HandlePrerender method of Microsoft.SharePoint.Meetings.PropertyBag Class. The following is the snippet of the "HandlePrerender" method in which you can find that the "g_meetingCount" is assigned with the value of meeting count from the current web object.
private void HandlePreRender(object sender, EventArgs e)
{
// This item is obfuscated and can not be translated.
SPWeb contextWeb = SPControl.GetContextWeb(this.Context);
if (SPMeeting.IsMeetingWorkspaceWeb(contextWeb))
{
ScriptBuilder builder = new ScriptBuilder();
string fullOrRelativeUri = contextWeb.GetWebRelativeUrlFromUrl(SPGlobal.GetVTIRequestUrl(this.Page.Request, contextWeb.Request).ToString(), false, false);
builder.AddVariable("g_pageUrl", "\"" + SPHttpUtility.UrlPathEncode(MtgUtility.GetServerRelativeUrl(contextWeb) + fullOrRelativeUri, true) + "\"");
builder.AddVariable("g_webUrl", "\"" + SPHttpUtility.UrlPathEncode(contextWeb.ServerRelativeUrl, true) + "\"");
builder.AddVariable("g_instanceId", "\"" + contextWeb.MeetingInformation.InstanceId + "\"");
builder.AddVariable("g_meetingCount", "\"" + contextWeb.MeetingCount + "\"");
SPList homepageLib = MtgUtility.GetHomepageLib(contextWeb);
if (homepageLib != null)
{
string str2;
string str3;
Utility.SplitUrl(fullOrRelativeUri, out str2, out str3);
if (SPUtility.StsCompareStrings(str2, contextWeb.MeetingInformation.MakeInstanceFolder(homepageLib.RootFolder.Url)))
{
}
builder.AddVariable("g_fPageGlobal", "0");
}
this.Page.ClientScript.RegisterClientScriptBlock(typeof(PropertyBag), "Mtg_MeetingsPropertyBag", builder.ScriptText);
}
}
So we need to add this class to the Body of the custom master page so that the "g_meetingCount" variable will be assigned with the value and hide/show of the meeting Navigator panel will work as expected. There is already a Tag prefix has been included in the custom Master page for the "Microsoft.SharePoint.Meetings" namespace in the master page so we just need to add the reference to the "PropertyBag" class in the "Body" of the custom master page. This fixed the issue and now the Branding works as expected :
Note :
This "PropertyBag" class is very important and it's not only important for this "g_meetingCount" because there are some other variables are also used in this PropertyBag. So when you are missing any functionalities while customizing the "MWSDefault.master" please make sure that you adds this "PropertyBag" to the "Body" of the custom master page.
State based workflow that has a DelayActivity. The DelayActivity never fires and errors out eventually. The problem lies in the fact that the workflow calls out to another DLL in the code activity following the DelayActivity. When the call is made a "System.IO.FileNotFoundException: Could not load file or assembly" exception is raised.
System.IO.FileNotFoundException: Could not load file or assembly 'Utilities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=584ac65395d6cc3f' or one of its dependencies. The system cannot find the file specified. File name: 'Utilities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=584ac65395d6cc3f' at RRURequestWorkflow.Workflow1.codeActivity2_ExecuteCode(Object sender, EventArgs e) at System.Workflow.ComponentModel.Activity.RaiseEvent(DependencyProperty dependencyEvent, Object sender, EventArgs e) at System.Workflow.Activities.CodeActivity.Execute(ActivityExecutionContext executionContext) at System.Workflow.ComponentModel.ActivityExecutor`1.Execute(T activity, ActivityExecutionContext executionContext) at System.Workflow.ComponentModel.ActivityExecutor`1.Execute...
Go ahead and start Windows Sharepoint Services Timer. This will resolve the issue.
Reason behind this
----------------------------
When you have workflow that has got a delay activity, the event is fired by the service(SPTimerV3), before it fires, it has to load the assembly from its bin or from the GAC, only one file(module loads) based on the assembly information specified in workflow.xml file loads.
Deploying the new binary after changing the workflow activities, the SPTimerV3 is not aware of the newly added binary, it won’t reload it unless you do a time reset.
Unless you do a reset, the persistence (serialization or de-serialization) or loading of assembly would fail due to mismatch of types.
Small Search control which is rendered in default pages of Sharepoint is a delegate control which is rendered through Default.Master available under C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\GLOBAL (assuming C drive is the installation drive for MOSS)
Entry in Default.Master Page
<asp:ContentPlaceHolder id="PlaceHolderSearchArea" runat="server">
<SharePoint:DelegateControl runat="server" ControlId="SmallSearchInputBox" />
</asp:ContentPlaceHolder>
This control is actually available through a feature called "OSearchBasicFeature" located under 12\TEMPLATE\FEATURES. In the feature elements file (in this feature it's called "SearchArea.xml"), you can see the following:
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Control
Id="SmallSearchInputBox"
Sequence="50"
ControlClass="Microsoft.SharePoint.Portal.WebControls.SearchBoxEx"
ControlAssembly="Microsoft.SharePoint.Portal, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c">
<Property Name="GoImageUrl">/_layouts/images/gosearch.gif</Property>
<Property Name="GoImageUrlRTL">/_layouts/images/goRTL.gif</Property>
<Property Name="GoImageActiveUrl">/_layouts/images/gosearch.gif</Property>
<Property Name="GoImageActiveUrlRTL">/_layouts/images/goRTL.gif</Property>
<Property Name="DropDownMode">ShowDD</Property>
<Property Name="SearchResultPageURL">/_layouts/osssearchresults.aspx</Property>
<Property Name="ScopeDisplayGroupName"></Property>
<Property Name="FrameType">None</Property>
</Control>
</Elements>
The main class here seems to be Microsoft.SharePoint.Portal.WebControls.SearchBoxEx. To customize small search, you need to inherit from "SearchBoxEx" class and override "CreateChildControls" method to get your custom small search rendered
If you would like to customize this Delegate control (within support boundries ) there are 2 ways.
· Create a custom master page by copying the default.master page
· Copy/Paste "OSearchBasicFeature" in the same feature folder with a different name (like CustomSimpleSearch).
· Change the feature ID in the feature.xml file within CustomSimpleSearch feature
· Change the control ID in the SearchArea.xml file to something like "myCustomSearchcontrol".
· Change the class & assembly referenced in the SearchArea.xml file to the control library you created above.
· Install & Activate this feature
· Render the custom control you created in the custom master page have and remove "SmallSearchInputBox" control from the master page
Microsoft.SharePoint.PortalWebControls.SearchBoxEx class and you don't need to create a new control.
There are a large number of properties you can use to customize the control and you should find the one you're looking for microsoft.sharepoint.portal.webcontrols.searchboxex_properties.
· Create a new Feature (DelegateControl) under “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\TEMPLATE\FEATURES”
· Create a new manifest.xml in DelegateControl feature folder
<?xml version="1.0" encoding="utf-8"?>
<Solution xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
SolutionId="GENERATE_A_GUID"
ResetWebServer="True"
xmlns="http://schemas.microsoft.com/sharepoint/">
<FeatureManifests>
<FeatureManifest Location="PortalSearch\feature.xml" />
</FeatureManifests>
</Solution>
· Create a new feature.xml in DelegateControl feature folder
<?xml version="1.0" encoding="utf-8"?>
<Feature Id="7095f774-1efa-4879-b074-ff211f5559c7"
Title="Small Portal Search"
Description="Smaller search bar without scopes"
Version="12.0.0.0"
Hidden="FALSE"
Scope="Web"
DefaultResourceFile="core" xmlns="http://schemas.microsoft.com/sharepoint/">
<ElementManifests>
<ElementManifest Location="elements.xml"/>
</ElementManifests>
</Feature>
· Create a new elements.xml in DelegateControl feature folder
<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Control
Id="SmallSearchInputBox"
Sequence="23"
ControlClass="Microsoft.SharePoint.Portal.WebControls.SearchBoxEx"
ControlAssembly="Microsoft.SharePoint.Portal, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c">
<Property Name="SearchResultPageURL">/search/results.aspx</Property>
<Property Name="FrameType">None</Property>
<Property Name="DropDownMode">HideDD_NoScope</Property>
<Property Name="TextBoxWidth">140</Property>
<Property Name="ShowAdvancedSearch">false</Property>
</Control>
</Elements>
Install and activate the feature. Scope of this sample feature is Site level. You can modify it to reflect any scope.
The parameters I'm using set the "SearchResultPageURL", hide the scope drop down and remove the advance search link.
Architectural behavior of wiki site definition and the wiki page library
1. When a site is created based on the OOB wiki site definition, a wiki page library (List Template Type = 119) instance is created which is attached with the wiki document content type. Following is the snippet from the Onet.xml which explains you the remaining
<Lists>
<List FeatureId="00BFEA71-C796-4402-9F2F-0EB9A6E71B18" Type="119" Title="$Resources:core,WikiWebLibPages;" Url="$Resources:core,WikiWebLibPages_Folder;" VersioningEnabled="TRUE" />
</Lists>
2. You can find the wiki document content type details in the OOB “ctypes” feature. The wiki content type has the document template pointing to the “CreateWebpage.aspx” application page. So whenever you create a new item (wiki page) in the wiki page library it will navigate you to the “Createwebpage.aspx” where you can fill the wiki field and create the wiki page.
<DocumentTemplate TargetName="/_layouts/CreateWebPage.aspx" />
Another thing you need to notice in the wiki document content type is the “Wiki Field”. This content type has the site column called “Wiki field” which is of “Multiple lines of text” field type with RichTextMode enabled. This is the field where one can enter the wiki content and create wiki pages.
<FieldRef ID="{c33527b4-d920-4587-b791-45024d00068a}" Name="WikiField" />
3. Wiki page library is basically a document library based and we need to add the items as a files in the wiki page library. To explore this truth more you can investigate the button click code behind of “CreateWebpage.aspx”. This event basically calls the overloaded “Add” method of SPFileCollection class to create the wiki page in the wiki pages library. There are several overloaded “Add” method available with SPFileCollection class among them the following “Add” method is called to create wiki pages.
public SPFile Add(string urlOfFile, SPTemplateFileType templateFileType)
{
string str;
switch (templateFileType)
{
case SPTemplateFileType.StandardPage:
str = @"pages\viewpage.aspx";
break;
case SPTemplateFileType.WikiPage:
str = @"DocumentTemplates\wkpstd.aspx";
break;
default:
throw new ArgumentException();
}
return this.AddGhosted(str, urlOfFile);
}
4. From the above snippet you can find that if the page created is wiki page then it will be created based on the “wkpstd.aspx” layout which is available under the DocumentTemplates folder of 12 hive. This page has been hard coded in the “Add” method of SPFileCollection.
I had worked on a requirement where in I want to change the layout (wkpstd.aspx) of the wiki pages based on the business requirement.
I have created the own layout based on the OOB wkpstd.aspx and placed it under the DocumentTemplates folder. So the custom layout is created successfully and available for creating wiki pages.
OOPS !! But very unlikely the OOB wkpstd.aspx page is hard coded as mentioned above in the “Add” method of SPFileCollection class. So even if you create a custom layout for creating wiki pages the “CreateWebPage.aspx” is going to use the OOB wkpstd.aspx to create the wiki pages.
Now let’s see how to workaround this by design behavior of SharePoint with some customization technique. This walkthrough will help us understand the steps to follow when we start customizing/ bending some behavior of SharePoint.
1. First we need to change the code behind of “CreateWebPage.aspx” so that we can avoid using the OOB “wkpastd.aspx” to create wiki pages.
2. Modifying the OOB pages are not supported so we need to copy the OOB “CreateWebPage.aspx” and need to override the button click event in the custom “CreateWebpage.aspx”.
3. Need to create a custom content type inheriting from OOB wiki document content type which will have the custom “CreateWebpage.aspx” as the document template.
4. Attach the custom content type with the wiki pages library and detach the OOB wiki document content type.
5. So now when you are going to create wiki page from wiki pages library then you will be navigated to the custom “CreateWebPage.aspx” page which will use the custom “wkpstad.aspx” as a template to create the wiki pages.
Step 1 : Creating custom “CreateWebpage.aspx” and overriding the Code behind for creating the wiki pages based on the custom “wkpastd.aspx”
From the above explanation it’s sure that we cannot use the “Add” method of SPFileCollection. But we need to use any of the overloaded “Add” method to add a wiki page as a file to the wiki page library. So the only work around which seems to be possible for me is as follows :
a. Provision the default wiki pages for the wiki site based on the custom “wkpastd.aspx” during the site creation through “Onet.xml”. You can achieve this by creating a custom wiki site definition and making the changes to the Onet.xml of the custom wiki site definition.
<Module Name="DefaultWikiPages" List="119" Url="$Resources:core,WikiWebLibPages_Folder;" Path="" SetupPath="DocumentTemplates">
<File Url="Customwkpstd.aspx" Name="$Resources:core,nav_Home;.aspx" Type="GhostableInLibrary">
<Property Name="WikiField" Value="$Resources:core,WikiHomeContent;" />
<NavBarPage Name="$Resources:core,nav_Home;" ID="1002" Position="Start" />
<NavBarPage Name="$Resources:core,nav_Home;" ID="1010" Position="Start" />
</File>
<File Url="Customwkpstd.aspx" Name="$Resources:core,nav_HowToUseThisWikiSite;.aspx" Type="GhostableInLibrary">
<Property Name="WikiField" Value="$Resources:core,WikiHowToUseContent_Part1;$Resources:core,WikiHowToUseContent_Part2;$Resources:core,WikiHowToUseContent_Part3;$Resources:core,WikiHowToUseContent_Part4;$Resources:core,WikiHowToUseContent_Part5;$Resources:core,WikiHowToUseContent_Part6;$Resources:core,WikiHowToUseContent_Part7;$Resources:core,WikiHowToUseContent_Part8;" />
<NavBarPage Name="$Resources:core,nav_HowToUseThisWikiSite;" ID="1010" />
</File>
</Module>
b. So these pages now will be successfully created based on the custom “wkpastd.aspx” template. Now get the byte array of this page in the wiki pages library and pass it to the overloaded “Add” method of SPFileCollection to add the wiki page into the wiki pages library.
You can use the code sample given below in the code behind of button click Event in your custom "CreateWebPage.aspx"
SPWeb oWeb = SPContext.Current.Web;
SPList oList = oWeb.Lists["Wiki Pages"];
SPFileCollection oFiles = oList.RootFolder.Files;
SPFile oFile = oFiles[0];//getting the home page to collect the byte array of the custom document template
byte[] oByte = oFile.OpenBinary();
SPFile oFile1 = oFiles.Add(oList.RootFolder + "/TestWiki.aspx", oByte);
SPListItem oItem = oFile1.Item;
oItem["Name"] = "Test";
oItem["WikiField"] = Name.Text;// (Name is the ID of the textbox. Get the wiki content from the Rich textbox of the CreateWebPage.aspx
oItem.Update();
Step 2: Creating custom content type inheriting from OOB wiki document content type.
a. Create a custom feature to create a custom content type inherited from the OOB wiki content type. You can get the content type ID of OOB wiki document content type from the OOB “ctypes” feature.
b. You can install this custom feature and can include this feature in the “Site Features” of the Onet.xml so that this feature will get activated by default when the site is created based on this custom wiki site definition.
Step 3: Attaching the custom content type and detaching the OOB wiki content type from the wiki pages library
Create a feature event receiver for the custom content type feature and in the feature activated event attach the custom content type to the wiki pages library and detach the OOB wiki content type from the wiki pages library.
Now the custom wiki site definition with all the customization is ready for the use. Now create a site based on the custom wiki site definition and you can find some default pages created based on the custom “wkpastd.aspx” in the wiki pages library. The custom content type will be attached with the wiki pages library and you will be navigated to the custom “CreateWebPage.aspx” for creating wiki pages where you have already implemented the logic to create the wiki pages based on the custom “wkpstd.aspx”.
Following are the two steps to implement this requirement in the supported way and its quite easy, thanks to master page and the SharePoint Application Page link control.
1. Create a custom user control based on the OOB “WelCome.ascx” control. Override the “OnLoad” event and hide the “Sign In” application page link for the anonymous access user.
2. Create a custom master page based on the any OOB parent master page with respect to your requirement and site definition. Render the Custom welcome control in the place of OOB welcome control.
You can find the Welcome.ascx user control under the “Control Templates” folder. Bunch of menu items are available for the authenticated user like My Settings, Sign in as different user, Log Out and Personalize the page. All these menu items are available as feature menu template and will be available only if the user was authenticated successfully. Following is the structure of the feature menu template and all the menu items are available under the ID “ExplicitLogOut”. You can see that the visibility of this Personal Actions control is false and the visibility will be made to true when the user is successfully authenticated.
<SharePoint:PersonalActions AccessKey="<%$Resources:wss,personalactions_menu_ak%>" ToolTip="<%$Resources:wss,open_menu%>" runat="server" id="ExplicitLogout" Visible="false">
<CustomTemplate>
<SharePoint:FeatureMenuTemplate runat="server"
FeatureScope="Site"
Location="Microsoft.SharePoint.StandardMenu"
GroupId="PersonalActions"
id="ID_PersonalActionMenu"
UseShortId="true"
>
<SharePoint:MenuItemTemplate runat="server" id="ID_PersonalInformation"
Text="<%$Resources:wss,personalactions_personalinformation%>"
Description="<%$Resources:wss,personalactions_personalinformationdescription%>"
MenuGroupId="100"
Sequence="100"
ImageUrl="/_layouts/images/menuprofile.gif"
UseShortId="true"
/>
<SharePoint:MenuItemTemplate runat="server" id="ID_LoginAsDifferentUser"
Text="<%$Resources:wss,personalactions_loginasdifferentuser%>"
Description="<%$Resources:wss,personalactions_loginasdifferentuserdescription%>"
MenuGroupId="200"
Sequence="100"
UseShortId="true"
/>
<SharePoint:MenuItemTemplate runat="server" id="ID_RequestAccess"
Text="<%$Resources:wss,personalactions_requestaccess%>"
Description="<%$Resources:wss,personalactions_requestaccessdescription%>"
MenuGroupId="200"
UseShortId="true"
Sequence="200"
/>
<SharePoint:MenuItemTemplate runat="server" id="ID_Logout"
Text="<%$Resources:wss,personalactions_logout%>"
Description="<%$Resources:wss,personalactions_logoutdescription%>"
MenuGroupId="200"
Sequence="300"
UseShortId="true"
/>
<SharePoint:MenuItemTemplate runat="server" id="ID_PersonalizePage"
Text="<%$Resources:wss,personalactions_personalizepage%>"
Description="<%$Resources:wss,personalactions_personalizepagedescription%>"
ImageUrl="/_layouts/images/menupersonalize.gif"
ClientOnClickScript="javascript:MSOLayout_ChangeLayoutMode(true);"
PermissionsString="AddDelPrivateWebParts,UpdatePersonalWebParts"
PermissionMode="Any"
MenuGroupId="300"
Sequence="100"
UseShortId="true"
/>
<SharePoint:MenuItemTemplate runat="server" id="ID_SwitchView"
MenuGroupId="300"
Sequence="200"
UseShortId="true"
/>
<SharePoint:MenuItemTemplate runat="server" id="MSOMenu_RestoreDefaults"
Text="<%$Resources:wss,personalactions_restorepagedefaults%>"
Description="<%$Resources:wss,personalactions_restorepagedefaultsdescription%>"
ClientOnClickNavigateUrl="javascript:MSOWebPartPage_RestorePageDefault()"
MenuGroupId="300"
Sequence="300"
UseShortId="true"
/>
</SharePoint:FeatureMenuTemplate>
</CustomTemplate>
</SharePoint:PersonalActions>
The another part of the welcome user control is “ExplicitLogin” which has been rendered as the SharePoint Application Page Link as follows.
<SharePoint:ApplicationPageLink runat="server" id="ExplicitLogin"
ApplicationPageFileName="Authenticate.aspx" AppendCurrentPageUrl=true
Text="<%$Resources:wss,login_pagetitle%>" style="display:none" Visible="false" />
This is the link which we need to concentrate for this requirement. By default this link visibility is false and will come alive when the user is not authenticated. This is what happens with the anonymous access user. When the anonymous user access the site this link is visible so that the unauthenticated user can sign in.
Fair enough on the post mortem of the welcome user control. Now copy this welcome user control and paste it under the Control templates folder as “CustomWelcome.ascx” control. In the “CustomWelcome.ascx” control add an In Line script and override the “OnLoad” event. In the “OnLoad” event for the unauthenticated user hide the “ExplicitLogin” link.
protected override void OnLoad(EventArgs e)
{
//base.OnLoad(e);
base.OnLoad(e);
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
this.ExplicitLogout.Visible = true;
}
else
{
this.ExplicitLogin.Visible = false;
this.ExplicitLogin.Attributes.CssStyle.Add("display", "block");
}
}
Now we are done with the custom welcome user control. Let us have a look on rendering it through the custom master page based on the “default.master” master page. Copy the default.master page and add the Tag prefix reference for the “CustomWelcom.ascx” control as follows in the custom master page :
<%@ Register TagPrefix="wssuc" TagName="CustomWelcome" src="~/_controltemplates/CustomWelcome.ascx" %>
Find the following entry in the master page :
<wssuc:Welcome id="IdWelcome" runat="server" EnableViewState="false">
</wssuc:Welcome>
Replace the above entry with the following entry to replace the OOB welcome user control with your custom welcome user control :
<wssuc:CustomWelcome id="IdWelcome" runat="server" EnableViewState="false">
</wssuc:Welcome>
Save the custom master page and use it for the public facing internet site and now “Sign In” link will not be available for the unauthenticated anonymous access user.
The view pages for the list are created based on the “ViewPage.aspx” in the C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\Pages. The ViewPage.aspx inserts the DataFormWebpart to display the items in the View. Now What if you have a custom web part in which you want to display the items in the view and wants to insert the custom web part to the view page of all the view created for the list.
Following are the steps to achieve this requirement. So…. Get ready to bend the SharePoint without bending the rules J
1. Copy the OOB “ViewPage.aspx” and place it in the same folder as “CustomViewPage.aspx”
2. Create the custom web part which shows the items of the view with your custom approach.
3. Create a feature to provision the “CustomViewpage.aspx” along with your custom web part.
4. Create a custom list definition. In the schema.xml file of the definition for all the views mention the “SetupPath” to refer your “CustomViewPage.aspx”. With this step all the views mentioned in the list definition will use the “CustomViewPage.aspx” to create the view pages and as well all the views which you create after the creation of the list will use the “customViewPage.aspx” as the base page to create the view pages.
Step 1:
Copy the OOB “ViewPage.aspx” and place it in the C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\Pages folder as “CustomViewPage.aspx”
Step 2:
Create the custom web part which shows the items of the view with your custom approach. Basically use the GetViewfromUrl(“Lists/<Listname>/<View_Name.aspx>”) method to get the view and then retrieve the items in the web part. The code snippet for the custom web part looks like follows :
SPList list = SPContext.Current.List;
String strViewName = <get the current view name from the URL>
SPView view = SPContext.Current.Web.GetViewFromUrl("Lists/list/ "+strViewName);
SPListItemCollection listItems = list.GetItems(view);
.
.
.
.
After getting the list items then do your custom approach of displaying the items.
Build the web part DLL and place it in the GAC.
Step 3:
Create a feature to provision the “CustomViewPage.aspx” along with your custom web part. By activating this feature the “Customviewpage.aspx” will be embedded with your custom web part. The elements manifest file content looks like follows :
<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="CustomViewPage" Url="" Path="">
<File Url="Pages/CustomViewPage.aspx" Type="Ghostable">
<AllUsersWebPart WebPartZoneID="Main" WebPartOrder="1">
<![CDATA[
<WebPart xmlns="http://schemas.microsoft.com/WebPart/v2">
<Assembly>CustomWebpartforView, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e3483ddc8b0d437a</Assembly>
<TypeName> CustomWebpartforView.MyCustomview</TypeName>
<FrameType>None</FrameType>
<IsVisible>true</IsVisible>
<Title>Custom Web part for View</Title>
</WebPart>
]]>
</AllUsersWebPart>
</File>
</Module>
</Elements>
Step 4:
Now the base custom view page is available along with the web part. Now how to integrate the custom view page with the list definition. Create a custom List definition and for all the views defined in the schema mention the “SetupPath” to use the “CustomViewPage.aspx”. Now the custom view page will be used as the base view page for all the views defined in the list definition and as well the views created later also will be using the “CustomViewpage.aspx” as the base page. The sample snippet for declaring the views inside the schema.xml is as follows :
<View BaseViewID="1" Type="HTML" WebPartZoneID="Main" DisplayName="$Resources:core,All_Tasks;" DefaultView="TRUE" MobileView="True" MobileDefaultView="False" SetupPath="pages\Customviewpage.aspx" ImageUrl="/_layouts/images/issues.png" Url="AllItems.aspx"><!-- _locID@DisplayName="camlidT8" _locComment=" " -->
Install and activate the feature of your custom list definition.
Now with all these steps, when you create a list based on this custom list definition then all the views created will be using the “CustomViewPage.aspx” to create the view pages and your custom web part will be rendered instead of the OOB dataformwebpart. The views which you will be creating after the creation of the List (Through UI, Through Object Model) will also use the “CustomViewpage.aspx” as the base page to create view pages.
One of the requirements which I had encountered was to write a Asynchronous webpart which would show a progress bar on the webpart while the webpart is retrieving data from SQL in the backend.
Attaching Sample code of the webpart which I have created using Ajax similar to Business Data list which is available out of the box.
RaiseCallbackEvent method in the below sample is method which will contain all the business logic to fetch the details to be shown.
In the below sample I have added a System.Threading.Thread.Sleep(10000); to show the behavior of the webpart.
using System;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
using System.Web.UI.WebControls;
using System.IO;
using System.Text;
using System.Threading;
namespace AjaxWebPart
{
[Guid("f93b9ec7-a679-41e4-a58b-202318e39bea")]
public class AjaxWebPart : System.Web.UI.WebControls.WebParts.WebPart, ICallbackEventHandler
{
private string datadiv; //This will hold the name of your div tag
private string ajaxdata; //This will hold the data that is returned via ajax…
protected override void Render(HtmlTextWriter writer)
{
// TODO: add custom rendering code here.
this.datadiv = this.ClientID + "content"; //Uses the client side id of the web part instance + a name we give it
writer.Write("<div id=\"" + this.datadiv + "\"><img src=\"_layouts/kpiprogressbar.GIF\" width\"150\"></div>");
}
protected override void OnLoad(EventArgs e)
{
this.datadiv = this.ClientID + "content";
string js = Page.ClientScript.GetCallbackEventReference(this, "'blah'", "filldiv", "'" + this.datadiv + "'", true);
string contentloader = "var ajaxcommands=''; window.onload = ajaxloader; function ajaxloader () { eval(ajaxcommands); } function filldiv(arg, ctx) { var mydiv = document.getElementById(ctx); mydiv.innerHTML = arg; }";
if (Page.ClientScript.IsClientScriptBlockRegistered("contentloader ") == false)
Page.ClientScript.RegisterClientScriptBlock(Page.ClientScript.GetType(), "contentloader", contentloader, true);
Page.ClientScript.RegisterStartupScript(this.GetType(), "myloader", " ajaxcommands = ajaxcommands + \"" + js + ";\";", true);
base.OnLoad(e);
}
public string GetCallbackResult()
{
return this.ajaxdata;
}
public void RaiseCallbackEvent(string eventArgs)
{
StringBuilder sb = new StringBuilder();
System.Threading.Thread.Sleep(10000);
SPSite mySite = SPContext.Current.Site;
SPWebCollection subSites = mySite.AllWebs;
this.ajaxdata = mySite.Url.ToString() + "<BR>";
for (int i = 0; i < subSites.Count; i++)
{
SPListCollection lists = subSites[i].Lists;
for (int j = 0; j < lists.Count; j++)
{
sb.Append (subSites[i].Title + "--" + lists[j].Title + "<BR>");
this.ajaxdata = sb.ToString();
}
}
}
}
}
Assume you have enabled Target Audience on your list. While adding list items there would be scenarios where you would not add any value to that TargetAudience column.
While trying to Query this List Item through Object Model using (FullText Sql Query) IsNull predicate, the query will not return expected results.
For Ex; UserA is added to TargetAudience1 and TargetAudience1 is enabled on specific set of lists, they would like to return results accordingly.
FullText Search Query which they would want to execute is
SELECT TargetAudience,Title, Path FROM portal..scope()
WHERE CONTAINS ('"staff*"') AND ( ("SCOPE" = 'All Sites') )
AND (
CONTAINS("TargetAudience",'"ac533252-9ac6-4f75-93df-ce87ffb27782"')
OR CONTAINS("TargetAudience",'"6df05c7b-2567-41a0-80c8-ff0511ceeaaa"')
OR TARGETAUDIENCE IS NULL
)
ORDER BY "Rank" DESC
This Query is not returning expected results. The query is not returning results where TARGETAUDIENCE column is null.
The reason for this behavior being
Properties which don’t have a value are not stored in DB of Sharepoint to minimize the storage.
WORKAROUND
=============
One of the workarounds is to use a constant string value to represent NULL in the Target Audience column. This can be done by creating a dummy SharePoint group which must be used when there is no other entry in target audience column. In order to ensure that the target audience column always has an entry, you must make sure that target audience column always requires a value.
For e.g. Site can use NONE as the name of the entry that represents no other
entry in the target audience column and then at query time use
CONTAINS("TargetAudience",'"NONE"') as the restriction.