In the time I have been developing in Sitecore I have seen many different approaches to caching, but only once have I seen someone actually use caching based on Sitecore's cache service. It is in fact almost how scary it is to implement.
Basically all you have to do is create a class that inherits from Sitecore.Caching.CustomCache. Of course you need to implement a constructor that calls the base constructor, and override the SetObject and GetObject methods. That is pretty much it though.
This cache will be handled in the same admin page as the other Sitecore caches, and if you install the Cache Admin or Cache Manager shared source modules you can easily use them to monitor and manage the caches without needing to build your own custom admin page.
I highly recommend at least investigating this approach to anyone considering putting in a custom cache on their site. It is efficient enough for most scenarios as well as being very easy to setup potentially saving you hours of coding.
Friday, July 31, 2015
Monday, June 29, 2015
Getting Started with Sitecore Content Search using Lucene or Solr
Using Sitecore Content Search with Solr is basically
identical to that with Lucene at the general use level, with the exception of
the configuration. Solr does have some
more advanced features that Lucene does not have.
To get started though with either
all we need is a search context. We can
get this using the context item (or any item for that matter). We simply cast the item to the
SitecoreIndexableItem class and pass it into ContentSearchManager.CreatesearchContext
and that will give us our context.
This is generally set up
as:
using (var context = ContentSearchManager.CreateSearchContext((SitecoreIndexableItem)item)){
//search code….
}
using (var context = ContentSearchManager.CreateSearchContext((SitecoreIndexableItem)item)){
//search code….
}
From inside this context we can
get our search results by calling the generic method GetQueryable that takes
all types that derive from SearchResultItem on our context and using Linq to
filter our results.
An example where we get all
items under the current item:
var results = context.GetQueryable<SearchResultItem>().Where(i => i.Paths.Contains(item.ID));
var results = context.GetQueryable<SearchResultItem>().Where(i => i.Paths.Contains(item.ID));
If you have custom fields you
want to search on it is highly recommended that you create a class that derives
from SearchResultItem and add your fields as properties. What is really nice with this is that
Sitecore content search will reflect on this class and do the search on those
properties.
An example with an item
with an author field:
public class MyCustomResultItem: SearchResultItem{
public string Author { get; set; }
}
public class MyCustomResultItem: SearchResultItem{
public string Author { get; set; }
}
var results = context.GetQueryable<MyCustomResultItem>().Where(i => string.Equals(i.Author,”me”));
At this point you can iterate through results and get the
items using .GetItem() on each resulting item.
Using SignalR with Sitecore
If you are unfamiliar with SignalR and need to set it up I
will be posting an intro to SignalR on my other blog (Programming Medeley) in a
few days. Otherwise reading this post I
will assume that you have a basic understanding of what SignalR is and how it
works.
When developing in Sitecore with SignalR there is one major
issue that you will run into (aside from basic routing). That issue is the fact that you do not have a
Sitecore site context within your SignalR context. This causes issues with getting item URLs or
using the Glass Mapper.
Missing Site Context
The fix for this is actually quite simple though. You simply need to set the Sitecore site
context on every request.
To get the SiteContext you simply call
Sitecore.Sites.SiteContextFactory.GetSiteContext and pass in the hostname (which
can be found within the context of the Hub as this.Context.Request.Url.Host)
and an empty path and it will give you a SiteContext.
Once we have this all we have to do is set the current site
to this context by setting Sitecore.Context.Site to this SiteContext.
I use the single line:
Sitecore.Context.Site = Sitecore.Context.Site ?? SitecontextFactory.GetSiteContext(this.Context.Request.Url.Host, “/”);
Sitecore.Context.Site = Sitecore.Context.Site ?? SitecontextFactory.GetSiteContext(this.Context.Request.Url.Host, “/”);
Routing
As for the routing issue that you
may run into, this is the same as setting up WebApi. You need to ensure the route is registered in
the Application_Start of the Global.asax.
The command for this is (using
the Global Configuration):
GlobalConfiguration.Configuration.Routes.MapHttpRoute(“signalrHub”, “signalr/{controller}”);
GlobalConfiguration.Configuration.Routes.MapHttpRoute(“signalrHub”, “signalr/{controller}”);
Sunday, May 31, 2015
Saving Form data with the Web Forms For Marketers MVC Form API
As many people have noticed with the Web Forms For Marketers MVC Forms the data does not serialize correctly so using custom save actions you cannot get the data from the form correctly.
There are a few rather complex workarounds for this mostly involving parsing the form from the form values in the http response.
The easiest solution to this issue is to create your own Form Processor which you can inject with a configuration file.
There are a few rather complex workarounds for this mostly involving parsing the form from the form values in the http response.
The easiest solution to this issue is to create your own Form Processor which you can inject with a configuration file.
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <wffm> <formProcessors> <processor type="MyNamespace.Forms.MVC.Processors.FormProcessor, MyNamespace.Forms.MVC" /> </formProcessors> </wffm> </sitecore> </configuration>
The processor itself is actually quite easy to write you simply need a class that inherits Sitecore.Forms.MVC.Processors.FormProcessor, then you can implement your save action in the FormSuccessValidation event.
namespace MyNamespace.Forms.MVC.Processors { public class FormProcessor : FormProcessor { public FormProcessor() { SuccessValidation += FormSuccessValidation; } private void SaveForm(FormModel form) { var Fields = form.Sections.SelectMany(i => i.Fields); } public void FormSuccessValidation(object source, FormEventArgs args) { SaveForm(args.Form); } } }You can see from my code that you can access the fields through the given model in the form.Sections property. I find that this is much easier to implement than a custom save action (though it will not show up in the drop down list which may be a problem for some people, you can use this to store the data to access in a custom save action).
If you have any questions related to Web Forms For Marketers please don't hesitate to ask as I have spent a lot of time in that code lately.
Error 500.19 Configuration File Too Long Sitecore Web.config
It is quite well known that the web.config file in Sitecore is monstrous. It takes very little to make it large enough to get the 500.19 .NET error with the file being too large. Most resolutions to this involve modifying the registry, but it does not really have to be that difficult.
The easiest solution is to take the Sitecore node out of the file and put it in a separate file similar to how the connection strings are pulled out.
The easiest way to get the whole node selected would be to use a text editor that supports regular expressions and use the expression (<sitecore)[\s\S]*(</sitecore>) to select it. Then you simply cut it and past it into a file (I generally put it in a file called Sitecore.config in the App_Config folder).
After this you need to put an empty Sitecore node where you removed the old one from and add a configsource attribute that points to the new config file.
<sitecore configSource="App_Config/Sitecore.config" />
The easiest solution is to take the Sitecore node out of the file and put it in a separate file similar to how the connection strings are pulled out.
The easiest way to get the whole node selected would be to use a text editor that supports regular expressions and use the expression (<sitecore)[\s\S]*(</sitecore>) to select it. Then you simply cut it and past it into a file (I generally put it in a file called Sitecore.config in the App_Config folder).
After this you need to put an empty Sitecore node where you removed the old one from and add a configsource attribute that points to the new config file.
<sitecore configSource="App_Config/Sitecore.config" />
Tuesday, April 14, 2015
Language Fallback: A word of caution
The
concept of Language Fallback in Sitecore is great, actually having the ability
to have Sitecore return content in a different language if none exists in the
current context is tremendously helpful.
Even the concept behind this functionality is pretty straight
forward.
The
module simply checks if there is a version in the current language. If not it checks if there is a fallback for
the current language and repeats the process on that language if there is until
it either finds a version of the item or creates an empty item to return.
Due
to the simplicity of this module it has continued to work with every version of
Sitecore since it was introduced, and will likely continue working for years to
come.
Unfortunately,
if you think about the recursion in this module there is a large problem that
can happen. Endless loops can occur when
languages eventually fall back to the original language.
The
current module
in the marketplace written by Alex Shyba does not have checks to ensure that
you do not have an endless loop. Nikola Gotsev and I ran into
this problem a while back where we had a language that would fall back to
another language that fell back to the first language. Not knowing this content error had happened
led us into a bit of a nightmare of disabling modules and checking changes to
find what brought our environment to its’ knees.
Nikola
actually posted a fix
for this last month. This fix simply
includes checking if we have already fallen back to a specific language (by
enumerating fallback languages in a list).
This solves the problem no matter how complex the chain is.
Thursday, April 9, 2015
Dynamic Key Placeholders Revisited
A little over a year ago I did a post on Dynamic Key Placeholders, since then I have noticed
there was some information lacking, such as what versions of Sitecore it works
with, and how to get Placeholder Settings to work.
From my testing this should work in every version of Sitecore from
6.5 to 8.0, and likely will continue working in previous and future versions as
well.
The Old Way
By default when
using dynamic key placeholders you can create placeholder settings for each
index of the key. Such as if the
original key was Placeholder, you
would need to create settings for Placeholder1,
Placeholder2, etc… This can lead to a
lot of work, and a cluttered collection of placeholder settings.
A New Way to Do It
An easier way would
be to create an override of the GetAllowedRenderings processor to allow for an
experience more like the original when creating placeholder settings.
A Quick Modification to the Original
In the original
source for the dynamic key placeholders we need to make a simple modification. In the part of the code that we set the key I have added the # symbol in there to allow us to
split it apart far easier to create an override for Placeholder Settings. You can use any symbol you want, but keep I
will continue with this one for the rest of the post.
// Count represents
how many of the same container have already been added to the page
if (renderings.Any())
{
_dynamicKey = _key + "#" + renderings.Count();
}
Our New Processor
The big changes though are in the addition of overriding the
GetAllowedRenderings processor. The change
is not too complex as we are basically mirroring what Sitecore is already
doing, but with the exception of overriding the placeholder key.
To override the key
we want to split the key first into the segments by the / character and then
for each segment we use the split on the # to remove our index from the dynamic
key giving us the original key.
After that we want
to try to get the placeholder item using our cleaned up key. Of course if there is a device specified we
should get the item in the context of that device.
If the Item exists
we should get the list of renderings it supports.
public class GetAllowedRenderings : Sitecore.Pipelines.GetPlaceholderRenderings.GetAllowedRenderings
{
/// <summary>
/// This is a method
based on the original Sitecore method to allow for dynamic placeholder keys
/// </summary>
/// <param name="args">The Placeholder
Rendering Args (Device, Key, Database, etc...)</param>
public new void Process(GetPlaceholderRenderingsArgs args)
{
Assert.IsNotNull((object)args, "args");
//Remove the dynamic
indexing from the key
var key = string.Join("/", args.PlaceholderKey.Split('/').Select(i => i.Split('#')[0]));
//Get the
placeholder item, if there is a Device selected get it using that Device
Context
Item placeholderItem = null;
if (ID.IsNullOrEmpty(args.DeviceId))
{
placeholderItem = Client.Page.GetPlaceholderItem(key, args.ContentDatabase, args.LayoutDefinition);
}
else
{
using (new DeviceSwitcher(args.DeviceId, args.ContentDatabase))
{
placeholderItem = Client.Page.GetPlaceholderItem(key, args.ContentDatabase, args.LayoutDefinition);
}
}
//If there was a
placeholder item (Placeholder Settings) set the allowed renderings
if (placeholderItem != null)
{
args.HasPlaceholderSettings
= true;
bool
allowedControlsSpecified;
args.PlaceholderRenderings
= this.GetRenderings(placeholderItem,
out
allowedControlsSpecified);
if
(allowedControlsSpecified)
args.Options.ShowTree = false;
}
}
}
Our Configuration Change
Finally we have the configuration file to enable
this override, this is simply done by replacing the processor in the
getPlaceholderRenderings pipeline.
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<pipelines>
<getPlaceholderRenderings>
<processor patch:instead="*[@type='Sitecore.Pipelines.GetPlaceholderRenderings.GetAllowedRenderings,
Sitecore.Kernel']" type="[Your Method], [Your Assembly] />
</getPlaceholderRenderings>
</pipelines>
</sitecore>
</configuration>
You can find the source from this post at https://github.com/musicman89/Sitecore-Dynamic-Key-Placeholders
Update (4/10/2015):
You can find a
Shared Source Module created at the Sitecore Hackathon 2015 that incorporates
this concept at https://marketplace.sitecore.net/en/Modules/Integrated_Dynamic_Placeholders.aspx
by Jamie Stump and Mark Servais, do note that I had no involvement in the
creation of this module.
Thank you Jamie
Stump for pointing this out.
Subscribe to:
Posts (Atom)