Second Life of a Hungarian SharePoint Geek

October 21, 2011

How to deploy a custom field with custom properties from a feature – the missing second part

Filed under: Custom fields, Deployment, Features, PowerShell, SP 2010 — Tags: , , , , — Peter Holpar @ 14:49

Only readers following my blog from the beginning may remember my post I wrote about the deployment of custom fields having custom properties more than three years ago. Of course that post is about WSS 3.0.

At that time I described how to include the custom properties into the field definition even if these properties are not declared by the XSD validation schema. However, injecting the custom properties the way I described there did not have effect on the values of the custom properties the custom field actually deployed with. I promised then that in the next part I would show you how to resolve this issue. It’s better late than never, the second part comes now, updated for SharePoint 2010.

The trick to achieve our goal seemed originally to be really simple. The base idea behind  it was that an instance of the field must be created sooner or later before usage, and the values we would like to push into the custom properties were available on object creation in the field schema deployed.

I’ve modified the constructors of the custom field, adding a call to my custom InitializeProps method.

  1. public SPFieldRegEx(SPFieldCollection fields, string fieldName)
  2.     : base(fields, fieldName)
  3. {
  4.     InitializeProps();
  5. }
  6.  
  7. public SPFieldRegEx(SPFieldCollection fields, string typeName, string displayName)
  8.     : base(fields, typeName, displayName)
  9. {
  10.     InitializeProps();
  11. }

The InitializeProps method is responsible for reading up the deployed custom property values from the field schema and setting them to the standard custom location in the schema.

  1. // in the custom field feature definition
  2. private readonly string customNamespaceUri = "http://schemas.grepton.com/sharepoint/";
  3.  
  4. private void InitializeProps()
  5. {
  6.     String regEx = (String)GetCustomProperty("RegEx");
  7.  
  8.     // value of custom property is null if not yet set
  9.     if (regEx == null)
  10.     {
  11.  
  12.         // load the field schema into an XML document
  13.         XmlDocument schemaXml = new XmlDocument();
  14.         schemaXml.LoadXml(SchemaXml);
  15.  
  16.         XmlNode fieldNode = schemaXml.SelectSingleNode("Field");
  17.  
  18.         if (fieldNode != null)
  19.         {
  20.             InitCustomProperty(fieldNode, "RegEx");
  21.             InitCustomProperty(fieldNode, "ErrMsg");
  22.             InitCustomProperty(fieldNode, "MaxLen");
  23.         }
  24.  
  25.     }
  26.  
  27. }
  28.  
  29. private void InitCustomProperty(XmlNode fieldNode, String custPropName)
  30. {
  31.     XmlAttribute custPropOrigAttr = fieldNode.Attributes[custPropName, customNamespaceUri];
  32.     // should not be null, but we check it
  33.     if (custPropOrigAttr != null)
  34.     {
  35.         SetCustomProperty(custPropName, custPropOrigAttr.Value);
  36.     }
  37. }

After deploying my custom field, I’ve checked my field at the Change Site Column page and was happy to see my custom property values there:

image

However, it turned out quickly that the schema of my field was not updated as expected:

  1. <Field
  2.   ID="{54634385-A8AC-4898-BF24-E533EB23444F}"
  3.   Name="RegExField"
  4.   DisplayName="RegExField"
  5.   StaticName="RegExField"
  6.   Group="Grepton Fields"
  7.   Type="SPFieldRegEx"
  8.   Sealed="FALSE"
  9.   AllowDeletion="TRUE"
  10.   SourceID="http://schemas.microsoft.com/sharepoint/v3/fields"
  11.   Description="This is the RegEx field"
  12.   grp:RegEx="[0-9]"
  13.   grp:MaxLen="20"
  14.   grp:ErrMsg="Error!"
  15.   xmlns:grp="http://schemas.grepton.com/sharepoint/" />

