Posted in: C#

Fix issue with ASP.NET MVC Razor page intellisense

Posted on July 2, 2015 by Michael Roma

There is an issue that causes ASP.NET MVC Razor pages to stop syntax highlighting and intellisense to not work.

Add the following key to the web.config appSettings node:

<add key="webpages:Version" value="2.0.0.0" />

Automatically push data from a Kentico form to SalesForce using a Web to Lead form

Posted on April 5, 2014 by Michael Roma

This post shows how to have data from a Kentico form builder pushed to SalesForce automatically using the Web-To-Lead functionality.

The two pieces of information you need from SalesForce is your OID and the Web-To-Lead post URL.

Below is the class to post to SalesForce from a BizFormItem. This class can be created in your App_Code folder. You want to make sure you set SalesForceOID and SalesForceWebToLeadURL to the correct values. Next set SalesForceWebToLeadForms to list each Kentico Form you want to push over.

You will also want to update the mapField(LeadModel, string) function to correctly map your Kentico Form field names to the corresponding SalesForce field. If you have any custom fields, you will need to generate the Web-To-Lead form html and find the custom field name used.

// add these includes
using CMS.CMSHelper;
using CMS.FormEngine;

namespace roma
{
    public class SalesForceService : IDisposable
    {
    	// define sales force settings - should be in web.config
    	public static string SalesForceOID  = "00Di0000000AAAA";
    	public static string SalesForceWebToLeadURL = "https://www.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8";

    	// define forms we should integrate
    	public static string[] SalesForceWebToLeadForms = { "BizForm.ContactForm", "BizForm.SignUpForm" }

        // function that posts from the given form record
        public void PostLeadFromForm(BizFormItem formObject)
        {
            // get the form name that represents the list
            var form = 
                BizFormInfoProvider.GetBizFormInfo(
                    formObject.BizFormClassName.ToLower().Replace("bizform.", ""),
                    CMSContext.CurrentSiteID);
            string formName = form.FormDisplayName;
            
            // check if a web to lead
            if (SalesForceWebToLeadForms.Contains(formObject.BizFormClassName))
            {
                // create the model
                var model = new LeadModel();

                // map the fields
                foreach (var col in formObject.ColumnNames)
                {
                    mapField(model, formObject, col);
                }

                // post to sales force
                PostWebToLead(model);
            }
        }

        // function that maps the col to the correct model field
        private void mapField(LeadModel model, BizFormItem formObject, string col)
        {
        	// check the name
            switch (col.ToLower())
            {
                // email
                case "email":
                    model.Email = formObject.GetStringValue(col, "");
                    break;

                // company
                case "company":
                    model.Company = formObject.GetStringValue(col, "");
                    break;

                // first name
                case "fname":
                    model.FirstName = formObject.GetStringValue(col, "");
                    break;

                // last name
                case "lname":
                    model.LastName = formObject.GetStringValue(col, "");
                    break;

                // title
                case "title":
                    model.Title = formObject.GetStringValue(col, "");
                    break;

                // phone
                case "phone":
                    model.Phone = formObject.GetStringValue(col, "");
                    break;

                // last name
                case "city":
                    model.City = formObject.GetStringValue(col, "");
                    break;

                // last name
                case "state":
                    model.State = formObject.GetStringValue(col, "");
                    break;

                // message
                case "comments":
                    model.Message = formObject.GetStringValue(col, "");
                    break;
            }
        }

        // function that sends the given data to sales fource
        public void PostWebToLead(LeadModel model)
        {
            // build the request
            var request = WebRequest.Create(Config.SalesForceWebToLeadURL);
            request.Method = "POST";

            // build the post data
            var sb = new StringBuilder()            
                .AppendFormat("oid={0}", Config.SalesForceOID)
                .AppendFormat("&{0}={1}", "company", model.Company)
                .AppendFormat("&{0}={1}", "first_name", model.FirstName)
                .AppendFormat("&{0}={1}", "last_name", model.LastName)
                .AppendFormat("&{0}={1}", "email", model.Email)
                .AppendFormat("&{0}={1}", "title", model.Title)
                .AppendFormat("&{0}={1}", "city", model.City)
                .AppendFormat("&{0}={1}", "state", model.State)
                .AppendFormat("&{0}={1}", "phone", model.Phone)
                .AppendFormat("&{0}={1}", "description", model.Description)
                .AppendFormat("&{0}={1}", "lead_source", model.LeadSource)
                ;
            
            // convert post data to byte[]
            byte[] byteArray = Encoding.UTF8.GetBytes(sb.ToString());
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = byteArray.Length;

            // write to request
            Stream dataStream = request.GetRequestStream();
            dataStream.Write(byteArray, 0, byteArray.Length);
            dataStream.Close();

            // get a response, read into string
            WebResponse response = request.GetResponse();
            dataStream = response.GetResponseStream();
            StreamReader reader = new StreamReader(dataStream);
            string responseFromServer = reader.ReadToEnd();            

            // clean up
            reader.Close();
            dataStream.Close();
            response.Close();
        }



