Tuesday, December 22, 2009

Publish to pre-production web database


In my experience with enterprise level implementations, there is often a need for a separate “stage environment” for final content preview or a pre-production phase of workflow.

It is also often the case that the environmental limitations or restrictions make the content delivery part of the authoring environment suitable for such purpose. For example, your e-commerce infrastructure could not be available from the authoring instance. One of the possible ways to approach such a requirement is to configure a stand-alone staging instance of Sitecore which will be used for content delivery instance and is commonly configured exactly like one of the servers in the web farm. There is one exception though – the database that is used for content delivery is generally different from the one that is used in production content delivery.

With this approach, now the question is how to plug in workflow in a way that content could go via this “stage” environment and then only upon final approval will be able to get to “production”.

Now there is a problem that you cannot get get items published until they are in final workflow state. You could have two workflow states “Staged” and “Published”, both marked as final and with auto publish actions connected.
I don’t like this approach since it goes against the workflow nature – only one state should be marked as final.

Alternatively, you can plug in a “copy item” workflow action to the “Staged” workflow state where you can programmatically copy an item from master database to stage web.

While this approach does seem legitimate, it works around the need of publishing thus any additional processing you may be having in publish(item) pipeline would not work.

I looked into the option of having publishing process ignore workflow when publishing to stage. While this seemed like a dangerous path to go, I have soon discovered that the PublishHelper’s GetVersionToPublish method already accepts this notion of passing a “requireapproval” flag to the underlying publishing mechanism. Since it is always passes “true”, I started looking into ways to make this flag dynamic and figured that setting it on the level of PublishOptions could be a good idea. For example, from the code of my workflow action I would be able to define whether I want to have workflow respected or not.

Here is what you will need to do to make it work.

First, override the default PipelinePublishProvider and plug it into the web.config:

<publishManager defaultProvider="default" enabled="true">
    <providers>
        <clear />
         <!--<add name="default" type="Sitecore.Publishing.PipelinePublishProvider, Sitecore.Kernel" />-->
         <add name="default" type="SCUSAINC.Publishing.ExtendedPublishProvider, SCUSAINC.Publishing" />
     </providers>
</publishManager>

Secondly, you will need to override the CreatePublishHelper class:

public class ExtendedPublishProvider : PipelinePublishProvider
    {
        public override PublishHelper CreatePublishHelper(PublishOptions options)
        {
            Assert.ArgumentNotNull(options, "options");
            if (options is ExtendedPublishOptions)
                return new ExtendedPublishHelper(options as ExtendedPublishOptions);

            return new PublishHelper(options);
        }
    }

After that, introduce two more classes – ExtendedPublishHelper and ExtendedPublishOptions:

public class ExtendedPublishHelper : PublishHelper
    {
        private readonly ExtendedPublishOptions _options;

        public ExtendedPublishHelper(ExtendedPublishOptions options)
            : base(options)
        {
            _options = options;
        }

        public override Item GetVersionToPublish(Item sourceItem)
        {
            Assert.ArgumentNotNull(sourceItem, "sourceItem");
            if (Options is ExtendedPublishOptions)
            {
                return sourceItem.Publishing.GetValidVersion(Options.PublishDate, _options.RequireApproval);
            }

            return sourceItem.Publishing.GetValidVersion(Options.PublishDate, true);
        }
    }

public class ExtendedPublishOptions : PublishOptions
    {
        public ExtendedPublishOptions(Database sourceDatabase, Database targetDatabase, PublishMode mode, Language language, DateTime publishDate, bool requireApproval)
            : base(sourceDatabase, targetDatabase, mode, language, publishDate)
        {
            RequireApproval = requireApproval;
        }

        public bool RequireApproval { get; set; }
    }

Now you are ready to launch publishing with workflow settings completely ignored. For example, this is how you can do it from the workflow action:

var database = Factory.GetDatabase(databaseName);

var options = new ExtendedPublishOptions(dataItem.Database, database, PublishMode.SingleItem, dataItem.Language, DateTime.Now, true)
{
         RootItem = dataItem,
         Deep = true;
};

new Publisher(options).PublishAsync();

That’s it!

Tested on Sitecore 6.1. Expected to work with 6.2.

Monday, December 14, 2009

Get all published items in Sitecore 6.1/6.2


You may need to get all items that were processes by a publishing operation for different reasons, for example, pull certain information for each item and notify CDN of a change so cache could be purged. AKAMAI can be configured to behave that way.
Currently we have two articles on the subject. This one describes how to use the PublishEngine, second article suggests approach with custom PublishLog. Both are not compatible with 6.x. So I’ve looked into the options for the latest Sitecore with tech support. Our findings included introduction of a new PublishProcessor which examines the Queue property of PublishContext, but this did not work properly for smart publishing and I wanted a bulletproof yet simple approach applicable to all possible publishing modes.
Then I remembered that we are already doing similar things for indexing. The update index job processes only changed items and gets that information from the History storage.

Sunday, December 13, 2009

WFFM 2.0 module: Form Reports Viewer throws exception


If you are like me who enjoys stripping down Sitecore solution as much as possible, you may stumble across this as well.

On Sitecore 6.1/6.2 install with awesome Web Forms for Marketers module 2.0, you may get the following exception if your bin folder is missing SQLite assemblies (Sitecore.SQLite.dll and System.Data.SQLite.DLL). Even if you are not using SQLite, apparently, there is a dependency on these guys from the module’s assemblies. Putting them back obviously solves the problem.

Server Error in '/' Application.


Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[ReflectionTypeLoadException: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.]

System.Reflection.Module._GetTypesInternal(StackCrawlMark& stackMark) +0

System.Reflection.Assembly.GetTypes() +111

