Skip to content

Instantly share code, notes, and snippets.

@proteux
Created December 13, 2013 04:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save proteux/8501c8e259f9bfdadfff to your computer and use it in GitHub Desktop.
Save proteux/8501c8e259f9bfdadfff to your computer and use it in GitHub Desktop.
Allows for custom fields and attributes to be easily set on stardard objects
/****************************
*
* Written by: Jack Whitehouse @ 45 North
* Date: 12/12/13
* Purpose: Allows for custom fields and attributes to be easily set on stardard objects
*
****************************/
using EasyCustomFields.RightNow.Soap;
using System;
using System.Linq;
using System.Reflection;
namespace EasyCustomFields
{
public static class CustomFields
{
/// <summary>
/// EXTENSION METHOD (RNObject): returns the value for a custom field or attribute
/// </summary>
/// <param name="root">the RNObject to look for the custom field in</param>
/// <param name="fieldName">the custom field name (c$fieldName or fieldName) or custom attribute (namespace$fieldName)</param>
/// <returns>value for a custom field or attribute</returns>
public static object GetCustomField(this RNObject root, string fieldName)
{
object retVal = null;
//split out the field and namespace names
string packageName = "c";
if (fieldName.Contains('$'))
{
string[] parts = fieldName.Split('$');
packageName = parts[0];
fieldName = parts[1];
}
//pull out the custom field array based on name
GenericObject customFields;
try
{
PropertyInfo cf_prop = root.GetType().GetProperty("CustomFields");
customFields = cf_prop.GetValue(root, null) as GenericObject;
}
catch (Exception ex)
{
throw new ArgumentException("Root RNObject does not contain a 'CustomFields' property", "root", ex);
}
//make sure we found 'em
if (customFields == null)
throw new Exception("Object doesn't contain custom fields, validate SOAP template");
//find the package
GenericField package = customFields.GenericFields.FirstOrDefault(o => o.name == packageName);
if (package != null &&
package.DataValue != null &&
package.DataValue.Items != null &&
package.DataValue.Items.Length > 0)
{
GenericObject obj = package.DataValue.Items[0] as GenericObject;
//find the object
if (obj != null)
{
GenericField field = obj.GenericFields.FirstOrDefault(o => o.name.ToLower() == fieldName.ToLower());
//find the field
if (field == null)
{
throw new Exception("Could not find custom field: " + packageName + "$" + fieldName);
}
else if (field.DataValue != null && field.DataValue.Items != null && field.DataValue.Items.Length > 0)
{
retVal = field.DataValue.Items[0];
}
}
}
else
{
throw new Exception("Could not find custom field package: " + packageName);
}
return retVal;
}
/// <summary>
///
/// </summary>
/// <param name="root"></param>
/// <param name="fieldName">the custom field name (c$fieldName or fieldName) or custom attribute (namespace$fieldName)</param>
/// <param name="value">value to set the custom field or attribute to. NamedIDs can be passed in as a NamedID, int, long or string</param>
/// <param name="typeEnum">data type of the field</param>
public static void SetCustomField(this RNObject root, string fieldName, object value, DataTypeEnum typeEnum)
{
ItemsChoiceType choiceEnum = GetChoiceByType(typeEnum);
Type type = root.GetType();
GenericObject customFields;
PropertyInfo cf_prop;
//break out the field name
string packageName = "c";
if (fieldName.Contains('$'))
{
string[] parts = fieldName.Split('$');
packageName = parts[0];
fieldName = parts[1];
}
//create the layer names
string objectTypeName = type.Name;
string fieldTypeName = objectTypeName + "CustomFieldsc";
string fieldArrayTypeName = objectTypeName + "CustomFields";
try
{
//get the custom field property on the object
cf_prop = root.GetType().GetProperty("CustomFields");
customFields = cf_prop.GetValue(root, null) as GenericObject;
}
catch (Exception ex)
{
throw new ArgumentException("Root RNObject does not contain a 'CustomFields' property", "root", ex);
}
//create the field
GenericField newField = new GenericField
{
name = packageName,
dataType = DataTypeEnum.OBJECT,
dataTypeSpecified = true,
DataValue = new DataValue
{
Items = new object[]
{
new GenericObject
{
GenericFields = new GenericField[]
{
CreateGenericField(fieldName,typeEnum, choiceEnum, value),
},
ObjectType = new RNObjectType()
{
TypeName = fieldTypeName
}
},
},
ItemsElementName = new ItemsChoiceType[]
{
ItemsChoiceType.ObjectValue
},
}
};
// Update existing custom field array
if (customFields != null && customFields.GenericFields != null && customFields.GenericFields.Count() > 0)
{
foreach (GenericField field in customFields.GenericFields)
{
if (field.name == fieldName)
{
// Found the field, ensure that all the ridiculous extra meta-junk is correct
if (field.dataType == typeEnum && field.DataValue.ItemsElementName.Contains(choiceEnum))
{
field.DataValue.Items = new object[] { value };
return;
}
else
{
throw new Exception(String.Format("Field types do not match for {0} field {1}.", objectTypeName, fieldName));
}
}
}
// Not found in existing custom fields list, so add it
customFields.GenericFields = customFields.GenericFields.Concat(new[] { newField }).ToArray();
}
else
{
//set new custom field array
customFields = new GenericObject
{
GenericFields = new GenericField[]
{
newField
},
ObjectType = new RNObjectType
{
TypeName = fieldArrayTypeName
}
};
}
//store the updated custom field array back on the root object
cf_prop.SetValue(root, customFields, null);
}
/*================================ PRIVATE ================================*/
/// <summary>
/// HELPER: to get the appropriate item type for the given data type
/// </summary>
/// <param name="typeEnum">Data type choice</param>
private static ItemsChoiceType GetChoiceByType(DataTypeEnum typeEnum)
{
ItemsChoiceType choiceEnum = ItemsChoiceType.ObjectValue;
switch (typeEnum)
{
case DataTypeEnum.BASE64_BINARY:
choiceEnum = ItemsChoiceType.Base64BinaryValue;
break;
case DataTypeEnum.BOOLEAN:
choiceEnum = ItemsChoiceType.BooleanValue;
break;
case DataTypeEnum.BOOLEAN_LIST:
choiceEnum = ItemsChoiceType.BooleanValueList;
break;
case DataTypeEnum.DATETIME:
choiceEnum = ItemsChoiceType.DateTimeValue;
break;
case DataTypeEnum.DATETIME_LIST:
choiceEnum = ItemsChoiceType.DateTimeValueList;
break;
case DataTypeEnum.DATE:
choiceEnum = ItemsChoiceType.DateValue;
break;
case DataTypeEnum.DATE_LIST:
choiceEnum = ItemsChoiceType.DateValueList;
break;
case DataTypeEnum.DECIMAL:
choiceEnum = ItemsChoiceType.DecimalValue;
break;
case DataTypeEnum.DECIMAL_LIST:
choiceEnum = ItemsChoiceType.DecimalValueList;
break;
case DataTypeEnum.ID:
choiceEnum = ItemsChoiceType.IDValue;
break;
case DataTypeEnum.ID_LIST:
choiceEnum = ItemsChoiceType.IDValueList;
break;
case DataTypeEnum.INTEGER:
choiceEnum = ItemsChoiceType.IntegerValue;
break;
case DataTypeEnum.INTEGER_LIST:
choiceEnum = ItemsChoiceType.IntegerValueList;
break;
case DataTypeEnum.LONG:
choiceEnum = ItemsChoiceType.LongValue;
break;
case DataTypeEnum.LONG_LIST:
choiceEnum = ItemsChoiceType.LongValueList;
break;
case DataTypeEnum.NAMED_ID:
choiceEnum = ItemsChoiceType.NamedIDValue;
break;
case DataTypeEnum.NAMED_ID_LIST:
choiceEnum = ItemsChoiceType.NamedIDValueList;
break;
case DataTypeEnum.NAMED_ID_DELTA_LIST:
choiceEnum = ItemsChoiceType.NamedIDDeltaValueList;
break;
case DataTypeEnum.NAMED_ID_HIERARCHY:
choiceEnum = ItemsChoiceType.NamedIDHierarchyValue;
break;
case DataTypeEnum.NAMED_ID_HIERARCHY_LIST:
choiceEnum = ItemsChoiceType.NamedIDHierarchyValueList;
break;
case DataTypeEnum.OBJECT:
choiceEnum = ItemsChoiceType.ObjectValue;
break;
case DataTypeEnum.OBJECT_LIST:
choiceEnum = ItemsChoiceType.ObjectValueList;
break;
case DataTypeEnum.STRING:
choiceEnum = ItemsChoiceType.StringValue;
break;
case DataTypeEnum.STRING_LIST:
choiceEnum = ItemsChoiceType.StringValueList;
break;
default:
break;
}
return choiceEnum;
}
/// <summary>
/// HELPER: creates generic fields
/// </summary>
/// <param name="name">field name</param>
/// <param name="type">field type</param>
/// <param name="valueType">value type (must match field type)</param>
/// <param name="value">value must be of the correct type</param>
/// <returns>Generic field matching the specified values</returns>
private static GenericField CreateGenericField(string name, DataTypeEnum type, ItemsChoiceType valueType, object value)
{
object updatedObj = value;
//if it's a named ID handle the passing of a string or int w/o fuss
if (type == DataTypeEnum.NAMED_ID)
{
if (value is int || value is long)
{
updatedObj = new NamedID
{
ID = new ID
{
id = (value is int) ? (int)value : (long)value,
idSpecified = true
}
};
}
else if (value is string)
{
if (string.IsNullOrWhiteSpace((string)value))
{
updatedObj = null;
}
else
{
updatedObj = new NamedID
{
Name = (string)value
};
}
}
}
else if (type == DataTypeEnum.STRING && (string)value == "")
updatedObj = null;
GenericField retVal = new GenericField
{
name = name,
dataTypeSpecified = true,
dataType = type,
DataValue = (updatedObj == null) ? null : new DataValue
{
Items = new object[] { updatedObj },
ItemsElementName = new ItemsChoiceType[] { valueType }
}
};
return retVal;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment