// <Migration features="f1, f2" />
        // <Migration features="*" />
        // Run migration for features.
        public void ExecuteRecipeStep(RecipeContext recipeContext)
        {
            if (!String.Equals(recipeContext.RecipeStep.Name, "Migration", StringComparison.OrdinalIgnoreCase)) {
                return;
            }

            bool runAll = false;
            var features = new List<string>();
            foreach (var attribute in recipeContext.RecipeStep.Step.Attributes()) {
                if (String.Equals(attribute.Name.LocalName, "features", StringComparison.OrdinalIgnoreCase)) {
                    features = ParseFeatures(attribute.Value);
                    if (features.Contains("*"))
                        runAll = true;
                }
                else {
                    Logger.Error("Unrecognized attribute {0} encountered in step Migration. Skipping.", attribute.Name.LocalName);
                }
            }

            if (runAll) {
                foreach (var feature in _dataMigrationManager.GetFeaturesThatNeedUpdate()) {
                    _dataMigrationManager.Update(feature);
                }
            }
            else {
                _dataMigrationManager.Update(features);
            }

            // run migrations
            recipeContext.Executed = true;
        }
        /*
           <Metadata>
            <Types>
             <Blog creatable="true">
              <Body format="abodyformat"/>
             </Blog>
            </Types>
            <Parts>
            </Parts>
           </Metadata>
         */
        // Set type settings and attach parts to types.
        // Create dynamic parts.
        public void ExecuteRecipeStep(RecipeContext recipeContext)
        {
            if (!String.Equals(recipeContext.RecipeStep.Name, "Metadata", StringComparison.OrdinalIgnoreCase)) {
                return;
            }

            foreach (var metadataElement in recipeContext.RecipeStep.Step.Elements()) {
                switch (metadataElement.Name.LocalName) {
                    case "Types":
                        foreach (var element in metadataElement.Elements()) {
                            var typeElement = element;
                            var typeName = XmlConvert.DecodeName(element.Name.LocalName);
                            _contentDefinitionManager.AlterTypeDefinition(typeName, alteration => _contentDefinitionReader.Merge(typeElement, alteration));
                        }
                        break;
                    case "Parts":
                        // create dynamic part.
                        foreach (var element in metadataElement.Elements()) {
                            var partElement = element;
                            var partName = XmlConvert.DecodeName(element.Name.LocalName);
                            _contentDefinitionManager.AlterPartDefinition(partName, alteration => _contentDefinitionReader.Merge(partElement, alteration));
                        }
                        break;
                    default:
                        Logger.Error("Unrecognized element {0} encountered in step Metadata. Skipping.", metadataElement.Name.LocalName);
                        break;
                }
            }

            recipeContext.Executed = true;
        }
        public void ExecuteRecipeStep(RecipeContext recipeContext)
        {
            if (!String.Equals(recipeContext.RecipeStep.Name, "Roles", StringComparison.OrdinalIgnoreCase)) {
                return;
            }

            var installedPermissions = _roleService.GetInstalledPermissions().SelectMany(p => p.Value).ToList();

            foreach (var roleElement in recipeContext.RecipeStep.Step.Elements()) {
                var roleName = roleElement.Attribute("Name").Value;

                var role = _roleService.GetRoleByName(roleName);
                if (role == null) {
                    _roleService.CreateRole(roleName);
                    role = _roleService.GetRoleByName(roleName);
                }

                var permissions = roleElement.Attribute("Permissions").Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                // only import permissions for currenlty installed modules
                var permissionsValid = permissions.Where(permission => installedPermissions.Any(x => x.Name == permission)).ToList();

                // union to keep existing permissions
                _roleService.UpdateRole(role.Id, role.Name, permissionsValid.Union(role.RolesPermissions.Select(p => p.Permission.Name)));
            }
            recipeContext.Executed = true;
        }
        // <Data />
        // Import Data
        public void ExecuteRecipeStep(RecipeContext recipeContext)
        {
            if (!String.Equals(recipeContext.RecipeStep.Name, "Data", StringComparison.OrdinalIgnoreCase)) {
                return;
            }

            var importContentSession = new ImportContentSession(_coeveryServices.ContentManager);

            // Populate local dictionary with elements and their ids
            var elementDictionary = CreateElementDictionary(recipeContext.RecipeStep.Step);

            //Populate import session with all identities to be imported
            foreach (var identity in elementDictionary.Keys) {
                importContentSession.Set(identity.ToString(), elementDictionary[identity].Name.LocalName);
            }

            //Determine if the import is to be batched in multiple transactions
            var startIndex = 0;
            int batchSize = GetBatchSizeForDataStep(recipeContext.RecipeStep.Step);

            //Run the import
            ContentIdentity nextIdentity = null;
            try {
                while (startIndex < elementDictionary.Count) {
                    importContentSession.InitializeBatch(startIndex, batchSize);

                    //the session determines which items are included in the current batch
                    //so that dependencies can be managed within the same transaction
                    nextIdentity = importContentSession.GetNextInBatch();
                    while (nextIdentity != null) {
                        _coeveryServices.ContentManager.Import(elementDictionary[nextIdentity], importContentSession);
                        nextIdentity = importContentSession.GetNextInBatch();
                    }

                    startIndex += batchSize;

                    //Create a new transaction for each batch
                    if (startIndex < elementDictionary.Count) {
                        _coeveryServices.ContentManager.Clear();
                        _transactionManager.RequireNew();
                    }
                }
            }
            catch (Exception) {
                //Ensure a failed batch is rolled back
                _transactionManager.Cancel();
                throw;
            }

            recipeContext.Executed = true;
        }
        // <Feature enable="f1,f2,f3" disable="f4" />
        // Enable/Disable features.
        public void ExecuteRecipeStep(RecipeContext recipeContext)
        {
            if (!String.Equals(recipeContext.RecipeStep.Name, "Feature", StringComparison.OrdinalIgnoreCase)) {
                return;
            }

            var featuresToEnable = new List<string>();
            var featuresToDisable = new List<string>();
            foreach (var attribute in recipeContext.RecipeStep.Step.Attributes()) {
                if (String.Equals(attribute.Name.LocalName, "disable", StringComparison.OrdinalIgnoreCase)) {
                    featuresToDisable = ParseFeatures(attribute.Value);
                }
                else if (String.Equals(attribute.Name.LocalName, "enable", StringComparison.OrdinalIgnoreCase)) {
                    featuresToEnable = ParseFeatures(attribute.Value);
                }
                else {
                    Logger.Error("Unrecognized attribute {0} encountered in step Feature. Skipping.", attribute.Name.LocalName);
                }
            }

            var availableFeatures = _featureManager.GetAvailableFeatures().Select(x => x.Id).ToArray();
            foreach (var featureName in featuresToDisable) {
                if (!availableFeatures.Contains(featureName)) {
                    throw new InvalidOperationException(string.Format("Could not disable feature {0} because it was not found.", featureName));
                }
            }

            foreach (var featureName in featuresToEnable) {
                if (!availableFeatures.Contains(featureName)) {
                    throw new InvalidOperationException(string.Format("Could not enable feature {0} because it was not found.", featureName));
                }
            }

            if (featuresToDisable.Count != 0) {
                _featureManager.DisableFeatures(featuresToDisable, true);
            }
            if (featuresToEnable.Count != 0) {
                _featureManager.EnableFeatures(featuresToEnable, true);
            }

            recipeContext.Executed = true;
        }
        public bool ExecuteNextStep(string executionId)
        {
            var nextRecipeStep= _recipeStepQueue.Dequeue(executionId);
            if (nextRecipeStep == null) {
                _recipeJournal.ExecutionComplete(executionId);
                _recipeExecuteEventHandler.ExecutionComplete(executionId);
                return false;
            }
            _recipeJournal.WriteJournalEntry(executionId, string.Format("Executing step {0}.", nextRecipeStep.Name));
            var recipeContext = new RecipeContext { RecipeStep = nextRecipeStep, Executed = false };
            try {
                _recipeExecuteEventHandler.RecipeStepExecuting(executionId, recipeContext);
                foreach (var recipeHandler in _recipeHandlers) {
                    recipeHandler.ExecuteRecipeStep(recipeContext);
                }
                _recipeExecuteEventHandler.RecipeStepExecuted(executionId, recipeContext);
            }
            catch(Exception exception) {
                Logger.Error(exception, "Recipe execution {0} was cancelled because a step failed to execute", executionId);
                while (_recipeStepQueue.Dequeue(executionId) != null) ;
                _recipeJournal.ExecutionFailed(executionId);
                var message = T("Recipe execution with id {0} was cancelled because the \"{1}\" step failed to execute. The following exception was thrown: {2}. Refer to the error logs for more information.",
                                executionId, nextRecipeStep.Name, exception.Message);
                _recipeJournal.WriteJournalEntry(executionId, message.ToString());

                throw new CoeveryCoreException(message);
            }

            if (!recipeContext.Executed) {
                Logger.Error("Could not execute recipe step '{0}' because the recipe handler was not found.", recipeContext.RecipeStep.Name);
                while (_recipeStepQueue.Dequeue(executionId) != null) ;
                _recipeJournal.ExecutionFailed(executionId);
                var message = T("Recipe execution with id {0} was cancelled because the recipe handler for step \"{1}\" was not found. Refer to the error logs for more information.",
                                executionId, nextRecipeStep.Name);
                _recipeJournal.WriteJournalEntry(executionId, message.ToString());

                throw new CoeveryCoreException(message);
            }

            return true;
        }
        /*
         <Settings>
          <SiteSettingsPart PageSize="30" />
          <CommentSettingsPart ModerateComments="true" />
         </Settings>
        */
        // Set site and part settings.
        public void ExecuteRecipeStep(RecipeContext recipeContext)
        {
            if (!String.Equals(recipeContext.RecipeStep.Name, "Settings", StringComparison.OrdinalIgnoreCase)) {
                return;
            }

            var site = _siteService.GetSiteSettings();
            foreach (var element in recipeContext.RecipeStep.Step.Elements()) {
                var partName = XmlConvert.DecodeName(element.Name.LocalName);
                foreach (var contentPart in site.ContentItem.Parts) {
                    if (!String.Equals(contentPart.PartDefinition.Name, partName, StringComparison.OrdinalIgnoreCase)) {
                        continue;
                    }
                    foreach (var attribute in element.Attributes()) {
                        SetSetting(attribute, contentPart);
                    }
                }
            }

            recipeContext.Executed = true;
        }
        /*
         <Command>
            command1
            command2
            command3
         </Command>
        */
        // run Coevery commands.
        public void ExecuteRecipeStep(RecipeContext recipeContext)
        {
            if (!String.Equals(recipeContext.RecipeStep.Name, "Command", StringComparison.OrdinalIgnoreCase)) {
                return;
            }

            var commands =
                recipeContext.RecipeStep.Step.Value
                .Split(new[] {"\r\n", "\n"}, StringSplitOptions.RemoveEmptyEntries)
                .Select(commandEntry => commandEntry.Trim());

            foreach (var command in commands) {
                if (!String.IsNullOrEmpty(command)) {
                    var commandParameters = _commandParser.ParseCommandParameters(command);
                    var input = new StringReader("");
                    var output = new StringWriter();
                    _commandManager.Execute(new CommandParameters { Arguments = commandParameters.Arguments, Input = input, Output = output, Switches = commandParameters.Switches });
                }
            }

            recipeContext.Executed = true;
        }