Of course, pressing OK on the Change Site Column page updated the field schema with the custom properties:

  1. <Field
  2.   ID="{54634385-A8AC-4898-BF24-E533EB23444F}"
  3.   Name="RegExField"
  4.   DisplayName="RegExField"
  5.   StaticName="RegExField"
  6.   Group="Grepton Fields"
  7.   Type="SPFieldRegEx"
  8.   Sealed="FALSE"
  9.   AllowDeletion="TRUE"
  10.   SourceID="http://schemas.microsoft.com/sharepoint/v3/fields"
  11.   Description="This is the RegEx field"
  12.   grp:RegEx="[0-9]"
  13.   grp:MaxLen="20"
  14.   grp:ErrMsg="Error!"
  15.   xmlns:grp="http://schemas.grepton.com/sharepoint/"
  16.   Required="FALSE"
  17.   EnforceUniqueValues="FALSE"
  18.   Version="1">
  19.   <Customization>
  20.     <ArrayOfProperty>
  21.       <Property>
  22.         <Name>RegEx</Name>
  23.         <Value
  24.           xmlns:q1="http://www.w3.org/2001/XMLSchema"
  25.           p4:type="q1:string"
  26.           xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">[0-9]</Value>
  27.       </Property>
  28.       <Property>
  29.         <Name>MaxLen</Name>
  30.         <Value
  31.           xmlns:q2="http://www.w3.org/2001/XMLSchema"
  32.           p4:type="q2:double"
  33.           xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">20</Value>
  34.       </Property>
  35.       <Property>
  36.         <Name>ErrMsg</Name>
  37.         <Value
  38.           xmlns:q3="http://www.w3.org/2001/XMLSchema"
  39.           p4:type="q3:string"
  40.           xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">Error!</Value>
  41.       </Property>
  42.     </ArrayOfProperty>
  43.   </Customization>
  44. </Field>

That gave me the idea that I should try to deploy my custom field using this schema. I removed my custom attributes from the Field element and used the following XML to deploy the field.

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  3.   <Field
  4.     ID="{54634385-A8AC-4898-BF24-E533EB23444F}"
  5.     Name="RegExField"
  6.     DisplayName="RegExField"
  7.     StaticName="RegExField"
  8.     Group="Grepton Fields"
  9.     Type="SPFieldRegEx"
  10.     Sealed="FALSE"
  11.     AllowDeletion="TRUE"
  12.     SourceID="http://schemas.microsoft.com/sharepoint/v3/fields"
  13.     Description="This is the RegEx field"
  14.     Version="1">
  15.     <Customization>
  16.       <ArrayOfProperty>
  17.         <Property>
  18.           <Name>RegEx</Name>
  19.           <Value
  20.             xmlns:q1="http://www.w3.org/2001/XMLSchema"
  21.             p4:type="q1:string"
  22.             xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">[0-9]</Value>
  23.         </Property>
  24.         <Property>
  25.           <Name>MaxLen</Name>
  26.           <Value
  27.             xmlns:q2="http://www.w3.org/2001/XMLSchema"
  28.             p4:type="q2:double"
  29.             xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">20</Value>
  30.         </Property>
  31.         <Property>
  32.           <Name>ErrMsg</Name>
  33.           <Value
  34.             xmlns:q3="http://www.w3.org/2001/XMLSchema"
  35.             p4:type="q3:string"
  36.             xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">Error!</Value>
  37.         </Property>
  38.       </ArrayOfProperty>
  39.     </Customization>
  40.   </Field>
  41. </Elements>

I’ve removed my custom codes described above either, and tried to deploy the field.

Voilà! The field is deployed successfully, including the custom properties. At that point I really don’t understand if it would work for WSS 3.0 and if so, how I missed to find that solution three years ago. The main point it is working now and requires no hacking at all.

A mystical observation is that after deploying the field I was not able to get a reference for the new field using PowerShell:

$avFields = $web.AvailableFields
$avFields["RegExField"]

At the same time, the field is visible on the web UI, and the following C# code finds it:

SPFieldCollection avFields = web.AvailableFields;
SPField field = avFields["RegExField"];

If I create the field from code, PowerShell finds that either:

SPFieldCollection fields = web.Fields;
SPField fieldCode = new SPField(fields, "SPFieldRegEx", "RegExCode");
fields.Add(fieldCode);

