Prasad Bolla's SharePoint Blog

Click Here to go through the Interesting posts within my Blog.

Click Here to go through the new posts in my blog.

Thursday, December 01, 2011

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

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<http://msdn.microsoft.com/en-us/library/ms256443.aspx>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/" >
<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" xmlns:RegEx="[0-9]" xmlns:MaxLen="20" xmlns:ErrMsg="Error!"/>
</Elements>

But I faced another problem. Although this XML passed schema Validation, It
didn't update extended attributes. After investigations I found only way to
do that create event receiver for activation.

public class LookupFeatureEvents : SPFeatureReceiver
{
private const string CONST_FIELD = "Field";
public override void FeatureActivated(SPFeatureReceiverProperties
properties)
{
string str = string.Empty;
string lastXml = string.Empty;
SPElementDefinitionCollection elementDefinitionCollection =
properties.Definition.GetElementDefinitions(new CultureInfo(1033));
foreach (SPElementDefinition elementDefinition in
elementDefinitionCollection)
{
if (elementDefinition.ElementType == CONST_FIELD)
{
XmlNode node = elementDefinition.XmlDefinition;
List<string> arrNS = new List<string>();
for(int i=0; i<node.Attributes.Count; i++)
{
if (node.Attributes[i].Prefix!=string.Empty)
arrNS.Add(node.Attributes[i].Prefix);

}
SPSite parent = properties.Feature.Parent as SPSite;
if (parent != null)
{
SPWeb web = parent.RootWeb;
string webid = web.ID.ToString("D");
string fieldId = node.Attributes["ID"].Value;
SPField lookup = web.Fields[new Guid(fieldId)];
lastXml = node.OuterXml;
for (int i = 0; i < arrNS.Count; i++)
{
lastXml = lastXml.Replace(arrNS[i] + ":", "");
}
lastXml = lastXml.Replace("xmlns=\"http://schemas.microsoft.com/sharepoint//
"","");
lookup.SchemaXml = lastXml; lookup.Update(true);
}
}
}
}
public override void FeatureDeactivating(SPFeatureReceiverProperties
properties)
{
//throw new NotImplementedException();
}
public override void FeatureInstalled(SPFeatureReceiverProperties
properties)
{
//throw new NotImplementedException();
}
public override void FeatureUninstalling(SPFeatureReceiverProperties
properties)
{
//throw new NotImplementedException();
}
}

No comments:

Post a Comment