Showing posts with label custom field. Show all posts
Showing posts with label custom field. Show all posts

Tuesday, November 4, 2008

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 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();
}
}

Monday, October 13, 2008

Custom field type that can hold more than the 255

Many people ask me, they need to use field holds more than the 255 characters. They used MultiLine text. It worked fine with custom list but failed (generate error can't hold than 255 characters) with content type.
Many bloggers suggest to create custom field type, I tried this suggestion but gave me same result.
I found simplest solution Create Site Column of type MultiLine text, and set property UnlimitedLengthInDocumentLibrary to TRUE.
You can use tool Imtech Fields Explorer v1.5.0.0 to set this property.
download Imtech Fields Explorer v1.5.0.0

Friday, September 26, 2008

Unlimited length text field

SharePoint 2007 SPFieldMultiLineText is limited to 255 characters. How do we bypass this restriction?

We need to create a new field that inherits from SPFieldMultiLineText. and create your own FLDTYPES.XML file.

First We have to create custom field
public class CustomMultilineField : SPFieldMultiLineText
{
#region SPField Constructors
public CustomMultilineField(SPFieldCollection fields, string fieldName) : base(fields, fieldName)
{

}

public CustomMultilineField(SPFieldCollection fields, string typeName, string displayName) : base(fields, typeName, displayName)
{

}
#endregion

public new string GetFieldValueAsHtml(object value, SPListItem item)
{
string html = base.GetFieldValueAsHtml(value, item);
return html.Replace("CreateWebPage", "CreateCustomPage");
}

public override BaseFieldControl FieldRenderingControl
{
get
{
BaseFieldControl fieldControl = new CustomMultilineFieldField();
fieldControl.FieldName = InternalName;
return fieldControl;
}
}
}

Render a Control


public class CustomMultilineFieldField : NoteField
{
protected override void RenderFieldForDisplay(HtmlTextWriter output)
{
CustomMultilineField field = (CustomMultilineField) Field;
if (field != null)
{
if (field.CustomMultilineFieldLinking)
{
output.Write("<div class=\"ms-CustomMultilineFieldcontent\">");
output.Write(field.GetFieldValueAsHtml(ItemFieldValue, ListItem));
output.Write("<p></p></div>");
return;
}
}
else
{
return;
}
base.RenderFieldForDisplay(output);
}
}

Create a definition file

<FieldType>
<Field Name="TypeName">CustomMultilineField</Field>
<Field Name="ParentType">Note</Field>
<Field Name="TypeDisplayName">
CustomMultilineFieldField
</Field>
<Field Name="TypeShortDescription">
CustomMultilineFieldField supports custom page
</Field>
<Field Name="UserCreatable">FALSE</Field>
<Field Name="ShowInListCreate">FALSE</Field>
<Field Name="ShowInSurveyCreate">FALSE</Field>
<Field Name="ShowInDocumentLibraryCreate">FALSE</Field>
<Field Name="ShowInColumnTemplateCreate">FALSE</Field>
<Field Name="Sortable">FALSE</Field>
<Field Name="Filterable">FALSE</Field>
<Field Name="FieldTypeClass">IdeSeg.SharePoint.CustomMultilineFields.CustomMultilineField.CustomMultilineField,IdeSeg.SharePoint.CustomMultilineFields,Version=1.0.0.0, Culture=neutral, PublicKeyToken=...</Field>
</FieldType>