        // dispose
        public void Dispose() { }

        // define model
        public class LeadModel
        {
            // define fields
            public string Company { get; set; }
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public string Email { get; set; }
            public string Title { get; set; }
            public string City { get; set; }
            public string State { get; set; }
            public string Phone { get; set; }
            public string Message { get; set; }
            public string Description { get; set; }

            public string LeadSource
            {
                get { return "Web"; }
            }
        }
    }
}

Last, you need to add an event handler so we can pick up when a form has been submitted and push that data to SalesForce. Create a new class in your App_Code folder for the following class:

// add these includes
using CMS.FormEngine;
using CMS.SettingsProvider;

[CustomDocumentEvents]
public partial class CMSModuleLoader
{
	// define class for events
    private class CustomDocumentEventsAttribute : CMSLoaderAttribute
    {
    	// init - add events
        public override void Init()
        {
            // set event handlers
            ObjectEvents.Insert.After += Insert_After;
        }

        // event handler for after insert for an object
        void Insert_After(object sender, ObjectEventArgs e)
        {
            // check if a form record added
            if (e.Object is BizFormItem)
            {
                // push to sales force
                using( var s = new roma.SalesForceService())
                {
                    s.PostLeadFromForm((BizFormItem)e.Object);
                }
            }
        }
    }
}

 

ASP.NET C# Web.Config helper class

Posted on April 5, 2014 by Michael Roma

The following is a C# helper class that reads App Settings in your ASP.NET Web.config file.

// add this include
using System.Configuration;

namespace roma
{
    public static class Config
    {
        // example setting
        // numeric example   
        public static double CacheMinutes { get { return Get("roma.CacheMinutes"); } }
        
        // boolean example
        public static bool IsTestMode { get { return Get("roma.IsTestMode"); } }
        
        // email addresses
        public static string DefaultEmailTo { get { return Get("roma.DefaultEmailTo"); } }
        
        // constant contact
        public static string ConstantContactAPIKey { get { return Get("roma.ConstantContact.APIKey"); } }
        public static string ConstantContactAccessToken { get { return Get("roma.ConstantContact.AccessToken"); } }
        public static string ConstantContactEmailList { get { return Get("roma.ConstantContact.EmailList"); } }

        // sales force
        public static string SalesForceOID { get { return Get("roma.SalesForce.OID"); } }
        public static string SalesForceWebToLeadURL { get { return Get("roma.SalesForce.WebToLeadURL"); } }        


        // helper to extract a web config value
        private static T Get(string key)
        {
            // try to convert
            try
            {
                return (T)Convert.ChangeType(ConfigurationManager.AppSettings[key], typeof(T));
            }
            catch (Exception) { }

            // return default
            return default(T);
        }
    }
}

Kentico CMS Mapper functions using Reflection in C# .NET

Posted on October 17, 2013 by Michael Roma

The following post shows functionality on mapping a Kentico CMS TreeNode object to a strong type .NET model class. These functions use reflection to map a property in a class to a Kentico document type field.

The examples below assumes your Document Types in Kentico have a corresponding .NET model class.

Below is the base class BaseModel that all model classes inherit from:

using CMS.PortalControls;
using CMS.DocumentEngine;
using CMS.CMSHelper;
using CMS.SettingsProvider;

public abstract class BaseModel
{
	// define document id
	public int id
	{
		get { return Document == null ? 0 : Document.DocumentID; }
	}

	// define node id
	public int NodeId
	{
		get { return Document != null ? Document.NodeID : 0; }
	}

	// define name
	public string DocumentName
	{
		get { return Document != null ? Document.DocumentName : ""; }
	}

	// define url
	public string URL
	{
		get { return Document == null ? "" : Document.RelativeURL.Replace("~",""); }
	}

	// define url with extension
	public string URLWithExtension
	{
		get { return URL + (Document == null ? "" : Document.DocumentExtensions); }
	}

	// external url
	public string ExternalURL
	{
		get { return Document == null ? "" : Document.AbsoluteURL; }
	}        

