Wednesday, January 6, 2016

Installing CRM 2015


It’s been a while since I blogged anything. I’ve been working on a CRM 2011 environment and didn’t have much time on my hands. But now I have something interesting while installing CRM 2015.

The first thing to remember before doing this is making sure that your Reporting Services (Native) is running. If you do not have the reporting services running you can install it using the setup from the SQL server DVD.
After that run the CRM installation wizard. This should run fine, I however ran into a problem on my windows 2012 server. It may be due to the loginname I entered during the installation “CRM_ADMIN@Wingtip.Com” It may have been better to use “Wingtip\CRM_Admin” but both should be supported by the wizard.

I created a service account named CRM_Admin with the loginname CRM_Admin@Wingtip.com and gave this account admin rights on my development server (not best practice so don’t do this in a production environment)
While going thought the last steps of the wizard I encountered the following exception:
12:08:49|   Info| Invoking setspn -s MSCRMAsyncService/WINGTIPSERVER CRM_ADMIN@WINGTIP.COM
12:08:49|  Error| setspn -s MSCRMAsyncService/WINGTIPSERVER CRM_ADMIN@WINGTIP.COM reported error: 1 - FindDomainForAccount: Call to DsGetDcNameWithAccountW failed with return value 0x00000525
Unable to locate account CRM_ADMIN@WINGTIP.COM
12:08:49|  Error| System.Exception: Action Microsoft.Crm.Setup.Server.SetAsyncServicePrincipalNameAction failed. ---> Microsoft.Crm.Setup.Shared.SetupException: There was a problem setting the service principal name (SPN) for service MSCRMAsyncService with user account CRM_ADMIN@WINGTIP.COM. The reason was: Checking domain DC=wingtip,DC=com
.   at Microsoft.Crm.Setup.Server.Core.ServicePrincipalNameManager.SetServicePrincipalName(String serviceClassName, String machineName, String serviceAccountName, Boolean isWebService)
   at Microsoft.Crm.Setup.Server.Core.SetServicePrincipalNameAction.Do(IDictionary parameters)
   at Microsoft.Crm.Setup.Shared.CrmAction.ExecuteAction(CrmAction action, IDictionary parameters, Boolean undo)
   --- End of inner exception stack trace ---, Error, AbortRetryIgnore, Option1
12:09:37|   Info| InputResult: Ignore

Pay attention to the Bold Italic part this is the command that doesn’t work. I replaced it with: SETSPN -a MSCRMAsyncService/Wingtipserver CRM_Admin which seems to work.

After the installation of CRM, the wizard will try to run the “CRM Reporting Extentions” These Extentions require that the reporting Services are running using a Domain Account so if your Reporting Services are running using the default ReportServices Account. The installation will not succeed. The account can be changed by running the reporting services configuration manager and setting the account to a domain account instead of the default ReportService account.

Hope that these small hints will help you to install CRM 2015.

Thursday, February 26, 2015

SharePoint Search using Keyword Queries and Result Sources

Working on a SharePoint 2013 Farm solution I found that there are many blogs out there that state that we should use the "query.SourceId" to indicate which source should be used by the Keyword query.

Now the SourceId is a Guid and can be found by asking the search service proxy. To get the search service proxy the developer would need to create some mechanism to reverence the proxy by its name. This is all server side and not the way to do it.


 

There is a much better way of pointing towards a Result source.

The Keyword query supports two properties for this the "SourceName" and the "SourceLevel" properties.

Here is an example of how these can be used in a keyword query to use a Result Source named "NewsItems"


 

SPSite site = SPContext.Current.Site;

KeywordQuery query = new
KeywordQuery();

query.SelectProperties.Clear();

string[] propertiesToReturn = { "ListItemID", "Title", "Created", "LastModifiedTime", "ExpirationTime", "Contents", "WebId", "SPSiteURL" };

query.SelectProperties.AddRange(propertiesToReturn);

query.Properties["SourceName"] = "NewsItems";

query.Properties["SourceLevel"] = "SPSiteSubscription";

query.RowLimit = int.Parse(ParentWebPart.PageSize);


 

SearchExecutor searchExecutor = new
SearchExecutor();

ResultTableCollection resultTableCollection = searchExecutor.ExecuteQuery(query);

var resultTables = resultTableCollection.Filter("TableType", KnownTableTypes.RelevantResults);

