Showing posts with label Sitecore. Show all posts
Showing posts with label Sitecore. Show all posts

Monday, September 30, 2019

Override xaml.xml files in the ~/sitecore/shell/override folder

Occasionally you'll have the desire to add your own custom functionality to dialogs in the Content Editor.  Many of these dialogs can be found under the ~/sitecore/shell/ folder.

Sitecore already comes pre-configured to look in the ~/sitecore/shell/override to find override XmlControls under the node.  That is baked into the Sitecore.config file.

<controlSources>
  <source mode="on" namespace="Sitecore.Web.UI.XmlControls" folder="/sitecore/shell/override" deep="true"/>

This allows you to drop into that folder your customized .xml dialog file that contains your own layout and custom CodeBeside.  The Content Editor will load that one instead of the Sitecore coded one.

However some of the controls under the ~/sitecore/shell/ folder are built with a different technology.

(To read more about the two different control types, Mark Stiles has done an amazing job at documenting them: https://markstiles.net/blog/2014/1/5/sheer-ui-1-a-tale-of-two-systems/)

The extension of these controls is xaml.xml.  Sitecore defines where it finds these controls in the xamlSharp.config file.  Oddly enough, this one doesn't include the  ~/sitecore/shell/override folder.

It is simple to create your own override config for this in Sitecore.

1. Create a new file called xamlSharpOverride.config

2. It should contain the following:
<configuration>
  <sitecore>
    <xamlsharp>
      <sources>
        <source type="Sitecore.Web.UI.XamlSharp.Xaml.XamlFileControlSource,Sitecore.Kernel" patch:before="*">
          <watchers hint="list:AddWatcher">
            <watcher type="Sitecore.Web.UI.XamlSharp.Xaml.XamlFileWatcher,Sitecore.Kernel">
              <folder>/sitecore/shell/override</folder>
              <filter>*.xaml.xml</filter>
              <codefilter>*.xaml.xml.cs</codefilter>
              <includesubdirectories>true</includesubdirectories>
            </watcher>
          </watchers>
        </source>
      </sources>
    </xamlsharp>
  </sitecore>
</configuration>

This config will insert that source folder at the top of the xamlSharp sources list.  Any .xaml.xml file you customize can now be placed into the ~/sitecore/shell/override folder.

Friday, January 6, 2017

Why do I have a binding for SitecoreApplicationCenter in my web.config?

I was doing some cleanup in our config files, and I noticed we have a binding called "SitecoreApplicationCenter".  Looking through our Sitecore solution, I didn't see this referred to anywhere, until I opened the Sitecore.Kernel.dll.

This reference is used when communicating with Sitecore's App Center... just like it's named.  It will be used when making calls to the service: https://apps.sitecore.net/appsservice.asmx

We'll never use this on our frontend servers, so I transformed it out of that config.  But I kept it in our Content Manager server config.

Friday, July 29, 2016

Careful with Sitecore Icon's Recent Items List


It is tempting to select that recent icon when choosing an icon for your new template.  However, when you package up this template and deploy it to a new server, the path to that icon may be invalid.

I installed a package that I built onto a different server, and most of my icons never loaded.


When I checked the path to those icons in the package file items, I noticed they all were pointing to a temporary IconCache folder.


So, I changed all my icons in my original templates, by finding them through the Icon tab.  Now all of my paths to the icons were valid and relative to where Sitecore installs them by default, not where they may get temporarily cached.

Thursday, April 14, 2016

Quick View of Active Users on the Sitecore Content Manager Server

Before you perform upgrades of your Sitecore deployment, it is useful to know if anyone is active on the system.  Also, if you need to do your upgrade now, you want to know who to inform that you will be disrupting them.

This is extremely common when you have a global site and content editors have working hours 24 hours long.

Depending on the version of Sitecore you are running, you can see the active users from these links:

Prior to 8.x
http://cm.sitcore/sitecore/shell/Applications/Login/Users/Kick.aspx

8.x (MVC page)
http://cm.sitcore/sitecore/client/Applications/LicenseOptions/KickUser


A coworker realized that the LaunchPad in 8.x is a convenient place to add a link for admins to see active users.  You don't need to keep a bookmark in each of your browsers anymore.

Connect to the Core database and add a new LaunchPad-Button under the path /sitecore/client/Applications/Launchpad/PageSettings/Buttons/Security.

Give it some Text, choose an Icon, and set the Link to the KickUser page.

That's it.



Presumably you'll already have security set correctly at a higher level.  So, this should appear for the same Sitecore users as would already have access to user management.



Friday, April 1, 2016

Hosting multiple client side telemetry Application Insight endpoints on same domain


We have a website where one division in the company controls and builds the main content of our website, and another division deals with the online ordering cart system.  We each host our websites on separate servers, but they are served up under the same domain name and we share cookies.  The routing to the correct server is all handled up-stream by our network team, so it is seamless to the developers and our users.

The division that handles the online cart has already installed AppInsights and have been collecting client-side javascript telemetry data for a while now.  I work on the other content side of the website and we want to collect our own telemetry.

Looking at the code, the tracking logic and config is loaded and saved to a global variable: window.appInsights.  That variable is reloaded every time a page is requested and the trackPageView() is called when the page loads.

If we were to install the ApplicationInsights client code in our part of the website, which is the typical entry point into our website, then the window.appInsights object will be initialized by us and use our instrumentationKey.

To prevent our collection from replacing theirs, we updated the client code to set our own global variable.

OLD:
<script type="text/javascript">
  var appInsights = window.appInsights || function (config) { 
  ... 
  window.appInsights = appInsights; 
  appInsights.trackPageView(); 
</script>

NEW:
<script type="text/javascript">
  var appInsights = window.appInsightsSitecore || function (config) { 
  ... 
  window.appInsightsSitecore = appInsights; 
  appInsights.trackPageView(); 
</script>

Application Insights on Sitecore – Filtering the SQL telemetry


Microsoft Application Insights is a great solution to monitor telemetry data from your Sitecore installs.

The only problem is that if you enable all of the normal telemetry modules, you’ll end up flooding your data points with SQL calls.  There are thousands of SQL calls every minute in an average Sitecore database (especially in the EventQueue table).
We wanted to filter out all of those SQL calls, because we have not seen performance issues with Sitecore and SQL in our setup.


This requires at least v2.0 of the AppInsights SDK.  And then you need to create a custom filter.  I followed an example from the AppInsights documention.

namespace Sitecore.Website.AppInsights.Filters

    public class SQLFilter : ITelemetryProcessor
    {
        private ITelemetryProcessor Next { get; set; }

        // Link processors to each other in a chain.
        public SQLFilter(ITelemetryProcessor next)
        {
            this.Next = next;
        }
        public void Process(ITelemetry item)
        {
            // To filter out an item, just return 
            if (!OKtoSend(item)) { return; }

            this.Next.Process(item);
        }

        // Example: replace with your own criteria.
        private bool OKtoSend(ITelemetry item)
        {
            var dependency = item as DependencyTelemetry;
            if (dependency != null
                && dependency.DependencyKind == "SQL")
            {
                return false;
            }

            return true;
        }
    }
}

Once you have your filter class built, you need to add it to the Process 
pipeline of AppInsights in your ApplicationInsights.config file.

<TelemetryProcessors>
  <Add Type="Sitecore.Website.AppInsights.Filters.SQLFilter, Sitecore.Website" />
</TelemetryProcessors>

That’s it!  This prevents any SQL calls from flooding your AppInsights Azure
resource.

If collecting some of this SQL data is important to you, you could also look 
into the Sampling features of the SDK, which allows you to throttle the 
data that is sent.

Another option is to inspect the CommandName property and possibly just filter out the chattiness to the EventQueue table, but allow the other commands through.  I chose not to do this for now, because the goal is to fail fast in this processor, so not to cause a lot of extra logic to happen on the data collection and slow things down.  If you decide to do the extra logic in here to allow some of the SQL data through, make sure to order you conditional tests correctly, so if the DependencyTelemetry object is not a SQL kind, short-circuit and skip testing the other conditionals.

Monday, March 28, 2016

Object reference not set error when launching Sitecore Path Analyzer

image
When I try to run Path Analyzer for the first time, I get an error in the MapSelector.cshtml file, line 41: Object reference not set to an instance of an object.

helper.MakeTreeDefinition("TreeDefinitionFilter", fieldsWrapper, selectedTreeDefinitionId.ToString(), mapRootId.ToString());

UPDATE 4/5/2016: Sandip Patel found the actual solution to this problem.  Please read his solution to fix your Sitecore install.

The issue, in my case, was with the mapRootId object.  That parameter is empty when launching the Path Analyzer, so the function that looks for this value finds nothing.  This happens a few lines earlier, on line 32: 

var mapRootId = parametersHelper.GetMapRootId();

The main problem is that GetMapRootId() is retuning a null value from this command, when that param doesn’t exist.  You can see this by decompiling Sitecore.PathAnalyzer.Client.dll.  Instead, what that command should be doing is returning Sitecore.Data.ID.Null.

A quick fix for this is to update your MapSelector.cshtml file’s line 32 to:

var mapRootId = parametersHelper.GetMapRootId() ?? Sitecore.Data.ID.Null;


This is not a great long term solution, since any Sitecore update may replace this change.  However, one would expect that there will be an updated version of this page that does not rely on Silverlight in the future anyways, so this can work as a temporary patch for you.


The full text of the error is:
Server Error in '/' Application.

Object reference not set to an instance of an object.
Description: An unhandled exception occurred.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Source Error: 

Line 39:                     helper.MakeBorder("FieldsWrapper", filterToggleButton, fieldsWrapper =>
Line 40:                     {
Line 41:                         helper.MakeTreeDefinition("TreeDefinitionFilter", fieldsWrapper, selectedTreeDefinitionId.ToString(), mapRootId.ToString());
Line 42:
Line 43:                         helper.MakeDateRange("DateRangeFilter", fieldsWrapper, true, initialStartDate, initialEndDate, datePreset);
Stack Trace: 

[NullReferenceException: Object reference not set to an instance of an object.]
   ASP.<>c__DisplayClassa.b__5(String fieldsWrapper) in c:\inetpub\Sitecore\Website\sitecore\shell\client\Applications\PathAnalyzer\Common\Layouts\Renderings\MapSelector.cshtml:41
   Sitecore.PathAnalyzer.Client.Sitecore.Shell.Client.Applications.PathAnalyzer.Common.Layouts.Renderings.Shared.RenderingHelper.MakeBorder(String controlId, String parent, Action`1 nextControl, String isVisible, Boolean usePadding, String contentAlignment) +43
   ASP.<>c__DisplayClassa.b__4(String filterToggleButton) in c:\inetpub\Sitecore\Website\sitecore\shell\client\Applications\PathAnalyzer\Common\Layouts\Renderings\MapSelector.cshtml:39
   Sitecore.PathAnalyzer.Client.Sitecore.Shell.Client.Applications.PathAnalyzer.Common.Layouts.Renderings.Shared.RenderingHelper.MakeDropDownButton(String controlId, String parent, String textDictionaryKey, Boolean showArrow, Action`1 nextControl) +54
   ASP.<>c__DisplayClassa.b__3(String filterToggleButtonColumnPanel) in c:\inetpub\Sitecore\Website\sitecore\shell\client\Applications\PathAnalyzer\Common\Layouts\Renderings\MapSelector.cshtml:37
   Sitecore.PathAnalyzer.Client.Sitecore.Shell.Client.Applications.PathAnalyzer.Common.Layouts.Renderings.Shared.RenderingHelper.MakeColumn(String controlId, String parent, Int32 gridColumns, Action`1 nextControl) +52
   ASP.<>c__DisplayClassa.b__2(String rowPanel) in c:\inetpub\Sitecore\Website\sitecore\shell\client\Applications\PathAnalyzer\Common\Layouts\Renderings\MapSelector.cshtml:36
   Sitecore.PathAnalyzer.Client.Sitecore.Shell.Client.Applications.PathAnalyzer.Common.Layouts.Renderings.Shared.RenderingHelper.MakeRow(String controlId, String parent, Boolean usePadding, Action`1 nextControl) +52
   ASP.<>c__DisplayClassa.b__1(String contentWrapper) in c:\inetpub\Sitecore\Website\sitecore\shell\client\Applications\PathAnalyzer\Common\Layouts\Renderings\MapSelector.cshtml:35
   Sitecore.PathAnalyzer.Client.Sitecore.Shell.Client.Applications.PathAnalyzer.Common.Layouts.Renderings.Shared.RenderingHelper.MakeBorder(String controlId, String parent, Action`1 nextControl, String isVisible, Boolean usePadding, String contentAlignment) +43
   ASP.<>c__DisplayClass8.b__0(TextWriter __razor_helper_writer) in c:\inetpub\Sitecore\Website\sitecore\shell\client\Applications\PathAnalyzer\Common\Layouts\Renderings\MapSelector.cshtml:34
   System.Web.WebPages.WebPageBase.Write(HelperResult result) +85
   ASP._Page_sitecore_shell_client_Applications_PathAnalyzer_Common_Layouts_Renderings_MapSelector_cshtml.Execute() in c:\inetpub\Sitecore\Website\sitecore\shell\client\Applications\PathAnalyzer\Common\Layouts\Renderings\MapSelector.cshtml:18
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +234
   System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +123
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +121
   System.Web.Mvc.Html.PartialExtensions.Partial(HtmlHelper htmlHelper, String partialViewName, Object model, ViewDataDictionary viewData) +126
   Sitecore.Mvc.Presentation.ViewRenderer.Render(TextWriter writer) +220
[InvalidOperationException: Error while rendering view: '/sitecore/shell/client/Applications/PathAnalyzer/Common/Layouts/Renderings/MapSelector.cshtml' (model: 'Sitecore.Mvc.Presentation.RenderingModel, Sitecore.Mvc').
]
   Sitecore.Mvc.Presentation.ViewRenderer.Render(TextWriter writer) +704
   Sitecore.Mvc.Pipelines.Response.RenderRendering.ExecuteRenderer.Render(Renderer renderer, TextWriter writer, RenderRenderingArgs args) +31
   Sitecore.Mvc.Pipelines.Response.RenderRendering.ExecuteRenderer.Process(RenderRenderingArgs args) +75
   (Object , Object[] ) +74
   Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args) +480
   Sitecore.Mvc.Pipelines.PipelineService.RunPipeline(String pipelineName, TArgs args) +184
   Sitecore.Mvc.Pipelines.Response.RenderPlaceholder.PerformRendering.Render(String placeholderName, TextWriter writer, RenderPlaceholderArgs args) +224
   (Object , Object[] ) +74
   Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args) +480
   Sitecore.Mvc.Pipelines.PipelineService.RunPipeline(String pipelineName, TArgs args) +184
   Sitecore.Mvc.Helpers.SitecoreHelper.Placeholder(String placeholderName) +267
   Sitecore.Web.UI.Controls.Containers.Borders.Border.RenderPlaceHolder(HtmlTextWriter output) +59
   Sitecore.Web.UI.Controls.Containers.Borders.Border.Render(HtmlTextWriter output) +86
   Sitecore.Web.UI.Controls.ComponentBase.Render() +127
   Sitecore.Web.UI.Controls.Containers.Borders.ControlsExtension.Border(Controls controls, Rendering rendering) +112
   ASP._Page_sitecore_shell_client_Business_Component_Library_Layouts_Renderings_Containers_Borders_Border_cshtml.Execute() in c:\inetpub\Sitecore\Website\sitecore\shell\client\Business Component Library\Layouts\Renderings\Containers\Borders\Border.cshtml:4
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +235
   System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +124
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +122
   System.Web.Mvc.Html.PartialExtensions.Partial(HtmlHelper htmlHelper, String partialViewName, Object model, ViewDataDictionary viewData) +127
   Sitecore.Mvc.Presentation.ViewRenderer.Render(TextWriter writer) +221
[InvalidOperationException: Error while rendering view: '/sitecore/shell/client/Business Component Library/Layouts/Renderings/Containers/Borders/Border.cshtml' (model: 'Sitecore.Mvc.Presentation.RenderingModel, Sitecore.Mvc').
]
   Sitecore.Mvc.Presentation.ViewRenderer.Render(TextWriter writer) +704
   Sitecore.Mvc.Pipelines.Response.RenderRendering.ExecuteRenderer.Render(Renderer renderer, TextWriter writer, RenderRenderingArgs args) +31
   Sitecore.Mvc.Pipelines.Response.RenderRendering.ExecuteRenderer.Process(RenderRenderingArgs args) +75
   (Object , Object[] ) +74
   Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args) +480
   Sitecore.Mvc.Pipelines.PipelineService.RunPipeline(String pipelineName, TArgs args) +184
   Sitecore.Mvc.Pipelines.Response.RenderPlaceholder.PerformRendering.Render(String placeholderName, TextWriter writer, RenderPlaceholderArgs args) +224
   (Object , Object[] ) +74
   Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args) +480
   Sitecore.Mvc.Pipelines.PipelineService.RunPipeline(String pipelineName, TArgs args) +184
   Sitecore.Mvc.Helpers.SitecoreHelper.Placeholder(String placeholderName) +267
   ASP._Page_sitecore_shell_client_Business_Component_Library_Layouts_Renderings_Structures_Substructures_ApplicationContentM_cshtml.Execute() in c:\inetpub\Sitecore\Website\sitecore\shell\client\Business Component Library\Layouts\Renderings\Structures\Substructures\ApplicationContentM.cshtml:7
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +235
   System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +124
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +122
   System.Web.Mvc.Html.PartialExtensions.Partial(HtmlHelper htmlHelper, String partialViewName, Object model, ViewDataDictionary viewData) +127
   Sitecore.Mvc.Presentation.ViewRenderer.Render(TextWriter writer) +221
[InvalidOperationException: Error while rendering view: '/sitecore/shell/client/Business Component Library/Layouts/Renderings/Structures/Substructures/ApplicationContentM.cshtml' (model: 'Sitecore.Mvc.Presentation.RenderingModel, Sitecore.Mvc').
]
   Sitecore.Mvc.Presentation.ViewRenderer.Render(TextWriter writer) +704
   Sitecore.Mvc.Pipelines.Response.RenderRendering.ExecuteRenderer.Render(Renderer renderer, TextWriter writer, RenderRenderingArgs args) +31
   Sitecore.Mvc.Pipelines.Response.RenderRendering.ExecuteRenderer.Process(RenderRenderingArgs args) +75
   (Object , Object[] ) +74
   Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args) +480
   Sitecore.Mvc.Pipelines.PipelineService.RunPipeline(String pipelineName, TArgs args) +184
   Sitecore.Mvc.Pipelines.Response.RenderPlaceholder.PerformRendering.Render(String placeholderName, TextWriter writer, RenderPlaceholderArgs args) +224
   (Object , Object[] ) +74
   Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args) +480
   Sitecore.Mvc.Pipelines.PipelineService.RunPipeline(String pipelineName, TArgs args) +184
   Sitecore.Mvc.Helpers.SitecoreHelper.Placeholder(String placeholderName) +267
   ASP._Page_sitecore_shell_client_Business_Component_Library_Layouts_Renderings_Structures_Page_Structures_Application_Dashboard_cshtml.Execute() in c:\inetpub\Sitecore\Website\sitecore\shell\client\Business Component Library\Layouts\Renderings\Structures\Page Structures\Application\Dashboard.cshtml:14
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +235
   System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +124
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +122
   System.Web.Mvc.Html.PartialExtensions.Partial(HtmlHelper htmlHelper, String partialViewName, Object model, ViewDataDictionary viewData) +127
   Sitecore.Mvc.Presentation.ViewRenderer.Render(TextWriter writer) +221
[InvalidOperationException: Error while rendering view: '/sitecore/shell/client/Business Component Library/Layouts/Renderings/Structures/Page Structures/Application/Dashboard.cshtml' (model: 'Sitecore.Mvc.Presentation.RenderingModel, Sitecore.Mvc').
]
   Sitecore.Mvc.Presentation.ViewRenderer.Render(TextWriter writer) +704
   Sitecore.Mvc.Pipelines.Response.RenderRendering.ExecuteRenderer.Render(Renderer renderer, TextWriter writer, RenderRenderingArgs args) +31
   Sitecore.Mvc.Pipelines.Response.RenderRendering.ExecuteRenderer.Process(RenderRenderingArgs args) +75
   (Object , Object[] ) +74
   Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args) +480
   Sitecore.Mvc.Pipelines.PipelineService.RunPipeline(String pipelineName, TArgs args) +184
   Sitecore.Mvc.Pipelines.Response.RenderPlaceholder.PerformRendering.Render(String placeholderName, TextWriter writer, RenderPlaceholderArgs args) +224
   (Object , Object[] ) +74
   Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args) +480
   Sitecore.Mvc.Pipelines.PipelineService.RunPipeline(String pipelineName, TArgs args) +184
   Sitecore.Mvc.Helpers.SitecoreHelper.Placeholder(String placeholderName) +267
   ASP._Page_sitecore_shell_client_Speak_Layouts_Layouts_Speak_Layout_cshtml.Execute() in c:\inetpub\Sitecore\Website\sitecore\shell\client\Speak\Layouts\Layouts\Speak-Layout.cshtml:28
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +235
   System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +124
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +122
   System.Web.Mvc.Html.PartialExtensions.Partial(HtmlHelper htmlHelper, String partialViewName, Object model, ViewDataDictionary viewData) +127
   Sitecore.Mvc.Presentation.ViewRenderer.Render(TextWriter writer) +221
[InvalidOperationException: Error while rendering view: '/sitecore/shell/client/Speak/Layouts/Layouts/Speak-Layout.cshtml' (model: 'Sitecore.Mvc.Presentation.RenderingModel, Sitecore.Mvc').
]
   Sitecore.Mvc.Presentation.ViewRenderer.Render(TextWriter writer) +704
   Sitecore.Mvc.Pipelines.Response.RenderRendering.ExecuteRenderer.Render(Renderer renderer, TextWriter writer, RenderRenderingArgs args) +31
   Sitecore.Mvc.Pipelines.Response.RenderRendering.ExecuteRenderer.Process(RenderRenderingArgs args) +75
   (Object , Object[] ) +74
   Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args) +480
   Sitecore.Mvc.Pipelines.PipelineService.RunPipeline(String pipelineName, TArgs args) +184
   Sitecore.Mvc.Presentation.RenderingView.Render(ViewContext viewContext, TextWriter writer) +321
   System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) +365
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +90
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +833
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +833
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +81
   System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +635

Thursday, March 17, 2016

Add a Pacemaker to your Sitecore

The topic of Sitecore scheduling agents and tasks have been blogged about in the past.  I often refer back to this post by John West:

To ensure your scheduled agents and tasks run at expected times, the Sitecore instance needs to be running.  If you have these configured on a Sitecore server that is used infrequently, there is a possibility your worker process will shutdown from inactivity.  There is an agent that runs in Sitecore already to keep the worker process alive (Sitecore.Tasks.UrlAgent).  But this process and any other Sitecore process will only run if the website is still active and running (i.e. the app. pool wasn't recycled).

An Application Pool can get recycled if you update the web.config, if you set your app. pool to recycle at a specific time, if your system gets rebooted after OS patches are installed, or other similar reasons.

In our specific instance, we have a content management server configured to run custom tasks that find Sitecore items with an expiration date/time field set and will email a report of those.  We also have our app. pool recycled overnight when no one would be on the system.  We found that rebuilding indexes and some other tasks really bloat the app. pool, and so recycling it automatically daily was a nice solution, instead of only doing it once things started running slow.

Our solution to this, was to create a Windows Scheduled Task that would hit the website periodically.  We take advantage of that lightweight page request that Sitecore uses already, /sitecore/service/keepalive.aspx.  Then we take advantage of Powershell's ability to access .NET assemblies and we can one-line a Url request:

powershell -ExecutionPolicy unrestricted -Command "(New-Object Net.WebClient).DownloadString(\"http://sitecoreCM.mydomain.com/sitecore/service/keepalive.aspx\")"

Finally, we setup our scheduled task to run daily.  And repeat the task every 20 minutes (NOTE: 20 minutes isn't a value available in the combobox, but you can just type in that text and the task scheduler will respect it).

You can set this up on any machine, although running it on the same server hosting your Sitecore web instance is recommended, so you can eliminate the need to keep another computer on and/or work around any network connectivity issues that may exist between those machines.