	// path for searching
	public string Path
	{
		get { return Document == null ? "" : URL + (!URL.EndsWith("/") ? "/" : "") + "%"; }
	}

	// define document
	public TreeNode Document { get; set; }


	// define function that maps extra properties
	public abstract void MapExtras();
}

The following are helper types to define an Image and a Generic Document:

public class Image
{
	// fields
	public string Guid { get; set; }

	// function that checks if an image
	public bool HasImage { get { return !String.IsNullOrEmpty(Guid); } } 

	// define path for the image
	public string Path
	{
		get { return String.Format("/getfile/{0}/img/", Guid); }
	}

	// function that returns the html tag for this image
	public string ToHtml(string alt)
	{
		// check if no guid
		if (String.IsNullOrEmpty(Guid))
			return "";

		// return the html tag for this image
		return String.Format("\"{1}\"", Path, alt);            
	}
}   
public class GenericDocument : BaseModel
{
	public override void MapExtras() { }
}

Below is a generic mapper function to map a given TreeNode and return the requested type:

public static T MapTo(TreeNode document)
	where T : BaseModel, new()
{
	// init item
	T item = new T();

	// check if a document passed
	if (document != null)
	{
		// set the document
		item.Document = document;

		// go through each of this model's properties
		foreach (var p in item.GetType().GetProperties())
		{
			// check if a writeable property
			if (p.CanWrite)
			{
				// check primitive types
				if (p.PropertyType == typeof(string))
				{
					p.SetValue(item, document.GetStringValue(p.Name, ""), null);
				}
				else if (p.PropertyType == typeof(int))
				{
					p.SetValue(item, document.GetIntegerValue(p.Name, 0), null);
				}
				else if (p.PropertyType == typeof(bool))
				{
					p.SetValue(item, document.GetBooleanValue(p.Name, false), null);
				}
				else if (p.PropertyType == typeof(double))
				{
					p.SetValue(item, document.GetDoubleValue(p.Name, 0), null);
				}
				else if (p.PropertyType == typeof(Guid))
				{
					p.SetValue(item, document.GetGuidValue(p.Name, new Guid()), null);
				}
				else if (p.PropertyType == typeof(DateTime))
				{
					p.SetValue(item, document.GetDateTimeValue(p.Name, DateTime.Parse("1/1/1900").Date), null);
				}

				// check complex types
				else if (p.PropertyType == typeof(Image))
				{
					p.SetValue(item, new Image { Guid = document.GetStringValue(p.Name, "") }, null);
				}				
				else if (p.PropertyType == typeof(GenericDocument))
				{
					p.SetValue(item, MapTo(GetDocumentFromGUID(document.GetStringValue(p.Name, ""))), null);
				}			
			}
		}

		// map extras
		item.MapExtras();
	}

	// return the item
	return item;
}

Below is a generic mapper function to map a given TreeNodeDataSet and return a list of the requested type:

public static List MapToList(TreeNodeDataSet documentList)
	where T : BaseModel, new()
{
	// define return list
	List list = new List();

	// check if a document list
	if (documentList != null)
	{
		// go through each
		foreach (var n in documentList)
		{
			list.Add(MapTo(n));
		}
	}

	// return 
	return list;
}

Below are helper functions needed for mapping:

// function that gets a tree node from guid
public static TreeNode GetDocumentFromGUID(string guidStr)
{
	// check if no guid
	Guid guid;
	if (Guid.TryParse(guidStr, out guid))
	{
		// get a tree provider
		var th = new TreeProvider();

		// find the node
		var n = th.SelectSingleNode(guid, CultureCode, CMSContext.CurrentSiteName);

		// check if a node
		if (n != null)
		{
			return DocumentHelper.GetDocument(n, th);
		}
	}

	// if here, return nothing
	return null;
}

// function that gets a tree node from id
public static TreeNode GetDocumentFromID(int id)
{
	// check if no id            
	if (id > 0)
	{
		// get a tree provider
		var th = new TreeProvider();
		return DocumentHelper.GetDocument(id, th);                
	}

	// if here, return nothing
	return null;
}

// function that trys to find the current culture, falls back to default
public static string CultureCode
{
	get
	{
		// return nothing if blank
		return CMSContext.CurrentDocumentCulture == null ? "en-US"
			: CMSContext.CurrentDocumentCulture.CultureCode;
	}
}

Below is an example on using this functionality:

// blog post model
public class BlogPost : BaseModel
{
	// fields
	public int BlogPostID { get; set; }
	public string Title { get; set; }
	public DateTime PostDate { get; set; }
	public string PostURL { get; set; }
	public Image Photo { get; set; }
	public string Author { get; set; }
	public string AuthorURL { get; set; }
	public string ReadMoreText { get; set; }

