A Modern Small Business

When I first started working in an IT focused job, (as a paid intern for the Social Security Administration in Golden, Colorado, USA in May of 1994 as a high school student) my job involved a fair number of things. I was required to sync the local mail server, cc:Mail, which updated our branch office as well as downloaded emails from headquarters. This involved a 14400 baud modem and a fair amount of coordination with the office secretary so she would not pickup the phone or send a fax. The other Social Security employees used some type of main frame system (IBM 3270) to look up peoples data. I was also responsible for maintain some type of Lotus 1-2-3 spreadsheet but I can’t remember the data in it. These were held on multiple 5 1/4 disks that I had to load and work with.

Fast forward 21+ years, we have a different looking landscape. Mobile devices with amazing connectivity and performance. We crave for connections to people close by as well people far away. We anticipate data and creating and consuming it.

At the same time, there are lots of similarities. We have devices in front of us, with both local and remote sources of data, services running on systems in data centers and connections between them.

I started a new job about 6 months for a company that was less than two years old. I came to help with SharePoint, but again I hope to be more for them. This company made lots of decisions with systems, software and even people. I agree with some of their choices and some I don’t. I don’t know their industry very well so that most likely factored into their decisions where I differed.

After attending a conference on my day job around web development and SharePoint, I made a commitment to myself to write more blog posts and really build my brand. I also have some goals to do more than just SharePoint and I hope to use this vehicle to write about that as I do it. To accomplish this, I am going to write about building a modern small business that uses modern systems and keeps as much off premises (deferring to the cloud as much as possible) while explaining and documenting the process, choices and costs along the way. My first pass is going to focus on Microsoft technologies (Azure, Office 365, Accounting?/ HR?, IoT) just because I am more familiar with it. I hope to defer to open source solutions in some key areas and call out where there might be other choices and why. I hope people who read this point out other options and argue with me. Posts in this topic might stay here or move to other blog system.  I promise to not moderate discussions except when it gets spammy or crude. I hope people argue with me (constructively.)

I plan on forming a company to really push this journey to the extreme and call for discussions and questions and get help from the community.

So I think I am making my own IT director school?

My first Arduino project!

I made my first 100% original animated Halloween prop using #Arduino and #Makeblock libraries as well as ArduinoTimerObject

It moves on random but also has a IR remote so you can make it move on demand. My kids loved moving it when trick or treaters were at the door.

Code is here but looking for suggestions and feedback on the code since I know it needs it.

I noticed the random function that was built it did not really appear random so I used someone else’s  randomizer until I can understand why the standard random is broken. I found lots of references that it might be.

 

Also not powered but a rocking chair that rocks on its own, when I pull a rope I attached through some pulleys into my house. The fog machine added some drama..

Thoughts about outsorcing…

I normally don’t write about the business around the work I do but I read this and felt it showed almost the same experience my current employer seems to have with the same outsourced vendor. Hopefully my current employer can really look at the value and determine if our vendor is worth what we pay for but then still have tell them how to monitor our hardware and software and cover up failures left and right.

http://www.cringely.com/2012/06/14/an-it-labor-economics-lesson-from-memphis-for-ibm/

Report Builder 3.0–Application with the same identity already installed

Just a quick note, is if you are trying to use report builder, deployed via ClickOnce, in multiple environments (dev,test,prod) you will have to clean the ClickOnce cache by running, from a command line,:

rundll32 dfshim CleanOnlineAppCache

 

Thanks to:

http://www.wolffhaven45.com/blog/sccm/report-builder-3-0-fails-to-install/

SharePoint 2013 Search Result Types

While trying to deploy SharePoint 2013 Search Result Types through a feature, I was a bit struck at the lack of examples or documentation in that area. After a few days of looking at the code via JustDecompile or Reflector, I found key hiding in the SearchServiceApplicationProxy.AddResultItemType method. It takes a ResultItemType object that can be created from the Microsoft.Office.Server.Search.Administration name. It seems convoluted but you have to take a “copy” of the built in result type and the change the properities of one you want to deploy and then add it back to the collection of result types;

Eventually I want to expand this into a detailed post and get some feedback but here is what works as part of feature activation. A few points:

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.Office.Server.Search.Administration.Query;
using Microsoft.Office.Server.Search.Query.Rules;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.Office.Server.Search.Administration;
using KP.SP.Common;
using KP.SP.Common.Code;
using Microsoft.SharePoint.JSGrid;

