mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-02-09 09:16:41 +08:00
Recipes: Moving Extension recipe execution from Orchard.Modules, recipe execution result now shows failed steps (#6957), fixes #6924
The changes are taken from PR #6957, but this commit also includes the necessary merge conflict resolution.
This commit is contained in:
@@ -15,7 +15,6 @@ namespace Orchard.Modules {
|
||||
.Add(T("Modules"), "9", menu => menu.Action("Features", "Admin", new {area = "Orchard.Modules"}).Permission(Permissions.ManageFeatures)
|
||||
.Add(T("Features"), "0", item => item.Action("Features", "Admin", new {area = "Orchard.Modules"}).Permission(Permissions.ManageFeatures).LocalNav())
|
||||
.Add(T("Installed"), "1", item => item.Action("Index", "Admin", new { area = "Orchard.Modules" }).Permission(StandardPermissions.SiteOwner).LocalNav())
|
||||
.Add(T("Recipes"), "2", item => item.Action("Recipes", "Admin", new { area = "Orchard.Modules" }).Permission(StandardPermissions.SiteOwner).LocalNav())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@ using Orchard.Modules.Services;
|
||||
using Orchard.Modules.ViewModels;
|
||||
using Orchard.Mvc;
|
||||
using Orchard.Mvc.Extensions;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
using Orchard.Security;
|
||||
using Orchard.UI.Navigation;
|
||||
using Orchard.UI.Notify;
|
||||
@@ -30,8 +28,6 @@ namespace Orchard.Modules.Controllers {
|
||||
private readonly IDataMigrationManager _dataMigrationManager;
|
||||
private readonly IExtensionManager _extensionManager;
|
||||
private readonly IFeatureManager _featureManager;
|
||||
private readonly IRecipeHarvester _recipeHarvester;
|
||||
private readonly IRecipeManager _recipeManager;
|
||||
private readonly ShellDescriptor _shellDescriptor;
|
||||
private readonly ShellSettings _shellSettings;
|
||||
|
||||
@@ -42,8 +38,6 @@ namespace Orchard.Modules.Controllers {
|
||||
IDataMigrationManager dataMigrationManager,
|
||||
IExtensionManager extensionManager,
|
||||
IFeatureManager featureManager,
|
||||
IRecipeHarvester recipeHarvester,
|
||||
IRecipeManager recipeManager,
|
||||
ShellDescriptor shellDescriptor,
|
||||
ShellSettings shellSettings,
|
||||
IShapeFactory shapeFactory) {
|
||||
@@ -53,8 +47,6 @@ namespace Orchard.Modules.Controllers {
|
||||
_dataMigrationManager = dataMigrationManager;
|
||||
_extensionManager = extensionManager;
|
||||
_featureManager = featureManager;
|
||||
_recipeHarvester = recipeHarvester;
|
||||
_recipeManager = recipeManager;
|
||||
_shellDescriptor = shellDescriptor;
|
||||
_shellSettings = shellSettings;
|
||||
Shape = shapeFactory;
|
||||
@@ -111,62 +103,6 @@ namespace Orchard.Modules.Controllers {
|
||||
});
|
||||
}
|
||||
|
||||
public ActionResult Recipes() {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not allowed to manage modules")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
IEnumerable<ModuleEntry> modules = _extensionManager.AvailableExtensions()
|
||||
.Where(extensionDescriptor => ExtensionIsAllowed(extensionDescriptor))
|
||||
.OrderBy(extensionDescriptor => extensionDescriptor.Name)
|
||||
.Select(extensionDescriptor => new ModuleEntry { Descriptor = extensionDescriptor });
|
||||
|
||||
var viewModel = new RecipesViewModel();
|
||||
|
||||
if (_recipeHarvester != null) {
|
||||
viewModel.Modules = modules.Select(x => new ModuleRecipesViewModel {
|
||||
Module = x,
|
||||
Recipes = _recipeHarvester.HarvestRecipes(x.Descriptor.Id).Where(recipe => !recipe.IsSetupRecipe).ToList()
|
||||
})
|
||||
.Where(x => x.Recipes.Any())
|
||||
.ToList();
|
||||
}
|
||||
|
||||
return View(viewModel);
|
||||
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("Recipes")]
|
||||
public ActionResult RecipesPOST(string moduleId, string name) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not allowed to manage modules")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
ModuleEntry module = _extensionManager.AvailableExtensions()
|
||||
.Where(extensionDescriptor => extensionDescriptor.Id == moduleId && ExtensionIsAllowed(extensionDescriptor))
|
||||
.Select(extensionDescriptor => new ModuleEntry { Descriptor = extensionDescriptor }).FirstOrDefault();
|
||||
|
||||
if (module == null) {
|
||||
return HttpNotFound();
|
||||
}
|
||||
|
||||
Recipe recipe = _recipeHarvester.HarvestRecipes(module.Descriptor.Id).FirstOrDefault(x => !x.IsSetupRecipe && x.Name == name);
|
||||
|
||||
if (recipe == null) {
|
||||
return HttpNotFound();
|
||||
}
|
||||
|
||||
try {
|
||||
_recipeManager.Execute(recipe);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Logger.Error(e, "Error while executing recipe {0} in {1}", moduleId, name);
|
||||
Services.Notifier.Error(T("Recipes {0} contains unsupported module installation steps.", recipe.Name));
|
||||
}
|
||||
|
||||
Services.Notifier.Success(T("The recipe {0} was executed successfully.", recipe.Name));
|
||||
|
||||
return RedirectToAction("Recipes");
|
||||
|
||||
}
|
||||
public ActionResult Features() {
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to manage features")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
@@ -116,7 +116,6 @@
|
||||
<Compile Include="Services\IModuleService.cs" />
|
||||
<Compile Include="Models\ModuleEntry.cs" />
|
||||
<Compile Include="Models\ModuleFeature.cs" />
|
||||
<Compile Include="ViewModels\RecipesViewModel.cs" />
|
||||
<Compile Include="ViewModels\FeaturesViewModel.cs" />
|
||||
<Compile Include="Permissions.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
@@ -163,9 +162,6 @@
|
||||
<ItemGroup>
|
||||
<Content Include="Views\ModuleEntry.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Admin\Recipes.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Scripts\features.admin.js" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
using Orchard.Localization;
|
||||
using Orchard.Security;
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.Recipes
|
||||
{
|
||||
public class AdminMenu : INavigationProvider
|
||||
{
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public string MenuName
|
||||
{
|
||||
get { return "admin"; }
|
||||
}
|
||||
|
||||
public void GetNavigation(NavigationBuilder builder)
|
||||
{
|
||||
builder.AddImageSet("modules")
|
||||
.Add(T("Modules"), "9", BuildMenu);
|
||||
}
|
||||
|
||||
private void BuildMenu(NavigationItemBuilder menu)
|
||||
{
|
||||
menu.Add(T("Recipes"), "2", item => item.Action("Index", "Admin", new { area = "Orchard.Recipes" }).Permission(StandardPermissions.SiteOwner).LocalNav());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.Environment.Extensions;
|
||||
using Orchard.Environment.Extensions.Models;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
using Orchard.Security;
|
||||
using Orchard.UI.Notify;
|
||||
using Orchard.UI.Admin;
|
||||
using Orchard.Recipes.ViewModels;
|
||||
|
||||
|
||||
namespace Orchard.Recipes.Controllers
|
||||
{
|
||||
[Admin]
|
||||
public class AdminController : Controller
|
||||
{
|
||||
private readonly IExtensionManager _extensionManager;
|
||||
private readonly IRecipeHarvester _recipeHarvester;
|
||||
private readonly IRecipeManager _recipeManager;
|
||||
private readonly IRecipeResultAccessor _recipeResultAccessor;
|
||||
private readonly ShellSettings _shellSettings;
|
||||
|
||||
public AdminController(
|
||||
IOrchardServices services,
|
||||
IExtensionManager extensionManager,
|
||||
IRecipeHarvester recipeHarvester,
|
||||
IRecipeManager recipeManager,
|
||||
IRecipeResultAccessor recipeResultAccessor,
|
||||
ShellSettings shellSettings,
|
||||
IShapeFactory shapeFactory)
|
||||
{
|
||||
Services = services;
|
||||
_extensionManager = extensionManager;
|
||||
_recipeHarvester = recipeHarvester;
|
||||
_recipeManager = recipeManager;
|
||||
_recipeResultAccessor = recipeResultAccessor;
|
||||
_shellSettings = shellSettings;
|
||||
Shape = shapeFactory;
|
||||
|
||||
T = NullLocalizer.Instance;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
public IOrchardServices Services { get; set; }
|
||||
public ILogger Logger { get; set; }
|
||||
public dynamic Shape { get; set; }
|
||||
|
||||
public ActionResult Index()
|
||||
{
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not allowed to execute recipe files.")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
IEnumerable<ModuleEntry> modules = _extensionManager.AvailableExtensions()
|
||||
.Where(extensionDescriptor => ExtensionIsAllowed(extensionDescriptor))
|
||||
.OrderBy(extensionDescriptor => extensionDescriptor.Name)
|
||||
.Select(extensionDescriptor => new ModuleEntry { Descriptor = extensionDescriptor });
|
||||
|
||||
var viewModel = new RecipesViewModel();
|
||||
|
||||
if (_recipeHarvester != null)
|
||||
{
|
||||
viewModel.Modules = modules.Select(x => new ModuleRecipesViewModel
|
||||
{
|
||||
Module = x,
|
||||
Recipes = _recipeHarvester.HarvestRecipes(x.Descriptor.Id).Where(recipe => !recipe.IsSetupRecipe).ToList()
|
||||
})
|
||||
.Where(x => x.Recipes.Any())
|
||||
.ToList();
|
||||
}
|
||||
|
||||
return View(viewModel);
|
||||
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("Recipes")]
|
||||
public ActionResult RecipesPOST(string moduleId, string name)
|
||||
{
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not allowed to execute recipe files.")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
ModuleEntry module = _extensionManager.AvailableExtensions()
|
||||
.Where(extensionDescriptor => extensionDescriptor.Id == moduleId && ExtensionIsAllowed(extensionDescriptor))
|
||||
.Select(extensionDescriptor => new ModuleEntry { Descriptor = extensionDescriptor }).FirstOrDefault();
|
||||
|
||||
if (module == null)
|
||||
{
|
||||
return HttpNotFound();
|
||||
}
|
||||
|
||||
Recipe recipe = _recipeHarvester.HarvestRecipes(module.Descriptor.Id).FirstOrDefault(x => !x.IsSetupRecipe && x.Name == name);
|
||||
|
||||
if (recipe == null)
|
||||
{
|
||||
return HttpNotFound();
|
||||
}
|
||||
|
||||
var executionId = _recipeManager.Execute(recipe);
|
||||
|
||||
if (String.IsNullOrEmpty(executionId))
|
||||
{
|
||||
Logger.Error("Error while executing recipe {0} in {1}.", name, moduleId);
|
||||
|
||||
Services.Notifier.Error(T("Error while executing recipe {0} in {1}.", name, moduleId));
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
else
|
||||
{
|
||||
return RedirectToAction("RecipeResult", new { executionId = executionId });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ActionResult RecipeResult(string executionId)
|
||||
{
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not allowed to view recipe file execution results.")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var result = _recipeResultAccessor.GetResult(executionId);
|
||||
|
||||
var viewModel = new RecipeResultViewModel()
|
||||
{
|
||||
Result = result
|
||||
};
|
||||
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the module is allowed for the current tenant
|
||||
/// </summary>
|
||||
private bool ExtensionIsAllowed(ExtensionDescriptor extensionDescriptor)
|
||||
{
|
||||
return _shellSettings.Modules.Length == 0 || _shellSettings.Modules.Contains(extensionDescriptor.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Orchard.Environment.Extensions.Models;
|
||||
|
||||
namespace Orchard.Recipes.Models {
|
||||
/// <summary>
|
||||
/// Represents a module.
|
||||
/// </summary>
|
||||
public class ModuleEntry {
|
||||
/// <summary>
|
||||
/// Default constructor.
|
||||
/// </summary>
|
||||
public ModuleEntry() {
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The module's extension descriptor.
|
||||
/// </summary>
|
||||
public ExtensionDescriptor Descriptor { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -114,8 +114,11 @@
|
||||
<Content Include="Web.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AdminMenu.cs" />
|
||||
<Compile Include="Commands\RecipesCommands.cs" />
|
||||
<Compile Include="Controllers\AdminController.cs" />
|
||||
<Compile Include="Migrations.cs" />
|
||||
<Compile Include="Models\ModuleEntry.cs" />
|
||||
<Compile Include="Models\RecipeResultRecord.cs" />
|
||||
<Compile Include="Models\RecipeStepResultRecord.cs" />
|
||||
<Compile Include="Models\VersionHistoryOptions.cs" />
|
||||
@@ -147,8 +150,9 @@
|
||||
<Compile Include="ViewModels\ContentTypeEntry.cs" />
|
||||
<Compile Include="ViewModels\CustomStepEntry.cs" />
|
||||
<Compile Include="ViewModels\ContentBuilderStepViewModel.cs" />
|
||||
<Compile Include="ViewModels\ImportResultViewModel.cs" />
|
||||
<Compile Include="ViewModels\RecipeResultViewModel.cs" />
|
||||
<Compile Include="ViewModels\ImportViewModel.cs" />
|
||||
<Compile Include="ViewModels\RecipesViewModel.cs" />
|
||||
<Compile Include="ViewModels\SetupRecipeStepViewModel.cs" />
|
||||
<Compile Include="ViewModels\SiteSettingsStepViewModel.cs" />
|
||||
</ItemGroup>
|
||||
@@ -189,6 +193,12 @@
|
||||
<ItemGroup>
|
||||
<Content Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Admin\RecipeResult.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Admin\Index.cshtml" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
using Orchard.Recipes.Models;
|
||||
|
||||
namespace Orchard.Recipes.ViewModels {
|
||||
public class ImportResultViewModel {
|
||||
public class RecipeResultViewModel {
|
||||
public RecipeResult Result { get; set; }
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -1,8 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Modules.Models;
|
||||
using Orchard.Recipes.Models;
|
||||
|
||||
namespace Orchard.Modules.ViewModels {
|
||||
namespace Orchard.Recipes.ViewModels {
|
||||
public class RecipesViewModel {
|
||||
public IEnumerable<ModuleRecipesViewModel> Modules { get; set; }
|
||||
}
|
||||
+2
-5
@@ -1,5 +1,5 @@
|
||||
@using Orchard.Utility.Extensions
|
||||
@model Orchard.Modules.ViewModels.RecipesViewModel
|
||||
@model Orchard.Recipes.ViewModels.RecipesViewModel
|
||||
|
||||
@{
|
||||
Layout.Title = T("Recipes");
|
||||
@@ -16,14 +16,11 @@
|
||||
<div class="properties">
|
||||
<h2>@descriptor.Name<span> - @T("Version: {0}", !string.IsNullOrEmpty(descriptor.Version) ? descriptor.Version : T("1.0").ToString())</span></h2>
|
||||
|
||||
@*@if (!string.IsNullOrEmpty(descriptor.Description)) {
|
||||
<p>@descriptor.Description</p>
|
||||
}*@
|
||||
|
||||
@foreach (var recipe in moduleEntry.Recipes) {
|
||||
<br />
|
||||
<div>
|
||||
<h4>@recipe.Name.CamelFriendly() - @Html.ActionLink(T("Execute").Text, "Recipes", "Admin", new { area = "Orchard.Modules", moduleId = descriptor.Id, name = recipe.Name }, new { itemprop = "UnsafeUrl" })</h4>
|
||||
<h4>@recipe.Name.CamelFriendly() - @Html.ActionLink(T("Execute").Text, "Recipes", "Admin", new { area = "Orchard.Recipes", moduleId = descriptor.Id, name = recipe.Name }, new { itemprop = "UnsafeUrl" })</h4>
|
||||
<p>@(!string.IsNullOrEmpty(recipe.Description) ? recipe.Description : T("No description").ToString())</p>
|
||||
</div>
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
@model Orchard.Recipes.ViewModels.RecipeResultViewModel
|
||||
@{
|
||||
Layout.Title = T("Recipe File Execution Result").ToString();
|
||||
}
|
||||
@if (Model.Result.IsSuccessful) {
|
||||
<div class="message message-Information">
|
||||
@T("The recipe file was successfully executed.")
|
||||
</div>
|
||||
}
|
||||
else {
|
||||
<div class="message message-Error">
|
||||
@T("The recipe file execution failed. Check the logs (recipe execution ID <strong>{0}</strong>) for more information.", Model.Result.ExecutionId)
|
||||
</div>
|
||||
}
|
||||
<h2>@T("Recipe steps")</h2>
|
||||
<table class="items" style="width: auto;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>@T("Recipe")</th>
|
||||
<th>@T("Step")</th>
|
||||
<th>@T("Executed")</th>
|
||||
<th>@T("Result")</th>
|
||||
<th>@T("Message")</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var step in Model.Result.Steps) {
|
||||
<tr>
|
||||
<td>@(!String.IsNullOrWhiteSpace(step.RecipeName) ? step.RecipeName : T("Untitled").ToString())</td>
|
||||
<td>@step.StepName</td>
|
||||
<td>@step.IsCompleted</td>
|
||||
<td>@if (step.IsSuccessful) { @T("Successful") } else if (step.IsCompleted) { <strong>@T("Failed")</strong> }</td>
|
||||
<td><strong>@step.ErrorMessage</strong></td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
Reference in New Issue
Block a user