	public override void MapExtras()
	{
	}
}

// example
public class Example
{
	public static void GetSinglePost()
	{
		// get a single post from the current document
		BlogPost post = Mapper.MapTo(CurrentDocument);				
	}
	
	public static void GetPostList()
	{
		// get a list of blog posts in the path		
		var th = new TreeProvider();

		// find the nodes
		var nodes = th.SelectNodes(
			CMSContext.CurrentSiteName, "/%",
			Globals.CultureCode, false,
			"EXAMPLE.BlogPost", "", "NodeOrder", -1, true, -1);

		// get the list            
		List posts = Mapper.MapToList(nodes).OrderBy(x => x.Document.NodeOrder).ToList();
	}
}

Kentico CMS helper functions to query nodes and return strong typed objects

Posted on October 17, 2013 by Michael Roma

The following post shows Kentico 7 CMS wrapper functions that allow you to easily query nodes and return back a strong type.

The functionality here is using the mapping functionality shown in this post: Kentico CMS Mapper functions using Reflection in C# .NET.

GetListByClass - this function returns back a list of nodes mapped to the requested type with the given options: (Please see below regarding caching)

// function that returns a strong typed list by class and caches by default
public static List GetListByClass(string className, string path, int levels, int limit)
	where T : BaseModel, new()
{
	return GetListByClass(className, path, levels, limit, true);
}

// function that a strong typed list by class
public static List GetListByClass(string className, string path, int levels, int limit, bool cache)
	where T : BaseModel, new()
{
	// define function
	Func> fn = delegate()
	{
		// get a tree provider
		var th = new TreeProvider();

		// find the node
		var nodes = th.SelectNodes(
			CMSContext.CurrentSiteName, path,
			Globals.CultureCode, false,
			className, "", "NodeOrder", levels, true, limit);

		// return the list            
		return Mapper.MapToList(nodes).OrderBy(x => x.Document.NodeOrder).ToList();
	};

	// check if 
	if (cache)
	{
		// define cache key
		string cacheKey = String.Concat("GetList", "|", path, "|", className, "|", levels, "|", limit);

		// return the item from the cahc if exists
		return CacheManager.Get>(cacheKey, fn);
	}

	// else return the live data
	return fn();
}

GetListByClasses - this function takes a list of classes to filter

public static List GetListByClasses(List className, string path, int levels, int limit)
	where T : BaseModel, new()
{
	// join the filter list, return the list
	return GetListByClass(String.Join(";", className), path, levels, limit);
}

GetSingleByClass - this function returns a single object

// function that returns a single strong type by class
public static T GetSingleByClass(string className, string path, int levels)
	where T : BaseModel, new()
{
	// return the first item
	var itm = GetListByClass(className, path, levels, 1).FirstOrDefault();

	// check if null
	if (itm == null)
		itm = new T();

	// return;
	return itm;
}

The functions above use the Caching class below. I found this example from http://codepm.wordpress.com/2010/09/28/using-c-delegates-with-asp-net-data-caching/

// http://codepm.wordpress.com/2010/09/28/using-c-delegates-with-asp-net-data-caching/
public static class CacheManager
{
	// define default timeout
	static double CacheTimeoutMinutes = 20;

	// function that returns the full key
	private static string FullKey(string key)
	{
		return String.Concat("MySite.", key);
	}

	// function that added the object to the cache
	private static void Add(object cacheObject, string key, double timeoutminutes)
	{
		HttpContext.Current.Cache.Insert(FullKey(key), cacheObject, 
			null, 
			DateTime.MaxValue,
			TimeSpan.FromMinutes(timeoutminutes),
			CacheItemPriority.Normal, 
			null);
	}

	// function that returns a single item by key
	public static T Get(string key) where T : class
	{
		return HttpContext.Current.Cache[FullKey(key)] as T;
	}

	// function that returns tries to get the cached item with default time
	public static T Get(string key, Func fn) where T : class
	{
		return Get(key, CacheTimeoutMinutes, fn);
	}

	// function that returns tries to get the cached item with the given time
	public static T Get(string key, double timeoutminutes, Func fn) where T : class
	{        
		// try to get the item
		var obj = Get(key);
		if (obj == default(T))
		{         
			// if fails, execute function to get the data
			obj = fn();

			// add to the cache
			Add(obj, key, timeoutminutes);
		}

		// return the item
		return obj;
	}
}