System.Data.Metadata.Edm.AssemblyCacheEntry.LoadTypesFromAssembly(LoadingContext context) +28

System.Data.Metadata.Edm.AssemblyCacheEntry.InternalLoadAssemblyFromCache(LoadingContext context) +290

System.Data.Metadata.Edm.AssemblyCacheEntry.LoadAssemblyFromCache(Assembly assembly, Boolean loadReferencedAssemblies, Dictionary`2 knownAssemblies, Dictionary`2& typesInLoading, List`1& errors) +137

System.Data.Metadata.Edm.ObjectItemCollection.LoadAssemblyFromCache(ObjectItemCollection objectItemCollection, Assembly assembly, Boolean loadReferencedAssemblies) +284

System.Data.Metadata.Edm.ObjectItemCollection.LoadAssemblyForType(Type type) +33

System.Data.Metadata.Edm.MetadataWorkspace.LoadAssemblyForType(Type type, Assembly callingAssembly) +80

System.Data.Common.Utils.MetadataHelper.TryDetermineCSpaceModelType(MetadataWorkspace workspace, EntityType& modelEntityType) +67

System.Data.Objects.ObjectContext.ExecuteFunction(String functionName, ObjectParameter[] parameters) +238

Sitecore.Forms.Data.SitecoreWebFormsContext.GetFormsByMainParams(Nullable`1 formId, String storageName, Nullable`1 pageIndex, Nullable`1 pageSize, Nullable`1 asc, ObjectParameter returnValue) +593

Sitecore.Forms.Data.DataProviders.WFMDataProvider.GetForms(QueryParams queryParams, Int32& total) +911

Sitecore.Forms.Data.DataProvider.GetPage(PageCriteria page, IList`1 sort, IList`1 filters) +534

Sitecore.Form.DataViewer.SortedFilterable`1.GetPage(PageCriteria page, IList`1 sort, IList`1 filter) +55

Sitecore.Form.Core.DataViewer.FormGridSource`1.GetEntries(Int32 pageIndex, Int32 pageSize) +104

Sitecore.Form.DataViewer.ComponentArtGridHandler`1.GetDataSource(Int32 pageIndex, Int32 pageSize) +56

Sitecore.Form.DataViewer.ComponentArtGridHandler`1.ApplyDataSource() +76

Sitecore.Form.DataViewer.ComponentArtGridHandler`1.Grid_NeedDataSource(Object sender, EventArgs e) +54

ComponentArt.Web.UI.Grid.OnNeedDataSource(EventArgs e) +36

ComponentArt.Web.UI.Grid.DataBind() +475

Sitecore.Form.DataViewer.ComponentArtGridHandler`1.DataBind() +17

Sitecore.Form.DataViewer.ComponentArtGridHandler`1.InitializeGrid(Boolean dataBind) +503

Sitecore.Form.DataViewer.ComponentArtGridHandler`1..ctor(Grid grid, IGridSource`1 source, Boolean dataBind) +173

Sitecore.Form.DataViewer.ComponentArtGridHandler`1.Manage(Grid grid, IGridSource`1 source, Boolean dataBind) +94

Sitecore.Forms.Shell.UI.FormDataViewer.FormDataViewerPage.GridRender(Boolean update) +360

Sitecore.Forms.Shell.UI.FormDataViewer.FormDataViewerPage.OnPageSelected(Object sender, MultiPageSelectEventArgs e) +110

Sitecore.Forms.Shell.UI.FormDataViewer.FormDataViewerPage.OnLoad(EventArgs e) +223

System.Web.UI.Control.LoadRecursive() +50

System.Web.UI.Control.LoadRecursive() +141

System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +627


Version Information: Microsoft .NET Framework Version:2.0.50727.4927; ASP.NET Version:2.0.50727.4927

Friday, December 11, 2009

Friday case: Sitecore is not starting up


Just had to deal with a pretty mysterious issue when my Sitecore app stopped staring up for whatever reasons.
Symptoms:
- nothing happens, browser just hangs with the “Connecting…” message
- nothing in the Sitecore logs
- nothing in the Event Viewer
- nothing unusual on the SQL side
- nothing unusually heavy on the worker process – CPU is idling out, memory is fine.

In overall, a sign that something is inherently wrong.

Thursday, December 10, 2009

Quick tip: resolving section sort order problem with inheritance


There is a known issue currently with the sort order set on the standard values for the “/sitecore/templates/System/Templates/Template section” template and not being picked correctly and always treated as zero until you set the sort order specifically for the section thus overriding the standard value.

This has impact on section inheritance, producing unexpected behavior in the following case:
1. You have an abstract “base product” template with the following sections:
image

2. You have a derivative template “monitor” with its own domain specific data section:
image

3. You want to have certain sections from the “base product” template show up first, then the “data” section from the “monitor” template itself, then the rest of sections from base:
image 

Since the default inherited sort order for a section is 100 (coming from __Standard values), however you won’t be able to accomplish this by setting the sort order of “descriptors” and “product data” section to 80 and 90 correspondingly.
This is because the value for the section sort order that is coming from the __Standard values will always be considered as zero.

There is an easy and simple way to solve this by simply setting the sort order of “descriptors” and “product data” sections to negative values (-2 and -1) for example.

Enjoy!

Thursday, December 03, 2009

Partial (Item) Cache Clearing Module – SitecoreStager (update)


A quick update. We have released new version of the Staging module with the full support of partial item cache clearing.
Not everyone was comfortable with full cache clearing process and that we released Stager as a shared source, now we have an official, supported, ready to go component addressing this need.

If you are not using Stager (I blogged about it some time ago) yet and have Staging module installed, seriously consider upgrading.

Check out the release notes here, there are some important things fixed also.
Enjoy!