If you know the reason for that, let me know, please!

June 28, 2011

Creating WSP packages for all of your SharePoint projects

Filed under: Deployment, SP 2010, Visual Studio — Tags: , , — Peter Holpar @ 09:19

Assume you have a solution in Visual Studio 2010 with several SharePoint projects. You have to create the WSP files for the SharePoint artifacts, but no deployment.

In this case you can use the built-in Package menu on the project Build menu as described here. Of course, you have to package the projects one by one.

If you have to do it several times and you have many SharePoint projects in your solution it might be quicker to create your own deployment configuration as illustrated below:

image

and set it as the default deployment step:

image

After you repeat these steps for each of the SharePoint projects, you can select the Deploy Solution in the solution menu. Your project will be packaged at a single action, but without deployment.

BTW deployment configurations: Interesting behavior (bug?) in Visual Studio 2010 that you can delete the active deployment configuration (see image below), that results the following error on deployment:

The active deployment configuration ‘Package Only – No Deployment’ cannot be located. Specify a valid deployment configuration in the Project Properties.

image

March 10, 2010

Changing the namespace of the web part in a Visual Web Part project

Filed under: Deployment, SP 2010, Visual Web Part — Tags: , , — Peter Holpar @ 19:08

The default namespace for the web part class in a Visual Web Part project is YourVisualWebPartProjectName.VisualWebPart1. When you change the namespace and deploy the project you get the following exception:

UnsafeControlException: A Web Part or Web Form Control on this Page cannot be displayed or imported.

The exception is displayed on the web part page as:

Web Part Error: A Web Part or Web Form Control on this Page cannot be displayed or imported. The type {your web part class type} could not be found or it is not registered as safe.

To change the namespace that is registered in the SafeControls block, you have to open and edit a file that is hidden a bit. At least I found no direct way to set the values on the Visual Studio IDE (like project or package properties), so I searched for the original namespace in the entire project and found a file called SharePointProjectItem.spdata that contains the namespace for the SafeControl. You can even add multiple SafeControl element if you need, all of these elements will be added to the SafeControls block in the web.config file.

It is wired that after changing the namespace of the web part, when we check the preview of the package manifest (see the upper part of the Manifest tab on the Package designer) still the original namespace is displayed in the SafeControl element, but it does not affect the deployment (SafeControl registration) and working of the web part.

And do not forget to change the type of the web part (name attribute of the webParts/webPart/metaData/type element) in the .webpart file according the new namespace value!

August 12, 2008

How to deploy a custom field with custom properties from a feature

Filed under: Custom fields, Deployment, Features, SharePoint — Tags: , , , — Peter Holpar @ 00:47

Note: This is a repost, the original one was published on SharePoint Blogs on August 12, 2008

When MOSS 2007 was still in beta and features and custom fields were new areas to discover we created the classic regular expression field type too, just to play with and learn the new technology.

Our implementation SPFieldRegEx was inherited from the Text type and had three custom properties defined in the field type definition XML:

 

<PropertySchema>

<Fields>

<Field Name="RegEx" 

DisplayName="Regular Expression" MaxLength="255" DisplaySize="15" 

Type="Text">

</Field>

<Field Name="MaxLen" 

DisplayName="Maximum length" MaxLength="3" DisplaySize="3" 

Type="Integer">

<Default>255</Default>

</Field>

<Field 

Name="ErrMsg" DisplayName="Validation message" MaxLength="255" DisplaySize="30" 

Type="Text">

<Default>The value does not match the regular 

expression</Default>

</Field>

</Fields>

</PropertySchema>

The RegEx property stores the regular expression pattern, the MaxLen controls the maximal length of the field content and finally, the ErrMsg holds the validation message to be displayed when the input text does not match with the regular expression.

There is nothing interesting in that up to this point, but if you would like to deploy this custom field using a feature setting custom values to the properties you might encounter some difficulty.

Since I haven’t found that documented neither in the WSS SDK nor on developer blogs in the past years, I decided to share my experience.

