MOSS: Search by "Created By" and "Modified By"

I have spent the whole day battling with what seems to be a SharePoint bug – the “created by” and “modified by” search mappings in advanced search never actually index the relevant metadata, meaning that you cannot usefully search on either of these. Looking at the meta data properties returned on items from a fulltextsqlquery, createdby and lastmodifiedby information is always blank – meaning that these properties are not indexed. This is pretty surprising as these criteria are available on the advanced search on by default. Note that apparently these property mappings do work for legacy (SPS2003) content, see http://www.networkworld.com/community/node/19021.

From what I’ve read on other blogs, the current workaround is to manually map meta data properties, see http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1937319&SiteID=1. You can also use the author property as an alternative to the createdby property.

SharePoint: Calling Web Services Using WCF

This article describes how to call a SharePoint web service using WCF. In the example that follows, the Lists.asmx GetListItem webmethod is called from a console application that resides off a SharePoint server.

The first step is to add a service reference to your console application (simply right click on your console application project and select “Add Service Reference”. Set the address to the lists webservice at the root of your SharePoint webapp (this can be dynamically altered later). In my example, I call the service namespace “ListsWS”.

image

Note that for the code example below to work, you should ensure that the “Generate Asynchronous Operations” checkbox is unchecked in the advanced dialog.

image

On adding a service reference, some entries are automatically add to the app.config / web.config file for your project. By default the security mode for the webservice binding is set to “None” (meaning that your SharePoint webservice fails because it is called anonymously). To work around this, you must change the security block from:

Original

<security mode=”None”>
    <transport clientCredentialType=”None” proxyCredentialType=”None” realm=”” />
    <message clientCredentialType=”UserName” algorithmSuite=”Default” />
</security>

to…

New

<security mode=”TransportCredentialOnly”>
  <transport clientCredentialType=”Ntlm”/>
</security>

Now you can call the webservice as follows using the following code (ensure that you class references the System.ServiceModel, System.Net and System.Xml namespaces…

// Set up the webservice
ListsWS.ListsSoapClient lists = new ListsWS.ListsSoapClient();
// The line below forces the credentials passed to the webservice. If this is not set,
// the credentials that this console app is running as will be passed through
lists.ClientCredentials.Windows.ClientCredential = new NetworkCredential(“user”, “password”, “domain”);
lists.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;

// Dynamically set the endpoint to the appropriate spweb               
lists.Endpoint.Address = new EndpointAddress(“
http://[serverName]/marketing/_vti_bin/lists.asmx”);

// Create xml nodes containing params to be passed to the webservice
// In this example, we are going to filter results to only show the item where ID = 1
XmlDocument xmlDoc = new XmlDocument();
XmlElement viewFields = xmlDoc.CreateElement(“ViewFields”);
viewFields.InnerXml = “”;
XmlElement queryOptions = xmlDoc.CreateElement(“QueryOptions”);
queryOptions.InnerXml = “”;
XmlElement query = xmlDoc.CreateElement(“Query”);
query.InnerXml = “<Where><Eq><FieldRef Name=\”ID\” /><Value Type=\”String\”>1</Value></Eq></Where>”;

// Call the webmethod
XmlElement nodeListItems = lists.GetListItems(“Shared Documents”, null, query, viewFields, “1”, queryOptions, null);

// Parse out the items
XmlDocument resultsDoc = new XmlDocument();
resultsDoc.LoadXml(nodeListItems.InnerXml);

// An XmlNameSpaceManager is required to pass out rs and z elements
XmlNamespaceManager listsNsManager = new XmlNamespaceManager(resultsDoc.NameTable);
listsNsManager.AddNamespace(“rs”, “urn:schemas-microsoft-com:rowset”);
listsNsManager.AddNamespace(“z”, “#RowsetSchema”);

// Display the fileref of the first returned item
XmlNodeList items = nodeListItems.SelectNodes(“/rs:data/z:row”, listsNsManager);
Console.WriteLine(items[0].Attributes[“ows_FileRef”].Value);

 

Key points to note on the above code are that:

  1. lists.ClientCredentials.Windows.ClientCredential can be omitted – in which case, the credentials that your app is running under will be passed through to the SharePoint webservice.
  2. The URL to the webservice can be overridden using lists.Endpoint.Address.
  3. In the example above, an xmlnamespacemanager is required to perform xpath on the results.

SharePoint: LINQ query to get child content types

I had been putting off using / learning linq for a little while, but am now finally writing some .net 3.5 code for SharePoint where there are some clear uses for it. I am finding it really intuitive.

Anyway, below is one of my first real-world linq queries – it is a simple query to get all of the descendant content types from a given content type (note that the parent content type is also included):

SPContentTypeId parentId = new SPContentTypeId(“parentContentTypeId”);

using (SPSite mySiteCollect= new SPSite(“http://url/to/my/spweb”))
{
    using (SPWeb website = siteCol.OpenWeb())
    {
        IEnumerable<SPContentType> descendantContentTypes =
           from SPContentType childCt in myspweb.AvailableContentTypes
           where childCt.Id.IsChildOf(parentId)
           select childCt;

        // Do something here with descendant content types
    }
}

SharePoint: 403 Error Using Embedded Resources

I encountered an issue yesterday using embedded resources in a .NET 2.0 WebPart consumed by SharePoint. The dll used by the webpart contained an embedded js and css file that was accessed by this.Page.ClientScript.GetWebResourceUrl (note dll was deployed to 80\bin).

Whenever a page containing the webpart was accessed first by a non-admin user after an app pool recycle, a 403 error was encountered. This error went away to all users once an admin user had viewed the webpart. This error implied to me that the appropriate safecontrol assembly setting in the web.config was not correct, however this was not the case.

I have not yet found a proper solution to this problem, however the workaround I am currently using  is to call GetWebResourceUrl via SPSecurity.RunWithElevatedPrivileges. I will update this post if and when I find the actual cause / solution to this problem.

Related Link:

http://www.katriendg.com/aboutdotnet/2007-6-webresources-webpart-403-forbidden.aspx

Updating webpart properties from outside the tool pane

Today I created a webpart that contained a form. I wanted the input values from the form to be persisted in the same way as normal webpart properties that are set through the toolpane. To achieve this, I added appropriate attributes to the property I wanted to persist (notably WebBrowsable(false), as I did not want the property to be edited through the UI). I also ensured that the SetPersonalizationDirty() method was called after the property was set – this is crucial to ensure that the value is persisted. The code below illustrates this:

public class SavedSearch : System.Web.UI.WebControls.WebParts.WebPart
{
     private string _savedFreeTextSearch;
     private TextBox _inputBox;
     private Button _inputButton;
     private Label _label;

     [WebBrowsable(false),
     Personalizable(PersonalizationScope.Shared),
     DefaultValue(“”)]
     public string SavedFreeTextSearch
     {
         get { return _savedFreeTextSearch; }
         set { _savedFreeTextSearch = value; }
     }

     protected override void CreateChildControls()
     {
         _label = new Label();
         _label.Text = this.SavedFreeTextSearch;
         this.Controls.Add(_label);

         _inputBox = new TextBox();
         this.Controls.Add(_inputBox);

         _inputButton = new Button();
         _inputButton.Click += new EventHandler(_inputButton_Click);
         this.Controls.Add(_inputButton);

         base.CreateChildControls();
     }

     private void _inputButton_Click(object sender, EventArgs e)
     {
         this.SavedFreeTextSearch = _inputBox.Text;
         _label.Text = this.SavedFreeTextSearch;
         this.SetPersonalizationDirty();
     }
}

SharePoint Content Types: Hidden Attribute

Just a quick note – the hidden attribute on a content type seems to have little effect on how a content type can be used. The content type can still be used on document libraries, however the only side effect is that the content type does not appear on the new button drop down (even though “Visible on New Button” is set to true). The content type still appears when you upload a document.

Hmm, I’m just thinking as I write – maybe the hidden attribute stops a content type from being used in Office directly (so it is just hidden to Office)? If true, this may form a workaround to the Document Information Panel (DIP) problem (where custom field types are not rendered correctly in the DIP) by stopping content types that use custom field types from being accessed in Office.

This is just speculation at the moment – it will be interesting to see how the DIP behaves when a document is already set to use a hidden content type, I will test tomorrow morning…

Related link:

http://msdn.microsoft.com/en-us/library/aa544268.aspx 

EDIT:

I have just tested in office, and hidden content types are available both when creating new docs and when updating docs that already use a hidden content type. This poses the question, what is the hidden attribute for if it only hides the content type from the new button?