var resultTable = resultTables.FirstOrDefault();

DataTable results = resultTable.Table;


 

foreach (DataRow row in results.Rows)

{

System.Diagnostics.Debug.Writeline( row["Title"].ToString());

}    

Friday, February 13, 2015

ASP.NET Classic mode and the magic control ID’s

Having worked in a MVC environment returning to a project that uses ASP Server Controls is rather painful for a multitude of reasons. The one that hurts the most is the fact that asp.net controls generate their own client side ids. In doing that they really destroy the fluent communication between server side and client side code.

Pinpointing the asp.net control and manipulating it using standard libraries like JQuery isn't as easy as it should be.

But now with SharePoint 2013 we have the .NET 4.0 Framework and with that comes the option to tell ASP.NET to keep his hands off of the control id's.

This is the magic word: ClientIDMode='Static'

With the clientidmode set to static we are able to set the server id and asp.net will leave it the way we set is. The developer has the responsibility to make sure that every id is unique

Friday, January 30, 2015

Manually created SharePoint List definitions using XML a bad practice.

While developing SharePoint solutions using Visual Studio we can create lists and views using XML. When we choose to use this we can use a visual editor within Visual Studio. It is nice looks like this.


This editor will create the list definition and it will do so correctly. However the editor is limited in it's functionality.
For instance in the example above the fields displayed are from an external list, the editor can't cope with that. Forcing the developer to manually edit the XML.

This is where thing go wrong, manually edited xml definitions are not validated they are just consumed by SharePoint. Here is a real life example of a View I found.

<ViewFields>
  <!--<FieldRef Name="LinTitle"></FieldRef>-->
<FieldRef Name="TestKolom" /><FieldRef Name="ConcernNaam" /></ViewFields>

 
 

The "TestKolom" field did not exist within the project, but the definition got imported and applied by SharePoint. The result was that everything looked nice at first glance until a site administrator tried to edit the view on the list. He couldn't, at that point SharePoint raised an exception. The project had formal testers and a iterative process that would guaranty quality, they failed to find this problem.

Thinking about a solution making sure that problems like these are prevented. I can only come up with one do not edit the xml definitions manually.

  • Use the Visual Studio editor and learn to live with the fact that you cannot define everything.
    This will result in an increase in manual creation of lists and views using the SharePoint user interface and thus in the increase of installation manuals.
  • Use code to create lists and their views.
    When using code it is much harder to introduce errors in list definitions. Taking the example above I would not be able to add a non existing field to a view using code. The down side of creating list definitions in code is that it generally takes more time to create the list.

Edit 13-2-2015: Having spoken to Frank Marasco on the Office 365 Summit in Amsterdam, it is really only option 2 that is really viable. Just don't do XML definitions anymore, because these don't upgrade. Just learn how to do it the proper way and use Code

Wednesday, January 21, 2015

Runtime Exception building a solution from Visual Studio

Today I encountered the situation in which one of my assemblies gave the following exception

MissingFieldException was unhandled
An unhandled exception of type 'System.MissingFieldException' occurred in ...
Additional information: Field not found:

Visual studio gave the following hint: if a field in a class library has been removed, recompile any assemblies that reference that library.

After adding a new method to the failing assembly and calling it as expected I got the following exception: An unhandled exception of type 'System.MissingMethodException' occurred in

Somehow Visual Studio did not update the assembly, it may be that Visual Studio did not detect that the assembly changed or the assembly got locked and visual studio couldn’t update the assembly.

The solution was: Removing the assembly from the GAC C:\Windows\Microsoft.NET\assembly\GAC_MSIL

Monday, July 14, 2014

MVC 5 SharePoint 2013 and Data Annotations

 

Within MVC they have this great way of validation, they specify the restrains the data has to abide to on the model class. Unfortunately most data annotations that are available out of the box do not support SharePoint as backend data system. So You will soon find yourself in need to develop your own data annotations. To make live a little easier on you here is an example on how to make one. The example data annotation will test the list field combination to make sure that the value entered is unique.

using Microsoft.SharePoint.Client;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;