If you create your feature definition for the field as you do normally with the built in field types, the result is the following XML:

<?xml version="1.0" encoding="utf-8" ?>

<Elements 

xmlns="http://schemas.microsoft.com/sharepoint/">

<Field 

ID="{54634385-A8AC-4898-BF24-E533EB23444F}" Name="RegExField" 

DisplayName="RegExField" StaticName="RegExField" Group="Grepton Fields" 

Type="SPFieldRegEx" Sealed="FALSE" AllowDeletion="TRUE" 

SourceID="http://schemas.microsoft.com/sharepoint/v3/fields" Description="This 

is the RegEx field" RegEx="[0-9]" MaxLen="20" 

ErrMsg="Error!"/>

</Elements>

But if you try to install the feature, you get the following error:

Feature definition with Id 6fd6ca04-3ac3-490f-b22f-4461a2253001 failed validation, file ‘feature_definition2.xml’, line 5, character 299:

The ‘RegEx’ attribute is not allowed.

If you remove the RegEx attribute, the same error message appears with MaxLen, if you remove that too, the ErrMsg causes problem.

So what to do to make this attributes allowed?

The schema of the features is defined in the wss.xsd. Now the most important part for us is the FieldDefinition complexType that is responsible – what a surprise! – for describing the format of the field definitions in the features. Besides other things it contains the list of the allowed attributes.

<xs:complexType name="FieldDefinition" 

mixed="true">

...

<xs:attribute name="Decimals" type="xs:int" 

/>

<xs:attribute name="Description" type="xs:string" 

/>

...

<xs:attribute name="DisplayName" type="xs:string" 

/>

...

<xs:attribute name="FillInChoice" type="TRUEFALSE" 

/>

...

<xs:attribute name="Hidden" type="TRUEFALSE" 

/>

...

<xs:attribute name="Max" type="xs:float" 

/>

<xs:attribute name="Min" type="xs:string" 

/>

...

<xs:attribute name="Name" type="xs:string" 

use="required"/>

...

<xs:attribute name="ReadOnly" type="TRUEFALSE" 

/>

...

<xs:attribute name="Required" type="TRUEFALSE" 

/>

...

<xs:attribute name="Title" type="xs:string" 

/>

<xs:attribute name="Type" type="xs:string" use="required" 

/>

...

<xs:attribute name="ID" type="UniqueIdentifier" 

/>

<xs:attribute name="Group" type="xs:string" 

/>

<xs:attribute name="MaxLength" type="xs:int" 

/>

<xs:attribute name="SourceID" type="xs:string" 

/>

<xs:attribute name="StaticName" type="xs:string" 

/>

...

<xs:anyAttribute namespace="##other" processContents="lax" 

/>

</xs:complexType>

The fragment above contains only the most widely used attributes for example.

One quick and dirty solution would be to include our custom attributes in the XSD schema but this probably wouldn’t be a supported method. Fortunately in this case MS has left the back door open: if you check the last attribute in the schema, it is anyAttribute with namespace ##other, meaning that you can inject your own attributes in the XML files using your own namespace.

After a minor modification in the feature definition XML (highlighted below) the XML was passed the schema check and our custom field feature was installed successfully.

<?xml version="1.0" encoding="utf-8" ?>

<Elements 

xmlns="http://schemas.microsoft.com/sharepoint/" 

xmlns:grp="http://schemas.grepton.com/sharepoint/">

<Field 

ID="{54634385-A8AC-4898-BF24-E533EB23444F}" Name="RegExField" 

DisplayName="RegExField" StaticName="RegExField" Group="Grepton Fields" 

Type="SPFieldRegEx" Sealed="FALSE" AllowDeletion="TRUE" 

SourceID="http://schemas.microsoft.com/sharepoint/v3/fields" Description="This 

is the RegEx field" grp:RegEx="[0-9]" grp:MaxLen="20" 

grp:ErrMsg="Error!"/>

</Elements>

Update: Although the technique described in this post help to avoid the validation error, the custom properties are not populated. We will discuss the resolution for this issue in the next part.

Blog at WordPress.com.