Extended recipe execution logging.

This commit is contained in:
Daniel Stolt
2015-08-03 15:01:33 +01:00
parent 4740283ab2
commit 43922eaf20
24 changed files with 275 additions and 120 deletions

View File

@@ -24,6 +24,7 @@ using Orchard.Packaging.Models;
using Orchard.Packaging.Services;
using Orchard.Recipes.Models;
using Orchard.Recipes.Providers.Executors;
using Orchard.Recipes.Services;
using Orchard.Tests.Environment.Extensions;
using Orchard.Tests.Environment.Features;
using Orchard.Tests.Stubs;
@@ -53,6 +54,8 @@ namespace Orchard.Tests.Modules.Recipes.RecipeHandlers {
_packagesInRepository = new StubPackagingSourceManager();
_packageManager = new StubPackageManager();
builder.RegisterInstance(_folders).As<IExtensionFolders>();
builder.RegisterType<StubWorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterType<RecipeExecutionLogger>().AsSelf();
builder.RegisterType<ExtensionManager>().As<IExtensionManager>();
builder.RegisterType<FeatureManager>().As<IFeatureManager>();
builder.RegisterType<StubCacheManager>().As<ICacheManager>();

View File

@@ -4,6 +4,7 @@ using NUnit.Framework;
using Orchard.Recipes.Models;
using Orchard.Recipes.Providers.RecipeHandlers;
using Orchard.Recipes.Services;
using Orchard.Tests.Stubs;
namespace Orchard.Tests.Modules.Recipes.RecipeHandlers {
[TestFixture]
@@ -13,6 +14,8 @@ namespace Orchard.Tests.Modules.Recipes.RecipeHandlers {
[SetUp]
public void Init() {
var builder = new ContainerBuilder();
builder.RegisterType<StubWorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterType<RecipeExecutionLogger>().AsSelf();
builder.RegisterType<StubRecipeExecutionStep>().As<IRecipeExecutionStep>().AsSelf().SingleInstance();
builder.RegisterType<RecipeExecutionStepHandler>().SingleInstance();
@@ -37,6 +40,10 @@ namespace Orchard.Tests.Modules.Recipes.RecipeHandlers {
}
public class StubRecipeExecutionStep : RecipeExecutionStep {
public StubRecipeExecutionStep(IWorkContextAccessor workContextAccessor) : base(workContextAccessor) {
}
public override string Name {
get { return "FakeRecipeStep"; }
}

View File

@@ -22,6 +22,7 @@ using Orchard.FileSystems.VirtualPath;
using Orchard.Packaging.GalleryServer;
using Orchard.Packaging.Services;
using Orchard.Recipes.Models;
using Orchard.Recipes.Services;
using Orchard.Tests.DisplayManagement.Descriptors;
using Orchard.Tests.Environment.Extensions;
using Orchard.Tests.Environment.Features;
@@ -56,6 +57,8 @@ namespace Orchard.Tests.Modules.Recipes.RecipeHandlers {
_packagesInRepository = new ModuleStepTest.StubPackagingSourceManager();
_packageManager = new ModuleStepTest.StubPackageManager();
builder.RegisterInstance(_folders).As<IExtensionFolders>();
builder.RegisterType<StubWorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterType<RecipeExecutionLogger>().AsSelf();
builder.RegisterType<ExtensionManager>().As<IExtensionManager>();
builder.RegisterType<FeatureManager>().As<IFeatureManager>();
builder.RegisterType<StubCacheManager>().As<ICacheManager>();

View File

@@ -1,4 +1,5 @@
using System;
using System.Linq;
using Orchard.AuditTrail.Models;
using Orchard.ContentManagement;
using Orchard.Data;
@@ -15,7 +16,11 @@ namespace Orchard.AuditTrail.Recipes.Executors {
private readonly IAuthorizer _authorizer;
private readonly IWorkContextAccessor _wca;
public AuditTrailStep(IRepository<AuditTrailEventRecord> auditTrailEventRepository, IAuthorizer authorizer, IWorkContextAccessor wca) {
public AuditTrailStep(
IRepository<AuditTrailEventRecord> auditTrailEventRepository,
IAuthorizer authorizer,
IWorkContextAccessor wca) : base(wca) {
_auditTrailEventRepository = auditTrailEventRepository;
_authorizer = authorizer;
_wca = wca;
@@ -31,20 +36,30 @@ namespace Orchard.AuditTrail.Recipes.Executors {
return;
}
foreach (var eventElement in context.RecipeStep.Step.Elements()) {
var record = new AuditTrailEventRecord {
EventName = eventElement.Attr<string>("Name"),
FullEventName = eventElement.Attr<string>("FullName"),
Category = eventElement.Attr<string>("Category"),
UserName = eventElement.Attr<string>("User"),
CreatedUtc = eventElement.Attr<DateTime>("CreatedUtc"),
EventFilterKey = eventElement.Attr<string>("EventFilterKey"),
EventFilterData = eventElement.Attr<string>("EventFilterData"),
Comment = eventElement.El("Comment"),
EventData = eventElement.Element("EventData").ToString(),
};
var elements = context.RecipeStep.Step.Elements().ToArray();
for (var i = 0; i < elements.Length; i ++) {
var eventElement = elements[i];
Logger.Information("Importing audit trail event {0}/{1}.", i + 1, elements.Length);
_auditTrailEventRepository.Create(record);
try {
var record = new AuditTrailEventRecord {
EventName = eventElement.Attr<string>("Name"),
FullEventName = eventElement.Attr<string>("FullName"),
Category = eventElement.Attr<string>("Category"),
UserName = eventElement.Attr<string>("User"),
CreatedUtc = eventElement.Attr<DateTime>("CreatedUtc"),
EventFilterKey = eventElement.Attr<string>("EventFilterKey"),
EventFilterData = eventElement.Attr<string>("EventFilterData"),
Comment = eventElement.El("Comment"),
EventData = eventElement.Element("EventData").ToString(),
};
_auditTrailEventRepository.Create(record);
}
catch (Exception ex) {
Logger.Error(ex, "Error while importing audit trail event {0}/{1}.", i + 1, elements.Length);
throw;
}
}
}
}

View File

@@ -1,33 +1,47 @@
using System;
using System.Linq;
using Orchard.ContentManagement;
using Orchard.DynamicForms.Models;
using Orchard.DynamicForms.Services;
using Orchard.Environment.Extensions;
using Orchard.Logging;
using Orchard.Recipes.Models;
using Orchard.Recipes.Services;
namespace Orchard.DynamicForms.Recipes.Executors {
public class FormSubmissionsStep : RecipeExecutionStep {
private readonly IFormService _formService;
public FormSubmissionsStep(IFormService formService) {
public FormSubmissionsStep(IFormService formService,
IWorkContextAccessor workContextAccessor) : base(workContextAccessor) {
_formService = formService;
}
public override string Name {
get { return "Forms"; }
}
public override void Execute(RecipeExecutionContext context) {
var formsElement = context.RecipeStep.Step.Elements();
foreach (var formElement in formsElement) {
var formName = formElement.Attr<string>("Name");
var submissionElements = formElement.Element("Submissions").Elements();
foreach (var submissionElement in submissionElements) {
_formService.CreateSubmission(new Submission {
FormName = formName,
CreatedUtc = submissionElement.Attr<DateTime>("CreatedUtc"),
FormData = submissionElement.Value
});
public override void Execute(RecipeExecutionContext context) {
var formElements = context.RecipeStep.Step.Elements();
foreach (var formElement in formElements) {
var formName = formElement.Attr<string>("Name");
Logger.Information("Importing form '{0}'.", formName);
try {
var submissionElements = formElement.Element("Submissions").Elements().ToArray();
for (var i = 0; i < submissionElements.Length; i++) {
Logger.Information("Importing form submission {0}/{1}.", i + 1, submissionElements.Length);
var submissionElement = submissionElements[i];
_formService.CreateSubmission(new Submission {
FormName = formName,
CreatedUtc = submissionElement.Attr<DateTime>("CreatedUtc"),
FormData = submissionElement.Value
});
}
}
catch (Exception ex) {
Logger.Error(ex, "Error while importing form '{0}'.", formName);
throw;
}
}
}

View File

@@ -1,6 +1,8 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Orchard.Data;
using Orchard.Layouts.Models;
using Orchard.Logging;
using Orchard.Recipes.Models;
using Orchard.Recipes.Services;
@@ -8,7 +10,10 @@ namespace Orchard.Layouts.Recipes.Executors {
public class CustomElementsStep : RecipeExecutionStep {
private readonly IRepository<ElementBlueprint> _repository;
public CustomElementsStep(IRepository<ElementBlueprint> repository) {
public CustomElementsStep(
IRepository<ElementBlueprint> repository,
IWorkContextAccessor workContextAccessor) : base(workContextAccessor) {
_repository = repository;
}
@@ -23,13 +28,20 @@ namespace Orchard.Layouts.Recipes.Executors {
public override void Execute(RecipeExecutionContext context) {
foreach (var elementElement in context.RecipeStep.Step.Elements()) {
var typeName = elementElement.Attribute("ElementTypeName").Value;
var element = GetOrCreateElement(typeName);
Logger.Information("Importing custom element '{0}'.", typeName);
element.BaseElementTypeName = elementElement.Attribute("BaseElementTypeName").Value;
element.ElementDisplayName = elementElement.Attribute("ElementDisplayName").Value;
element.ElementDescription = elementElement.Attribute("ElementDescription").Value;
element.ElementCategory = elementElement.Attribute("ElementCategory").Value;
element.BaseElementState = elementElement.Element("BaseElementState").Value;
try {
var element = GetOrCreateElement(typeName);
element.BaseElementTypeName = elementElement.Attribute("BaseElementTypeName").Value;
element.ElementDisplayName = elementElement.Attribute("ElementDisplayName").Value;
element.ElementDescription = elementElement.Attribute("ElementDescription").Value;
element.ElementCategory = elementElement.Attribute("ElementCategory").Value;
element.BaseElementState = elementElement.Element("BaseElementState").Value;
}
catch (Exception ex) {
Logger.Error(ex, "Error while importing custom element '{0}'.", typeName);
throw;
}
}
}

View File

@@ -10,12 +10,11 @@ namespace Orchard.Modules.Recipes.Executors {
public class FeatureStep : RecipeExecutionStep {
private readonly IFeatureManager _featureManager;
public FeatureStep(IFeatureManager featureManager) {
public FeatureStep(IFeatureManager featureManager, IWorkContextAccessor workContextAccessor) : base(workContextAccessor) {
_featureManager = featureManager;
}
public override string Name
{
public override string Name {
get { return "Feature"; }
}

View File

@@ -13,7 +13,8 @@ namespace Orchard.Recipes.Providers.Executors {
private readonly ICommandManager _commandManager;
private readonly CommandParser _commandParser;
public CommandStep(ICommandManager commandManager) {
public CommandStep(ICommandManager commandManager,
IWorkContextAccessor workContextAccessor) : base(workContextAccessor) {
_commandManager = commandManager;
_commandParser = new CommandParser();
}

View File

@@ -23,7 +23,8 @@ namespace Orchard.Recipes.Providers.Executors {
public ContentDefinitionStep(
IContentDefinitionManager contentDefinitionManager,
IContentDefinitionReader contentDefinitionReader,
IContentDefinitionEventHandler contentDefinitonEventHandlers) {
IContentDefinitionEventHandler contentDefinitonEventHandlers,
IWorkContextAccessor workContextAccessor) : base(workContextAccessor) {
_contentDefinitionManager = contentDefinitionManager;
_contentDefinitionReader = contentDefinitionReader;

View File

@@ -14,7 +14,11 @@ namespace Orchard.Recipes.Providers.Executors {
private readonly IOrchardServices _orchardServices;
private readonly ITransactionManager _transactionManager;
public ContentStep(IOrchardServices orchardServices, ITransactionManager transactionManager) {
public ContentStep(
IOrchardServices orchardServices,
ITransactionManager transactionManager,
IWorkContextAccessor workContextAccessor) : base(workContextAccessor) {
_orchardServices = orchardServices;
_transactionManager = transactionManager;
BatchSize = 64;
@@ -79,6 +83,7 @@ namespace Orchard.Recipes.Providers.Executors {
// Determine if the import is to be batched in multiple transactions.
var startIndex = 0;
var itemIndex = 0;
var batchSize = GetBatchSizeForDataStep(context.RecipeStep.Step);
Logger.Debug("Using batch size {0}.", batchSize);
@@ -96,7 +101,7 @@ namespace Orchard.Recipes.Providers.Executors {
if (elementDictionary[nextIdentity.ToString()].HasAttributes) {
itemId = elementDictionary[nextIdentity.ToString()].FirstAttribute.Value;
}
Logger.Information("Importing data item '{0}'.", itemId);
Logger.Information("Importing data item '{0}' (item {1}/{2}).", itemId, itemIndex + 1, elementDictionary.Count);
try {
_orchardServices.ContentManager.Import(
elementDictionary[nextIdentity.ToString()],
@@ -106,6 +111,7 @@ namespace Orchard.Recipes.Providers.Executors {
Logger.Error(ex, "Error while importing data item '{0}'.", itemId);
throw;
}
itemIndex++;
nextIdentity = importContentSession.GetNextInBatch();
}

View File

@@ -10,7 +10,10 @@ namespace Orchard.Recipes.Providers.Executors {
public class MigrationStep : RecipeExecutionStep {
private readonly IDataMigrationManager _dataMigrationManager;
public MigrationStep(IDataMigrationManager dataMigrationManager) {
public MigrationStep(
IDataMigrationManager dataMigrationManager,
IWorkContextAccessor workContextAccessor) : base(workContextAccessor) {
_dataMigrationManager = dataMigrationManager;
}

View File

@@ -18,7 +18,8 @@ namespace Orchard.Recipes.Providers.Executors {
public ModuleStep(
IPackagingSourceManager packagingSourceManager,
IPackageManager packageManager,
IExtensionManager extensionManager) {
IExtensionManager extensionManager,
IWorkContextAccessor workContextAccessor) : base(workContextAccessor) {
_packagingSourceManager = packagingSourceManager;
_packageManager = packageManager;
@@ -47,7 +48,7 @@ namespace Orchard.Recipes.Providers.Executors {
}
if (packageId == null) {
throw new InvalidOperationException("PackageId is required in a Module declaration in a recipe file.");
throw new InvalidOperationException("PackageId is required in a module declaration in a recipe file.");
}
// download and install module from the orchard feed or a custom feed if repository is specified.

View File

@@ -19,7 +19,8 @@ namespace Orchard.Recipes.Providers.Executors {
IRecipeHarvester recipeHarvester,
IRecipeStepQueue recipeStepQueue,
IRepository<RecipeStepResultRecord> recipeStepResultRecordRepository,
ISessionLocator sessionLocator) {
ISessionLocator sessionLocator,
IWorkContextAccessor workContextAccessor) : base(workContextAccessor) {
_recipeHarvester = recipeHarvester;
_recipeStepQueue = recipeStepQueue;
@@ -42,20 +43,23 @@ namespace Orchard.Recipes.Providers.Executors {
foreach (var recipeElement in recipeElements) {
var extensionId = recipeElement.Attr("ExtensionId");
var recipeName = recipeElement.Attr("Name");
var recipes = recipesDictionary.ContainsKey(extensionId) ? recipesDictionary[extensionId] : default(IDictionary<string, Recipe>);
if (recipes == null) {
recipes = recipesDictionary[extensionId] = HarvestRecipes(extensionId);
Logger.Information("Executing recipe '{0}' in extension '{1}'.", recipeName, extensionId);
try {
var recipes = recipesDictionary.ContainsKey(extensionId) ? recipesDictionary[extensionId] : default(IDictionary<string, Recipe>);
if (recipes == null)
recipes = recipesDictionary[extensionId] = HarvestRecipes(extensionId);
if (!recipes.ContainsKey(recipeName))
throw new Exception(String.Format("No recipe named '{0}' was found in extension '{1}'.", recipeName, extensionId));
EnqueueRecipe(session, context.ExecutionId, recipes[recipeName]);
}
var recipe = recipes.ContainsKey(recipeName) ? recipes[recipeName] : default(Recipe);
if (recipe == null) {
Logger.Error(String.Format("No recipe named {0} was found for extension {1}", recipeName, extensionId));
continue;
catch (Exception ex) {
Logger.Error(ex, "Error while executing recipe '{0}' in extension '{1}'.", recipeName, extensionId);
throw;
}
EnqueueRecipe(session, context.ExecutionId, recipe);
}
}

View File

@@ -14,7 +14,12 @@ namespace Orchard.Recipes.Providers.Executors {
private readonly IContentManager _contentManager;
private readonly Lazy<IEnumerable<IContentHandler>> _handlers;
public SettingsStep(ISiteService siteService, IContentManager contentManager, Lazy<IEnumerable<IContentHandler>> handlers) {
public SettingsStep(
ISiteService siteService,
IContentManager contentManager,
Lazy<IEnumerable<IContentHandler>> handlers,
IWorkContextAccessor workContextAccessor) : base(workContextAccessor) {
_siteService = siteService;
_contentManager = contentManager;
_handlers = handlers;

View File

@@ -33,7 +33,6 @@ namespace Orchard.Recipes.Services {
public void ScheduleWork(string executionId) {
var shellDescriptor = _shellDescriptorManager.GetShellDescriptor();
Logger.Information("Scheduling execution of recipe {0}.", executionId);
// TODO: this task entry may need to become appdata folder backed if it isn't already
_processingEngine.AddTask(
_shellSettings,
@@ -45,15 +44,18 @@ namespace Orchard.Recipes.Services {
public void ExecuteWork(string executionId) {
ThreadContext.Properties["ExecutionId"] = executionId;
try {
Logger.Information("Executing next step of recipe.");
// todo: this callback should be guarded against concurrency by the IProcessingEngine
var scheduleMore = _recipeStepExecutor.Value.ExecuteNextStep(executionId);
if (scheduleMore)
if (scheduleMore) {
Logger.Information("Scheduling next step of recipe.");
ScheduleWork(executionId);
else
}
else {
Logger.Information("All recipe steps executed; restarting shell.");
// https://github.com/OrchardCMS/Orchard/issues/3672
// Because recipes execute in their own workcontext, we need to restart the shell, as signaling a cache won't work across workcontexts.
_events.Changed(_shellDescriptorManager.GetShellDescriptor(), _shellSettings.Name);
}
}
finally {
ThreadContext.Properties["ExecutionId"] = null;

View File

@@ -28,12 +28,12 @@ namespace Orchard.Recipes.Services {
public bool ExecuteNextStep(string executionId) {
var nextRecipeStep = _recipeStepQueue.Dequeue(executionId);
if (nextRecipeStep == null) {
Logger.Information("Recipe execution completed.");
Logger.Information("No more recipe steps left to execute.");
_recipeExecuteEventHandler.ExecutionComplete(executionId);
return false;
}
Logger.Information("Running all recipe handlers for step '{0}'.", nextRecipeStep.Name);
Logger.Information("Executing recipe step '{0}'.", nextRecipeStep.Name);
var recipeContext = new RecipeContext { RecipeStep = nextRecipeStep, Executed = false, ExecutionId = executionId };

View File

@@ -9,7 +9,10 @@ namespace Orchard.Roles.Recipes.Executors {
public class RolesStep : RecipeExecutionStep {
private readonly IRoleService _roleService;
public RolesStep(IRoleService roleService) {
public RolesStep(
IRoleService roleService,
IWorkContextAccessor workContextAccessor) : base(workContextAccessor) {
_roleService = roleService;
}
@@ -23,7 +26,7 @@ namespace Orchard.Roles.Recipes.Executors {
foreach (var roleElement in context.RecipeStep.Step.Elements()) {
var roleName = roleElement.Attribute("Name").Value;
Logger.Information("Processing role '{0}'.", roleName);
Logger.Information("Importing role '{0}'.", roleName);
try {
var role = _roleService.GetRoleByName(roleName);
@@ -40,7 +43,7 @@ namespace Orchard.Roles.Recipes.Executors {
_roleService.UpdateRole(role.Id, role.Name, permissionsValid.Union(role.RolesPermissions.Select(p => p.Permission.Name)));
}
catch (Exception ex) {
Logger.Error(ex, "Error while processing role '{0}'.", roleName);
Logger.Error(ex, "Error while importing role '{0}'.", roleName);
throw;
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Linq;
using Orchard.Logging;
using Orchard.Recipes.Models;
using Orchard.Recipes.Services;
using Orchard.Rules.Models;
@@ -9,7 +10,10 @@ namespace Orchard.Rules.Recipes.Executors {
public class RulesStep : RecipeExecutionStep {
private readonly IRulesServices _rulesServices;
public RulesStep(IRulesServices rulesServices) {
public RulesStep(
IRulesServices rulesServices,
IWorkContextAccessor workContextAccessor) : base(workContextAccessor) {
_rulesServices = rulesServices;
}
@@ -19,27 +23,34 @@ namespace Orchard.Rules.Recipes.Executors {
public override void Execute(RecipeExecutionContext context) {
foreach (var rule in context.RecipeStep.Step.Elements()) {
var ruleName = rule.Attribute("Name").Value;
Logger.Information("Importing rule '{0}'.", ruleName);
var ruleRecord = _rulesServices.CreateRule(rule.Attribute("Name").Value);
ruleRecord.Enabled = bool.Parse(rule.Attribute("Enabled").Value);
try {
var ruleRecord = _rulesServices.CreateRule(ruleName);
ruleRecord.Enabled = bool.Parse(rule.Attribute("Enabled").Value);
ruleRecord.Actions = rule.Element("Actions").Elements().Select(action =>
new ActionRecord {
Type = action.Attribute("Type").Value,
Category = action.Attribute("Category").Value,
Position = int.Parse(action.Attribute("Position").Value),
Parameters = action.Attribute("Parameters").Value,
RuleRecord = ruleRecord
}).ToList();
ruleRecord.Events = rule.Element("Events").Elements().Select(action =>
new EventRecord {
Type = action.Attribute("Type").Value,
Category = action.Attribute("Category").Value,
Parameters = action.Attribute("Parameters").Value,
RuleRecord = ruleRecord
}).ToList();
ruleRecord.Actions = rule.Element("Actions").Elements().Select(action =>
new ActionRecord {
Type = action.Attribute("Type").Value,
Category = action.Attribute("Category").Value,
Position = int.Parse(action.Attribute("Position").Value),
Parameters = action.Attribute("Parameters").Value,
RuleRecord = ruleRecord
}).ToList();
ruleRecord.Events = rule.Element("Events").Elements().Select(action =>
new EventRecord {
Type = action.Attribute("Type").Value,
Category = action.Attribute("Category").Value,
Parameters = action.Attribute("Parameters").Value,
RuleRecord = ruleRecord
}).ToList();
}
catch (Exception ex) {
Logger.Error(ex, "Error while importing rule '{0}'.", ruleName);
throw;
}
}
}
}

View File

@@ -1,3 +1,5 @@
using System;
using Orchard.Logging;
using Orchard.Recipes.Models;
using Orchard.Recipes.Services;
using Orchard.Themes.Services;
@@ -10,13 +12,21 @@ namespace Orchard.Themes.Recipes.Executors {
get { return "CurrentTheme"; }
}
public CurrentThemeStep(ISiteThemeService siteThemeService) {
public CurrentThemeStep(ISiteThemeService siteThemeService, IWorkContextAccessor workContextAccessor) : base(workContextAccessor) {
_siteThemeService = siteThemeService;
}
public override void Execute(RecipeExecutionContext context) {
var themeId = context.RecipeStep.Step.Attribute("id").Value;
_siteThemeService.SetSiteTheme(themeId);
Logger.Information("Setting site theme to '{0}'.", themeId);
try {
_siteThemeService.SetSiteTheme(themeId);
}
catch (Exception ex) {
Logger.Error(ex, "Error while setting site theme to '{0}'.", themeId);
throw;
}
}
}
}

View File

@@ -23,7 +23,8 @@ namespace Orchard.Themes.Recipes.Executors {
IPackageManager packageManager,
IExtensionManager extensionManager,
IThemeService themeService,
ISiteThemeService siteThemeService) {
ISiteThemeService siteThemeService,
IWorkContextAccessor workContextAccessor) : base(workContextAccessor) {
_packagingSourceManager = packagingSourceManager;
_packageManager = packageManager;

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Orchard.Data;
using Orchard.Logging;
using Orchard.Recipes.Models;
using Orchard.Recipes.Services;
using Orchard.Workflows.Models;
@@ -14,7 +15,8 @@ namespace Orchard.Workflows.Recipes.Executors {
public WorkflowsStep(
IRepository<WorkflowDefinitionRecord> workflowDefinitionRepository,
IRepository<ActivityRecord> activityRepository,
IRepository<TransitionRecord> transitionRepository) {
IRepository<TransitionRecord> transitionRepository,
IWorkContextAccessor workContextAccessor) : base(workContextAccessor) {
_workflowDefinitionRepository = workflowDefinitionRepository;
_activityRepository = activityRepository;
@@ -27,39 +29,51 @@ namespace Orchard.Workflows.Recipes.Executors {
public override void Execute(RecipeExecutionContext context) {
foreach (var workflowDefinitionElement in context.RecipeStep.Step.Elements()) {
var workflowDefinition = GetOrCreateWorkflowDefinition(workflowDefinitionElement.Attribute("Name").Value);
var activitiesElement = workflowDefinitionElement.Element("Activities");
var transitionsElement = workflowDefinitionElement.Element("Transitions");
var activitiesDictionary = new Dictionary<int, ActivityRecord>();
var workflowName = workflowDefinitionElement.Attribute("Name").Value;
Logger.Information("Importing workflow '{0}'.", workflowName);
workflowDefinition.Enabled = Boolean.Parse(workflowDefinitionElement.Attribute("Enabled").Value);
try {
var workflowDefinition = GetOrCreateWorkflowDefinition(workflowName);
var activitiesElement = workflowDefinitionElement.Element("Activities");
var transitionsElement = workflowDefinitionElement.Element("Transitions");
var activitiesDictionary = new Dictionary<int, ActivityRecord>();
foreach (var activityElement in activitiesElement.Elements()) {
var localId = Int32.Parse(activityElement.Attribute("Id").Value);
var activity = new ActivityRecord {
Name = activityElement.Attribute("Name").Value,
Start = Boolean.Parse(activityElement.Attribute("Start").Value),
X = Int32.Parse(activityElement.Attribute("X").Value),
Y = Int32.Parse(activityElement.Attribute("Y").Value),
State = activityElement.Element("State").Value
};
workflowDefinition.Enabled = Boolean.Parse(workflowDefinitionElement.Attribute("Enabled").Value);
activitiesDictionary.Add(localId, activity);
workflowDefinition.ActivityRecords.Add(activity);
foreach (var activityElement in activitiesElement.Elements()) {
var localId = Int32.Parse(activityElement.Attribute("Id").Value);
var activityName = activityElement.Attribute("Name").Value;
Logger.Information("Importing activity '{0}' with ID '{1}'.", activityName, localId);
var activity = new ActivityRecord {
Name = activityName,
Start = Boolean.Parse(activityElement.Attribute("Start").Value),
X = Int32.Parse(activityElement.Attribute("X").Value),
Y = Int32.Parse(activityElement.Attribute("Y").Value),
State = activityElement.Element("State").Value
};
activitiesDictionary.Add(localId, activity);
workflowDefinition.ActivityRecords.Add(activity);
}
foreach (var transitionElement in transitionsElement.Elements()) {
var sourceActivityId = Int32.Parse(transitionElement.Attribute("SourceActivityId").Value);
var sourceEndpoint = transitionElement.Attribute("SourceEndpoint").Value;
var destinationActivityId = Int32.Parse(transitionElement.Attribute("DestinationActivityId").Value);
var destinationEndpoint = transitionElement.Attribute("DestinationEndpoint").Value;
Logger.Information("Importing transition between activities '{0}' and '{1}'.", sourceActivityId, destinationActivityId);
workflowDefinition.TransitionRecords.Add(new TransitionRecord {
SourceActivityRecord = activitiesDictionary[sourceActivityId],
SourceEndpoint = sourceEndpoint,
DestinationActivityRecord = activitiesDictionary[destinationActivityId],
DestinationEndpoint = destinationEndpoint
});
}
}
foreach (var transitionElement in transitionsElement.Elements()) {
var sourceActivityId = Int32.Parse(transitionElement.Attribute("SourceActivityId").Value);
var sourceEndpoint = transitionElement.Attribute("SourceEndpoint").Value;
var destinationActivityId = Int32.Parse(transitionElement.Attribute("DestinationActivityId").Value);
var destinationEndpoint = transitionElement.Attribute("DestinationEndpoint").Value;
workflowDefinition.TransitionRecords.Add(new TransitionRecord {
SourceActivityRecord = activitiesDictionary[sourceActivityId],
SourceEndpoint = sourceEndpoint,
DestinationActivityRecord = activitiesDictionary[destinationActivityId],
DestinationEndpoint = destinationEndpoint
});
catch (Exception ex) {
Logger.Error(ex, "Error while importing workflow '{0}'.", workflowName);
throw;
}
}
}

View File

@@ -165,6 +165,7 @@
<Compile Include="Recipes\Services\RecipeBuilder.cs" />
<Compile Include="Recipes\Services\RecipeBuilderStep.cs" />
<Compile Include="Recipes\Models\RecipeExecutionContext.cs" />
<Compile Include="Recipes\Services\RecipeExecutionLogger.cs" />
<Compile Include="Recipes\Services\RecipeExecutionStep.cs" />
<Compile Include="Recipes\Services\RecipeExecutor.cs" />
<Compile Include="Recipes\Services\UpdateRecipeExecutionStepContext.cs" />

View File

@@ -0,0 +1,22 @@
using System;
using Orchard.Logging;
namespace Orchard.Recipes.Services {
public class RecipeExecutionLogger : ITransientDependency, ILogger {
public RecipeExecutionLogger() {
Logger = NullLogger.Instance;
}
public Type ComponentType { get; internal set; }
public ILogger Logger { get; set; }
public bool IsEnabled(LogLevel level) {
return Logger.IsEnabled(level);
}
public void Log(LogLevel level, Exception exception, string format, params object[] args) {
var message = String.Format(format, args);
Logger.Log(level, exception, "{0}: {1}", ComponentType.Name, message);
}
}
}

View File

@@ -1,10 +1,23 @@
using System.Collections.Generic;
using Orchard.ContentManagement;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Recipes.Models;
namespace Orchard.Recipes.Services {
public abstract class RecipeExecutionStep : Component, IRecipeExecutionStep {
public abstract class RecipeExecutionStep : IDependency, IRecipeExecutionStep {
private readonly IWorkContextAccessor _workContextAccessor;
private readonly RecipeExecutionLogger _logger;
public RecipeExecutionStep(IWorkContextAccessor workContextAccessor) {
_workContextAccessor = workContextAccessor;
_logger = _workContextAccessor.GetContext().Resolve<RecipeExecutionLogger>();
_logger.ComponentType = GetType();
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public abstract string Name { get; }
public virtual IEnumerable<string> Names {
@@ -23,6 +36,10 @@ namespace Orchard.Recipes.Services {
get { return GetType().Name; }
}
protected virtual ILogger Logger {
get { return _logger; }
}
public virtual dynamic BuildEditor(dynamic shapeFactory) {
return null;
}