namespace System.ComponentModel.DataAnnotations
{
public sealed class UniqueValueAttribute : ValidationAttribute
{
private const string _defaultErrorMessage = "'{0}' moet uniek zijn";
private string _listName;
private string _fieldName;
private string _errorMessage;

public UniqueValueAttribute(string ListName, string FieldName) : base(_defaultErrorMessage)
{
_listName = ListName;
_fieldName = FieldName;
_errorMessage = _defaultErrorMessage;
}

public UniqueValueAttribute(string ListName, string FieldName, string ErrorMessage):base(ErrorMessage)
{
_listName = ListName;
_fieldName = FieldName;
_errorMessage = ErrorMessage;
}

public override string FormatErrorMessage(string name)
{
return string.Format(_errorMessage, name);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null)
return null;

string result = null;
SharePointContext spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext.Current);
if (spContext == null)
return new ValidationResult("SharePoint context was not found. Did you leave this screen open for a long time?");

using (ClientContext context = spContext.CreateAppOnlyClientContextForSPHost())
{
if (context != null)
{
var lst = context.Web.Lists.GetByTitle(_listName);
if (lst == null)
{
result =
String.Format("List name {0} could not be found in the SharePoint Web", _listName);
throw new ValidationException(result);
}
else
{
CamlQuery query = new CamlQuery();
string whereXml = @"<Where><Eq><FieldRef Name='{0}'></FieldRef><Value Type='Text'>{1}</Value></Eq></Where>";
whereXml =
String.Format(whereXml, _fieldName, value.ToString());
string viewXml = "<View><Query>{0}</Query></View>";
query.ViewXml =
String.Format(viewXml, whereXml);
ListItemCollection queryResult = lst.GetItems(query);
context.Load(queryResult);
context.ExecuteQuery();
if (queryResult != null && queryResult.Count > 0)
{
result =
String.Format(_errorMessage, _fieldName);
return new ValidationResult(result);
}
return null;
}
}
else
{
return new ValidationResult("No sharePoint context");
}
}
}
}
}

The use of the DataAnnotation is the same as it allways was in MVC just place the attribute on top of the property that needs the validation.


[UniqueValueAttribute("Foxy Documents", "Title", "De kamervraag met deze naam bestaat al")]
[Display(Name= "Naam")]
[Required(ErrorMessage="De naam van de Kamervraag is verplicht")]
public string Titel
{
get;
set;
}

To make sure that datetime fields where not defined in the past I also created a data annotation to validate datetime fields. I Noticed that there weren't many working answers on the forums so here you go

 

    public class DateTimeLargerThenNowMinus : ValidationAttribute
{
private const string _defaultErrorMessage = "Date is not allowed";
private string _errorMessage;
private DateTime _validationDate;

public DateTimeLargerThenNowMinus(int days=0, int months=0, int years=0, string ErrorMessage=_defaultErrorMessage)
:
base(ErrorMessage)
{
_validationDate =
DateTime.Now.AddDays(days * -1);
_validationDate = _validationDate.AddMonths(months * -1);
_validationDate = _validationDate.AddYears(years * -1);
_errorMessage = ErrorMessage;
}

public override string FormatErrorMessage(string name)
{
return string.Format(_errorMessage, name);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null)
return null;

var date = (DateTime)value;
if (date > _validationDate)
return null;
else
{
return new ValidationResult(_errorMessage);
}
}
}

Thursday, July 10, 2014

SharePoint 2013 X-FrameOptions in MVC 5

 

Modern browsers do not allow pages to be displayed within I-Frames by default. The idea of this security measure is to prevent ‘click-jacking’. Click Jacking is best explained by an example: Lets say you are working for a bank and create an internet page. Now a hacker can try to display your banking page in an I-Frame on his own site and adds a little extra functionality a key logger and a click jacker. With doing this the hacker will try to capture all the users actions and will potentially be able to get the users password.

This is why modern browsers prevent pages to be displayed in I-Frames by default.

However, within the secured confines of a SharePoint communicating with a on premise provider hosted app this security measure can be a hindrance.  Because SharePoint won’t be allowed to show the provider hosted pages in I-Frames. The good news, the developer of the pages can turn the security measure off for his application by stating that displaying the pages in I-Frames is allowed. This can be done with the ‘SuppressXFrameOptionsHeader’  in the Global.asax.

    public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);

AntiForgeryConfig.SuppressXFrameOptionsHeader = true;
}
}
This option was a default setting in MVC 4 however in the MVC 5 template for SharePoint this option needs to be added manually.