This post has been migrated from www.experimentsincode.com, we apologise if some of the images or content is missing

Today I received an email from Mike and Pekto about the possibility of using Razl to move an item from one Sitecore environment (staging) to another Sitecore environment (live) as the item goes through workflow, the challenge was set, so time to see if it is possible. Three things I knew I was going to need to solve this problem:
  1. Razl
  2. A custom Sitecore workflow action.
  3. A custom script.
Number 1 was easy, I just installed a new version of Razl on the staging server since this is where the copy will be kicked off from. Number 2 was going to be more complex so I decided I would tackle number 3 next. Since the script would be copying from staging to live and the only variable is the item to copy I knew I would need a single placeholder for the item ID and everything else can have fixed values:
<razl>
	<connection name="Live" readOnly ="false" install="false">
		<url>http://live.demo.tds/</url>
		<accessGuid>757cb84a-04c3-4bac-a95e-d05b7b05eb11</accessGuid>
		<database>master</database>
	</connection>
	<connection name="Staging" readOnly ="false" install="false">
		<url>http://staging.demo.tds/</url>
		<accessGuid>757cb84a-04c3-4bac-a95e-d05b7b05eb11</accessGuid>
		<database>master</database>		
	</connection>
	<operation name="CopyItem" source="Staging" target="Live">
		<parameter name="itemId">$(itemId)</parameter>
		<parameter name="overwrite">true</parameter>
	</operation>
</razl>
My script makes use of the CopyItem command and has the $(itemId) placeholder, this will copy just the item specified by the ID. The script also assumes that the Razl service is already installed on both environments, I recommend you do this because you don't want the script always installing the connector. Ok that bit was simple next to tackle number 2 the workflow action, all I need to do is make my workflow action call Razl and pass it the ID of the item I want to copy:
    public class RazlPublish
    {
        public const string RazlPath = @"C:\Program Files (x86)\Hedgehog Development\Razl\Razl.exe";
        
        public void Process(WorkflowPipelineArgs args)
        {
            Item dataItem = args.DataItem;
            Item innerItem = args.ProcessorItem.InnerItem;

            string script = WebUtil.ParseUrlParameters(innerItem["parameters"])["script"];
            string fullScript = HttpContext.Current.Server.MapPath(script);

            string parameters = string.Format("itemId={0};templateId={1};parentId={2}", dataItem.ID, dataItem.TemplateID, dataItem.ParentID);
            
            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.CreateNoWindow = false;
            startInfo.UseShellExecute = false;
            startInfo.FileName = RazlPath;
            startInfo.WindowStyle = ProcessWindowStyle.Hidden;
            startInfo.Arguments = string.Format("/script:{0} /p:{1}", fullScript, parameters);

            startInfo.RedirectStandardOutput = true;
            startInfo.RedirectStandardError = true;

            Sitecore.Diagnostics.Log.Info(string.Format("Razl Script Started {0} {1}", startInfo.FileName, startInfo.Arguments), this);

            string info = string.Empty;
            string error = string.Empty;
            StringBuilder data = new StringBuilder();

            using (Process exeProcess = System.Diagnostics.Process.Start(startInfo))
            {
                info = exeProcess.StandardOutput.ReadToEnd();
                error = exeProcess.StandardError.ReadToEnd();

                exeProcess.OutputDataReceived += (sender, args1) => data.AppendLine(args1.Data);
                exeProcess.WaitForExit();
            }

            if (!string.IsNullOrEmpty(info))
            {
                Sitecore.Diagnostics.Log.Info(info, this);
            }
            if (!string.IsNullOrEmpty(error))
            {
                Sitecore.Diagnostics.Log.Error(error, this);
            }

            if (!string.IsNullOrEmpty(data.ToString()))
            {
                Sitecore.Diagnostics.Log.Info(data.ToString(), this);
            }


            Sitecore.Diagnostics.Log.Info(string.Format("Razl Script Finished {0} {1}", startInfo.FileName, startInfo.Arguments), this);
        }
    }
A few key points about this workflow action, firstly notice that I am going to pull the script I want to run from a parameter on the workflow action item:
            string script = WebUtil.ParseUrlParameters(innerItem["parameters"])["script"];
            string fullScript = HttpContext.Current.Server.MapPath(script);
Secondly I pass a load of parameters into Razl, this is to give a little bit of future proofing:
           string parameters = string.Format("itemId={0};templateId={1};parentId={2}", dataItem.ID, dataItem.TemplateID, dataItem.ParentID);
Finally I need to create my action item in Sitecore: With everything in place I can now push items through workflow on staging and then have them automatically replicated to Live without any manual interaction! This process could easily be modified to work with events such as item saved or item created if you didn't want to use a workflow action.