namespace KP.SP.Search.Features.SearchEnhancement
{
/// <summary>
/// This class handles events raised during feature activation, deactivation, installation, uninstallation, and upgrade.
/// </summary>
/// <remarks>
/// The GUID attached to this class may be used during packaging and should not be modified.
/// </remarks>

[Guid("98362dff-c2b1-4cc8-b5b4-0133aec185bd")]
public class SearchEnhancementEventReceiver : SPFeatureReceiver
{
private const string LOCAL_SHAREPOINT_RESULTS = "Local SharePoint Results";
private const string LOCAL_PEOPLE_RESULTS = "Local People Results";
private const string EXISTING_PEOPLE_NAME_IN_SHAREPOINT_SEARCH = "People Name in Sharepoint Search";
private const string RESULT_TYPE_NAME = "Person KP";
private const string QUERY_RULE_NAME = "KP People";

private readonly string[] folderUrls = { "_catalogs/masterpage/Display Templates/Search" };

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
PrefixTrace.WriteLine("Begin");
try
{
var spSite = properties.Feature.Parent as SPSite;
if (spSite != null)
{
SPWeb rootWeb = spSite.RootWeb;
SPList gallery = spSite.GetCatalog(SPListTemplateType.MasterPageCatalog);
if (gallery != null)
{
SPListItemCollection folders = gallery.Folders;
string featureId = properties.Feature.Definition.Id.ToString();

foreach (string folderUrl in folderUrls)
{
SPFolder folder = FeatureHelpers.GetFolderByUrl(folders, folderUrl);
if (folder != null)
{
FeatureHelpers.PublishFiles(folder, featureId, "Published by Feature: KP Search Enhancements");
}
}
}

//http://dev.net.ua/blogs/ivanbilokon/archive/2012/11/22/11389.aspx
//http://mcnextpost.com/2013/10/30/les-conditions-dans-les-types-de-resultats-types-de-contenus-ou-types-de-contenus/
SecurityHelper.AnonymousSafeRunWithElevatedPrivileges(spSite.ID, rootWeb.ID, (elevatedSPSite, elevatedSPWeb) =>
{
DeployResultType(elevatedSPSite, elevatedSPWeb);

DeployQueryRule(elevatedSPSite, elevatedSPWeb);
});
}
}
catch (Exception ex)
{
PrefixTrace.WriteLine(ex.ToString());
throw new SPException(ex.ToString());
}
PrefixTrace.WriteLine("End");
base.FeatureActivated(properties);
}

private static void DeployResultType(SPSite spSite, SPWeb spWeb)
{
PrefixTrace.WriteLine("Begin deploy search result types");
try
{
SPServiceContext spServiceContext = null;
SPServiceApplicationProxy spServiceApplicationProxy = null;
SearchServiceApplicationProxy searchServiceApplicationProxy = null;

if (!ValidateAndGetSearchConnectors(spSite, out spServiceContext, out spServiceApplicationProxy, out searchServiceApplicationProxy))
{
throw new SPException("Unable to locate appropriate service and search connectors");
}

var resultItemTypeManager = new ResultItemTypeManager(searchServiceApplicationProxy);
var searchObjectOwner = new SearchObjectOwner(SearchObjectLevel.SPWeb, spWeb);
var resultItemTypes = resultItemTypeManager.GetResultItemTypes(searchObjectOwner, true);
var personResultType =
(from result in resultItemTypes
where result.BuiltIn
where result.Name == "Person"
select result).FirstOrDefault();

if (personResultType != null)
{
foreach (var resultType in searchServiceApplicationProxy.GetResultItemTypes(null, null, searchObjectOwner, false))
{
if (resultType.Name == RESULT_TYPE_NAME)
{
PrefixTrace.WriteLine(string.Format("Deleting existing result type named {0}...", RESULT_TYPE_NAME));
searchServiceApplicationProxy.DeleteResultItemType(resultType);
}
}
ResultItemType personKPResultItemType = new ResultItemType(personResultType, searchObjectOwner);
personKPResultItemType.Name = RESULT_TYPE_NAME;
personKPResultItemType.DisplayTemplateUrl = "~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Person_KP.js";
personKPResultItemType.RulePriority = 1;
searchServiceApplicationProxy.AddResultItemType(personKPResultItemType);
}
else
{
throw new ObjectNotFoundException("Unable to locate BuiltIn Person ResultItemType");
}
}

catch (Exception ex)
{
PrefixTrace.WriteLine(string.Format("Unable to deploy search template! {0}",ex));
throw;
}
}

private static void DeployQueryRule(SPSite spSite, SPWeb spWeb)
{
PrefixTrace.WriteLine("Begin deploy KP query rule");
try
{
SPServiceContext spServiceContext = null;
SPServiceApplicationProxy spServiceApplicationProxy = null;
SearchServiceApplicationProxy searchServiceApplicationProxy = null;

if (!ValidateAndGetSearchConnectors(spSite, out spServiceContext, out spServiceApplicationProxy, out searchServiceApplicationProxy))
{
throw new SPException("Unable to locate appropriate service and search connectors");
}

var qrm = new QueryRuleManager(searchServiceApplicationProxy);
var federationManager = new FederationManager(searchServiceApplicationProxy);
var searchObjectOwner = new SearchObjectOwner(SearchObjectLevel.SPWeb, spWeb);
var searchObjectFilter = new SearchObjectFilter(searchObjectOwner);
var queryRules = qrm.GetQueryRules(searchObjectFilter);

foreach (QueryRule rule in queryRules.Where(rule => rule.DisplayName == QUERY_RULE_NAME))
{
PrefixTrace.WriteLine(string.Format("Deleting existing query rule named {0}...", QUERY_RULE_NAME));
queryRules.RemoveQueryRule(rule);
break;
}

queryRules = qrm.GetQueryRules(searchObjectFilter);

// Create a new rule as a active one.
var queryRule = queryRules.CreateQueryRule(QUERY_RULE_NAME, null, null, true);
foreach (var qc in queryRule.QueryConditions)
{
queryRule.QueryConditions.Remove(qc);
}

var localSharePointResults = ValidateAndGetResultSource(federationManager, LOCAL_SHAREPOINT_RESULTS, searchObjectOwner);
var localPeopleResults = ValidateAndGetResultSource(federationManager, LOCAL_PEOPLE_RESULTS, searchObjectOwner);

queryRule.CreateSourceContextCondition(localSharePointResults);

queryRule.CreateQueryAction(QueryActionType.CreateResultBlock);
if (queryRule.CreateResultBlockActions != null && queryRule.CreateResultBlockActions.Count > 0)
{
var crba = queryRule.CreateResultBlockActions[0];
if (crba != null)
{
if (crba.QueryTransform.OverrideProperties == null)
{
crba.QueryTransform.OverrideProperties = new QueryTransformProperties();
}
crba.QueryTransform.OverrideProperties["RowLimit"] = 2;
crba.QueryTransform.QueryTemplate = "{subjectTerms}";
crba.QueryTransform.SourceId = localPeopleResults.Id;
if (!crba.ResultTitle.ContainsLCID(1033))
{
crba.ResultTitle.Add(1033, "KP People Results for \"{subjectTerms}\"");
}
crba.ResultTitleUrl = "peopleresults.aspx?k={subjectTerms}";
crba.GroupTemplateId = "~sitecollection/_catalogs/masterpage/Display Templates/Search/Group_Default.js";
crba.ItemTemplateId = "~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Person_KP.js";
crba.AlwaysShow = true;
crba.EnableOnAllPages = true;
crba.EnableOnRefinement = true;
}
}
queryRule.Update();

//deactive exiting people search query
UpdateRuleActiveFlag(qrm.GetQueryRules(searchObjectFilter), EXISTING_PEOPLE_NAME_IN_SHAREPOINT_SEARCH, false);
}
catch (Exception ex)
{
PrefixTrace.WriteLine(string.Format("Unable to KP query rule! {0}",ex));
throw;
}
PrefixTrace.WriteLine("End deploy KP query rule");
}

private static bool ValidateAndGetSearchConnectors(SPSite spSite, out SPServiceContext spServiceContext, out SPServiceApplicationProxy spServiceAppProxy, out SearchServiceApplicationProxy spSearchAppProxy)
{
SPServiceContext spsc = SPServiceContext.GetContext(spSite);
if (spsc == null)
{
throw new ObjectNotFoundException("Unable to locate Service Context");
}
SPServiceApplicationProxy proxy = spsc.GetDefaultProxy(typeof (SearchServiceApplicationProxy));
if (proxy == null)
{
throw new ObjectNotFoundException("Unable to locate Search Service");
}

SearchServiceApplicationProxy searchAppProxy = proxy as SearchServiceApplicationProxy;
if (searchAppProxy == null)
{
throw new ObjectNotFoundException("Unable to locate Search Service Proxy");
}

spServiceContext = spsc;
spServiceAppProxy = proxy;
spSearchAppProxy = searchAppProxy;
return true;
}

private static void UpdateRuleActiveFlag(IEnumerable<QueryRule> queryRules, string ruleName, bool ruleIsActive)
{
foreach (QueryRule rule in queryRules.Where(rule => rule.DisplayName == ruleName))
{
PrefixTrace.WriteLine(string.Format("Deactivating existing query rule named {0}...", EXISTING_PEOPLE_NAME_IN_SHAREPOINT_SEARCH));
rule.IsActive = ruleIsActive;
rule.UpdateActiveStatus();
break;
}
}

private static Source ValidateAndGetResultSource(FederationManager federationManager, string resultSourceName, SearchObjectOwner searchObjectOwner)
{
var localSharePointResults = federationManager.GetSourceByName(resultSourceName, searchObjectOwner);
if (localSharePointResults == null)
{
throw new Exception(string.Format("Unable to locate source {0}", resultSourceName));
}
return localSharePointResults;
}

}

}